Incoming statuses with characters 0x80-0xFF were double-escaped.
[thrasher.git] / thperl.c
blob1a78dad3e2fbc4b2404f4f334bcc4f3801ed6f0b
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 if (tf > 0) {
61 purple_debug_set_enabled(1);
63 if (tf > 1) {
64 purple_debug_set_verbose(1);
69 void thrasher_perl_init()
71 accts = g_hash_table_new(g_str_hash, g_str_equal);
74 /**
75 * @brief create a PurpleAccount and start connecting it.
76 * @param hash_args of arguments for thrasher_login().
77 * @return 0 for success. 1 if thrasher_login() fails. 2 if the PurpleAccount already exists.
79 int thrasher_action_login (GHashTable *hash_args)
81 int err = 0;
83 #ifdef TH_DEBUG
84 purple_debug_info("thrasher",
85 "Login request:\n");
86 GList *keys = g_hash_table_get_keys(hash_args);
87 GList *values = g_hash_table_get_values(hash_args);
88 while (keys)
90 purple_debug_info("thrasher",
91 " %s -> %s\n",
92 (char *)keys->data,
93 (char *)values->data);
94 keys = keys->next;
95 values = values->next;
98 g_list_free(keys);
99 g_list_free(values);
100 #endif /* TH_DEBUG */
102 // some parameters we always have
103 char *jid = (char *)g_strdup(g_hash_table_lookup(hash_args, "jid"));
104 char *username = (char *)g_strdup(g_hash_table_lookup(hash_args, "username"));
105 char *password = (char *)g_strdup(g_hash_table_lookup(hash_args, "password"));
106 char *proto = (char *)g_strdup(g_hash_table_lookup(hash_args, "proto"));
108 g_return_val_if_fail(jid, 0);
109 g_return_val_if_fail(username, 0);
110 g_return_val_if_fail(password, 0);
111 g_return_val_if_fail(proto, 0);
113 g_hash_table_remove(hash_args, "jid");
114 g_hash_table_remove(hash_args, "username");
115 g_hash_table_remove(hash_args, "password");
116 g_hash_table_remove(hash_args, "proto");
118 /* Verify user is not already logged in */
119 if (g_hash_table_lookup(accts, jid) == NULL)
121 PurpleAccount *account = thrasher_login(proto, username, password,
122 jid, hash_args);
124 // In the event of an error, connection_error has already been
125 // called.
126 if (account == NULL) {
127 return 1;
130 char *new_jid = g_strdup(jid);
132 /* Add user to hash */
133 g_hash_table_insert(
134 accts,
135 new_jid, /* Should eventually be the protocol + username */
136 account
139 /* Success! */
140 err = 0;
142 else
144 // At least indicate something bad has happened.
145 purple_debug_info("thrasher",
146 "login request for [%s] fail because they are apparently already logged in\n",
147 jid);
148 err = 2;
151 g_free(jid);
152 g_free(username);
153 g_free(password);
154 g_free(proto);
155 g_hash_table_destroy(hash_args);
157 return err;
162 * @brief Get a list of all JIDs with active accounts.
164 GList_String*
165 thrasher_action_get_logged_in_jids() {
166 GList* jids = g_hash_table_get_keys(accts);
167 return jids;
173 * @brief add buddy (@p buddy_name) for given @p jid buddy list
174 * @param jid releational key to bound to account
175 * @param buddy_name name of buddy
176 * @return 1 if there is a problem, 0 otherwise (C)
178 int thrasher_action_buddy_add (char *jid, char *buddy_name)
180 PurpleAccount *account = NULL;
181 PurpleBuddy *buddy = NULL;
182 PurpleGroup *group = NULL;
184 purple_debug_info("thrasher", "Buddy_add request\n");
186 account = g_hash_table_lookup(accts, jid);
187 g_return_val_if_fail(account != NULL, 1);
189 buddy = purple_find_buddy(account, buddy_name);
191 /* Re-requesting authorization addresses cases in which a buddy is
192 * implicitly added somewhere else but never actually sent an
193 * authorization request. Such a state was sometimes possible with
194 * Yahoo but consistently reproducible with as follows in ICQ:
196 * 1. Register user1 and user2 with the ICQ transport, each on the
197 * other's roster.
198 * 2. Remove user1's ICQ contact from user2's roster.
199 * 3. Remove user2's ICQ contact from user1's roster.
200 * 4. Add user2's ICQ contact to user1's roster.
201 * 5. thrasher_buddy_request_authorize() receives and handles an
202 * authorization request from user1 on behalf of user2. It
203 * adds user1 to user2's buddy list.
204 * 6. user1 is now fully authorized with user2.
205 * 7. user2, however, had user1 added as an *un*authorized buddy.
207 * This also means Thrasher tries to do something sensible with
208 * XMPP client operations like "re-request authorization from
209 * contact". Otherwise, they'd always be handled as no-ops because
210 * the libpurple buddy is already present.
212 gboolean need_auth;
213 if (buddy) {
214 /* Re-add: check if (re-)auth is needed. */
215 need_auth = ! thrasher_account_buddy_is_authorized(buddy);
217 else {
218 /* Not yet on list: assume an auth request is needed. */
219 need_auth = 1;
221 /* FIXME: There's no cross-prpl calls for checking/sending
222 * authorization like oscar.c:purple_auth_request()? Could try to
223 * parse for the menu items added by each prpl we know requires
224 * authorization. :(
226 if (need_auth) {
227 purple_debug_info("thrasher",
228 "%s needs authorization from %s\n",
229 jid, buddy_name);
232 // FIXME: Figure out if we need this buddy list stuff
233 // at all. We don't actually care, as long as we get
234 // the messages and presence to send out.
236 /* We're done if buddy already exists and is authorized.
238 * libpurple says prpl should interpret a re-add of a existent
239 * buddy as an attempt to (re)send an authorization request if
240 * applicable.
242 if (buddy == NULL || need_auth)
244 char *default_group = "General";
246 /* Check to see if default group exists */
247 group = purple_find_group(default_group);
249 /* If not, create default group */
250 if (group == NULL)
251 group = purple_group_new(default_group);
253 purple_blist_add_group(group, NULL);
255 /* Add buddy to default group (NULL should be alias...) */
256 buddy = purple_buddy_new(account, buddy_name, NULL);
257 purple_blist_add_buddy(buddy, NULL, group, NULL);
258 purple_account_add_buddy(account, buddy);
260 purple_debug_info("thrasher",
261 "finished adding %s to %s's buddy list\n",
262 buddy_name, jid);
264 return 0;
265 } else {
266 purple_debug_info("thrasher",
267 "%s already on %s's buddy list\n",
268 buddy_name, jid);
271 /* Buddy already exists */
272 return 1;
276 * @brief remove buddy (@p buddy_name) from given @p jid buddy list
277 * @param jid releational key to bound to account
278 * @param buddy_name name of buddy
279 * @return 1 if there is a problem, 0 otherwise (C)
281 int thrasher_action_buddy_remove (char *jid, char *buddy_name)
283 PurpleGroup *group = NULL;
284 PurpleBuddy *buddy = NULL;
285 PurpleAccount *account = NULL;
287 purple_debug_info("thrasher", "Buddy remove\n");
289 account = g_hash_table_lookup(accts, jid);
290 g_return_val_if_fail(account != NULL, 1);
292 // Don't try and remove a buddy if the account
293 // isn't connected
294 if (purple_account_is_connected(account)) {
296 purple_debug_info("thrasher", "find_buddy...\n");
297 buddy = purple_find_buddy(account, buddy_name);
299 /* Only remove a buddy if they exist */
300 if (buddy != NULL)
302 purple_debug_info("thrasher", "get_group...\n");
303 /* Give up us the group */
304 group = purple_buddy_get_group(buddy);
306 purple_debug_info("thrasher", "account_remove_buddy...\n");
307 /* Remove server-side buddy entry */
308 purple_account_remove_buddy(account, buddy, group);
310 purple_debug_info("thrasher", "blist_remove_buddy...\n");
311 /* Also need to remove buddy entry from memory */
312 purple_blist_remove_buddy(buddy);
314 return 0;
318 /* Account isn't connected or buddy is not found */
319 return 1;
322 void thrasher_action_buddy_authorize(char *jid, char *legacy_username)
324 thrasher_action_buddy_add(jid, legacy_username);
327 void thrasher_action_buddy_deauthorize(char *jid, char *legacy_username) {
328 thrasher_action_buddy_remove(jid, legacy_username);
332 * @brief update presence status for given @p jid
333 * @param jid releational key to bound to account
334 * @param status integer representation of PurpleStatusPrimitive
335 * @param message a status message (may be NULL)
336 * @return 1 if there is a problem, 0 otherwise (C)
338 int thrasher_action_presence (char *jid, int status, char *message)
340 PurpleAccount *account;
342 g_return_val_if_fail(jid != NULL, 0);
344 account = g_hash_table_lookup(accts, jid);
346 if (account == NULL)
347 return 1;
349 thrasher_set_presence(account, status, message);
351 return 0;
356 * @brief Send @p message from the user given by @p jid to the user @p recipient
357 * @param jid
358 * @param recipient
359 * @param message
360 * @return 1 on success 0 on failure
362 int thrasher_action_outgoing_msg (char *jid, char *recipient, char *message)
364 int ret = 0;
365 PurpleAccount *account = NULL;
367 g_return_val_if_fail(jid != NULL, 0);
368 g_return_val_if_fail(recipient != NULL, 0);
369 g_return_val_if_fail(message != NULL, 0);
371 purple_debug_info("thrasher", "Send message request\n");
372 /* Pull in user if they exist */
373 account = g_hash_table_lookup(accts, jid);
375 /* User does not exist (may not be logged in) */
376 if (account == NULL) {
377 return 0;
380 /* Try to send message (message might not be sent) */
381 if (thrasher_send_msg(account,recipient,message) == TRUE)
382 ret = 1;
384 return ret;
389 * @brief Send @p chatstate from the user given by @p jid to the @p recipient
390 * @param jid
391 * @param recipient
392 * @param chatstate
394 void
395 thrasher_action_outgoing_chatstate(char *jid,
396 char *recipient,
397 PurpleTypingState chatstate) {
398 g_return_if_fail(jid != NULL);
399 g_return_if_fail(recipient != NULL);
401 PurpleAccount *pa = NULL;
402 pa = g_hash_table_lookup(accts, jid);
403 if (pa == NULL) {
404 /* User does not exist (may not be logged in) */
405 return;
408 /* Try to send chatstate */
409 thrasher_outgoing_chatstate(pa, recipient, chatstate);
414 * @brief Log the user out with the given jid
415 * @param jid releational key to bind to account
416 * @return 1 if logout was successful, 0 if it was not
418 * NOTE: As we're going to be running a process for each protocol,
419 * there's no need to worry about the protocol used.
421 int thrasher_action_logout (char *jid)
423 int ret = 0;
424 PurpleAccount *account;
426 g_return_val_if_fail(jid != NULL, 0);
428 purple_debug_info("thrasher", "Logout requested for %s\n", jid);
430 /* Verify user is logged in */
431 account =g_hash_table_lookup(accts, jid);
432 if (account != NULL)
434 thrasher_logout(account);
435 thrasher_remove_account(jid);
436 ret = 1;
439 return ret;
443 void _thrasher_action_debug_logged_in_accts_printer(gpointer key, gpointer value, gpointer user_data);
446 * @brief Dump active account name lists to STDERR from libpurple and thperl.
448 void
449 thrasher_action_debug_logged_in() {
450 fprintf(stderr, "purple_accounts_get_all_active:\n");
451 GList* list = purple_accounts_get_all_active();
452 if (list) {
453 GList *l = list;
454 while (l) {
455 PurpleAccount *pa = (PurpleAccount*)l->data;
456 fprintf(stderr, "\t%s = %p\n", purple_account_get_username(pa), pa);
457 l = l->next;
459 g_list_free(list);
462 if (accts) {
463 fprintf(stderr, "thperl accts:\n");
464 g_hash_table_foreach(accts,
465 _thrasher_action_debug_logged_in_accts_printer,
466 NULL);
468 else {
469 fprintf(stderr, "No thperl accts?!!\n");
473 void
474 _thrasher_action_debug_logged_in_accts_printer(gpointer key,
475 gpointer value,
476 gpointer user_data) {
477 const char *jid = (char*)key;
478 PurpleAccount *pa = (PurpleAccount*)value;
479 fprintf(stderr, "\t%s = %s = %p\n",
480 jid, purple_account_get_username(pa), pa);
485 * @brief Destruction function for our internal function hash
486 * @param key data element
488 void destroy_record_key (gpointer key)
490 free(key);
495 * @brief Bizarro passthrough to allow perl calling of dynamically-set C events
496 * @param key a guint value corresponding to the pointer of a callback.
497 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
499 int thrasher_wrapper_trigger_timeout_func(long key)
501 /* @exception key cannot be NULL */
502 g_return_val_if_fail(key != TNULL,TFALSE);
504 char *real_key = LONG_TO_PTR(key);
506 return func_call(real_key);
510 * @brief Bizarro passthrough (two) to allow perl calling of dynamically-set C events
511 * @param file descriptor
512 * @param condition trigger was called on
513 * @param key a long value corresponding to the pointer of a callback.
514 * @return boolean as int (0 || 1) on success. Returns 0 on failure as well.
516 int thrasher_wrapper_trigger_input_func(long fd, SV *cond, long key)
518 /* @exception fd cannot be zero */
519 g_return_val_if_fail(fd != 0, 0);
520 /* @exception cond cannot be zero */
521 g_return_val_if_fail(cond != 0, 0);
522 /* @exception key cannot be zero */
523 g_return_val_if_fail(key != 0, 0);
525 // GIOCondition real_cond = cond;
526 char *real_key = LONG_TO_PTR(key);
528 /* FIXME: We need to pass cond and key to this call... */
529 return func_call(real_key);
533 * @brief Simple verifcation for callbacks
534 * @param subref to callback
537 void validate_callback (SV *cb)
539 if (!SvROK(cb) || SvTYPE(SvRV(cb)) != SVt_PVCV)
541 croak("Not a callback!");
542 exit(0);
548 * @brief Initialize our wrapper
549 * @param subref (SV *) to call for timeout_add
550 * @param subref (SV *) to call for input_add
551 * @param subref (SV *) to call for source_remove
552 * @param subref (SV *) to call on incoming messages
553 * @param subref (SV *) to call on incoming presence information
554 * @param subref (SV *) to call on subscription requests
555 * @param subref (SV *) to call on subscription requests from legacy users
556 * @param subref (SV *) to call on connection errors
557 * @param subref (SV *) to call when connections have succeeded
558 * @param subref (SV *) to call on incoming chatstate (optional)
560 void thrasher_wrapper_init (SV *timeout_add_cb,
561 SV *input_add_cb,
562 SV *source_remove_cb,
563 SV *incoming_msg_cb,
564 SV *presence_in_cb,
565 SV *subscription_add_cb,
566 SV *legacy_user_add_user_cb,
567 SV *connection_error_cb,
568 SV *connection_cb,
569 ...)
571 va_list argp;
572 va_start(argp, connection_cb);
573 SV* incoming_chatstate_cb = va_arg(argp, SV*);
575 g_return_if_fail(timeout_add_cb);
576 g_return_if_fail(input_add_cb);
577 g_return_if_fail(source_remove_cb);
578 g_return_if_fail(incoming_msg_cb);
579 g_return_if_fail(presence_in_cb);
580 g_return_if_fail(subscription_add_cb);
581 g_return_if_fail(legacy_user_add_user_cb);
582 g_return_if_fail(connection_error_cb);
583 g_return_if_fail(connection_cb);
585 /* Set the bindings for our function table */
586 callback_funcs = g_hash_table_new_full(g_direct_hash,
587 g_direct_equal,
588 destroy_record_key,
589 NULL);
591 /* Create the lookup table so we know what to do when Perl triggers a remove */
592 perl_key_lookup = g_hash_table_new(g_int_hash, g_int_equal);
594 /* Validate callback, this burns up if it fails so no
595 * need to check for errors!
597 validate_callback(timeout_add_cb);
598 validate_callback(input_add_cb);
599 validate_callback(source_remove_cb);
600 validate_callback(incoming_msg_cb);
601 validate_callback(presence_in_cb);
602 validate_callback(subscription_add_cb);
603 validate_callback(legacy_user_add_user_cb);
604 validate_callback(connection_error_cb);
605 validate_callback(connection_cb);
606 if (incoming_chatstate_cb) {
607 validate_callback(incoming_chatstate_cb);
610 dSP;
612 ENTER;
613 SAVETMPS;
615 PUSHMARK(SP);
616 PUTBACK;
618 callback.timeout_add = newSVsv(timeout_add_cb);
619 callback.input_add = newSVsv(input_add_cb);
620 callback.source_remove = newSVsv(source_remove_cb);
621 callback.incoming_msg = newSVsv(incoming_msg_cb);
622 callback.presence_in = newSVsv(presence_in_cb);
623 callback.subscription_add = newSVsv(subscription_add_cb);
624 callback.legacy_user_add_user = newSVsv(legacy_user_add_user_cb);
625 callback.connection_error = newSVsv(connection_error_cb);
626 callback.connection = newSVsv(connection_cb);
627 if (incoming_chatstate_cb) {
628 callback.incoming_chatstate = newSVsv(incoming_chatstate_cb);
630 else {
631 callback.incoming_chatstate = 0;
634 SPAGAIN;
635 PUTBACK;
637 FREETMPS;
638 LEAVE;
642 * @brief Initialize our file transfer wrapper
643 * @param subref (SV *) to call for recv_accept
644 * @param subref (SV *) to call for recv_start
645 * @param subref (SV *) to call for recv_cancel
646 * @param subref (SV *) to call for recv_complete
647 * @param subref (SV *) to call for send_accept
648 * @param subref (SV *) to call for send_start
649 * @param subref (SV *) to call for send_cancel
650 * @param subref (SV *) to call for send_complete
651 * @param subref (SV *) to call for ui_read. Must return (buffer, size).
652 * @param subref (SV *) to call for ui_write
653 * @param subref (SV *) to call for ui_data_not_sent
655 void thrasher_wrapper_ft_init(SV *ft_recv_request_cb,
656 SV *ft_recv_accept_cb,
657 SV *ft_recv_start_cb,
658 SV *ft_recv_cancel_cb,
659 SV *ft_recv_complete_cb,
660 SV *ft_send_accept_cb,
661 SV *ft_send_start_cb,
662 SV *ft_send_cancel_cb,
663 SV *ft_send_complete_cb,
664 SV *ft_read_cb,
665 SV *ft_write_cb,
666 SV *ft_data_not_sent_cb)
668 g_return_if_fail(ft_recv_request_cb);
669 g_return_if_fail(ft_recv_accept_cb);
670 g_return_if_fail(ft_recv_start_cb);
671 g_return_if_fail(ft_recv_cancel_cb);
672 g_return_if_fail(ft_recv_complete_cb);
673 g_return_if_fail(ft_send_accept_cb);
674 g_return_if_fail(ft_send_start_cb);
675 g_return_if_fail(ft_send_cancel_cb);
676 g_return_if_fail(ft_send_complete_cb);
677 g_return_if_fail(ft_read_cb);
678 g_return_if_fail(ft_write_cb);
679 g_return_if_fail(ft_data_not_sent_cb);
681 // This assumes thrasher_wrapper_init was already called.
682 g_return_if_fail(callback_funcs);
684 validate_callback(ft_recv_request_cb);
685 validate_callback(ft_recv_accept_cb);
686 validate_callback(ft_recv_start_cb);
687 validate_callback(ft_recv_cancel_cb);
688 validate_callback(ft_recv_complete_cb);
689 validate_callback(ft_send_accept_cb);
690 validate_callback(ft_send_start_cb);
691 validate_callback(ft_send_cancel_cb);
692 validate_callback(ft_send_complete_cb);
693 validate_callback(ft_read_cb);
694 validate_callback(ft_write_cb);
695 validate_callback(ft_data_not_sent_cb);
697 dSP;
699 ENTER;
700 SAVETMPS;
702 PUSHMARK(SP);
703 PUTBACK;
705 ft_callback.ft_recv_request_cb = newSVsv(ft_recv_request_cb);
706 ft_callback.ft_recv_accept_cb = newSVsv(ft_recv_accept_cb);
707 ft_callback.ft_recv_start_cb = newSVsv(ft_recv_start_cb);
708 ft_callback.ft_recv_cancel_cb = newSVsv(ft_recv_cancel_cb);
709 ft_callback.ft_recv_complete_cb = newSVsv(ft_recv_complete_cb);
710 ft_callback.ft_send_accept_cb = newSVsv(ft_send_accept_cb);
711 ft_callback.ft_send_start_cb = newSVsv(ft_send_start_cb);
712 ft_callback.ft_send_cancel_cb = newSVsv(ft_send_cancel_cb);
713 ft_callback.ft_send_complete_cb = newSVsv(ft_send_complete_cb);
714 ft_callback.ft_read_cb = newSVsv(ft_read_cb);
715 ft_callback.ft_write_cb = newSVsv(ft_write_cb);
716 ft_callback.ft_data_not_sent_cb = newSVsv(ft_data_not_sent_cb);
718 SPAGAIN;
719 PUTBACK;
721 FREETMPS;
722 LEAVE;
728 * @brief Validate then call the given callback pointer.
729 * @param key a hash key for the local function callback table.
730 * @return boolean as int (0 || 1) on success
732 int func_call(gpointer key)
734 /* @exception key cannot be NULL */
735 g_return_val_if_fail(key != NULL,TFALSE);
737 gpointer orig_key = NULL;
738 hash_record *record = NULL;
739 int (*timeout_add_caller)(gpointer);
740 int (*input_add_caller)(gpointer, gint, PurpleInputCondition);
741 int ret = 0;
743 /* Crazy voodoo madness!
744 * We're using direct pointers as keys and a struct at the
745 * end of the pointer, so no need to get any values.
747 if (g_hash_table_lookup_extended(callback_funcs, key, &orig_key, NULL))
749 record = orig_key;
751 if (record->type == TIMEOUT_ADD)
753 timeout_add_caller = record->function;
754 ret = (timeout_add_caller)(record->data);
757 else if (record->type == INPUT_ADD)
759 input_add_caller = record->function;
760 (input_add_caller)(record->data, record->fd,
761 record->cond);
762 // these functions are void
763 ret = TRUE;
766 if (ret == FALSE)
768 /* We're never gonna be calling this again, so get rid of it */
769 g_hash_table_remove(callback_funcs, record);
772 return (ret);
775 else
777 purple_debug_info("thrasher", "Callback for [%p] does not exist!\n", key);
780 return(TFALSE);
785 * @brief Wraps Glib::Source->remove
786 * @param key to return to the Perl end
787 * @return Success of call, including the success of the callback
789 gboolean thrasher_wrapper_call_source_remove (long key)
791 long ret = FALSE;
793 /* @exception key cannot be NULL */
794 g_return_val_if_fail(callback.source_remove, 0);
796 /* @exception callback.source_remove cannot be NULL */
797 g_return_val_if_fail(key != TNULL, 0);
799 dSP;
801 ENTER;
802 SAVETMPS;
804 PUSHMARK(sp);
806 /* Push the key onto the stack */
807 XPUSHs(sv_2mortal(newSViv(key)));
809 PUTBACK;
811 call_sv(callback.source_remove, G_EVAL | G_SCALAR);
813 SPAGAIN;
815 ret = POPl;
817 FREETMPS;
818 PUTBACK;
820 LEAVE;
822 /* Remove call from lookup table */
823 g_hash_table_remove(perl_key_lookup, (gpointer)&key);
825 return ret;
830 * PurpleInputCondition is a bitwise OR of read/write events
831 * PurpleInputFunction is a callback to a guint function with the args gpointer, gint, and PurpleInoutCondition
834 * @brief Wraps Glib::IO->add_watch
835 * @param File descriptor
836 * @param Condition to trigger on
837 * @param Nested callback
838 * @param Data to pass to callback
839 * @return Hash key
841 long thrasher_wrapper_set_input_add (guint fd, PurpleInputCondition cond, PurpleInputFunction function, gpointer data)
843 g_return_val_if_fail(callback.input_add, 0);
844 g_return_val_if_fail(function != NULL, 0);
846 long key = 0;
848 hash_record *record = (hash_record *)malloc(sizeof(hash_record));
850 record->type = INPUT_ADD;
851 record->function = function;
852 record->data = data;
853 record->cond = cond;
854 record->fd = fd;
856 g_hash_table_insert(callback_funcs, record, NULL);
858 key = PTR_TO_LONG(record);
859 g_return_val_if_fail(key != 0, 0);
861 dSP;
863 ENTER;
864 SAVETMPS;
867 SV *pc = NULL;
868 CV *temp_cv = NULL;
869 SV *svkey = NULL;
872 svkey = newSViv(key);
873 temp_cv = get_cv(trigger_input_func_name, FALSE);
875 if (temp_cv)
877 pc = newRV((SV*)temp_cv);
879 /* Ok, this is hairy, first we push the vars we want to send to the
880 * timeout_add Perl callback. Next we push the var we want to give
881 * back to ourselves via trigger_input_func.
884 PUSHMARK(sp);
886 XPUSHs(sv_2mortal(newSViv(fd)));
887 XPUSHs(sv_2mortal(newSViv(cond)));
888 XPUSHs(sv_2mortal(pc));
889 XPUSHs(sv_2mortal(svkey));
891 PUTBACK;
893 call_sv(callback.input_add, G_EVAL | G_SCALAR);
894 SPAGAIN;
896 /* Tweak our internal key to an external key */
897 key = POPl;
899 else
901 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
904 g_hash_table_insert(perl_key_lookup, (gpointer)&key, record);
906 return key;
911 * @brief Wraps Glib::Timeout->add
912 * @param interval in milliseconds
913 * @param callback
914 * @param data to pass to the callback
915 * @return Key for the internal timeout_add function hash. (Should this really be returning anything?!)
917 long thrasher_wrapper_set_timeout_add (guint interval, GSourceFunc function, gpointer data)
919 /* @exception callback.timeout_add cannot be NULL */
920 g_return_val_if_fail(callback.timeout_add, 0);
921 g_return_val_if_fail(function != NULL, TFALSE);
923 long key = 0;
925 hash_record *record = (hash_record *)malloc(sizeof(hash_record *));
927 record->type = TIMEOUT_ADD;
928 record->function = function;
929 record->data = data;
931 g_hash_table_insert(callback_funcs, record, NULL);
933 key = PTR_TO_LONG(record);
934 g_return_val_if_fail(key != 0, 0);
936 dSP;
938 ENTER;
939 SAVETMPS;
942 SV *pc = NULL;
943 CV *temp_cv = NULL;
944 SV *svkey = NULL;
946 svkey = newSViv(key);
947 temp_cv = get_cv(trigger_timeout_func_name, FALSE);
949 if (temp_cv)
951 pc = newRV((SV*)temp_cv);
953 /* Ok, this is hairy, first we push the vars we want to send to the
954 * timeout_add Perl callback. Next we push the var we want to give
955 * back to ourselves via trigger_input_func.
958 PUSHMARK(sp);
960 XPUSHs(sv_2mortal(newSViv(interval)));
961 XPUSHs(sv_2mortal(pc));
962 XPUSHs(sv_2mortal(svkey));
964 PUTBACK;
966 call_sv(callback.timeout_add, G_EVAL | G_SCALAR);
968 SPAGAIN;
970 /* Tweak our internal key to an external key */
971 key = POPl;
973 else
975 croak("We've got serious problems, cannot create CV* to [%s]", trigger_input_func_name);
978 SPAGAIN;
980 FREETMPS;
981 PUTBACK;
983 LEAVE;
985 return key;
989 * @brief Send a @p message out via callback.incoming_msg
990 * @param jid user the @p message is being sent to
991 * @param sender user name of the @p message sender
992 * @param alias alternate user name of the @p sender (may be NULL)
993 * @param message string being sent to @p jid
994 * @return 1 on success, 0 on failure
996 int thrasher_wrapper_incoming_msg (const char *jid,
997 const char *sender,
998 const char *alias,
999 const char *message,
1000 PurpleMessageFlags flags)
1002 int ret = 0;
1004 /* @exception jid, sender, and message cannot be NULL */
1005 g_return_val_if_fail(jid != NULL, 0);
1006 g_return_val_if_fail(sender != NULL, 0);
1007 g_return_val_if_fail(message != NULL, 0);
1009 /* @exception bail if our callback doesn't exist */
1010 g_return_val_if_fail(callback.incoming_msg != NULL, 0);
1012 dSP;
1014 ENTER;
1015 SAVETMPS;
1017 PUSHMARK(sp);
1019 /* Push the args onto the stack */
1020 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
1021 XPUSHs(sv_2mortal(newSVpvn(sender, strlen(sender))));
1022 XPUSHs(sv_2mortal(newSVpvn(alias, strlen(alias))));
1023 XPUSHs(sv_2mortal(newSVpvn(message, strlen(message))));
1024 XPUSHs(sv_2mortal(newSViv(flags)));
1026 PUTBACK;
1028 call_sv(callback.incoming_msg, G_EVAL | G_SCALAR);
1030 SPAGAIN;
1032 ret = POPl;
1034 FREETMPS;
1035 PUTBACK;
1037 LEAVE;
1039 return ret;
1043 * @brief Forwards the 'connection' event, that authentication has
1044 * completed
1045 * @param jid user who has successfully connected
1047 int thrasher_wrapper_connection(const char *jid)
1049 int ret = 0;
1051 /* @exception jid, sender, and message cannot be NULL */
1052 g_return_val_if_fail(jid != NULL, 0);
1054 /* @exception bail if our callback doesn't exist */
1055 g_return_val_if_fail(callback.connection != NULL, 0);
1057 dSP;
1059 ENTER;
1060 SAVETMPS;
1062 PUSHMARK(sp);
1064 /* Push the args onto the stack */
1065 XPUSHs(sv_2mortal(newSVpvn(jid, strlen(jid))));
1067 PUTBACK;
1069 call_sv(callback.connection, G_EVAL | G_SCALAR);
1071 SPAGAIN;
1073 ret = POPl;
1075 FREETMPS;
1076 PUTBACK;
1078 LEAVE;
1080 return ret;
1083 SV *clean_newSVpv(const char *inp)
1085 if (inp)
1086 return sv_2mortal(newSVpv(inp, 0));
1087 else
1088 return sv_2mortal(&PL_sv_undef);
1093 * @brief Forward presence information via callback.presence_in
1094 * @param jid user the @p status is being sent to
1095 * @param sender user name of the @p status sender
1096 * @param alias alternate user name of the @p sender (may be NULL)
1097 * @param group which the @p sender belongs to
1098 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1099 * @param message string being sent to @p jid (may be NULL)
1100 * @return 1 on success, 0 on failure (wrapped Perl)
1102 int thrasher_wrapper_presence_in(const char *jid,
1103 const char *sender,
1104 const char *alias,
1105 const char *group,
1106 const PurpleStatusPrimitive status,
1107 const char *message)
1109 int ret = 0;
1111 /* @exception jid, sender, and group cannot be NULL */
1112 g_return_val_if_fail(jid != NULL, 0);
1113 g_return_val_if_fail(sender != NULL, 0);
1114 g_return_val_if_fail(group != NULL, 0);
1116 /* @exception bail if our callback doesn't exist */
1117 g_return_val_if_fail(callback.presence_in != NULL, 0);
1119 dSP;
1121 ENTER;
1122 SAVETMPS;
1124 PUSHMARK(sp);
1126 /* Push the args onto the stack */
1127 XPUSHs(clean_newSVpv(jid));
1128 XPUSHs(clean_newSVpv(sender));
1129 XPUSHs(clean_newSVpv(alias));
1130 XPUSHs(clean_newSVpv(group));
1131 XPUSHs(sv_2mortal(newSViv(status)));
1132 XPUSHs(clean_newSVpv(message));
1134 PUTBACK;
1136 call_sv(callback.presence_in, G_EVAL | G_SCALAR);
1138 SPAGAIN;
1140 ret = POPl;
1142 FREETMPS;
1143 PUTBACK;
1145 LEAVE;
1147 return ret;
1150 int thrasher_wrapper_legacy_user_add_user(const char *jid,
1151 const char *sender) {
1152 int ret = 0;
1154 g_return_val_if_fail(jid, 0);
1155 g_return_val_if_fail(sender, 0);
1157 g_return_val_if_fail(callback.legacy_user_add_user, 0);
1159 dSP;
1161 ENTER;
1162 SAVETMPS;
1164 PUSHMARK(sp);
1166 XPUSHs(clean_newSVpv(jid));
1167 XPUSHs(clean_newSVpv(sender));
1169 PUTBACK;
1171 call_sv(callback.legacy_user_add_user, G_EVAL | G_SCALAR);
1173 SPAGAIN;
1175 ret = POPl;
1177 FREETMPS;
1178 PUTBACK;
1180 LEAVE;
1182 return ret;
1186 * @brief Forward subscription request information via callback.subscription_add
1187 * @param jid user the @p status is being sent to
1188 * @param sender user name of the requester
1189 * @param status PurpleStatusPrimitive (guint) being sent to @p jid
1190 * @return 1 on success, 0 on failure (wrapped Perl)
1192 int thrasher_wrapper_subscription_add(const char *jid,
1193 const char *sender,
1194 guint status)
1196 int ret = 0;
1198 /* @exception jid, sender, and group cannot be NULL */
1199 g_return_val_if_fail(jid != NULL, 0);
1200 g_return_val_if_fail(sender != NULL, 0);
1202 /* @exception bail if our callback doesn't exist */
1203 g_return_val_if_fail(callback.subscription_add != NULL, 0);
1205 dSP;
1207 ENTER;
1208 SAVETMPS;
1210 PUSHMARK(sp);
1212 /* Push the args onto the stack */
1213 XPUSHs(clean_newSVpv(jid));
1214 XPUSHs(clean_newSVpv(sender));
1215 XPUSHs(sv_2mortal(newSViv(status)));
1217 PUTBACK;
1219 call_sv(callback.subscription_add, G_EVAL | G_SCALAR);
1221 SPAGAIN;
1223 ret = POPl;
1225 FREETMPS;
1226 PUTBACK;
1228 LEAVE;
1230 return ret;
1234 * @brief Forward connection error messages via callback.connection_error
1235 * @param jid user the @p error_code is being sent to
1236 * @param error_code PurpleConnectionError (guint) being sent to @p jid
1237 * @param message a error message (may be NULL)
1238 * @return 1 on success, 0 on failure (wrapped Perl)
1240 int thrasher_wrapper_connection_error(const char *jid,
1241 const guint error_code,
1242 const char *message)
1244 int ret = 0;
1246 // If the JID is null, pick it up off of
1247 // thrasher.c:current_login_jid, per the comment in that file.
1248 if (jid == NULL) {
1249 purple_debug_info("thrasher", "had to retrieve the jid from mem\n");
1250 jid = get_current_login_jid();
1253 // If we're in the middle of a connection, pick up that we have an error.
1254 set_got_error(1);
1256 g_return_val_if_fail(jid, 0);
1259 /* @exception error_code cannot be less than zero or greater than PURPLE_CONNECTION_ERROR_OTHER_ERROR */
1260 g_return_val_if_fail(error_code >= 0, ret);
1261 g_return_val_if_fail(error_code <= PURPLE_CONNECTION_ERROR_OTHER_ERROR, ret);
1263 dSP;
1265 ENTER;
1266 SAVETMPS;
1268 PUSHMARK(sp);
1270 /* Push the args onto the stack */
1271 XPUSHs(clean_newSVpv(jid));
1272 XPUSHs(sv_2mortal(newSViv(error_code)));
1273 XPUSHs(clean_newSVpv(message));
1275 PUTBACK;
1277 call_sv(callback.connection_error, G_EVAL | G_SCALAR);
1279 SPAGAIN;
1281 ret = POPl;
1283 FREETMPS;
1284 PUTBACK;
1286 LEAVE;
1288 // Remove the account from our current active set
1289 thrasher_remove_account(jid);
1291 return ret;
1294 void thrasher_remove_account (const char *jid)
1296 PurpleAccount *account;
1298 g_return_if_fail(jid != NULL);
1300 purple_debug_info("thrasher", "Removing active account for %s\n",
1301 jid);
1303 account = g_hash_table_lookup(accts, jid);
1304 if (account != NULL)
1306 g_hash_table_remove(accts, jid);
1311 * @brief Forwards typing notification state changes from libpurple buddies.
1312 * @param pa Account receiving the notification
1313 * @param who Buddy name sending the notification
1314 * @param state
1316 void thrasher_wrapper_incoming_chatstate(PurpleAccount* pa,
1317 const char* who,
1318 PurpleTypingState state) {
1319 /* @exception pa and who cannot be NULL */
1320 g_return_if_fail(pa != NULL);
1321 g_return_if_fail(who != NULL);
1323 /* bail if Perl-side doesn't implement this */
1324 if (! callback.incoming_chatstate) {
1325 return;
1328 gchar* jid = thrasher_account_get_jid(pa);
1329 /* @exception bail if account has no JID */
1330 g_return_if_fail(jid != NULL);
1332 dSP;
1334 ENTER;
1335 SAVETMPS;
1337 PUSHMARK(sp);
1339 /* Push the args onto the stack */
1340 XPUSHs(clean_newSVpv(jid));
1341 XPUSHs(clean_newSVpv(who));
1342 XPUSHs(sv_2mortal(newSViv(state)));
1344 PUTBACK;
1346 call_sv(callback.incoming_chatstate, G_EVAL | G_SCALAR);
1348 SPAGAIN;
1350 FREETMPS;
1351 PUTBACK;
1353 LEAVE;
1357 * @begin External trigger to flush the internal function tables
1359 void thrasher_wrapper_destructor ()
1361 g_hash_table_destroy(callback_funcs);
1362 g_hash_table_destroy(perl_key_lookup);
1365 size_t
1366 thrasher_wrapper_send_file(const char *jid,
1367 const char *who,
1368 char *filename,
1369 size_t size,
1370 char *desc) {
1371 g_return_val_if_fail(jid, 0);
1372 g_return_val_if_fail(who, 0);
1374 purple_debug_info("thrasher ft",
1375 "Sending file %s -> %s\n",
1376 jid, who);
1378 PurpleAccount *account = g_hash_table_lookup(accts, jid);
1380 g_return_val_if_fail(account, 0);
1382 return thrasher_send_file(account, who, filename, size, desc);
1385 void
1386 thrasher_action_ft_ui_ready(size_t id) {
1387 PurpleXfer* xfer = get_xfer_by_id(id);
1388 g_return_if_fail(xfer);
1389 purple_xfer_ui_ready(xfer);
1390 /* HACK: READY_PRPL is usually not set (or was cleared) right now,
1391 * and some prpls don't appear to set it correctly on repeats.
1392 * Spam prpl_ready to force a do_transfer anyway.
1394 * Using a separate watch on the remote xfer->fd would avoid some
1395 * EAGAINs. I'd be happy to be proven wrong, but testing found no
1396 * significant benefit to Thrasher in that.
1398 purple_xfer_prpl_ready(xfer);
1402 * @brief Called on libpurple file-send-start signal
1403 * @param id of Thrasher file transfer structure.
1406 thrasher_wrapper_ft_send_start(guint id) {
1407 int ret = 0;
1409 /* @exception bail if our callback doesn't exist */
1410 g_return_val_if_fail(ft_callback.ft_send_start_cb != NULL, 0);
1412 dSP;
1414 ENTER;
1415 SAVETMPS;
1417 PUSHMARK(sp);
1419 /* Push the args onto the stack */
1420 XPUSHs(sv_2mortal(newSViv(id)));
1422 PUTBACK;
1424 call_sv(ft_callback.ft_send_start_cb, G_EVAL | G_SCALAR);
1426 SPAGAIN;
1428 ret = POPl;
1430 FREETMPS;
1431 PUTBACK;
1433 LEAVE;
1435 return ret;
1439 * @brief Called on libpurple file-send-cancel signal
1440 * @param id of Thrasher file transfer structure.
1442 void
1443 thrasher_wrapper_ft_send_cancel(guint id) {
1444 /* @exception bail if our callback doesn't exist */
1445 g_return_if_fail(ft_callback.ft_send_cancel_cb != NULL);
1447 dSP;
1449 ENTER;
1450 SAVETMPS;
1452 PUSHMARK(sp);
1454 /* Push the args onto the stack */
1455 XPUSHs(sv_2mortal(newSViv(id)));
1457 PUTBACK;
1459 call_sv(ft_callback.ft_send_cancel_cb, G_EVAL | G_SCALAR);
1461 SPAGAIN;
1463 FREETMPS;
1464 PUTBACK;
1466 LEAVE;
1468 return;
1472 * @brief Called on libpurple file-send-complete signal
1473 * @param id of Thrasher file transfer structure.
1475 void
1476 thrasher_wrapper_ft_send_complete(guint id) {
1477 /* @exception bail if our callback doesn't exist */
1478 g_return_if_fail(ft_callback.ft_send_complete_cb != NULL);
1480 dSP;
1482 ENTER;
1483 SAVETMPS;
1485 PUSHMARK(sp);
1487 /* Push the args onto the stack */
1488 XPUSHs(sv_2mortal(newSViv(id)));
1490 PUTBACK;
1492 call_sv(ft_callback.ft_send_complete_cb, G_EVAL | G_SCALAR);
1494 SPAGAIN;
1496 FREETMPS;
1497 PUTBACK;
1499 LEAVE;
1501 return;
1504 gssize
1505 thrasher_wrapper_ft_read(guint id,
1506 guchar **buffer,
1507 gssize size) {
1508 int read_sz = -1;
1509 int count = 0;
1511 /* @exception bail if our callback doesn't exist */
1512 g_return_val_if_fail(ft_callback.ft_read_cb != NULL, 0);
1514 dSP;
1516 ENTER;
1517 SAVETMPS;
1519 PUSHMARK(sp);
1521 /* Push the args onto the stack */
1522 XPUSHs(sv_2mortal(newSViv(id)));
1523 XPUSHs(sv_2mortal(newSViv(size)));
1525 PUTBACK;
1527 count = call_sv(ft_callback.ft_read_cb, G_EVAL | G_ARRAY);
1529 SPAGAIN;
1531 if (count == 2) {
1532 read_sz = POPl;
1533 void* read = POPpbytex;
1534 *buffer = malloc(read_sz);
1535 /* This buffer is free'd by libpurple. */
1536 memcpy(*buffer, read, read_sz);
1538 else {
1539 *buffer = NULL;
1540 read_sz = -1;
1543 FREETMPS;
1544 PUTBACK;
1546 LEAVE;
1548 return read_sz;
1551 void
1552 thrasher_wrapper_ft_data_not_sent(guint id,
1553 const char *buffer,
1554 gsize size) {
1555 /* @exception buffer cannot be NULL */
1556 g_return_if_fail(buffer != NULL);
1558 /* @exception bail if our callback doesn't exist */
1559 g_return_if_fail(ft_callback.ft_data_not_sent_cb != NULL);
1561 dSP;
1563 ENTER;
1564 SAVETMPS;
1566 PUSHMARK(sp);
1568 /* Push the args onto the stack */
1569 XPUSHs(sv_2mortal(newSViv(id)));
1570 /* buffer is not null-terminated. */
1571 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1573 PUTBACK;
1575 call_sv(ft_callback.ft_data_not_sent_cb, G_EVAL | G_SCALAR);
1577 SPAGAIN;
1579 FREETMPS;
1580 PUTBACK;
1582 LEAVE;
1584 return;
1588 * @brief ask user's permission for ft and set up bytestream
1589 * @param xfer a filled-in PURPLE_XFER_RECEIVE PurpleXfer.
1590 * @param filename suggested local filename
1591 * @return nothing; use thrasher_action_ft_recv_request_respond when ready
1593 void
1594 thrasher_wrapper_ft_recv_request(PurpleXfer *xfer,
1595 const char* filename) {
1596 /* @exception xfer cannot be NULL */
1597 g_return_if_fail(xfer != NULL);
1599 /* @exception bail if our callback doesn't exist */
1600 g_return_if_fail(ft_callback.ft_recv_request_cb != NULL);
1602 dSP;
1604 ENTER;
1605 SAVETMPS;
1607 PUSHMARK(sp);
1609 /* Push the args onto the stack */
1610 guint id = get_id_by_xfer(xfer);
1611 XPUSHs(sv_2mortal(newSViv(id)));
1612 PurpleAccount* pa = purple_xfer_get_account(xfer);
1613 gchar* jid = thrasher_account_get_jid(pa);
1614 XPUSHs(clean_newSVpv(jid));
1615 const char* who = purple_xfer_get_remote_user(xfer);
1616 XPUSHs(clean_newSVpv(who));
1617 XPUSHs(clean_newSVpv(filename));
1618 size_t size = purple_xfer_get_size(xfer);
1619 XPUSHs(sv_2mortal(newSViv(size)));
1621 PUTBACK;
1623 call_sv(ft_callback.ft_recv_request_cb, G_EVAL | G_SCALAR);
1625 SPAGAIN;
1627 FREETMPS;
1628 PUTBACK;
1630 LEAVE;
1632 return;
1635 void
1636 thrasher_action_ft_recv_request_respond(size_t id,
1637 unsigned int accept) {
1638 thrasher_xfer_recv_request_responder(id, accept);
1641 gssize
1642 thrasher_wrapper_ft_write(guint id,
1643 const char *buffer,
1644 gssize size) {
1645 int written_sz = -1;
1647 /* @exception bail if our callback doesn't exist */
1648 g_return_val_if_fail(ft_callback.ft_write_cb != NULL, 0);
1650 dSP;
1652 ENTER;
1653 SAVETMPS;
1655 PUSHMARK(sp);
1657 /* Push the args onto the stack */
1658 XPUSHs(sv_2mortal(newSViv(id)));
1659 /* buffer is not null-terminated. */
1660 XPUSHs(sv_2mortal(newSVpvn(buffer, size)));
1662 PUTBACK;
1664 call_sv(ft_callback.ft_write_cb, G_EVAL | G_ARRAY);
1666 SPAGAIN;
1668 written_sz = POPl;
1670 FREETMPS;
1671 PUTBACK;
1673 LEAVE;
1675 return written_sz;
1679 * @brief Called on libpurple file-recv-cancel signal
1680 * @param id of Thrasher file transfer structure.
1682 void
1683 thrasher_wrapper_ft_recv_cancel(guint id) {
1684 /* @exception bail if our callback doesn't exist */
1685 g_return_if_fail(ft_callback.ft_recv_cancel_cb != NULL);
1687 dSP;
1689 ENTER;
1690 SAVETMPS;
1692 PUSHMARK(sp);
1694 /* Push the args onto the stack */
1695 XPUSHs(sv_2mortal(newSViv(id)));
1697 PUTBACK;
1699 call_sv(ft_callback.ft_recv_cancel_cb, G_EVAL | G_SCALAR);
1701 SPAGAIN;
1703 FREETMPS;
1704 PUTBACK;
1706 LEAVE;
1708 return;
1712 * @brief Called on libpurple file-recv-complete signal
1713 * @param id of Thrasher file transfer structure.
1715 void
1716 thrasher_wrapper_ft_recv_complete(guint id) {
1717 /* @exception bail if our callback doesn't exist */
1718 g_return_if_fail(ft_callback.ft_recv_complete_cb != NULL);
1720 dSP;
1722 ENTER;
1723 SAVETMPS;
1725 PUSHMARK(sp);
1727 /* Push the args onto the stack */
1728 XPUSHs(sv_2mortal(newSViv(id)));
1730 PUTBACK;
1732 call_sv(ft_callback.ft_recv_complete_cb, G_EVAL | G_SCALAR);
1734 SPAGAIN;
1736 FREETMPS;
1737 PUTBACK;
1739 LEAVE;
1741 return;
1744 void
1745 thrasher_action_ft_cancel_local(size_t id) {
1746 PurpleXfer* xfer = get_xfer_by_id(id);
1747 purple_xfer_cancel_local(xfer);
1750 #ifdef TH_DEBUG
1751 /*********************************************************
1753 * Only debugging stuff beyond this point!
1755 *********************************************************/
1756 gboolean foo_callback (void *data);
1757 gboolean foo_callback2 (void *data);
1758 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond);
1759 long thrasher_wrapper_trigger_timeout_add (void);
1760 long thrasher_wrapper_trigger_timeout_add2 (void);
1761 long thrasher_wrapper_trigger_timeout_add3 (void);
1762 long thrasher_wrapper_trigger_input_add (void);
1763 void thrasher_wrapper_dump_calls (void);
1764 void spit_func_info (gpointer key, gpointer value, gpointer user_data);
1767 gboolean
1768 foo_callback (void *data)
1770 printf("!!!!!!Calling a callback w/ data [%s]!!!!\n", (char *)data);
1771 return TRUE;
1775 int cntr = 9;
1778 gboolean
1779 foo_callback2 (void *data)
1781 printf("will die in [%d] cycles\n", cntr);
1783 if (cntr--)
1784 return TRUE;
1785 else
1786 return FALSE;
1789 void foo_callback3 (gpointer data, gint fd, PurpleInputCondition cond)
1791 printf("data [%p]\tfd [%d]\tcond [%d]\n", data, fd, cond);
1794 /* Test functions to allow an external caller to trigger internal actions */
1795 long thrasher_wrapper_trigger_timeout_add ()
1797 printf("trigger_timeout_add called\n");
1798 return thrasher_wrapper_set_timeout_add(1234, foo_callback, "YAY! test data");
1802 long thrasher_wrapper_trigger_timeout_add2 ()
1804 printf("trigger_timeout_add2 called\n");
1805 return thrasher_wrapper_set_timeout_add(1867, foo_callback, "test data says what?");
1808 long thrasher_wrapper_trigger_timeout_add3 ()
1810 printf("trigger_timeout_add3 called\n");
1811 return thrasher_wrapper_set_timeout_add(200, foo_callback2, "this shoudl fail shortly");
1814 long thrasher_wrapper_trigger_input_add ()
1816 printf("trigger_input_add called\n");
1818 int fd = open("/tmp/testfoo", O_RDWR);
1819 PurpleInputCondition cond;
1820 cond = PURPLE_INPUT_WRITE;
1821 long face = 0;
1822 printf("HAMMERTIME\n");
1823 face = thrasher_wrapper_set_input_add(fd, cond, foo_callback3, "double plus win ungood");
1825 return face;
1828 int thrasher_wrapper_trigger_timeout_remove (long key)
1830 printf("trigger_timeout_remove called\n");
1831 /* We currently have no way to correlate external keys to internal keys... */
1832 return !thrasher_wrapper_call_source_remove(key);
1836 void spit_func_info (gpointer key, gpointer value, gpointer user_data)
1838 hash_record *record;
1839 printf("key [%p]\tvalue [%p]\n", key, value);
1841 record = key;
1843 if (record->type == INPUT_ADD)
1845 printf("\tINPUT_ADD\n\tfunction [%p]\n\tdata [%p]\n\tcond [%d]\n\tfd [%d]\n", record->function, record->data, record->cond, record->fd);
1848 else if (record->type == TIMEOUT_ADD)
1851 printf("\tTIMEOUT_ADD\n\tfunction [%p]\n\tdata [%p]\n", record->function, record->data);
1854 else
1856 printf("record type [%d] is unrecognized!\n", record->type);
1860 void thrasher_wrapper_dump_calls ()
1862 g_hash_table_foreach(callback_funcs, spit_func_info, NULL);
1865 #endif /* TH_DEBUG */