Translate incoming typing notifications from libpurple into chatstates.
[thrasher.git] / thperl.c
blob1fa198ebcbc815fbec1cfce3dba84179f00ac0c3
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 "thft.h"
29 #include "unistd.h"
31 /* Perl */
32 #include "EXTERN.h"
33 #include "perl.h"
34 #include "XSUB.h"
36 /* Internal prototypes */
37 int func_call(gpointer func);
38 void destroy_record_key (gpointer key);
39 void validate_callback (SV *cb);
40 void thrasher_wrapper_destructor (void);
41 SV *clean_newSVpv(const char *inp);
42 void thrasher_remove_account(const char *jid);
44 perl_callbacks callback;
45 perl_ft_callbacks ft_callback;
48 char *trigger_timeout_func_name = "THPPW::thrasher_wrapper_trigger_timeout_func";
49 char *trigger_input_func_name = "THPPW::thrasher_wrapper_trigger_input_func";
52 GHashTable *accts = NULL;
53 GHashTable *callback_funcs = NULL;
54 GHashTable *perl_key_lookup = NULL;
56 void thrasher_purple_debug (int tf)
58 /* Flip on libpurple debugging info */
59 purple_debug_set_enabled(tf);
63 void thrasher_perl_init()
65 accts = g_hash_table_new(g_str_hash, g_str_equal);
68 /**
69 * @brief create a PurpleAccount and start connecting it.
70 * @param hash_args of arguments for thrasher_login().
71 * @return 0 for success. 1 if thrasher_login() fails. 2 if the PurpleAccount already exists.
73 int thrasher_action_login (GHashTable *hash_args)
75 int err = 0;
77 #ifdef TH_DEBUG
78 purple_debug_info("thrasher",
79 "Login request:\n");
80 GList *keys = g_hash_table_get_keys(hash_args);
81 GList *values = g_hash_table_get_values(hash_args);
82 while (keys)
84 purple_debug_info("thrasher",
85 " %s -> %s\n",
86 (char *)keys->data,
87 (char *)values->data);
88 keys = keys->next;
89 values = values->next;
92 g_list_free(keys);
93 g_list_free(values);
94 #endif /* TH_DEBUG */
96 // some parameters we always have
97 char *jid = (char *)g_strdup(g_hash_table_lookup(hash_args, "jid"));
98 char *username = (char *)g_strdup(g_hash_table_lookup(hash_args, "username"));
99 char *password = (char *)g_strdup(g_hash_table_lookup(hash_args, "password"));
100 char *proto = (char *)g_strdup(g_hash_table_lookup(hash_args, "proto"));
102 g_return_val_if_fail(jid, 0);
103 g_return_val_if_fail(username, 0);
104 g_return_val_if_fail(password, 0);
105 g_return_val_if_fail(proto, 0);
107 g_hash_table_remove(hash_args, "jid");
108 g_hash_table_remove(hash_args, "username");
109 g_hash_table_remove(hash_args, "password");
110 g_hash_table_remove(hash_args, "proto");
112 /* Verify user is not already logged in */
113 if (g_hash_table_lookup(accts, jid) == NULL)
115 PurpleAccount *account = thrasher_login(proto, username, password,
116 jid, hash_args);
118 // In the event of an error, connection_error has already been
119 // called.
120 if (account == NULL) {
121 return 1;
124 char *new_jid = g_strdup(jid);
126 /* Add user to hash */
127 g_hash_table_insert(
128 accts,
129 new_jid, /* Should eventually be the protocol + username */
130 account
133 /* Success! */
134 err = 0;
136 else
138 // At least indicate something bad has happened.
139 purple_debug_info("thrasher",
140 "login request for [%s] fail because they are apparently already logged in\n",
141 jid);
142 err = 2;
145 g_free(jid);
146 g_free(username);
147 g_free(password);
148 g_free(proto);
149 g_hash_table_destroy(hash_args);
151 return err;
157 * @brief add buddy (@p buddy_name) for given @p jid buddy list
158 * @param jid releational key to bound to account
159 * @param buddy_name name of buddy
160 * @return 1 if there is a problem, 0 otherwise (C)
162 int thrasher_action_buddy_add (char *jid, char *buddy_name)
164 PurpleAccount *account = NULL;
165 PurpleBuddy *buddy = NULL;
166 PurpleGroup *group = NULL;
168 purple_debug_info("thrasher", "Buddy_add request\n");
170 account = g_hash_table_lookup(accts, jid);
171 g_return_val_if_fail(account != NULL, 1);
173 buddy = purple_find_buddy(account, buddy_name);
175 // FIXME: Figure out if we need this buddy list stuff
176 // at all. We don't actually care, as long as we get
177 // the messages and presence to send out.
179 /* We're done if buddy already exists */
180 if (buddy == NULL)
182 char *default_group = "General";
184 /* Check to see if default group exists */
185 group = purple_find_group(default_group);
187 /* If not, create default group */
188 if (group == NULL)
189 group = purple_group_new(default_group);
191 purple_blist_add_group(group, NULL);
193 /* Add buddy to default group (NULL should be alias...) */
194 buddy = purple_buddy_new(account, buddy_name, NULL);
195 purple_blist_add_buddy(buddy, NULL, group, NULL);
196 purple_account_add_buddy(account, buddy);
198 purple_debug_info("thrasher",
199 "finished adding %s to %s's buddy list\n",
200 buddy_name, jid);
202 return 0;
203 } else {
204 purple_debug_info("thrasher",
205 "%s already on %s's buddy list\n",
206 buddy_name, jid);
209 /* Buddy already exists */
210 return 1;
214 * @brief remove buddy (@p buddy_name) from given @p jid buddy list
215 * @param jid releational key to bound to account
216 * @param buddy_name name of buddy
217 * @return 1 if there is a problem, 0 otherwise (C)
219 int thrasher_action_buddy_remove (char *jid, char *buddy_name)
221 PurpleGroup *group = NULL;
222 PurpleBuddy *buddy = NULL;
223 PurpleAccount *account = NULL;
225 purple_debug_info("thrasher", "Buddy remove\n");
227 account = g_hash_table_lookup(accts, jid);
228 g_return_val_if_fail(account != NULL, 1);
230 // Don't try and remove a buddy if the account
231 // isn't connected
232 if (purple_account_is_connected(account)) {
234 purple_debug_info("thrasher", "find_buddy...\n");
235 buddy = purple_find_buddy(account, buddy_name);
237 /* Only remove a buddy if they exist */
238 if (buddy != NULL)
240 purple_debug_info("thrasher", "get_group...\n");
241 /* Give up us the group */
242 group = purple_buddy_get_group(buddy);
244 purple_debug_info("thrasher", "account_remove_buddy...\n");
245 /* Remove server-side buddy entry */
246 purple_account_remove_buddy(account, buddy, group);
248 purple_debug_info("thrasher", "blist_remove_buddy...\n");
249 /* Also need to remove buddy entry from memory */
250 purple_blist_remove_buddy(buddy);
252 return 0;
256 /* Account isn't connected or buddy is not found */
257 return 1;
260 void thrasher_action_buddy_authorize(char *jid, char *legacy_username)
262 thrasher_action_buddy_add(jid, legacy_username);
265 void thrasher_action_buddy_deauthorize(char *jid, char *legacy_username) {
266 thrasher_action_buddy_remove(jid, legacy_username);
270 * @brief update presence status for given @p jid
271 * @param jid releational key to bound to account
272 * @param status integer representation of PurpleStatusPrimitive
273 * @param message a status message (may be NULL)
274 * @return 1 if there is a problem, 0 otherwise (C)
276 int thrasher_action_presence (char *jid, int status, char *message)
278 PurpleAccount *account;
280 g_return_val_if_fail(jid != NULL, 0);
282 account = g_hash_table_lookup(accts, jid);
284 if (account == NULL)
285 return 1;
287 thrasher_set_presence(account, status, message);
289 return 0;
294 * @brief Send @p message from the user given by @p jid to the user @p recipient
295 * @param jid
296 * @param recipient
297 * @param message
298 * @return 1 on success 0 on failure
300 int thrasher_action_outgoing_msg (char *jid, char *recipient, char *message)
302 int ret = 0;
303 PurpleAccount *account = NULL;
305 g_return_val_if_fail(jid != NULL, 0);
306 g_return_val_if_fail(recipient != NULL, 0);
307 g_return_val_if_fail(message != NULL, 0);
309 purple_debug_info("thrasher", "Send message request\n");
310 /* Pull in user if they exist */
311 account = g_hash_table_lookup(accts, jid);
313 /* User does not exist (may not be logged in) */
314 if (account == NULL) {
315 return 0;
318 /* Try to send message (message might not be sent) */
319 if (thrasher_send_msg(account,recipient,message) == TRUE)
320 ret = 1;
322 return ret;
327 * @brief Log the user out with the given jid
328 * @param jid releational key to bind to account
329 * @return 1 if logout was successful, 0 if it was not
331 * NOTE: As we're going to be running a process for each protocol,
332 * there's no need to worry about the protocol used.
334 int thrasher_action_logout (char *jid)
336 int ret = 0;
337 PurpleAccount *account;
339 g_return_val_if_fail(jid != NULL, 0);
341 purple_debug_info("thrasher", "Logout requested for %s\n", jid);
343 /* Verify user is logged in */
344 account =g_hash_table_lookup(accts, jid);
345 if (account != NULL)
347 thrasher_logout(account);
348 thrasher_remove_account(jid);
349 ret = 1;
352 return ret;
356 void _thrasher_action_debug_logged_in_accts_printer(gpointer key, gpointer value, gpointer user_data);
359 * @brief Dump active account name lists to STDERR from libpurple and thperl.
361 void
362 thrasher_action_debug_logged_in() {
363 fprintf(stderr, "purple_accounts_get_all_active:\n");
364 GList* list = purple_accounts_get_all_active();
365 if (list) {
366 GList *l = list;
367 while (l) {
368 PurpleAccount *pa = (PurpleAccount*)l->data;
369 fprintf(stderr, "\t%s = %p\n", purple_account_get_username(pa), pa);
370 l = l->next;
372 g_list_free(list);
375 if (accts) {
376 fprintf(stderr, "thperl accts:\n");
377 g_hash_table_foreach(accts,
378 _thrasher_action_debug_logged_in_accts_printer,
379 NULL);
381 else {
382 fprintf(stderr, "No thperl accts?!!\n");
386 void
387 _thrasher_action_debug_logged_in_accts_printer(gpointer key,
388 gpointer value,
389 gpointer user_data) {
390 const char *jid = (char*)key;
391 PurpleAccount *pa = (PurpleAccount*)value;
392 fprintf(stderr, "\t%s = %s = %p\n",
393 jid, purple_account_get_username(pa), pa);
398 * @brief Destruction function for our internal function hash
399 * @param key data element
401 void destroy_record_key (gpointer key)
403 free(key);
408 * @brief Bizarro passthrough to allow perl calling of dynamically-set C events
409 * @param key a guint value corresponding to the pointer of a callback.
410 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
412 int thrasher_wrapper_trigger_timeout_func(long key)
414 /* @exception key cannot be NULL */
415 g_return_val_if_fail(key != TNULL,TFALSE);
417 char *real_key = LONG_TO_PTR(key);
419 return func_call(real_key);
423 * @brief Bizarro passthrough (two) to allow perl calling of dynamically-set C events
424 * @param file descriptor
425 * @param condition trigger was called on
426 * @param key a long value corresponding to the pointer of a callback.
427 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
429 int thrasher_wrapper_trigger_input_func(long fd, SV *cond, long key)
431 /* @exception fd cannot be zero */
432 g_return_val_if_fail(fd != 0, 0);
433 /* @exception cond cannot be zero */
434 g_return_val_if_fail(cond != 0, 0);
435 /* @exception key cannot be zero */
436 g_return_val_if_fail(key != 0, 0);
438 // GIOCondition real_cond = cond;
439 char *real_key = LONG_TO_PTR(key);
441 /* FIXME: We need to pass cond and key to this call... */
442 return func_call(real_key);
446 * @brief Simple verifcation for callbacks
447 * @param subref to callback
450 void validate_callback (SV *cb)
452 if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV)
454 croak("Not a callback!");
455 exit(0);
461 * @brief Initialize our wrapper
462 * @param subref (SV *) to call for timeout_add
463 * @param subref (SV *) to call for input_add
464 * @param subref (SV *) to call for source_remove
465 * @param subref (SV *) to call on incoming messages
466 * @param subref (SV *) to call on incoming presence information
467 * @param subref (SV *) to call on subscription requests
468 * @param subref (SV *) to call on subscription requests from legacy users
469 * @param subref (SV *) to call on connection errors
470 * @param subref (SV *) to call when connections have succeeded
472 void thrasher_wrapper_init (SV *timeout_add_cb,
473 SV *input_add_cb,
474 SV *source_remove_cb,
475 SV *incoming_msg_cb,
476 SV *presence_in_cb,
477 SV *subscription_add_cb,
478 SV *legacy_user_add_user_cb,
479 SV *connection_error_cb,
480 SV *connection_cb,
481 SV *incoming_chatstate_cb)
484 g_return_if_fail(timeout_add_cb);
485 g_return_if_fail(input_add_cb);
486 g_return_if_fail(source_remove_cb);
487 g_return_if_fail(incoming_msg_cb);
488 g_return_if_fail(presence_in_cb);
489 g_return_if_fail(subscription_add_cb);
490 g_return_if_fail(legacy_user_add_user_cb);
491 g_return_if_fail(connection_error_cb);
492 g_return_if_fail(connection_cb);
493 g_return_if_fail(incoming_chatstate_cb);
495 /* Set the bindings for our function table */
496 callback_funcs = g_hash_table_new_full(g_direct_hash,
497 g_direct_equal,
498 destroy_record_key,
499 NULL);
501 /* Create the lookup table so we know what to do when Perl triggers a remove */
502 perl_key_lookup = g_hash_table_new(g_int_hash, g_int_equal);
504 /* Validate callback, this burns up if it fails so no
505 * need to check for errors!
507 validate_callback(timeout_add_cb);
508 validate_callback(input_add_cb);
509 validate_callback(source_remove_cb);
510 validate_callback(incoming_msg_cb);
511 validate_callback(presence_in_cb);
512 validate_callback(subscription_add_cb);
513 validate_callback(legacy_user_add_user_cb);
514 validate_callback(connection_error_cb);
515 validate_callback(connection_cb);
516 validate_callback(incoming_chatstate_cb);
518 dSP;
520 ENTER;
521 SAVETMPS;
523 PUSHMARK(SP);
524 PUTBACK;
526 callback.timeout_add = newSVsv(timeout_add_cb);
527 callback.input_add = newSVsv(input_add_cb);
528 callback.source_remove = newSVsv(source_remove_cb);
529 callback.incoming_msg = newSVsv(incoming_msg_cb);
530 callback.presence_in = newSVsv(presence_in_cb);
531 callback.subscription_add = newSVsv(subscription_add_cb);
532 callback.legacy_user_add_user = newSVsv(legacy_user_add_user_cb);
533 callback.connection_error = newSVsv(connection_error_cb);
534 callback.connection = newSVsv(connection_cb);
535 callback.incoming_chatstate = newSVsv(incoming_chatstate_cb);
537 SPAGAIN;
538 PUTBACK;
540 FREETMPS;
541 LEAVE;
545 * @brief Initialize our file transfer wrapper
546 * @param subref (SV *) to call for recv_accept
547 * @param subref (SV *) to call for recv_start
548 * @param subref (SV *) to call for recv_cancel
549 * @param subref (SV *) to call for recv_complete
550 * @param subref (SV *) to call for send_accept
551 * @param subref (SV *) to call for send_start
552 * @param subref (SV *) to call for send_cancel
553 * @param subref (SV *) to call for send_complete
554 * @param subref (SV *) to call for ui_read. Must return (buffer, size).
555 * @param subref (SV *) to call for ui_write
556 * @param subref (SV *) to call for ui_data_not_sent
558 void thrasher_wrapper_ft_init(SV *ft_recv_request_cb,
559 SV *ft_recv_accept_cb,
560 SV *ft_recv_start_cb,
561 SV *ft_recv_cancel_cb,
562 SV *ft_recv_complete_cb,
563 SV *ft_send_accept_cb,
564 SV *ft_send_start_cb,
565 SV *ft_send_cancel_cb,
566 SV *ft_send_complete_cb,
567 SV *ft_read_cb,
568 SV *ft_write_cb,
569 SV *ft_data_not_sent_cb)
571 g_return_if_fail(ft_recv_request_cb);
572 g_return_if_fail(ft_recv_accept_cb);
573 g_return_if_fail(ft_recv_start_cb);
574 g_return_if_fail(ft_recv_cancel_cb);
575 g_return_if_fail(ft_recv_complete_cb);
576 g_return_if_fail(ft_send_accept_cb);
577 g_return_if_fail(ft_send_start_cb);
578 g_return_if_fail(ft_send_cancel_cb);
579 g_return_if_fail(ft_send_complete_cb);
580 g_return_if_fail(ft_read_cb);
581 g_return_if_fail(ft_write_cb);
582 g_return_if_fail(ft_data_not_sent_cb);
584 // This assumes thrasher_wrapper_init was already called.
585 g_return_if_fail(callback_funcs);
587 validate_callback(ft_recv_request_cb);
588 validate_callback(ft_recv_accept_cb);
589 validate_callback(ft_recv_start_cb);
590 validate_callback(ft_recv_cancel_cb);
591 validate_callback(ft_recv_complete_cb);
592 validate_callback(ft_send_accept_cb);
593 validate_callback(ft_send_start_cb);
594 validate_callback(ft_send_cancel_cb);
595 validate_callback(ft_send_complete_cb);
596 validate_callback(ft_read_cb);
597 validate_callback(ft_write_cb);
598 validate_callback(ft_data_not_sent_cb);
600 dSP;
602 ENTER;
603 SAVETMPS;
605 PUSHMARK(SP);
606 PUTBACK;
608 ft_callback.ft_recv_request_cb = newSVsv(ft_recv_request_cb);
609 ft_callback.ft_recv_accept_cb = newSVsv(ft_recv_accept_cb);
610 ft_callback.ft_recv_start_cb = newSVsv(ft_recv_start_cb);
611 ft_callback.ft_recv_cancel_cb = newSVsv(ft_recv_cancel_cb);
612 ft_callback.ft_recv_complete_cb = newSVsv(ft_recv_complete_cb);
613 ft_callback.ft_send_accept_cb = newSVsv(ft_send_accept_cb);
614 ft_callback.ft_send_start_cb = newSVsv(ft_send_start_cb);
615 ft_callback.ft_send_cancel_cb = newSVsv(ft_send_cancel_cb);
616 ft_callback.ft_send_complete_cb = newSVsv(ft_send_complete_cb);
617 ft_callback.ft_read_cb = newSVsv(ft_read_cb);
618 ft_callback.ft_write_cb = newSVsv(ft_write_cb);
619 ft_callback.ft_data_not_sent_cb = newSVsv(ft_data_not_sent_cb);
621 SPAGAIN;
622 PUTBACK;
624 FREETMPS;
625 LEAVE;
631 * @brief Validate then call the given callback pointer.
632 * @param key a hash key for the local function callback table.
633 * @return boolean as int (0 || 1) on success
635 int func_call(gpointer key)
637 /* @exception key cannot be NULL */
638 g_return_val_if_fail(key != NULL,TFALSE);
640 gpointer orig_key = NULL;
641 hash_record *record = NULL;
642 int (*timeout_add_caller)(gpointer);
643 int (*input_add_caller)(gpointer, gint, PurpleInputCondition);
644 int ret = 0;
646 /* Crazy voodoo madness!
647 * We're using direct pointers as keys and a struct at the
648 * end of the pointer, so no need to get any values.
650 if (g_hash_table_lookup_extended(callback_funcs, key, &orig_key, NULL))
652 record = orig_key;
654 if (record->type == TIMEOUT_ADD)
656 timeout_add_caller = record->function;
657 ret = (timeout_add_caller)(record->data);
660 else if (record->type == INPUT_ADD)
662 input_add_caller = record->function;
663 (input_add_caller)(record->data, record->fd,
664 record->cond);
665 // these functions are void
666 ret = TRUE;
669 if (ret == FALSE)
671 /* We're never gonna be calling this again, so get rid of it */
672 g_hash_table_remove(callback_funcs, record);
675 return (ret);
678 else
680 purple_debug_info("thrasher", "Callback for [%p] does not exist!\n", key);
683 return(TFALSE);
688 * @brief Wraps Glib::Source->remove
689 * @param key to return to the Perl end
690 * @return Success of call, including the success of the callback
692 gboolean thrasher_wrapper_call_source_remove (long key)
694 long ret = FALSE;
696 /* @exception key cannot be NULL */
697 g_return_val_if_fail(callback.source_remove, 0);
699 /* @exception callback.source_remove cannot be NULL */
700 g_return_val_if_fail(key != TNULL, 0);
702 dSP;
704 ENTER;
705 SAVETMPS;
707 PUSHMARK(sp);
709 /* Push the key onto the stack */
710 XPUSHs(sv_2mortal(newSViv(key)));
712 PUTBACK;
714 call_sv(callback.source_remove, G_EVAL | G_SCALAR);
716 SPAGAIN;
718 ret = POPl;
720 FREETMPS;
721 PUTBACK;
723 LEAVE;
725 /* Remove call from lookup table */
726 g_hash_table_remove(perl_key_lookup, (gpointer)&key);
728 return ret;
733 * PurpleInputCondition is a bitwise OR of read/write events
734 * PurpleInputFunction is a callback to a guint function with the args gpointer, gint, and PurpleInoutCondition
737 * @brief Wraps Glib::IO->add_watch
738 * @param File descriptor
739 * @param Condition to trigger on
740 * @param Nested callback
741 * @param Data to pass to callback
742 * @return Hash key
744 long thrasher_wrapper_set_input_add (guint fd, PurpleInputCondition cond, PurpleInputFunction function, gpointer data)
746 g_return_val_if_fail(callback.input_add, 0);
747 g_return_val_if_fail(function != NULL, 0);
749 long key = 0;
751 hash_record *record = (hash_record *)malloc(sizeof(hash_record));
753 record->type = INPUT_ADD;
754 record->function = function;
755 record->data = data;
756 record->cond = cond;
757 record->fd = fd;
759 g_hash_table_insert(callback_funcs, record, NULL);
761 key = PTR_TO_LONG(record);
762 g_return_val_if_fail(key != 0, 0);
764 dSP;
766 ENTER;
767 SAVETMPS;
770 SV *pc = NULL;
771 CV *temp_cv = NULL;
772 SV *svkey = NULL;
775 svkey = newSViv(key);
776 temp_cv = get_cv(trigger_input_func_name, FALSE);
778 if (temp_cv)
780 pc = newRV((SV*)temp_cv);
782 /* Ok, this is hairy, first we push the vars we want to send to the
783 * timeout_add Perl callback. Next we push the var we want to give
784 * back to ourselves via trigger_input_func.
787 PUSHMARK(sp);
789 XPUSHs(sv_2mortal(newSViv(fd)));
790 XPUSHs(sv_2mortal(newSViv(cond)));
791 XPUSHs(sv_2mortal(pc));
792 XPUSHs(sv_2mortal(svkey));
794 PUTBACK;
796 call_sv(callback.input_add, G_EVAL | G_SCALAR);
797 SPAGAIN;
799 /* Tweak our internal key to an external key */
800 key = POPl;
802 else
804 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
807 g_hash_table_insert(perl_key_lookup, (gpointer)&key, record);
809 return key;
814 * @brief Wraps Glib::Timeout->add
815 * @param interval in milliseconds
816 * @param callback
817 * @param data to pass to the callback
818 * @return Key for the internal timeout_add function hash. (Should this really be returning anything?!)
820 long thrasher_wrapper_set_timeout_add (guint interval, GSourceFunc function, gpointer data)
822 /* @exception callback.timeout_add cannot be NULL */
823 g_return_val_if_fail(callback.timeout_add, 0);
824 g_return_val_if_fail(function != NULL, TFALSE);
826 long key = 0;
828 hash_record *record = (hash_record *)malloc(sizeof(hash_record *));
830 record->type = TIMEOUT_ADD;
831 record->function = function;
832 record->data = data;
834 g_hash_table_insert(callback_funcs, record, NULL);
836 key = PTR_TO_LONG(record);
837 g_return_val_if_fail(key != 0, 0);
839 dSP;
841 ENTER;
842 SAVETMPS;
845 SV *pc = NULL;
846 CV *temp_cv = NULL;
847 SV *svkey = NULL;
849 svkey = newSViv(key);
850 temp_cv = get_cv(trigger_timeout_func_name, FALSE);
852 if (temp_cv)
854 pc = newRV((SV*)temp_cv);
856 /* Ok, this is hairy, first we push the vars we want to send to the
857 * timeout_add Perl callback. Next we push the var we want to give
858 * back to ourselves via trigger_input_func.
861 PUSHMARK(sp);
863 XPUSHs(sv_2mortal(newSViv(interval)));
864 XPUSHs(sv_2mortal(pc));
865 XPUSHs(sv_2mortal(svkey));
867 PUTBACK;
869 call_sv(callback.timeout_add, G_EVAL | G_SCALAR);
871 SPAGAIN;
873 /* Tweak our internal key to an external key */
874 key = POPl;
876 else
878 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
881 SPAGAIN;
883 FREETMPS;
884 PUTBACK;
886 LEAVE;
888 return key;
892 * @brief Send a @p message out via callback.incoming_msg
893 * @param jid user the @p message is being sent to
894 * @param sender user name of the @p message sender
895 * @param alias alternate user name of the @p sender (may be NULL)
896 * @param message string being sent to @p jid
897 * @return 1 on success, 0 on failure
899 int thrasher_wrapper_incoming_msg (const char *jid,
900 const char *sender,
901 const char *alias,
902 const char *message)
904 int ret = 0;
906 /* @exception jid, sender, and message cannot be NULL */
907 g_return_val_if_fail(jid != NULL, 0);
908 g_return_val_if_fail(sender != NULL, 0);
909 g_return_val_if_fail(message != NULL, 0);
911 /* @exception bail if our callback doesn't exist */
912 g_return_val_if_fail(callback.incoming_msg != NULL, 0);
914 dSP;
916 ENTER;
917 SAVETMPS;
919 PUSHMARK(sp);
921 /* Push the args onto the stack */
922 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
923 XPUSHs(sv_2mortal(newSVpvn(sender, strlen(sender))));
924 XPUSHs(sv_2mortal(newSVpvn(alias, strlen(alias))));
925 XPUSHs(sv_2mortal(newSVpvn(message, strlen(message))));
927 PUTBACK;
929 call_sv(callback.incoming_msg, G_EVAL | G_SCALAR);
931 SPAGAIN;
933 ret = POPl;
935 FREETMPS;
936 PUTBACK;
938 LEAVE;
940 return ret;
944 * @brief Forwards the 'connection' event, that authentication has
945 * completed
946 * @param jid user who has successfully connected
948 int thrasher_wrapper_connection(const char *jid)
950 int ret = 0;
952 /* @exception jid, sender, and message cannot be NULL */
953 g_return_val_if_fail(jid != NULL, 0);
955 /* @exception bail if our callback doesn't exist */
956 g_return_val_if_fail(callback.connection != NULL, 0);
958 dSP;
960 ENTER;
961 SAVETMPS;
963 PUSHMARK(sp);
965 /* Push the args onto the stack */
966 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
968 PUTBACK;
970 call_sv(callback.connection, G_EVAL | G_SCALAR);
972 SPAGAIN;
974 ret = POPl;
976 FREETMPS;
977 PUTBACK;
979 LEAVE;
981 return ret;
984 SV *clean_newSVpv(const char *inp)
986 if (inp)
987 return sv_2mortal(newSVpv(inp, 0));
988 else
989 return sv_2mortal(&PL_sv_undef);
994 * @brief Forward presence information via callback.presence_in
995 * @param jid user the @p status is being sent to
996 * @param sender user name of the @p status sender
997 * @param alias alternate user name of the @p sender (may be NULL)
998 * @param group which the @p sender belongs to
999 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1000 * @param message string being sent to @p jid (may be NULL)
1001 * @return 1 on success, 0 on failure (wrapped Perl)
1003 int thrasher_wrapper_presence_in(const char *jid,
1004 const char *sender,
1005 const char *alias,
1006 const char *group,
1007 const guint status,
1008 const char *message)
1010 int ret = 0;
1012 /* @exception jid, sender, and group cannot be NULL */
1013 g_return_val_if_fail(jid != NULL, 0);
1014 g_return_val_if_fail(sender != NULL, 0);
1015 g_return_val_if_fail(group != NULL, 0);
1017 /* @exception bail if our callback doesn't exist */
1018 g_return_val_if_fail(callback.presence_in != NULL, 0);
1020 dSP;
1022 ENTER;
1023 SAVETMPS;
1025 PUSHMARK(sp);
1027 /* Push the args onto the stack */
1028 XPUSHs(clean_newSVpv(jid));
1029 XPUSHs(clean_newSVpv(sender));
1030 XPUSHs(clean_newSVpv(alias));
1031 XPUSHs(clean_newSVpv(group));
1032 XPUSHs(sv_2mortal(newSViv(status)));
1033 XPUSHs(clean_newSVpv(message));
1035 PUTBACK;
1037 call_sv(callback.presence_in, G_EVAL | G_SCALAR);
1039 SPAGAIN;
1041 ret = POPl;
1043 FREETMPS;
1044 PUTBACK;
1046 LEAVE;
1048 return ret;
1051 int thrasher_wrapper_legacy_user_add_user(const char *jid,
1052 const char *sender) {
1053 int ret = 0;
1055 g_return_val_if_fail(jid, 0);
1056 g_return_val_if_fail(sender, 0);
1058 g_return_val_if_fail(callback.legacy_user_add_user, 0);
1060 dSP;
1062 ENTER;
1063 SAVETMPS;
1065 PUSHMARK(sp);
1067 XPUSHs(clean_newSVpv(jid));
1068 XPUSHs(clean_newSVpv(sender));
1070 PUTBACK;
1072 call_sv(callback.legacy_user_add_user, G_EVAL | G_SCALAR);
1074 SPAGAIN;
1076 ret = POPl;
1078 FREETMPS;
1079 PUTBACK;
1081 LEAVE;
1083 return ret;
1087 * @brief Forward subscription request information via callback.subscription_add
1088 * @param jid user the @p status is being sent to
1089 * @param sender user name of the requester
1090 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1091 * @return 1 on success, 0 on failure (wrapped Perl)
1093 int thrasher_wrapper_subscription_add(const char *jid,
1094 const char *sender,
1095 guint status)
1097 int ret = 0;
1099 /* @exception jid, sender, and group cannot be NULL */
1100 g_return_val_if_fail(jid != NULL, 0);
1101 g_return_val_if_fail(sender != NULL, 0);
1103 /* @exception bail if our callback doesn't exist */
1104 g_return_val_if_fail(callback.subscription_add != NULL, 0);
1106 dSP;
1108 ENTER;
1109 SAVETMPS;
1111 PUSHMARK(sp);
1113 /* Push the args onto the stack */
1114 XPUSHs(clean_newSVpv(jid));
1115 XPUSHs(clean_newSVpv(sender));
1116 XPUSHs(sv_2mortal(newSViv(status)));
1118 PUTBACK;
1120 call_sv(callback.subscription_add, G_EVAL | G_SCALAR);
1122 SPAGAIN;
1124 ret = POPl;
1126 FREETMPS;
1127 PUTBACK;
1129 LEAVE;
1131 return ret;
1135 * @brief Forward connection error messages via callback.connection_error
1136 * @param jid user the @p error_code is being sent to
1137 * @param error_code PurpleConnectionError (guint) being sent to @p jid
1138 * @param message a error message (may be NULL)
1139 * @return 1 on success, 0 on failure (wrapped Perl)
1141 int thrasher_wrapper_connection_error(const char *jid,
1142 const guint error_code,
1143 const char *message)
1145 int ret = 0;
1147 // If the JID is null, pick it up off of
1148 // thrasher.c:current_login_jid, per the comment in that file.
1149 if (jid == NULL) {
1150 purple_debug_info("thrasher", "had to retrieve the jid from mem\n");
1151 jid = get_current_login_jid();
1154 // If we're in the middle of a connection, pick up that we have an error.
1155 set_got_error(1);
1157 g_return_val_if_fail(jid, 0);
1160 /* @exception error_code cannot be less than zero or greater than PURPLE_CONNECTION_ERROR_OTHER_ERROR */
1161 g_return_val_if_fail(error_code >= 0, ret);
1162 g_return_val_if_fail(error_code <= PURPLE_CONNECTION_ERROR_OTHER_ERROR, ret);
1164 dSP;
1166 ENTER;
1167 SAVETMPS;
1169 PUSHMARK(sp);
1171 /* Push the args onto the stack */
1172 XPUSHs(clean_newSVpv(jid));
1173 XPUSHs(sv_2mortal(newSViv(error_code)));
1174 XPUSHs(clean_newSVpv(message));
1176 PUTBACK;
1178 call_sv(callback.connection_error, G_EVAL | G_SCALAR);
1180 SPAGAIN;
1182 ret = POPl;
1184 FREETMPS;
1185 PUTBACK;
1187 LEAVE;
1189 // Remove the account from our current active set
1190 thrasher_remove_account(jid);
1192 return ret;
1195 void thrasher_remove_account (const char *jid)
1197 PurpleAccount *account;
1199 g_return_if_fail(jid != NULL);
1201 purple_debug_info("thrasher", "Removing active account for %s\n",
1202 jid);
1204 account = g_hash_table_lookup(accts, jid);
1205 if (account != NULL)
1207 g_hash_table_remove(accts, jid);
1212 * @brief Forwards typing notification state changes from libpurple buddies.
1213 * @param pa Account receiving the notification
1214 * @param who Buddy name sending the notification
1215 * @param state
1217 void thrasher_wrapper_incoming_chatstate(PurpleAccount* pa,
1218 const char* who,
1219 PurpleTypingState state) {
1220 /* @exception pa and who cannot be NULL */
1221 g_return_if_fail(pa != NULL);
1222 g_return_if_fail(who != NULL);
1224 /* @exception bail if our callback doesn't exist */
1225 g_return_if_fail(callback.incoming_chatstate != NULL);
1227 gchar* jid = thrasher_account_get_jid(pa);
1228 /* @exception bail if account has no JID */
1229 g_return_if_fail(jid != NULL);
1231 dSP;
1233 ENTER;
1234 SAVETMPS;
1236 PUSHMARK(sp);
1238 /* Push the args onto the stack */
1239 XPUSHs(clean_newSVpv(jid));
1240 XPUSHs(clean_newSVpv(who));
1241 XPUSHs(sv_2mortal(newSViv(state)));
1243 PUTBACK;
1245 call_sv(callback.incoming_chatstate, G_EVAL | G_SCALAR);
1247 SPAGAIN;
1249 FREETMPS;
1250 PUTBACK;
1252 LEAVE;
1256 * @begin External trigger to flush the internal function tables
1258 void thrasher_wrapper_destructor ()
1260 g_hash_table_destroy(callback_funcs);
1261 g_hash_table_destroy(perl_key_lookup);
1264 size_t
1265 thrasher_wrapper_send_file(const char *jid,
1266 const char *who,
1267 char *filename,
1268 guint size,
1269 char *desc) {
1270 g_return_val_if_fail(jid, 0);
1271 g_return_val_if_fail(who, 0);
1273 purple_debug_info("thrasher ft",
1274 "Sending file %s -> %s\n",
1275 jid, who);
1277 PurpleAccount *account = g_hash_table_lookup(accts, jid);
1279 g_return_val_if_fail(account, 0);
1281 return thrasher_send_file(account, who, filename, size, desc);
1284 void
1285 thrasher_action_ft_ui_ready(size_t id) {
1286 PurpleXfer* xfer = get_xfer_by_id(id);
1287 g_return_if_fail(xfer);
1288 purple_xfer_ui_ready(xfer);
1289 /* HACK: READY_PRPL is usually not set (or was cleared) right now,
1290 * and some prpls don't appear to set it correctly on repeats.
1291 * Spam prpl_ready to force a do_transfer anyway.
1293 * Using a separate watch on the remote xfer->fd would avoid some
1294 * EAGAINs. I'd be happy to be proven wrong, but testing found no
1295 * significant benefit to Thrasher in that.
1297 purple_xfer_prpl_ready(xfer);
1301 * @brief Called on libpurple file-send-start signal
1302 * @param id of Thrasher file transfer structure.
1305 thrasher_wrapper_ft_send_start(guint id) {
1306 int ret = 0;
1308 /* @exception bail if our callback doesn't exist */
1309 g_return_val_if_fail(ft_callback.ft_send_start_cb != NULL, 0);
1311 dSP;
1313 ENTER;
1314 SAVETMPS;
1316 PUSHMARK(sp);
1318 /* Push the args onto the stack */
1319 XPUSHs(sv_2mortal(newSViv(id)));
1321 PUTBACK;
1323 call_sv(ft_callback.ft_send_start_cb, G_EVAL | G_SCALAR);
1325 SPAGAIN;
1327 ret = POPl;
1329 FREETMPS;
1330 PUTBACK;
1332 LEAVE;
1334 return ret;
1338 * @brief Called on libpurple file-send-cancel signal
1339 * @param id of Thrasher file transfer structure.
1341 void
1342 thrasher_wrapper_ft_send_cancel(guint id) {
1343 /* @exception bail if our callback doesn't exist */
1344 g_return_if_fail(ft_callback.ft_send_cancel_cb != NULL);
1346 dSP;
1348 ENTER;
1349 SAVETMPS;
1351 PUSHMARK(sp);
1353 /* Push the args onto the stack */
1354 XPUSHs(sv_2mortal(newSViv(id)));
1356 PUTBACK;
1358 call_sv(ft_callback.ft_send_cancel_cb, G_EVAL | G_SCALAR);
1360 SPAGAIN;
1362 FREETMPS;
1363 PUTBACK;
1365 LEAVE;
1367 return;
1371 * @brief Called on libpurple file-send-complete signal
1372 * @param id of Thrasher file transfer structure.
1374 void
1375 thrasher_wrapper_ft_send_complete(guint id) {
1376 /* @exception bail if our callback doesn't exist */
1377 g_return_if_fail(ft_callback.ft_send_complete_cb != NULL);
1379 dSP;
1381 ENTER;
1382 SAVETMPS;
1384 PUSHMARK(sp);
1386 /* Push the args onto the stack */
1387 XPUSHs(sv_2mortal(newSViv(id)));
1389 PUTBACK;
1391 call_sv(ft_callback.ft_send_complete_cb, G_EVAL | G_SCALAR);
1393 SPAGAIN;
1395 FREETMPS;
1396 PUTBACK;
1398 LEAVE;
1400 return;
1403 gssize
1404 thrasher_wrapper_ft_read(guint id,
1405 guchar **buffer,
1406 gssize size) {
1407 int read_sz = -1;
1408 int count = 0;
1410 /* @exception bail if our callback doesn't exist */
1411 g_return_val_if_fail(ft_callback.ft_read_cb != NULL, 0);
1413 dSP;
1415 ENTER;
1416 SAVETMPS;
1418 PUSHMARK(sp);
1420 /* Push the args onto the stack */
1421 XPUSHs(sv_2mortal(newSViv(id)));
1422 XPUSHs(sv_2mortal(newSViv(size)));
1424 PUTBACK;
1426 count = call_sv(ft_callback.ft_read_cb, G_EVAL | G_ARRAY);
1428 SPAGAIN;
1430 if (count == 2) {
1431 read_sz = POPl;
1432 void* read = POPpbytex;
1433 *buffer = malloc(read_sz);
1434 /* This buffer is free'd by libpurple. */
1435 memcpy(*buffer, read, read_sz);
1437 else {
1438 *buffer = NULL;
1439 read_sz = -1;
1442 FREETMPS;
1443 PUTBACK;
1445 LEAVE;
1447 return read_sz;
1450 void
1451 thrasher_wrapper_ft_data_not_sent(guint id,
1452 const guchar *buffer,
1453 gsize size) {
1454 /* @exception buffer cannot be NULL */
1455 g_return_if_fail(buffer != NULL);
1457 /* @exception bail if our callback doesn't exist */
1458 g_return_if_fail(ft_callback.ft_data_not_sent_cb != NULL);
1460 dSP;
1462 ENTER;
1463 SAVETMPS;
1465 PUSHMARK(sp);
1467 /* Push the args onto the stack */
1468 XPUSHs(sv_2mortal(newSViv(id)));
1469 /* buffer is not null-terminated. */
1470 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1472 PUTBACK;
1474 call_sv(ft_callback.ft_data_not_sent_cb, G_EVAL | G_SCALAR);
1476 SPAGAIN;
1478 FREETMPS;
1479 PUTBACK;
1481 LEAVE;
1483 return;
1487 * @brief ask user's permission for ft and set up bytestream
1488 * @param xfer a filled-in PURPLE_XFER_RECEIVE PurpleXfer.
1489 * @param filename suggested local filename
1490 * @return nothing; use thrasher_action_ft_recv_request_respond when ready
1492 void
1493 thrasher_wrapper_ft_recv_request(PurpleXfer *xfer,
1494 const char* filename) {
1495 /* @exception xfer cannot be NULL */
1496 g_return_if_fail(xfer != NULL);
1498 /* @exception bail if our callback doesn't exist */
1499 g_return_if_fail(ft_callback.ft_recv_request_cb != NULL);
1501 dSP;
1503 ENTER;
1504 SAVETMPS;
1506 PUSHMARK(sp);
1508 /* Push the args onto the stack */
1509 guint id = get_id_by_xfer(xfer);
1510 XPUSHs(sv_2mortal(newSViv(id)));
1511 PurpleAccount* pa = purple_xfer_get_account(xfer);
1512 gchar* jid = thrasher_account_get_jid(pa);
1513 XPUSHs(clean_newSVpv(jid));
1514 const char* who = purple_xfer_get_remote_user(xfer);
1515 XPUSHs(clean_newSVpv(who));
1516 XPUSHs(clean_newSVpv(filename));
1517 size_t size = purple_xfer_get_size(xfer);
1518 XPUSHs(sv_2mortal(newSViv(size)));
1520 PUTBACK;
1522 call_sv(ft_callback.ft_recv_request_cb, G_EVAL | G_SCALAR);
1524 SPAGAIN;
1526 FREETMPS;
1527 PUTBACK;
1529 LEAVE;
1531 return;
1534 void
1535 thrasher_action_ft_recv_request_respond(size_t id,
1536 unsigned int accept) {
1537 thrasher_xfer_recv_request_responder(id, accept);
1540 gssize
1541 thrasher_wrapper_ft_write(guint id,
1542 const guchar *buffer,
1543 gssize size) {
1544 int written_sz = -1;
1546 /* @exception bail if our callback doesn't exist */
1547 g_return_val_if_fail(ft_callback.ft_write_cb != NULL, 0);
1549 dSP;
1551 ENTER;
1552 SAVETMPS;
1554 PUSHMARK(sp);
1556 /* Push the args onto the stack */
1557 XPUSHs(sv_2mortal(newSViv(id)));
1558 /* buffer is not null-terminated. */
1559 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1561 PUTBACK;
1563 call_sv(ft_callback.ft_write_cb, G_EVAL | G_ARRAY);
1565 SPAGAIN;
1567 written_sz = POPl;
1569 FREETMPS;
1570 PUTBACK;
1572 LEAVE;
1574 return written_sz;
1578 * @brief Called on libpurple file-recv-cancel signal
1579 * @param id of Thrasher file transfer structure.
1581 void
1582 thrasher_wrapper_ft_recv_cancel(guint id) {
1583 /* @exception bail if our callback doesn't exist */
1584 g_return_if_fail(ft_callback.ft_recv_cancel_cb != NULL);
1586 dSP;
1588 ENTER;
1589 SAVETMPS;
1591 PUSHMARK(sp);
1593 /* Push the args onto the stack */
1594 XPUSHs(sv_2mortal(newSViv(id)));
1596 PUTBACK;
1598 call_sv(ft_callback.ft_recv_cancel_cb, G_EVAL | G_SCALAR);
1600 SPAGAIN;
1602 FREETMPS;
1603 PUTBACK;
1605 LEAVE;
1607 return;
1611 * @brief Called on libpurple file-recv-complete signal
1612 * @param id of Thrasher file transfer structure.
1614 void
1615 thrasher_wrapper_ft_recv_complete(guint id) {
1616 /* @exception bail if our callback doesn't exist */
1617 g_return_if_fail(ft_callback.ft_recv_complete_cb != NULL);
1619 dSP;
1621 ENTER;
1622 SAVETMPS;
1624 PUSHMARK(sp);
1626 /* Push the args onto the stack */
1627 XPUSHs(sv_2mortal(newSViv(id)));
1629 PUTBACK;
1631 call_sv(ft_callback.ft_recv_complete_cb, G_EVAL | G_SCALAR);
1633 SPAGAIN;
1635 FREETMPS;
1636 PUTBACK;
1638 LEAVE;
1640 return;
1643 void
1644 thrasher_action_ft_cancel_local(size_t id) {
1645 PurpleXfer* xfer = get_xfer_by_id(id);
1646 purple_xfer_cancel_local(xfer);
1649 #ifdef TH_DEBUG
1650 /*********************************************************
1652 * Only debugging stuff beyond this point!
1654 *********************************************************/
1655 gboolean foo_callback (void *data);
1656 gboolean foo_callback2 (void *data);
1657 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond);
1658 long thrasher_wrapper_trigger_timeout_add (void);
1659 long thrasher_wrapper_trigger_timeout_add2 (void);
1660 long thrasher_wrapper_trigger_timeout_add3 (void);
1661 long thrasher_wrapper_trigger_input_add (void);
1662 void thrasher_wrapper_dump_calls (void);
1663 void spit_func_info (gpointer key, gpointer value, gpointer user_data);
1666 gboolean
1667 foo_callback (void *data)
1669 printf("!!!!!!Calling a callback w/ data [%s]!!!!\n", (char *)data);
1670 return TRUE;
1674 int cntr = 9;
1677 gboolean
1678 foo_callback2 (void *data)
1680 printf("will die in [%d] cycles\n", cntr);
1682 if (cntr--)
1683 return TRUE;
1684 else
1685 return FALSE;
1688 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond)
1690 printf("data [%p]\tfd [%d]\tcond [%d]\n", data, fd, cond);
1693 /* Test functions to allow an external caller to trigger internal actions */
1694 long thrasher_wrapper_trigger_timeout_add ()
1696 printf("trigger_timeout_add called\n");
1697 return thrasher_wrapper_set_timeout_add(1234, foo_callback, "YAY! test data");
1701 long thrasher_wrapper_trigger_timeout_add2 ()
1703 printf("trigger_timeout_add2 called\n");
1704 return thrasher_wrapper_set_timeout_add(1867, foo_callback, "test data says what?");
1707 long thrasher_wrapper_trigger_timeout_add3 ()
1709 printf("trigger_timeout_add3 called\n");
1710 return thrasher_wrapper_set_timeout_add(200, foo_callback2, "this shoudl fail shortly");
1713 long thrasher_wrapper_trigger_input_add ()
1715 printf("trigger_input_add called\n");
1717 int fd = open("/tmp/testfoo", O_RDWR);
1718 PurpleInputCondition cond;
1719 cond = PURPLE_INPUT_WRITE;
1720 long face = 0;
1721 printf("HAMMERTIME\n");
1722 face = thrasher_wrapper_set_input_add(fd, cond, foo_callback3, "double plus win ungood");
1724 return face;
1727 int thrasher_wrapper_trigger_timeout_remove (long key)
1729 printf("trigger_timeout_remove called\n");
1730 /* We currently have no way to correlate external keys to internal keys... */
1731 return !thrasher_wrapper_call_source_remove(key);
1735 void spit_func_info (gpointer key, gpointer value, gpointer user_data)
1737 hash_record *record;
1738 printf("key [%p]\tvalue [%p]\n", key, value);
1740 record = key;
1742 if (record->type == INPUT_ADD)
1744 printf("\tINPUT_ADD\n\tfunction [%p]\n\tdata [%p]\n\tcond [%d]\n\tfd [%d]\n", record->function, record->data, record->cond, record->fd);
1747 else if (record->type == TIMEOUT_ADD)
1750 printf("\tTIMEOUT_ADD\n\tfunction [%p]\n\tdata [%p]\n", record->function, record->data);
1753 else
1755 printf("record type [%d] is unrecognized!\n", record->type);
1759 void thrasher_wrapper_dump_calls ()
1761 g_hash_table_foreach(callback_funcs, spit_func_info, NULL);
1764 #endif /* TH_DEBUG */