xmlnode * _h_elim_set_prefs ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); elim_ping(); xmlnode *rval = xnode_new( "alist" ); xmlnode *set = xnode_new( "alist" ); GHashTable *prefs = ALIST_VAL_ALIST( args, "prefs" ); SEXP_VALUE *PREFS = ALIST_VAL ( args, "prefs" ); AL_NODE( rval, "prefs", set ); fprintf( stderr, "hash: %p; sexp: %p\n", prefs, PREFS ); if( prefs ) { GList *key = NULL; GList *keys = g_hash_table_get_keys( prefs ); fprintf( stderr, "keys: %p", keys ); for( key = keys; key; key = key->next ) { gboolean done = TRUE; const char *pref = key->data; PurplePrefType type = purple_prefs_get_type( pref ); switch( type ) { case PURPLE_PREF_BOOLEAN: purple_prefs_set_bool ( pref, ALIST_VAL_BOOL( PREFS, pref ) ); break; case PURPLE_PREF_INT: purple_prefs_set_int ( pref, ALIST_VAL_INT ( PREFS, pref ) ); break; case PURPLE_PREF_STRING: purple_prefs_set_string( pref, ALIST_VAL_STR ( PREFS, pref ) ); break; case PURPLE_PREF_PATH: purple_prefs_set_path ( pref, ALIST_VAL_STR ( PREFS, pref ) ); break; default: done = FALSE; } AL_BOOL( set, pref, done ); } g_list_free( keys ); } sexp_val_free( args ); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_status ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { fprintf(stderr, "(elim-debug entered _h_elim_status)"); ASSERT_ALISTP( args, id, name ); elim_ping(); const char *sid = ALIST_VAL_STR( args, "status-id" ); const char *mesg = ALIST_VAL_STR( args, "status-message" ); signed int _stype = ALIST_VAL_INT( args, "status-type" ); PurpleSavedStatus *status = purple_savedstatus_find( sid ); PurpleStatusPrimitive stype = PURPLE_STATUS_UNSET; // create a new status: if( !status ) { CHECK_STATUS ( args, id, name, stype, _stype ); status = purple_savedstatus_new( sid, stype ); if( status ) { if ( mesg ) purple_savedstatus_set_message( status, mesg ); else if( sid ) purple_savedstatus_set_message( status, sid ); } } else { if( mesg ) purple_savedstatus_set_message( status, mesg ); } if( !status ) { sexp_val_free( args ); return response_error( EINVAL, id, name, "Could not create status" ); } purple_savedstatus_activate( status ); xmlnode *rval = xnode_new( "alist" ); AL_STR ( rval, "status-id" , purple_savedstatus_get_title (status) ); AL_STR ( rval, "status-message", purple_savedstatus_get_message(status) ); AL_ENUM( rval, "status-type" , purple_savedstatus_get_type (status) , ":status-primitive" ); sexp_val_free( args ); fprintf(stderr, "(elim-debug leaving _h_elim_status)"); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_send_file ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); elim_ping(); const char *aname = ALIST_VAL_STR( args, "account-name" ); const char *proto = ALIST_VAL_STR( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR( args, "account-uid" ); PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } PurpleConnection *conn = purple_account_get_connection( acct ); if( !conn ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "account not online" ); } // file can be NULL, but that's Ok, it just triggers a req to the user: const char *b_arg = ALIST_VAL_STRING( args, "recipient" ); const char *file = ALIST_VAL_STRING( args, "filename" ); serv_send_file( conn, b_arg, file ); // bname is a static buf allocated in purple_normalize: don't free it! // also, this means that bname is volatile, so use it immediately after // acquiring it, if you make any calls into libpurple they might invalidate // its state: const char *bname = purple_normalize( acct, b_arg ); xmlnode *rval = xnode_new( "alist" ); AL_PTR( rval, "account-uid" , acct ); AL_STR( rval, "recipient" , bname ); sexp_val_free( args ); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_set_icon ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); elim_ping(); const char *aname = ALIST_VAL_STR ( args, "account-name" ); const char *proto = ALIST_VAL_STR ( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR ( args, "account-uid" ); const char *file = ALIST_VAL_STR ( args, "icon-file" ); GString *img = ALIST_VAL_DATA( args, "icon-data" ); gchar *bytes = NULL; gsize len = 0; gpointer set = NULL; PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } if( !img && file && *file ) { g_file_get_contents( file, &bytes, &len, NULL ); } else if( img ) { bytes = g_memdup( img->str, img->len ); len = img->len; } // the imgstore owns `bytes' after this, don't free it: set = purple_buddy_icons_set_account_icon( acct, (guchar *)bytes, len ); xmlnode *rval = xnode_new( "alist" ); AL_PTR ( rval, "account-uid" , acct ); AL_STR ( rval, "account-name", purple_account_get_username ( acct ) ); AL_STR ( rval, "im-protocol" , purple_account_get_protocol_id( acct ) ); AL_BOOL( rval, "has-icon" , set ? TRUE : FALSE ); sexp_val_free( args ); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_notify_search_callback ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { GList *item = NULL; xmlnode *rval = NULL; const char *sid = ALIST_VAL_STR( args, "search-id" ); CB_HANDLER *cbh = check_cb_data( sid ); NOTIFY_RESP *resp = cbh ? cbh->data : NULL; if( ( (cbh ->type) == CB_TYPE_NOTIFY_SEARCH ) && ( (resp->type) == PURPLE_NOTIFY_SEARCHRESULTS ) ) { GList *row = NULL; PNSB *btn = NULL; PNSR *res = resp->sres; GList *rows = resp->rows; PA *auid = ALIST_VAL_PTR( args, "account-uid" ); PNSRCB cbid = ALIST_VAL_PTR( args, "callback" ); gint ridx = ALIST_VAL_INT( args, "row-index" ); PA *acct = find_acct_by_uid( auid ); PC *gc = purple_account_get_connection( acct ); gint i; fprintf( stderr, "cached row data: %p\n", rows ); for( item = rows; item; item = item->next ) { GList *cell; fprintf( stderr, "|" ); for( cell = item->data; cell; cell = cell->next ) fprintf( stderr, " %s |", (char *)cell->data ); fprintf( stderr, "\n" ); } if( gc && rows ) { for( i = 0, item = rows; item; item = item->next, i++ ) if( i == ridx ) { row = item; break; } for( item = res->buttons; item; item = item->next ) if( ((PNSB *)item->data)->callback == cbid ) { btn = item->data; break; } } fprintf( stderr, "+ search-row: %s\n", (gchar *)row->data ); if( gc && row && btn && btn->callback ) ( btn->callback )( gc, row->data, resp->user_data ); } sexp_val_free( args ); return rval; }
xmlnode * _h_elim_add_buddy ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); elim_ping(); int loopc = 0; const char *aname = ALIST_VAL_STR( args, "account-name" ); const char *proto = ALIST_VAL_STR( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR( args, "account-uid" ); PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } const char *b_arg = ALIST_VAL_STRING( args, "bnode-name" ); const char *bname = purple_normalize( acct, b_arg ); const char *gname = ALIST_VAL_STRING( args, "group" ); if( !gname || !*gname ) gname = "Buddies"; PurpleGroup *group = purple_group_new( gname ); PurpleBuddy *buddy = purple_buddy_new( acct, bname, b_arg ); PurpleBuddy *clone = NULL; //fprintf( stderr, "add-buddy( b: %p, g: %p )\n", buddy, group ); // remove other references to this buddy purple_blist_add_buddy ( buddy, NULL, group, NULL ); purple_account_add_buddy( acct , buddy ); while( ( clone = (PurpleBuddy*)find_blist_node_clone( buddy ) ) ) { if( loopc++ > 99 ) { fprintf( stderr, "ARGH! clone reaping looped: %d\n", loopc ); break; } if( clone == buddy ) { fprintf( stderr, "ARGH! %p not a clone of %p\n", buddy, clone ); break; } fprintf( stderr, "(removing clone %p %ld (of buddy: %p)\n", clone, (long)clone, buddy ); fprintf( stderr, " name : %s\n", purple_buddy_get_name(clone) ); fprintf( stderr, " group: %s)\n", purple_group_get_name( purple_buddy_get_group(clone) ) ); purple_blist_remove_buddy( clone ); } xmlnode *rval = xnode_new( "alist" ); AL_PTR( rval, "account-uid" , acct ); AL_PTR( rval, "bnode-uid" , buddy ); AL_PTR( rval, "group-uid" , group ); AL_STR( rval, "bnode-name" , purple_buddy_get_name ( buddy ) ); AL_STR( rval, "bnode-alias" , purple_buddy_get_alias ( buddy ) ); AL_STR( rval, "account-name", purple_account_get_username ( acct ) ); AL_STR( rval, "im-protocol" , purple_account_get_protocol_id( acct ) ); AL_STR( rval, "group-name" , purple_group_get_name ( group ) ); sexp_val_free( args ); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_message ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { fprintf(stderr, "(elim-debug entered _h_elim_message)"); ASSERT_ALISTP( args, id, name ); elim_ping(); const char *aname = ALIST_VAL_STR( args, "account-name" ); const char *proto = ALIST_VAL_STR( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR( args, "account-uid" ); PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } PurpleConversationType pt = PURPLE_CONV_TYPE_UNKNOWN; gpointer cuid = ALIST_VAL_PTR( args, "conv-uid" ); const char *cname = ALIST_VAL_STR( args, "conv-name" ); PurpleConversation *pc = find_conv_by_acct_uid( acct, cuid ); if ( pc ) pt = purple_conversation_get_type( pc ); else { pt = PURPLE_CONV_TYPE_ANY; pc = purple_find_conversation_with_account( pt, cname, acct ); if( !pc ) { pt = PURPLE_CONV_TYPE_IM; pc = purple_conversation_new( pt, acct, cname ); } else { pt = purple_conversation_get_type( pc ); } } if( !pc ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "new conversation failed" ); } PurpleConvIm *pci = NULL; PurpleConvChat *pcc = NULL; const char *msg = ALIST_VAL_STRING( args, "text" ); char *esc = g_markup_escape_text( msg, -1 ); int len = strlen( esc ); switch( pt ) { case PURPLE_CONV_TYPE_IM: pci = purple_conversation_get_im_data( pc ); purple_conv_im_send( pci, esc ); break; case PURPLE_CONV_TYPE_CHAT: pcc = purple_conversation_get_chat_data( pc ); purple_conv_chat_send( pcc, esc ); break; default: g_free ( esc ); sexp_val_free( args ); return response_error( EINVAL, id, name, "unknown conversation type" ); } xmlnode *rval = xnode_new( "alist" ); AL_INT( rval, "bytes" , len ); AL_PTR( rval, "conv-uid" , pc ); AL_STR( rval, "conv-name", purple_conversation_get_name(pc) ); g_free ( esc ); sexp_val_free( args ); fprintf(stderr, "(elim-debug leaving _h_elim_message)"); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_remove_buddy ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); fprintf( stderr, "(elim-remove-buddy)\n" ); elim_ping(); const char *aname = ALIST_VAL_STR( args, "account-name" ); const char *proto = ALIST_VAL_STR( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR( args, "account-uid" ); PurpleAccount *acct = NULL; gpointer b_uid = ALIST_VAL_PTR( args, "bnode-uid" ); const char *b_arg = NULL; const char *bname = NULL; const char *gname = NULL; PurpleGroup *group = NULL; PurpleBuddy *buddy = NULL; gboolean gone = FALSE; if( b_uid ) { PurpleBlistNodeType type = PURPLE_BLIST_OTHER_NODE; PurpleBlistNode *node = find_blist_node_by_uid( b_uid , TRUE ); if( !node ) { sexp_val_free( args ); return response_error( EINVAL, id, name, "rogue buddy pointer" ); } type = purple_blist_node_get_type( node ); // =========================================================== // groups, contacts and chats can safely be removed here: // buddies should instead be noted for removal in the next // block of code as they require client<->server interaction: switch( type ) { case PURPLE_BLIST_GROUP_NODE : purple_blist_remove_group ( (PurpleGroup *)node ); gone = TRUE; break; case PURPLE_BLIST_CONTACT_NODE: purple_blist_remove_contact( (PurpleContact *)node ); gone = TRUE; break; case PURPLE_BLIST_CHAT_NODE : FIND_ACCOUNT( args, id, name, acct, auid, aname, proto ); BNODE_ACCOUNT_CHECK(chat,(PurpleChat *)node, acct, args, id, name); purple_blist_remove_chat ( (PurpleChat *)node ); gone = TRUE; break; case PURPLE_BLIST_BUDDY_NODE : buddy = (PurpleBuddy *)node; FIND_ACCOUNT( args, id, name, acct, auid, aname, proto ); BNODE_ACCOUNT_CHECK( buddy, buddy, acct, args, id, name ); b_arg = purple_buddy_get_name( buddy ); bname = purple_normalize( acct, b_arg ); break; default: sexp_val_free( args ); return response_error( EINVAL, id, name, "Unknown buddy list node type" ); } if( gone ) { xmlnode *rval = xnode_new( "alist" ); if( acct ) { AL_STR( rval, "account-name", aname ); AL_STR( rval, "im-protocol" , proto ); AL_PTR( rval, "account-uid" , acct ); } AL_PTR ( rval, "bnode-uid" , buddy ); AL_ENUM( rval, "bnode-type", type , ":blist-node-type" ); sexp_val_free( args ); return response_value( 0, id, name, rval ); } } else { b_arg = ALIST_VAL_STRING( args, "bnode-name" ); FIND_ACCOUNT( args, id, name, acct, auid, aname, proto ); if( b_arg ) { bname = purple_normalize( acct, b_arg ); gname = ALIST_VAL_STRING( args, "group" ); group = ( gname && *gname ) ? purple_find_group( gname ) : NULL; buddy = ( group ? purple_find_buddy_in_group( acct, bname, group ) : purple_find_buddy ( acct, bname ) ); } } if( !b_arg || !*b_arg ) { sexp_val_free( args ); return response_error( EINVAL, id, name, "buddy not specified" ); } // buddy must be in our local list or libpurple won't remove it from the // server list ( determined empirically, confirmed by inspecting code ): if( !buddy ) { buddy = purple_buddy_new( acct, bname, bname ); purple_blist_add_buddy ( buddy, NULL, NULL, NULL ); } if( buddy ) { // the order of the remove operations is important: it has to be // this way round, as noted above: account buddy removal won't // happen if the buddy is not in the blist when we try: if( !group ) group = purple_buddy_get_group( buddy ); // is this correct? what if we have more than one copy of said buddy? // potentially confusing. dunno what the right thing to do is here. purple_account_remove_buddy( acct, buddy, group ); purple_blist_remove_buddy( buddy ); } else { sexp_val_free( args ); return response_error( ENXIO, id, name, "no such buddy" ); } xmlnode *rval = xnode_new( "alist" ); AL_STR ( rval, "account-name", purple_account_get_username ( acct ) ); AL_STR ( rval, "im-protocol" , purple_account_get_protocol_id( acct ) ); AL_PTR ( rval, "account-uid" , acct ); AL_PTR ( rval, "bnode-uid" , buddy ); AL_ENUM( rval, "bnode-type", PURPLE_BLIST_BUDDY_NODE, ":blist-node-type" ); sexp_val_free( args ); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_command ( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { fprintf(stderr, "(elim-debug entered _h_elim_command)"); ASSERT_ALISTP( args, id, name ); elim_ping(); const char *aname = ALIST_VAL_STR( args, "account-name" ); const char *proto = ALIST_VAL_STR( args, "im-protocol" ); gpointer auid = ALIST_VAL_PTR( args, "account-uid" ); PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } PurpleConversationType pt = PURPLE_CONV_TYPE_UNKNOWN; gpointer cuid = ALIST_VAL_PTR( args, "conv-uid" ); const char *cname = ALIST_VAL_STR( args, "conv-name" ); PurpleConversation *pc = find_conv_by_acct_uid( acct, cuid ); if ( pc ) pt = purple_conversation_get_type( pc ); else { pt = PURPLE_CONV_TYPE_ANY; pc = purple_find_conversation_with_account( pt, cname, acct ); if( !pc ) { sexp_val_free( args ); return response_error( ENOENT, id, name, "conversation not found" ); } else { pt = purple_conversation_get_type( pc ); } } PurpleCmdStatus c_s = PURPLE_CMD_STATUS_FAILED; const char *cmd = ALIST_VAL_STRING( args, "command" ); char *esc = g_markup_escape_text( cmd, -1 ); char *err = NULL; const char *error = NULL; c_s = purple_cmd_do_command( pc, cmd, esc, &err ); if( c_s != PURPLE_CMD_STATUS_OK && (!err || !*err) ) switch( c_s ) { case PURPLE_CMD_STATUS_FAILED : error = "Command failed"; break; case PURPLE_CMD_STATUS_NOT_FOUND : error = "Command not found"; break; case PURPLE_CMD_STATUS_WRONG_ARGS: error = "Bad command arguments"; break; case PURPLE_CMD_STATUS_WRONG_PRPL: error = "Command not valid for this IM protocol"; break; case PURPLE_CMD_STATUS_WRONG_TYPE: error = "Command not valid in this conversation"; break; default: error = "Unknown command error"; } xmlnode *rval = xnode_new( "alist" ); AL_PTR ( rval, "conv-uid" , pc ); AL_STR ( rval, "conv-name" , purple_conversation_get_name(pc) ); AL_ENUM( rval, "command-status", c_s , ":cmd-status" ); AL_STR ( rval, "command-error" , err ? err : error ); AL_STR ( rval, "command-line" , cmd ); g_free ( err ); g_free ( esc ); sexp_val_free( args ); fprintf(stderr, "(elim-debug leaving _h_elim_command)"); return response_value( 0, id, name, rval ); }
xmlnode * _h_elim_add_chat( const char *name , const char *id , SEXP_VALUE *args , gpointer data ) { ASSERT_ALISTP( args, id, name ); elim_ping(); const char *aname = ALIST_VAL_STR ( args, "account-name" ); const char *proto = ALIST_VAL_STR ( args, "im-protocol" ); const char *alias = ALIST_VAL_STR ( args, "chat-alias" ); gpointer auid = ALIST_VAL_PTR ( args, "account-uid" ); GHashTable *opts = ALIST_VAL_ALIST( args, "chat-options" ); GHashTable *options = __ghash_str_sexp__str_str( opts ); PurpleAccount *acct = auid ? find_acct_by_uid( auid ) : purple_accounts_find( aname, proto ); if( !acct ) { sexp_val_free( args ); return response_error( ENXIO, id, name, "unknown account" ); } // cook up a chat node. if it's already on our buddy list, uncook it // and use the old one instead (name should be unique per account // so the operation is reasonable - we cannot supply a name as this // parameter can be delegated to the plugin to generate automatically): // this will trigger a new_node call, and possibly a remove call PurpleChat *chat = purple_chat_new( acct, alias, options ); const char *chn = purple_chat_get_name( chat ); PurpleChat *ch_2 = purple_blist_find_chat( acct, chn ); if( ch_2 ) { fprintf( stderr, "(elim-debug chat already exists)\n" ); purple_blist_remove_chat( chat ); chat = ch_2; chn = purple_chat_get_name( chat ); purple_blist_alias_chat( chat, alias ); } fprintf( stderr, "(elim-debug adding chat to blist)\n" ); purple_blist_add_chat( chat, NULL, NULL ); // if we have a conversation already, prod the client to show it fprintf( stderr, "(elim-debug looking for conversation)\n" ); PurpleConversation *conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, chn, acct ); if( conv ) purple_conversation_present( conv ); xmlnode *rval = xnode_new( "alist" ); AL_STR( rval, "account-name", purple_account_get_username (acct) ); AL_STR( rval, "im-protocol" , purple_account_get_protocol_id(acct) ); AL_PTR( rval, "account-uid" , acct ); AL_STR( rval, "chat-name" , chn ); if( conv ) { PurpleConversationType pct = purple_conversation_get_type ( conv ); PurpleConnectionFlags pcf = purple_conversation_get_features( conv ); AL_PTR ( rval, "conv-uid" , conv ); AL_STR ( rval, "conv-name" , purple_conversation_get_name (conv) ); AL_STR ( rval, "conv-title" , purple_conversation_get_title(conv) ); AL_ENUM( rval, "conv-type" , pct, ":conversation-type" ); AL_ENUM( rval, "conv-features", pcf, ":connection-flags" ); } sexp_val_free( args ); return response_value( 0, id, name, rval ); }