comparison libpurple/pounce.c @ 15374:5fe8042783c1

Rename gtk/ and libgaim/ to pidgin/ and libpurple/
author Sean Egan <seanegan@gmail.com>
date Sat, 20 Jan 2007 02:32:10 +0000
parents
children 6d8728fd3dda
comparison
equal deleted inserted replaced
15373:f79e0f4df793 15374:5fe8042783c1
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 }