2 * @file pounce.c Buddy Pounce API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "conversation.h"
40 PurplePounceEvent events
;
41 PurplePounceOption options
;
62 } PurplePounceActionData
;
68 void (*new_pounce
)(PurplePounce
*);
69 void (*free_pounce
)(PurplePounce
*);
71 } PurplePounceHandler
;
74 static GHashTable
*pounce_handlers
= NULL
;
75 static GList
*pounces
= NULL
;
76 static guint save_timer
= 0;
77 static gboolean pounces_loaded
= FALSE
;
80 /*********************************************************************
81 * Private utility functions *
82 *********************************************************************/
84 static PurplePounceActionData
*
85 find_action_data(const PurplePounce
*pounce
, const char *name
)
87 PurplePounceActionData
*action
;
89 g_return_val_if_fail(pounce
!= NULL
, NULL
);
90 g_return_val_if_fail(name
!= NULL
, NULL
);
92 action
= g_hash_table_lookup(pounce
->actions
, name
);
98 free_action_data(gpointer data
)
100 PurplePounceActionData
*action_data
= data
;
102 g_free(action_data
->name
);
104 g_hash_table_destroy(action_data
->atts
);
110 /*********************************************************************
112 *********************************************************************/
115 action_parameter_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
117 const char *name
, *param_value
;
118 xmlnode
*node
, *child
;
120 name
= (const char *)key
;
121 param_value
= (const char *)value
;
122 node
= (xmlnode
*)user_data
;
124 child
= xmlnode_new_child(node
, "param");
125 xmlnode_set_attrib(child
, "name", name
);
126 xmlnode_insert_data(child
, param_value
, -1);
130 action_parameter_list_to_xmlnode(gpointer key
, gpointer value
, gpointer user_data
)
133 PurplePounceActionData
*action_data
;
134 xmlnode
*node
, *child
;
136 action
= (const char *)key
;
137 action_data
= (PurplePounceActionData
*)value
;
138 node
= (xmlnode
*)user_data
;
140 if (!action_data
->enabled
)
143 child
= xmlnode_new_child(node
, "action");
144 xmlnode_set_attrib(child
, "type", action
);
146 g_hash_table_foreach(action_data
->atts
, action_parameter_to_xmlnode
, child
);
150 add_event_to_xmlnode(xmlnode
*node
, const char *type
)
154 child
= xmlnode_new_child(node
, "event");
155 xmlnode_set_attrib(child
, "type", type
);
159 add_option_to_xmlnode(xmlnode
*node
, const char *type
)
163 child
= xmlnode_new_child(node
, "option");
164 xmlnode_set_attrib(child
, "type", type
);
168 pounce_to_xmlnode(PurplePounce
*pounce
)
170 xmlnode
*node
, *child
;
171 PurpleAccount
*pouncer
;
172 PurplePounceEvent events
;
173 PurplePounceOption options
;
175 pouncer
= purple_pounce_get_pouncer(pounce
);
176 events
= purple_pounce_get_events(pounce
);
177 options
= purple_pounce_get_options(pounce
);
179 node
= xmlnode_new("pounce");
180 xmlnode_set_attrib(node
, "ui", pounce
->ui_type
);
182 child
= xmlnode_new_child(node
, "account");
183 xmlnode_set_attrib(child
, "protocol", pouncer
->protocol_id
);
184 xmlnode_insert_data(child
,
185 purple_normalize(pouncer
, purple_account_get_username(pouncer
)), -1);
187 child
= xmlnode_new_child(node
, "pouncee");
188 xmlnode_insert_data(child
, purple_pounce_get_pouncee(pounce
), -1);
190 /* Write pounce options */
191 child
= xmlnode_new_child(node
, "options");
192 if (options
& PURPLE_POUNCE_OPTION_AWAY
)
193 add_option_to_xmlnode(child
, "on-away");
195 /* Write pounce events */
196 child
= xmlnode_new_child(node
, "events");
197 if (events
& PURPLE_POUNCE_SIGNON
)
198 add_event_to_xmlnode(child
, "sign-on");
199 if (events
& PURPLE_POUNCE_SIGNOFF
)
200 add_event_to_xmlnode(child
, "sign-off");
201 if (events
& PURPLE_POUNCE_AWAY
)
202 add_event_to_xmlnode(child
, "away");
203 if (events
& PURPLE_POUNCE_AWAY_RETURN
)
204 add_event_to_xmlnode(child
, "return-from-away");
205 if (events
& PURPLE_POUNCE_IDLE
)
206 add_event_to_xmlnode(child
, "idle");
207 if (events
& PURPLE_POUNCE_IDLE_RETURN
)
208 add_event_to_xmlnode(child
, "return-from-idle");
209 if (events
& PURPLE_POUNCE_TYPING
)
210 add_event_to_xmlnode(child
, "start-typing");
211 if (events
& PURPLE_POUNCE_TYPED
)
212 add_event_to_xmlnode(child
, "typed");
213 if (events
& PURPLE_POUNCE_TYPING_STOPPED
)
214 add_event_to_xmlnode(child
, "stop-typing");
215 if (events
& PURPLE_POUNCE_MESSAGE_RECEIVED
)
216 add_event_to_xmlnode(child
, "message-received");
218 /* Write pounce actions */
219 child
= xmlnode_new_child(node
, "actions");
220 g_hash_table_foreach(pounce
->actions
, action_parameter_list_to_xmlnode
, child
);
222 if (purple_pounce_get_save(pounce
))
223 child
= xmlnode_new_child(node
, "save");
229 pounces_to_xmlnode(void)
231 xmlnode
*node
, *child
;
234 node
= xmlnode_new("pounces");
235 xmlnode_set_attrib(node
, "version", "1.0");
237 for (cur
= purple_pounces_get_all(); cur
!= NULL
; cur
= cur
->next
)
239 child
= pounce_to_xmlnode(cur
->data
);
240 xmlnode_insert_child(node
, child
);
254 purple_debug_error("pounce", "Attempted to save buddy pounces before "
255 "they were read!\n");
259 node
= pounces_to_xmlnode();
260 data
= xmlnode_to_formatted_str(node
, NULL
);
261 purple_util_write_data_to_file("pounces.xml", data
, -1);
267 save_cb(gpointer data
)
275 schedule_pounces_save(void)
278 save_timer
= purple_timeout_add_seconds(5, save_cb
, NULL
);
282 /*********************************************************************
283 * Reading from disk *
284 *********************************************************************/
287 free_parser_data(gpointer user_data
)
289 PounceParserData
*data
= user_data
;
291 if (data
->buffer
!= NULL
)
292 g_string_free(data
->buffer
, TRUE
);
294 g_free(data
->ui_name
);
295 g_free(data
->pouncee
);
296 g_free(data
->protocol_id
);
297 g_free(data
->event_type
);
298 g_free(data
->option_type
);
299 g_free(data
->action_name
);
300 g_free(data
->param_name
);
301 g_free(data
->account_name
);
307 start_element_handler(GMarkupParseContext
*context
,
308 const gchar
*element_name
,
309 const gchar
**attribute_names
,
310 const gchar
**attribute_values
,
311 gpointer user_data
, GError
**error
)
313 PounceParserData
*data
= user_data
;
317 atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, g_free
);
319 for (i
= 0; attribute_names
[i
] != NULL
; i
++) {
320 g_hash_table_insert(atts
, g_strdup(attribute_names
[i
]),
321 g_strdup(attribute_values
[i
]));
324 if (data
->buffer
!= NULL
) {
325 g_string_free(data
->buffer
, TRUE
);
329 if (purple_strequal(element_name
, "pounce")) {
330 const char *ui
= g_hash_table_lookup(atts
, "ui");
333 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
334 "Unset 'ui' parameter for pounce!\n");
337 data
->ui_name
= g_strdup(ui
);
341 else if (purple_strequal(element_name
, "account")) {
342 const char *protocol_id
= g_hash_table_lookup(atts
, "protocol");
344 if (protocol_id
== NULL
) {
345 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
346 "Unset 'protocol' parameter for account!\n");
349 data
->protocol_id
= g_strdup(protocol_id
);
351 else if (purple_strequal(element_name
, "option")) {
352 const char *type
= g_hash_table_lookup(atts
, "type");
355 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
356 "Unset 'type' parameter for option!\n");
359 data
->option_type
= g_strdup(type
);
361 else if (purple_strequal(element_name
, "event")) {
362 const char *type
= g_hash_table_lookup(atts
, "type");
365 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
366 "Unset 'type' parameter for event!\n");
369 data
->event_type
= g_strdup(type
);
371 else if (purple_strequal(element_name
, "action")) {
372 const char *type
= g_hash_table_lookup(atts
, "type");
375 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
376 "Unset 'type' parameter for action!\n");
379 data
->action_name
= g_strdup(type
);
381 else if (purple_strequal(element_name
, "param")) {
382 const char *param_name
= g_hash_table_lookup(atts
, "name");
384 if (param_name
== NULL
) {
385 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
386 "Unset 'name' parameter for param!\n");
389 data
->param_name
= g_strdup(param_name
);
392 g_hash_table_destroy(atts
);
396 end_element_handler(GMarkupParseContext
*context
, const gchar
*element_name
,
397 gpointer user_data
, GError
**error
)
399 PounceParserData
*data
= user_data
;
400 gchar
*buffer
= NULL
;
402 if (data
->buffer
!= NULL
) {
403 buffer
= g_string_free(data
->buffer
, FALSE
);
407 if (purple_strequal(element_name
, "account")) {
409 g_free(data
->account_name
);
410 data
->account_name
= g_strdup(buffer
);
411 tmp
= data
->protocol_id
;
412 data
->protocol_id
= g_strdup(_purple_oscar_convert(buffer
, tmp
));
415 else if (purple_strequal(element_name
, "pouncee")) {
416 g_free(data
->pouncee
);
417 data
->pouncee
= g_strdup(buffer
);
419 else if (purple_strequal(element_name
, "option")) {
420 if (purple_strequal(data
->option_type
, "on-away"))
421 data
->options
|= PURPLE_POUNCE_OPTION_AWAY
;
423 g_free(data
->option_type
);
424 data
->option_type
= NULL
;
426 else if (purple_strequal(element_name
, "event")) {
427 if (purple_strequal(data
->event_type
, "sign-on"))
428 data
->events
|= PURPLE_POUNCE_SIGNON
;
429 else if (purple_strequal(data
->event_type
, "sign-off"))
430 data
->events
|= PURPLE_POUNCE_SIGNOFF
;
431 else if (purple_strequal(data
->event_type
, "away"))
432 data
->events
|= PURPLE_POUNCE_AWAY
;
433 else if (purple_strequal(data
->event_type
, "return-from-away"))
434 data
->events
|= PURPLE_POUNCE_AWAY_RETURN
;
435 else if (purple_strequal(data
->event_type
, "idle"))
436 data
->events
|= PURPLE_POUNCE_IDLE
;
437 else if (purple_strequal(data
->event_type
, "return-from-idle"))
438 data
->events
|= PURPLE_POUNCE_IDLE_RETURN
;
439 else if (purple_strequal(data
->event_type
, "start-typing"))
440 data
->events
|= PURPLE_POUNCE_TYPING
;
441 else if (purple_strequal(data
->event_type
, "typed"))
442 data
->events
|= PURPLE_POUNCE_TYPED
;
443 else if (purple_strequal(data
->event_type
, "stop-typing"))
444 data
->events
|= PURPLE_POUNCE_TYPING_STOPPED
;
445 else if (purple_strequal(data
->event_type
, "message-received"))
446 data
->events
|= PURPLE_POUNCE_MESSAGE_RECEIVED
;
448 g_free(data
->event_type
);
449 data
->event_type
= NULL
;
451 else if (purple_strequal(element_name
, "action")) {
452 if (data
->pounce
!= NULL
) {
453 purple_pounce_action_register(data
->pounce
, data
->action_name
);
454 purple_pounce_action_set_enabled(data
->pounce
, data
->action_name
, TRUE
);
457 g_free(data
->action_name
);
458 data
->action_name
= NULL
;
460 else if (purple_strequal(element_name
, "param")) {
461 if (data
->pounce
!= NULL
) {
462 purple_pounce_action_set_attribute(data
->pounce
, data
->action_name
,
463 data
->param_name
, buffer
);
466 g_free(data
->param_name
);
467 data
->param_name
= NULL
;
469 else if (purple_strequal(element_name
, "events")) {
470 PurpleAccount
*account
;
472 account
= purple_accounts_find(data
->account_name
, data
->protocol_id
);
474 g_free(data
->account_name
);
475 g_free(data
->protocol_id
);
477 data
->account_name
= NULL
;
478 data
->protocol_id
= NULL
;
480 if (account
== NULL
) {
481 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
482 "Account for pounce not found!\n");
484 * This pounce has effectively been removed, so make
485 * sure that we save the changes to pounces.xml
487 schedule_pounces_save();
490 purple_debug(PURPLE_DEBUG_INFO
, "pounce",
491 "Creating pounce: %s, %s\n", data
->ui_name
,
494 data
->pounce
= purple_pounce_new(data
->ui_name
, account
,
495 data
->pouncee
, data
->events
,
499 g_free(data
->pouncee
);
500 data
->pouncee
= NULL
;
502 else if (purple_strequal(element_name
, "save")) {
503 if (data
->pounce
!= NULL
)
504 purple_pounce_set_save(data
->pounce
, TRUE
);
506 else if (purple_strequal(element_name
, "pounce")) {
511 g_free(data
->ui_name
);
512 g_free(data
->pouncee
);
513 g_free(data
->protocol_id
);
514 g_free(data
->event_type
);
515 g_free(data
->option_type
);
516 g_free(data
->action_name
);
517 g_free(data
->param_name
);
518 g_free(data
->account_name
);
520 data
->ui_name
= NULL
;
522 data
->protocol_id
= NULL
;
523 data
->event_type
= NULL
;
524 data
->option_type
= NULL
;
525 data
->action_name
= NULL
;
526 data
->param_name
= NULL
;
527 data
->account_name
= NULL
;
534 text_handler(GMarkupParseContext
*context
, const gchar
*text
,
535 gsize text_len
, gpointer user_data
, GError
**error
)
537 PounceParserData
*data
= user_data
;
539 if (data
->buffer
== NULL
)
540 data
->buffer
= g_string_new_len(text
, text_len
);
542 g_string_append_len(data
->buffer
, text
, text_len
);
545 static GMarkupParser pounces_parser
=
547 start_element_handler
,
555 purple_pounces_load(void)
557 gchar
*filename
= g_build_filename(purple_user_dir(), "pounces.xml", NULL
);
558 gchar
*contents
= NULL
;
560 GMarkupParseContext
*context
;
561 GError
*error
= NULL
;
562 PounceParserData
*parser_data
;
564 if (filename
== NULL
) {
565 pounces_loaded
= TRUE
;
569 if (!g_file_get_contents(filename
, &contents
, &length
, &error
)) {
570 purple_debug(PURPLE_DEBUG_ERROR
, "pounce",
571 "Error reading pounces: %s\n", error
->message
);
576 pounces_loaded
= TRUE
;
580 parser_data
= g_new0(PounceParserData
, 1);
582 context
= g_markup_parse_context_new(&pounces_parser
, 0,
583 parser_data
, free_parser_data
);
585 if (!g_markup_parse_context_parse(context
, contents
, length
, NULL
)) {
586 g_markup_parse_context_free(context
);
590 pounces_loaded
= TRUE
;
595 if (!g_markup_parse_context_end_parse(context
, NULL
)) {
596 purple_debug(PURPLE_DEBUG_ERROR
, "pounce", "Error parsing %s\n",
599 g_markup_parse_context_free(context
);
602 pounces_loaded
= TRUE
;
607 g_markup_parse_context_free(context
);
611 pounces_loaded
= TRUE
;
618 purple_pounce_new(const char *ui_type
, PurpleAccount
*pouncer
,
619 const char *pouncee
, PurplePounceEvent event
,
620 PurplePounceOption option
)
622 PurplePounce
*pounce
;
623 PurplePounceHandler
*handler
;
625 g_return_val_if_fail(ui_type
!= NULL
, NULL
);
626 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
627 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
628 g_return_val_if_fail(event
!= 0, NULL
);
630 pounce
= g_new0(PurplePounce
, 1);
632 pounce
->ui_type
= g_strdup(ui_type
);
633 pounce
->pouncer
= pouncer
;
634 pounce
->pouncee
= g_strdup(pouncee
);
635 pounce
->events
= event
;
636 pounce
->options
= option
;
638 pounce
->actions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
639 g_free
, free_action_data
);
641 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
643 if (handler
!= NULL
&& handler
->new_pounce
!= NULL
)
644 handler
->new_pounce(pounce
);
646 pounces
= g_list_append(pounces
, pounce
);
648 schedule_pounces_save();
654 purple_pounce_destroy(PurplePounce
*pounce
)
656 PurplePounceHandler
*handler
;
658 g_return_if_fail(pounce
!= NULL
);
660 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
662 pounces
= g_list_remove(pounces
, pounce
);
664 g_free(pounce
->ui_type
);
665 g_free(pounce
->pouncee
);
667 g_hash_table_destroy(pounce
->actions
);
669 if (handler
!= NULL
&& handler
->free_pounce
!= NULL
)
670 handler
->free_pounce(pounce
);
674 schedule_pounces_save();
678 purple_pounce_destroy_all_by_account(PurpleAccount
*account
)
680 PurpleAccount
*pouncer
;
681 PurplePounce
*pounce
;
684 g_return_if_fail(account
!= NULL
);
686 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
688 pounce
= (PurplePounce
*)l
->data
;
691 pouncer
= purple_pounce_get_pouncer(pounce
);
692 if (pouncer
== account
)
693 purple_pounce_destroy(pounce
);
698 purple_pounce_set_events(PurplePounce
*pounce
, PurplePounceEvent events
)
700 g_return_if_fail(pounce
!= NULL
);
701 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
703 pounce
->events
= events
;
705 schedule_pounces_save();
709 purple_pounce_set_options(PurplePounce
*pounce
, PurplePounceOption options
)
711 g_return_if_fail(pounce
!= NULL
);
713 pounce
->options
= options
;
715 schedule_pounces_save();
719 purple_pounce_set_pouncer(PurplePounce
*pounce
, PurpleAccount
*pouncer
)
721 g_return_if_fail(pounce
!= NULL
);
722 g_return_if_fail(pouncer
!= NULL
);
724 pounce
->pouncer
= pouncer
;
726 schedule_pounces_save();
730 purple_pounce_set_pouncee(PurplePounce
*pounce
, const char *pouncee
)
732 g_return_if_fail(pounce
!= NULL
);
733 g_return_if_fail(pouncee
!= NULL
);
735 g_free(pounce
->pouncee
);
736 pounce
->pouncee
= g_strdup(pouncee
);
738 schedule_pounces_save();
742 purple_pounce_set_save(PurplePounce
*pounce
, gboolean save
)
744 g_return_if_fail(pounce
!= NULL
);
748 schedule_pounces_save();
752 purple_pounce_action_register(PurplePounce
*pounce
, const char *name
)
754 PurplePounceActionData
*action_data
;
756 g_return_if_fail(pounce
!= NULL
);
757 g_return_if_fail(name
!= NULL
);
759 if (g_hash_table_lookup(pounce
->actions
, name
) != NULL
)
762 action_data
= g_new0(PurplePounceActionData
, 1);
764 action_data
->name
= g_strdup(name
);
765 action_data
->enabled
= FALSE
;
766 action_data
->atts
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
769 g_hash_table_insert(pounce
->actions
, g_strdup(name
), action_data
);
771 schedule_pounces_save();
775 purple_pounce_action_set_enabled(PurplePounce
*pounce
, const char *action
,
778 PurplePounceActionData
*action_data
;
780 g_return_if_fail(pounce
!= NULL
);
781 g_return_if_fail(action
!= NULL
);
783 action_data
= find_action_data(pounce
, action
);
785 g_return_if_fail(action_data
!= NULL
);
787 action_data
->enabled
= enabled
;
789 schedule_pounces_save();
793 purple_pounce_action_set_attribute(PurplePounce
*pounce
, const char *action
,
794 const char *attr
, const char *value
)
796 PurplePounceActionData
*action_data
;
798 g_return_if_fail(pounce
!= NULL
);
799 g_return_if_fail(action
!= NULL
);
800 g_return_if_fail(attr
!= NULL
);
802 action_data
= find_action_data(pounce
, action
);
804 g_return_if_fail(action_data
!= NULL
);
807 g_hash_table_remove(action_data
->atts
, attr
);
809 g_hash_table_insert(action_data
->atts
, g_strdup(attr
),
812 schedule_pounces_save();
816 purple_pounce_set_data(PurplePounce
*pounce
, void *data
)
818 g_return_if_fail(pounce
!= NULL
);
822 schedule_pounces_save();
826 purple_pounce_get_events(const PurplePounce
*pounce
)
828 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_NONE
);
830 return pounce
->events
;
834 purple_pounce_get_options(const PurplePounce
*pounce
)
836 g_return_val_if_fail(pounce
!= NULL
, PURPLE_POUNCE_OPTION_NONE
);
838 return pounce
->options
;
842 purple_pounce_get_pouncer(const PurplePounce
*pounce
)
844 g_return_val_if_fail(pounce
!= NULL
, NULL
);
846 return pounce
->pouncer
;
850 purple_pounce_get_pouncee(const PurplePounce
*pounce
)
852 g_return_val_if_fail(pounce
!= NULL
, NULL
);
854 return pounce
->pouncee
;
858 purple_pounce_get_save(const PurplePounce
*pounce
)
860 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
866 purple_pounce_action_is_enabled(const PurplePounce
*pounce
, const char *action
)
868 PurplePounceActionData
*action_data
;
870 g_return_val_if_fail(pounce
!= NULL
, FALSE
);
871 g_return_val_if_fail(action
!= NULL
, FALSE
);
873 action_data
= find_action_data(pounce
, action
);
875 g_return_val_if_fail(action_data
!= NULL
, FALSE
);
877 return action_data
->enabled
;
881 purple_pounce_action_get_attribute(const PurplePounce
*pounce
,
882 const char *action
, const char *attr
)
884 PurplePounceActionData
*action_data
;
886 g_return_val_if_fail(pounce
!= NULL
, NULL
);
887 g_return_val_if_fail(action
!= NULL
, NULL
);
888 g_return_val_if_fail(attr
!= NULL
, NULL
);
890 action_data
= find_action_data(pounce
, action
);
892 g_return_val_if_fail(action_data
!= NULL
, NULL
);
894 return g_hash_table_lookup(action_data
->atts
, attr
);
898 purple_pounce_get_data(const PurplePounce
*pounce
)
900 g_return_val_if_fail(pounce
!= NULL
, NULL
);
906 purple_pounce_execute(const PurpleAccount
*pouncer
, const char *pouncee
,
907 PurplePounceEvent events
)
909 PurplePounce
*pounce
;
910 PurplePounceHandler
*handler
;
911 PurplePresence
*presence
;
915 g_return_if_fail(pouncer
!= NULL
);
916 g_return_if_fail(pouncee
!= NULL
);
917 g_return_if_fail(events
!= PURPLE_POUNCE_NONE
);
919 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
921 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l_next
)
923 pounce
= (PurplePounce
*)l
->data
;
926 presence
= purple_account_get_presence(pouncer
);
928 if ((purple_pounce_get_events(pounce
) & events
) &&
929 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
930 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
932 (pounce
->options
== PURPLE_POUNCE_OPTION_NONE
||
933 (pounce
->options
& PURPLE_POUNCE_OPTION_AWAY
&&
934 !purple_presence_is_available(presence
))))
936 handler
= g_hash_table_lookup(pounce_handlers
, pounce
->ui_type
);
938 if (handler
!= NULL
&& handler
->cb
!= NULL
)
940 handler
->cb(pounce
, events
, purple_pounce_get_data(pounce
));
942 if (!purple_pounce_get_save(pounce
))
943 purple_pounce_destroy(pounce
);
948 g_free(norm_pouncee
);
952 purple_find_pounce(const PurpleAccount
*pouncer
, const char *pouncee
,
953 PurplePounceEvent events
)
955 PurplePounce
*pounce
= NULL
;
959 g_return_val_if_fail(pouncer
!= NULL
, NULL
);
960 g_return_val_if_fail(pouncee
!= NULL
, NULL
);
961 g_return_val_if_fail(events
!= PURPLE_POUNCE_NONE
, NULL
);
963 norm_pouncee
= g_strdup(purple_normalize(pouncer
, pouncee
));
965 for (l
= purple_pounces_get_all(); l
!= NULL
; l
= l
->next
)
967 pounce
= (PurplePounce
*)l
->data
;
969 if ((purple_pounce_get_events(pounce
) & events
) &&
970 (purple_pounce_get_pouncer(pounce
) == pouncer
) &&
971 !purple_utf8_strcasecmp(purple_normalize(pouncer
, purple_pounce_get_pouncee(pounce
)),
980 g_free(norm_pouncee
);
986 purple_pounces_register_handler(const char *ui
, PurplePounceCb cb
,
987 void (*new_pounce
)(PurplePounce
*pounce
),
988 void (*free_pounce
)(PurplePounce
*pounce
))
990 PurplePounceHandler
*handler
;
992 g_return_if_fail(ui
!= NULL
);
993 g_return_if_fail(cb
!= NULL
);
995 handler
= g_new0(PurplePounceHandler
, 1);
997 handler
->ui
= g_strdup(ui
);
999 handler
->new_pounce
= new_pounce
;
1000 handler
->free_pounce
= free_pounce
;
1002 g_hash_table_insert(pounce_handlers
, g_strdup(ui
), handler
);
1006 purple_pounces_unregister_handler(const char *ui
)
1008 g_return_if_fail(ui
!= NULL
);
1010 g_hash_table_remove(pounce_handlers
, ui
);
1014 purple_pounces_get_all(void)
1019 GList
*purple_pounces_get_all_for_ui(const char *ui
)
1021 GList
*list
= NULL
, *iter
;
1022 g_return_val_if_fail(ui
!= NULL
, NULL
);
1024 for (iter
= pounces
; iter
; iter
= iter
->next
) {
1025 PurplePounce
*pounce
= iter
->data
;
1026 if (purple_strequal(pounce
->ui_type
, ui
))
1027 list
= g_list_prepend(list
, pounce
);
1029 list
= g_list_reverse(list
);
1034 free_pounce_handler(gpointer user_data
)
1036 PurplePounceHandler
*handler
= (PurplePounceHandler
*)user_data
;
1038 g_free(handler
->ui
);
1043 buddy_state_cb(PurpleBuddy
*buddy
, PurplePounceEvent event
)
1045 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1046 const gchar
*name
= purple_buddy_get_name(buddy
);
1048 purple_pounce_execute(account
, name
, event
);
1052 buddy_status_changed_cb(PurpleBuddy
*buddy
, PurpleStatus
*old_status
,
1053 PurpleStatus
*status
)
1055 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1056 const gchar
*name
= purple_buddy_get_name(buddy
);
1057 gboolean old_available
, available
;
1059 available
= purple_status_is_available(status
);
1060 old_available
= purple_status_is_available(old_status
);
1062 if (available
&& !old_available
)
1063 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY_RETURN
);
1064 else if (!available
&& old_available
)
1065 purple_pounce_execute(account
, name
, PURPLE_POUNCE_AWAY
);
1069 buddy_idle_changed_cb(PurpleBuddy
*buddy
, gboolean old_idle
, gboolean idle
)
1071 PurpleAccount
*account
= purple_buddy_get_account(buddy
);
1072 const gchar
*name
= purple_buddy_get_name(buddy
);
1074 if (idle
&& !old_idle
)
1075 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE
);
1076 else if (!idle
&& old_idle
)
1077 purple_pounce_execute(account
, name
, PURPLE_POUNCE_IDLE_RETURN
);
1081 buddy_typing_cb(PurpleAccount
*account
, const char *name
, void *data
)
1083 PurpleConversation
*conv
;
1085 conv
= purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM
, name
, account
);
1088 PurpleTypingState state
;
1089 PurplePounceEvent event
;
1091 state
= purple_conv_im_get_typing_state(PURPLE_CONV_IM(conv
));
1092 if (state
== PURPLE_TYPED
)
1093 event
= PURPLE_POUNCE_TYPED
;
1094 else if (state
== PURPLE_NOT_TYPING
)
1095 event
= PURPLE_POUNCE_TYPING_STOPPED
;
1097 event
= PURPLE_POUNCE_TYPING
;
1099 purple_pounce_execute(account
, name
, event
);
1104 received_message_cb(PurpleAccount
*account
, const char *name
, void *data
)
1106 purple_pounce_execute(account
, name
, PURPLE_POUNCE_MESSAGE_RECEIVED
);
1110 purple_pounces_get_handle(void)
1112 static int pounce_handle
;
1114 return &pounce_handle
;
1118 purple_pounces_init(void)
1120 void *handle
= purple_pounces_get_handle();
1121 void *blist_handle
= purple_blist_get_handle();
1122 void *conv_handle
= purple_conversations_get_handle();
1124 pounce_handlers
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1125 g_free
, free_pounce_handler
);
1127 purple_signal_connect(blist_handle
, "buddy-idle-changed",
1128 handle
, PURPLE_CALLBACK(buddy_idle_changed_cb
), NULL
);
1129 purple_signal_connect(blist_handle
, "buddy-status-changed",
1130 handle
, PURPLE_CALLBACK(buddy_status_changed_cb
), NULL
);
1131 purple_signal_connect(blist_handle
, "buddy-signed-on",
1132 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1133 GINT_TO_POINTER(PURPLE_POUNCE_SIGNON
));
1134 purple_signal_connect(blist_handle
, "buddy-signed-off",
1135 handle
, PURPLE_CALLBACK(buddy_state_cb
),
1136 GINT_TO_POINTER(PURPLE_POUNCE_SIGNOFF
));
1138 purple_signal_connect(conv_handle
, "buddy-typing",
1139 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1140 purple_signal_connect(conv_handle
, "buddy-typed",
1141 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1142 purple_signal_connect(conv_handle
, "buddy-typing-stopped",
1143 handle
, PURPLE_CALLBACK(buddy_typing_cb
), NULL
);
1145 purple_signal_connect(conv_handle
, "received-im-msg",
1146 handle
, PURPLE_CALLBACK(received_message_cb
), NULL
);
1150 purple_pounces_uninit()
1152 if (save_timer
!= 0)
1154 purple_timeout_remove(save_timer
);
1159 purple_signals_disconnect_by_handle(purple_pounces_get_handle());
1161 g_hash_table_destroy(pounce_handlers
);
1162 pounce_handlers
= NULL
;