10418
|
1 /**
|
|
2 * @file savedstatus.c Saved Status API
|
|
3 * @ingroup core
|
|
4 *
|
|
5 * gaim
|
|
6 *
|
|
7 * Gaim is the legal property of its developers, whose names are too numerous
|
|
8 * to list here. Please refer to the COPYRIGHT file distributed with this
|
|
9 * source distribution.
|
|
10 *
|
|
11 * This program is free software; you can redistribute it and/or modify
|
|
12 * it under the terms of the GNU General Public License as published by
|
|
13 * the Free Software Foundation; either version 2 of the License, or
|
|
14 * (at your option) any later version.
|
|
15 *
|
|
16 * This program is distributed in the hope that it will be useful,
|
|
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
19 * GNU General Public License for more details.
|
|
20 *
|
|
21 * You should have received a copy of the GNU General Public License
|
|
22 * along with this program; if not, write to the Free Software
|
|
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
24 */
|
|
25 #include "internal.h"
|
|
26
|
|
27 #include "debug.h"
|
|
28 #include "notify.h"
|
|
29 #include "savedstatuses.h"
|
|
30 #include "status.h"
|
|
31 #include "util.h"
|
|
32 #include "xmlnode.h"
|
|
33
|
|
34 /**
|
|
35 * The information of a snap-shot of the statuses of all
|
|
36 * your accounts. Basically these are your saved away messages.
|
|
37 * There is an overall status and message that applies to
|
|
38 * all your accounts, and then each individual account can
|
|
39 * optionally have a different custom status and message.
|
|
40 *
|
|
41 * The changes to status.xml caused by the new status API
|
|
42 * are fully backward compatible. The new status API just
|
|
43 * adds the optional sub-statuses to the XML file.
|
|
44 */
|
10419
|
45 struct _GaimSavedStatus
|
10418
|
46 {
|
|
47 char *title;
|
|
48 GaimStatusPrimitive type;
|
|
49 char *message;
|
|
50
|
10419
|
51 GList *substatuses; /**< A list of GaimSavedStatusSub's. */
|
10418
|
52 };
|
|
53
|
|
54 /*
|
|
55 * TODO: If an account is deleted, need to also delete any associated
|
10419
|
56 * GaimSavedStatusSub's.
|
10418
|
57 * TODO: If a GaimStatusType is deleted, need to also delete any
|
10419
|
58 * associated GaimSavedStatusSub's?
|
10418
|
59 */
|
10419
|
60 struct _GaimSavedStatusSub
|
10418
|
61 {
|
|
62 GaimAccount *account;
|
|
63 const GaimStatusType *type;
|
|
64 char *message;
|
|
65 };
|
|
66
|
10423
|
67 static GList *saved_statuses = NULL;
|
|
68 static guint statuses_save_timer = 0;
|
|
69 static gboolean statuses_loaded = FALSE;
|
10418
|
70
|
|
71 /**************************************************************************
|
|
72 * Helper functions
|
|
73 **************************************************************************/
|
|
74
|
|
75 static void
|
10419
|
76 free_statussavedsub(GaimSavedStatusSub *substatus)
|
10418
|
77 {
|
|
78 g_return_if_fail(substatus != NULL);
|
|
79
|
|
80 g_free(substatus->message);
|
|
81 g_free(substatus);
|
|
82 }
|
|
83
|
|
84 static void
|
10419
|
85 free_statussaved(GaimSavedStatus *status)
|
10418
|
86 {
|
|
87 g_return_if_fail(status != NULL);
|
|
88
|
|
89 g_free(status->title);
|
|
90 g_free(status->message);
|
|
91
|
|
92 while (status->substatuses != NULL)
|
|
93 {
|
10419
|
94 GaimSavedStatusSub *substatus = status->substatuses->data;
|
10418
|
95 status->substatuses = g_list_remove(status->substatuses, substatus);
|
|
96 free_statussavedsub(substatus);
|
|
97 }
|
|
98
|
|
99 g_free(status);
|
|
100 }
|
|
101
|
|
102
|
|
103 /**************************************************************************
|
10425
|
104 * Writting to disk
|
10418
|
105 **************************************************************************/
|
|
106
|
|
107 static xmlnode *
|
10419
|
108 substatus_to_xmlnode(GaimSavedStatusSub *substatus)
|
10418
|
109 {
|
|
110 xmlnode *node, *child;
|
|
111
|
|
112 node = xmlnode_new("substatus");
|
|
113
|
10424
|
114 child = xmlnode_new_child(node, "account");
|
|
115 xmlnode_set_attrib(child, "protocol", gaim_account_get_protocol_id(substatus->account));
|
|
116 xmlnode_insert_data(child, gaim_account_get_username(substatus->account), -1);
|
10418
|
117
|
10424
|
118 child = xmlnode_new_child(node, "state");
|
10418
|
119 xmlnode_insert_data(child, gaim_status_type_get_id(substatus->type), -1);
|
|
120
|
|
121 if (substatus->message != NULL)
|
|
122 {
|
10424
|
123 child = xmlnode_new_child(node, "message");
|
10418
|
124 xmlnode_insert_data(child, substatus->message, -1);
|
|
125 }
|
|
126
|
|
127 return node;
|
|
128 }
|
|
129
|
|
130 static xmlnode *
|
10419
|
131 status_to_xmlnode(GaimSavedStatus *status)
|
10418
|
132 {
|
|
133 xmlnode *node, *child;
|
|
134 GList *cur;
|
|
135
|
|
136 node = xmlnode_new("status");
|
|
137 xmlnode_set_attrib(node, "name", status->title);
|
|
138
|
10424
|
139 child = xmlnode_new_child(node, "state");
|
|
140 xmlnode_insert_data(child, gaim_primitive_get_id_from_type(status->type), -1);
|
10418
|
141
|
10424
|
142 child = xmlnode_new_child(node, "message");
|
10418
|
143 xmlnode_insert_data(child, status->message, -1);
|
|
144
|
|
145 for (cur = status->substatuses; cur != NULL; cur = cur->next)
|
|
146 {
|
|
147 child = substatus_to_xmlnode(cur->data);
|
|
148 xmlnode_insert_child(node, child);
|
|
149 }
|
|
150
|
|
151 return node;
|
|
152 }
|
|
153
|
|
154 static xmlnode *
|
|
155 statuses_to_xmlnode(void)
|
|
156 {
|
|
157 xmlnode *node, *child;
|
|
158 GList *cur;
|
|
159
|
|
160 node = xmlnode_new("statuses");
|
10423
|
161 xmlnode_set_attrib(node, "version", "1.0");
|
10418
|
162
|
|
163 for (cur = saved_statuses; cur != NULL; cur = cur->next)
|
|
164 {
|
|
165 child = status_to_xmlnode(cur->data);
|
|
166 xmlnode_insert_child(node, child);
|
|
167 }
|
|
168
|
|
169 return node;
|
|
170 }
|
|
171
|
|
172 static void
|
|
173 sync_statuses(void)
|
|
174 {
|
10423
|
175 xmlnode *node;
|
10418
|
176 char *data;
|
|
177
|
10423
|
178 if (!statuses_loaded) {
|
10418
|
179 gaim_debug_error("status", "Attempted to save statuses before they "
|
|
180 "were read!\n");
|
|
181 return;
|
|
182 }
|
|
183
|
10423
|
184 node = statuses_to_xmlnode();
|
|
185 data = xmlnode_to_formatted_str(node, NULL);
|
10418
|
186 gaim_util_write_data_to_file("status.xml", data, -1);
|
|
187 g_free(data);
|
10423
|
188 xmlnode_free(node);
|
10418
|
189 }
|
|
190
|
|
191 static gboolean
|
|
192 save_callback(gpointer data)
|
|
193 {
|
|
194 sync_statuses();
|
|
195 statuses_save_timer = 0;
|
|
196 return FALSE;
|
|
197 }
|
|
198
|
|
199 static void
|
|
200 schedule_save(void)
|
|
201 {
|
|
202 if (statuses_save_timer != 0)
|
|
203 gaim_timeout_remove(statuses_save_timer);
|
|
204 statuses_save_timer = gaim_timeout_add(1000, save_callback, NULL);
|
|
205 }
|
|
206
|
|
207
|
|
208 /**************************************************************************
|
10425
|
209 * Reading from disk
|
10418
|
210 **************************************************************************/
|
10419
|
211 static GaimSavedStatusSub *
|
10418
|
212 parse_substatus(xmlnode *substatus)
|
|
213 {
|
10419
|
214 GaimSavedStatusSub *ret;
|
10418
|
215 xmlnode *node;
|
10425
|
216 char *data;
|
10418
|
217
|
10419
|
218 ret = g_new0(GaimSavedStatusSub, 1);
|
10418
|
219
|
|
220 /* Read the account */
|
|
221 node = xmlnode_get_child(substatus, "account");
|
|
222 if (node != NULL)
|
|
223 {
|
|
224 char *acct_name;
|
|
225 const char *protocol;
|
|
226 acct_name = xmlnode_get_data(node);
|
|
227 protocol = xmlnode_get_attrib(node, "protocol");
|
|
228 if ((acct_name != NULL) && (protocol != NULL))
|
|
229 ret->account = gaim_accounts_find(acct_name, protocol);
|
|
230 g_free(acct_name);
|
|
231 }
|
|
232
|
|
233 if (ret->account == NULL)
|
|
234 {
|
|
235 g_free(ret);
|
|
236 return NULL;
|
|
237 }
|
|
238
|
|
239 /* Read the state */
|
|
240 node = xmlnode_get_child(substatus, "state");
|
10425
|
241 if ((node != NULL) && ((data = xmlnode_get_data(node)) == NULL))
|
|
242 {
|
10418
|
243 ret->type = gaim_status_type_find_with_id(
|
10425
|
244 ret->account->status_types, data);
|
10418
|
245 g_free(data);
|
|
246 }
|
|
247
|
|
248 /* Read the message */
|
|
249 node = xmlnode_get_child(substatus, "message");
|
10425
|
250 if ((node != NULL) && ((data = xmlnode_get_data(node)) == NULL))
|
|
251 {
|
10418
|
252 ret->message = data;
|
10425
|
253 }
|
10418
|
254
|
|
255 return ret;
|
|
256 }
|
|
257
|
|
258 /**
|
|
259 * Parse a saved status and add it to the saved_statuses linked list.
|
|
260 *
|
|
261 * Here's an example of the XML for a saved status:
|
|
262 * <status name="Girls">
|
|
263 * <state>away</state>
|
|
264 * <message>I like the way that they walk
|
|
265 * And it's chill to hear them talk
|
|
266 * And I can always make them smile
|
|
267 * From White Castle to the Nile</message>
|
|
268 * <substatus>
|
|
269 * <account protocol='prpl-oscar'>markdoliner</account>
|
|
270 * <state>available</state>
|
|
271 * <message>The ladies man is here to answer your queries.</message>
|
|
272 * </substatus>
|
|
273 * <substatus>
|
|
274 * <account protocol='prpl-oscar'>giantgraypanda</account>
|
|
275 * <state>away</state>
|
|
276 * <message>A.C. ain't in charge no more.</message>
|
|
277 * </substatus>
|
|
278 * </status>
|
|
279 *
|
|
280 * I know. Moving, huh?
|
|
281 */
|
10419
|
282 static GaimSavedStatus *
|
10418
|
283 parse_status(xmlnode *status)
|
|
284 {
|
10419
|
285 GaimSavedStatus *ret;
|
10418
|
286 xmlnode *node;
|
|
287 const char *attrib;
|
10425
|
288 char *data;
|
10418
|
289 int i;
|
|
290
|
10419
|
291 ret = g_new0(GaimSavedStatus, 1);
|
10418
|
292
|
|
293 /* Read the title */
|
|
294 attrib = xmlnode_get_attrib(status, "name");
|
|
295 if (attrib == NULL)
|
|
296 attrib = "No Title";
|
|
297 /* Ensure the title is unique */
|
|
298 ret->title = g_strdup(attrib);
|
|
299 i = 2;
|
10419
|
300 while (gaim_savedstatus_find(ret->title) != NULL)
|
10418
|
301 {
|
|
302 g_free(ret->title);
|
|
303 ret->title = g_strdup_printf("%s %d", attrib, i);
|
|
304 i++;
|
|
305 }
|
|
306
|
|
307 /* Read the primitive status type */
|
|
308 node = xmlnode_get_child(status, "state");
|
10425
|
309 if ((node != NULL) && ((data = xmlnode_get_data(node)) == NULL))
|
|
310 {
|
10419
|
311 ret->type = gaim_primitive_get_type_from_id(data);
|
10418
|
312 g_free(data);
|
|
313 }
|
|
314
|
|
315 /* Read the message */
|
|
316 node = xmlnode_get_child(status, "message");
|
10425
|
317 if ((node != NULL) && ((data = xmlnode_get_data(node)) == NULL))
|
|
318 {
|
10418
|
319 ret->message = data;
|
10425
|
320 }
|
10418
|
321
|
|
322 /* Read substatuses */
|
|
323 for (node = xmlnode_get_child(status, "status"); node != NULL;
|
|
324 node = xmlnode_get_next_twin(node))
|
|
325 {
|
10419
|
326 GaimSavedStatusSub *new;
|
10418
|
327 new = parse_substatus(node);
|
|
328 if (new != NULL)
|
|
329 ret->substatuses = g_list_append(ret->substatuses, new);
|
|
330 }
|
|
331
|
|
332 return ret;
|
|
333 }
|
|
334
|
|
335 /**
|
|
336 * Read the saved statuses from a file in the Gaim user dir.
|
|
337 *
|
|
338 * @return TRUE on success, FALSE on failure (if the file can not
|
|
339 * be opened, or if it contains invalid XML).
|
|
340 */
|
10425
|
341 static void
|
|
342 load_statuses(void)
|
10418
|
343 {
|
|
344 xmlnode *statuses, *status;
|
|
345
|
10425
|
346 statuses = gaim_util_read_xml_from_file("status.xml", _("saved statuses"));
|
10418
|
347
|
|
348 if (statuses == NULL)
|
10425
|
349 return;
|
10418
|
350
|
|
351 for (status = xmlnode_get_child(statuses, "status"); status != NULL;
|
|
352 status = xmlnode_get_next_twin(status))
|
|
353 {
|
10419
|
354 GaimSavedStatus *new;
|
10418
|
355 new = parse_status(status);
|
|
356 saved_statuses = g_list_append(saved_statuses, new);
|
|
357 }
|
|
358
|
|
359 xmlnode_free(statuses);
|
|
360 }
|
|
361
|
|
362
|
|
363 /**************************************************************************
|
|
364 * Saved status API
|
|
365 **************************************************************************/
|
10419
|
366 GaimSavedStatus *
|
|
367 gaim_savedstatus_new(const char *title, GaimStatusPrimitive type)
|
10418
|
368 {
|
10419
|
369 GaimSavedStatus *status;
|
10418
|
370
|
10420
|
371 g_return_val_if_fail(gaim_savedstatus_find(title) == NULL, NULL);
|
|
372
|
10419
|
373 status = g_new0(GaimSavedStatus, 1);
|
10418
|
374 status->title = g_strdup(title);
|
|
375 status->type = type;
|
|
376
|
|
377 saved_statuses = g_list_append(saved_statuses, status);
|
|
378
|
|
379 schedule_save();
|
|
380
|
|
381 return status;
|
|
382 }
|
|
383
|
10420
|
384 void
|
|
385 gaim_savedstatus_set_message(GaimSavedStatus *status, const char *message)
|
|
386 {
|
|
387 g_return_if_fail(status != NULL);
|
|
388
|
|
389 g_free(status->message);
|
|
390 status->message = g_strdup(message);
|
|
391
|
|
392 schedule_save();
|
|
393 }
|
|
394
|
10418
|
395 gboolean
|
10419
|
396 gaim_savedstatus_delete(const char *title)
|
10418
|
397 {
|
10419
|
398 GaimSavedStatus *status;
|
10418
|
399
|
10419
|
400 status = gaim_savedstatus_find(title);
|
10418
|
401
|
|
402 if (status == NULL)
|
|
403 return FALSE;
|
|
404
|
|
405 saved_statuses = g_list_remove(saved_statuses, status);
|
|
406 free_statussaved(status);
|
|
407
|
|
408 schedule_save();
|
|
409
|
|
410 return TRUE;
|
|
411 }
|
|
412
|
|
413 const GList *
|
|
414 gaim_savedstatuses_get_all(void)
|
|
415 {
|
|
416 return saved_statuses;
|
|
417 }
|
|
418
|
10419
|
419 GaimSavedStatus *
|
|
420 gaim_savedstatus_find(const char *title)
|
10418
|
421 {
|
|
422 GList *l;
|
10419
|
423 GaimSavedStatus *status;
|
10418
|
424
|
|
425 for (l = saved_statuses; l != NULL; l = g_list_next(l))
|
|
426 {
|
10419
|
427 status = (GaimSavedStatus *)l->data;
|
10418
|
428 if (!strcmp(status->title, title))
|
|
429 return status;
|
|
430 }
|
|
431
|
|
432 return NULL;
|
|
433 }
|
|
434
|
|
435 const char *
|
10419
|
436 gaim_savedstatus_get_title(const GaimSavedStatus *saved_status)
|
10418
|
437 {
|
|
438 return saved_status->title;
|
|
439 }
|
|
440
|
|
441 GaimStatusPrimitive
|
10419
|
442 gaim_savedstatus_get_type(const GaimSavedStatus *saved_status)
|
10418
|
443 {
|
|
444 return saved_status->type;
|
|
445 }
|
|
446
|
|
447 const char *
|
10419
|
448 gaim_savedstatus_get_message(const GaimSavedStatus *saved_status)
|
10418
|
449 {
|
|
450 return saved_status->message;
|
|
451 }
|
|
452
|
|
453 void
|
|
454 gaim_savedstatuses_init(void)
|
|
455 {
|
|
456 load_statuses();
|
|
457 }
|
|
458
|
|
459 void
|
|
460 gaim_savedstatuses_uninit(void)
|
|
461 {
|
|
462 if (statuses_save_timer != 0)
|
|
463 {
|
|
464 gaim_timeout_remove(statuses_save_timer);
|
|
465 statuses_save_timer = 0;
|
|
466 sync_statuses();
|
|
467 }
|
|
468
|
|
469 while (saved_statuses != NULL) {
|
10419
|
470 GaimSavedStatus *status = saved_statuses->data;
|
10418
|
471 saved_statuses = g_list_remove(saved_statuses, status);
|
|
472 free_statussaved(status);
|
|
473 }
|
|
474 }
|