Get the status actually active at buddy signon instead of hardcoding AVAILABLE.
[thrasher.git] / thperl.c
blob4f297d2e07af5d3147c7e3fa364de347bf67be34
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 /* Re-requesting authorization addresses cases in which a buddy is
177 * implicitly added somewhere else but never actually sent an
178 * authorization request. Such a state was sometimes possible with
179 * Yahoo but consistently reproducible with as follows in ICQ:
181 * 1. Register user1 and user2 with the ICQ transport, each on the
182 * other's roster.
183 * 2. Remove user1's ICQ contact from user2's roster.
184 * 3. Remove user2's ICQ contact from user1's roster.
185 * 4. Add user2's ICQ contact to user1's roster.
186 * 5. thrasher_buddy_request_authorize() receives and handles an
187 * authorization request from user1 on behalf of user2. It
188 * adds user1 to user2's buddy list.
189 * 6. user1 is now fully authorized with user2.
190 * 7. user2, however, had user1 added as an *un*authorized buddy.
192 * This also means Thrasher tries to do something sensible with
193 * XMPP client operations like "re-request authorization from
194 * contact". Otherwise, they'd always be handled as no-ops because
195 * the libpurple buddy is already present.
197 gboolean need_auth = ! thrasher_account_buddy_is_authorized(account, buddy);
198 /* FIXME: There's no cross-prpl calls for checking/sending
199 * authorization like oscar.c:purple_auth_request()? Could try to
200 * parse for the menu items added by each prpl we know requires
201 * authorization. :(
203 if (need_auth) {
204 purple_debug_info("thrasher",
205 "%s needs authorization from %s\n",
206 jid, buddy_name);
209 // FIXME: Figure out if we need this buddy list stuff
210 // at all. We don't actually care, as long as we get
211 // the messages and presence to send out.
213 /* We're done if buddy already exists and is authorized.
215 * libpurple says prpl should interpret a re-add of a existent
216 * buddy as an attempt to (re)send an authorization request if
217 * applicable.
219 if (buddy == NULL || need_auth)
221 char *default_group = "General";
223 /* Check to see if default group exists */
224 group = purple_find_group(default_group);
226 /* If not, create default group */
227 if (group == NULL)
228 group = purple_group_new(default_group);
230 purple_blist_add_group(group, NULL);
232 /* Add buddy to default group (NULL should be alias...) */
233 buddy = purple_buddy_new(account, buddy_name, NULL);
234 purple_blist_add_buddy(buddy, NULL, group, NULL);
235 purple_account_add_buddy(account, buddy);
237 purple_debug_info("thrasher",
238 "finished adding %s to %s's buddy list\n",
239 buddy_name, jid);
241 return 0;
242 } else {
243 purple_debug_info("thrasher",
244 "%s already on %s's buddy list\n",
245 buddy_name, jid);
248 /* Buddy already exists */
249 return 1;
253 * @brief remove buddy (@p buddy_name) from given @p jid buddy list
254 * @param jid releational key to bound to account
255 * @param buddy_name name of buddy
256 * @return 1 if there is a problem, 0 otherwise (C)
258 int thrasher_action_buddy_remove (char *jid, char *buddy_name)
260 PurpleGroup *group = NULL;
261 PurpleBuddy *buddy = NULL;
262 PurpleAccount *account = NULL;
264 purple_debug_info("thrasher", "Buddy remove\n");
266 account = g_hash_table_lookup(accts, jid);
267 g_return_val_if_fail(account != NULL, 1);
269 // Don't try and remove a buddy if the account
270 // isn't connected
271 if (purple_account_is_connected(account)) {
273 purple_debug_info("thrasher", "find_buddy...\n");
274 buddy = purple_find_buddy(account, buddy_name);
276 /* Only remove a buddy if they exist */
277 if (buddy != NULL)
279 purple_debug_info("thrasher", "get_group...\n");
280 /* Give up us the group */
281 group = purple_buddy_get_group(buddy);
283 purple_debug_info("thrasher", "account_remove_buddy...\n");
284 /* Remove server-side buddy entry */
285 purple_account_remove_buddy(account, buddy, group);
287 purple_debug_info("thrasher", "blist_remove_buddy...\n");
288 /* Also need to remove buddy entry from memory */
289 purple_blist_remove_buddy(buddy);
291 return 0;
295 /* Account isn't connected or buddy is not found */
296 return 1;
299 void thrasher_action_buddy_authorize(char *jid, char *legacy_username)
301 thrasher_action_buddy_add(jid, legacy_username);
304 void thrasher_action_buddy_deauthorize(char *jid, char *legacy_username) {
305 thrasher_action_buddy_remove(jid, legacy_username);
309 * @brief update presence status for given @p jid
310 * @param jid releational key to bound to account
311 * @param status integer representation of PurpleStatusPrimitive
312 * @param message a status message (may be NULL)
313 * @return 1 if there is a problem, 0 otherwise (C)
315 int thrasher_action_presence (char *jid, int status, char *message)
317 PurpleAccount *account;
319 g_return_val_if_fail(jid != NULL, 0);
321 account = g_hash_table_lookup(accts, jid);
323 if (account == NULL)
324 return 1;
326 thrasher_set_presence(account, status, message);
328 return 0;
333 * @brief Send @p message from the user given by @p jid to the user @p recipient
334 * @param jid
335 * @param recipient
336 * @param message
337 * @return 1 on success 0 on failure
339 int thrasher_action_outgoing_msg (char *jid, char *recipient, char *message)
341 int ret = 0;
342 PurpleAccount *account = NULL;
344 g_return_val_if_fail(jid != NULL, 0);
345 g_return_val_if_fail(recipient != NULL, 0);
346 g_return_val_if_fail(message != NULL, 0);
348 purple_debug_info("thrasher", "Send message request\n");
349 /* Pull in user if they exist */
350 account = g_hash_table_lookup(accts, jid);
352 /* User does not exist (may not be logged in) */
353 if (account == NULL) {
354 return 0;
357 /* Try to send message (message might not be sent) */
358 if (thrasher_send_msg(account,recipient,message) == TRUE)
359 ret = 1;
361 return ret;
366 * @brief Send @p chatstate from the user given by @p jid to the @p recipient
367 * @param jid
368 * @param recipient
369 * @param chatstate
371 void
372 thrasher_action_outgoing_chatstate(char *jid,
373 char *recipient,
374 PurpleTypingState chatstate) {
375 g_return_if_fail(jid != NULL);
376 g_return_if_fail(recipient != NULL);
378 PurpleAccount *pa = NULL;
379 pa = g_hash_table_lookup(accts, jid);
380 if (pa == NULL) {
381 /* User does not exist (may not be logged in) */
382 return;
385 /* Try to send chatstate */
386 thrasher_outgoing_chatstate(pa, recipient, chatstate);
391 * @brief Log the user out with the given jid
392 * @param jid releational key to bind to account
393 * @return 1 if logout was successful, 0 if it was not
395 * NOTE: As we're going to be running a process for each protocol,
396 * there's no need to worry about the protocol used.
398 int thrasher_action_logout (char *jid)
400 int ret = 0;
401 PurpleAccount *account;
403 g_return_val_if_fail(jid != NULL, 0);
405 purple_debug_info("thrasher", "Logout requested for %s\n", jid);
407 /* Verify user is logged in */
408 account =g_hash_table_lookup(accts, jid);
409 if (account != NULL)
411 thrasher_logout(account);
412 thrasher_remove_account(jid);
413 ret = 1;
416 return ret;
420 void _thrasher_action_debug_logged_in_accts_printer(gpointer key, gpointer value, gpointer user_data);
423 * @brief Dump active account name lists to STDERR from libpurple and thperl.
425 void
426 thrasher_action_debug_logged_in() {
427 fprintf(stderr, "purple_accounts_get_all_active:\n");
428 GList* list = purple_accounts_get_all_active();
429 if (list) {
430 GList *l = list;
431 while (l) {
432 PurpleAccount *pa = (PurpleAccount*)l->data;
433 fprintf(stderr, "\t%s = %p\n", purple_account_get_username(pa), pa);
434 l = l->next;
436 g_list_free(list);
439 if (accts) {
440 fprintf(stderr, "thperl accts:\n");
441 g_hash_table_foreach(accts,
442 _thrasher_action_debug_logged_in_accts_printer,
443 NULL);
445 else {
446 fprintf(stderr, "No thperl accts?!!\n");
450 void
451 _thrasher_action_debug_logged_in_accts_printer(gpointer key,
452 gpointer value,
453 gpointer user_data) {
454 const char *jid = (char*)key;
455 PurpleAccount *pa = (PurpleAccount*)value;
456 fprintf(stderr, "\t%s = %s = %p\n",
457 jid, purple_account_get_username(pa), pa);
462 * @brief Destruction function for our internal function hash
463 * @param key data element
465 void destroy_record_key (gpointer key)
467 free(key);
472 * @brief Bizarro passthrough to allow perl calling of dynamically-set C events
473 * @param key a guint value corresponding to the pointer of a callback.
474 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
476 int thrasher_wrapper_trigger_timeout_func(long key)
478 /* @exception key cannot be NULL */
479 g_return_val_if_fail(key != TNULL,TFALSE);
481 char *real_key = LONG_TO_PTR(key);
483 return func_call(real_key);
487 * @brief Bizarro passthrough (two) to allow perl calling of dynamically-set C events
488 * @param file descriptor
489 * @param condition trigger was called on
490 * @param key a long value corresponding to the pointer of a callback.
491 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
493 int thrasher_wrapper_trigger_input_func(long fd, SV *cond, long key)
495 /* @exception fd cannot be zero */
496 g_return_val_if_fail(fd != 0, 0);
497 /* @exception cond cannot be zero */
498 g_return_val_if_fail(cond != 0, 0);
499 /* @exception key cannot be zero */
500 g_return_val_if_fail(key != 0, 0);
502 // GIOCondition real_cond = cond;
503 char *real_key = LONG_TO_PTR(key);
505 /* FIXME: We need to pass cond and key to this call... */
506 return func_call(real_key);
510 * @brief Simple verifcation for callbacks
511 * @param subref to callback
514 void validate_callback (SV *cb)
516 if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV)
518 croak("Not a callback!");
519 exit(0);
525 * @brief Initialize our wrapper
526 * @param subref (SV *) to call for timeout_add
527 * @param subref (SV *) to call for input_add
528 * @param subref (SV *) to call for source_remove
529 * @param subref (SV *) to call on incoming messages
530 * @param subref (SV *) to call on incoming presence information
531 * @param subref (SV *) to call on subscription requests
532 * @param subref (SV *) to call on subscription requests from legacy users
533 * @param subref (SV *) to call on connection errors
534 * @param subref (SV *) to call when connections have succeeded
535 * @param subref (SV *) to call on incoming chatstate (optional)
537 void thrasher_wrapper_init (SV *timeout_add_cb,
538 SV *input_add_cb,
539 SV *source_remove_cb,
540 SV *incoming_msg_cb,
541 SV *presence_in_cb,
542 SV *subscription_add_cb,
543 SV *legacy_user_add_user_cb,
544 SV *connection_error_cb,
545 SV *connection_cb,
546 ...)
548 va_list argp;
549 va_start(argp, connection_cb);
550 SV* incoming_chatstate_cb = va_arg(argp, SV*);
552 g_return_if_fail(timeout_add_cb);
553 g_return_if_fail(input_add_cb);
554 g_return_if_fail(source_remove_cb);
555 g_return_if_fail(incoming_msg_cb);
556 g_return_if_fail(presence_in_cb);
557 g_return_if_fail(subscription_add_cb);
558 g_return_if_fail(legacy_user_add_user_cb);
559 g_return_if_fail(connection_error_cb);
560 g_return_if_fail(connection_cb);
562 /* Set the bindings for our function table */
563 callback_funcs = g_hash_table_new_full(g_direct_hash,
564 g_direct_equal,
565 destroy_record_key,
566 NULL);
568 /* Create the lookup table so we know what to do when Perl triggers a remove */
569 perl_key_lookup = g_hash_table_new(g_int_hash, g_int_equal);
571 /* Validate callback, this burns up if it fails so no
572 * need to check for errors!
574 validate_callback(timeout_add_cb);
575 validate_callback(input_add_cb);
576 validate_callback(source_remove_cb);
577 validate_callback(incoming_msg_cb);
578 validate_callback(presence_in_cb);
579 validate_callback(subscription_add_cb);
580 validate_callback(legacy_user_add_user_cb);
581 validate_callback(connection_error_cb);
582 validate_callback(connection_cb);
583 if (incoming_chatstate_cb) {
584 validate_callback(incoming_chatstate_cb);
587 dSP;
589 ENTER;
590 SAVETMPS;
592 PUSHMARK(SP);
593 PUTBACK;
595 callback.timeout_add = newSVsv(timeout_add_cb);
596 callback.input_add = newSVsv(input_add_cb);
597 callback.source_remove = newSVsv(source_remove_cb);
598 callback.incoming_msg = newSVsv(incoming_msg_cb);
599 callback.presence_in = newSVsv(presence_in_cb);
600 callback.subscription_add = newSVsv(subscription_add_cb);
601 callback.legacy_user_add_user = newSVsv(legacy_user_add_user_cb);
602 callback.connection_error = newSVsv(connection_error_cb);
603 callback.connection = newSVsv(connection_cb);
604 if (incoming_chatstate_cb) {
605 callback.incoming_chatstate = newSVsv(incoming_chatstate_cb);
607 else {
608 callback.incoming_chatstate = 0;
611 SPAGAIN;
612 PUTBACK;
614 FREETMPS;
615 LEAVE;
619 * @brief Initialize our file transfer wrapper
620 * @param subref (SV *) to call for recv_accept
621 * @param subref (SV *) to call for recv_start
622 * @param subref (SV *) to call for recv_cancel
623 * @param subref (SV *) to call for recv_complete
624 * @param subref (SV *) to call for send_accept
625 * @param subref (SV *) to call for send_start
626 * @param subref (SV *) to call for send_cancel
627 * @param subref (SV *) to call for send_complete
628 * @param subref (SV *) to call for ui_read. Must return (buffer, size).
629 * @param subref (SV *) to call for ui_write
630 * @param subref (SV *) to call for ui_data_not_sent
632 void thrasher_wrapper_ft_init(SV *ft_recv_request_cb,
633 SV *ft_recv_accept_cb,
634 SV *ft_recv_start_cb,
635 SV *ft_recv_cancel_cb,
636 SV *ft_recv_complete_cb,
637 SV *ft_send_accept_cb,
638 SV *ft_send_start_cb,
639 SV *ft_send_cancel_cb,
640 SV *ft_send_complete_cb,
641 SV *ft_read_cb,
642 SV *ft_write_cb,
643 SV *ft_data_not_sent_cb)
645 g_return_if_fail(ft_recv_request_cb);
646 g_return_if_fail(ft_recv_accept_cb);
647 g_return_if_fail(ft_recv_start_cb);
648 g_return_if_fail(ft_recv_cancel_cb);
649 g_return_if_fail(ft_recv_complete_cb);
650 g_return_if_fail(ft_send_accept_cb);
651 g_return_if_fail(ft_send_start_cb);
652 g_return_if_fail(ft_send_cancel_cb);
653 g_return_if_fail(ft_send_complete_cb);
654 g_return_if_fail(ft_read_cb);
655 g_return_if_fail(ft_write_cb);
656 g_return_if_fail(ft_data_not_sent_cb);
658 // This assumes thrasher_wrapper_init was already called.
659 g_return_if_fail(callback_funcs);
661 validate_callback(ft_recv_request_cb);
662 validate_callback(ft_recv_accept_cb);
663 validate_callback(ft_recv_start_cb);
664 validate_callback(ft_recv_cancel_cb);
665 validate_callback(ft_recv_complete_cb);
666 validate_callback(ft_send_accept_cb);
667 validate_callback(ft_send_start_cb);
668 validate_callback(ft_send_cancel_cb);
669 validate_callback(ft_send_complete_cb);
670 validate_callback(ft_read_cb);
671 validate_callback(ft_write_cb);
672 validate_callback(ft_data_not_sent_cb);
674 dSP;
676 ENTER;
677 SAVETMPS;
679 PUSHMARK(SP);
680 PUTBACK;
682 ft_callback.ft_recv_request_cb = newSVsv(ft_recv_request_cb);
683 ft_callback.ft_recv_accept_cb = newSVsv(ft_recv_accept_cb);
684 ft_callback.ft_recv_start_cb = newSVsv(ft_recv_start_cb);
685 ft_callback.ft_recv_cancel_cb = newSVsv(ft_recv_cancel_cb);
686 ft_callback.ft_recv_complete_cb = newSVsv(ft_recv_complete_cb);
687 ft_callback.ft_send_accept_cb = newSVsv(ft_send_accept_cb);
688 ft_callback.ft_send_start_cb = newSVsv(ft_send_start_cb);
689 ft_callback.ft_send_cancel_cb = newSVsv(ft_send_cancel_cb);
690 ft_callback.ft_send_complete_cb = newSVsv(ft_send_complete_cb);
691 ft_callback.ft_read_cb = newSVsv(ft_read_cb);
692 ft_callback.ft_write_cb = newSVsv(ft_write_cb);
693 ft_callback.ft_data_not_sent_cb = newSVsv(ft_data_not_sent_cb);
695 SPAGAIN;
696 PUTBACK;
698 FREETMPS;
699 LEAVE;
705 * @brief Validate then call the given callback pointer.
706 * @param key a hash key for the local function callback table.
707 * @return boolean as int (0 || 1) on success
709 int func_call(gpointer key)
711 /* @exception key cannot be NULL */
712 g_return_val_if_fail(key != NULL,TFALSE);
714 gpointer orig_key = NULL;
715 hash_record *record = NULL;
716 int (*timeout_add_caller)(gpointer);
717 int (*input_add_caller)(gpointer, gint, PurpleInputCondition);
718 int ret = 0;
720 /* Crazy voodoo madness!
721 * We're using direct pointers as keys and a struct at the
722 * end of the pointer, so no need to get any values.
724 if (g_hash_table_lookup_extended(callback_funcs, key, &orig_key, NULL))
726 record = orig_key;
728 if (record->type == TIMEOUT_ADD)
730 timeout_add_caller = record->function;
731 ret = (timeout_add_caller)(record->data);
734 else if (record->type == INPUT_ADD)
736 input_add_caller = record->function;
737 (input_add_caller)(record->data, record->fd,
738 record->cond);
739 // these functions are void
740 ret = TRUE;
743 if (ret == FALSE)
745 /* We're never gonna be calling this again, so get rid of it */
746 g_hash_table_remove(callback_funcs, record);
749 return (ret);
752 else
754 purple_debug_info("thrasher", "Callback for [%p] does not exist!\n", key);
757 return(TFALSE);
762 * @brief Wraps Glib::Source->remove
763 * @param key to return to the Perl end
764 * @return Success of call, including the success of the callback
766 gboolean thrasher_wrapper_call_source_remove (long key)
768 long ret = FALSE;
770 /* @exception key cannot be NULL */
771 g_return_val_if_fail(callback.source_remove, 0);
773 /* @exception callback.source_remove cannot be NULL */
774 g_return_val_if_fail(key != TNULL, 0);
776 dSP;
778 ENTER;
779 SAVETMPS;
781 PUSHMARK(sp);
783 /* Push the key onto the stack */
784 XPUSHs(sv_2mortal(newSViv(key)));
786 PUTBACK;
788 call_sv(callback.source_remove, G_EVAL | G_SCALAR);
790 SPAGAIN;
792 ret = POPl;
794 FREETMPS;
795 PUTBACK;
797 LEAVE;
799 /* Remove call from lookup table */
800 g_hash_table_remove(perl_key_lookup, (gpointer)&key);
802 return ret;
807 * PurpleInputCondition is a bitwise OR of read/write events
808 * PurpleInputFunction is a callback to a guint function with the args gpointer, gint, and PurpleInoutCondition
811 * @brief Wraps Glib::IO->add_watch
812 * @param File descriptor
813 * @param Condition to trigger on
814 * @param Nested callback
815 * @param Data to pass to callback
816 * @return Hash key
818 long thrasher_wrapper_set_input_add (guint fd, PurpleInputCondition cond, PurpleInputFunction function, gpointer data)
820 g_return_val_if_fail(callback.input_add, 0);
821 g_return_val_if_fail(function != NULL, 0);
823 long key = 0;
825 hash_record *record = (hash_record *)malloc(sizeof(hash_record));
827 record->type = INPUT_ADD;
828 record->function = function;
829 record->data = data;
830 record->cond = cond;
831 record->fd = fd;
833 g_hash_table_insert(callback_funcs, record, NULL);
835 key = PTR_TO_LONG(record);
836 g_return_val_if_fail(key != 0, 0);
838 dSP;
840 ENTER;
841 SAVETMPS;
844 SV *pc = NULL;
845 CV *temp_cv = NULL;
846 SV *svkey = NULL;
849 svkey = newSViv(key);
850 temp_cv = get_cv(trigger_input_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(fd)));
864 XPUSHs(sv_2mortal(newSViv(cond)));
865 XPUSHs(sv_2mortal(pc));
866 XPUSHs(sv_2mortal(svkey));
868 PUTBACK;
870 call_sv(callback.input_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 g_hash_table_insert(perl_key_lookup, (gpointer)&key, record);
883 return key;
888 * @brief Wraps Glib::Timeout->add
889 * @param interval in milliseconds
890 * @param callback
891 * @param data to pass to the callback
892 * @return Key for the internal timeout_add function hash. (Should this really be returning anything?!)
894 long thrasher_wrapper_set_timeout_add (guint interval, GSourceFunc function, gpointer data)
896 /* @exception callback.timeout_add cannot be NULL */
897 g_return_val_if_fail(callback.timeout_add, 0);
898 g_return_val_if_fail(function != NULL, TFALSE);
900 long key = 0;
902 hash_record *record = (hash_record *)malloc(sizeof(hash_record *));
904 record->type = TIMEOUT_ADD;
905 record->function = function;
906 record->data = data;
908 g_hash_table_insert(callback_funcs, record, NULL);
910 key = PTR_TO_LONG(record);
911 g_return_val_if_fail(key != 0, 0);
913 dSP;
915 ENTER;
916 SAVETMPS;
919 SV *pc = NULL;
920 CV *temp_cv = NULL;
921 SV *svkey = NULL;
923 svkey = newSViv(key);
924 temp_cv = get_cv(trigger_timeout_func_name, FALSE);
926 if (temp_cv)
928 pc = newRV((SV*)temp_cv);
930 /* Ok, this is hairy, first we push the vars we want to send to the
931 * timeout_add Perl callback. Next we push the var we want to give
932 * back to ourselves via trigger_input_func.
935 PUSHMARK(sp);
937 XPUSHs(sv_2mortal(newSViv(interval)));
938 XPUSHs(sv_2mortal(pc));
939 XPUSHs(sv_2mortal(svkey));
941 PUTBACK;
943 call_sv(callback.timeout_add, G_EVAL | G_SCALAR);
945 SPAGAIN;
947 /* Tweak our internal key to an external key */
948 key = POPl;
950 else
952 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
955 SPAGAIN;
957 FREETMPS;
958 PUTBACK;
960 LEAVE;
962 return key;
966 * @brief Send a @p message out via callback.incoming_msg
967 * @param jid user the @p message is being sent to
968 * @param sender user name of the @p message sender
969 * @param alias alternate user name of the @p sender (may be NULL)
970 * @param message string being sent to @p jid
971 * @return 1 on success, 0 on failure
973 int thrasher_wrapper_incoming_msg (const char *jid,
974 const char *sender,
975 const char *alias,
976 const char *message,
977 PurpleMessageFlags flags)
979 int ret = 0;
981 /* @exception jid, sender, and message cannot be NULL */
982 g_return_val_if_fail(jid != NULL, 0);
983 g_return_val_if_fail(sender != NULL, 0);
984 g_return_val_if_fail(message != NULL, 0);
986 /* @exception bail if our callback doesn't exist */
987 g_return_val_if_fail(callback.incoming_msg != NULL, 0);
989 dSP;
991 ENTER;
992 SAVETMPS;
994 PUSHMARK(sp);
996 /* Push the args onto the stack */
997 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
998 XPUSHs(sv_2mortal(newSVpvn(sender, strlen(sender))));
999 XPUSHs(sv_2mortal(newSVpvn(alias, strlen(alias))));
1000 XPUSHs(sv_2mortal(newSVpvn(message, strlen(message))));
1001 XPUSHs(sv_2mortal(newSViv(flags)));
1003 PUTBACK;
1005 call_sv(callback.incoming_msg, G_EVAL | G_SCALAR);
1007 SPAGAIN;
1009 ret = POPl;
1011 FREETMPS;
1012 PUTBACK;
1014 LEAVE;
1016 return ret;
1020 * @brief Forwards the 'connection' event, that authentication has
1021 * completed
1022 * @param jid user who has successfully connected
1024 int thrasher_wrapper_connection(const char *jid)
1026 int ret = 0;
1028 /* @exception jid, sender, and message cannot be NULL */
1029 g_return_val_if_fail(jid != NULL, 0);
1031 /* @exception bail if our callback doesn't exist */
1032 g_return_val_if_fail(callback.connection != NULL, 0);
1034 dSP;
1036 ENTER;
1037 SAVETMPS;
1039 PUSHMARK(sp);
1041 /* Push the args onto the stack */
1042 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
1044 PUTBACK;
1046 call_sv(callback.connection, G_EVAL | G_SCALAR);
1048 SPAGAIN;
1050 ret = POPl;
1052 FREETMPS;
1053 PUTBACK;
1055 LEAVE;
1057 return ret;
1060 SV *clean_newSVpv(const char *inp)
1062 if (inp)
1063 return sv_2mortal(newSVpv(inp, 0));
1064 else
1065 return sv_2mortal(&PL_sv_undef);
1070 * @brief Forward presence information via callback.presence_in
1071 * @param jid user the @p status is being sent to
1072 * @param sender user name of the @p status sender
1073 * @param alias alternate user name of the @p sender (may be NULL)
1074 * @param group which the @p sender belongs to
1075 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1076 * @param message string being sent to @p jid (may be NULL)
1077 * @return 1 on success, 0 on failure (wrapped Perl)
1079 int thrasher_wrapper_presence_in(const char *jid,
1080 const char *sender,
1081 const char *alias,
1082 const char *group,
1083 const PurpleStatusPrimitive status,
1084 const char *message)
1086 int ret = 0;
1088 /* @exception jid, sender, and group cannot be NULL */
1089 g_return_val_if_fail(jid != NULL, 0);
1090 g_return_val_if_fail(sender != NULL, 0);
1091 g_return_val_if_fail(group != NULL, 0);
1093 /* @exception bail if our callback doesn't exist */
1094 g_return_val_if_fail(callback.presence_in != NULL, 0);
1096 dSP;
1098 ENTER;
1099 SAVETMPS;
1101 PUSHMARK(sp);
1103 /* Push the args onto the stack */
1104 XPUSHs(clean_newSVpv(jid));
1105 XPUSHs(clean_newSVpv(sender));
1106 XPUSHs(clean_newSVpv(alias));
1107 XPUSHs(clean_newSVpv(group));
1108 XPUSHs(sv_2mortal(newSViv(status)));
1109 XPUSHs(clean_newSVpv(message));
1111 PUTBACK;
1113 call_sv(callback.presence_in, G_EVAL | G_SCALAR);
1115 SPAGAIN;
1117 ret = POPl;
1119 FREETMPS;
1120 PUTBACK;
1122 LEAVE;
1124 return ret;
1127 int thrasher_wrapper_legacy_user_add_user(const char *jid,
1128 const char *sender) {
1129 int ret = 0;
1131 g_return_val_if_fail(jid, 0);
1132 g_return_val_if_fail(sender, 0);
1134 g_return_val_if_fail(callback.legacy_user_add_user, 0);
1136 dSP;
1138 ENTER;
1139 SAVETMPS;
1141 PUSHMARK(sp);
1143 XPUSHs(clean_newSVpv(jid));
1144 XPUSHs(clean_newSVpv(sender));
1146 PUTBACK;
1148 call_sv(callback.legacy_user_add_user, G_EVAL | G_SCALAR);
1150 SPAGAIN;
1152 ret = POPl;
1154 FREETMPS;
1155 PUTBACK;
1157 LEAVE;
1159 return ret;
1163 * @brief Forward subscription request information via callback.subscription_add
1164 * @param jid user the @p status is being sent to
1165 * @param sender user name of the requester
1166 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1167 * @return 1 on success, 0 on failure (wrapped Perl)
1169 int thrasher_wrapper_subscription_add(const char *jid,
1170 const char *sender,
1171 guint status)
1173 int ret = 0;
1175 /* @exception jid, sender, and group cannot be NULL */
1176 g_return_val_if_fail(jid != NULL, 0);
1177 g_return_val_if_fail(sender != NULL, 0);
1179 /* @exception bail if our callback doesn't exist */
1180 g_return_val_if_fail(callback.subscription_add != NULL, 0);
1182 dSP;
1184 ENTER;
1185 SAVETMPS;
1187 PUSHMARK(sp);
1189 /* Push the args onto the stack */
1190 XPUSHs(clean_newSVpv(jid));
1191 XPUSHs(clean_newSVpv(sender));
1192 XPUSHs(sv_2mortal(newSViv(status)));
1194 PUTBACK;
1196 call_sv(callback.subscription_add, G_EVAL | G_SCALAR);
1198 SPAGAIN;
1200 ret = POPl;
1202 FREETMPS;
1203 PUTBACK;
1205 LEAVE;
1207 return ret;
1211 * @brief Forward connection error messages via callback.connection_error
1212 * @param jid user the @p error_code is being sent to
1213 * @param error_code PurpleConnectionError (guint) being sent to @p jid
1214 * @param message a error message (may be NULL)
1215 * @return 1 on success, 0 on failure (wrapped Perl)
1217 int thrasher_wrapper_connection_error(const char *jid,
1218 const guint error_code,
1219 const char *message)
1221 int ret = 0;
1223 // If the JID is null, pick it up off of
1224 // thrasher.c:current_login_jid, per the comment in that file.
1225 if (jid == NULL) {
1226 purple_debug_info("thrasher", "had to retrieve the jid from mem\n");
1227 jid = get_current_login_jid();
1230 // If we're in the middle of a connection, pick up that we have an error.
1231 set_got_error(1);
1233 g_return_val_if_fail(jid, 0);
1236 /* @exception error_code cannot be less than zero or greater than PURPLE_CONNECTION_ERROR_OTHER_ERROR */
1237 g_return_val_if_fail(error_code >= 0, ret);
1238 g_return_val_if_fail(error_code <= PURPLE_CONNECTION_ERROR_OTHER_ERROR, ret);
1240 dSP;
1242 ENTER;
1243 SAVETMPS;
1245 PUSHMARK(sp);
1247 /* Push the args onto the stack */
1248 XPUSHs(clean_newSVpv(jid));
1249 XPUSHs(sv_2mortal(newSViv(error_code)));
1250 XPUSHs(clean_newSVpv(message));
1252 PUTBACK;
1254 call_sv(callback.connection_error, G_EVAL | G_SCALAR);
1256 SPAGAIN;
1258 ret = POPl;
1260 FREETMPS;
1261 PUTBACK;
1263 LEAVE;
1265 // Remove the account from our current active set
1266 thrasher_remove_account(jid);
1268 return ret;
1271 void thrasher_remove_account (const char *jid)
1273 PurpleAccount *account;
1275 g_return_if_fail(jid != NULL);
1277 purple_debug_info("thrasher", "Removing active account for %s\n",
1278 jid);
1280 account = g_hash_table_lookup(accts, jid);
1281 if (account != NULL)
1283 g_hash_table_remove(accts, jid);
1288 * @brief Forwards typing notification state changes from libpurple buddies.
1289 * @param pa Account receiving the notification
1290 * @param who Buddy name sending the notification
1291 * @param state
1293 void thrasher_wrapper_incoming_chatstate(PurpleAccount* pa,
1294 const char* who,
1295 PurpleTypingState state) {
1296 /* @exception pa and who cannot be NULL */
1297 g_return_if_fail(pa != NULL);
1298 g_return_if_fail(who != NULL);
1300 /* bail if Perl-side doesn't implement this */
1301 if (! callback.incoming_chatstate) {
1302 return;
1305 gchar* jid = thrasher_account_get_jid(pa);
1306 /* @exception bail if account has no JID */
1307 g_return_if_fail(jid != NULL);
1309 dSP;
1311 ENTER;
1312 SAVETMPS;
1314 PUSHMARK(sp);
1316 /* Push the args onto the stack */
1317 XPUSHs(clean_newSVpv(jid));
1318 XPUSHs(clean_newSVpv(who));
1319 XPUSHs(sv_2mortal(newSViv(state)));
1321 PUTBACK;
1323 call_sv(callback.incoming_chatstate, G_EVAL | G_SCALAR);
1325 SPAGAIN;
1327 FREETMPS;
1328 PUTBACK;
1330 LEAVE;
1334 * @begin External trigger to flush the internal function tables
1336 void thrasher_wrapper_destructor ()
1338 g_hash_table_destroy(callback_funcs);
1339 g_hash_table_destroy(perl_key_lookup);
1342 size_t
1343 thrasher_wrapper_send_file(const char *jid,
1344 const char *who,
1345 char *filename,
1346 guint size,
1347 char *desc) {
1348 g_return_val_if_fail(jid, 0);
1349 g_return_val_if_fail(who, 0);
1351 purple_debug_info("thrasher ft",
1352 "Sending file %s -> %s\n",
1353 jid, who);
1355 PurpleAccount *account = g_hash_table_lookup(accts, jid);
1357 g_return_val_if_fail(account, 0);
1359 return thrasher_send_file(account, who, filename, size, desc);
1362 void
1363 thrasher_action_ft_ui_ready(size_t id) {
1364 PurpleXfer* xfer = get_xfer_by_id(id);
1365 g_return_if_fail(xfer);
1366 purple_xfer_ui_ready(xfer);
1367 /* HACK: READY_PRPL is usually not set (or was cleared) right now,
1368 * and some prpls don't appear to set it correctly on repeats.
1369 * Spam prpl_ready to force a do_transfer anyway.
1371 * Using a separate watch on the remote xfer->fd would avoid some
1372 * EAGAINs. I'd be happy to be proven wrong, but testing found no
1373 * significant benefit to Thrasher in that.
1375 purple_xfer_prpl_ready(xfer);
1379 * @brief Called on libpurple file-send-start signal
1380 * @param id of Thrasher file transfer structure.
1383 thrasher_wrapper_ft_send_start(guint id) {
1384 int ret = 0;
1386 /* @exception bail if our callback doesn't exist */
1387 g_return_val_if_fail(ft_callback.ft_send_start_cb != NULL, 0);
1389 dSP;
1391 ENTER;
1392 SAVETMPS;
1394 PUSHMARK(sp);
1396 /* Push the args onto the stack */
1397 XPUSHs(sv_2mortal(newSViv(id)));
1399 PUTBACK;
1401 call_sv(ft_callback.ft_send_start_cb, G_EVAL | G_SCALAR);
1403 SPAGAIN;
1405 ret = POPl;
1407 FREETMPS;
1408 PUTBACK;
1410 LEAVE;
1412 return ret;
1416 * @brief Called on libpurple file-send-cancel signal
1417 * @param id of Thrasher file transfer structure.
1419 void
1420 thrasher_wrapper_ft_send_cancel(guint id) {
1421 /* @exception bail if our callback doesn't exist */
1422 g_return_if_fail(ft_callback.ft_send_cancel_cb != NULL);
1424 dSP;
1426 ENTER;
1427 SAVETMPS;
1429 PUSHMARK(sp);
1431 /* Push the args onto the stack */
1432 XPUSHs(sv_2mortal(newSViv(id)));
1434 PUTBACK;
1436 call_sv(ft_callback.ft_send_cancel_cb, G_EVAL | G_SCALAR);
1438 SPAGAIN;
1440 FREETMPS;
1441 PUTBACK;
1443 LEAVE;
1445 return;
1449 * @brief Called on libpurple file-send-complete signal
1450 * @param id of Thrasher file transfer structure.
1452 void
1453 thrasher_wrapper_ft_send_complete(guint id) {
1454 /* @exception bail if our callback doesn't exist */
1455 g_return_if_fail(ft_callback.ft_send_complete_cb != NULL);
1457 dSP;
1459 ENTER;
1460 SAVETMPS;
1462 PUSHMARK(sp);
1464 /* Push the args onto the stack */
1465 XPUSHs(sv_2mortal(newSViv(id)));
1467 PUTBACK;
1469 call_sv(ft_callback.ft_send_complete_cb, G_EVAL | G_SCALAR);
1471 SPAGAIN;
1473 FREETMPS;
1474 PUTBACK;
1476 LEAVE;
1478 return;
1481 gssize
1482 thrasher_wrapper_ft_read(guint id,
1483 guchar **buffer,
1484 gssize size) {
1485 int read_sz = -1;
1486 int count = 0;
1488 /* @exception bail if our callback doesn't exist */
1489 g_return_val_if_fail(ft_callback.ft_read_cb != NULL, 0);
1491 dSP;
1493 ENTER;
1494 SAVETMPS;
1496 PUSHMARK(sp);
1498 /* Push the args onto the stack */
1499 XPUSHs(sv_2mortal(newSViv(id)));
1500 XPUSHs(sv_2mortal(newSViv(size)));
1502 PUTBACK;
1504 count = call_sv(ft_callback.ft_read_cb, G_EVAL | G_ARRAY);
1506 SPAGAIN;
1508 if (count == 2) {
1509 read_sz = POPl;
1510 void* read = POPpbytex;
1511 *buffer = malloc(read_sz);
1512 /* This buffer is free'd by libpurple. */
1513 memcpy(*buffer, read, read_sz);
1515 else {
1516 *buffer = NULL;
1517 read_sz = -1;
1520 FREETMPS;
1521 PUTBACK;
1523 LEAVE;
1525 return read_sz;
1528 void
1529 thrasher_wrapper_ft_data_not_sent(guint id,
1530 const guchar *buffer,
1531 gsize size) {
1532 /* @exception buffer cannot be NULL */
1533 g_return_if_fail(buffer != NULL);
1535 /* @exception bail if our callback doesn't exist */
1536 g_return_if_fail(ft_callback.ft_data_not_sent_cb != NULL);
1538 dSP;
1540 ENTER;
1541 SAVETMPS;
1543 PUSHMARK(sp);
1545 /* Push the args onto the stack */
1546 XPUSHs(sv_2mortal(newSViv(id)));
1547 /* buffer is not null-terminated. */
1548 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1550 PUTBACK;
1552 call_sv(ft_callback.ft_data_not_sent_cb, G_EVAL | G_SCALAR);
1554 SPAGAIN;
1556 FREETMPS;
1557 PUTBACK;
1559 LEAVE;
1561 return;
1565 * @brief ask user's permission for ft and set up bytestream
1566 * @param xfer a filled-in PURPLE_XFER_RECEIVE PurpleXfer.
1567 * @param filename suggested local filename
1568 * @return nothing; use thrasher_action_ft_recv_request_respond when ready
1570 void
1571 thrasher_wrapper_ft_recv_request(PurpleXfer *xfer,
1572 const char* filename) {
1573 /* @exception xfer cannot be NULL */
1574 g_return_if_fail(xfer != NULL);
1576 /* @exception bail if our callback doesn't exist */
1577 g_return_if_fail(ft_callback.ft_recv_request_cb != NULL);
1579 dSP;
1581 ENTER;
1582 SAVETMPS;
1584 PUSHMARK(sp);
1586 /* Push the args onto the stack */
1587 guint id = get_id_by_xfer(xfer);
1588 XPUSHs(sv_2mortal(newSViv(id)));
1589 PurpleAccount* pa = purple_xfer_get_account(xfer);
1590 gchar* jid = thrasher_account_get_jid(pa);
1591 XPUSHs(clean_newSVpv(jid));
1592 const char* who = purple_xfer_get_remote_user(xfer);
1593 XPUSHs(clean_newSVpv(who));
1594 XPUSHs(clean_newSVpv(filename));
1595 size_t size = purple_xfer_get_size(xfer);
1596 XPUSHs(sv_2mortal(newSViv(size)));
1598 PUTBACK;
1600 call_sv(ft_callback.ft_recv_request_cb, G_EVAL | G_SCALAR);
1602 SPAGAIN;
1604 FREETMPS;
1605 PUTBACK;
1607 LEAVE;
1609 return;
1612 void
1613 thrasher_action_ft_recv_request_respond(size_t id,
1614 unsigned int accept) {
1615 thrasher_xfer_recv_request_responder(id, accept);
1618 gssize
1619 thrasher_wrapper_ft_write(guint id,
1620 const guchar *buffer,
1621 gssize size) {
1622 int written_sz = -1;
1624 /* @exception bail if our callback doesn't exist */
1625 g_return_val_if_fail(ft_callback.ft_write_cb != NULL, 0);
1627 dSP;
1629 ENTER;
1630 SAVETMPS;
1632 PUSHMARK(sp);
1634 /* Push the args onto the stack */
1635 XPUSHs(sv_2mortal(newSViv(id)));
1636 /* buffer is not null-terminated. */
1637 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1639 PUTBACK;
1641 call_sv(ft_callback.ft_write_cb, G_EVAL | G_ARRAY);
1643 SPAGAIN;
1645 written_sz = POPl;
1647 FREETMPS;
1648 PUTBACK;
1650 LEAVE;
1652 return written_sz;
1656 * @brief Called on libpurple file-recv-cancel signal
1657 * @param id of Thrasher file transfer structure.
1659 void
1660 thrasher_wrapper_ft_recv_cancel(guint id) {
1661 /* @exception bail if our callback doesn't exist */
1662 g_return_if_fail(ft_callback.ft_recv_cancel_cb != NULL);
1664 dSP;
1666 ENTER;
1667 SAVETMPS;
1669 PUSHMARK(sp);
1671 /* Push the args onto the stack */
1672 XPUSHs(sv_2mortal(newSViv(id)));
1674 PUTBACK;
1676 call_sv(ft_callback.ft_recv_cancel_cb, G_EVAL | G_SCALAR);
1678 SPAGAIN;
1680 FREETMPS;
1681 PUTBACK;
1683 LEAVE;
1685 return;
1689 * @brief Called on libpurple file-recv-complete signal
1690 * @param id of Thrasher file transfer structure.
1692 void
1693 thrasher_wrapper_ft_recv_complete(guint id) {
1694 /* @exception bail if our callback doesn't exist */
1695 g_return_if_fail(ft_callback.ft_recv_complete_cb != NULL);
1697 dSP;
1699 ENTER;
1700 SAVETMPS;
1702 PUSHMARK(sp);
1704 /* Push the args onto the stack */
1705 XPUSHs(sv_2mortal(newSViv(id)));
1707 PUTBACK;
1709 call_sv(ft_callback.ft_recv_complete_cb, G_EVAL | G_SCALAR);
1711 SPAGAIN;
1713 FREETMPS;
1714 PUTBACK;
1716 LEAVE;
1718 return;
1721 void
1722 thrasher_action_ft_cancel_local(size_t id) {
1723 PurpleXfer* xfer = get_xfer_by_id(id);
1724 purple_xfer_cancel_local(xfer);
1727 #ifdef TH_DEBUG
1728 /*********************************************************
1730 * Only debugging stuff beyond this point!
1732 *********************************************************/
1733 gboolean foo_callback (void *data);
1734 gboolean foo_callback2 (void *data);
1735 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond);
1736 long thrasher_wrapper_trigger_timeout_add (void);
1737 long thrasher_wrapper_trigger_timeout_add2 (void);
1738 long thrasher_wrapper_trigger_timeout_add3 (void);
1739 long thrasher_wrapper_trigger_input_add (void);
1740 void thrasher_wrapper_dump_calls (void);
1741 void spit_func_info (gpointer key, gpointer value, gpointer user_data);
1744 gboolean
1745 foo_callback (void *data)
1747 printf("!!!!!!Calling a callback w/ data [%s]!!!!\n", (char *)data);
1748 return TRUE;
1752 int cntr = 9;
1755 gboolean
1756 foo_callback2 (void *data)
1758 printf("will die in [%d] cycles\n", cntr);
1760 if (cntr--)
1761 return TRUE;
1762 else
1763 return FALSE;
1766 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond)
1768 printf("data [%p]\tfd [%d]\tcond [%d]\n", data, fd, cond);
1771 /* Test functions to allow an external caller to trigger internal actions */
1772 long thrasher_wrapper_trigger_timeout_add ()
1774 printf("trigger_timeout_add called\n");
1775 return thrasher_wrapper_set_timeout_add(1234, foo_callback, "YAY! test data");
1779 long thrasher_wrapper_trigger_timeout_add2 ()
1781 printf("trigger_timeout_add2 called\n");
1782 return thrasher_wrapper_set_timeout_add(1867, foo_callback, "test data says what?");
1785 long thrasher_wrapper_trigger_timeout_add3 ()
1787 printf("trigger_timeout_add3 called\n");
1788 return thrasher_wrapper_set_timeout_add(200, foo_callback2, "this shoudl fail shortly");
1791 long thrasher_wrapper_trigger_input_add ()
1793 printf("trigger_input_add called\n");
1795 int fd = open("/tmp/testfoo", O_RDWR);
1796 PurpleInputCondition cond;
1797 cond = PURPLE_INPUT_WRITE;
1798 long face = 0;
1799 printf("HAMMERTIME\n");
1800 face = thrasher_wrapper_set_input_add(fd, cond, foo_callback3, "double plus win ungood");
1802 return face;
1805 int thrasher_wrapper_trigger_timeout_remove (long key)
1807 printf("trigger_timeout_remove called\n");
1808 /* We currently have no way to correlate external keys to internal keys... */
1809 return !thrasher_wrapper_call_source_remove(key);
1813 void spit_func_info (gpointer key, gpointer value, gpointer user_data)
1815 hash_record *record;
1816 printf("key [%p]\tvalue [%p]\n", key, value);
1818 record = key;
1820 if (record->type == INPUT_ADD)
1822 printf("\tINPUT_ADD\n\tfunction [%p]\n\tdata [%p]\n\tcond [%d]\n\tfd [%d]\n", record->function, record->data, record->cond, record->fd);
1825 else if (record->type == TIMEOUT_ADD)
1828 printf("\tTIMEOUT_ADD\n\tfunction [%p]\n\tdata [%p]\n", record->function, record->data);
1831 else
1833 printf("record type [%d] is unrecognized!\n", record->type);
1837 void thrasher_wrapper_dump_calls ()
1839 g_hash_table_foreach(callback_funcs, spit_func_info, NULL);
1842 #endif /* TH_DEBUG */