| |
1 /** |
| |
2 * @file pounce.c Buddy Pounce 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 #include "conversation.h" |
| |
27 #include "debug.h" |
| |
28 #include "pounce.h" |
| |
29 |
| |
30 #include "debug.h" |
| |
31 #include "pounce.h" |
| |
32 #include "util.h" |
| |
33 |
| |
34 typedef struct |
| |
35 { |
| |
36 GString *buffer; |
| |
37 |
| |
38 GaimPounce *pounce; |
| |
39 GaimPounceEvent events; |
| |
40 GaimPounceOption options; |
| |
41 |
| |
42 char *ui_name; |
| |
43 char *pouncee; |
| |
44 char *protocol_id; |
| |
45 char *event_type; |
| |
46 char *option_type; |
| |
47 char *action_name; |
| |
48 char *param_name; |
| |
49 char *account_name; |
| |
50 |
| |
51 } PounceParserData; |
| |
52 |
| |
53 typedef struct |
| |
54 { |
| |
55 char *name; |
| |
56 |
| |
57 gboolean enabled; |
| |
58 |
| |
59 GHashTable *atts; |
| |
60 |
| |
61 } GaimPounceActionData; |
| |
62 |
| |
63 typedef struct |
| |
64 { |
| |
65 char *ui; |
| |
66 GaimPounceCb cb; |
| |
67 void (*new_pounce)(GaimPounce *); |
| |
68 void (*free_pounce)(GaimPounce *); |
| |
69 |
| |
70 } GaimPounceHandler; |
| |
71 |
| |
72 |
| |
73 static GHashTable *pounce_handlers = NULL; |
| |
74 static GList *pounces = NULL; |
| |
75 static guint save_timer = 0; |
| |
76 static gboolean pounces_loaded = FALSE; |
| |
77 |
| |
78 |
| |
79 /********************************************************************* |
| |
80 * Private utility functions * |
| |
81 *********************************************************************/ |
| |
82 |
| |
83 static GaimPounceActionData * |
| |
84 find_action_data(const GaimPounce *pounce, const char *name) |
| |
85 { |
| |
86 GaimPounceActionData *action; |
| |
87 |
| |
88 g_return_val_if_fail(pounce != NULL, NULL); |
| |
89 g_return_val_if_fail(name != NULL, NULL); |
| |
90 |
| |
91 action = g_hash_table_lookup(pounce->actions, name); |
| |
92 |
| |
93 return action; |
| |
94 } |
| |
95 |
| |
96 static void |
| |
97 free_action_data(gpointer data) |
| |
98 { |
| |
99 GaimPounceActionData *action_data = data; |
| |
100 |
| |
101 g_free(action_data->name); |
| |
102 |
| |
103 g_hash_table_destroy(action_data->atts); |
| |
104 |
| |
105 g_free(action_data); |
| |
106 } |
| |
107 |
| |
108 |
| |
109 /********************************************************************* |
| |
110 * Writing to disk * |
| |
111 *********************************************************************/ |
| |
112 |
| |
113 static void |
| |
114 action_parameter_to_xmlnode(gpointer key, gpointer value, gpointer user_data) |
| |
115 { |
| |
116 const char *name, *param_value; |
| |
117 xmlnode *node, *child; |
| |
118 |
| |
119 name = (const char *)key; |
| |
120 param_value = (const char *)value; |
| |
121 node = (xmlnode *)user_data; |
| |
122 |
| |
123 child = xmlnode_new_child(node, "param"); |
| |
124 xmlnode_set_attrib(child, "name", name); |
| |
125 xmlnode_insert_data(child, param_value, -1); |
| |
126 } |
| |
127 |
| |
128 static void |
| |
129 action_parameter_list_to_xmlnode(gpointer key, gpointer value, gpointer user_data) |
| |
130 { |
| |
131 const char *action; |
| |
132 GaimPounceActionData *action_data; |
| |
133 xmlnode *node, *child; |
| |
134 |
| |
135 action = (const char *)key; |
| |
136 action_data = (GaimPounceActionData *)value; |
| |
137 node = (xmlnode *)user_data; |
| |
138 |
| |
139 if (!action_data->enabled) |
| |
140 return; |
| |
141 |
| |
142 child = xmlnode_new_child(node, "action"); |
| |
143 xmlnode_set_attrib(child, "type", action); |
| |
144 |
| |
145 g_hash_table_foreach(action_data->atts, action_parameter_to_xmlnode, child); |
| |
146 } |
| |
147 |
| |
148 static void |
| |
149 add_event_to_xmlnode(xmlnode *node, const char *type) |
| |
150 { |
| |
151 xmlnode *child; |
| |
152 |
| |
153 child = xmlnode_new_child(node, "event"); |
| |
154 xmlnode_set_attrib(child, "type", type); |
| |
155 } |
| |
156 |
| |
157 static void |
| |
158 add_option_to_xmlnode(xmlnode *node, const char *type) |
| |
159 { |
| |
160 xmlnode *child; |
| |
161 |
| |
162 child = xmlnode_new_child(node, "option"); |
| |
163 xmlnode_set_attrib(child, "type", type); |
| |
164 } |
| |
165 |
| |
166 static xmlnode * |
| |
167 pounce_to_xmlnode(GaimPounce *pounce) |
| |
168 { |
| |
169 xmlnode *node, *child; |
| |
170 GaimAccount *pouncer; |
| |
171 GaimPounceEvent events; |
| |
172 GaimPounceOption options; |
| |
173 |
| |
174 pouncer = gaim_pounce_get_pouncer(pounce); |
| |
175 events = gaim_pounce_get_events(pounce); |
| |
176 options = gaim_pounce_get_options(pounce); |
| |
177 |
| |
178 node = xmlnode_new("pounce"); |
| |
179 xmlnode_set_attrib(node, "ui", pounce->ui_type); |
| |
180 |
| |
181 child = xmlnode_new_child(node, "account"); |
| |
182 xmlnode_set_attrib(child, "protocol", pouncer->protocol_id); |
| |
183 xmlnode_insert_data(child, gaim_account_get_username(pouncer), -1); |
| |
184 |
| |
185 child = xmlnode_new_child(node, "pouncee"); |
| |
186 xmlnode_insert_data(child, gaim_pounce_get_pouncee(pounce), -1); |
| |
187 |
| |
188 /* Write pounce options */ |
| |
189 child = xmlnode_new_child(node, "options"); |
| |
190 if (options & GAIM_POUNCE_OPTION_AWAY) |
| |
191 add_option_to_xmlnode(child, "on-away"); |
| |
192 |
| |
193 /* Write pounce events */ |
| |
194 child = xmlnode_new_child(node, "events"); |
| |
195 if (events & GAIM_POUNCE_SIGNON) |
| |
196 add_event_to_xmlnode(child, "sign-on"); |
| |
197 if (events & GAIM_POUNCE_SIGNOFF) |
| |
198 add_event_to_xmlnode(child, "sign-off"); |
| |
199 if (events & GAIM_POUNCE_AWAY) |
| |
200 add_event_to_xmlnode(child, "away"); |
| |
201 if (events & GAIM_POUNCE_AWAY_RETURN) |
| |
202 add_event_to_xmlnode(child, "return-from-away"); |
| |
203 if (events & GAIM_POUNCE_IDLE) |
| |
204 add_event_to_xmlnode(child, "idle"); |
| |
205 if (events & GAIM_POUNCE_IDLE_RETURN) |
| |
206 add_event_to_xmlnode(child, "return-from-idle"); |
| |
207 if (events & GAIM_POUNCE_TYPING) |
| |
208 add_event_to_xmlnode(child, "start-typing"); |
| |
209 if (events & GAIM_POUNCE_TYPED) |
| |
210 add_event_to_xmlnode(child, "typed"); |
| |
211 if (events & GAIM_POUNCE_TYPING_STOPPED) |
| |
212 add_event_to_xmlnode(child, "stop-typing"); |
| |
213 if (events & GAIM_POUNCE_MESSAGE_RECEIVED) |
| |
214 add_event_to_xmlnode(child, "message-received"); |
| |
215 |
| |
216 /* Write pounce actions */ |
| |
217 child = xmlnode_new_child(node, "actions"); |
| |
218 g_hash_table_foreach(pounce->actions, action_parameter_list_to_xmlnode, child); |
| |
219 |
| |
220 if (gaim_pounce_get_save(pounce)) |
| |
221 child = xmlnode_new_child(node, "save"); |
| |
222 |
| |
223 return node; |
| |
224 } |
| |
225 |
| |
226 static xmlnode * |
| |
227 pounces_to_xmlnode(void) |
| |
228 { |
| |
229 xmlnode *node, *child; |
| |
230 GList *cur; |
| |
231 |
| |
232 node = xmlnode_new("pounces"); |
| |
233 xmlnode_set_attrib(node, "version", "1.0"); |
| |
234 |
| |
235 for (cur = gaim_pounces_get_all(); cur != NULL; cur = cur->next) |
| |
236 { |
| |
237 child = pounce_to_xmlnode(cur->data); |
| |
238 xmlnode_insert_child(node, child); |
| |
239 } |
| |
240 |
| |
241 return node; |
| |
242 } |
| |
243 |
| |
244 static void |
| |
245 sync_pounces(void) |
| |
246 { |
| |
247 xmlnode *node; |
| |
248 char *data; |
| |
249 |
| |
250 if (!pounces_loaded) |
| |
251 { |
| |
252 gaim_debug_error("pounce", "Attempted to save buddy pounces before " |
| |
253 "they were read!\n"); |
| |
254 return; |
| |
255 } |
| |
256 |
| |
257 node = pounces_to_xmlnode(); |
| |
258 data = xmlnode_to_formatted_str(node, NULL); |
| |
259 gaim_util_write_data_to_file("pounces.xml", data, -1); |
| |
260 g_free(data); |
| |
261 xmlnode_free(node); |
| |
262 } |
| |
263 |
| |
264 static gboolean |
| |
265 save_cb(gpointer data) |
| |
266 { |
| |
267 sync_pounces(); |
| |
268 save_timer = 0; |
| |
269 return FALSE; |
| |
270 } |
| |
271 |
| |
272 static void |
| |
273 schedule_pounces_save(void) |
| |
274 { |
| |
275 if (save_timer == 0) |
| |
276 save_timer = gaim_timeout_add(5000, save_cb, NULL); |
| |
277 } |
| |
278 |
| |
279 |
| |
280 /********************************************************************* |
| |
281 * Reading from disk * |
| |
282 *********************************************************************/ |
| |
283 |
| |
284 static void |
| |
285 free_parser_data(gpointer user_data) |
| |
286 { |
| |
287 PounceParserData *data = user_data; |
| |
288 |
| |
289 if (data->buffer != NULL) |
| |
290 g_string_free(data->buffer, TRUE); |
| |
291 |
| |
292 g_free(data->ui_name); |
| |
293 g_free(data->pouncee); |
| |
294 g_free(data->protocol_id); |
| |
295 g_free(data->event_type); |
| |
296 g_free(data->option_type); |
| |
297 g_free(data->action_name); |
| |
298 g_free(data->param_name); |
| |
299 g_free(data->account_name); |
| |
300 |
| |
301 g_free(data); |
| |
302 } |
| |
303 |
| |
304 static void |
| |
305 start_element_handler(GMarkupParseContext *context, |
| |
306 const gchar *element_name, |
| |
307 const gchar **attribute_names, |
| |
308 const gchar **attribute_values, |
| |
309 gpointer user_data, GError **error) |
| |
310 { |
| |
311 PounceParserData *data = user_data; |
| |
312 GHashTable *atts; |
| |
313 int i; |
| |
314 |
| |
315 atts = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); |
| |
316 |
| |
317 for (i = 0; attribute_names[i] != NULL; i++) { |
| |
318 g_hash_table_insert(atts, g_strdup(attribute_names[i]), |
| |
319 g_strdup(attribute_values[i])); |
| |
320 } |
| |
321 |
| |
322 if (data->buffer != NULL) { |
| |
323 g_string_free(data->buffer, TRUE); |
| |
324 data->buffer = NULL; |
| |
325 } |
| |
326 |
| |
327 if (!strcmp(element_name, "pounce")) { |
| |
328 const char *ui = g_hash_table_lookup(atts, "ui"); |
| |
329 |
| |
330 if (ui == NULL) { |
| |
331 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
332 "Unset 'ui' parameter for pounce!\n"); |
| |
333 } |
| |
334 else |
| |
335 data->ui_name = g_strdup(ui); |
| |
336 |
| |
337 data->events = 0; |
| |
338 } |
| |
339 else if (!strcmp(element_name, "account")) { |
| |
340 const char *protocol_id = g_hash_table_lookup(atts, "protocol"); |
| |
341 |
| |
342 if (protocol_id == NULL) { |
| |
343 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
344 "Unset 'protocol' parameter for account!\n"); |
| |
345 } |
| |
346 else |
| |
347 data->protocol_id = g_strdup(protocol_id); |
| |
348 } |
| |
349 else if (!strcmp(element_name, "option")) { |
| |
350 const char *type = g_hash_table_lookup(atts, "type"); |
| |
351 |
| |
352 if (type == NULL) { |
| |
353 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
354 "Unset 'type' parameter for option!\n"); |
| |
355 } |
| |
356 else |
| |
357 data->option_type = g_strdup(type); |
| |
358 } |
| |
359 else if (!strcmp(element_name, "event")) { |
| |
360 const char *type = g_hash_table_lookup(atts, "type"); |
| |
361 |
| |
362 if (type == NULL) { |
| |
363 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
364 "Unset 'type' parameter for event!\n"); |
| |
365 } |
| |
366 else |
| |
367 data->event_type = g_strdup(type); |
| |
368 } |
| |
369 else if (!strcmp(element_name, "action")) { |
| |
370 const char *type = g_hash_table_lookup(atts, "type"); |
| |
371 |
| |
372 if (type == NULL) { |
| |
373 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
374 "Unset 'type' parameter for action!\n"); |
| |
375 } |
| |
376 else |
| |
377 data->action_name = g_strdup(type); |
| |
378 } |
| |
379 else if (!strcmp(element_name, "param")) { |
| |
380 const char *param_name = g_hash_table_lookup(atts, "name"); |
| |
381 |
| |
382 if (param_name == NULL) { |
| |
383 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
384 "Unset 'name' parameter for param!\n"); |
| |
385 } |
| |
386 else |
| |
387 data->param_name = g_strdup(param_name); |
| |
388 } |
| |
389 |
| |
390 g_hash_table_destroy(atts); |
| |
391 } |
| |
392 |
| |
393 static void |
| |
394 end_element_handler(GMarkupParseContext *context, const gchar *element_name, |
| |
395 gpointer user_data, GError **error) |
| |
396 { |
| |
397 PounceParserData *data = user_data; |
| |
398 gchar *buffer = NULL; |
| |
399 |
| |
400 if (data->buffer != NULL) { |
| |
401 buffer = g_string_free(data->buffer, FALSE); |
| |
402 data->buffer = NULL; |
| |
403 } |
| |
404 |
| |
405 if (!strcmp(element_name, "account")) { |
| |
406 g_free(data->account_name); |
| |
407 data->account_name = g_strdup(buffer); |
| |
408 } |
| |
409 else if (!strcmp(element_name, "pouncee")) { |
| |
410 g_free(data->pouncee); |
| |
411 data->pouncee = g_strdup(buffer); |
| |
412 } |
| |
413 else if (!strcmp(element_name, "option")) { |
| |
414 if (!strcmp(data->option_type, "on-away")) |
| |
415 data->options |= GAIM_POUNCE_OPTION_AWAY; |
| |
416 |
| |
417 g_free(data->option_type); |
| |
418 data->option_type = NULL; |
| |
419 } |
| |
420 else if (!strcmp(element_name, "event")) { |
| |
421 if (!strcmp(data->event_type, "sign-on")) |
| |
422 data->events |= GAIM_POUNCE_SIGNON; |
| |
423 else if (!strcmp(data->event_type, "sign-off")) |
| |
424 data->events |= GAIM_POUNCE_SIGNOFF; |
| |
425 else if (!strcmp(data->event_type, "away")) |
| |
426 data->events |= GAIM_POUNCE_AWAY; |
| |
427 else if (!strcmp(data->event_type, "return-from-away")) |
| |
428 data->events |= GAIM_POUNCE_AWAY_RETURN; |
| |
429 else if (!strcmp(data->event_type, "idle")) |
| |
430 data->events |= GAIM_POUNCE_IDLE; |
| |
431 else if (!strcmp(data->event_type, "return-from-idle")) |
| |
432 data->events |= GAIM_POUNCE_IDLE_RETURN; |
| |
433 else if (!strcmp(data->event_type, "start-typing")) |
| |
434 data->events |= GAIM_POUNCE_TYPING; |
| |
435 else if (!strcmp(data->event_type, "typed")) |
| |
436 data->events |= GAIM_POUNCE_TYPED; |
| |
437 else if (!strcmp(data->event_type, "stop-typing")) |
| |
438 data->events |= GAIM_POUNCE_TYPING_STOPPED; |
| |
439 else if (!strcmp(data->event_type, "message-received")) |
| |
440 data->events |= GAIM_POUNCE_MESSAGE_RECEIVED; |
| |
441 |
| |
442 g_free(data->event_type); |
| |
443 data->event_type = NULL; |
| |
444 } |
| |
445 else if (!strcmp(element_name, "action")) { |
| |
446 if (data->pounce != NULL) { |
| |
447 gaim_pounce_action_register(data->pounce, data->action_name); |
| |
448 gaim_pounce_action_set_enabled(data->pounce, data->action_name, TRUE); |
| |
449 } |
| |
450 |
| |
451 g_free(data->action_name); |
| |
452 data->action_name = NULL; |
| |
453 } |
| |
454 else if (!strcmp(element_name, "param")) { |
| |
455 if (data->pounce != NULL) { |
| |
456 gaim_pounce_action_set_attribute(data->pounce, data->action_name, |
| |
457 data->param_name, buffer); |
| |
458 } |
| |
459 |
| |
460 g_free(data->param_name); |
| |
461 data->param_name = NULL; |
| |
462 } |
| |
463 else if (!strcmp(element_name, "events")) { |
| |
464 GaimAccount *account; |
| |
465 |
| |
466 account = gaim_accounts_find(data->account_name, data->protocol_id); |
| |
467 |
| |
468 g_free(data->account_name); |
| |
469 g_free(data->protocol_id); |
| |
470 |
| |
471 data->account_name = NULL; |
| |
472 data->protocol_id = NULL; |
| |
473 |
| |
474 if (account == NULL) { |
| |
475 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
476 "Account for pounce not found!\n"); |
| |
477 /* |
| |
478 * This pounce has effectively been removed, so make |
| |
479 * sure that we save the changes to pounces.xml |
| |
480 */ |
| |
481 schedule_pounces_save(); |
| |
482 } |
| |
483 else { |
| |
484 gaim_debug(GAIM_DEBUG_INFO, "pounce", |
| |
485 "Creating pounce: %s, %s\n", data->ui_name, |
| |
486 data->pouncee); |
| |
487 |
| |
488 data->pounce = gaim_pounce_new(data->ui_name, account, |
| |
489 data->pouncee, data->events, |
| |
490 data->options); |
| |
491 } |
| |
492 |
| |
493 g_free(data->pouncee); |
| |
494 data->pouncee = NULL; |
| |
495 } |
| |
496 else if (!strcmp(element_name, "save")) { |
| |
497 if (data->pounce != NULL) |
| |
498 gaim_pounce_set_save(data->pounce, TRUE); |
| |
499 } |
| |
500 else if (!strcmp(element_name, "pounce")) { |
| |
501 data->pounce = NULL; |
| |
502 data->events = 0; |
| |
503 data->options = 0; |
| |
504 |
| |
505 g_free(data->ui_name); |
| |
506 g_free(data->pouncee); |
| |
507 g_free(data->protocol_id); |
| |
508 g_free(data->event_type); |
| |
509 g_free(data->option_type); |
| |
510 g_free(data->action_name); |
| |
511 g_free(data->param_name); |
| |
512 g_free(data->account_name); |
| |
513 |
| |
514 data->ui_name = NULL; |
| |
515 data->pounce = NULL; |
| |
516 data->protocol_id = NULL; |
| |
517 data->event_type = NULL; |
| |
518 data->option_type = NULL; |
| |
519 data->action_name = NULL; |
| |
520 data->param_name = NULL; |
| |
521 data->account_name = NULL; |
| |
522 } |
| |
523 |
| |
524 g_free(buffer); |
| |
525 } |
| |
526 |
| |
527 static void |
| |
528 text_handler(GMarkupParseContext *context, const gchar *text, |
| |
529 gsize text_len, gpointer user_data, GError **error) |
| |
530 { |
| |
531 PounceParserData *data = user_data; |
| |
532 |
| |
533 if (data->buffer == NULL) |
| |
534 data->buffer = g_string_new_len(text, text_len); |
| |
535 else |
| |
536 g_string_append_len(data->buffer, text, text_len); |
| |
537 } |
| |
538 |
| |
539 static GMarkupParser pounces_parser = |
| |
540 { |
| |
541 start_element_handler, |
| |
542 end_element_handler, |
| |
543 text_handler, |
| |
544 NULL, |
| |
545 NULL |
| |
546 }; |
| |
547 |
| |
548 gboolean |
| |
549 gaim_pounces_load(void) |
| |
550 { |
| |
551 gchar *filename = g_build_filename(gaim_user_dir(), "pounces.xml", NULL); |
| |
552 gchar *contents = NULL; |
| |
553 gsize length; |
| |
554 GMarkupParseContext *context; |
| |
555 GError *error = NULL; |
| |
556 PounceParserData *parser_data; |
| |
557 |
| |
558 if (filename == NULL) { |
| |
559 pounces_loaded = TRUE; |
| |
560 return FALSE; |
| |
561 } |
| |
562 |
| |
563 if (!g_file_get_contents(filename, &contents, &length, &error)) { |
| |
564 gaim_debug(GAIM_DEBUG_ERROR, "pounce", |
| |
565 "Error reading pounces: %s\n", error->message); |
| |
566 |
| |
567 g_free(filename); |
| |
568 g_error_free(error); |
| |
569 |
| |
570 pounces_loaded = TRUE; |
| |
571 return FALSE; |
| |
572 } |
| |
573 |
| |
574 parser_data = g_new0(PounceParserData, 1); |
| |
575 |
| |
576 context = g_markup_parse_context_new(&pounces_parser, 0, |
| |
577 parser_data, free_parser_data); |
| |
578 |
| |
579 if (!g_markup_parse_context_parse(context, contents, length, NULL)) { |
| |
580 g_markup_parse_context_free(context); |
| |
581 g_free(contents); |
| |
582 g_free(filename); |
| |
583 |
| |
584 pounces_loaded = TRUE; |
| |
585 |
| |
586 return FALSE; |
| |
587 } |
| |
588 |
| |
589 if (!g_markup_parse_context_end_parse(context, NULL)) { |
| |
590 gaim_debug(GAIM_DEBUG_ERROR, "pounce", "Error parsing %s\n", |
| |
591 filename); |
| |
592 |
| |
593 g_markup_parse_context_free(context); |
| |
594 g_free(contents); |
| |
595 g_free(filename); |
| |
596 pounces_loaded = TRUE; |
| |
597 |
| |
598 return FALSE; |
| |
599 } |
| |
600 |
| |
601 g_markup_parse_context_free(context); |
| |
602 g_free(contents); |
| |
603 g_free(filename); |
| |
604 |
| |
605 pounces_loaded = TRUE; |
| |
606 |
| |
607 return TRUE; |
| |
608 } |
| |
609 |
| |
610 |
| |
611 GaimPounce * |
| |
612 gaim_pounce_new(const char *ui_type, GaimAccount *pouncer, |
| |
613 const char *pouncee, GaimPounceEvent event, |
| |
614 GaimPounceOption option) |
| |
615 { |
| |
616 GaimPounce *pounce; |
| |
617 GaimPounceHandler *handler; |
| |
618 |
| |
619 g_return_val_if_fail(ui_type != NULL, NULL); |
| |
620 g_return_val_if_fail(pouncer != NULL, NULL); |
| |
621 g_return_val_if_fail(pouncee != NULL, NULL); |
| |
622 g_return_val_if_fail(event != 0, NULL); |
| |
623 |
| |
624 pounce = g_new0(GaimPounce, 1); |
| |
625 |
| |
626 pounce->ui_type = g_strdup(ui_type); |
| |
627 pounce->pouncer = pouncer; |
| |
628 pounce->pouncee = g_strdup(pouncee); |
| |
629 pounce->events = event; |
| |
630 pounce->options = option; |
| |
631 |
| |
632 pounce->actions = g_hash_table_new_full(g_str_hash, g_str_equal, |
| |
633 g_free, free_action_data); |
| |
634 |
| |
635 handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type); |
| |
636 |
| |
637 if (handler != NULL && handler->new_pounce != NULL) |
| |
638 handler->new_pounce(pounce); |
| |
639 |
| |
640 pounces = g_list_append(pounces, pounce); |
| |
641 |
| |
642 schedule_pounces_save(); |
| |
643 |
| |
644 return pounce; |
| |
645 } |
| |
646 |
| |
647 void |
| |
648 gaim_pounce_destroy(GaimPounce *pounce) |
| |
649 { |
| |
650 GaimPounceHandler *handler; |
| |
651 |
| |
652 g_return_if_fail(pounce != NULL); |
| |
653 |
| |
654 handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type); |
| |
655 |
| |
656 pounces = g_list_remove(pounces, pounce); |
| |
657 |
| |
658 g_free(pounce->ui_type); |
| |
659 g_free(pounce->pouncee); |
| |
660 |
| |
661 g_hash_table_destroy(pounce->actions); |
| |
662 |
| |
663 if (handler != NULL && handler->free_pounce != NULL) |
| |
664 handler->free_pounce(pounce); |
| |
665 |
| |
666 g_free(pounce); |
| |
667 |
| |
668 schedule_pounces_save(); |
| |
669 } |
| |
670 |
| |
671 void |
| |
672 gaim_pounce_destroy_all_by_account(GaimAccount *account) |
| |
673 { |
| |
674 GaimAccount *pouncer; |
| |
675 GaimPounce *pounce; |
| |
676 GList *l, *l_next; |
| |
677 |
| |
678 g_return_if_fail(account != NULL); |
| |
679 |
| |
680 for (l = gaim_pounces_get_all(); l != NULL; l = l_next) |
| |
681 { |
| |
682 pounce = (GaimPounce *)l->data; |
| |
683 l_next = l->next; |
| |
684 |
| |
685 pouncer = gaim_pounce_get_pouncer(pounce); |
| |
686 if (pouncer == account) |
| |
687 gaim_pounce_destroy(pounce); |
| |
688 } |
| |
689 } |
| |
690 |
| |
691 void |
| |
692 gaim_pounce_set_events(GaimPounce *pounce, GaimPounceEvent events) |
| |
693 { |
| |
694 g_return_if_fail(pounce != NULL); |
| |
695 g_return_if_fail(events != GAIM_POUNCE_NONE); |
| |
696 |
| |
697 pounce->events = events; |
| |
698 |
| |
699 schedule_pounces_save(); |
| |
700 } |
| |
701 |
| |
702 void |
| |
703 gaim_pounce_set_options(GaimPounce *pounce, GaimPounceOption options) |
| |
704 { |
| |
705 g_return_if_fail(pounce != NULL); |
| |
706 |
| |
707 pounce->options = options; |
| |
708 |
| |
709 schedule_pounces_save(); |
| |
710 } |
| |
711 |
| |
712 void |
| |
713 gaim_pounce_set_pouncer(GaimPounce *pounce, GaimAccount *pouncer) |
| |
714 { |
| |
715 g_return_if_fail(pounce != NULL); |
| |
716 g_return_if_fail(pouncer != NULL); |
| |
717 |
| |
718 pounce->pouncer = pouncer; |
| |
719 |
| |
720 schedule_pounces_save(); |
| |
721 } |
| |
722 |
| |
723 void |
| |
724 gaim_pounce_set_pouncee(GaimPounce *pounce, const char *pouncee) |
| |
725 { |
| |
726 g_return_if_fail(pounce != NULL); |
| |
727 g_return_if_fail(pouncee != NULL); |
| |
728 |
| |
729 g_free(pounce->pouncee); |
| |
730 pounce->pouncee = g_strdup(pouncee); |
| |
731 |
| |
732 schedule_pounces_save(); |
| |
733 } |
| |
734 |
| |
735 void |
| |
736 gaim_pounce_set_save(GaimPounce *pounce, gboolean save) |
| |
737 { |
| |
738 g_return_if_fail(pounce != NULL); |
| |
739 |
| |
740 pounce->save = save; |
| |
741 |
| |
742 schedule_pounces_save(); |
| |
743 } |
| |
744 |
| |
745 void |
| |
746 gaim_pounce_action_register(GaimPounce *pounce, const char *name) |
| |
747 { |
| |
748 GaimPounceActionData *action_data; |
| |
749 |
| |
750 g_return_if_fail(pounce != NULL); |
| |
751 g_return_if_fail(name != NULL); |
| |
752 |
| |
753 if (g_hash_table_lookup(pounce->actions, name) != NULL) |
| |
754 return; |
| |
755 |
| |
756 action_data = g_new0(GaimPounceActionData, 1); |
| |
757 |
| |
758 action_data->name = g_strdup(name); |
| |
759 action_data->enabled = FALSE; |
| |
760 action_data->atts = g_hash_table_new_full(g_str_hash, g_str_equal, |
| |
761 g_free, g_free); |
| |
762 |
| |
763 g_hash_table_insert(pounce->actions, g_strdup(name), action_data); |
| |
764 |
| |
765 schedule_pounces_save(); |
| |
766 } |
| |
767 |
| |
768 void |
| |
769 gaim_pounce_action_set_enabled(GaimPounce *pounce, const char *action, |
| |
770 gboolean enabled) |
| |
771 { |
| |
772 GaimPounceActionData *action_data; |
| |
773 |
| |
774 g_return_if_fail(pounce != NULL); |
| |
775 g_return_if_fail(action != NULL); |
| |
776 |
| |
777 action_data = find_action_data(pounce, action); |
| |
778 |
| |
779 g_return_if_fail(action_data != NULL); |
| |
780 |
| |
781 action_data->enabled = enabled; |
| |
782 |
| |
783 schedule_pounces_save(); |
| |
784 } |
| |
785 |
| |
786 void |
| |
787 gaim_pounce_action_set_attribute(GaimPounce *pounce, const char *action, |
| |
788 const char *attr, const char *value) |
| |
789 { |
| |
790 GaimPounceActionData *action_data; |
| |
791 |
| |
792 g_return_if_fail(pounce != NULL); |
| |
793 g_return_if_fail(action != NULL); |
| |
794 g_return_if_fail(attr != NULL); |
| |
795 |
| |
796 action_data = find_action_data(pounce, action); |
| |
797 |
| |
798 g_return_if_fail(action_data != NULL); |
| |
799 |
| |
800 if (value == NULL) |
| |
801 g_hash_table_remove(action_data->atts, attr); |
| |
802 else |
| |
803 g_hash_table_insert(action_data->atts, g_strdup(attr), |
| |
804 g_strdup(value)); |
| |
805 |
| |
806 schedule_pounces_save(); |
| |
807 } |
| |
808 |
| |
809 void |
| |
810 gaim_pounce_set_data(GaimPounce *pounce, void *data) |
| |
811 { |
| |
812 g_return_if_fail(pounce != NULL); |
| |
813 |
| |
814 pounce->data = data; |
| |
815 |
| |
816 schedule_pounces_save(); |
| |
817 } |
| |
818 |
| |
819 GaimPounceEvent |
| |
820 gaim_pounce_get_events(const GaimPounce *pounce) |
| |
821 { |
| |
822 g_return_val_if_fail(pounce != NULL, GAIM_POUNCE_NONE); |
| |
823 |
| |
824 return pounce->events; |
| |
825 } |
| |
826 |
| |
827 GaimPounceOption |
| |
828 gaim_pounce_get_options(const GaimPounce *pounce) |
| |
829 { |
| |
830 g_return_val_if_fail(pounce != NULL, GAIM_POUNCE_OPTION_NONE); |
| |
831 |
| |
832 return pounce->options; |
| |
833 } |
| |
834 |
| |
835 GaimAccount * |
| |
836 gaim_pounce_get_pouncer(const GaimPounce *pounce) |
| |
837 { |
| |
838 g_return_val_if_fail(pounce != NULL, NULL); |
| |
839 |
| |
840 return pounce->pouncer; |
| |
841 } |
| |
842 |
| |
843 const char * |
| |
844 gaim_pounce_get_pouncee(const GaimPounce *pounce) |
| |
845 { |
| |
846 g_return_val_if_fail(pounce != NULL, NULL); |
| |
847 |
| |
848 return pounce->pouncee; |
| |
849 } |
| |
850 |
| |
851 gboolean |
| |
852 gaim_pounce_get_save(const GaimPounce *pounce) |
| |
853 { |
| |
854 g_return_val_if_fail(pounce != NULL, FALSE); |
| |
855 |
| |
856 return pounce->save; |
| |
857 } |
| |
858 |
| |
859 gboolean |
| |
860 gaim_pounce_action_is_enabled(const GaimPounce *pounce, const char *action) |
| |
861 { |
| |
862 GaimPounceActionData *action_data; |
| |
863 |
| |
864 g_return_val_if_fail(pounce != NULL, FALSE); |
| |
865 g_return_val_if_fail(action != NULL, FALSE); |
| |
866 |
| |
867 action_data = find_action_data(pounce, action); |
| |
868 |
| |
869 g_return_val_if_fail(action_data != NULL, FALSE); |
| |
870 |
| |
871 return action_data->enabled; |
| |
872 } |
| |
873 |
| |
874 const char * |
| |
875 gaim_pounce_action_get_attribute(const GaimPounce *pounce, |
| |
876 const char *action, const char *attr) |
| |
877 { |
| |
878 GaimPounceActionData *action_data; |
| |
879 |
| |
880 g_return_val_if_fail(pounce != NULL, NULL); |
| |
881 g_return_val_if_fail(action != NULL, NULL); |
| |
882 g_return_val_if_fail(attr != NULL, NULL); |
| |
883 |
| |
884 action_data = find_action_data(pounce, action); |
| |
885 |
| |
886 g_return_val_if_fail(action_data != NULL, NULL); |
| |
887 |
| |
888 return g_hash_table_lookup(action_data->atts, attr); |
| |
889 } |
| |
890 |
| |
891 void * |
| |
892 gaim_pounce_get_data(const GaimPounce *pounce) |
| |
893 { |
| |
894 g_return_val_if_fail(pounce != NULL, NULL); |
| |
895 |
| |
896 return pounce->data; |
| |
897 } |
| |
898 |
| |
899 void |
| |
900 gaim_pounce_execute(const GaimAccount *pouncer, const char *pouncee, |
| |
901 GaimPounceEvent events) |
| |
902 { |
| |
903 GaimPounce *pounce; |
| |
904 GaimPounceHandler *handler; |
| |
905 GaimPresence *presence; |
| |
906 GList *l, *l_next; |
| |
907 char *norm_pouncee; |
| |
908 |
| |
909 g_return_if_fail(pouncer != NULL); |
| |
910 g_return_if_fail(pouncee != NULL); |
| |
911 g_return_if_fail(events != GAIM_POUNCE_NONE); |
| |
912 |
| |
913 norm_pouncee = g_strdup(gaim_normalize(pouncer, pouncee)); |
| |
914 |
| |
915 for (l = gaim_pounces_get_all(); l != NULL; l = l_next) |
| |
916 { |
| |
917 pounce = (GaimPounce *)l->data; |
| |
918 l_next = l->next; |
| |
919 |
| |
920 presence = gaim_account_get_presence(pouncer); |
| |
921 |
| |
922 if ((gaim_pounce_get_events(pounce) & events) && |
| |
923 (gaim_pounce_get_pouncer(pounce) == pouncer) && |
| |
924 !gaim_utf8_strcasecmp(gaim_normalize(pouncer, gaim_pounce_get_pouncee(pounce)), |
| |
925 norm_pouncee) && |
| |
926 (pounce->options == GAIM_POUNCE_OPTION_NONE || |
| |
927 (pounce->options & GAIM_POUNCE_OPTION_AWAY && |
| |
928 !gaim_presence_is_available(presence)))) |
| |
929 { |
| |
930 handler = g_hash_table_lookup(pounce_handlers, pounce->ui_type); |
| |
931 |
| |
932 if (handler != NULL && handler->cb != NULL) |
| |
933 { |
| |
934 handler->cb(pounce, events, gaim_pounce_get_data(pounce)); |
| |
935 |
| |
936 if (!gaim_pounce_get_save(pounce)) |
| |
937 gaim_pounce_destroy(pounce); |
| |
938 } |
| |
939 } |
| |
940 } |
| |
941 |
| |
942 g_free(norm_pouncee); |
| |
943 } |
| |
944 |
| |
945 GaimPounce * |
| |
946 gaim_find_pounce(const GaimAccount *pouncer, const char *pouncee, |
| |
947 GaimPounceEvent events) |
| |
948 { |
| |
949 GaimPounce *pounce = NULL; |
| |
950 GList *l; |
| |
951 char *norm_pouncee; |
| |
952 |
| |
953 g_return_val_if_fail(pouncer != NULL, NULL); |
| |
954 g_return_val_if_fail(pouncee != NULL, NULL); |
| |
955 g_return_val_if_fail(events != GAIM_POUNCE_NONE, NULL); |
| |
956 |
| |
957 norm_pouncee = g_strdup(gaim_normalize(pouncer, pouncee)); |
| |
958 |
| |
959 for (l = gaim_pounces_get_all(); l != NULL; l = l->next) |
| |
960 { |
| |
961 pounce = (GaimPounce *)l->data; |
| |
962 |
| |
963 if ((gaim_pounce_get_events(pounce) & events) && |
| |
964 (gaim_pounce_get_pouncer(pounce) == pouncer) && |
| |
965 !gaim_utf8_strcasecmp(gaim_normalize(pouncer, gaim_pounce_get_pouncee(pounce)), |
| |
966 norm_pouncee)) |
| |
967 { |
| |
968 break; |
| |
969 } |
| |
970 |
| |
971 pounce = NULL; |
| |
972 } |
| |
973 |
| |
974 g_free(norm_pouncee); |
| |
975 |
| |
976 return pounce; |
| |
977 } |
| |
978 |
| |
979 void |
| |
980 gaim_pounces_register_handler(const char *ui, GaimPounceCb cb, |
| |
981 void (*new_pounce)(GaimPounce *pounce), |
| |
982 void (*free_pounce)(GaimPounce *pounce)) |
| |
983 { |
| |
984 GaimPounceHandler *handler; |
| |
985 |
| |
986 g_return_if_fail(ui != NULL); |
| |
987 g_return_if_fail(cb != NULL); |
| |
988 |
| |
989 handler = g_new0(GaimPounceHandler, 1); |
| |
990 |
| |
991 handler->ui = g_strdup(ui); |
| |
992 handler->cb = cb; |
| |
993 handler->new_pounce = new_pounce; |
| |
994 handler->free_pounce = free_pounce; |
| |
995 |
| |
996 g_hash_table_insert(pounce_handlers, g_strdup(ui), handler); |
| |
997 } |
| |
998 |
| |
999 void |
| |
1000 gaim_pounces_unregister_handler(const char *ui) |
| |
1001 { |
| |
1002 g_return_if_fail(ui != NULL); |
| |
1003 |
| |
1004 g_hash_table_remove(pounce_handlers, ui); |
| |
1005 } |
| |
1006 |
| |
1007 GList * |
| |
1008 gaim_pounces_get_all(void) |
| |
1009 { |
| |
1010 return pounces; |
| |
1011 } |
| |
1012 |
| |
1013 static void |
| |
1014 free_pounce_handler(gpointer user_data) |
| |
1015 { |
| |
1016 GaimPounceHandler *handler = (GaimPounceHandler *)user_data; |
| |
1017 |
| |
1018 g_free(handler->ui); |
| |
1019 g_free(handler); |
| |
1020 } |
| |
1021 |
| |
1022 static void |
| |
1023 buddy_state_cb(GaimBuddy *buddy, GaimPounceEvent event) |
| |
1024 { |
| |
1025 gaim_pounce_execute(buddy->account, buddy->name, event); |
| |
1026 } |
| |
1027 |
| |
1028 static void |
| |
1029 buddy_status_changed_cb(GaimBuddy *buddy, GaimStatus *old_status, |
| |
1030 GaimStatus *status) |
| |
1031 { |
| |
1032 gboolean old_available, available; |
| |
1033 |
| |
1034 available = gaim_status_is_available(status); |
| |
1035 old_available = gaim_status_is_available(old_status); |
| |
1036 |
| |
1037 if (available && !old_available) |
| |
1038 gaim_pounce_execute(buddy->account, buddy->name, |
| |
1039 GAIM_POUNCE_AWAY_RETURN); |
| |
1040 else if (!available && old_available) |
| |
1041 gaim_pounce_execute(buddy->account, buddy->name, |
| |
1042 GAIM_POUNCE_AWAY); |
| |
1043 } |
| |
1044 |
| |
1045 static void |
| |
1046 buddy_idle_changed_cb(GaimBuddy *buddy, gboolean old_idle, gboolean idle) |
| |
1047 { |
| |
1048 if (idle && !old_idle) |
| |
1049 gaim_pounce_execute(buddy->account, buddy->name, |
| |
1050 GAIM_POUNCE_IDLE); |
| |
1051 else if (!idle && old_idle) |
| |
1052 gaim_pounce_execute(buddy->account, buddy->name, |
| |
1053 GAIM_POUNCE_IDLE_RETURN); |
| |
1054 } |
| |
1055 |
| |
1056 static void |
| |
1057 buddy_typing_cb(GaimAccount *account, const char *name, void *data) |
| |
1058 { |
| |
1059 GaimConversation *conv; |
| |
1060 |
| |
1061 conv = gaim_find_conversation_with_account(GAIM_CONV_TYPE_IM, name, account); |
| |
1062 if (conv != NULL) |
| |
1063 { |
| |
1064 GaimTypingState state; |
| |
1065 GaimPounceEvent event; |
| |
1066 |
| |
1067 state = gaim_conv_im_get_typing_state(GAIM_CONV_IM(conv)); |
| |
1068 if (state == GAIM_TYPED) |
| |
1069 event = GAIM_POUNCE_TYPED; |
| |
1070 else if (state == GAIM_NOT_TYPING) |
| |
1071 event = GAIM_POUNCE_TYPING_STOPPED; |
| |
1072 else |
| |
1073 event = GAIM_POUNCE_TYPING; |
| |
1074 |
| |
1075 gaim_pounce_execute(account, name, event); |
| |
1076 } |
| |
1077 } |
| |
1078 |
| |
1079 static void |
| |
1080 received_message_cb(GaimAccount *account, const char *name, void *data) |
| |
1081 { |
| |
1082 gaim_pounce_execute(account, name, GAIM_POUNCE_MESSAGE_RECEIVED); |
| |
1083 } |
| |
1084 |
| |
1085 void * |
| |
1086 gaim_pounces_get_handle(void) |
| |
1087 { |
| |
1088 static int pounce_handle; |
| |
1089 |
| |
1090 return &pounce_handle; |
| |
1091 } |
| |
1092 |
| |
1093 void |
| |
1094 gaim_pounces_init(void) |
| |
1095 { |
| |
1096 void *handle = gaim_pounces_get_handle(); |
| |
1097 void *blist_handle = gaim_blist_get_handle(); |
| |
1098 void *conv_handle = gaim_conversations_get_handle(); |
| |
1099 |
| |
1100 pounce_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, |
| |
1101 g_free, free_pounce_handler); |
| |
1102 |
| |
1103 gaim_signal_connect(blist_handle, "buddy-idle-changed", |
| |
1104 handle, GAIM_CALLBACK(buddy_idle_changed_cb), NULL); |
| |
1105 gaim_signal_connect(blist_handle, "buddy-status-changed", |
| |
1106 handle, GAIM_CALLBACK(buddy_status_changed_cb), NULL); |
| |
1107 gaim_signal_connect(blist_handle, "buddy-signed-on", |
| |
1108 handle, GAIM_CALLBACK(buddy_state_cb), |
| |
1109 GINT_TO_POINTER(GAIM_POUNCE_SIGNON)); |
| |
1110 gaim_signal_connect(blist_handle, "buddy-signed-off", |
| |
1111 handle, GAIM_CALLBACK(buddy_state_cb), |
| |
1112 GINT_TO_POINTER(GAIM_POUNCE_SIGNOFF)); |
| |
1113 |
| |
1114 gaim_signal_connect(conv_handle, "buddy-typing", |
| |
1115 handle, GAIM_CALLBACK(buddy_typing_cb), NULL); |
| |
1116 gaim_signal_connect(conv_handle, "buddy-typed", |
| |
1117 handle, GAIM_CALLBACK(buddy_typing_cb), NULL); |
| |
1118 gaim_signal_connect(conv_handle, "buddy-typing-stopped", |
| |
1119 handle, GAIM_CALLBACK(buddy_typing_cb), NULL); |
| |
1120 |
| |
1121 gaim_signal_connect(conv_handle, "received-im-msg", |
| |
1122 handle, GAIM_CALLBACK(received_message_cb), NULL); |
| |
1123 } |
| |
1124 |
| |
1125 void |
| |
1126 gaim_pounces_uninit() |
| |
1127 { |
| |
1128 if (save_timer != 0) |
| |
1129 { |
| |
1130 gaim_timeout_remove(save_timer); |
| |
1131 save_timer = 0; |
| |
1132 sync_pounces(); |
| |
1133 } |
| |
1134 |
| |
1135 gaim_signals_disconnect_by_handle(gaim_pounces_get_handle()); |
| |
1136 } |