Mercurial > pidgin
comparison libgaim/pounce.c @ 20389:e354528c4163
propagate from branch 'im.pidgin.gaim' (head 70ac931e4936c7916eec18a07fe46a0af0fd7403)
to branch 'im.pidgin.rlaager.merging.soc-msnp13-to-svn18164' (head 5b5cde92182d2a922a8e7e6c2308342a5490a8c9)
author | Richard Laager <rlaager@wiktel.com> |
---|---|
date | Sun, 15 Apr 2007 02:10:37 +0000 |
parents | 60b1bc8dbf37 |
children | a6aad36ca735 |
comparison
equal
deleted
inserted
replaced
19842:21cb7a79ac7f | 20389:e354528c4163 |
---|---|
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 } |