Refactor & centralize mapping of incoming libpurple presence codes.
[thrasher.git] / thperl.c
blobba2fb53c1b4837fb49171a57d5a3ed4f1801435c
1 /*
2 * Thrasher Bird - XMPP transport via libpurple
3 * Copyright (C) 2008 Barracuda Networks, Inc.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with Thrasher Bird; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
19 #include <glib.h>
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <string.h>
23 #include "thperl.h"
24 #include "thblist.h"
25 #include "blist.h"
26 #include "thrasher.h"
27 #include "thpresence.h"
28 #include "thconversations.h"
29 #include "thft.h"
30 #include "unistd.h"
32 /* Perl */
33 #include "EXTERN.h"
34 #include "perl.h"
35 #include "XSUB.h"
37 /* Internal prototypes */
38 int func_call(gpointer func);
39 void destroy_record_key (gpointer key);
40 void validate_callback (SV *cb);
41 void thrasher_wrapper_destructor (void);
42 SV *clean_newSVpv(const char *inp);
43 void thrasher_remove_account(const char *jid);
45 perl_callbacks callback;
46 perl_ft_callbacks ft_callback;
49 char *trigger_timeout_func_name = "THPPW::thrasher_wrapper_trigger_timeout_func";
50 char *trigger_input_func_name = "THPPW::thrasher_wrapper_trigger_input_func";
53 GHashTable *accts = NULL;
54 GHashTable *callback_funcs = NULL;
55 GHashTable *perl_key_lookup = NULL;
57 void thrasher_purple_debug (int tf)
59 /* Flip on libpurple debugging info */
60 purple_debug_set_enabled(tf);
64 void thrasher_perl_init()
66 accts = g_hash_table_new(g_str_hash, g_str_equal);
69 /**
70 * @brief create a PurpleAccount and start connecting it.
71 * @param hash_args of arguments for thrasher_login().
72 * @return 0 for success. 1 if thrasher_login() fails. 2 if the PurpleAccount already exists.
74 int thrasher_action_login (GHashTable *hash_args)
76 int err = 0;
78 #ifdef TH_DEBUG
79 purple_debug_info("thrasher",
80 "Login request:\n");
81 GList *keys = g_hash_table_get_keys(hash_args);
82 GList *values = g_hash_table_get_values(hash_args);
83 while (keys)
85 purple_debug_info("thrasher",
86 " %s -> %s\n",
87 (char *)keys->data,
88 (char *)values->data);
89 keys = keys->next;
90 values = values->next;
93 g_list_free(keys);
94 g_list_free(values);
95 #endif /* TH_DEBUG */
97 // some parameters we always have
98 char *jid = (char *)g_strdup(g_hash_table_lookup(hash_args, "jid"));
99 char *username = (char *)g_strdup(g_hash_table_lookup(hash_args, "username"));
100 char *password = (char *)g_strdup(g_hash_table_lookup(hash_args, "password"));
101 char *proto = (char *)g_strdup(g_hash_table_lookup(hash_args, "proto"));
103 g_return_val_if_fail(jid, 0);
104 g_return_val_if_fail(username, 0);
105 g_return_val_if_fail(password, 0);
106 g_return_val_if_fail(proto, 0);
108 g_hash_table_remove(hash_args, "jid");
109 g_hash_table_remove(hash_args, "username");
110 g_hash_table_remove(hash_args, "password");
111 g_hash_table_remove(hash_args, "proto");
113 /* Verify user is not already logged in */
114 if (g_hash_table_lookup(accts, jid) == NULL)
116 PurpleAccount *account = thrasher_login(proto, username, password,
117 jid, hash_args);
119 // In the event of an error, connection_error has already been
120 // called.
121 if (account == NULL) {
122 return 1;
125 char *new_jid = g_strdup(jid);
127 /* Add user to hash */
128 g_hash_table_insert(
129 accts,
130 new_jid, /* Should eventually be the protocol + username */
131 account
134 /* Success! */
135 err = 0;
137 else
139 // At least indicate something bad has happened.
140 purple_debug_info("thrasher",
141 "login request for [%s] fail because they are apparently already logged in\n",
142 jid);
143 err = 2;
146 g_free(jid);
147 g_free(username);
148 g_free(password);
149 g_free(proto);
150 g_hash_table_destroy(hash_args);
152 return err;
158 * @brief add buddy (@p buddy_name) for given @p jid buddy list
159 * @param jid releational key to bound to account
160 * @param buddy_name name of buddy
161 * @return 1 if there is a problem, 0 otherwise (C)
163 int thrasher_action_buddy_add (char *jid, char *buddy_name)
165 PurpleAccount *account = NULL;
166 PurpleBuddy *buddy = NULL;
167 PurpleGroup *group = NULL;
169 purple_debug_info("thrasher", "Buddy_add request\n");
171 account = g_hash_table_lookup(accts, jid);
172 g_return_val_if_fail(account != NULL, 1);
174 buddy = purple_find_buddy(account, buddy_name);
176 // FIXME: Figure out if we need this buddy list stuff
177 // at all. We don't actually care, as long as we get
178 // the messages and presence to send out.
180 /* We're done if buddy already exists */
181 if (buddy == NULL)
183 char *default_group = "General";
185 /* Check to see if default group exists */
186 group = purple_find_group(default_group);
188 /* If not, create default group */
189 if (group == NULL)
190 group = purple_group_new(default_group);
192 purple_blist_add_group(group, NULL);
194 /* Add buddy to default group (NULL should be alias...) */
195 buddy = purple_buddy_new(account, buddy_name, NULL);
196 purple_blist_add_buddy(buddy, NULL, group, NULL);
197 purple_account_add_buddy(account, buddy);
199 purple_debug_info("thrasher",
200 "finished adding %s to %s's buddy list\n",
201 buddy_name, jid);
203 return 0;
204 } else {
205 purple_debug_info("thrasher",
206 "%s already on %s's buddy list\n",
207 buddy_name, jid);
210 /* Buddy already exists */
211 return 1;
215 * @brief remove buddy (@p buddy_name) from given @p jid buddy list
216 * @param jid releational key to bound to account
217 * @param buddy_name name of buddy
218 * @return 1 if there is a problem, 0 otherwise (C)
220 int thrasher_action_buddy_remove (char *jid, char *buddy_name)
222 PurpleGroup *group = NULL;
223 PurpleBuddy *buddy = NULL;
224 PurpleAccount *account = NULL;
226 purple_debug_info("thrasher", "Buddy remove\n");
228 account = g_hash_table_lookup(accts, jid);
229 g_return_val_if_fail(account != NULL, 1);
231 // Don't try and remove a buddy if the account
232 // isn't connected
233 if (purple_account_is_connected(account)) {
235 purple_debug_info("thrasher", "find_buddy...\n");
236 buddy = purple_find_buddy(account, buddy_name);
238 /* Only remove a buddy if they exist */
239 if (buddy != NULL)
241 purple_debug_info("thrasher", "get_group...\n");
242 /* Give up us the group */
243 group = purple_buddy_get_group(buddy);
245 purple_debug_info("thrasher", "account_remove_buddy...\n");
246 /* Remove server-side buddy entry */
247 purple_account_remove_buddy(account, buddy, group);
249 purple_debug_info("thrasher", "blist_remove_buddy...\n");
250 /* Also need to remove buddy entry from memory */
251 purple_blist_remove_buddy(buddy);
253 return 0;
257 /* Account isn't connected or buddy is not found */
258 return 1;
261 void thrasher_action_buddy_authorize(char *jid, char *legacy_username)
263 thrasher_action_buddy_add(jid, legacy_username);
266 void thrasher_action_buddy_deauthorize(char *jid, char *legacy_username) {
267 thrasher_action_buddy_remove(jid, legacy_username);
271 * @brief update presence status for given @p jid
272 * @param jid releational key to bound to account
273 * @param status integer representation of PurpleStatusPrimitive
274 * @param message a status message (may be NULL)
275 * @return 1 if there is a problem, 0 otherwise (C)
277 int thrasher_action_presence (char *jid, int status, char *message)
279 PurpleAccount *account;
281 g_return_val_if_fail(jid != NULL, 0);
283 account = g_hash_table_lookup(accts, jid);
285 if (account == NULL)
286 return 1;
288 thrasher_set_presence(account, status, message);
290 return 0;
295 * @brief Send @p message from the user given by @p jid to the user @p recipient
296 * @param jid
297 * @param recipient
298 * @param message
299 * @return 1 on success 0 on failure
301 int thrasher_action_outgoing_msg (char *jid, char *recipient, char *message)
303 int ret = 0;
304 PurpleAccount *account = NULL;
306 g_return_val_if_fail(jid != NULL, 0);
307 g_return_val_if_fail(recipient != NULL, 0);
308 g_return_val_if_fail(message != NULL, 0);
310 purple_debug_info("thrasher", "Send message request\n");
311 /* Pull in user if they exist */
312 account = g_hash_table_lookup(accts, jid);
314 /* User does not exist (may not be logged in) */
315 if (account == NULL) {
316 return 0;
319 /* Try to send message (message might not be sent) */
320 if (thrasher_send_msg(account,recipient,message) == TRUE)
321 ret = 1;
323 return ret;
328 * @brief Send @p chatstate from the user given by @p jid to the @p recipient
329 * @param jid
330 * @param recipient
331 * @param chatstate
333 void
334 thrasher_action_outgoing_chatstate(char *jid,
335 char *recipient,
336 PurpleTypingState chatstate) {
337 g_return_if_fail(jid != NULL);
338 g_return_if_fail(recipient != NULL);
340 PurpleAccount *pa = NULL;
341 pa = g_hash_table_lookup(accts, jid);
342 if (pa == NULL) {
343 /* User does not exist (may not be logged in) */
344 return;
347 /* Try to send chatstate */
348 thrasher_outgoing_chatstate(pa, recipient, chatstate);
353 * @brief Log the user out with the given jid
354 * @param jid releational key to bind to account
355 * @return 1 if logout was successful, 0 if it was not
357 * NOTE: As we're going to be running a process for each protocol,
358 * there's no need to worry about the protocol used.
360 int thrasher_action_logout (char *jid)
362 int ret = 0;
363 PurpleAccount *account;
365 g_return_val_if_fail(jid != NULL, 0);
367 purple_debug_info("thrasher", "Logout requested for %s\n", jid);
369 /* Verify user is logged in */
370 account =g_hash_table_lookup(accts, jid);
371 if (account != NULL)
373 thrasher_logout(account);
374 thrasher_remove_account(jid);
375 ret = 1;
378 return ret;
382 void _thrasher_action_debug_logged_in_accts_printer(gpointer key, gpointer value, gpointer user_data);
385 * @brief Dump active account name lists to STDERR from libpurple and thperl.
387 void
388 thrasher_action_debug_logged_in() {
389 fprintf(stderr, "purple_accounts_get_all_active:\n");
390 GList* list = purple_accounts_get_all_active();
391 if (list) {
392 GList *l = list;
393 while (l) {
394 PurpleAccount *pa = (PurpleAccount*)l->data;
395 fprintf(stderr, "\t%s = %p\n", purple_account_get_username(pa), pa);
396 l = l->next;
398 g_list_free(list);
401 if (accts) {
402 fprintf(stderr, "thperl accts:\n");
403 g_hash_table_foreach(accts,
404 _thrasher_action_debug_logged_in_accts_printer,
405 NULL);
407 else {
408 fprintf(stderr, "No thperl accts?!!\n");
412 void
413 _thrasher_action_debug_logged_in_accts_printer(gpointer key,
414 gpointer value,
415 gpointer user_data) {
416 const char *jid = (char*)key;
417 PurpleAccount *pa = (PurpleAccount*)value;
418 fprintf(stderr, "\t%s = %s = %p\n",
419 jid, purple_account_get_username(pa), pa);
424 * @brief Destruction function for our internal function hash
425 * @param key data element
427 void destroy_record_key (gpointer key)
429 free(key);
434 * @brief Bizarro passthrough to allow perl calling of dynamically-set C events
435 * @param key a guint value corresponding to the pointer of a callback.
436 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
438 int thrasher_wrapper_trigger_timeout_func(long key)
440 /* @exception key cannot be NULL */
441 g_return_val_if_fail(key != TNULL,TFALSE);
443 char *real_key = LONG_TO_PTR(key);
445 return func_call(real_key);
449 * @brief Bizarro passthrough (two) to allow perl calling of dynamically-set C events
450 * @param file descriptor
451 * @param condition trigger was called on
452 * @param key a long value corresponding to the pointer of a callback.
453 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
455 int thrasher_wrapper_trigger_input_func(long fd, SV *cond, long key)
457 /* @exception fd cannot be zero */
458 g_return_val_if_fail(fd != 0, 0);
459 /* @exception cond cannot be zero */
460 g_return_val_if_fail(cond != 0, 0);
461 /* @exception key cannot be zero */
462 g_return_val_if_fail(key != 0, 0);
464 // GIOCondition real_cond = cond;
465 char *real_key = LONG_TO_PTR(key);
467 /* FIXME: We need to pass cond and key to this call... */
468 return func_call(real_key);
472 * @brief Simple verifcation for callbacks
473 * @param subref to callback
476 void validate_callback (SV *cb)
478 if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV)
480 croak("Not a callback!");
481 exit(0);
487 * @brief Initialize our wrapper
488 * @param subref (SV *) to call for timeout_add
489 * @param subref (SV *) to call for input_add
490 * @param subref (SV *) to call for source_remove
491 * @param subref (SV *) to call on incoming messages
492 * @param subref (SV *) to call on incoming presence information
493 * @param subref (SV *) to call on subscription requests
494 * @param subref (SV *) to call on subscription requests from legacy users
495 * @param subref (SV *) to call on connection errors
496 * @param subref (SV *) to call when connections have succeeded
498 void thrasher_wrapper_init (SV *timeout_add_cb,
499 SV *input_add_cb,
500 SV *source_remove_cb,
501 SV *incoming_msg_cb,
502 SV *presence_in_cb,
503 SV *subscription_add_cb,
504 SV *legacy_user_add_user_cb,
505 SV *connection_error_cb,
506 SV *connection_cb,
507 SV *incoming_chatstate_cb)
510 g_return_if_fail(timeout_add_cb);
511 g_return_if_fail(input_add_cb);
512 g_return_if_fail(source_remove_cb);
513 g_return_if_fail(incoming_msg_cb);
514 g_return_if_fail(presence_in_cb);
515 g_return_if_fail(subscription_add_cb);
516 g_return_if_fail(legacy_user_add_user_cb);
517 g_return_if_fail(connection_error_cb);
518 g_return_if_fail(connection_cb);
519 g_return_if_fail(incoming_chatstate_cb);
521 /* Set the bindings for our function table */
522 callback_funcs = g_hash_table_new_full(g_direct_hash,
523 g_direct_equal,
524 destroy_record_key,
525 NULL);
527 /* Create the lookup table so we know what to do when Perl triggers a remove */
528 perl_key_lookup = g_hash_table_new(g_int_hash, g_int_equal);
530 /* Validate callback, this burns up if it fails so no
531 * need to check for errors!
533 validate_callback(timeout_add_cb);
534 validate_callback(input_add_cb);
535 validate_callback(source_remove_cb);
536 validate_callback(incoming_msg_cb);
537 validate_callback(presence_in_cb);
538 validate_callback(subscription_add_cb);
539 validate_callback(legacy_user_add_user_cb);
540 validate_callback(connection_error_cb);
541 validate_callback(connection_cb);
542 validate_callback(incoming_chatstate_cb);
544 dSP;
546 ENTER;
547 SAVETMPS;
549 PUSHMARK(SP);
550 PUTBACK;
552 callback.timeout_add = newSVsv(timeout_add_cb);
553 callback.input_add = newSVsv(input_add_cb);
554 callback.source_remove = newSVsv(source_remove_cb);
555 callback.incoming_msg = newSVsv(incoming_msg_cb);
556 callback.presence_in = newSVsv(presence_in_cb);
557 callback.subscription_add = newSVsv(subscription_add_cb);
558 callback.legacy_user_add_user = newSVsv(legacy_user_add_user_cb);
559 callback.connection_error = newSVsv(connection_error_cb);
560 callback.connection = newSVsv(connection_cb);
561 callback.incoming_chatstate = newSVsv(incoming_chatstate_cb);
563 SPAGAIN;
564 PUTBACK;
566 FREETMPS;
567 LEAVE;
571 * @brief Initialize our file transfer wrapper
572 * @param subref (SV *) to call for recv_accept
573 * @param subref (SV *) to call for recv_start
574 * @param subref (SV *) to call for recv_cancel
575 * @param subref (SV *) to call for recv_complete
576 * @param subref (SV *) to call for send_accept
577 * @param subref (SV *) to call for send_start
578 * @param subref (SV *) to call for send_cancel
579 * @param subref (SV *) to call for send_complete
580 * @param subref (SV *) to call for ui_read. Must return (buffer, size).
581 * @param subref (SV *) to call for ui_write
582 * @param subref (SV *) to call for ui_data_not_sent
584 void thrasher_wrapper_ft_init(SV *ft_recv_request_cb,
585 SV *ft_recv_accept_cb,
586 SV *ft_recv_start_cb,
587 SV *ft_recv_cancel_cb,
588 SV *ft_recv_complete_cb,
589 SV *ft_send_accept_cb,
590 SV *ft_send_start_cb,
591 SV *ft_send_cancel_cb,
592 SV *ft_send_complete_cb,
593 SV *ft_read_cb,
594 SV *ft_write_cb,
595 SV *ft_data_not_sent_cb)
597 g_return_if_fail(ft_recv_request_cb);
598 g_return_if_fail(ft_recv_accept_cb);
599 g_return_if_fail(ft_recv_start_cb);
600 g_return_if_fail(ft_recv_cancel_cb);
601 g_return_if_fail(ft_recv_complete_cb);
602 g_return_if_fail(ft_send_accept_cb);
603 g_return_if_fail(ft_send_start_cb);
604 g_return_if_fail(ft_send_cancel_cb);
605 g_return_if_fail(ft_send_complete_cb);
606 g_return_if_fail(ft_read_cb);
607 g_return_if_fail(ft_write_cb);
608 g_return_if_fail(ft_data_not_sent_cb);
610 // This assumes thrasher_wrapper_init was already called.
611 g_return_if_fail(callback_funcs);
613 validate_callback(ft_recv_request_cb);
614 validate_callback(ft_recv_accept_cb);
615 validate_callback(ft_recv_start_cb);
616 validate_callback(ft_recv_cancel_cb);
617 validate_callback(ft_recv_complete_cb);
618 validate_callback(ft_send_accept_cb);
619 validate_callback(ft_send_start_cb);
620 validate_callback(ft_send_cancel_cb);
621 validate_callback(ft_send_complete_cb);
622 validate_callback(ft_read_cb);
623 validate_callback(ft_write_cb);
624 validate_callback(ft_data_not_sent_cb);
626 dSP;
628 ENTER;
629 SAVETMPS;
631 PUSHMARK(SP);
632 PUTBACK;
634 ft_callback.ft_recv_request_cb = newSVsv(ft_recv_request_cb);
635 ft_callback.ft_recv_accept_cb = newSVsv(ft_recv_accept_cb);
636 ft_callback.ft_recv_start_cb = newSVsv(ft_recv_start_cb);
637 ft_callback.ft_recv_cancel_cb = newSVsv(ft_recv_cancel_cb);
638 ft_callback.ft_recv_complete_cb = newSVsv(ft_recv_complete_cb);
639 ft_callback.ft_send_accept_cb = newSVsv(ft_send_accept_cb);
640 ft_callback.ft_send_start_cb = newSVsv(ft_send_start_cb);
641 ft_callback.ft_send_cancel_cb = newSVsv(ft_send_cancel_cb);
642 ft_callback.ft_send_complete_cb = newSVsv(ft_send_complete_cb);
643 ft_callback.ft_read_cb = newSVsv(ft_read_cb);
644 ft_callback.ft_write_cb = newSVsv(ft_write_cb);
645 ft_callback.ft_data_not_sent_cb = newSVsv(ft_data_not_sent_cb);
647 SPAGAIN;
648 PUTBACK;
650 FREETMPS;
651 LEAVE;
657 * @brief Validate then call the given callback pointer.
658 * @param key a hash key for the local function callback table.
659 * @return boolean as int (0 || 1) on success
661 int func_call(gpointer key)
663 /* @exception key cannot be NULL */
664 g_return_val_if_fail(key != NULL,TFALSE);
666 gpointer orig_key = NULL;
667 hash_record *record = NULL;
668 int (*timeout_add_caller)(gpointer);
669 int (*input_add_caller)(gpointer, gint, PurpleInputCondition);
670 int ret = 0;
672 /* Crazy voodoo madness!
673 * We're using direct pointers as keys and a struct at the
674 * end of the pointer, so no need to get any values.
676 if (g_hash_table_lookup_extended(callback_funcs, key, &orig_key, NULL))
678 record = orig_key;
680 if (record->type == TIMEOUT_ADD)
682 timeout_add_caller = record->function;
683 ret = (timeout_add_caller)(record->data);
686 else if (record->type == INPUT_ADD)
688 input_add_caller = record->function;
689 (input_add_caller)(record->data, record->fd,
690 record->cond);
691 // these functions are void
692 ret = TRUE;
695 if (ret == FALSE)
697 /* We're never gonna be calling this again, so get rid of it */
698 g_hash_table_remove(callback_funcs, record);
701 return (ret);
704 else
706 purple_debug_info("thrasher", "Callback for [%p] does not exist!\n", key);
709 return(TFALSE);
714 * @brief Wraps Glib::Source->remove
715 * @param key to return to the Perl end
716 * @return Success of call, including the success of the callback
718 gboolean thrasher_wrapper_call_source_remove (long key)
720 long ret = FALSE;
722 /* @exception key cannot be NULL */
723 g_return_val_if_fail(callback.source_remove, 0);
725 /* @exception callback.source_remove cannot be NULL */
726 g_return_val_if_fail(key != TNULL, 0);
728 dSP;
730 ENTER;
731 SAVETMPS;
733 PUSHMARK(sp);
735 /* Push the key onto the stack */
736 XPUSHs(sv_2mortal(newSViv(key)));
738 PUTBACK;
740 call_sv(callback.source_remove, G_EVAL | G_SCALAR);
742 SPAGAIN;
744 ret = POPl;
746 FREETMPS;
747 PUTBACK;
749 LEAVE;
751 /* Remove call from lookup table */
752 g_hash_table_remove(perl_key_lookup, (gpointer)&key);
754 return ret;
759 * PurpleInputCondition is a bitwise OR of read/write events
760 * PurpleInputFunction is a callback to a guint function with the args gpointer, gint, and PurpleInoutCondition
763 * @brief Wraps Glib::IO->add_watch
764 * @param File descriptor
765 * @param Condition to trigger on
766 * @param Nested callback
767 * @param Data to pass to callback
768 * @return Hash key
770 long thrasher_wrapper_set_input_add (guint fd, PurpleInputCondition cond, PurpleInputFunction function, gpointer data)
772 g_return_val_if_fail(callback.input_add, 0);
773 g_return_val_if_fail(function != NULL, 0);
775 long key = 0;
777 hash_record *record = (hash_record *)malloc(sizeof(hash_record));
779 record->type = INPUT_ADD;
780 record->function = function;
781 record->data = data;
782 record->cond = cond;
783 record->fd = fd;
785 g_hash_table_insert(callback_funcs, record, NULL);
787 key = PTR_TO_LONG(record);
788 g_return_val_if_fail(key != 0, 0);
790 dSP;
792 ENTER;
793 SAVETMPS;
796 SV *pc = NULL;
797 CV *temp_cv = NULL;
798 SV *svkey = NULL;
801 svkey = newSViv(key);
802 temp_cv = get_cv(trigger_input_func_name, FALSE);
804 if (temp_cv)
806 pc = newRV((SV*)temp_cv);
808 /* Ok, this is hairy, first we push the vars we want to send to the
809 * timeout_add Perl callback. Next we push the var we want to give
810 * back to ourselves via trigger_input_func.
813 PUSHMARK(sp);
815 XPUSHs(sv_2mortal(newSViv(fd)));
816 XPUSHs(sv_2mortal(newSViv(cond)));
817 XPUSHs(sv_2mortal(pc));
818 XPUSHs(sv_2mortal(svkey));
820 PUTBACK;
822 call_sv(callback.input_add, G_EVAL | G_SCALAR);
823 SPAGAIN;
825 /* Tweak our internal key to an external key */
826 key = POPl;
828 else
830 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
833 g_hash_table_insert(perl_key_lookup, (gpointer)&key, record);
835 return key;
840 * @brief Wraps Glib::Timeout->add
841 * @param interval in milliseconds
842 * @param callback
843 * @param data to pass to the callback
844 * @return Key for the internal timeout_add function hash. (Should this really be returning anything?!)
846 long thrasher_wrapper_set_timeout_add (guint interval, GSourceFunc function, gpointer data)
848 /* @exception callback.timeout_add cannot be NULL */
849 g_return_val_if_fail(callback.timeout_add, 0);
850 g_return_val_if_fail(function != NULL, TFALSE);
852 long key = 0;
854 hash_record *record = (hash_record *)malloc(sizeof(hash_record *));
856 record->type = TIMEOUT_ADD;
857 record->function = function;
858 record->data = data;
860 g_hash_table_insert(callback_funcs, record, NULL);
862 key = PTR_TO_LONG(record);
863 g_return_val_if_fail(key != 0, 0);
865 dSP;
867 ENTER;
868 SAVETMPS;
871 SV *pc = NULL;
872 CV *temp_cv = NULL;
873 SV *svkey = NULL;
875 svkey = newSViv(key);
876 temp_cv = get_cv(trigger_timeout_func_name, FALSE);
878 if (temp_cv)
880 pc = newRV((SV*)temp_cv);
882 /* Ok, this is hairy, first we push the vars we want to send to the
883 * timeout_add Perl callback. Next we push the var we want to give
884 * back to ourselves via trigger_input_func.
887 PUSHMARK(sp);
889 XPUSHs(sv_2mortal(newSViv(interval)));
890 XPUSHs(sv_2mortal(pc));
891 XPUSHs(sv_2mortal(svkey));
893 PUTBACK;
895 call_sv(callback.timeout_add, G_EVAL | G_SCALAR);
897 SPAGAIN;
899 /* Tweak our internal key to an external key */
900 key = POPl;
902 else
904 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
907 SPAGAIN;
909 FREETMPS;
910 PUTBACK;
912 LEAVE;
914 return key;
918 * @brief Send a @p message out via callback.incoming_msg
919 * @param jid user the @p message is being sent to
920 * @param sender user name of the @p message sender
921 * @param alias alternate user name of the @p sender (may be NULL)
922 * @param message string being sent to @p jid
923 * @return 1 on success, 0 on failure
925 int thrasher_wrapper_incoming_msg (const char *jid,
926 const char *sender,
927 const char *alias,
928 const char *message,
929 PurpleMessageFlags flags)
931 int ret = 0;
933 /* @exception jid, sender, and message cannot be NULL */
934 g_return_val_if_fail(jid != NULL, 0);
935 g_return_val_if_fail(sender != NULL, 0);
936 g_return_val_if_fail(message != NULL, 0);
938 /* @exception bail if our callback doesn't exist */
939 g_return_val_if_fail(callback.incoming_msg != NULL, 0);
941 dSP;
943 ENTER;
944 SAVETMPS;
946 PUSHMARK(sp);
948 /* Push the args onto the stack */
949 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
950 XPUSHs(sv_2mortal(newSVpvn(sender, strlen(sender))));
951 XPUSHs(sv_2mortal(newSVpvn(alias, strlen(alias))));
952 XPUSHs(sv_2mortal(newSVpvn(message, strlen(message))));
953 XPUSHs(sv_2mortal(newSViv(flags)));
955 PUTBACK;
957 call_sv(callback.incoming_msg, G_EVAL | G_SCALAR);
959 SPAGAIN;
961 ret = POPl;
963 FREETMPS;
964 PUTBACK;
966 LEAVE;
968 return ret;
972 * @brief Forwards the 'connection' event, that authentication has
973 * completed
974 * @param jid user who has successfully connected
976 int thrasher_wrapper_connection(const char *jid)
978 int ret = 0;
980 /* @exception jid, sender, and message cannot be NULL */
981 g_return_val_if_fail(jid != NULL, 0);
983 /* @exception bail if our callback doesn't exist */
984 g_return_val_if_fail(callback.connection != NULL, 0);
986 dSP;
988 ENTER;
989 SAVETMPS;
991 PUSHMARK(sp);
993 /* Push the args onto the stack */
994 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
996 PUTBACK;
998 call_sv(callback.connection, G_EVAL | G_SCALAR);
1000 SPAGAIN;
1002 ret = POPl;
1004 FREETMPS;
1005 PUTBACK;
1007 LEAVE;
1009 return ret;
1012 SV *clean_newSVpv(const char *inp)
1014 if (inp)
1015 return sv_2mortal(newSVpv(inp, 0));
1016 else
1017 return sv_2mortal(&PL_sv_undef);
1022 * @brief Forward presence information via callback.presence_in
1023 * @param jid user the @p status is being sent to
1024 * @param sender user name of the @p status sender
1025 * @param alias alternate user name of the @p sender (may be NULL)
1026 * @param group which the @p sender belongs to
1027 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1028 * @param message string being sent to @p jid (may be NULL)
1029 * @return 1 on success, 0 on failure (wrapped Perl)
1031 int thrasher_wrapper_presence_in(const char *jid,
1032 const char *sender,
1033 const char *alias,
1034 const char *group,
1035 const guint status,
1036 const char *message)
1038 int ret = 0;
1040 /* @exception jid, sender, and group cannot be NULL */
1041 g_return_val_if_fail(jid != NULL, 0);
1042 g_return_val_if_fail(sender != NULL, 0);
1043 g_return_val_if_fail(group != NULL, 0);
1045 /* @exception bail if our callback doesn't exist */
1046 g_return_val_if_fail(callback.presence_in != NULL, 0);
1048 dSP;
1050 ENTER;
1051 SAVETMPS;
1053 PUSHMARK(sp);
1055 /* Push the args onto the stack */
1056 XPUSHs(clean_newSVpv(jid));
1057 XPUSHs(clean_newSVpv(sender));
1058 XPUSHs(clean_newSVpv(alias));
1059 XPUSHs(clean_newSVpv(group));
1060 XPUSHs(sv_2mortal(newSViv(status)));
1061 XPUSHs(clean_newSVpv(message));
1063 PUTBACK;
1065 call_sv(callback.presence_in, G_EVAL | G_SCALAR);
1067 SPAGAIN;
1069 ret = POPl;
1071 FREETMPS;
1072 PUTBACK;
1074 LEAVE;
1076 return ret;
1079 int thrasher_wrapper_legacy_user_add_user(const char *jid,
1080 const char *sender) {
1081 int ret = 0;
1083 g_return_val_if_fail(jid, 0);
1084 g_return_val_if_fail(sender, 0);
1086 g_return_val_if_fail(callback.legacy_user_add_user, 0);
1088 dSP;
1090 ENTER;
1091 SAVETMPS;
1093 PUSHMARK(sp);
1095 XPUSHs(clean_newSVpv(jid));
1096 XPUSHs(clean_newSVpv(sender));
1098 PUTBACK;
1100 call_sv(callback.legacy_user_add_user, G_EVAL | G_SCALAR);
1102 SPAGAIN;
1104 ret = POPl;
1106 FREETMPS;
1107 PUTBACK;
1109 LEAVE;
1111 return ret;
1115 * @brief Forward subscription request information via callback.subscription_add
1116 * @param jid user the @p status is being sent to
1117 * @param sender user name of the requester
1118 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1119 * @return 1 on success, 0 on failure (wrapped Perl)
1121 int thrasher_wrapper_subscription_add(const char *jid,
1122 const char *sender,
1123 guint status)
1125 int ret = 0;
1127 /* @exception jid, sender, and group cannot be NULL */
1128 g_return_val_if_fail(jid != NULL, 0);
1129 g_return_val_if_fail(sender != NULL, 0);
1131 /* @exception bail if our callback doesn't exist */
1132 g_return_val_if_fail(callback.subscription_add != NULL, 0);
1134 dSP;
1136 ENTER;
1137 SAVETMPS;
1139 PUSHMARK(sp);
1141 /* Push the args onto the stack */
1142 XPUSHs(clean_newSVpv(jid));
1143 XPUSHs(clean_newSVpv(sender));
1144 XPUSHs(sv_2mortal(newSViv(status)));
1146 PUTBACK;
1148 call_sv(callback.subscription_add, G_EVAL | G_SCALAR);
1150 SPAGAIN;
1152 ret = POPl;
1154 FREETMPS;
1155 PUTBACK;
1157 LEAVE;
1159 return ret;
1163 * @brief Forward connection error messages via callback.connection_error
1164 * @param jid user the @p error_code is being sent to
1165 * @param error_code PurpleConnectionError (guint) being sent to @p jid
1166 * @param message a error message (may be NULL)
1167 * @return 1 on success, 0 on failure (wrapped Perl)
1169 int thrasher_wrapper_connection_error(const char *jid,
1170 const guint error_code,
1171 const char *message)
1173 int ret = 0;
1175 // If the JID is null, pick it up off of
1176 // thrasher.c:current_login_jid, per the comment in that file.
1177 if (jid == NULL) {
1178 purple_debug_info("thrasher", "had to retrieve the jid from mem\n");
1179 jid = get_current_login_jid();
1182 // If we're in the middle of a connection, pick up that we have an error.
1183 set_got_error(1);
1185 g_return_val_if_fail(jid, 0);
1188 /* @exception error_code cannot be less than zero or greater than PURPLE_CONNECTION_ERROR_OTHER_ERROR */
1189 g_return_val_if_fail(error_code >= 0, ret);
1190 g_return_val_if_fail(error_code <= PURPLE_CONNECTION_ERROR_OTHER_ERROR, ret);
1192 dSP;
1194 ENTER;
1195 SAVETMPS;
1197 PUSHMARK(sp);
1199 /* Push the args onto the stack */
1200 XPUSHs(clean_newSVpv(jid));
1201 XPUSHs(sv_2mortal(newSViv(error_code)));
1202 XPUSHs(clean_newSVpv(message));
1204 PUTBACK;
1206 call_sv(callback.connection_error, G_EVAL | G_SCALAR);
1208 SPAGAIN;
1210 ret = POPl;
1212 FREETMPS;
1213 PUTBACK;
1215 LEAVE;
1217 // Remove the account from our current active set
1218 thrasher_remove_account(jid);
1220 return ret;
1223 void thrasher_remove_account (const char *jid)
1225 PurpleAccount *account;
1227 g_return_if_fail(jid != NULL);
1229 purple_debug_info("thrasher", "Removing active account for %s\n",
1230 jid);
1232 account = g_hash_table_lookup(accts, jid);
1233 if (account != NULL)
1235 g_hash_table_remove(accts, jid);
1240 * @brief Forwards typing notification state changes from libpurple buddies.
1241 * @param pa Account receiving the notification
1242 * @param who Buddy name sending the notification
1243 * @param state
1245 void thrasher_wrapper_incoming_chatstate(PurpleAccount* pa,
1246 const char* who,
1247 PurpleTypingState state) {
1248 /* @exception pa and who cannot be NULL */
1249 g_return_if_fail(pa != NULL);
1250 g_return_if_fail(who != NULL);
1252 /* @exception bail if our callback doesn't exist */
1253 g_return_if_fail(callback.incoming_chatstate != NULL);
1255 gchar* jid = thrasher_account_get_jid(pa);
1256 /* @exception bail if account has no JID */
1257 g_return_if_fail(jid != NULL);
1259 dSP;
1261 ENTER;
1262 SAVETMPS;
1264 PUSHMARK(sp);
1266 /* Push the args onto the stack */
1267 XPUSHs(clean_newSVpv(jid));
1268 XPUSHs(clean_newSVpv(who));
1269 XPUSHs(sv_2mortal(newSViv(state)));
1271 PUTBACK;
1273 call_sv(callback.incoming_chatstate, G_EVAL | G_SCALAR);
1275 SPAGAIN;
1277 FREETMPS;
1278 PUTBACK;
1280 LEAVE;
1284 * @begin External trigger to flush the internal function tables
1286 void thrasher_wrapper_destructor ()
1288 g_hash_table_destroy(callback_funcs);
1289 g_hash_table_destroy(perl_key_lookup);
1292 size_t
1293 thrasher_wrapper_send_file(const char *jid,
1294 const char *who,
1295 char *filename,
1296 guint size,
1297 char *desc) {
1298 g_return_val_if_fail(jid, 0);
1299 g_return_val_if_fail(who, 0);
1301 purple_debug_info("thrasher ft",
1302 "Sending file %s -> %s\n",
1303 jid, who);
1305 PurpleAccount *account = g_hash_table_lookup(accts, jid);
1307 g_return_val_if_fail(account, 0);
1309 return thrasher_send_file(account, who, filename, size, desc);
1312 void
1313 thrasher_action_ft_ui_ready(size_t id) {
1314 PurpleXfer* xfer = get_xfer_by_id(id);
1315 g_return_if_fail(xfer);
1316 purple_xfer_ui_ready(xfer);
1317 /* HACK: READY_PRPL is usually not set (or was cleared) right now,
1318 * and some prpls don't appear to set it correctly on repeats.
1319 * Spam prpl_ready to force a do_transfer anyway.
1321 * Using a separate watch on the remote xfer->fd would avoid some
1322 * EAGAINs. I'd be happy to be proven wrong, but testing found no
1323 * significant benefit to Thrasher in that.
1325 purple_xfer_prpl_ready(xfer);
1329 * @brief Called on libpurple file-send-start signal
1330 * @param id of Thrasher file transfer structure.
1333 thrasher_wrapper_ft_send_start(guint id) {
1334 int ret = 0;
1336 /* @exception bail if our callback doesn't exist */
1337 g_return_val_if_fail(ft_callback.ft_send_start_cb != NULL, 0);
1339 dSP;
1341 ENTER;
1342 SAVETMPS;
1344 PUSHMARK(sp);
1346 /* Push the args onto the stack */
1347 XPUSHs(sv_2mortal(newSViv(id)));
1349 PUTBACK;
1351 call_sv(ft_callback.ft_send_start_cb, G_EVAL | G_SCALAR);
1353 SPAGAIN;
1355 ret = POPl;
1357 FREETMPS;
1358 PUTBACK;
1360 LEAVE;
1362 return ret;
1366 * @brief Called on libpurple file-send-cancel signal
1367 * @param id of Thrasher file transfer structure.
1369 void
1370 thrasher_wrapper_ft_send_cancel(guint id) {
1371 /* @exception bail if our callback doesn't exist */
1372 g_return_if_fail(ft_callback.ft_send_cancel_cb != NULL);
1374 dSP;
1376 ENTER;
1377 SAVETMPS;
1379 PUSHMARK(sp);
1381 /* Push the args onto the stack */
1382 XPUSHs(sv_2mortal(newSViv(id)));
1384 PUTBACK;
1386 call_sv(ft_callback.ft_send_cancel_cb, G_EVAL | G_SCALAR);
1388 SPAGAIN;
1390 FREETMPS;
1391 PUTBACK;
1393 LEAVE;
1395 return;
1399 * @brief Called on libpurple file-send-complete signal
1400 * @param id of Thrasher file transfer structure.
1402 void
1403 thrasher_wrapper_ft_send_complete(guint id) {
1404 /* @exception bail if our callback doesn't exist */
1405 g_return_if_fail(ft_callback.ft_send_complete_cb != NULL);
1407 dSP;
1409 ENTER;
1410 SAVETMPS;
1412 PUSHMARK(sp);
1414 /* Push the args onto the stack */
1415 XPUSHs(sv_2mortal(newSViv(id)));
1417 PUTBACK;
1419 call_sv(ft_callback.ft_send_complete_cb, G_EVAL | G_SCALAR);
1421 SPAGAIN;
1423 FREETMPS;
1424 PUTBACK;
1426 LEAVE;
1428 return;
1431 gssize
1432 thrasher_wrapper_ft_read(guint id,
1433 guchar **buffer,
1434 gssize size) {
1435 int read_sz = -1;
1436 int count = 0;
1438 /* @exception bail if our callback doesn't exist */
1439 g_return_val_if_fail(ft_callback.ft_read_cb != NULL, 0);
1441 dSP;
1443 ENTER;
1444 SAVETMPS;
1446 PUSHMARK(sp);
1448 /* Push the args onto the stack */
1449 XPUSHs(sv_2mortal(newSViv(id)));
1450 XPUSHs(sv_2mortal(newSViv(size)));
1452 PUTBACK;
1454 count = call_sv(ft_callback.ft_read_cb, G_EVAL | G_ARRAY);
1456 SPAGAIN;
1458 if (count == 2) {
1459 read_sz = POPl;
1460 void* read = POPpbytex;
1461 *buffer = malloc(read_sz);
1462 /* This buffer is free'd by libpurple. */
1463 memcpy(*buffer, read, read_sz);
1465 else {
1466 *buffer = NULL;
1467 read_sz = -1;
1470 FREETMPS;
1471 PUTBACK;
1473 LEAVE;
1475 return read_sz;
1478 void
1479 thrasher_wrapper_ft_data_not_sent(guint id,
1480 const guchar *buffer,
1481 gsize size) {
1482 /* @exception buffer cannot be NULL */
1483 g_return_if_fail(buffer != NULL);
1485 /* @exception bail if our callback doesn't exist */
1486 g_return_if_fail(ft_callback.ft_data_not_sent_cb != NULL);
1488 dSP;
1490 ENTER;
1491 SAVETMPS;
1493 PUSHMARK(sp);
1495 /* Push the args onto the stack */
1496 XPUSHs(sv_2mortal(newSViv(id)));
1497 /* buffer is not null-terminated. */
1498 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1500 PUTBACK;
1502 call_sv(ft_callback.ft_data_not_sent_cb, G_EVAL | G_SCALAR);
1504 SPAGAIN;
1506 FREETMPS;
1507 PUTBACK;
1509 LEAVE;
1511 return;
1515 * @brief ask user's permission for ft and set up bytestream
1516 * @param xfer a filled-in PURPLE_XFER_RECEIVE PurpleXfer.
1517 * @param filename suggested local filename
1518 * @return nothing; use thrasher_action_ft_recv_request_respond when ready
1520 void
1521 thrasher_wrapper_ft_recv_request(PurpleXfer *xfer,
1522 const char* filename) {
1523 /* @exception xfer cannot be NULL */
1524 g_return_if_fail(xfer != NULL);
1526 /* @exception bail if our callback doesn't exist */
1527 g_return_if_fail(ft_callback.ft_recv_request_cb != NULL);
1529 dSP;
1531 ENTER;
1532 SAVETMPS;
1534 PUSHMARK(sp);
1536 /* Push the args onto the stack */
1537 guint id = get_id_by_xfer(xfer);
1538 XPUSHs(sv_2mortal(newSViv(id)));
1539 PurpleAccount* pa = purple_xfer_get_account(xfer);
1540 gchar* jid = thrasher_account_get_jid(pa);
1541 XPUSHs(clean_newSVpv(jid));
1542 const char* who = purple_xfer_get_remote_user(xfer);
1543 XPUSHs(clean_newSVpv(who));
1544 XPUSHs(clean_newSVpv(filename));
1545 size_t size = purple_xfer_get_size(xfer);
1546 XPUSHs(sv_2mortal(newSViv(size)));
1548 PUTBACK;
1550 call_sv(ft_callback.ft_recv_request_cb, G_EVAL | G_SCALAR);
1552 SPAGAIN;
1554 FREETMPS;
1555 PUTBACK;
1557 LEAVE;
1559 return;
1562 void
1563 thrasher_action_ft_recv_request_respond(size_t id,
1564 unsigned int accept) {
1565 thrasher_xfer_recv_request_responder(id, accept);
1568 gssize
1569 thrasher_wrapper_ft_write(guint id,
1570 const guchar *buffer,
1571 gssize size) {
1572 int written_sz = -1;
1574 /* @exception bail if our callback doesn't exist */
1575 g_return_val_if_fail(ft_callback.ft_write_cb != NULL, 0);
1577 dSP;
1579 ENTER;
1580 SAVETMPS;
1582 PUSHMARK(sp);
1584 /* Push the args onto the stack */
1585 XPUSHs(sv_2mortal(newSViv(id)));
1586 /* buffer is not null-terminated. */
1587 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1589 PUTBACK;
1591 call_sv(ft_callback.ft_write_cb, G_EVAL | G_ARRAY);
1593 SPAGAIN;
1595 written_sz = POPl;
1597 FREETMPS;
1598 PUTBACK;
1600 LEAVE;
1602 return written_sz;
1606 * @brief Called on libpurple file-recv-cancel signal
1607 * @param id of Thrasher file transfer structure.
1609 void
1610 thrasher_wrapper_ft_recv_cancel(guint id) {
1611 /* @exception bail if our callback doesn't exist */
1612 g_return_if_fail(ft_callback.ft_recv_cancel_cb != NULL);
1614 dSP;
1616 ENTER;
1617 SAVETMPS;
1619 PUSHMARK(sp);
1621 /* Push the args onto the stack */
1622 XPUSHs(sv_2mortal(newSViv(id)));
1624 PUTBACK;
1626 call_sv(ft_callback.ft_recv_cancel_cb, G_EVAL | G_SCALAR);
1628 SPAGAIN;
1630 FREETMPS;
1631 PUTBACK;
1633 LEAVE;
1635 return;
1639 * @brief Called on libpurple file-recv-complete signal
1640 * @param id of Thrasher file transfer structure.
1642 void
1643 thrasher_wrapper_ft_recv_complete(guint id) {
1644 /* @exception bail if our callback doesn't exist */
1645 g_return_if_fail(ft_callback.ft_recv_complete_cb != NULL);
1647 dSP;
1649 ENTER;
1650 SAVETMPS;
1652 PUSHMARK(sp);
1654 /* Push the args onto the stack */
1655 XPUSHs(sv_2mortal(newSViv(id)));
1657 PUTBACK;
1659 call_sv(ft_callback.ft_recv_complete_cb, G_EVAL | G_SCALAR);
1661 SPAGAIN;
1663 FREETMPS;
1664 PUTBACK;
1666 LEAVE;
1668 return;
1671 void
1672 thrasher_action_ft_cancel_local(size_t id) {
1673 PurpleXfer* xfer = get_xfer_by_id(id);
1674 purple_xfer_cancel_local(xfer);
1677 #ifdef TH_DEBUG
1678 /*********************************************************
1680 * Only debugging stuff beyond this point!
1682 *********************************************************/
1683 gboolean foo_callback (void *data);
1684 gboolean foo_callback2 (void *data);
1685 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond);
1686 long thrasher_wrapper_trigger_timeout_add (void);
1687 long thrasher_wrapper_trigger_timeout_add2 (void);
1688 long thrasher_wrapper_trigger_timeout_add3 (void);
1689 long thrasher_wrapper_trigger_input_add (void);
1690 void thrasher_wrapper_dump_calls (void);
1691 void spit_func_info (gpointer key, gpointer value, gpointer user_data);
1694 gboolean
1695 foo_callback (void *data)
1697 printf("!!!!!!Calling a callback w/ data [%s]!!!!\n", (char *)data);
1698 return TRUE;
1702 int cntr = 9;
1705 gboolean
1706 foo_callback2 (void *data)
1708 printf("will die in [%d] cycles\n", cntr);
1710 if (cntr--)
1711 return TRUE;
1712 else
1713 return FALSE;
1716 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond)
1718 printf("data [%p]\tfd [%d]\tcond [%d]\n", data, fd, cond);
1721 /* Test functions to allow an external caller to trigger internal actions */
1722 long thrasher_wrapper_trigger_timeout_add ()
1724 printf("trigger_timeout_add called\n");
1725 return thrasher_wrapper_set_timeout_add(1234, foo_callback, "YAY! test data");
1729 long thrasher_wrapper_trigger_timeout_add2 ()
1731 printf("trigger_timeout_add2 called\n");
1732 return thrasher_wrapper_set_timeout_add(1867, foo_callback, "test data says what?");
1735 long thrasher_wrapper_trigger_timeout_add3 ()
1737 printf("trigger_timeout_add3 called\n");
1738 return thrasher_wrapper_set_timeout_add(200, foo_callback2, "this shoudl fail shortly");
1741 long thrasher_wrapper_trigger_input_add ()
1743 printf("trigger_input_add called\n");
1745 int fd = open("/tmp/testfoo", O_RDWR);
1746 PurpleInputCondition cond;
1747 cond = PURPLE_INPUT_WRITE;
1748 long face = 0;
1749 printf("HAMMERTIME\n");
1750 face = thrasher_wrapper_set_input_add(fd, cond, foo_callback3, "double plus win ungood");
1752 return face;
1755 int thrasher_wrapper_trigger_timeout_remove (long key)
1757 printf("trigger_timeout_remove called\n");
1758 /* We currently have no way to correlate external keys to internal keys... */
1759 return !thrasher_wrapper_call_source_remove(key);
1763 void spit_func_info (gpointer key, gpointer value, gpointer user_data)
1765 hash_record *record;
1766 printf("key [%p]\tvalue [%p]\n", key, value);
1768 record = key;
1770 if (record->type == INPUT_ADD)
1772 printf("\tINPUT_ADD\n\tfunction [%p]\n\tdata [%p]\n\tcond [%d]\n\tfd [%d]\n", record->function, record->data, record->cond, record->fd);
1775 else if (record->type == TIMEOUT_ADD)
1778 printf("\tTIMEOUT_ADD\n\tfunction [%p]\n\tdata [%p]\n", record->function, record->data);
1781 else
1783 printf("record type [%d] is unrecognized!\n", record->type);
1787 void thrasher_wrapper_dump_calls ()
1789 g_hash_table_foreach(callback_funcs, spit_func_info, NULL);
1792 #endif /* TH_DEBUG */