From 796da03f9f54f8fb193529288592571b371bf0cd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 5 Oct 2009 00:28:11 +0100 Subject: Something that compiles and runs, but otherwise utterly useless. Added a protocols/purple/ module and included it in the build system. Already picks up all the supported protocols and adds them individually. --- configure | 12 +++ protocols/nogaim.c | 5 + protocols/purple/Makefile | 41 ++++++++ protocols/purple/purple.c | 245 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 protocols/purple/Makefile create mode 100644 protocols/purple/purple.c diff --git a/configure b/configure index b3a98086..65eb9e89 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ msn=1 jabber=1 oscar=1 yahoo=1 +purple=0 debug=0 strip=1 @@ -66,6 +67,8 @@ Option Description Default --oscar=0/1 Disable/enable Oscar part (ICQ, AIM) $oscar --yahoo=0/1 Disable/enable Yahoo part $yahoo +--purple=0/1 Disable/enable libpurple support $purple + --debug=0/1 Disable/enable debugging $debug --strip=0/1 Disable/enable binary stripping $strip --gcov=0/1 Disable/enable test coverage reporting $gcov @@ -478,6 +481,15 @@ else protoobjs=$protoobjs'yahoo_mod.o ' fi +if [ "$purple" = 0 ]; then + echo '#undef WITH_PURPLE' >> config.h +else + echo '#define WITH_PURPLE' >> config.h + echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + protocols=$protocols'purple ' + protoobjs=$protoobjs'purple_mod.o ' +fi + if [ "$protocols" = "PROTOCOLS = " ]; then echo "Warning: You haven't selected any communication protocol to compile!" echo " BitlBee will run, but you will be unable to connect to IM servers!" diff --git a/protocols/nogaim.c b/protocols/nogaim.c index fd445324..8eae178d 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -117,6 +117,7 @@ void nogaim_init() extern void oscar_initmodule(); extern void byahoo_initmodule(); extern void jabber_initmodule(); + extern void purple_initmodule(); #ifdef WITH_MSN msn_initmodule(); @@ -133,6 +134,10 @@ void nogaim_init() #ifdef WITH_JABBER jabber_initmodule(); #endif + +#ifdef WITH_PURPLE + purple_initmodule(); +#endif #ifdef WITH_PLUGINS load_plugins(); diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile new file mode 100644 index 00000000..bdefbd5f --- /dev/null +++ b/protocols/purple/Makefile @@ -0,0 +1,41 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = purple.o + +CFLAGS += -Wall $$(pkg-config purple --cflags) +LFLAGS += -r + +# [SH] Phony targets +all: purple_mod.o +check: all +lcov: check +gcov: + gcov *.c + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +purple_mod.o: $(objects) + @echo '*' Linking purple_mod.o + $(LD) $(LFLAGS) $(objects) -o purple_mod.o diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c new file mode 100644 index 00000000..333f6674 --- /dev/null +++ b/protocols/purple/purple.c @@ -0,0 +1,245 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - Main file * +* * +* Copyright 2009 Wilmer van der Gaast * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include +#include + +#include "bitlbee.h" + +GSList *purple_connections; + +#undef g_io_add_watch +#undef g_io_add_watch_full +#undef g_timeout_add +#undef g_source_remove + +/** + * The following eventloop functions are used in both pidgin and purple-text. If your + * application uses glib mainloop, you can safely use this verbatim. + */ +#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +typedef struct _PurpleGLibIOClosure { + PurpleInputFunction function; + guint result; + gpointer data; +} PurpleGLibIOClosure; + +static void purple_glib_io_destroy(gpointer data) +{ + g_free(data); +} + +static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + PurpleGLibIOClosure *closure = data; + PurpleInputCondition purple_cond = 0; + + if (condition & PURPLE_GLIB_READ_COND) + purple_cond |= PURPLE_INPUT_READ; + if (condition & PURPLE_GLIB_WRITE_COND) + purple_cond |= PURPLE_INPUT_WRITE; + + closure->function(closure->data, g_io_channel_unix_get_fd(source), + purple_cond); + + return TRUE; +} + +static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, + gpointer data) +{ + PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & PURPLE_INPUT_READ) + cond |= PURPLE_GLIB_READ_COND; + if (condition & PURPLE_INPUT_WRITE) + cond |= PURPLE_GLIB_WRITE_COND; + + channel = g_io_channel_unix_new(fd); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + purple_glib_io_invoke, closure, purple_glib_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + g_timeout_add, + g_source_remove, + glib_input_add, + g_source_remove, + NULL, +#if GLIB_CHECK_VERSION(2,14,0) + g_timeout_add_seconds, +#else + NULL, +#endif + + /* padding */ + NULL, + NULL, + NULL +}; + +static PurpleCoreUiOps bee_core_uiops = +{ + NULL, + NULL, + NULL, //null_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static PurpleConversationUiOps bee_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + NULL, //null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void purple_init( account_t *acc ) +{ + set_t *s; + char str[16]; + +} + +static void purple_login( account_t *acc ) +{ + struct im_connection *ic = imcb_new( acc ); + struct ns_srv_reply *srv = NULL; + char *connect_to, *s; + int i; + + /* For now this is needed in the _connected() handlers if using + GLib event handling, to make sure we're not handling events + on dead connections. */ + purple_connections = g_slist_prepend( purple_connections, ic ); + +} + +static void purple_logout( struct im_connection *ic ) +{ + purple_connections = g_slist_remove( purple_connections, ic ); +} + +static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) +{ +} + +static GList *purple_away_states( struct im_connection *ic ) +{ +} + +static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) +{ +} + +static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void purple_remove_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void purple_keepalive( struct im_connection *ic ) +{ +} + +static int purple_send_typing( struct im_connection *ic, char *who, int typing ) +{ +} + +void purple_initmodule() +{ + GList *prots; + + purple_util_set_user_dir("/tmp"); + purple_debug_set_enabled(FALSE); + purple_core_set_ui_ops(&bee_core_uiops); + purple_eventloop_set_ui_ops(&glib_eventloops); + if( !purple_core_init( "BitlBee") ) + { + /* Initializing the core failed. Terminate. */ + fprintf( stderr, "libpurple initialization failed.\n" ); + abort(); + } + + /* This seems like stateful shit we don't want... */ + purple_set_blist(purple_blist_new()); + purple_blist_load(); + + /* Meh? */ + purple_prefs_load(); + + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) + { + struct prpl *ret = g_new0( struct prpl, 1 ); + PurplePlugin *prot = prots->data; + + ret->name = prot->info->id; + ret->login = purple_login; + ret->init = purple_init; + ret->logout = purple_logout; + ret->buddy_msg = purple_buddy_msg; + ret->away_states = purple_away_states; + ret->set_away = purple_set_away; + ret->add_buddy = purple_add_buddy; + ret->remove_buddy = purple_remove_buddy; + ret->keepalive = purple_keepalive; + ret->send_typing = purple_send_typing; + ret->handle_cmp = g_strcasecmp; + + register_protocol( ret ); + } +} -- cgit v1.2.3 From 860ba6aaeabb25cd27ec70fb4a37d910dd5b3746 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 00:32:34 +0100 Subject: Moved some stuff around, got something that logs in and reports status now. --- protocols/purple/purple.c | 152 +++++++++++++++++++++++++++++++++------------- 1 file changed, 109 insertions(+), 43 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 333f6674..5817373b 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,6 +46,18 @@ typedef struct _PurpleGLibIOClosure { gpointer data; } PurpleGLibIOClosure; +static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +{ + PurpleAccount *pa = purple_connection_get_account( gc ); + GSList *i; + + for( i = purple_connections; i; i = i->next ) + if( ((struct im_connection *)i->data)->proto_data == pa ) + return i->data; + + return NULL; +} + static void purple_glib_io_destroy(gpointer data) { g_free(data); @@ -109,62 +121,31 @@ static PurpleEventLoopUiOps glib_eventloops = NULL }; -static PurpleCoreUiOps bee_core_uiops = -{ - NULL, - NULL, - NULL, //null_ui_init, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurpleConversationUiOps bee_conv_uiops = -{ - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ - NULL, /* write_im */ - NULL, //null_write_conv, /* write_conv */ - NULL, /* chat_add_users */ - NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ - NULL, /* chat_update_user */ - NULL, /* present */ - NULL, /* has_focus */ - NULL, /* custom_smiley_add */ - NULL, /* custom_smiley_write */ - NULL, /* custom_smiley_close */ - NULL, /* send_confirm */ - NULL, - NULL, - NULL, - NULL -}; - static void purple_init( account_t *acc ) { - set_t *s; - char str[16]; - + /* TODO: Figure out variables to export via set. */ } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); - struct ns_srv_reply *srv = NULL; - char *connect_to, *s; - int i; + PurpleAccount *pa; + PurpleSavedStatus *ps; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); + pa = purple_account_new( acc->user, acc->prpl->name ); + purple_account_set_password( pa, acc->pass ); + + ic->proto_data = pa; + + purple_account_set_enabled( pa, "BitlBee", TRUE ); + + //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); + //purple_savedstatus_activate_for_account( ps, pa ); } static void purple_logout( struct im_connection *ic ) @@ -178,6 +159,7 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, static GList *purple_away_states( struct im_connection *ic ) { + return NULL; } static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) @@ -200,6 +182,90 @@ static int purple_send_typing( struct im_connection *ic, char *who, int typing ) { } +static void purple_ui_init(); + +static PurpleCoreUiOps bee_core_uiops = +{ + NULL, + NULL, + purple_ui_init, + NULL, + + /* padding */ + NULL, + NULL, + NULL, + NULL +}; + +static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) +{ + imcb_log( purple_ic_by_gc( gc ), "%s", text ); +} + +static void prplcb_conn_connected( PurpleConnection *gc ) +{ + imcb_connected( purple_ic_by_gc( gc ) ); +} + +static void prplcb_conn_disconnected( PurpleConnection *gc ) +{ + imc_logout( purple_ic_by_gc( gc ), TRUE ); +} + +static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) +{ + imcb_log( purple_ic_by_gc( gc ), "%s", text ); +} + +static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text ) +{ + /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login, + should probably handle that. */ + imcb_error( purple_ic_by_gc( gc ), "%s", text ); +} + +static PurpleConnectionUiOps bee_conn_uiops = +{ + prplcb_conn_progress, + prplcb_conn_connected, + prplcb_conn_disconnected, + prplcb_conn_notice, + NULL, + NULL, + NULL, + prplcb_conn_report_disconnect_reason, +}; + +static PurpleConversationUiOps bee_conv_uiops = +{ + NULL, /* create_conversation */ + NULL, /* destroy_conversation */ + NULL, /* write_chat */ + NULL, /* write_im */ + NULL, //null_write_conv, /* write_conv */ + NULL, /* chat_add_users */ + NULL, /* chat_rename_user */ + NULL, /* chat_remove_users */ + NULL, /* chat_update_user */ + NULL, /* present */ + NULL, /* has_focus */ + NULL, /* custom_smiley_add */ + NULL, /* custom_smiley_write */ + NULL, /* custom_smiley_close */ + NULL, /* send_confirm */ + NULL, + NULL, + NULL, + NULL +}; + +static void purple_ui_init() +{ + purple_connections_set_ui_ops( &bee_conn_uiops ); + purple_conversations_set_ui_ops( &bee_conv_uiops ); +} + void purple_initmodule() { GList *prots; -- cgit v1.2.3 From 7da726b12a546a5022d8f91fa3a34764335ba037 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 22:49:42 +0100 Subject: Getting a contact list and online status now. Time to handle messages. --- protocols/purple/purple.c | 51 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 5817373b..5807d01a 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,9 +46,8 @@ typedef struct _PurpleGLibIOClosure { gpointer data; } PurpleGLibIOClosure; -static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { - PurpleAccount *pa = purple_connection_get_account( gc ); GSList *i; for( i = purple_connections; i; i = i->next ) @@ -58,6 +57,11 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return NULL; } +static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) +{ + return purple_ic_by_pa( purple_connection_get_account( gc ) ); +} + static void purple_glib_io_destroy(gpointer data) { g_free(data); @@ -237,6 +241,48 @@ static PurpleConnectionUiOps bee_conn_uiops = prplcb_conn_report_disconnect_reason, }; +static void prplcb_blist_new( PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + { + imcb_add_buddy( ic, bud->name, NULL ); + if( bud->server_alias ) + imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); + } +} + +static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + { + imcb_buddy_status( purple_ic_by_pa( bud->account ), bud->name, + purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, + NULL, NULL ); + } +} + +static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) +{ + PurpleBuddy *bud = (PurpleBuddy*) node; + + if( node->type == PURPLE_BLIST_BUDDY_NODE ) + imcb_remove_buddy( purple_ic_by_pa( bud->account ), bud->name, NULL ); +} + +static PurpleBlistUiOps bee_blist_uiops = +{ + NULL, + prplcb_blist_new, + NULL, + prplcb_blist_update, + prplcb_blist_remove, +}; + static PurpleConversationUiOps bee_conv_uiops = { NULL, /* create_conversation */ @@ -262,6 +308,7 @@ static PurpleConversationUiOps bee_conv_uiops = static void purple_ui_init() { + purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); } -- cgit v1.2.3 From d250b2a5fa95724613d0176ce85a6c6859407ce6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 23:26:01 +0100 Subject: Receive messages. --- protocols/purple/purple.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 5807d01a..9edba87e 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -209,7 +209,12 @@ static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t static void prplcb_conn_connected( PurpleConnection *gc ) { - imcb_connected( purple_ic_by_gc( gc ) ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_connected( ic ); + + if( gc->flags & PURPLE_CONNECTION_HTML ) + ic->flags |= OPT_DOES_HTML; } static void prplcb_conn_disconnected( PurpleConnection *gc ) @@ -283,12 +288,19 @@ static PurpleBlistUiOps bee_blist_uiops = prplcb_blist_remove, }; +static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ + struct im_connection *ic = purple_ic_by_pa( conv->account ); + + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); +} + static PurpleConversationUiOps bee_conv_uiops = { NULL, /* create_conversation */ NULL, /* destroy_conversation */ NULL, /* write_chat */ - NULL, /* write_im */ + prplcb_conv_im, /* write_im */ NULL, //null_write_conv, /* write_conv */ NULL, /* chat_add_users */ NULL, /* chat_rename_user */ -- cgit v1.2.3 From 389f7bed787168abcc8aa5d01cdd49946cb863b6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 6 Oct 2009 23:55:46 +0100 Subject: Support for sending messages. --- protocols/purple/purple.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9edba87e..3c86490d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -159,6 +159,16 @@ static void purple_logout( struct im_connection *ic ) static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) { + PurpleConversation *conv; + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, + who, ic->proto_data ) ) == NULL ) + { + conv = purple_conversation_new( PURPLE_CONV_TYPE_IM, + ic->proto_data, who ); + } + + purple_conv_im_send( purple_conversation_get_im_data( conv ), message ); } static GList *purple_away_states( struct im_connection *ic ) @@ -292,7 +302,9 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha { struct im_connection *ic = purple_ic_by_pa( conv->account ); - imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); + /* ..._SEND means it's an outgoing message, no need to echo those. */ + if( !( flags & PURPLE_MESSAGE_SEND ) ) + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } static PurpleConversationUiOps bee_conv_uiops = -- cgit v1.2.3 From 0cbef26bd1f82787a8107e92b14839a59187e0c2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Oct 2009 00:37:32 +0100 Subject: Added some debugging stuff and handling (better said, ignoring) of events for closed connections where necessary. --- protocols/purple/purple.c | 60 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 3c86490d..cd908832 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -128,13 +128,15 @@ static PurpleEventLoopUiOps glib_eventloops = static void purple_init( account_t *acc ) { /* TODO: Figure out variables to export via set. */ + } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; - PurpleSavedStatus *ps; + //PurpleSavedStatus *ps; + GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -148,6 +150,15 @@ static void purple_login( account_t *acc ) purple_account_set_enabled( pa, "BitlBee", TRUE ); + /* + for( i = ((PurplePluginProtocolInfo *)pa->gc->prpl->info->extra_info)->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + + printf( "%s\n", o->pref_name ); + } + */ + //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); //purple_savedstatus_activate_for_account( ps, pa ); } @@ -169,6 +180,8 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, } purple_conv_im_send( purple_conversation_get_im_data( conv ), message ); + + return 1; } static GList *purple_away_states( struct im_connection *ic ) @@ -194,6 +207,7 @@ static void purple_keepalive( struct im_connection *ic ) static int purple_send_typing( struct im_connection *ic, char *who, int typing ) { + return 1; } static void purple_ui_init(); @@ -214,7 +228,9 @@ static PurpleCoreUiOps bee_core_uiops = static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) { - imcb_log( purple_ic_by_gc( gc ), "%s", text ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_log( ic, "%s", text ); } static void prplcb_conn_connected( PurpleConnection *gc ) @@ -229,19 +245,28 @@ static void prplcb_conn_connected( PurpleConnection *gc ) static void prplcb_conn_disconnected( PurpleConnection *gc ) { - imc_logout( purple_ic_by_gc( gc ), TRUE ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + if( ic != NULL ) + imc_logout( ic, TRUE ); } static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) { - imcb_log( purple_ic_by_gc( gc ), "%s", text ); + struct im_connection *ic = purple_ic_by_gc( gc ); + + if( ic != NULL ) + imcb_log( ic, "%s", text ); } static void prplcb_conn_report_disconnect_reason( PurpleConnection *gc, PurpleConnectionError reason, const char *text ) { + struct im_connection *ic = purple_ic_by_gc( gc ); + /* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login, should probably handle that. */ - imcb_error( purple_ic_by_gc( gc ), "%s", text ); + if( ic != NULL ) + imcb_error( ic, "%s", text ); } static PurpleConnectionUiOps bee_conn_uiops = @@ -261,7 +286,7 @@ static void prplcb_blist_new( PurpleBlistNode *node ) PurpleBuddy *bud = (PurpleBuddy*) node; struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) @@ -272,10 +297,11 @@ static void prplcb_blist_new( PurpleBlistNode *node ) static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { - imcb_buddy_status( purple_ic_by_pa( bud->account ), bud->name, + imcb_buddy_status( ic, bud->name, purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, NULL, NULL ); } @@ -284,9 +310,12 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE ) - imcb_remove_buddy( purple_ic_by_pa( bud->account ), bud->name, NULL ); + if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + { + imcb_remove_buddy( ic, bud->name, NULL ); + } } static PurpleBlistUiOps bee_blist_uiops = @@ -330,11 +359,22 @@ static PurpleConversationUiOps bee_conv_uiops = NULL }; +static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) +{ + printf( "DEBUG %s: %s", category, arg_s ); +} + +static PurpleDebugUiOps bee_debug_uiops = +{ + prplcb_debug_print, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From e046390da36e369c94af607fdedfe7b9f99d9e47 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:25:54 +0100 Subject: Make purple use BitlBee's event handling API. Since the APIs never really diverged too much this is fairly transparent. I did rename and redefine GAIM_INPUT_* variables to really make it work without adding another stupid layer in between. One problem left, the new libpurple input API doesn't care about return values. Fixing that in the next CL. --- bitlbee.c | 6 ++-- ipc.c | 6 ++-- irc.c | 8 +++--- lib/events.h | 4 +-- lib/events_glib.c | 8 +++--- lib/events_libevent.c | 12 ++++---- lib/http_client.c | 6 ++-- lib/proxy.c | 64 ++++++++++++++++++++--------------------- lib/ssl_bogus.c | 2 +- lib/ssl_client.h | 2 +- lib/ssl_gnutls.c | 4 +-- lib/ssl_nss.c | 2 +- lib/ssl_openssl.c | 4 +-- lib/ssl_sspi.c | 2 +- protocols/jabber/io.c | 4 +-- protocols/msn/ns.c | 2 +- protocols/msn/sb.c | 2 +- protocols/oscar/oscar.c | 14 ++++----- protocols/purple/purple.c | 72 +++++++++++------------------------------------ protocols/yahoo/yahoo.c | 4 +-- 20 files changed, 95 insertions(+), 133 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index b31c31fe..0feb88fc 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -92,7 +92,7 @@ int bitlbee_daemon_init() return( -1 ); } - global.listen_watch_source_id = b_input_add( global.listen_socket, GAIM_INPUT_READ, bitlbee_io_new_client, NULL ); + global.listen_watch_source_id = b_input_add( global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL ); #ifndef _WIN32 if( !global.conf->nofork ) @@ -283,7 +283,7 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition child = g_new0( struct bitlbee_child, 1 ); child->pid = client_pid; child->ipc_fd = fds[0]; - child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); child_list = g_slist_append( child_list, child ); log_message( LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid ); @@ -311,7 +311,7 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition /* We can store the IPC fd there now. */ global.listen_socket = fds[1]; - global.listen_watch_source_id = b_input_add( fds[1], GAIM_INPUT_READ, ipc_child_read, irc ); + global.listen_watch_source_id = b_input_add( fds[1], B_EV_IO_READ, ipc_child_read, irc ); close( fds[0] ); diff --git a/ipc.c b/ipc.c index d6b850f1..54053322 100644 --- a/ipc.c +++ b/ipc.c @@ -513,7 +513,7 @@ static gboolean new_ipc_client( gpointer data, gint serversock, b_input_conditio return TRUE; } - child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); child_list = g_slist_append( child_list, child ); @@ -551,7 +551,7 @@ int ipc_master_listen_socket() return 0; } - b_input_add( serversock, GAIM_INPUT_READ, new_ipc_client, NULL ); + b_input_add( serversock, B_EV_IO_READ, new_ipc_client, NULL ); return 1; } @@ -596,7 +596,7 @@ int ipc_master_load_state( char *statefile ) fclose( fp ); return 0; } - child->ipc_inpa = b_input_add( child->ipc_fd, GAIM_INPUT_READ, ipc_master_read, child ); + child->ipc_inpa = b_input_add( child->ipc_fd, B_EV_IO_READ, ipc_master_read, child ); child_list = g_slist_append( child_list, child ); } diff --git a/irc.c b/irc.c index 2dcc625d..ea0f7db7 100644 --- a/irc.c +++ b/irc.c @@ -89,7 +89,7 @@ irc_t *irc_new( int fd ) irc->fd = fd; sock_make_nonblocking( irc->fd ); - irc->r_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_READ, bitlbee_io_current_client_read, irc ); + irc->r_watch_source_id = b_input_add( irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc ); irc->status = USTATUS_OFFLINE; irc->last_pong = gettime(); @@ -652,10 +652,10 @@ void irc_vawrite( irc_t *irc, char *format, va_list params ) the queue. If it's FALSE, we emptied the buffer and saved ourselves some work in the event queue. */ /* Really can't be done as long as the code doesn't do error checking very well: - if( bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ) ) */ + if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */ /* So just always do it via the event handler. */ - irc->w_watch_source_id = b_input_add( irc->fd, GAIM_INPUT_WRITE, bitlbee_io_current_client_write, irc ); + irc->w_watch_source_id = b_input_add( irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc ); } return; @@ -681,7 +681,7 @@ void irc_write_all( int now, char *format, ... ) irc_vawrite( temp->data, format, params ); if( now ) { - bitlbee_io_current_client_write( irc, irc->fd, GAIM_INPUT_WRITE ); + bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ); } temp = temp->next; } diff --git a/lib/events.h b/lib/events.h index 4baea7b6..5bf2cfd8 100644 --- a/lib/events.h +++ b/lib/events.h @@ -47,8 +47,8 @@ /* The conditions you can pass to b_input_add()/that will be passed to the given callback function. */ typedef enum { - GAIM_INPUT_READ = 1 << 1, - GAIM_INPUT_WRITE = 1 << 2 + B_EV_IO_READ = 1 << 0, + B_EV_IO_WRITE = 1 << 1 } b_input_condition; typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); diff --git a/lib/events_glib.c b/lib/events_glib.c index 3e194e98..2260ec4e 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -75,9 +75,9 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin gboolean st; if (condition & GAIM_READ_COND) - gaim_cond |= GAIM_INPUT_READ; + gaim_cond |= B_EV_IO_READ; if (condition & GAIM_WRITE_COND) - gaim_cond |= GAIM_INPUT_WRITE; + gaim_cond |= B_EV_IO_WRITE; event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data ); @@ -105,9 +105,9 @@ gint b_input_add(gint source, b_input_condition condition, b_event_handler funct closure->function = function; closure->data = data; - if (condition & GAIM_INPUT_READ) + if (condition & B_EV_IO_READ) cond |= GAIM_READ_COND; - if (condition & GAIM_INPUT_WRITE) + if (condition & B_EV_IO_WRITE) cond |= GAIM_WRITE_COND; channel = g_io_channel_unix_new(source); diff --git a/lib/events_libevent.c b/lib/events_libevent.c index cf616576..b52a5dd3 100644 --- a/lib/events_libevent.c +++ b/lib/events_libevent.c @@ -125,9 +125,9 @@ static void b_event_passthrough( int fd, short event, void *data ) if( fd >= 0 ) { if( event & EV_READ ) - cond |= GAIM_INPUT_READ; + cond |= B_EV_IO_READ; if( event & EV_WRITE ) - cond |= GAIM_INPUT_WRITE; + cond |= B_EV_IO_WRITE; } event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id ); @@ -173,8 +173,8 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data ); - if( ( condition & GAIM_INPUT_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) || - ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) + if( ( condition & B_EV_IO_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) || + ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) { /* We'll stick with this libevent entry, but give it a new BitlBee id. */ g_hash_table_remove( id_hash, &b_ev->id ); @@ -197,9 +197,9 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function b_ev->data = data; out_cond = EV_PERSIST; - if( condition & GAIM_INPUT_READ ) + if( condition & B_EV_IO_READ ) out_cond |= EV_READ; - if( condition & GAIM_INPUT_WRITE ) + if( condition & B_EV_IO_WRITE ) out_cond |= EV_WRITE; event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev ); diff --git a/lib/http_client.c b/lib/http_client.c index aae5645b..e9d3c1bb 100644 --- a/lib/http_client.c +++ b/lib/http_client.c @@ -148,10 +148,10 @@ static gboolean http_connected( gpointer data, int source, b_input_condition con if( req->bytes_written < req->request_length ) req->inpa = b_input_add( source, - req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE, http_connected, req ); else - req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req ); + req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req ); return FALSE; @@ -233,7 +233,7 @@ static gboolean http_incoming_data( gpointer data, int source, b_input_condition /* There will be more! */ req->inpa = b_input_add( req->fd, - req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ, + req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ, http_incoming_data, req ); return FALSE; diff --git a/lib/proxy.c b/lib/proxy.c index e52837fe..baf5823a 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -90,9 +90,9 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition closesocket(source); b_event_remove(phb->inpa); if( phb->proxy_func ) - phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ); + phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ); else { - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb); } return FALSE; @@ -101,9 +101,9 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition sock_make_blocking(source); b_event_remove(phb->inpa); if( phb->proxy_func ) - phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ); + phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ); else { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb); } @@ -146,7 +146,7 @@ static int proxy_connect_none(const char *host, unsigned short port, struct PHB return -1; } else { - phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb); + phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb); phb->fd = fd; return fd; @@ -178,14 +178,14 @@ static gboolean http_canread(gpointer data, gint source, b_input_condition cond) if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -203,7 +203,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -214,7 +214,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond phb->host, phb->port); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -229,7 +229,7 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond g_free(t2); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -239,13 +239,13 @@ static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond g_snprintf(cmd, sizeof(cmd), "\r\n"); if (send(source, cmd, strlen(cmd), 0) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb); return FALSE; } @@ -272,14 +272,14 @@ static gboolean s4_canread(gpointer data, gint source, b_input_condition cond) memset(packet, 0, sizeof(packet)); if (read(source, packet, 9) >= 4 && packet[1] == 90) { - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -298,7 +298,7 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -308,7 +308,7 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) /* XXX does socks4 not support host name lookups by the proxy? */ if (!(hp = gethostbyname(phb->host))) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -325,13 +325,13 @@ static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) packet[8] = 0; if (write(source, packet, 9) != 9) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb); return FALSE; } @@ -358,20 +358,20 @@ static gboolean s5_canread_again(gpointer data, gint source, b_input_condition c if (read(source, buf, 10) < 10) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } if ((buf[0] != 0x05) || (buf[1] != 0x00)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->func(phb->data, source, GAIM_INPUT_READ); + phb->func(phb->data, source, B_EV_IO_READ); g_free(phb->host); g_free(phb); @@ -395,13 +395,13 @@ static void s5_sendconnect(gpointer data, gint source) if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb); } static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) @@ -413,7 +413,7 @@ static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) if (read(source, buf, 2) < 2) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -421,7 +421,7 @@ static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) if ((buf[0] != 0x01) || (buf[1] != 0x00)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -441,7 +441,7 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) if (read(source, buf, 2) < 2) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -449,7 +449,7 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) if ((buf[0] != 0x05) || (buf[1] == 0xff)) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -464,13 +464,13 @@ static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) memcpy(buf + 2 + i + 1, proxypass, j); if (write(source, buf, 3 + i + j) < 3 + i + j) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb); } else { s5_sendconnect(phb, source); } @@ -490,7 +490,7 @@ static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) len = sizeof(error); if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; @@ -512,13 +512,13 @@ static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) if (write(source, buf, i) < i) { close(source); - phb->func(phb->data, -1, GAIM_INPUT_READ); + phb->func(phb->data, -1, B_EV_IO_READ); g_free(phb->host); g_free(phb); return FALSE; } - phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb); + phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb); return FALSE; } diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index a07ea752..9c368c66 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -58,7 +58,7 @@ void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) b_input_condition ssl_getdirection( void *conn ) { - return GAIM_INPUT_READ; + return B_EV_IO_READ; } int ssl_pending( void *conn ) diff --git a/lib/ssl_client.h b/lib/ssl_client.h index f91d0d70..0a8e82d8 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -70,7 +70,7 @@ G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); handling. */ G_MODULE_EXPORT int ssl_getfd( void *conn ); -/* This function returns GAIM_INPUT_READ/WRITE. With SSL connections it's +/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's possible that something has to be read while actually were trying to write something (think about key exchange/refresh/etc). So when an SSL operation returned SSL_AGAIN, *always* use this function when diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index f5945442..5a14b825 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -105,7 +105,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition { struct scd *conn = data; - return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) @@ -243,5 +243,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? - GAIM_INPUT_WRITE : GAIM_INPUT_READ ); + B_EV_IO_WRITE : B_EV_IO_READ ); } diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index eba3c441..de6e7ec6 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -192,5 +192,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { /* Just in case someone calls us, let's return the most likely case: */ - return GAIM_INPUT_READ; + return B_EV_IO_READ; } diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index fc6d433e..8abff390 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -101,7 +101,7 @@ static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition { struct scd *conn = data; - return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); + return ssl_connected( conn, conn->fd, B_EV_IO_WRITE ); } static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) @@ -269,5 +269,5 @@ int ssl_getfd( void *conn ) b_input_condition ssl_getdirection( void *conn ) { - return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ ); + return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ ); } diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c index a16423b1..e14c451e 100644 --- a/lib/ssl_sspi.c +++ b/lib/ssl_sspi.c @@ -274,5 +274,5 @@ int ssl_getfd(void *conn) GaimInputCondition ssl_getdirection( void *conn ) { - return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */ + return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */ } diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c index 10efad37..3b0ef3ef 100644 --- a/protocols/jabber/io.c +++ b/protocols/jabber/io.c @@ -63,7 +63,7 @@ int jabber_write( struct im_connection *ic, char *buf, int len ) it via the event handler. If not, add the handler. (In most cases it probably won't be necessary.) */ if( ( ret = jabber_write_queue( ic ) ) && jd->tx_len > 0 ) - jd->w_inpa = b_input_add( jd->fd, GAIM_INPUT_WRITE, jabber_write_callback, ic ); + jd->w_inpa = b_input_add( jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic ); } else { @@ -528,7 +528,7 @@ gboolean jabber_start_stream( struct im_connection *ic ) jd->xt = xt_new( jabber_handlers, ic ); if( jd->r_inpa <= 0 ) - jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic ); + jd->r_inpa = b_input_add( jd->fd, B_EV_IO_READ, jabber_read_callback, ic ); greet = g_strdup_printf( "" "trId ); if( msn_write( ic, s, strlen( s ) ) ) { - ic->inpa = b_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, ic ); + ic->inpa = b_input_add( md->fd, B_EV_IO_READ, msn_ns_callback, ic ); imcb_log( ic, "Connected to server, waiting for reply" ); } diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e9526234..b0f7a2c5 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -308,7 +308,7 @@ gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ) g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, ic->acc->user, sb->key, sb->session ); if( msn_sb_write( sb, buf, strlen( buf ) ) ) - sb->inp = b_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb ); + sb->inp = b_input_add( sb->fd, B_EV_IO_READ, msn_sb_callback, sb ); else debug( "Error %d while connecting to switchboard server", 2 ); diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 1118c26d..06b8100f 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -290,7 +290,7 @@ static gboolean oscar_callback(gpointer data, gint source, odata = (struct oscar_data *)ic->proto_data; - if (condition & GAIM_INPUT_READ) { + if (condition & B_EV_IO_READ) { if (aim_get_command(odata->sess, conn) >= 0) { aim_rxdispatch(odata->sess); if (odata->killme) @@ -362,7 +362,7 @@ static gboolean oscar_login_connect(gpointer data, gint source, b_input_conditio } aim_conn_completeconnect(sess, conn); - ic->inpa = b_input_add(conn->fd, GAIM_INPUT_READ, + ic->inpa = b_input_add(conn->fd, B_EV_IO_READ, oscar_callback, conn); return FALSE; @@ -486,7 +486,7 @@ static gboolean oscar_bos_connect(gpointer data, gint source, b_input_condition } aim_conn_completeconnect(sess, bosconn); - ic->inpa = b_input_add(bosconn->fd, GAIM_INPUT_READ, + ic->inpa = b_input_add(bosconn->fd, B_EV_IO_READ, oscar_callback, bosconn); imcb_log(ic, _("Connection established, cookie sent")); @@ -662,7 +662,7 @@ static gboolean straight_to_hell(gpointer data, gint source, b_input_condition c write(pos->fd, buf, strlen(buf)); if (pos->modname) g_free(pos->modname); - pos->inpa = b_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); + pos->inpa = b_input_add(pos->fd, B_EV_IO_READ, damn_you, pos); return FALSE; } @@ -831,7 +831,7 @@ static gboolean oscar_chatnav_connect(gpointer data, gint source, b_input_condit } aim_conn_completeconnect(sess, tstconn); - odata->cnpa = b_input_add(tstconn->fd, GAIM_INPUT_READ, + odata->cnpa = b_input_add(tstconn->fd, B_EV_IO_READ, oscar_callback, tstconn); return FALSE; @@ -859,7 +859,7 @@ static gboolean oscar_auth_connect(gpointer data, gint source, b_input_condition } aim_conn_completeconnect(sess, tstconn); - odata->paspa = b_input_add(tstconn->fd, GAIM_INPUT_READ, + odata->paspa = b_input_add(tstconn->fd, B_EV_IO_READ, oscar_callback, tstconn); return FALSE; @@ -895,7 +895,7 @@ static gboolean oscar_chat_connect(gpointer data, gint source, b_input_condition aim_conn_completeconnect(sess, ccon->conn); ccon->inpa = b_input_add(tstconn->fd, - GAIM_INPUT_READ, + B_EV_IO_READ, oscar_callback, tstconn); odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon); diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index cd908832..08c14edf 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -62,67 +62,22 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } -static void purple_glib_io_destroy(gpointer data) +static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) { - g_free(data); + return b_timeout_add( interval, (b_event_handler) func, udata ); } -static gboolean purple_glib_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) { - PurpleGLibIOClosure *closure = data; - PurpleInputCondition purple_cond = 0; - - if (condition & PURPLE_GLIB_READ_COND) - purple_cond |= PURPLE_INPUT_READ; - if (condition & PURPLE_GLIB_WRITE_COND) - purple_cond |= PURPLE_INPUT_WRITE; - - closure->function(closure->data, g_io_channel_unix_get_fd(source), - purple_cond); - - return TRUE; -} - -static guint glib_input_add(gint fd, PurpleInputCondition condition, PurpleInputFunction function, - gpointer data) -{ - PurpleGLibIOClosure *closure = g_new0(PurpleGLibIOClosure, 1); - GIOChannel *channel; - GIOCondition cond = 0; - - closure->function = function; - closure->data = data; - - if (condition & PURPLE_INPUT_READ) - cond |= PURPLE_GLIB_READ_COND; - if (condition & PURPLE_INPUT_WRITE) - cond |= PURPLE_GLIB_WRITE_COND; - - channel = g_io_channel_unix_new(fd); - closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, - purple_glib_io_invoke, closure, purple_glib_io_destroy); - - g_io_channel_unref(channel); - return closure->result; + return (guint) b_input_add( fd, cond, (b_event_handler) func, udata ); } static PurpleEventLoopUiOps glib_eventloops = { - g_timeout_add, - g_source_remove, - glib_input_add, - g_source_remove, - NULL, -#if GLIB_CHECK_VERSION(2,14,0) - g_timeout_add_seconds, -#else - NULL, -#endif - - /* padding */ - NULL, - NULL, - NULL + prplcb_ev_timeout_add, + b_event_remove, + prplcb_ev_input_add, + b_event_remove, }; static void purple_init( account_t *acc ) @@ -136,7 +91,7 @@ static void purple_login( account_t *acc ) struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; //PurpleSavedStatus *ps; - GList *i; + //GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -342,7 +297,7 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* destroy_conversation */ NULL, /* write_chat */ prplcb_conv_im, /* write_im */ - NULL, //null_write_conv, /* write_conv */ + NULL, /* write_conv */ NULL, /* chat_add_users */ NULL, /* chat_rename_user */ NULL, /* chat_remove_users */ @@ -381,6 +336,13 @@ void purple_initmodule() { GList *prots; + if( B_EV_IO_READ != PURPLE_INPUT_READ || + B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) + { + /* FIXME FIXME FIXME FIXME FIXME :-) */ + exit( 1 ); + } + purple_util_set_user_dir("/tmp"); purple_debug_set_enabled(FALSE); purple_core_set_ui_ops(&bee_core_uiops); diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 3e844c55..65993d9d 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -681,7 +681,7 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat d->data = data; inp->d = d; - d->tag = inp->h = b_input_add( fd, GAIM_INPUT_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d ); + d->tag = inp->h = b_input_add( fd, B_EV_IO_READ, (b_event_handler) byahoo_read_ready_callback, (gpointer) d ); } else if( cond == YAHOO_INPUT_WRITE ) { @@ -692,7 +692,7 @@ int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *dat d->data = data; inp->d = d; - d->tag = inp->h = b_input_add( fd, GAIM_INPUT_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d ); + d->tag = inp->h = b_input_add( fd, B_EV_IO_WRITE, (b_event_handler) byahoo_write_ready_callback, (gpointer) d ); } else { -- cgit v1.2.3 From c5c18c155cfdc3edcbd764633761d33e3c5992a3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 00:57:26 +0100 Subject: Hacked up a B_EV_FLAG_FORCE_REPEAT event handler flag to make libpurple happy. --- lib/events.h | 4 +++- lib/events_glib.c | 9 ++++++++- lib/events_libevent.c | 4 +++- protocols/purple/purple.c | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/events.h b/lib/events.h index 5bf2cfd8..fa30cf27 100644 --- a/lib/events.h +++ b/lib/events.h @@ -48,7 +48,9 @@ the given callback function. */ typedef enum { B_EV_IO_READ = 1 << 0, - B_EV_IO_WRITE = 1 << 1 + B_EV_IO_WRITE = 1 << 1, + B_EV_FLAG_FORCE_ONCE = 1 << 16, + B_EV_FLAG_FORCE_REPEAT = 1 << 17, } b_input_condition; typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); diff --git a/lib/events_glib.c b/lib/events_glib.c index 2260ec4e..d6ac82cc 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -48,6 +48,7 @@ typedef struct _GaimIOClosure { b_event_handler function; gpointer data; + guint flags; } GaimIOClosure; static GMainLoop *loop = NULL; @@ -86,7 +87,12 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin if( !st ) event_debug( "Returned FALSE, cancelling.\n" ); - return st; + if (closure->flags & B_EV_FLAG_FORCE_ONCE) + return FALSE; + else if (closure->flags & B_EV_FLAG_FORCE_REPEAT) + return TRUE; + else + return st; } static void gaim_io_destroy(gpointer data) @@ -104,6 +110,7 @@ gint b_input_add(gint source, b_input_condition condition, b_event_handler funct closure->function = function; closure->data = data; + closure->flags = condition; if (condition & B_EV_IO_READ) cond |= GAIM_READ_COND; diff --git a/lib/events_libevent.c b/lib/events_libevent.c index b52a5dd3..43d770ea 100644 --- a/lib/events_libevent.c +++ b/lib/events_libevent.c @@ -59,6 +59,7 @@ struct b_event_data gint timeout; b_event_handler function; void *data; + guint flags; }; void b_main_init() @@ -149,7 +150,7 @@ static void b_event_passthrough( int fd, short event, void *data ) /* This event was killed already, don't touch it! */ return; } - else if( !st ) + else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) ) { event_debug( "Handler returned FALSE: " ); b_event_remove( id_cur ); @@ -211,6 +212,7 @@ gint b_input_add( gint fd, b_input_condition condition, b_event_handler function g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev ); } + b_ev->flags = condition; g_hash_table_insert( id_hash, &b_ev->id, b_ev ); return b_ev->id; } diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 08c14edf..9ef70bd3 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -69,7 +69,7 @@ static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer u static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) { - return (guint) b_input_add( fd, cond, (b_event_handler) func, udata ); + return (guint) b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); } static PurpleEventLoopUiOps glib_eventloops = -- cgit v1.2.3 From 4164e620b4f593a427a89d9292f4aef5c33e9def Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 11:40:40 +0100 Subject: Fixing a few compiler warnings and cleaning up the last remains of GLib-specific code. --- protocols/purple/purple.c | 60 +++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 36 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9ef70bd3..0a70b194 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -28,24 +28,6 @@ GSList *purple_connections; -#undef g_io_add_watch -#undef g_io_add_watch_full -#undef g_timeout_add -#undef g_source_remove - -/** - * The following eventloop functions are used in both pidgin and purple-text. If your - * application uses glib mainloop, you can safely use this verbatim. - */ -#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) -#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) - -typedef struct _PurpleGLibIOClosure { - PurpleInputFunction function; - guint result; - gpointer data; -} PurpleGLibIOClosure; - static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -62,24 +44,6 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } -static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) -{ - return b_timeout_add( interval, (b_event_handler) func, udata ); -} - -static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) -{ - return (guint) b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); -} - -static PurpleEventLoopUiOps glib_eventloops = -{ - prplcb_ev_timeout_add, - b_event_remove, - prplcb_ev_input_add, - b_event_remove, -}; - static void purple_init( account_t *acc ) { /* TODO: Figure out variables to export via set. */ @@ -324,6 +288,30 @@ static PurpleDebugUiOps bee_debug_uiops = prplcb_debug_print, }; +static guint prplcb_ev_timeout_add( guint interval, GSourceFunc func, gpointer udata ) +{ + return b_timeout_add( interval, (b_event_handler) func, udata ); +} + +static guint prplcb_ev_input_add( int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata ) +{ + return b_input_add( fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata ); +} + +static gboolean prplcb_ev_remove( guint id ) +{ + b_event_remove( (gint) id ); + return TRUE; +} + +static PurpleEventLoopUiOps glib_eventloops = +{ + prplcb_ev_timeout_add, + prplcb_ev_remove, + prplcb_ev_input_add, + prplcb_ev_remove, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); -- cgit v1.2.3 From 4f103ea401bb6b1ed8963ea33d4924f95e10473b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 12:26:09 +0100 Subject: Added handing of away states/messages of contacts. --- protocols/purple/purple.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0a70b194..f1355e25 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -137,12 +137,6 @@ static PurpleCoreUiOps bee_core_uiops = NULL, purple_ui_init, NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL }; static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) @@ -220,9 +214,16 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) { - imcb_buddy_status( ic, bud->name, - purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0, - NULL, NULL ); + PurpleStatus *as; + int flags = 0; + + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; + flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; + + as = purple_presence_get_active_status( bud->presence ); + + imcb_buddy_status( ic, bud->name, flags, purple_status_get_name( as ), + purple_status_get_attr_string( as, "message" ) ); } } -- cgit v1.2.3 From db4cd40374ade33ccb1feae113f12a1dd0b6bf37 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 13:22:23 +0100 Subject: Some valgrind cleaning/type safety fixes. --- protocols/purple/purple.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f1355e25..c9de15cd 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -84,7 +84,11 @@ static void purple_login( account_t *acc ) static void purple_logout( struct im_connection *ic ) { + PurpleAccount *pa = ic->proto_data; + + purple_account_set_enabled( pa, "BitlBee", FALSE ); purple_connections = g_slist_remove( purple_connections, ic ); + purple_account_destroy( pa ); } static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) @@ -197,10 +201,14 @@ static PurpleConnectionUiOps bee_conn_uiops = static void prplcb_blist_new( PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( ic == NULL ) + return; + imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); @@ -210,13 +218,16 @@ static void prplcb_blist_new( PurpleBlistNode *node ) static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); PurpleStatus *as; int flags = 0; + if( ic == NULL ) + return; + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; @@ -230,10 +241,14 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; - struct im_connection *ic = purple_ic_by_pa( bud->account ); - if( node->type == PURPLE_BLIST_BUDDY_NODE && ic != NULL ) + if( node->type == PURPLE_BLIST_BUDDY_NODE ) { + struct im_connection *ic = purple_ic_by_pa( bud->account ); + + if( ic == NULL ) + return; + imcb_remove_buddy( ic, bud->name, NULL ); } } -- cgit v1.2.3 From 0f7ee7e53f6bcb2d1d262a94c278440413c0103a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 13:57:29 +0100 Subject: Copy all the string/bool/int account settings with their defaults to "account set". They can be changed, but changes don't yet have any effect. --- protocols/purple/purple.c | 58 +++++++++++++++++++++++++++++++++++------------ set.c | 2 +- set.h | 2 +- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c9de15cd..f3d8f0f4 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,16 +46,56 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) static void purple_init( account_t *acc ) { - /* TODO: Figure out variables to export via set. */ + PurplePlugin *prpl = purple_plugins_find_with_id( acc->prpl->name ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GList *i; + for( i = pi->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + const char *name; + char *def = NULL; + set_eval eval = NULL; + set_t *s; + + name = purple_account_option_get_setting( o ); + + switch( purple_account_option_get_type( o ) ) + { + case PURPLE_PREF_STRING: + def = g_strdup( purple_account_option_get_default_string( o ) ); + break; + + case PURPLE_PREF_INT: + def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) ); + eval = set_eval_int; + break; + + case PURPLE_PREF_BOOLEAN: + if( purple_account_option_get_default_bool( o ) ) + def = g_strdup( "true" ); + else + def = g_strdup( "false" ); + eval = set_eval_bool; + break; + + default: + fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + } + + if( def != NULL ) + { + s = set_add( &acc->set, name, def, eval, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + g_free( def ); + } + } } static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); PurpleAccount *pa; - //PurpleSavedStatus *ps; - //GList *i; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -68,18 +108,6 @@ static void purple_login( account_t *acc ) ic->proto_data = pa; purple_account_set_enabled( pa, "BitlBee", TRUE ); - - /* - for( i = ((PurplePluginProtocolInfo *)pa->gc->prpl->info->extra_info)->protocol_options; i; i = i->next ) - { - PurpleAccountOption *o = i->data; - - printf( "%s\n", o->pref_name ); - } - */ - - //ps = purple_savedstatus_new( NULL, PURPLE_STATUS_AVAILABLE ); - //purple_savedstatus_activate_for_account( ps, pa ); } static void purple_logout( struct im_connection *ic ) diff --git a/set.c b/set.c index 18d5a50d..f72e0ace 100644 --- a/set.c +++ b/set.c @@ -28,7 +28,7 @@ /* Used to use NULL for this, but NULL is actually a "valid" value. */ char *SET_INVALID = "nee"; -set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data ) +set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ) { set_t *s = set_find( head, key ); diff --git a/set.h b/set.h index 19ea73fb..d20a58d3 100644 --- a/set.h +++ b/set.h @@ -72,7 +72,7 @@ typedef struct set } set_t; /* Should be pretty clear. */ -set_t *set_add( set_t **head, char *key, char *def, set_eval eval, void *data ); +set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ); /* Returns the raw set_t. Might be useful sometimes. */ set_t *set_find( set_t **head, char *key ); -- cgit v1.2.3 From b74b287af7ee980b01b89e911e21ec8f163d24b3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 11 Oct 2009 22:08:26 +0100 Subject: Fixed account cleanup (use remove, not destroy) and now using user's account settings. --- protocols/purple/purple.c | 46 ++++++++++++++++++++++++++++++++++++++++++---- set.c | 16 ++++++++-------- set.h | 16 ++++++++-------- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f3d8f0f4..82978dc4 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -92,6 +92,43 @@ static void purple_init( account_t *acc ) } } +static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) +{ + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GList *i; + + for( i = pi->protocol_options; i; i = i->next ) + { + PurpleAccountOption *o = i->data; + const char *name; + set_t *s; + + name = purple_account_option_get_setting( o ); + s = set_find( &acc->set, name ); + if( s->value == NULL ) + continue; + + switch( purple_account_option_get_type( o ) ) + { + case PURPLE_PREF_STRING: + purple_account_set_string( pa, name, set_getstr( &acc->set, name ) ); + break; + + case PURPLE_PREF_INT: + purple_account_set_int( pa, name, set_getint( &acc->set, name ) ); + break; + + case PURPLE_PREF_BOOLEAN: + purple_account_set_bool( pa, name, set_getbool( &acc->set, name ) ); + break; + + default: + break; + } + } +} + static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); @@ -102,10 +139,9 @@ static void purple_login( account_t *acc ) on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); - pa = purple_account_new( acc->user, acc->prpl->name ); + ic->proto_data = pa = purple_account_new( acc->user, acc->prpl->name ); purple_account_set_password( pa, acc->pass ); - - ic->proto_data = pa; + purple_sync_settings( acc, pa ); purple_account_set_enabled( pa, "BitlBee", TRUE ); } @@ -116,7 +152,7 @@ static void purple_logout( struct im_connection *ic ) purple_account_set_enabled( pa, "BitlBee", FALSE ); purple_connections = g_slist_remove( purple_connections, ic ); - purple_account_destroy( pa ); + purple_accounts_remove( pa ); } static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, int flags ) @@ -193,7 +229,9 @@ static void prplcb_conn_disconnected( PurpleConnection *gc ) struct im_connection *ic = purple_ic_by_gc( gc ); if( ic != NULL ) + { imc_logout( ic, TRUE ); + } } static void prplcb_conn_notice( PurpleConnection *gc, const char *text ) diff --git a/set.c b/set.c index f72e0ace..7abae37a 100644 --- a/set.c +++ b/set.c @@ -62,7 +62,7 @@ set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, v return s; } -set_t *set_find( set_t **head, char *key ) +set_t *set_find( set_t **head, const char *key ) { set_t *s = *head; @@ -76,7 +76,7 @@ set_t *set_find( set_t **head, char *key ) return s; } -char *set_getstr( set_t **head, char *key ) +char *set_getstr( set_t **head, const char *key ) { set_t *s = set_find( head, key ); @@ -86,7 +86,7 @@ char *set_getstr( set_t **head, char *key ) return s->value ? s->value : s->def; } -int set_getint( set_t **head, char *key ) +int set_getint( set_t **head, const char *key ) { char *s = set_getstr( head, key ); int i = 0; @@ -100,7 +100,7 @@ int set_getint( set_t **head, char *key ) return i; } -int set_getbool( set_t **head, char *key ) +int set_getbool( set_t **head, const char *key ) { char *s = set_getstr( head, key ); @@ -110,7 +110,7 @@ int set_getbool( set_t **head, char *key ) return bool2int( s ); } -int set_setstr( set_t **head, char *key, char *value ) +int set_setstr( set_t **head, const char *key, char *value ) { set_t *s = set_find( head, key ); char *nv = value; @@ -149,7 +149,7 @@ int set_setstr( set_t **head, char *key, char *value ) return 1; } -int set_setint( set_t **head, char *key, int value ) +int set_setint( set_t **head, const char *key, int value ) { char s[24]; /* Not quite 128-bit clean eh? ;-) */ @@ -157,7 +157,7 @@ int set_setint( set_t **head, char *key, int value ) return set_setstr( head, key, s ); } -void set_del( set_t **head, char *key ) +void set_del( set_t **head, const char *key ) { set_t *s = *head, *t = NULL; @@ -181,7 +181,7 @@ void set_del( set_t **head, char *key ) } } -int set_reset( set_t **head, char *key ) +int set_reset( set_t **head, const char *key ) { set_t *s; diff --git a/set.h b/set.h index d20a58d3..42522506 100644 --- a/set.h +++ b/set.h @@ -75,23 +75,23 @@ typedef struct set set_t *set_add( set_t **head, const char *key, const char *def, set_eval eval, void *data ); /* Returns the raw set_t. Might be useful sometimes. */ -set_t *set_find( set_t **head, char *key ); +set_t *set_find( set_t **head, const char *key ); /* Returns a pointer to the string value of this setting. Don't modify the returned string, and don't free() it! */ -G_MODULE_EXPORT char *set_getstr( set_t **head, char *key ); +G_MODULE_EXPORT char *set_getstr( set_t **head, const char *key ); /* Get an integer. In previous versions set_getint() was also used to read boolean values, but this SHOULD be done with set_getbool() now! */ -G_MODULE_EXPORT int set_getint( set_t **head, char *key ); -G_MODULE_EXPORT int set_getbool( set_t **head, char *key ); +G_MODULE_EXPORT int set_getint( set_t **head, const char *key ); +G_MODULE_EXPORT int set_getbool( set_t **head, const char *key ); /* set_setstr() strdup()s the given value, so after using this function you can free() it, if you want. */ -int set_setstr( set_t **head, char *key, char *value ); -int set_setint( set_t **head, char *key, int value ); -void set_del( set_t **head, char *key ); -int set_reset( set_t **head, char *key ); +int set_setstr( set_t **head, const char *key, char *value ); +int set_setint( set_t **head, const char *key, int value ); +void set_del( set_t **head, const char *key ); +int set_reset( set_t **head, const char *key ); /* Two very useful generic evaluators. */ char *set_eval_int( set_t *set, char *value ); -- cgit v1.2.3 From ec5e57d6f165a462ac686341f9075243f0a4586a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 01:00:24 +0100 Subject: Support for setting away states. Somewhat hackish but this stuff is hopelessly complicated in libpurple anyway.. --- protocols/purple/purple.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 82978dc4..c78b15e7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -173,11 +173,34 @@ static int purple_buddy_msg( struct im_connection *ic, char *who, char *message, static GList *purple_away_states( struct im_connection *ic ) { - return NULL; + PurpleAccount *pa = ic->proto_data; + GList *st, *ret = NULL; + + for( st = purple_account_get_status_types( pa ); st; st = st->next ) + { + printf( "%s\n", purple_status_type_get_name( st->data ) ); + ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + } + + return ret; } static void purple_set_away( struct im_connection *ic, char *state_txt, char *message ) { + PurpleAccount *pa = ic->proto_data; + GList *status_types = purple_account_get_status_types( pa ), *st; + PurpleStatusType *pst = NULL; + + for( st = status_types; st; st = st->next ) + { + pst = st->data; + + if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) + break; + } + + purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away", + TRUE, "message", message, NULL ); } static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) @@ -354,10 +377,6 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* custom_smiley_write */ NULL, /* custom_smiley_close */ NULL, /* send_confirm */ - NULL, - NULL, - NULL, - NULL }; static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) -- cgit v1.2.3 From dd0d57b10a8c2d07001ca2d4228232962ed8b95d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:19:41 +0100 Subject: Oops, forgot to drop a printf() of all away states for debugging. --- protocols/purple/purple.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c78b15e7..0e4a2f01 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -177,10 +177,7 @@ static GList *purple_away_states( struct im_connection *ic ) GList *st, *ret = NULL; for( st = purple_account_get_status_types( pa ); st; st = st->next ) - { - printf( "%s\n", purple_status_type_get_name( st->data ) ); ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); - } return ret; } -- cgit v1.2.3 From e248c7ff061e1582ed4c2919de6d615c1813e87a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:23:49 +0100 Subject: Automatically try prpl-$proto if $proto doesn't exist, and disable native protocol modules if purple is enabled; they don't go together very well. --- configure | 25 ++++++++++++++++--------- protocols/nogaim.c | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 65eb9e89..a87b3697 100755 --- a/configure +++ b/configure @@ -449,6 +449,22 @@ EOF protocols='' protoobjs='' +if [ "$purple" = 0 ]; then + echo '#undef WITH_PURPLE' >> config.h +else + echo '#define WITH_PURPLE' >> config.h + echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + protocols=$protocols'purple ' + protoobjs=$protoobjs'purple_mod.o ' + + # Having both libpurple and native IM modules in one binary may + # do strange things. Let's not do that. + msn=0 + jabber=0 + oscar=0 + yahoo=0 +fi + if [ "$msn" = 0 ]; then echo '#undef WITH_MSN' >> config.h else @@ -481,15 +497,6 @@ else protoobjs=$protoobjs'yahoo_mod.o ' fi -if [ "$purple" = 0 ]; then - echo '#undef WITH_PURPLE' >> config.h -else - echo '#define WITH_PURPLE' >> config.h - echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings - protocols=$protocols'purple ' - protoobjs=$protoobjs'purple_mod.o ' -fi - if [ "$protocols" = "PROTOCOLS = " ]; then echo "Warning: You haven't selected any communication protocol to compile!" echo " BitlBee will run, but you will be unable to connect to IM servers!" diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 8eae178d..7e8782ac 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -101,12 +101,23 @@ void register_protocol (struct prpl *p) struct prpl *find_protocol(const char *name) { GList *gl; - for (gl = protocols; gl; gl = gl->next) + + for( gl = protocols; gl; gl = gl->next ) { struct prpl *proto = gl->data; - if(!g_strcasecmp(proto->name, name)) + + if( g_strcasecmp( proto->name, name ) == 0 ) return proto; + +#ifdef WITH_PURPLE + /* I know, hardcoding is evil, but that doesn't make it + impossible. :-) */ + if( g_strncasecmp( proto->name, "prpl-", 5 ) == 0 && + g_strcasecmp( proto->name + 5, name ) == 0 ) + return proto; +#endif } + return NULL; } -- cgit v1.2.3 From 4524f664357f9f15e7535fc6f251c5be98d162da Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Oct 2009 23:37:28 +0100 Subject: Store real names in /whois. --- protocols/purple/purple.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0e4a2f01..aff4f3fa 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -297,7 +297,10 @@ static void prplcb_blist_new( PurpleBlistNode *node ) imcb_add_buddy( ic, bud->name, NULL ); if( bud->server_alias ) + { + imcb_rename_buddy( ic, bud->name, bud->server_alias ); imcb_buddy_nick_hint( ic, bud->name, bud->server_alias ); + } } } @@ -314,6 +317,9 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) if( ic == NULL ) return; + if( bud->server_alias ) + imcb_rename_buddy( ic, bud->name, bud->server_alias ); + flags |= purple_presence_is_online( bud->presence ) ? OPT_LOGGED_IN : 0; flags |= purple_presence_is_available( bud->presence ) ? 0 : OPT_AWAY; -- cgit v1.2.3 From 6967d012be48db989ce2723a6ecc2b10b537c8f7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Oct 2009 22:36:09 +0100 Subject: I think daemon mode and libpurple won't go together very well for now since libpurple seems to keep track of a merged contact list. For now people shouldn't be trying this combination. --- protocols/purple/purple.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index aff4f3fa..ca2a49f1 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -132,8 +132,16 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); + static void *irc_check = NULL; PurpleAccount *pa; + if( irc_check != NULL && irc_check != acc->irc ) + { + irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! Please use inetd or ForkDaemon mode instead." ); + return; + } + irc_check = acc->irc; + /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events on dead connections. */ @@ -384,7 +392,7 @@ static PurpleConversationUiOps bee_conv_uiops = static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { - printf( "DEBUG %s: %s", category, arg_s ); + fprintf( stderr, "DEBUG %s: %s", category, arg_s ); } static PurpleDebugUiOps bee_debug_uiops = -- cgit v1.2.3 From b3117f2524775ff7c61ead7c3bdb3799064ed97f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 22:58:20 +0000 Subject: Adding/removing contacts now works. --- protocols/purple/purple.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index ca2a49f1..6f1e6ae9 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -21,11 +21,11 @@ * * \***************************************************************************/ +#include "bitlbee.h" + #include #include -#include "bitlbee.h" - GSList *purple_connections; static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) @@ -81,9 +81,10 @@ static void purple_init( account_t *acc ) default: fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + name = NULL; } - if( def != NULL ) + if( name != NULL ) { s = set_add( &acc->set, name, def, eval, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -210,10 +211,23 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) { + PurpleBuddy *pb; + + pb = purple_buddy_new( (PurpleAccount*) ic->proto_data, who, NULL ); + purple_blist_add_buddy( pb, NULL, NULL, NULL ); + purple_account_add_buddy( (PurpleAccount*) ic->proto_data, pb ); } static void purple_remove_buddy( struct im_connection *ic, char *who, char *group ) { + PurpleBuddy *pb; + + pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); + if( pb != NULL ) + { + purple_account_remove_buddy( (PurpleAccount*) ic->proto_data, pb, NULL ); + purple_blist_remove_buddy( pb ); + } } static void purple_keepalive( struct im_connection *ic ) @@ -342,6 +356,7 @@ static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { PurpleBuddy *bud = (PurpleBuddy*) node; + /* if( node->type == PURPLE_BLIST_BUDDY_NODE ) { struct im_connection *ic = purple_ic_by_pa( bud->account ); @@ -351,6 +366,7 @@ static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) imcb_remove_buddy( ic, bud->name, NULL ); } + */ } static PurpleBlistUiOps bee_blist_uiops = -- cgit v1.2.3 From cd741d8e2bb0b7d08cf36d90f5332a639f190281 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 23:23:37 +0000 Subject: Fixed compatibility with non-libpurple version: oscar is now recognized as a protocol name, and removed prpl- hack from nogaim.c. --- protocols/nogaim.c | 8 -------- protocols/nogaim.h | 1 + protocols/purple/purple.c | 43 ++++++++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c0d4a953..f80653ff 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -110,14 +110,6 @@ struct prpl *find_protocol(const char *name) if( g_strcasecmp( proto->name, name ) == 0 ) return proto; - -#ifdef WITH_PURPLE - /* I know, hardcoding is evil, but that doesn't make it - impossible. :-) */ - if( g_strncasecmp( proto->name, "prpl-", 5 ) == 0 && - g_strcasecmp( proto->name + 5, name ) == 0 ) - return proto; -#endif } return NULL; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index dc6154e2..ae329b91 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -132,6 +132,7 @@ struct prpl { /* You should set this to the name of your protocol. * - The user sees this name ie. when imcb_log() is used. */ const char *name; + void *data; /* Added this one to be able to add per-account settings, don't think * it should be used for anything else. You are supposed to use the diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6f1e6ae9..a8733f5d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -148,7 +148,7 @@ static void purple_login( account_t *acc ) on dead connections. */ purple_connections = g_slist_prepend( purple_connections, ic ); - ic->proto_data = pa = purple_account_new( acc->user, acc->prpl->name ); + ic->proto_data = pa = purple_account_new( acc->user, (char*) acc->prpl->data ); purple_account_set_password( pa, acc->pass ); purple_sync_settings( acc, pa ); @@ -450,6 +450,7 @@ static void purple_ui_init() void purple_initmodule() { + struct prpl funcs; GList *prots; if( B_EV_IO_READ != PURPLE_INPUT_READ || @@ -477,24 +478,36 @@ void purple_initmodule() /* Meh? */ purple_prefs_load(); + memset( &funcs, 0, sizeof( funcs ) ); + funcs.login = purple_login; + funcs.init = purple_init; + funcs.logout = purple_logout; + funcs.buddy_msg = purple_buddy_msg; + funcs.away_states = purple_away_states; + funcs.set_away = purple_set_away; + funcs.add_buddy = purple_add_buddy; + funcs.remove_buddy = purple_remove_buddy; + funcs.keepalive = purple_keepalive; + funcs.send_typing = purple_send_typing; + funcs.handle_cmp = g_strcasecmp; + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { - struct prpl *ret = g_new0( struct prpl, 1 ); PurplePlugin *prot = prots->data; + struct prpl *ret; - ret->name = prot->info->id; - ret->login = purple_login; - ret->init = purple_init; - ret->logout = purple_logout; - ret->buddy_msg = purple_buddy_msg; - ret->away_states = purple_away_states; - ret->set_away = purple_set_away; - ret->add_buddy = purple_add_buddy; - ret->remove_buddy = purple_remove_buddy; - ret->keepalive = purple_keepalive; - ret->send_typing = purple_send_typing; - ret->handle_cmp = g_strcasecmp; - + ret = g_memdup( &funcs, sizeof( funcs ) ); + ret->name = ret->data = prot->info->id; + if( strncmp( ret->name, "prpl-", 5 ) == 0 ) + ret->name += 5; register_protocol( ret ); + + if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) + { + ret = g_memdup( &funcs, sizeof( funcs ) ); + ret->name = "oscar"; + ret->data = prot->info->id; + register_protocol( ret ); + } } } -- cgit v1.2.3 From 45a19e543cc567981df92b62f428e0f89f94cb74 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 23 Nov 2009 23:38:31 +0000 Subject: Oops, forgot to change one protocol name string pointer.. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index a8733f5d..f7b859ff 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -46,7 +46,7 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) static void purple_init( account_t *acc ) { - PurplePlugin *prpl = purple_plugins_find_with_id( acc->prpl->name ); + PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); PurplePluginProtocolInfo *pi = prpl->info->extra_info; GList *i; -- cgit v1.2.3 From 0ac1a37573f966d7a03b85816c583bd6976c402f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 25 Nov 2009 00:19:45 +0000 Subject: Added enough code to handle one class of queries (action-based), enough to make the "Please accept this SSL certificate" question work. Need to extend the BitlBee API a bit to *really* support this well though (yes/no is not enough). --- protocols/purple/purple.c | 90 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f7b859ff..33b19a67 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -23,11 +23,18 @@ #include "bitlbee.h" +#include + #include #include GSList *purple_connections; +/* This makes me VERY sad... :-( But some libpurple callbacks come in without + any context so this is the only way to get that. Don't want to support + libpurple in daemon mode anyway. */ +static irc_t *local_irc; + static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -133,15 +140,15 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) static void purple_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); - static void *irc_check = NULL; PurpleAccount *pa; - if( irc_check != NULL && irc_check != acc->irc ) + if( local_irc != NULL && local_irc != acc->irc ) { - irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! Please use inetd or ForkDaemon mode instead." ); + irc_usermsg( acc->irc, "Daemon mode detected. Do *not* try to use libpurple in daemon mode! " + "Please use inetd or ForkDaemon mode instead." ); return; } - irc_check = acc->irc; + local_irc = acc->irc; /* For now this is needed in the _connected() handlers if using GLib event handling, to make sure we're not handling events @@ -406,6 +413,80 @@ static PurpleConversationUiOps bee_conv_uiops = NULL, /* send_confirm */ }; +struct prplcb_request_action_data +{ + void *user_data, *bee_data; + PurpleRequestActionCb yes, no; + int yes_i, no_i; +}; + +static void prplcb_request_action_yes( void *data ) +{ + struct prplcb_request_action_data *pqad = data; + + pqad->yes( pqad->user_data, pqad->yes_i ); + g_free( pqad ); +} + +static void prplcb_request_action_no( void *data ) +{ + struct prplcb_request_action_data *pqad = data; + + pqad->no( pqad->user_data, pqad->no_i ); + g_free( pqad ); +} + +static void *prplcb_request_action( const char *title, const char *primary, const char *secondary, + int default_action, PurpleAccount *account, const char *who, + PurpleConversation *conv, void *user_data, size_t action_count, + va_list actions ) +{ + struct prplcb_request_action_data *pqad; + int i; + char *q; + + pqad = g_new0( struct prplcb_request_action_data, 1 ); + + for( i = 0; i < action_count; i ++ ) + { + char *caption; + void *fn; + + caption = va_arg( actions, char* ); + fn = va_arg( actions, void* ); + + if( strcmp( caption, "Accept" ) == 0 ) + { + pqad->yes = fn; + pqad->yes_i = i; + } + else if( strcmp( caption, "Reject" ) == 0 ) + { + pqad->no = fn; + pqad->no_i = i; + } + } + + pqad->user_data = user_data; + + q = g_strdup_printf( "Request: %s\n\n%s\n\n%s", title, primary, secondary ); + pqad->bee_data = query_add( local_irc, purple_ic_by_pa( account ), q, + prplcb_request_action_yes, prplcb_request_action_no, pqad ); + + g_free( q ); +} + +static PurpleRequestUiOps bee_request_uiops = +{ + NULL, + NULL, + prplcb_request_action, + NULL, + NULL, + NULL, + NULL, +}; + static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { fprintf( stderr, "DEBUG %s: %s", category, arg_s ); @@ -445,6 +526,7 @@ static void purple_ui_init() purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); + purple_request_set_ui_ops( &bee_request_uiops ); //purple_debug_set_ui_ops( &bee_debug_uiops ); } -- cgit v1.2.3 From e5d8d21fd20516be53f873d269b469be109eca91 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 25 Nov 2009 00:45:27 +0000 Subject: Added in-memory help info, which I wanted to implement for ages already. Sadly the way I'm using it now doesn't work yet since nogaim_init() is called before help_init(). I'll fix that later. (Have to do that anyway to at least make ForkDaemon mode work..) --- help.c | 26 +++++++++++++++++++++++++- help.h | 1 + protocols/purple/purple.c | 11 +++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/help.c b/help.c index 587b9940..c43d0459 100644 --- a/help.c +++ b/help.c @@ -1,7 +1,7 @@ /********************************************************************\ * BitlBee -- An IRC to other IM-networks gateway * * * - * Copyright 2002-2005 Wilmer van der Gaast and others * + * Copyright 2002-2009 Wilmer van der Gaast and others * \********************************************************************/ /* Help file control */ @@ -168,3 +168,27 @@ char *help_get( help_t **help, char *title ) return NULL; } + +int help_add_mem( help_t **help, const char *title, const char *content ) +{ + help_t *h, *l = NULL; + + for( h = *help; h; h = h->next ) + { + if( g_strcasecmp( h->title, title ) == 0 ) + return 0; + + l = h; + } + + if( l ) + h = l->next = g_new0( struct help, 1 ); + else + *help = h = g_new0( struct help, 1 ); + h->fd = -1; + h->title = g_strdup( title ); + h->length = strlen( content ); + h->offset.mem_offset = g_strdup( content ); + + return 1; +} diff --git a/help.h b/help.h index 5421220c..e689a93d 100644 --- a/help.h +++ b/help.h @@ -45,5 +45,6 @@ typedef struct help G_GNUC_MALLOC help_t *help_init( help_t **help, const char *helpfile ); void help_free( help_t **help ); char *help_get( help_t **help, char *title ); +int help_add_mem( help_t **help, const char *title, const char *content_ ); #endif diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 33b19a67..50770187 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -22,6 +22,7 @@ \***************************************************************************/ #include "bitlbee.h" +#include "help.h" #include @@ -474,6 +475,8 @@ static void *prplcb_request_action( const char *title, const char *primary, cons prplcb_request_action_yes, prplcb_request_action_no, pqad ); g_free( q ); + + return pqad; } static PurpleRequestUiOps bee_request_uiops = @@ -534,6 +537,7 @@ void purple_initmodule() { struct prpl funcs; GList *prots; + GString *help; if( B_EV_IO_READ != PURPLE_INPUT_READ || B_EV_IO_WRITE != PURPLE_INPUT_WRITE ) @@ -573,6 +577,8 @@ void purple_initmodule() funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; + help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); + for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { PurplePlugin *prot = prots->data; @@ -584,6 +590,8 @@ void purple_initmodule() ret->name += 5; register_protocol( ret ); + g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name ); + if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) { ret = g_memdup( &funcs, sizeof( funcs ) ); @@ -592,4 +600,7 @@ void purple_initmodule() register_protocol( ret ); } } + + help_add_mem( &global.help, "purple", help->str ); + g_string_free( help, TRUE ); } -- cgit v1.2.3 From 487f555c687571cee39d69b3954b4d1f82811d29 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 26 Nov 2009 00:04:40 +0000 Subject: Support for sending zomg-im-typing notifications. --- protocols/purple/purple.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 50770187..6bb8fc99 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -242,9 +242,26 @@ static void purple_keepalive( struct im_connection *ic ) { } -static int purple_send_typing( struct im_connection *ic, char *who, int typing ) +static int purple_send_typing( struct im_connection *ic, char *who, int flags ) { - return 1; + PurpleTypingState state = PURPLE_NOT_TYPING; + PurpleConversation *conv; + + if( flags & OPT_TYPING ) + state = PURPLE_TYPING; + else if( flags & OPT_THINKING ) + state = PURPLE_TYPED; + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_IM, + who, ic->proto_data ) ) == NULL ) + { + purple_conv_im_set_typing_state( purple_conversation_get_im_data( conv ), state ); + return 1; + } + else + { + return 0; + } } static void purple_ui_init(); -- cgit v1.2.3 From 3e7b640db6ae2d77122d93dcf5f1a0989ef0b3f1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 28 Nov 2009 00:47:20 +0000 Subject: Look up a buddy in the contact list on incoming msgs. This seems to be the best way to "normalize" handles (i.e. chopping off the resource part of JIDs). --- protocols/purple/purple.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6bb8fc99..f28e5cf0 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -379,9 +379,9 @@ static void prplcb_blist_update( PurpleBuddyList *list, PurpleBlistNode *node ) static void prplcb_blist_remove( PurpleBuddyList *list, PurpleBlistNode *node ) { + /* PurpleBuddy *bud = (PurpleBuddy*) node; - /* if( node->type == PURPLE_BLIST_BUDDY_NODE ) { struct im_connection *ic = purple_ic_by_pa( bud->account ); @@ -406,10 +406,17 @@ static PurpleBlistUiOps bee_blist_uiops = static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) { struct im_connection *ic = purple_ic_by_pa( conv->account ); + PurpleBuddy *buddy; /* ..._SEND means it's an outgoing message, no need to echo those. */ - if( !( flags & PURPLE_MESSAGE_SEND ) ) - imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); + if( flags & PURPLE_MESSAGE_SEND ) + return; + + buddy = purple_find_buddy( conv->account, who ); + if( buddy != NULL ) + who = purple_buddy_get_contact_alias( buddy ); + + imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } static PurpleConversationUiOps bee_conv_uiops = -- cgit v1.2.3 From 5674207b09a856149c110040f7f672182a04dee5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 28 Nov 2009 19:51:39 +0000 Subject: Reshuffled initialization sequence a little bit. Most important change: nogaim_init() should be done after fork() to make ForkDaemon mode work again. Also, doing help_init() earlie makes "help purple" work. --- irc.c | 2 ++ unix.c | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/irc.c b/irc.c index ea0f7db7..9a5c96d8 100644 --- a/irc.c +++ b/irc.c @@ -171,6 +171,8 @@ irc_t *irc_new( int fd ) /* Evaluator sets the iconv/oconv structures. */ set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) ); + nogaim_init(); + return( irc ); } diff --git a/unix.c b/unix.c index 56b0ab46..18b2f83f 100644 --- a/unix.c +++ b/unix.c @@ -46,16 +46,26 @@ int main( int argc, char *argv[] ) struct sigaction sig, old; log_init(); + global.conf_file = g_strdup( CONF_FILE_DEF ); global.conf = conf_load( argc, argv ); if( global.conf == NULL ) return( 1 ); b_main_init(); - nogaim_init(); srand( time( NULL ) ^ getpid() ); + global.helpfile = g_strdup( HELP_FILE ); + if( help_init( &global.help, global.helpfile ) == NULL ) + log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); + + global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); + if( global.storage == NULL ) + { + log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); + return( 1 ); + } if( global.conf->runmode == RUNMODE_INETD ) { @@ -104,13 +114,6 @@ int main( int argc, char *argv[] ) setuid( pw->pw_uid ); } } - - global.storage = storage_init( global.conf->primary_storage, global.conf->migrate_storage ); - if( global.storage == NULL ) - { - log_message( LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage ); - return( 1 ); - } /* Catch some signals to tell the user what's happening before quitting */ memset( &sig, 0, sizeof( sig ) ); @@ -129,8 +132,6 @@ int main( int argc, char *argv[] ) if( !getuid() || !geteuid() ) log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); - if( help_init( &global.help, global.helpfile ) == NULL ) - log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); b_main_run(); -- cgit v1.2.3 From 1b221e0abd6453e3ca9cf45916ff6d16f94eff2b Mon Sep 17 00:00:00 2001 From: Geert Mulders Date: Tue, 1 Dec 2009 22:08:02 +0100 Subject: Added twitter-module. --- configure | 10 ++ protocols/nogaim.c | 5 + protocols/twitter/Makefile | 43 +++++ protocols/twitter/twitter.c | 221 +++++++++++++++++++++++ protocols/twitter/twitter.h | 42 +++++ protocols/twitter/twitter_http.c | 235 ++++++++++++++++++++++++ protocols/twitter/twitter_http.h | 34 ++++ protocols/twitter/twitter_lib.c | 374 +++++++++++++++++++++++++++++++++++++++ protocols/twitter/twitter_lib.h | 84 +++++++++ 9 files changed, 1048 insertions(+) create mode 100644 protocols/twitter/Makefile create mode 100644 protocols/twitter/twitter.c create mode 100644 protocols/twitter/twitter.h create mode 100644 protocols/twitter/twitter_http.c create mode 100644 protocols/twitter/twitter_http.h create mode 100644 protocols/twitter/twitter_lib.c create mode 100644 protocols/twitter/twitter_lib.h diff --git a/configure b/configure index 9d92cedf..481493d4 100755 --- a/configure +++ b/configure @@ -25,6 +25,7 @@ msn=1 jabber=1 oscar=1 yahoo=1 +twitter=1 debug=0 strip=1 @@ -65,6 +66,7 @@ Option Description Default --jabber=0/1 Disable/enable Jabber part $jabber --oscar=0/1 Disable/enable Oscar part (ICQ, AIM) $oscar --yahoo=0/1 Disable/enable Yahoo part $yahoo +--twitter=0/1 Disable/enable Twitter part $twitter --debug=0/1 Disable/enable debugging $debug --strip=0/1 Disable/enable binary stripping $strip @@ -511,6 +513,14 @@ else protoobjs=$protoobjs'yahoo_mod.o ' fi +if [ "$twitter" = 0 ]; then + echo '#undef WITH_TWITTER' >> config.h +else + echo '#define WITH_TWITTER' >> config.h + protocols=$protocols'twitter ' + protoobjs=$protoobjs'twitter_mod.o ' +fi + if [ "$protocols" = "PROTOCOLS = " ]; then echo "Warning: You haven't selected any communication protocol to compile!" echo " BitlBee will run, but you will be unable to connect to IM servers!" diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 21f7dcb1..a9eb207a 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -119,6 +119,7 @@ void nogaim_init() extern void oscar_initmodule(); extern void byahoo_initmodule(); extern void jabber_initmodule(); + extern void twitter_initmodule(); #ifdef WITH_MSN msn_initmodule(); @@ -136,6 +137,10 @@ void nogaim_init() jabber_initmodule(); #endif +#ifdef WITH_TWITTER + twitter_initmodule(); +#endif + #ifdef WITH_PLUGINS load_plugins(); #endif diff --git a/protocols/twitter/Makefile b/protocols/twitter/Makefile new file mode 100644 index 00000000..ca1e4695 --- /dev/null +++ b/protocols/twitter/Makefile @@ -0,0 +1,43 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = twitter.o twitter_http.o twitter_lib.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: twitter_mod.o +check: all +lcov: check +gcov: + gcov *.c + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +twitter_mod.o: $(objects) + @echo '*' Linking twitter_mod.o + @$(LD) $(LFLAGS) $(objects) -o twitter_mod.o + + diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c new file mode 100644 index 00000000..1cc7eaeb --- /dev/null +++ b/protocols/twitter/twitter.c @@ -0,0 +1,221 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#include "nogaim.h" +#include "twitter.h" +#include "twitter_http.h" +#include "twitter_lib.h" + + +/** + * * Main loop function + * */ +gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) +{ + struct im_connection *ic = data; + // Check if we are still logged in... + if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) + return 0; + + // Do stuff.. + twitter_get_home_timeline(ic, -1); + + // If we are still logged in run this function again after timeout. + return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; +} + + +static void twitter_init( account_t *acc ) +{ +} + +/** + * Login method. Since the twitter API works with seperate HTTP request we + * only save the user and pass to the twitter_data object. + */ +static void twitter_login( account_t *acc ) +{ + struct im_connection *ic = imcb_new( acc ); + struct twitter_data *td = g_new0( struct twitter_data, 1 ); + + td->user = acc->user; + td->pass = acc->pass; + td->home_timeline_id = 0; + + ic->proto_data = td; + + // Set the status to logged in. + ic->flags = OPT_LOGGED_IN; + + // Try to get the buddies... + //twitter_get_friends_ids(ic, -1); + + //twitter_get_home_timeline(ic, -1); + + // Run this once. After this queue the main loop function. + twitter_main_loop(ic, -1, 0); + + // Queue the main_loop + b_timeout_add(60000, twitter_main_loop, ic); + + imcb_log( ic, "Connecting to twitter" ); + imcb_connected(ic); +} + +/** + * Logout method. Just free the twitter_data. + */ +static void twitter_logout( struct im_connection *ic ) +{ + struct twitter_data *td = ic->proto_data; + + // Set the status to logged out. + ic->flags = 0; + + if( td ) + { + g_free( td ); + } +} + +/** + * + */ +static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) +{ + imcb_log( ic, "In twitter_buddy_msg..."); + twitter_post_status(ic, message); + return( 0 ); +} + +/** + * + */ +static GList *twitter_away_states( struct im_connection *ic ) +{ + static GList *l = NULL; + return l; +} + +static void twitter_set_away( struct im_connection *ic, char *state, char *message ) +{ +} + +static void twitter_set_my_name( struct im_connection *ic, char *info ) +{ + imcb_log( ic, "In twitter_set_my_name..." ); +// char * aap = twitter_http("http://gertje.org", NULL, ic, 1, "geert", "poep", NULL, 0); + +// imcb_log( ic, aap ); +// g_free(aap); +} + +static void twitter_get_info(struct im_connection *ic, char *who) +{ +} + +static void twitter_add_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group ) +{ +} + +static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) +{ +} + +static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) +{ +} + +static void twitter_chat_leave( struct groupchat *c ) +{ +} + +static struct groupchat *twitter_chat_with( struct im_connection *ic, char *who ) +{ + return NULL; +} + +static void twitter_keepalive( struct im_connection *ic ) +{ +} + +static void twitter_add_permit( struct im_connection *ic, char *who ) +{ +} + +static void twitter_rem_permit( struct im_connection *ic, char *who ) +{ +} + +static void twitter_add_deny( struct im_connection *ic, char *who ) +{ +} + +static void twitter_rem_deny( struct im_connection *ic, char *who ) +{ +} + +static int twitter_send_typing( struct im_connection *ic, char *who, int typing ) +{ + return( 1 ); +} + +//static char *twitter_set_display_name( set_t *set, char *value ) +//{ +// return value; +//} + +void twitter_initmodule() +{ + struct prpl *ret = g_new0(struct prpl, 1); + + ret->name = "twitter"; + ret->login = twitter_login; + ret->init = twitter_init; + ret->logout = twitter_logout; + ret->buddy_msg = twitter_buddy_msg; + ret->away_states = twitter_away_states; + ret->set_away = twitter_set_away; + ret->get_info = twitter_get_info; + ret->set_my_name = twitter_set_my_name; + ret->add_buddy = twitter_add_buddy; + ret->remove_buddy = twitter_remove_buddy; + ret->chat_msg = twitter_chat_msg; + ret->chat_invite = twitter_chat_invite; + ret->chat_leave = twitter_chat_leave; + ret->chat_with = twitter_chat_with; + ret->keepalive = twitter_keepalive; + ret->add_permit = twitter_add_permit; + ret->rem_permit = twitter_rem_permit; + ret->add_deny = twitter_add_deny; + ret->rem_deny = twitter_rem_deny; + ret->send_typing = twitter_send_typing; + ret->handle_cmp = g_strcasecmp; + + register_protocol(ret); +} + diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h new file mode 100644 index 00000000..58791954 --- /dev/null +++ b/protocols/twitter/twitter.h @@ -0,0 +1,42 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#include "nogaim.h" + +#ifndef _TWITTER_H +#define _TWITTER_H + +#ifdef DEBUG_TWITTER +#define debug( text... ) imcb_log( ic, text ); +#else +#define debug( text... ) +#endif + +struct twitter_data +{ + char* user; + char* pass; + guint64 home_timeline_id; +}; + +#endif //_TWITTER_H diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c new file mode 100644 index 00000000..4385475c --- /dev/null +++ b/protocols/twitter/twitter_http.c @@ -0,0 +1,235 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +/***************************************************************************\ +* * +* Some funtions within this file have been copied from other files within * +* BitlBee. * +* * +****************************************************************************/ + +#include "twitter_http.h" +#include "twitter.h" +#include "bitlbee.h" +#include "url.h" +#include "misc.h" +#include "base64.h" +#include +#include + + +char *twitter_urlencode(const char *instr); +char *twitter_url_append(char *url, char *key, char* value); +static int isurlchar(unsigned char c); + +/** + * Do a request. + * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c + */ +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char** arguments, int arguments_len) +{ + url_t *url = g_new0( url_t, 1 ); + char *tmp; + char *request; + void *ret; + char *userpass = NULL; + char *userpass_base64; + char *url_arguments; + + // Fill the url structure. + if( !url_set( url, url_string ) ) + { + g_free( url ); + return NULL; + } + + if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS ) + { + g_free( url ); + return NULL; + } + + // Concatenate user and pass + if (user && pass) { + userpass = g_strdup_printf("%s:%s", user, pass); + userpass_base64 = base64_encode((unsigned char*)userpass, strlen(userpass)); + } else { + userpass_base64 = NULL; + } + + url_arguments = g_malloc(1); + url_arguments[0] = '\0'; + + // Construct the url arguments. + if (arguments_len != 0) + { + int i; + for (i=0; ifile + strlen(url->file); + tmp[0] = '?'; + // append the url_arguments to the end of the url->file. + // TODO GM: Check the length? + g_stpcpy (tmp+1, url_arguments); + } + + + // Make the request. + request = g_strdup_printf( "%s %s HTTP/1.0\r\n" + "Host: %s\r\n" + "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", + is_post ? "POST" : "GET", url->file, url->host ); + + // If a pass and user are given we append them to the request. + if (userpass_base64) + { + tmp = g_strdup_printf("%sAuthorization: Basic %s\r\n", request, userpass_base64); + g_free(request); + request = tmp; + } + + // Do POST stuff.. + if (is_post) + { + // Append the Content-Type and url-encoded arguments. + tmp = g_strdup_printf("%sContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %i\r\n\r\n%s", + request, strlen(url_arguments), url_arguments); + g_free(request); + request = tmp; + } else { + // Append an extra \r\n to end the request... + tmp = g_strdup_printf("%s\r\n", request); + g_free(request); + request = tmp; + } + + ret = http_dorequest( url->host, url->port, url->proto == PROTO_HTTPS, request, func, data ); + + g_free( url ); + g_free( userpass ); + g_free( userpass_base64 ); + g_free( url_arguments ); + g_free( request ); + return ret; +} + +char *twitter_url_append(char *url, char *key, char* value) +{ + char *key_encoded = twitter_urlencode(key); + char *value_encoded = twitter_urlencode(value); + char *retval; + if (strlen(url) != 0) + retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded); + else + retval = g_strdup_printf("%s=%s", key_encoded, value_encoded); + + g_free(key_encoded); + g_free(value_encoded); + + return retval; +} + +char *twitter_urlencode(const char *instr) +{ + int ipos=0, bpos=0; + char *str = NULL; + int len = strlen(instr); + + if(!(str = g_new(char, 3*len + 1) )) + return ""; + + while(instr[ipos]) { + while(isurlchar(instr[ipos])) + str[bpos++] = instr[ipos++]; + if(!instr[ipos]) + break; + + g_snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]); + bpos+=3; + ipos++; + } + str[bpos]='\0'; + + /* free extra alloc'ed mem. */ + len = strlen(str); + str = g_renew(char, str, len+1); + + return (str); +} + + +char *twitter_urldecode(const char *instr) +{ + int ipos=0, bpos=0; + char *str = NULL; + char entity[3]={0,0,0}; + unsigned dec; + int len = strlen(instr); + + if(!(str = g_new(char, len+1) )) + return ""; + + while(instr[ipos]) { + while(instr[ipos] && instr[ipos]!='%') + if(instr[ipos]=='+') { + str[bpos++]=' '; + ipos++; + } else + str[bpos++] = instr[ipos++]; + if(!instr[ipos]) + break; + + if(instr[ipos+1] && instr[ipos+2]) { + ipos++; + entity[0]=instr[ipos++]; + entity[1]=instr[ipos++]; + sscanf(entity, "%2x", &dec); + str[bpos++] = (char)dec; + } else { + str[bpos++] = instr[ipos++]; + } + } + str[bpos]='\0'; + + /* free extra alloc'ed mem. */ + len = strlen(str); + str = g_renew(char, str, len+1); + + return (str); +} + +static int isurlchar(unsigned char c) +{ + return (isalnum(c) || '-' == c || '_' == c); +} + diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h new file mode 100644 index 00000000..ec4a0b7c --- /dev/null +++ b/protocols/twitter/twitter_http.h @@ -0,0 +1,34 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#ifndef _TWITTER_HTTP_H +#define _TWITTER_HTTP_H + +#include "nogaim.h" +#include "http_client.h" + +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, + char* user, char* pass, char** arguments, int arguments_len); + +#endif //_TWITTER_HTTP_H + diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c new file mode 100644 index 00000000..7ecbfe15 --- /dev/null +++ b/protocols/twitter/twitter_lib.c @@ -0,0 +1,374 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + +#include "twitter_http.h" +#include "twitter.h" +#include "bitlbee.h" +#include "url.h" +#include "misc.h" +#include "base64.h" +#include "xmltree.h" +#include "twitter_lib.h" +#include +#include + +#define TXL_STATUS 1 +#define TXL_ID 1 + +struct twitter_xml_list { + int next_cursor; + GSList *list; + gpointer data; +}; + +struct twitter_xml_user { + char *name; + char *screen_name; +}; + +struct twitter_xml_status { + char *created_at; + char *text; + struct twitter_xml_user *user; + guint64 id; +}; + +void txl_free(struct twitter_xml_list *txl, int type); +void txs_free(struct twitter_xml_status *txs); +void txu_free(struct twitter_xml_user *txu); + +static void twitter_http_get_friends_ids(struct http_request *req); + +/** + * Get the friends ids. + */ +void twitter_get_friends_ids(struct im_connection *ic, int next_cursor) +{ + struct twitter_data *td = ic->proto_data; + + // Primitive, but hey! It works... + char* args[2]; + args[0] = "cursor"; + args[1] = g_strdup_printf ("%d", next_cursor); + twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2); + + g_free(args[1]); +} + +/** + * Function to help fill a list. + */ +static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl ) +{ + // Do something with the cursor. + txl->next_cursor = atoi(node->text); + + return XT_HANDLED; +} + +/** + * Fill a list of ids. + */ +static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl ) +{ + struct xt_node *child; + + // The root node should hold the list of statuses + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "id", child->name ) == 0) + { + // Add the item to the list. + txl->list = g_slist_append (txl->list, g_memdup( node->text, node->text_len + 1 )); + } + else if ( g_strcasecmp( "next_cursor", child->name ) == 0) + { + twitter_xt_next_cursor(child, txl); + } + } + + return XT_HANDLED; +} + +/** + * Callback for getting the friends ids. + */ +static void twitter_http_get_friends_ids(struct http_request *req) +{ + struct im_connection *ic; + struct xt_parser *parser; + struct twitter_xml_list *txl; + + ic = req->data; + + // Check if the HTTP request went well. + if (req->status_code != 200) { + // It didn't go well, output the error and return. + imcb_error(ic, "Could not retrieve friends. HTTP STATUS: %d", req->status_code); + return; + } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + + // Parse the data. + parser = xt_new( NULL, txl ); + xt_feed( parser, req->reply_body, req->body_size ); + twitter_xt_get_friends_id_list(parser->root, txl); + xt_free( parser ); + + if (txl->next_cursor) + twitter_get_friends_ids(ic, txl->next_cursor); + + txl_free(txl, TXL_ID); + g_free(txl); +} + +/** + * Function to fill a twitter_xml_user struct. + * It sets: + * - the name and + * - the screen_name. + */ +static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu ) +{ + struct xt_node *child; + + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "name", child->name ) == 0) + { + txu->name = g_memdup( child->text, child->text_len + 1 ); + } + else if (g_strcasecmp( "screen_name", child->name ) == 0) + { + txu->screen_name = g_memdup( child->text, child->text_len + 1 ); + } + } + return XT_HANDLED; +} + +/** + * Function to fill a twitter_xml_status struct. + * It sets: + * - the status text and + * - the created_at timestamp and + * - the status id and + * - the user in a twitter_xml_user struct. + */ +static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs ) +{ + struct xt_node *child; + + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "text", child->name ) == 0) + { + txs->text = g_memdup( child->text, child->text_len + 1 ); + } + else if (g_strcasecmp( "created_at", child->name ) == 0) + { + txs->created_at = g_memdup( child->text, child->text_len + 1 ); + } + else if (g_strcasecmp( "user", child->name ) == 0) + { + txs->user = g_new0(struct twitter_xml_user, 1); + twitter_xt_get_user( child, txs->user ); + } + else if (g_strcasecmp( "id", child->name ) == 0) + { + txs->id = g_ascii_strtoull (child->text, NULL, 10); + } + } + return XT_HANDLED; +} + +/** + * Function to fill a twitter_xml_list struct. + * It sets: + * - all es within the element and + * - the next_cursor. + */ +static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitter_xml_list *txl ) +{ + struct twitter_xml_status *txs; + struct xt_node *child; + + // The root node should hold the list of statuses + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "status", child->name ) == 0) + { + txs = g_new0(struct twitter_xml_status, 1); + twitter_xt_get_status(child, txs); + // Put the item in the front of the list. + txl->list = g_slist_prepend (txl->list, txs); + } + else if ( g_strcasecmp( "next_cursor", child->name ) == 0) + { + twitter_xt_next_cursor(child, txl); + } + } + + return XT_HANDLED; +} + +static void twitter_http_get_home_timeline(struct http_request *req); + +/** + * Get the timeline. + */ +void twitter_get_home_timeline(struct im_connection *ic, int next_cursor) +{ + struct twitter_data *td = ic->proto_data; + + char* args[4]; + args[0] = "cursor"; + args[1] = g_strdup_printf ("%d", next_cursor); + if (td->home_timeline_id) { + args[2] = "since_id"; + args[3] = g_strdup_printf ("%llu", td->home_timeline_id); + } + + twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2); + + g_free(args[1]); + if (td->home_timeline_id) { + g_free(args[3]); + } +} + +/** + * Callback for getting the home timeline. + */ +static void twitter_http_get_home_timeline(struct http_request *req) +{ + struct im_connection *ic = req->data;; + struct xt_parser *parser; + struct twitter_xml_list *txl; + struct twitter_data *td = ic->proto_data; + + // Check if the HTTP request went well. + if (req->status_code != 200) { + // It didn't go well, output the error and return. + imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code); + return; + } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + + // Parse the data. + parser = xt_new( NULL, txl ); + xt_feed( parser, req->reply_body, req->body_size ); + // The root node should hold the list of statuses + twitter_xt_get_status_list(parser->root, txl); + xt_free( parser ); + + GSList *l; + struct twitter_xml_status *status; + + imcb_add_buddy( ic, "home_timeline", NULL ); + imcb_buddy_status( ic, "home_timeline", OPT_LOGGED_IN, NULL, NULL ); + + for ( l = txl->list; l ; l = g_slist_next(l) ) + { + status = l->data; + imcb_buddy_msg( ic, "home_timeline", status->text, 0, 0 ); + td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; + } + + // Free the structure. + txl_free(txl, TXL_STATUS); + g_free(txl); +} + +/** + * Free a twitter_xml_list struct. + * type is the type of list the struct holds. + */ +void txl_free(struct twitter_xml_list *txl, int type) +{ + GSList *l; + for ( l = txl->list; l ; l = g_slist_next(l) ) + if (type == TXL_STATUS) + txs_free((struct twitter_xml_status *)l->data); + else if (type == TXL_ID) + g_free(l->data); + g_slist_free(txl->list); +} + +/** + * Frees a twitter_xml_status struct. + */ +void txs_free(struct twitter_xml_status *txs) +{ + g_free(txs->created_at); + g_free(txs->text); + txu_free(txs->user); +} + +/** + * Frees a twitter_xml_user struct. + */ +void txu_free(struct twitter_xml_user *txu) +{ + g_free(txu->name); + g_free(txu->screen_name); +} + +/** + * Callback after sending a new update to twitter. + */ +static void twitter_http_post_status(struct http_request *req) +{ + struct im_connection *ic = req->data; + + // Check if the HTTP request went well. + if (req->status_code != 200) { + // It didn't go well, output the error and return. + imcb_error(ic, "Could not post tweed... HTTP STATUS: %d", req->status_code); + imcb_error(ic, req->reply_body); + return; + } +} + +/** + * Function to POST a new status to twitter. + */ +void twitter_post_status(struct im_connection *ic, char* msg) +{ + struct twitter_data *td = ic->proto_data; + + char* args[2]; + args[0] = "status"; + args[1] = msg; + twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2); + g_free(args[1]); +} + + diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h new file mode 100644 index 00000000..28ca871f --- /dev/null +++ b/protocols/twitter/twitter_lib.h @@ -0,0 +1,84 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple module to facilitate twitter functionality. * +* * +* Copyright 2009 Geert Mulders * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +****************************************************************************/ + + +#ifndef _TWITTER_LIB_H +#define _TWITTER_LIB_H + +#include "nogaim.h" +#include "twitter_http.h" + +#define TWITTER_API_URL "http://twitter.com" + +/* Status URLs */ +#define TWITTER_STATUS_UPDATE_URL TWITTER_API_URL "/statuses/update.xml" +#define TWITTER_STATUS_SHOW_URL TWITTER_API_URL "/statuses/show/" +#define TWITTER_STATUS_DESTROY_URL TWITTER_API_URL "/statuses/destroy/" + +/* Timeline URLs */ +#define TWITTER_PUBLIC_TIMELINE_URL TWITTER_API_URL "/statuses/public_timeline.xml" +#define TWITTER_FEATURED_USERS_URL TWITTER_API_URL "/statuses/featured.xml" +#define TWITTER_FRIENDS_TIMELINE_URL TWITTER_API_URL "/statuses/friends_timeline.xml" +#define TWITTER_HOME_TIMELINE_URL TWITTER_API_URL "/statuses/home_timeline.xml" +#define TWITTER_MENTIONS_URL TWITTER_API_URL "/statuses/mentions.xml" +#define TWITTER_USER_TIMELINE_URL TWITTER_API_URL "/statuses/user_timeline.xml" + +/* Users URLs */ +#define TWITTER_SHOW_USERS_URL TWITTER_API_URL "/users/show.xml" +#define TWITTER_SHOW_FRIENDS_URL TWITTER_API_URL "/statuses/friends.xml" +#define TWITTER_SHOW_FOLLOWERS_URL TWITTER_API_URL "/statuses/followers.xml" + +/* Direct messages URLs */ +#define TWITTER_DIRECT_MESSAGES_URL TWITTER_API_URL "/direct_messages.xml" +#define TWITTER_DIRECT_MESSAGENEW_URL TWITTER_API_URL "/direct_messages/new.xml" +#define TWITTER_DIRECT_MESSAGESSENT_URL TWITTER_API_URL "/direct_messages/sent.xml" +#define TWITTER_DIRECT_MESSAGEDESTROY_URL TWITTER_API_URL "/direct_messages/destroy/" + +/* Friendships URLs */ +#define TWITTER_FRIENDSHIPS_CREATE_URL TWITTER_API_URL "/friendships/create.xml" +#define TWITTER_FRIENDSHIPS_DESTROY_URL TWITTER_API_URL "/friendships/destroy.xml" +#define TWITTER_FRIENDSHIPS_SHOW_URL TWITTER_API_URL "/friendships/show.xml" + +/* Social graphs URLs */ +#define TWITTER_FRIENDS_IDS_URL TWITTER_API_URL "/friends/ids.xml" +#define TWITTER_FOLLOWERS_IDS_URL TWITTER_API_URL "/followers/ids.xml" + +/* Account URLs */ +#define TWITTER_ACCOUNT_RATE_LIMIT_URL TWITTER_API_URL "/account/rate_limit_status.xml" + +/* Favorites URLs */ +#define TWITTER_FAVORITES_GET_URL TWITTER_API_URL "/favorites.xml" +#define TWITTER_FAVORITE_CREATE_URL TWITTER_API_URL "/favorites/create/" +#define TWITTER_FAVORITE_DESTROY_URL TWITTER_API_URL "/favorites/destroy/" + +/* Block URLs */ +#define TWITTER_BLOCKS_CREATE_URL TWITTER_API_URL "/blocks/create/" +#define TWITTER_BLOCKS_DESTROY_URL TWITTER_API_URL "/blocks/destroy/" + +void twitter_get_friends_ids(struct im_connection *ic, int next_cursor); +void twitter_get_home_timeline(struct im_connection *ic, int next_cursor); + +void twitter_post_status(struct im_connection *ic, char* msg); + +#endif //_TWITTER_LIB_H + -- cgit v1.2.3 From b4dd25398db477b06452be195de14ca352008665 Mon Sep 17 00:00:00 2001 From: Geert Mulders Date: Wed, 2 Dec 2009 19:08:40 +0100 Subject: home/timeline is now displayed in a groupchat instead of private window. --- protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_lib.c | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 58791954..5b032929 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -37,6 +37,7 @@ struct twitter_data char* user; char* pass; guint64 home_timeline_id; + struct groupchat *home_timeline_gc; }; #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 7ecbfe15..d548b5f2 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -271,6 +271,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) struct xt_parser *parser; struct twitter_xml_list *txl; struct twitter_data *td = ic->proto_data; + struct groupchat *gc; // Check if the HTTP request went well. if (req->status_code != 200) { @@ -292,13 +293,43 @@ static void twitter_http_get_home_timeline(struct http_request *req) GSList *l; struct twitter_xml_status *status; - imcb_add_buddy( ic, "home_timeline", NULL ); - imcb_buddy_status( ic, "home_timeline", OPT_LOGGED_IN, NULL, NULL ); + // Create a new groupchat if it does not exsist. + if (!td->home_timeline_gc) + { + td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); + // Add the current user to the chat... + imcb_chat_add_buddy( gc, ic->acc->user ); + } + else + { + gc = td->home_timeline_gc; + } for ( l = txl->list; l ; l = g_slist_next(l) ) { status = l->data; - imcb_buddy_msg( ic, "home_timeline", status->text, 0, 0 ); + // TODO Put the next part in a new function.... + + // Ugly hack, to show current user in chat... + if ( g_strcasecmp(status->user->screen_name, ic->acc->user) == 0) + { + char *tmp = g_strdup_printf ("_%s_", status->user->screen_name); + g_free(status->user->screen_name); + status->user->screen_name = tmp; + } + + // Check if the buddy is allready in the buddy list. + if (!user_findhandle( ic, status->user->screen_name )) + { + // The buddy is not in the list, add the buddy... + imcb_add_buddy( ic, status->user->screen_name, NULL ); + imcb_buddy_status( ic, status->user->screen_name, OPT_LOGGED_IN, NULL, NULL ); + } + + // Say it! + imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); + // Update the home_timeline_id to hold the highest id, so that by the next request + // we won't pick up the updates allready in the list. td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; } -- cgit v1.2.3 From a19ea7a21e082d0e28aea7198bea0f3bd3e2eb4f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 26 Dec 2009 16:14:52 +0100 Subject: Use purple_buddy_get_name, not purple_buddy_get_contact_alias. Makes sense, but I'm actually not sure if this function *is* the right one. Fixes issues with messages coming from the wrong handle. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f28e5cf0..ce7f0e56 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -414,7 +414,7 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha buddy = purple_find_buddy( conv->account, who ); if( buddy != NULL ) - who = purple_buddy_get_contact_alias( buddy ); + who = purple_buddy_get_name( buddy ); imcb_buddy_msg( ic, (char*) who, (char*) message, 0, mtime ); } -- cgit v1.2.3 From e08e53c9398700309000c6e6b7ff895185d567a9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 4 Jan 2010 12:16:18 +0000 Subject: Fixed build system: Run pkg-config at configure-time instead of just failing mysteriously at build time if libpurple-dev is missing. --- configure | 10 +++++++++- protocols/purple/Makefile | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/configure b/configure index a87b3697..b7f9a464 100755 --- a/configure +++ b/configure @@ -452,8 +452,16 @@ protoobjs='' if [ "$purple" = 0 ]; then echo '#undef WITH_PURPLE' >> config.h else + if ! $PKG_CONFIG purple; then + echo + echo 'Cannot find libpurple development libraries, aborting. (Install libpurple-dev?)' + exit 1 + fi echo '#define WITH_PURPLE' >> config.h - echo 'EFLAGS += $$(pkg-config purple --libs)' >> Makefile.settings + cat<>Makefile.settings +EFLAGS += $($PKG_CONFIG purple --libs) +PURPLE_CFLAGS += $($PKG_CONFIG purple --cflags) +EOF protocols=$protocols'purple ' protoobjs=$protoobjs'purple_mod.o ' diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index bdefbd5f..15460529 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -11,7 +11,7 @@ # [SH] Program variables objects = purple.o -CFLAGS += -Wall $$(pkg-config purple --cflags) +CFLAGS += -Wall $(PURPLE_CFLAGS) LFLAGS += -r # [SH] Phony targets -- cgit v1.2.3 From 279607e52513bba31b7dfdd508d51e9ac24950c2 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 7 Mar 2010 22:35:00 +0000 Subject: Fixed purple module to work with the new away interface. --- protocols/purple/purple.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index ce7f0e56..0873b6f5 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -194,7 +194,11 @@ static GList *purple_away_states( struct im_connection *ic ) GList *st, *ret = NULL; for( st = purple_account_get_status_types( pa ); st; st = st->next ) - ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + { + PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); + if( prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE ) + ret = g_list_append( ret, (void*) purple_status_type_get_name( st->data ) ); + } return ret; } @@ -204,17 +208,31 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me PurpleAccount *pa = ic->proto_data; GList *status_types = purple_account_get_status_types( pa ), *st; PurpleStatusType *pst = NULL; + GList *args = NULL; for( st = status_types; st; st = st->next ) { pst = st->data; - if( g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) + if( state_txt == NULL && + purple_status_type_get_primitive( st->data ) == PURPLE_STATUS_AVAILABLE ) + break; + + if( state_txt != NULL && + g_strcasecmp( state_txt, purple_status_type_get_name( pst ) ) == 0 ) break; } - purple_account_set_status( pa, st ? purple_status_type_get_id( pst ) : "away", - TRUE, "message", message, NULL ); + if( message ) + { + args = g_list_append( args, "message" ); + args = g_list_append( args, message ); + } + + purple_account_set_status_list( pa, st ? purple_status_type_get_id( pst ) : "away", + TRUE, args ); + + g_list_free( args ); } static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) -- cgit v1.2.3 From 52cae01137057f2cc3def802a661fef92cedbcae Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 7 Mar 2010 23:08:40 +0000 Subject: Set the ACC_FLAG_*_MESSAGE flags correctly depending on the prpl. --- protocols/purple/purple.c | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0873b6f5..6a9472dc 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -3,7 +3,7 @@ * BitlBee - An IRC to IM gateway * * libpurple module - Main file * * * -* Copyright 2009 Wilmer van der Gaast * +* Copyright 2010 Wilmer van der Gaast * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -56,8 +56,10 @@ static void purple_init( account_t *acc ) { PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); PurplePluginProtocolInfo *pi = prpl->info->extra_info; - GList *i; + PurpleAccount *pa; + GList *i, *st; + /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) { PurpleAccountOption *o = i->data; @@ -99,6 +101,26 @@ static void purple_init( account_t *acc ) g_free( def ); } } + + /* Go through all away states to figure out if away/status messages + are possible. */ + pa = purple_account_new( acc->user, (char*) acc->prpl->data ); + for( st = purple_account_get_status_types( pa ); st; st = st->next ) + { + PurpleStatusPrimitive prim = purple_status_type_get_primitive( st->data ); + + if( prim == PURPLE_STATUS_AVAILABLE ) + { + if( purple_status_type_get_attr( st->data, "message" ) ) + acc->flags |= ACC_FLAG_STATUS_MESSAGE; + } + else if( prim != PURPLE_STATUS_OFFLINE ) + { + if( purple_status_type_get_attr( st->data, "message" ) ) + acc->flags |= ACC_FLAG_AWAY_MESSAGE; + } + } + purple_accounts_remove( pa ); } static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) @@ -223,7 +245,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me break; } - if( message ) + if( message && purple_status_type_get_attr( st, "message" ) ) { args = g_list_append( args, "message" ); args = g_list_append( args, message ); -- cgit v1.2.3 From bab1c86e9e07553f58ab4ebdaad7f74018052b5e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 8 Mar 2010 01:21:08 +0000 Subject: Mail notifications, partially from http://irc.nfx.cz/patches/notify.patch written by sd@ircnet. --- protocols/purple/purple.c | 40 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 6a9472dc..3c1f505c 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -3,7 +3,7 @@ * BitlBee - An IRC to IM gateway * * libpurple module - Main file * * * -* Copyright 2010 Wilmer van der Gaast * +* Copyright 2009-2010 Wilmer van der Gaast * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -58,6 +58,7 @@ static void purple_init( account_t *acc ) PurplePluginProtocolInfo *pi = prpl->info->extra_info; PurpleAccount *pa; GList *i, *st; + set_t *s; /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) @@ -66,7 +67,6 @@ static void purple_init( account_t *acc ) const char *name; char *def = NULL; set_eval eval = NULL; - set_t *s; name = purple_account_option_get_setting( o ); @@ -102,6 +102,12 @@ static void purple_init( account_t *acc ) } } + if( pi->options & OPT_PROTO_MAIL_CHECK ) + { + s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; + } + /* Go through all away states to figure out if away/status messages are possible. */ pa = purple_account_new( acc->user, (char*) acc->prpl->data ); @@ -158,6 +164,9 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) break; } } + + if( pi->options & OPT_PROTO_MAIL_CHECK ) + purple_account_set_check_mail( pa, set_getbool( &acc->set, "mail_notifications" ) ); } static void purple_login( account_t *acc ) @@ -237,7 +246,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me pst = st->data; if( state_txt == NULL && - purple_status_type_get_primitive( st->data ) == PURPLE_STATUS_AVAILABLE ) + purple_status_type_get_primitive( pst ) == PURPLE_STATUS_AVAILABLE ) break; if( state_txt != NULL && @@ -245,7 +254,7 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me break; } - if( message && purple_status_type_get_attr( st, "message" ) ) + if( message && purple_status_type_get_attr( pst, "message" ) ) { args = g_list_append( args, "message" ); args = g_list_append( args, message ); @@ -588,12 +597,29 @@ static PurpleEventLoopUiOps glib_eventloops = prplcb_ev_remove, }; +static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, const char *from, + const char *to, const char *url ) +{ + struct im_connection *ic = purple_ic_by_gc( gc ); + + imcb_log( ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url ); + + return NULL; +} + +static PurpleNotifyUiOps bee_notify_uiops = +{ + NULL, + prplcb_notify_email, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); purple_request_set_ui_ops( &bee_request_uiops ); + purple_notify_set_ui_ops(&bee_notify_uiops); //purple_debug_set_ui_ops( &bee_debug_uiops ); } @@ -643,6 +669,8 @@ void purple_initmodule() help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); + /* Add a protocol entry to BitlBee's structures for every protocol + supported by this libpurple instance. */ for( prots = purple_plugins_get_protocols(); prots; prots = prots->next ) { PurplePlugin *prot = prots->data; @@ -656,6 +684,8 @@ void purple_initmodule() g_string_append_printf( help, "\n* %s (%s)", ret->name, prot->info->name ); + /* libpurple doesn't define a protocol called OSCAR, but we + need it to be compatible with normal BitlBee. */ if( g_strcasecmp( prot->info->id, "prpl-aim" ) == 0 ) { ret = g_memdup( &funcs, sizeof( funcs ) ); @@ -665,6 +695,8 @@ void purple_initmodule() } } + /* Add a simple dynamically-generated help item listing all + the supported protocols. */ help_add_mem( &global.help, "purple", help->str ); g_string_free( help, TRUE ); } -- cgit v1.2.3 From 56244c0a88b603a683da9c0cc6abfccdc7616265 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 00:38:40 +0000 Subject: Added set_eval_list which I will need for the Purple module now to make QQ (and others) work. --- set.c | 15 +++++++++++++++ set.h | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/set.c b/set.c index 7abae37a..baf85261 100644 --- a/set.c +++ b/set.c @@ -212,6 +212,21 @@ char *set_eval_bool( set_t *set, char *value ) return is_bool( value ) ? value : SET_INVALID; } +char *set_eval_list( set_t *set, char *value ) +{ + GSList *options = set->eval_data, *opt; + + for( opt = options; opt; opt = opt->next ) + if( strcmp( value, opt->data ) == 0 ) + return value; + + /* TODO: It'd be nice to show the user a list of allowed values, + but we don't have enough context here to do that. May + want to fix that. */ + + return NULL; +} + char *set_eval_to_char( set_t *set, char *value ) { char *s = g_new( char, 3 ); diff --git a/set.h b/set.h index 42522506..7ff1f985 100644 --- a/set.h +++ b/set.h @@ -68,6 +68,7 @@ typedef struct set the passed value variable. When returning a corrected value, set_setstr() should be able to free() the returned string! */ set_eval eval; + void *eval_data; struct set *next; } set_t; @@ -97,6 +98,9 @@ int set_reset( set_t **head, const char *key ); char *set_eval_int( set_t *set, char *value ); char *set_eval_bool( set_t *set, char *value ); +/* Another more complicated one. */ +char *set_eval_list( set_t *set, char *value ); + /* Some not very generic evaluators that really shouldn't be here... */ char *set_eval_to_char( set_t *set, char *value ); char *set_eval_ops( set_t *set, char *value ); -- cgit v1.2.3 From 4dc6b8d10786baafd3ead9a2ecb22d7065b9c4b9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 01:05:21 +0000 Subject: Added support for PURPLE_PREF_STRING_LIST style settings, this makes the QQ module (and maybe others) work. --- protocols/purple/purple.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 3c1f505c..9a6556b0 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -67,6 +67,9 @@ static void purple_init( account_t *acc ) const char *name; char *def = NULL; set_eval eval = NULL; + void *eval_data = NULL; + GList *io = NULL; + GSList *opts = NULL; name = purple_account_option_get_setting( o ); @@ -89,8 +92,20 @@ static void purple_init( account_t *acc ) eval = set_eval_bool; break; + case PURPLE_PREF_STRING_LIST: + def = g_strdup( purple_account_option_get_default_list_value( o ) ); + for( io = purple_account_option_get_list( o ); io; io = io->next ) + { + PurpleKeyValuePair *kv = io->data; + opts = g_slist_append( opts, kv->key ); + } + eval = set_eval_list; + eval_data = opts; + break; + default: - fprintf( stderr, "Setting with unknown type: %s (%d)\n", name, purple_account_option_get_type( o ) ); + irc_usermsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n", + name, purple_account_option_get_type( o ) ); name = NULL; } @@ -98,6 +113,7 @@ static void purple_init( account_t *acc ) { s = set_add( &acc->set, name, def, eval, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + s->eval_data = eval_data; g_free( def ); } } @@ -149,6 +165,7 @@ static void purple_sync_settings( account_t *acc, PurpleAccount *pa ) switch( purple_account_option_get_type( o ) ) { case PURPLE_PREF_STRING: + case PURPLE_PREF_STRING_LIST: purple_account_set_string( pa, name, set_getstr( &acc->set, name ) ); break; -- cgit v1.2.3 From 7c5affcabd08f23e36719afefe736f266b80912b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 12 Mar 2010 01:47:44 +0000 Subject: Add some simple information about available settings to the online help command. --- protocols/purple/purple.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 9a6556b0..b336b108 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -59,6 +59,12 @@ static void purple_init( account_t *acc ) PurpleAccount *pa; GList *i, *st; set_t *s; + char help_title[64]; + GString *help; + + help = g_string_new( "" ); + g_string_printf( help, "BitlBee libpurple module %s (%s).\n\nSupported settings:", + (char*) acc->prpl->name, prpl->info->name ); /* Convert all protocol_options into per-account setting variables. */ for( i = pi->protocol_options; i; i = i->next ) @@ -77,11 +83,21 @@ static void purple_init( account_t *acc ) { case PURPLE_PREF_STRING: def = g_strdup( purple_account_option_get_default_string( o ) ); + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "string", def ); + break; case PURPLE_PREF_INT: def = g_strdup_printf( "%d", purple_account_option_get_default_int( o ) ); eval = set_eval_int; + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "integer", def ); + break; case PURPLE_PREF_BOOLEAN: @@ -90,17 +106,31 @@ static void purple_init( account_t *acc ) else def = g_strdup( "false" ); eval = set_eval_bool; + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "boolean", def ); + break; case PURPLE_PREF_STRING_LIST: def = g_strdup( purple_account_option_get_default_list_value( o ) ); + + g_string_append_printf( help, "\n* %s (%s), %s, default: %s", + name, purple_account_option_get_text( o ), + "list", def ); + g_string_append( help, "\n Possible values: " ); + for( io = purple_account_option_get_list( o ); io; io = io->next ) { PurpleKeyValuePair *kv = io->data; opts = g_slist_append( opts, kv->key ); + g_string_append_printf( help, "%s, ", kv->key ); } + g_string_truncate( help, help->len - 2 ); eval = set_eval_list; eval_data = opts; + break; default: @@ -118,6 +148,10 @@ static void purple_init( account_t *acc ) } } + g_snprintf( help_title, sizeof( help_title ), "purple %s", (char*) acc->prpl->name ); + help_add_mem( &global.help, help_title, help->str ); + g_string_free( help, TRUE ); + if( pi->options & OPT_PROTO_MAIL_CHECK ) { s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); @@ -712,6 +746,9 @@ void purple_initmodule() } } + g_string_append( help, "\n\nFor used protocols, more information about available " + "settings can be found using \x02help purple \x02" ); + /* Add a simple dynamically-generated help item listing all the supported protocols. */ help_add_mem( &global.help, "purple", help->str ); -- cgit v1.2.3 From 0cb71a67163cbca49827855a4a56642f919330fd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 16:57:20 +0000 Subject: I renamed GAIM_INPUT_* to something else in this branch. --- dcc.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/dcc.c b/dcc.c index 558d923a..1f8ec611 100644 --- a/dcc.c +++ b/dcc.c @@ -153,7 +153,7 @@ file_transfer_t *dccs_send_start( struct im_connection *ic, char *user_nick, cha return NULL; /* watch */ - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_send_proto, df ); + df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_send_proto, df ); df->ic->irc->file_transfers = g_slist_prepend( df->ic->irc->file_transfers, file ); @@ -266,7 +266,7 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) dcc_file_transfer_t *df = data; file_transfer_t *file = df->ft; - if( ( cond & GAIM_INPUT_READ ) && + if( ( cond & B_EV_IO_READ ) && ( file->status & FT_STATUS_LISTENING ) ) { struct sockaddr *clt_addr; @@ -286,12 +286,12 @@ gboolean dccs_send_proto( gpointer data, gint fd, b_input_condition cond ) file->accept( file ); /* reschedule for reading on new fd */ - df->watch_in = b_input_add( fd, GAIM_INPUT_READ, dccs_send_proto, df ); + df->watch_in = b_input_add( fd, B_EV_IO_READ, dccs_send_proto, df ); return FALSE; } - if( cond & GAIM_INPUT_READ ) + if( cond & B_EV_IO_READ ) { int ret; @@ -363,7 +363,7 @@ gboolean dccs_recv_start( file_transfer_t *ft ) ft->status = FT_STATUS_CONNECTING; /* watch */ - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_recv_proto, df ); + df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_recv_proto, df ); ft->write_request = dccs_recv_write_request; df->progress_timeout = b_timeout_add( DCC_MAX_STALL * 1000, dcc_progress, df ); @@ -376,18 +376,18 @@ gboolean dccs_recv_proto( gpointer data, gint fd, b_input_condition cond ) dcc_file_transfer_t *df = data; file_transfer_t *ft = df->ft; - if( ( cond & GAIM_INPUT_WRITE ) && + if( ( cond & B_EV_IO_WRITE ) && ( ft->status & FT_STATUS_CONNECTING ) ) { ft->status = FT_STATUS_TRANSFERRING; - //df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + //df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df ); df->watch_out = 0; return FALSE; } - if( cond & GAIM_INPUT_READ ) + if( cond & B_EV_IO_READ ) { int ret, done; @@ -444,7 +444,7 @@ gboolean dccs_recv_write_request( file_transfer_t *ft ) if( df->watch_in ) return dcc_abort( df, "BUG: write_request() called while watching" ); - df->watch_in = b_input_add( df->fd, GAIM_INPUT_READ, dccs_recv_proto, df ); + df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df ); return TRUE; } @@ -487,7 +487,7 @@ gboolean dccs_send_write( file_transfer_t *file, char *data, unsigned int data_l df->bytes_sent += ret; if( df->bytes_sent < df->ft->file_size ) - df->watch_out = b_input_add( df->fd, GAIM_INPUT_WRITE, dccs_send_can_write, df ); + df->watch_out = b_input_add( df->fd, B_EV_IO_WRITE, dccs_send_can_write, df ); return TRUE; } -- cgit v1.2.3 From 437bd9b726339c44aa1a048cd84c2539bfa6cab5 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 21 Mar 2010 21:38:42 +0000 Subject: Enough code to make an incoming transfer show up properly and accept it. Not enough yet to handle the incoming data. --- protocols/purple/purple.c | 65 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b336b108..f0fc736e 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -580,12 +580,12 @@ static void *prplcb_request_action( const char *title, const char *primary, cons caption = va_arg( actions, char* ); fn = va_arg( actions, void* ); - if( strcmp( caption, "Accept" ) == 0 ) + if( strstr( caption, "Accept" ) ) { pqad->yes = fn; pqad->yes_i = i; } - else if( strcmp( caption, "Reject" ) == 0 ) + else if( strstr( caption, "Reject" ) || strstr( caption, "Cancel" ) ) { pqad->no = fn; pqad->no_i = i; @@ -658,20 +658,75 @@ static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, con return NULL; } -static PurpleNotifyUiOps bee_notify_uiops = +static PurpleNotifyUiOps bee_notify_uiops = { NULL, prplcb_notify_email, }; +static void prplcb_xfer( PurpleXfer *xfer ) +{ + fprintf( stderr, "ft bla: 0x%p\n", xfer ); +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + purple_xfer_request_accepted( ft->data, NULL ); + purple_xfer_ui_ready( ft->data ); +} + +static void prpl_xfer_reject( struct file_transfer *ft ) +{ + purple_xfer_request_denied( ft->data ); +} + +static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + file_transfer_t *ft; + + ft = imcb_file_send_start( ic, xfer->who, xfer->filename, xfer->size ); + ft->data = xfer; + xfer->ui_data = ft; + + ft->accept = prpl_xfer_accept; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + fprintf( stderr, "ft_new bla: 0x%p\n", xfer ); + + b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); +} + +static PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, + prplcb_xfer, +}; + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); purple_connections_set_ui_ops( &bee_conn_uiops ); purple_conversations_set_ui_ops( &bee_conv_uiops ); purple_request_set_ui_ops( &bee_request_uiops ); - purple_notify_set_ui_ops(&bee_notify_uiops); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_notify_set_ui_ops( &bee_notify_uiops ); + purple_xfers_set_ui_ops( &bee_xfer_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From c735200e7727a7b17161c2a205ba6639d61e9b54 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 22 Mar 2010 01:20:40 +0000 Subject: Incoming file transfers can now be accepted (and should work) and/or rejected. Tested with Jabber and msn/msn-pecan so far. --- protocols/purple/purple.c | 90 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f0fc736e..383ed55f 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -664,58 +664,102 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; -static void prplcb_xfer( PurpleXfer *xfer ) +struct prpl_xfer_data { - fprintf( stderr, "ft bla: 0x%p\n", xfer ); + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; +}; + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + purple_xfer_ui_ready( data ); + return TRUE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px->xfer ); + return TRUE; +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; } static void prpl_xfer_accept( struct file_transfer *ft ) { - purple_xfer_request_accepted( ft->data, NULL ); - purple_xfer_ui_ready( ft->data ); + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); } -static void prpl_xfer_reject( struct file_transfer *ft ) +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) { - purple_xfer_request_denied( ft->data ); + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); } static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); - file_transfer_t *ft; + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - ft = imcb_file_send_start( ic, xfer->who, xfer->filename, xfer->size ); - ft->data = xfer; - xfer->ui_data = ft; + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; - ft->accept = prpl_xfer_accept; + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; return FALSE; } static void prplcb_xfer_new( PurpleXfer *xfer ) { + /* This should suppress the stupid file dialog. */ purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); - fprintf( stderr, "ft_new bla: 0x%p\n", xfer ); - + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); } static PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, - prplcb_xfer, + NULL, + NULL, + NULL, + NULL, + NULL, + prplcb_xfer_write, }; static void purple_ui_init() @@ -726,7 +770,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From edfc6db1415558b7f202cc3fa2654ad58defea78 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 23 Mar 2010 01:06:25 +0000 Subject: Frankenstein, it lives! This stuff can send files but it has troubles with certain protocol modules, don't rely on this yet. It's also getting too messy and should be split off into a separate file. --- protocols/purple/purple.c | 133 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 118 insertions(+), 15 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 383ed55f..997b09f7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -364,6 +364,8 @@ static int purple_send_typing( struct im_connection *ic, char *who, int flags ) } } +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); + static void purple_ui_init(); static PurpleCoreUiOps bee_core_uiops = @@ -664,26 +666,34 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; + struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; gint ready_timer; + char *buf; + int buf_len; }; +static file_transfer_t *next_ft; + /* Glorious hack: We seem to have to remind at least some libpurple plugins that we're ready because this info may get lost if we give it too early. So just do it ten times a second. :-/ */ static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) { - purple_xfer_ui_ready( data ); - return TRUE; + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; } static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px->xfer ); + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); return TRUE; } @@ -703,6 +713,19 @@ static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize return st ? size : 0; } +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + static void prpl_xfer_accept( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; @@ -716,7 +739,7 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition cond ) +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); @@ -743,25 +766,103 @@ static gboolean prplcb_xfer_new_cb( gpointer data, gint fd, b_input_condition co static void prplcb_xfer_new( PurpleXfer *xfer ) { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; - /* Sadly the xfer struct is still empty ATM so come back after - the caller is done. */ - b_timeout_add( 0, prplcb_xfer_new_cb, xfer ); + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; } static PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - NULL, - NULL, - NULL, - NULL, - NULL, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_dbg, prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, }; +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} + static void purple_ui_init() { purple_blist_set_ui_ops( &bee_blist_uiops ); @@ -770,7 +871,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() @@ -816,6 +917,8 @@ void purple_initmodule() funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; + /* TODO(wilmer): Set this one only for protocols that support it? */ + funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From 62d2cfb0b7b5e7f3eda9ca13b1877d3ad74fcd5e Mon Sep 17 00:00:00 2001 From: Geert Mulders Date: Thu, 25 Mar 2010 22:31:27 +0100 Subject: Added option to get tweeds either through groupchat or privmes. --- protocols/twitter/twitter.c | 37 ++-- protocols/twitter/twitter.h | 7 + protocols/twitter/twitter_lib.c | 369 ++++++++++++++++++++++++++++++++-------- protocols/twitter/twitter_lib.h | 10 +- 4 files changed, 330 insertions(+), 93 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 1cc7eaeb..b6b23fa5 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -28,8 +28,8 @@ /** - * * Main loop function - * */ + * Main loop function + */ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) { struct im_connection *ic = data; @@ -37,6 +37,11 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) return 0; + // If the user uses multiple private message windows we need to get the + // users buddies. + if (!set_getbool( &ic->acc->set, "use_groupchat" )) + twitter_get_statuses_friends(ic, -1); + // Do stuff.. twitter_get_home_timeline(ic, -1); @@ -47,6 +52,8 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) static void twitter_init( account_t *acc ) { + set_t *s; + s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc ); } /** @@ -57,7 +64,7 @@ static void twitter_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); struct twitter_data *td = g_new0( struct twitter_data, 1 ); - + td->user = acc->user; td->pass = acc->pass; td->home_timeline_id = 0; @@ -67,11 +74,6 @@ static void twitter_login( account_t *acc ) // Set the status to logged in. ic->flags = OPT_LOGGED_IN; - // Try to get the buddies... - //twitter_get_friends_ids(ic, -1); - - //twitter_get_home_timeline(ic, -1); - // Run this once. After this queue the main loop function. twitter_main_loop(ic, -1, 0); @@ -80,6 +82,8 @@ static void twitter_login( account_t *acc ) imcb_log( ic, "Connecting to twitter" ); imcb_connected(ic); + + twitter_connections = g_slist_append( twitter_connections, ic ); } /** @@ -96,6 +100,8 @@ static void twitter_logout( struct im_connection *ic ) { g_free( td ); } + + twitter_connections = g_slist_remove( twitter_connections, ic ); } /** @@ -103,8 +109,11 @@ static void twitter_logout( struct im_connection *ic ) */ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { - imcb_log( ic, "In twitter_buddy_msg..."); - twitter_post_status(ic, message); + // Let's just update the status. +// if ( g_strcasecmp(who, ic->acc->user) == 0 ) + twitter_post_status(ic, message); +// else +// twitter_direct_messages_new(ic, who, message); return( 0 ); } @@ -123,11 +132,6 @@ static void twitter_set_away( struct im_connection *ic, char *state, char *messa static void twitter_set_my_name( struct im_connection *ic, char *info ) { - imcb_log( ic, "In twitter_set_my_name..." ); -// char * aap = twitter_http("http://gertje.org", NULL, ic, 1, "geert", "poep", NULL, 0); - -// imcb_log( ic, aap ); -// g_free(aap); } static void twitter_get_info(struct im_connection *ic, char *who) @@ -217,5 +221,8 @@ void twitter_initmodule() ret->handle_cmp = g_strcasecmp; register_protocol(ret); + + // Initialise the twitter_connections GSList. + twitter_connections = NULL; } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 5b032929..05a861bb 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -40,4 +40,11 @@ struct twitter_data struct groupchat *home_timeline_gc; }; +/** + * This has the same function as the msn_connections GSList. We use this to + * make sure the connection is still alive in callbacks before we do anything + * else. + */ +GSList *twitter_connections; + #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d548b5f2..f07897ed 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -33,9 +33,13 @@ #include #define TXL_STATUS 1 -#define TXL_ID 1 +#define TXL_USER 2 +#define TXL_ID 3 + +static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ); struct twitter_xml_list { + int type; int next_cursor; GSList *list; gpointer data; @@ -53,9 +57,54 @@ struct twitter_xml_status { guint64 id; }; -void txl_free(struct twitter_xml_list *txl, int type); -void txs_free(struct twitter_xml_status *txs); -void txu_free(struct twitter_xml_user *txu); +/** + * Frees a twitter_xml_user struct. + */ +static void txu_free(struct twitter_xml_user *txu) +{ + g_free(txu->name); + g_free(txu->screen_name); +} + + +/** + * Frees a twitter_xml_status struct. + */ +static void txs_free(struct twitter_xml_status *txs) +{ + g_free(txs->created_at); + g_free(txs->text); + txu_free(txs->user); +} + +/** + * Free a twitter_xml_list struct. + * type is the type of list the struct holds. + */ +static void txl_free(struct twitter_xml_list *txl) +{ + GSList *l; + for ( l = txl->list; l ; l = g_slist_next(l) ) + if (txl->type == TXL_STATUS) + txs_free((struct twitter_xml_status *)l->data); + else if (txl->type == TXL_ID) + g_free(l->data); + g_slist_free(txl->list); +} + +/** + * Add a buddy if it is not allready added, set the status to logged in. + */ +static void twitter_add_buddy(struct im_connection *ic, char *name) +{ + // Check if the buddy is allready in the buddy list. + if (!user_findhandle( ic, name )) + { + // The buddy is not in the list, add the buddy and set the status to logged in. + imcb_add_buddy( ic, name, NULL ); + imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); + } +} static void twitter_http_get_friends_ids(struct http_request *req); @@ -92,6 +141,9 @@ static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xm static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl ) { struct xt_node *child; + + // Set the list type. + txl->type = TXL_ID; // The root node should hold the list of statuses // Walk over the nodes children. @@ -122,6 +174,10 @@ static void twitter_http_get_friends_ids(struct http_request *req) ic = req->data; + // Check if the connection is still active. + if( !g_slist_find( twitter_connections, ic ) ) + return; + // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. @@ -141,7 +197,7 @@ static void twitter_http_get_friends_ids(struct http_request *req) if (txl->next_cursor) twitter_get_friends_ids(ic, txl->next_cursor); - txl_free(txl, TXL_ID); + txl_free(txl); g_free(txl); } @@ -170,6 +226,66 @@ static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_u return XT_HANDLED; } +/** + * Function to fill a twitter_xml_list struct. + * It sets: + * - all s from the element. + */ +static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl ) +{ + struct twitter_xml_user *txu; + struct xt_node *child; + + // Set the type of the list. + txl->type = TXL_USER; + + // The root node should hold the list of users + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "user", child->name ) == 0) + { + txu = g_new0(struct twitter_xml_user, 1); + twitter_xt_get_user(child, txu); + // Put the item in the front of the list. + txl->list = g_slist_prepend (txl->list, txu); + } + } + + return XT_HANDLED; +} + +/** + * Function to fill a twitter_xml_list struct. + * It calls twitter_xt_get_users to get the s from a element. + * It sets: + * - the next_cursor. + */ +static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl ) +{ + struct xt_node *child; + + // Set the type of the list. + txl->type = TXL_USER; + + // The root node should hold a users element + // Walk over the nodes children. + for( child = node->children ; child ; child = child->next ) + { + if ( g_strcasecmp( "users", child->name ) == 0) + { + twitter_xt_get_users(child, txl); + } + else if ( g_strcasecmp( "next_cursor", child->name ) == 0) + { + twitter_xt_next_cursor(child, txl); + } + } + + return XT_HANDLED; +} + + /** * Function to fill a twitter_xml_status struct. * It sets: @@ -217,6 +333,9 @@ static xt_status twitter_xt_get_status_list( struct xt_node *node, struct twitte struct twitter_xml_status *txs; struct xt_node *child; + // Set the type of the list. + txl->type = TXL_STATUS; + // The root node should hold the list of statuses // Walk over the nodes children. for( child = node->children ; child ; child = child->next ) @@ -262,16 +381,71 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor) } } +/** + * Function that is called to see the statuses in a groupchat window. + */ +static void twitter_groupchat(struct im_connection *ic, GSList *list) +{ + struct twitter_data *td = ic->proto_data; + GSList *l = NULL; + struct twitter_xml_status *status; + struct groupchat *gc; + + // Create a new groupchat if it does not exsist. + if (!td->home_timeline_gc) + { + td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); + // Add the current user to the chat... + imcb_chat_add_buddy( gc, ic->acc->user ); + } + else + { + gc = td->home_timeline_gc; + } + + for ( l = list; l ; l = g_slist_next(l) ) + { + status = l->data; + twitter_add_buddy(ic, status->user->screen_name); + // Say it! + twitter_imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); + // Update the home_timeline_id to hold the highest id, so that by the next request + // we won't pick up the updates allready in the list. + td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; + } +} + +/** + * Function that is called to see statuses as private messages. + */ +static void twitter_private_message_chat(struct im_connection *ic, GSList *list) +{ + struct twitter_data *td = ic->proto_data; + GSList *l = NULL; + struct twitter_xml_status *status; + + for ( l = list; l ; l = g_slist_next(l) ) + { + status = l->data; + imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, 0 ); + // Update the home_timeline_id to hold the highest id, so that by the next request + // we won't pick up the updates allready in the list. + td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; + } +} + /** * Callback for getting the home timeline. */ static void twitter_http_get_home_timeline(struct http_request *req) { - struct im_connection *ic = req->data;; + struct im_connection *ic = req->data; struct xt_parser *parser; struct twitter_xml_list *txl; - struct twitter_data *td = ic->proto_data; - struct groupchat *gc; + + // Check if the connection is still active. + if( !g_slist_find( twitter_connections, ic ) ) + return; // Check if the HTTP request went well. if (req->status_code != 200) { @@ -282,94 +456,92 @@ static void twitter_http_get_home_timeline(struct http_request *req) txl = g_new0(struct twitter_xml_list, 1); txl->list = NULL; - + // Parse the data. parser = xt_new( NULL, txl ); xt_feed( parser, req->reply_body, req->body_size ); // The root node should hold the list of statuses twitter_xt_get_status_list(parser->root, txl); xt_free( parser ); - - GSList *l; - struct twitter_xml_status *status; - // Create a new groupchat if it does not exsist. - if (!td->home_timeline_gc) - { - td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); - // Add the current user to the chat... - imcb_chat_add_buddy( gc, ic->acc->user ); - } + // See if the user wants to see the messages in a groupchat window or as private messages. + if (set_getbool( &ic->acc->set, "use_groupchat" )) + twitter_groupchat(ic, txl->list); else - { - gc = td->home_timeline_gc; - } - - for ( l = txl->list; l ; l = g_slist_next(l) ) - { - status = l->data; - // TODO Put the next part in a new function.... - - // Ugly hack, to show current user in chat... - if ( g_strcasecmp(status->user->screen_name, ic->acc->user) == 0) - { - char *tmp = g_strdup_printf ("_%s_", status->user->screen_name); - g_free(status->user->screen_name); - status->user->screen_name = tmp; - } - - // Check if the buddy is allready in the buddy list. - if (!user_findhandle( ic, status->user->screen_name )) - { - // The buddy is not in the list, add the buddy... - imcb_add_buddy( ic, status->user->screen_name, NULL ); - imcb_buddy_status( ic, status->user->screen_name, OPT_LOGGED_IN, NULL, NULL ); - } - - // Say it! - imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); - // Update the home_timeline_id to hold the highest id, so that by the next request - // we won't pick up the updates allready in the list. - td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; - } + twitter_private_message_chat(ic, txl->list); // Free the structure. - txl_free(txl, TXL_STATUS); + txl_free(txl); g_free(txl); } /** - * Free a twitter_xml_list struct. - * type is the type of list the struct holds. + * Callback for getting (twitter)friends... + * + * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has + * hundreds of friends?" you wonder? You probably not, since you are reading the source of + * BitlBee... Get a life and meet new people! */ -void txl_free(struct twitter_xml_list *txl, int type) +static void twitter_http_get_statuses_friends(struct http_request *req) { - GSList *l; + struct im_connection *ic = req->data; + struct xt_parser *parser; + struct twitter_xml_list *txl; + + // Check if the connection is still active. + if( !g_slist_find( twitter_connections, ic ) ) + return; + + // Check if the HTTP request went well. + if (req->status_code != 200) { + // It didn't go well, output the error and return. + imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code); + return; + } + + txl = g_new0(struct twitter_xml_list, 1); + txl->list = NULL; + + // Parse the data. + parser = xt_new( NULL, txl ); + xt_feed( parser, req->reply_body, req->body_size ); + + // Get the user list from the parsed xml feed. + twitter_xt_get_user_list(parser->root, txl); + xt_free( parser ); + + GSList *l = NULL; + struct twitter_xml_user *user; + // Add the users as buddies. for ( l = txl->list; l ; l = g_slist_next(l) ) - if (type == TXL_STATUS) - txs_free((struct twitter_xml_status *)l->data); - else if (type == TXL_ID) - g_free(l->data); - g_slist_free(txl->list); -} + { + user = l->data; + twitter_add_buddy(ic, user->screen_name); + } -/** - * Frees a twitter_xml_status struct. - */ -void txs_free(struct twitter_xml_status *txs) -{ - g_free(txs->created_at); - g_free(txs->text); - txu_free(txs->user); + // if the next_cursor is set to something bigger then 0 there are more friends to gather. + if (txl->next_cursor > 0) + twitter_get_statuses_friends(ic, txl->next_cursor); + + // Free the structure. + txl_free(txl); + g_free(txl); } /** - * Frees a twitter_xml_user struct. + * Get the friends. */ -void txu_free(struct twitter_xml_user *txu) +void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor) { - g_free(txu->name); - g_free(txu->screen_name); + struct twitter_data *td = ic->proto_data; + + char* args[2]; + args[0] = "cursor"; + args[1] = g_strdup_printf ("%d", next_cursor); + + twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2); + + g_free(args[1]); } /** @@ -379,6 +551,10 @@ static void twitter_http_post_status(struct http_request *req) { struct im_connection *ic = req->data; + // Check if the connection is still active. + if( !g_slist_find( twitter_connections, ic ) ) + return; + // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. @@ -399,7 +575,52 @@ void twitter_post_status(struct im_connection *ic, char* msg) args[0] = "status"; args[1] = msg; twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2); - g_free(args[1]); +// g_free(args[1]); } +/** + * Function to POST a new message to twitter. + */ +void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) +{ + struct twitter_data *td = ic->proto_data; + + char* args[4]; + args[0] = "screen_name"; + args[1] = who; + args[2] = "text"; + args[3] = msg; + // Use the same callback as for twitter_post_status, since it does basically the same. + twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4); +// g_free(args[1]); +// g_free(args[3]); +} + + +/** + * This function "overwrites" the imcb_chat_msg function. Because in the original the logged in user is filtered out. + */ +static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ) +{ + struct im_connection *ic = c->ic; + char *wrapped; + user_t *u; + + u = user_findhandle( ic, who ); + if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) + || ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) + strip_html( msg ); + + wrapped = word_wrap( msg, 425 ); + if( c && u ) + { + irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped ); + } + else + { + imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); + } + g_free( wrapped ); +} + diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index 28ca871f..e47bfd95 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -50,9 +50,9 @@ /* Direct messages URLs */ #define TWITTER_DIRECT_MESSAGES_URL TWITTER_API_URL "/direct_messages.xml" -#define TWITTER_DIRECT_MESSAGENEW_URL TWITTER_API_URL "/direct_messages/new.xml" -#define TWITTER_DIRECT_MESSAGESSENT_URL TWITTER_API_URL "/direct_messages/sent.xml" -#define TWITTER_DIRECT_MESSAGEDESTROY_URL TWITTER_API_URL "/direct_messages/destroy/" +#define TWITTER_DIRECT_MESSAGES_NEW_URL TWITTER_API_URL "/direct_messages/new.xml" +#define TWITTER_DIRECT_MESSAGES_SENT_URL TWITTER_API_URL "/direct_messages/sent.xml" +#define TWITTER_DIRECT_MESSAGES_DESTROY_URL TWITTER_API_URL "/direct_messages/destroy/" /* Friendships URLs */ #define TWITTER_FRIENDSHIPS_CREATE_URL TWITTER_API_URL "/friendships/create.xml" @@ -77,8 +77,10 @@ void twitter_get_friends_ids(struct im_connection *ic, int next_cursor); void twitter_get_home_timeline(struct im_connection *ic, int next_cursor); +void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor); -void twitter_post_status(struct im_connection *ic, char* msg); +void twitter_post_status(struct im_connection *ic, char *msg); +void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message); #endif //_TWITTER_LIB_H -- cgit v1.2.3 From c4bc92a42001a05a36678ae14f610ff3857be465 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 29 Mar 2010 20:26:11 -0400 Subject: Suppress empty "Headline:" messages for certain new XMPP broadcast messages. --- protocols/jabber/message.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protocols/jabber/message.c b/protocols/jabber/message.c index a226a225..e8161029 100644 --- a/protocols/jabber/message.c +++ b/protocols/jabber/message.c @@ -79,8 +79,8 @@ xt_status jabber_pkt_message( struct xt_node *node, gpointer data ) if( type && strcmp( type, "headline" ) == 0 ) { - c = xt_find_node( node->children, "subject" ); - g_string_append_printf( fullmsg, "Headline: %s\n", c && c->text_len > 0 ? c->text : "" ); + if( ( c = xt_find_node( node->children, "subject" ) ) && c->text_len > 0 ) + g_string_append_printf( fullmsg, "Headline: %s\n", c->text ); /* http://.... can contain a URL, it seems. */ for( c = node->children; c; c = c->next ) -- cgit v1.2.3 From e3413cc741d2b0a82183f859d7470922bc581efa Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 29 Mar 2010 21:30:19 -0400 Subject: Added local_display_name setting for MSN accounts and some hopefully clever enough handling for it. Should solve problems with MSN servers forgetting/ overriding display names set by the user. --- doc/user-guide/commands.xml | 11 +++++++++ protocols/msn/msn.c | 31 +++++++----------------- protocols/msn/msn.h | 1 + protocols/msn/msn_util.c | 16 ++++++++++-- protocols/msn/ns.c | 59 +++++++++++++++++++++++++++++++++------------ 5 files changed, 78 insertions(+), 40 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index df99c1fc..833ea00f 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -608,6 +608,17 @@ + + false + + + + Mostly meant to work around a bug in MSN servers (forgetting the display name set by the user), this setting tells BitlBee to store your display name locally and set this name on the MSN servers when connecting. + + + + + false diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 8930847d..37f6e1be 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -30,16 +30,13 @@ int msn_chat_id; GSList *msn_connections; GSList *msn_switchboards; -static char *msn_set_display_name( set_t *set, char *value ); +static char *set_eval_display_name( set_t *set, char *value ); static void msn_init( account_t *acc ) { - set_t *s; - - s = set_add( &acc->set, "display_name", NULL, msn_set_display_name, acc ); - s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; - - s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); + set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); + set_add( &acc->set, "local_display_name", "false", set_eval_bool, acc ); + set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); } static void msn_login( account_t *acc ) @@ -166,7 +163,7 @@ static void msn_set_away( struct im_connection *ic, char *state, char *message ) static void msn_set_my_name( struct im_connection *ic, char *info ) { - msn_set_display_name( set_find( &ic->acc->set, "display_name" ), info ); + msn_set_display_name( ic, info ); } static void msn_get_info(struct im_connection *ic, char *who) @@ -282,18 +279,14 @@ static int msn_send_typing( struct im_connection *ic, char *who, int typing ) return( 1 ); } -static char *msn_set_display_name( set_t *set, char *value ) +static char *set_eval_display_name( set_t *set, char *value ) { account_t *acc = set->data; struct im_connection *ic = acc->ic; - struct msn_data *md; - char buf[1024], *fn; - /* Double-check. */ + /* Allow any name if we're offline. */ if( ic == NULL ) - return NULL; - - md = ic->proto_data; + return value; if( strlen( value ) > 129 ) { @@ -301,16 +294,10 @@ static char *msn_set_display_name( set_t *set, char *value ) return NULL; } - fn = msn_http_encode( value ); - - g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); - msn_write( ic, buf, strlen( buf ) ); - g_free( fn ); - /* Returning NULL would be better, because the server still has to confirm the name change. However, it looks a bit confusing to the user. */ - return value; + return msn_set_display_name( ic, value ) ? value : NULL; } void msn_initmodule() diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 84914bc3..02d180b6 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -160,6 +160,7 @@ char **msn_linesplit( char *line ); int msn_handler( struct msn_handler_data *h ); char *msn_http_encode( const char *input ); void msn_msgq_purge( struct im_connection *ic, GSList **list ); +gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ); /* tables.c */ const struct msn_away_state *msn_away_state_by_number( int number ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c index 668a8b8a..9c9d2720 100644 --- a/protocols/msn/msn_util.c +++ b/protocols/msn/msn_util.c @@ -37,10 +37,10 @@ int msn_write( struct im_connection *ic, char *s, int len ) { imcb_error( ic, "Short write() to main server" ); imc_logout( ic, TRUE ); - return( 0 ); + return 0; } - return( 1 ); + return 1; } int msn_logged_in( struct im_connection *ic ) @@ -376,3 +376,15 @@ void msn_msgq_purge( struct im_connection *ic, GSList **list ) imcb_log( ic, "%s", ret->str ); g_string_free( ret, TRUE ); } + +gboolean msn_set_display_name( struct im_connection *ic, const char *rawname ) +{ + char *fn = msn_http_encode( rawname ); + struct msn_data *md = ic->proto_data; + char buf[1024]; + + g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, ic->acc->user, fn ); + g_free( fn ); + + return msn_write( ic, buf, strlen( buf ) ) != 0; +} diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index d78d753a..cb10df30 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -34,6 +34,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ); static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); static void msn_auth_got_passport_token( struct msn_auth_data *mad ); +static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ); gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond ) { @@ -230,25 +231,10 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( num_parts >= 7 && strcmp( cmd[2], "OK" ) == 0 ) { - set_t *s; - if( num_parts == 7 ) - { - http_decode( cmd[4] ); - - strncpy( ic->displayname, cmd[4], sizeof( ic->displayname ) ); - ic->displayname[sizeof(ic->displayname)-1] = 0; - - if( ( s = set_find( &ic->acc->set, "display_name" ) ) ) - { - g_free( s->value ); - s->value = g_strdup( cmd[4] ); - } - } + msn_ns_got_display_name( ic, cmd[4] ); else - { imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); - } imcb_log( ic, "Authenticated, getting buddy list" ); @@ -566,6 +552,9 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imc_logout( ic, allow_reconnect ); return( 0 ); } +#if 0 + /* Discard this one completely for now since I don't care about the ack + and since MSN servers can apparently screw up the formatting. */ else if( strcmp( cmd[0], "REA" ) == 0 ) { if( num_parts != 5 ) @@ -596,6 +585,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imcb_rename_buddy( ic, cmd[3], cmd[4] ); } } +#endif else if( strcmp( cmd[0], "IPG" ) == 0 ) { imcb_error( ic, "Received IPG command, we don't handle them yet." ); @@ -745,3 +735,40 @@ static void msn_auth_got_passport_token( struct msn_auth_data *mad ) imc_logout( ic, TRUE ); } } + +static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) +{ + set_t *s; + + if( ( s = set_find( &ic->acc->set, "display_name" ) ) == NULL ) + return FALSE; /* Shouldn't happen.. */ + + http_decode( name ); + + if( s->value && strcmp( s->value, name ) == 0 ) + { + return TRUE; + /* The names match, nothing to worry about. */ + } + else if( s->value != NULL && + ( strcmp( name, ic->acc->user ) == 0 || + set_getbool( &ic->acc->set, "local_display_name" ) ) ) + { + /* The server thinks our display name is our e-mail address + which is probably wrong, or the user *wants* us to do this: + Always use the locally set display_name. */ + return msn_set_display_name( ic, s->value ); + } + else + { + if( s->value && *s->value ) + imcb_log( ic, "BitlBee thinks your display name is `%s' but " + "the MSN server says it's `%s'. Using the MSN " + "server's name. Set local_display_name to true " + "to use the local name.", s->value, name ); + + g_free( s->value ); + s->value = g_strdup( name ); + return TRUE; + } +} -- cgit v1.2.3 From 2abceca711403e8e3308213954b4477ceecd4282 Mon Sep 17 00:00:00 2001 From: Geert Mulders Date: Tue, 6 Apr 2010 19:25:51 +0200 Subject: Updates made as a result to the comments on the review. --- protocols/twitter/twitter.c | 20 ++++++++++------ protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_http.c | 49 +++++++--------------------------------- protocols/twitter/twitter_lib.c | 18 +++++++-------- 4 files changed, 31 insertions(+), 57 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index b6b23fa5..fb7acc12 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -34,7 +34,9 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) { struct im_connection *ic = data; // Check if we are still logged in... - if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) + // We are logged in if the flag says so and the connection is still in the connections list. + if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN + && !g_slist_find( twitter_connections, ic )) return 0; // If the user uses multiple private message windows we need to get the @@ -54,6 +56,7 @@ static void twitter_init( account_t *acc ) { set_t *s; s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc ); + s->flags |= ACC_SET_OFFLINE_ONLY; } /** @@ -71,17 +74,15 @@ static void twitter_login( account_t *acc ) ic->proto_data = td; - // Set the status to logged in. - ic->flags = OPT_LOGGED_IN; + imcb_log( ic, "Connecting to twitter" ); + imcb_connected(ic); // Run this once. After this queue the main loop function. twitter_main_loop(ic, -1, 0); // Queue the main_loop - b_timeout_add(60000, twitter_main_loop, ic); - - imcb_log( ic, "Connecting to twitter" ); - imcb_connected(ic); + // Save the return value, so we can remove the timeout on logout. + td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); twitter_connections = g_slist_append( twitter_connections, ic ); } @@ -96,6 +97,9 @@ static void twitter_logout( struct im_connection *ic ) // Set the status to logged out. ic->flags = 0; + // Remove the main_loop function from the function queue. + b_event_remove(td->main_loop_id); + if( td ) { g_free( td ); @@ -148,6 +152,8 @@ static void twitter_remove_buddy( struct im_connection *ic, char *who, char *gro static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) { + if( c && message ) + twitter_post_status(c->ic, message); } static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 05a861bb..e13deddb 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -37,6 +37,7 @@ struct twitter_data char* user; char* pass; guint64 home_timeline_id; + gint main_loop_id; struct groupchat *home_timeline_gc; }; diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 4385475c..34b9408d 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -38,9 +38,7 @@ #include -char *twitter_urlencode(const char *instr); char *twitter_url_append(char *url, char *key, char* value); -static int isurlchar(unsigned char c); /** * Do a request. @@ -106,9 +104,9 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in // Make the request. request = g_strdup_printf( "%s %s HTTP/1.0\r\n" - "Host: %s\r\n" - "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", - is_post ? "POST" : "GET", url->file, url->host ); + "Host: %s\r\n" + "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", + is_post ? "POST" : "GET", url->file, url->host ); // If a pass and user are given we append them to the request. if (userpass_base64) @@ -145,8 +143,11 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in char *twitter_url_append(char *url, char *key, char* value) { - char *key_encoded = twitter_urlencode(key); - char *value_encoded = twitter_urlencode(value); + char *key_encoded = g_strndup(key, 3 * strlen(key)); + http_encode(key_encoded); + char *value_encoded = g_strndup(value, 3 * strlen(value)); + http_encode(value_encoded); + char *retval; if (strlen(url) != 0) retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded); @@ -159,35 +160,6 @@ char *twitter_url_append(char *url, char *key, char* value) return retval; } -char *twitter_urlencode(const char *instr) -{ - int ipos=0, bpos=0; - char *str = NULL; - int len = strlen(instr); - - if(!(str = g_new(char, 3*len + 1) )) - return ""; - - while(instr[ipos]) { - while(isurlchar(instr[ipos])) - str[bpos++] = instr[ipos++]; - if(!instr[ipos]) - break; - - g_snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]); - bpos+=3; - ipos++; - } - str[bpos]='\0'; - - /* free extra alloc'ed mem. */ - len = strlen(str); - str = g_renew(char, str, len+1); - - return (str); -} - - char *twitter_urldecode(const char *instr) { int ipos=0, bpos=0; @@ -228,8 +200,3 @@ char *twitter_urldecode(const char *instr) return (str); } -static int isurlchar(unsigned char c) -{ - return (isalnum(c) || '-' == c || '_' == c); -} - diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index f07897ed..d4e07c42 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -64,6 +64,7 @@ static void txu_free(struct twitter_xml_user *txu) { g_free(txu->name); g_free(txu->screen_name); + g_free(txu); } @@ -75,6 +76,7 @@ static void txs_free(struct twitter_xml_status *txs) g_free(txs->created_at); g_free(txs->text); txu_free(txs->user); + g_free(txs); } /** @@ -130,7 +132,7 @@ void twitter_get_friends_ids(struct im_connection *ic, int next_cursor) static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl ) { // Do something with the cursor. - txl->next_cursor = atoi(node->text); + txl->next_cursor = node->text != NULL ? atoi(node->text) : -1; return XT_HANDLED; } @@ -152,7 +154,7 @@ static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct tw if ( g_strcasecmp( "id", child->name ) == 0) { // Add the item to the list. - txl->list = g_slist_append (txl->list, g_memdup( node->text, node->text_len + 1 )); + txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 )); } else if ( g_strcasecmp( "next_cursor", child->name ) == 0) { @@ -186,7 +188,6 @@ static void twitter_http_get_friends_ids(struct http_request *req) } txl = g_new0(struct twitter_xml_list, 1); - txl->list = NULL; // Parse the data. parser = xt_new( NULL, txl ); @@ -450,7 +451,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code); + imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ". HTTP STATUS: %d", req->status_code); return; } @@ -487,6 +488,8 @@ static void twitter_http_get_statuses_friends(struct http_request *req) struct im_connection *ic = req->data; struct xt_parser *parser; struct twitter_xml_list *txl; + GSList *l = NULL; + struct twitter_xml_user *user; // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) @@ -495,7 +498,7 @@ static void twitter_http_get_statuses_friends(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not retrieve home/timeline. HTTP STATUS: %d", req->status_code); + imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL " HTTP STATUS: %d", req->status_code); return; } @@ -510,8 +513,6 @@ static void twitter_http_get_statuses_friends(struct http_request *req) twitter_xt_get_user_list(parser->root, txl); xt_free( parser ); - GSList *l = NULL; - struct twitter_xml_user *user; // Add the users as buddies. for ( l = txl->list; l ; l = g_slist_next(l) ) { @@ -558,8 +559,7 @@ static void twitter_http_post_status(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not post tweed... HTTP STATUS: %d", req->status_code); - imcb_error(ic, req->reply_body); + imcb_error(ic, "Could not post tweet... HTTP STATUS: %d", req->status_code); return; } } -- cgit v1.2.3 From 0519b0a42b5e0ed09f796a92aa7bd3b7d3f06b9d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 00:54:00 +0100 Subject: Killed unused twitter_urldecode() and silence some compiler warnings. --- protocols/twitter/twitter_http.c | 43 +--------------------------------------- protocols/twitter/twitter_lib.c | 2 +- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 34b9408d..3632140f 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -120,7 +120,7 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in if (is_post) { // Append the Content-Type and url-encoded arguments. - tmp = g_strdup_printf("%sContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %i\r\n\r\n%s", + tmp = g_strdup_printf("%sContent-Type: application/x-www-form-urlencoded\r\nContent-Length: %zd\r\n\r\n%s", request, strlen(url_arguments), url_arguments); g_free(request); request = tmp; @@ -159,44 +159,3 @@ char *twitter_url_append(char *url, char *key, char* value) return retval; } - -char *twitter_urldecode(const char *instr) -{ - int ipos=0, bpos=0; - char *str = NULL; - char entity[3]={0,0,0}; - unsigned dec; - int len = strlen(instr); - - if(!(str = g_new(char, len+1) )) - return ""; - - while(instr[ipos]) { - while(instr[ipos] && instr[ipos]!='%') - if(instr[ipos]=='+') { - str[bpos++]=' '; - ipos++; - } else - str[bpos++] = instr[ipos++]; - if(!instr[ipos]) - break; - - if(instr[ipos+1] && instr[ipos+2]) { - ipos++; - entity[0]=instr[ipos++]; - entity[1]=instr[ipos++]; - sscanf(entity, "%2x", &dec); - str[bpos++] = (char)dec; - } else { - str[bpos++] = instr[ipos++]; - } - } - str[bpos]='\0'; - - /* free extra alloc'ed mem. */ - len = strlen(str); - str = g_renew(char, str, len+1); - - return (str); -} - diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d4e07c42..53cbe643 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -371,7 +371,7 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor) args[1] = g_strdup_printf ("%d", next_cursor); if (td->home_timeline_id) { args[2] = "since_id"; - args[3] = g_strdup_printf ("%llu", td->home_timeline_id); + args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id); } twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2); -- cgit v1.2.3 From d5690197326bad1090dbb9f6bfc95470b479fe6b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 01:27:51 +0100 Subject: A little more cleanup. --- protocols/twitter/twitter.c | 10 +++++----- protocols/twitter/twitter_lib.c | 39 +++++++-------------------------------- 2 files changed, 12 insertions(+), 37 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index fb7acc12..d4e2ce3e 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -35,8 +35,8 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) struct im_connection *ic = data; // Check if we are still logged in... // We are logged in if the flag says so and the connection is still in the connections list. - if ((ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN - && !g_slist_find( twitter_connections, ic )) + if (!g_slist_find( twitter_connections, ic ) || + (ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) return 0; // If the user uses multiple private message windows we need to get the @@ -68,13 +68,15 @@ static void twitter_login( account_t *acc ) struct im_connection *ic = imcb_new( acc ); struct twitter_data *td = g_new0( struct twitter_data, 1 ); + twitter_connections = g_slist_append( twitter_connections, ic ); + td->user = acc->user; td->pass = acc->pass; td->home_timeline_id = 0; ic->proto_data = td; - imcb_log( ic, "Connecting to twitter" ); + imcb_log( ic, "Connecting to Twitter" ); imcb_connected(ic); // Run this once. After this queue the main loop function. @@ -83,8 +85,6 @@ static void twitter_login( account_t *acc ) // Queue the main_loop // Save the return value, so we can remove the timeout on logout. td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); - - twitter_connections = g_slist_append( twitter_connections, ic ); } /** diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 53cbe643..50f614a7 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -36,8 +36,6 @@ #define TXL_USER 2 #define TXL_ID 3 -static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ); - struct twitter_xml_list { int type; int next_cursor; @@ -100,7 +98,7 @@ static void txl_free(struct twitter_xml_list *txl) static void twitter_add_buddy(struct im_connection *ic, char *name) { // Check if the buddy is allready in the buddy list. - if (!user_findhandle( ic, name )) + if (!imcb_find_buddy( ic, name )) { // The buddy is not in the list, add the buddy and set the status to logged in. imcb_add_buddy( ic, name, NULL ); @@ -408,8 +406,13 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list) { status = l->data; twitter_add_buddy(ic, status->user->screen_name); + // Say it! - twitter_imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); + if (g_strcasecmp(td->user, status->user->screen_name) == 0) + imcb_chat_log (gc, "Your Tweet: %s", status->text); + else + imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); + // Update the home_timeline_id to hold the highest id, so that by the next request // we won't pick up the updates allready in the list. td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; @@ -596,31 +599,3 @@ void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) // g_free(args[1]); // g_free(args[3]); } - - -/** - * This function "overwrites" the imcb_chat_msg function. Because in the original the logged in user is filtered out. - */ -static void twitter_imcb_chat_msg( struct groupchat *c, char *who, char *msg, uint32_t flags, time_t sent_at ) -{ - struct im_connection *ic = c->ic; - char *wrapped; - user_t *u; - - u = user_findhandle( ic, who ); - if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) - || ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) - strip_html( msg ); - - wrapped = word_wrap( msg, 425 ); - if( c && u ) - { - irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped ); - } - else - { - imcb_log( ic, "Message from/to conversation %s@%p (unknown conv/user): %s", who, c, wrapped ); - } - g_free( wrapped ); -} - -- cgit v1.2.3 From 1014caba0ae2c737e35b8f51cafe77c1967e6b67 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 01:46:38 +0100 Subject: In groupchat mode, make contacts show up in the room instead of in &bitlbee. And clean up the room when disabling the Twitter account. --- protocols/twitter/twitter.c | 2 ++ protocols/twitter/twitter_lib.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index d4e2ce3e..f62aeada 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -100,6 +100,8 @@ static void twitter_logout( struct im_connection *ic ) // Remove the main_loop function from the function queue. b_event_remove(td->main_loop_id); + imcb_chat_free(td->home_timeline_gc); + if( td ) { g_free( td ); diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 50f614a7..3bcb59ca 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -97,12 +97,17 @@ static void txl_free(struct twitter_xml_list *txl) */ static void twitter_add_buddy(struct im_connection *ic, char *name) { + struct twitter_data *td = ic->proto_data; + // Check if the buddy is allready in the buddy list. if (!imcb_find_buddy( ic, name )) { // The buddy is not in the list, add the buddy and set the status to logged in. imcb_add_buddy( ic, name, NULL ); - imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); + if (set_getbool( &ic->acc->set, "use_groupchat" )) + imcb_chat_add_buddy( td->home_timeline_gc, name ); + else + imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); } } -- cgit v1.2.3 From 7815a2b57887751a7e026747b27abea04b13abae Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 03:15:44 +0100 Subject: Check MSN display names given by the server for UTF-8-correctness before using them since invalid XML ending up in user configs can get very ugly. --- protocols/msn/ns.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index cb10df30..8181c1af 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -767,8 +767,16 @@ static gboolean msn_ns_got_display_name( struct im_connection *ic, char *name ) "server's name. Set local_display_name to true " "to use the local name.", s->value, name ); - g_free( s->value ); - s->value = g_strdup( name ); + if( g_utf8_validate( name, -1, NULL ) ) + { + g_free( s->value ); + s->value = g_strdup( name ); + } + else + { + imcb_log( ic, "Warning: Friendly name in server response was corrupted" ); + } + return TRUE; } } -- cgit v1.2.3 From 33b306eaaa3e05cbc5d196d0d2f0b741ff11a9e6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 03:27:55 +0100 Subject: Don't allow non-8-bit character sets like utf-16 which completely break the IRC protocol. (Happened to at least two public server users by now and it renders the accounts useless without manual intervention.) --- irc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/irc.c b/irc.c index b33a483b..8cd4a33a 100644 --- a/irc.c +++ b/irc.c @@ -51,18 +51,29 @@ static char *set_eval_password( set_t *set, char *value ) static char *set_eval_charset( set_t *set, char *value ) { irc_t *irc = set->data; + char *test; + gsize test_bytes = 0; GIConv ic, oc; if( g_strcasecmp( value, "none" ) == 0 ) value = g_strdup( "utf-8" ); - if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) + if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) { return NULL; } - if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 ) + if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || + test_bytes > 1 ) + { + g_free( test ); + g_iconv_close( oc ); + irc_usermsg( irc, "Unsupported character set: The IRC protocol " + "only supports 8-bit character sets." ); + return NULL; + } + if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) { - g_iconv_close( ic ); + g_iconv_close( oc ); return NULL; } -- cgit v1.2.3 From d4efddfb7d34a8409cf78dd337f3933e0ed11d08 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 03:39:20 +0100 Subject: Unset the IPV6_V6ONLY sockctl so listening on IPv6 will automatically also listen on IPv4 again. This should close #583 (and apparently also fix this assumption on many non-Linux systems). --- bitlbee.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/bitlbee.c b/bitlbee.c index 26d12b6c..8f7be698 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -69,6 +69,15 @@ int bitlbee_daemon_init() if( global.listen_socket < 0 ) continue; +#ifdef IPV6_V6ONLY + if( res->ai_family == AF_INET6 ) + { + i = 0; + setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &i, sizeof( i ) ); + } +#endif + /* TIME_WAIT (?) sucks.. */ i = 1; setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) ); @@ -79,7 +88,6 @@ int bitlbee_daemon_init() log_error( "bind" ); return( -1 ); } - break; } -- cgit v1.2.3 From 3e5766022e8103765d62343956cf1aeba34b4d82 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 04:59:01 +0100 Subject: Show timestamps for offline messages. Including a timezone setting for people using servers outside their own timezone. --- doc/user-guide/commands.xml | 15 +++++++ irc.c | 1 + protocols/nogaim.c | 105 ++++++++++++++++++++++++++++++++++++++++++-- protocols/nogaim.h | 1 + 4 files changed, 118 insertions(+), 4 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 833ea00f..c8f2de4c 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -838,6 +838,21 @@ + + local + local, utc, gmt, timezone-spec + + + + If message timestamps are available for offline messages or chatroom backlogs, BitlBee will display them as part of the message. By default it will use the local timezone. If you're not in the same timezone as the BitlBee server, you can adjust the timestamps using this setting. + + + + Values local/utc/gmt should be self-explanatory. timezone-spec is a time offset in hours:minutes, for example: -8 for Pacific Standard Time, +2 for Central European Summer Time, +5:30 for Indian Standard Time. + + + + try diff --git a/irc.c b/irc.c index 8cd4a33a..b68c5adb 100644 --- a/irc.c +++ b/irc.c @@ -198,6 +198,7 @@ irc_t *irc_new( int fd ) s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); s->flags |= SET_NULL_OK; s = set_add( &irc->set, "strip_html", "true", NULL, irc ); + s = set_add( &irc->set, "timezone", "local", set_eval_timezone, irc ); s = set_add( &irc->set, "to_char", ": ", set_eval_to_char, irc ); s = set_add( &irc->set, "typing_notice", "false", set_eval_bool, irc ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index c326e378..3b4fe060 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -38,6 +38,7 @@ #include "chat.h" static int remove_chat_buddy_silent( struct groupchat *b, const char *handle ); +static char *format_timestamp( irc_t *irc, time_t msg_ts ); GSList *connections; @@ -717,7 +718,7 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { irc_t *irc = ic->irc; - char *wrapped; + char *wrapped, *ts; user_t *u; u = user_findhandle( ic, handle ); @@ -759,10 +760,18 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui if( ( g_strcasecmp( set_getstr( &ic->irc->set, "strip_html" ), "always" ) == 0 ) || ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) strip_html( msg ); - + + if( ( ts = format_timestamp( irc, sent_at ) ) ) + { + char *new = g_strconcat( ts, msg, NULL ); + g_free( ts ); + ts = msg = new; + } + wrapped = word_wrap( msg, 425 ); irc_msgfrom( irc, u->nick, wrapped ); g_free( wrapped ); + g_free( ts ); } void imcb_buddy_typing( struct im_connection *ic, char *handle, uint32_t flags ) @@ -866,7 +875,9 @@ void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl wrapped = word_wrap( msg, 425 ); if( c && u ) { - irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, "", wrapped ); + char *ts = format_timestamp( ic->irc, sent_at ); + irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); + g_free( ts ); } else { @@ -1060,8 +1071,94 @@ char *set_eval_away_devoice( set_t *set, char *value ) return value; } +char *set_eval_timezone( set_t *set, char *value ) +{ + char *s; + + if( strcmp( value, "local" ) == 0 || + strcmp( value, "gmt" ) == 0 || strcmp( value, "utc" ) == 0 ) + return value; + + /* Otherwise: +/- at the beginning optional, then one or more numbers, + possibly followed by a colon and more numbers. Don't bother bound- + checking them since users are free to shoot themselves in the foot. */ + s = value; + if( *s == '+' || *s == '-' ) + s ++; + + /* \d+ */ + if( !isdigit( *s ) ) + return SET_INVALID; + while( *s && isdigit( *s ) ) s ++; + + /* EOS? */ + if( *s == '\0' ) + return value; + + /* Otherwise, colon */ + if( *s != ':' ) + return SET_INVALID; + s ++; + + /* \d+ */ + if( !isdigit( *s ) ) + return SET_INVALID; + while( *s && isdigit( *s ) ) s ++; + + /* EOS */ + return *s == '\0' ? value : SET_INVALID; +} - +static char *format_timestamp( irc_t *irc, time_t msg_ts ) +{ + time_t now_ts = time( NULL ); + struct tm now, msg; + char *set; + + /* If the timestamp is <= 0 or less than a minute ago, discard it as + it doesn't seem to add to much useful info and/or might be noise. */ + if( msg_ts <= 0 || msg_ts > now_ts - 60 ) + return NULL; + + set = set_getstr( &irc->set, "timezone" ); + if( strcmp( set, "local" ) == 0 ) + { + localtime_r( &now_ts, &now ); + localtime_r( &msg_ts, &msg ); + } + else + { + int hr, min = 0, sign = 60; + + if( set[0] == '-' ) + { + sign *= -1; + set ++; + } + else if( set[0] == '+' ) + { + set ++; + } + + if( sscanf( set, "%d:%d", &hr, &min ) >= 1 ) + { + msg_ts += sign * ( hr * 60 + min ); + now_ts += sign * ( hr * 60 + min ); + } + + gmtime_r( &now_ts, &now ); + gmtime_r( &msg_ts, &msg ); + } + + if( msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday ) + return g_strdup_printf( "\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ", + msg.tm_hour, msg.tm_min, msg.tm_sec ); + else + return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d " + "%02d:%02d:%02d\x02]\x02 ", + msg.tm_year + 1900, msg.tm_mon, msg.tm_mday, + msg.tm_hour, msg.tm_min, msg.tm_sec ); +} /* The plan is to not allow straight calls to prpl functions anymore, but do them all from some wrappers. We'll start to define some down here: */ diff --git a/protocols/nogaim.h b/protocols/nogaim.h index a523a3a5..3c5e539f 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -323,6 +323,7 @@ void imc_add_block( struct im_connection *ic, char *handle ); void imc_rem_block( struct im_connection *ic, char *handle ); /* Misc. stuff */ +char *set_eval_timezone( set_t *set, char *value ); char *set_eval_away_devoice( set_t *set, char *value ); gboolean auto_reconnect( gpointer data, gint fd, b_input_condition cond ); void cancel_auto_reconnect( struct account *a ); -- cgit v1.2.3 From 91cec2ff02f956ec248dae6c8b8939f263ff8cfd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 7 Apr 2010 22:38:56 +0100 Subject: It'd be nice to not crash when the user goes away. :-) Don't export no-op set_away() funcs/etc and make nogaim detect that and give up in time. --- protocols/nogaim.c | 4 ++++ protocols/twitter/twitter.c | 12 ------------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 0c2094e2..53e459b5 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -1207,6 +1207,10 @@ int imc_away_send_update( struct im_connection *ic ) { char *away, *msg = NULL; + if( ic->acc->prpl->away_states == NULL || + ic->acc->prpl->set_away == NULL ) + return 0; + away = set_getstr( &ic->acc->set, "away" ) ? : set_getstr( &ic->irc->set, "away" ); if( away && *away ) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index f62aeada..812e0796 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -126,16 +126,6 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message /** * */ -static GList *twitter_away_states( struct im_connection *ic ) -{ - static GList *l = NULL; - return l; -} - -static void twitter_set_away( struct im_connection *ic, char *state, char *message ) -{ -} - static void twitter_set_my_name( struct im_connection *ic, char *info ) { } @@ -210,8 +200,6 @@ void twitter_initmodule() ret->init = twitter_init; ret->logout = twitter_logout; ret->buddy_msg = twitter_buddy_msg; - ret->away_states = twitter_away_states; - ret->set_away = twitter_set_away; ret->get_info = twitter_get_info; ret->set_my_name = twitter_set_my_name; ret->add_buddy = twitter_add_buddy; -- cgit v1.2.3 From 2e3a8576d6ca511df347426b4319bccde1871d06 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 01:27:42 +0100 Subject: Added a mktime_utc() to misc.c using code that used to be in jabber_util.c. I want to use this in the Twitter module. --- lib/misc.c | 35 +++++++++++++++++++++++++++++++++++ lib/misc.h | 1 + protocols/jabber/jabber_util.c | 29 +++-------------------------- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/misc.c b/lib/misc.c index fe2ff17c..1d36d639 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -78,6 +78,41 @@ time_t get_time(int year, int month, int day, int hour, int min, int sec) return mktime(&tm); } +time_t mktime_utc( struct tm *tp ) +{ + struct tm utc; + time_t res, tres; + + tp->tm_isdst = -1; + res = mktime( tp ); + /* Problem is, mktime() just gave us the GMT timestamp for the + given local time... While the given time WAS NOT local. So + we should fix this now. + + Now I could choose between messing with environment variables + (kludgy) or using timegm() (not portable)... Or doing the + following, which I actually prefer... + + tzset() may also work but in other places I actually want to + use local time. + + FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */ + gmtime_r( &res, &utc ); + utc.tm_isdst = -1; + if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min ) + /* Sweet! We're in UTC right now... */ + return res; + + tres = mktime( &utc ); + res += res - tres; + + /* Yes, this is a hack. And it will go wrong around DST changes. + BUT this is more likely to be threadsafe than messing with + environment variables, and possibly more portable... */ + + return res; +} + typedef struct htmlentity { char code[7]; diff --git a/lib/misc.h b/lib/misc.h index ce36caf5..9f2058b6 100644 --- a/lib/misc.h +++ b/lib/misc.h @@ -42,6 +42,7 @@ G_MODULE_EXPORT char *add_cr( char *text ); G_MODULE_EXPORT char *strip_newlines(char *source); G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec ); +G_MODULE_EXPORT time_t mktime_utc( struct tm *tp ); double gettime( void ); G_MODULE_EXPORT void strip_html( char *msg ); diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c index db5944bc..b8b625f7 100644 --- a/protocols/jabber/jabber_util.c +++ b/protocols/jabber/jabber_util.c @@ -666,10 +666,9 @@ int jabber_buddy_remove_bare( struct im_connection *ic, char *bare_jid ) time_t jabber_get_timestamp( struct xt_node *xt ) { - struct tm tp, utc; struct xt_node *c; - time_t res, tres; char *s = NULL; + struct tm tp; for( c = xt->children; ( c = xt_find_node( c, "x" ) ); c = c->next ) { @@ -687,30 +686,8 @@ time_t jabber_get_timestamp( struct xt_node *xt ) tp.tm_year -= 1900; tp.tm_mon --; - tp.tm_isdst = -1; /* GRRRRRRRRRRR */ - - res = mktime( &tp ); - /* Problem is, mktime() just gave us the GMT timestamp for the - given local time... While the given time WAS NOT local. So - we should fix this now. - - Now I could choose between messing with environment variables - (kludgy) or using timegm() (not portable)... Or doing the - following, which I actually prefer... */ - gmtime_r( &res, &utc ); - utc.tm_isdst = -1; /* Once more: GRRRRRRRRRRRRRRRRRR!!! */ - if( utc.tm_hour == tp.tm_hour && utc.tm_min == tp.tm_min ) - /* Sweet! We're in UTC right now... */ - return res; - - tres = mktime( &utc ); - res += res - tres; - - /* Yes, this is a hack. And it will go wrong around DST changes. - BUT this is more likely to be threadsafe than messing with - environment variables, and possibly more portable... */ - - return res; + + return mktime_utc( &tp ); } struct jabber_error *jabber_error_parse( struct xt_node *node, char *xmlns ) -- cgit v1.2.3 From 08579a1e871216a7bd7e50315894b6aeed6354ea Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 01:42:11 +0100 Subject: Parse timestamps in tweets. --- protocols/twitter/twitter_lib.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 3bcb59ca..9ca4ead6 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -21,6 +21,9 @@ * * ****************************************************************************/ +/* For strptime(): */ +#define _XOPEN_SOURCE + #include "twitter_http.h" #include "twitter.h" #include "bitlbee.h" @@ -49,7 +52,7 @@ struct twitter_xml_user { }; struct twitter_xml_status { - char *created_at; + time_t created_at; char *text; struct twitter_xml_user *user; guint64 id; @@ -71,7 +74,6 @@ static void txu_free(struct twitter_xml_user *txu) */ static void txs_free(struct twitter_xml_status *txs) { - g_free(txs->created_at); g_free(txs->text); txu_free(txs->user); g_free(txs); @@ -311,7 +313,13 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml } else if (g_strcasecmp( "created_at", child->name ) == 0) { - txs->created_at = g_memdup( child->text, child->text_len + 1 ); + struct tm parsed; + + /* Very sensitive to changes to the formatting of + this field. :-( Also assumes the timezone used + is UTC since C time handling functions suck. */ + if( strptime( child->text, "%a %b %d %H:%M:%S %z %Y", &parsed ) != NULL ) + txs->created_at = mktime_utc( &parsed ); } else if (g_strcasecmp( "user", child->name ) == 0) { @@ -416,7 +424,7 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list) if (g_strcasecmp(td->user, status->user->screen_name) == 0) imcb_chat_log (gc, "Your Tweet: %s", status->text); else - imcb_chat_msg (gc, status->user->screen_name, status->text, 0, 0 ); + imcb_chat_msg (gc, status->user->screen_name, status->text, 0, status->created_at ); // Update the home_timeline_id to hold the highest id, so that by the next request // we won't pick up the updates allready in the list. @@ -436,7 +444,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) for ( l = list; l ; l = g_slist_next(l) ) { status = l->data; - imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, 0 ); + imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, status->created_at ); // Update the home_timeline_id to hold the highest id, so that by the next request // we won't pick up the updates allready in the list. td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; -- cgit v1.2.3 From 37d84b32ca7f02f2e3b05858e090e2470b8c479b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 01:51:16 +0100 Subject: Don't free the Twitter chatroom if there isn't one.. --- protocols/twitter/twitter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 812e0796..dcbcfdfb 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -100,7 +100,8 @@ static void twitter_logout( struct im_connection *ic ) // Remove the main_loop function from the function queue. b_event_remove(td->main_loop_id); - imcb_chat_free(td->home_timeline_gc); + if(td->home_timeline_gc) + imcb_chat_free(td->home_timeline_gc); if( td ) { -- cgit v1.2.3 From 5b9b2b6413d66df01a866205af489eca9f8ea308 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 01:55:17 +0100 Subject: Added display_timestamps setting in case some people may not really like them. --- doc/user-guide/commands.xml | 10 ++++++++++ irc.c | 1 + protocols/nogaim.c | 9 ++++++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index c8f2de4c..df7ee0a9 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -559,6 +559,16 @@ + + true + + + + When incoming messages are old (i.e. offline messages and channel backlogs), BitlBee will prepend them with a timestamp. If you find them ugly or useless, you can use this setting to hide them. + + + + root root, add, add_private, add_channel, ignore diff --git a/irc.c b/irc.c index b68c5adb..28fee69d 100644 --- a/irc.c +++ b/irc.c @@ -185,6 +185,7 @@ irc_t *irc_new( int fd ) s = set_add( &irc->set, "debug", "false", set_eval_bool, irc ); s = set_add( &irc->set, "default_target", "root", NULL, irc ); s = set_add( &irc->set, "display_namechanges", "false", set_eval_bool, irc ); + s = set_add( &irc->set, "display_timestamps", "true", set_eval_bool, irc ); s = set_add( &irc->set, "handle_unknown", "root", NULL, irc ); s = set_add( &irc->set, "lcnicks", "true", set_eval_bool, irc ); s = set_add( &irc->set, "ops", "both", set_eval_ops, irc ); diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 53e459b5..36d97f51 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -723,7 +723,7 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, uint32_t flags, time_t sent_at ) { irc_t *irc = ic->irc; - char *wrapped, *ts; + char *wrapped, *ts = NULL; user_t *u; u = user_findhandle( ic, handle ); @@ -766,7 +766,8 @@ void imcb_buddy_msg( struct im_connection *ic, const char *handle, char *msg, ui ( ( ic->flags & OPT_DOES_HTML ) && set_getbool( &ic->irc->set, "strip_html" ) ) ) strip_html( msg ); - if( ( ts = format_timestamp( irc, sent_at ) ) ) + if( set_getbool( &ic->irc->set, "display_timestamps" ) && + ( ts = format_timestamp( irc, sent_at ) ) ) { char *new = g_strconcat( ts, msg, NULL ); g_free( ts ); @@ -880,7 +881,9 @@ void imcb_chat_msg( struct groupchat *c, const char *who, char *msg, uint32_t fl wrapped = word_wrap( msg, 425 ); if( c && u ) { - char *ts = format_timestamp( ic->irc, sent_at ); + char *ts = NULL; + if( set_getbool( &ic->irc->set, "display_timestamps" ) ) + ts = format_timestamp( ic->irc, sent_at ); irc_privmsg( ic->irc, u, "PRIVMSG", c->channel, ts ? : "", wrapped ); g_free( ts ); } -- cgit v1.2.3 From 92a9c686a1d2f5f98bfaed97333c9e856ec70166 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 22:56:27 +0100 Subject: Fixed 2-byte memory leak in set_eval_charset(). --- irc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/irc.c b/irc.c index 28fee69d..aa8bc140 100644 --- a/irc.c +++ b/irc.c @@ -62,6 +62,9 @@ static char *set_eval_charset( set_t *set, char *value ) { return NULL; } + + /* Do a test iconv to see if the user picked an IRC-compatible + charset (for example utf-16 goes *horribly* wrong). */ if( ( test = g_convert_with_iconv( " ", 1, oc, NULL, &test_bytes, NULL ) ) == NULL || test_bytes > 1 ) { @@ -71,6 +74,8 @@ static char *set_eval_charset( set_t *set, char *value ) "only supports 8-bit character sets." ); return NULL; } + g_free( test ); + if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 ) { g_iconv_close( oc ); -- cgit v1.2.3 From 7a90d02eede836f60a119cf516b145c8bf601d3c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Thu, 8 Apr 2010 23:16:38 +0100 Subject: Document the Twitter module. And fix the default for auto_reconnect while I'm at it. --- doc/user-guide/commands.xml | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index df7ee0a9..0664aaab 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -62,6 +62,24 @@ Account successfully added + + + account add twitter <handle> <password> + + + + This module gives you simple access to Twitter. Although it uses the Twitter API, only Twitter itself is supported at the moment. + + + + By default all your Twitter contacts will show up in your contact list and their tweets will show up as private messages or in &bitlbee depending on your settings. If you want them in a separate channel, use the use_groupchat setting (see help set use_groupchat). + + + + To send tweets yourself, send them to any of your Twitter contacts via /query (doesn't matter who), or just write in the groupchat channel if you enabled that option. + + + account add yahoo <handle> <password> @@ -400,7 +418,7 @@ - false + true @@ -901,6 +919,16 @@ + + false + + + + By default the Twitter module shows all Twitter contacts and their Tweet in &bitlbee and/or private messages. With this setting enabled the module will show all contacts and their Tweets in a separate channel. + + + + false -- cgit v1.2.3 From cca06921729ecd1ab4beaecfef001a218e5d0010 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Apr 2010 01:40:38 +0100 Subject: Added imcb_chat_nick_hint() and use it in the Twitter module to get saner channel names. This also closes bug #577, making the Skype module a bit nicer. --- protocols/nogaim.c | 29 +++++++++++++++++++++++++++++ protocols/nogaim.h | 1 + protocols/twitter/twitter_lib.c | 3 +++ 3 files changed, 33 insertions(+) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 36d97f51..fca8b302 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -821,6 +821,35 @@ struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ) return c; } +void imcb_chat_name_hint( struct groupchat *c, const char *name ) +{ + if( !c->joined ) + { + struct im_connection *ic = c->ic; + char stripped[MAX_NICK_LENGTH+1], *full_name; + + strncpy( stripped, name, MAX_NICK_LENGTH ); + stripped[MAX_NICK_LENGTH] = '\0'; + nick_strip( stripped ); + if( set_getbool( &ic->irc->set, "lcnicks" ) ) + nick_lc( stripped ); + + full_name = g_strdup_printf( "&%s", stripped ); + + if( stripped[0] && + nick_cmp( stripped, ic->irc->channel + 1 ) != 0 && + irc_chat_by_channel( ic->irc, full_name ) == NULL ) + { + g_free( c->channel ); + c->channel = full_name; + } + else + { + g_free( full_name ); + } + } +} + void imcb_chat_free( struct groupchat *c ) { struct im_connection *ic = c->ic; diff --git a/protocols/nogaim.h b/protocols/nogaim.h index 3c5e539f..48a80413 100644 --- a/protocols/nogaim.h +++ b/protocols/nogaim.h @@ -301,6 +301,7 @@ G_MODULE_EXPORT void imcb_chat_invited( struct im_connection *ic, char *handle, * the user her/himself. At that point the group chat will be visible to the * user, too. */ G_MODULE_EXPORT struct groupchat *imcb_chat_new( struct im_connection *ic, const char *handle ); +G_MODULE_EXPORT void imcb_chat_name_hint( struct groupchat *c, const char *name ); G_MODULE_EXPORT void imcb_chat_add_buddy( struct groupchat *b, const char *handle ); /* To remove a handle from a group chat. Reason can be NULL. */ G_MODULE_EXPORT void imcb_chat_remove_buddy( struct groupchat *b, const char *handle, const char *reason ); diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 9ca4ead6..93f71f3b 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -406,7 +406,10 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list) // Create a new groupchat if it does not exsist. if (!td->home_timeline_gc) { + char *name_hint = g_strdup_printf( "Twitter_%s", ic->acc->user ); td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); + imcb_chat_name_hint( gc, name_hint ); + g_free( name_hint ); // Add the current user to the chat... imcb_chat_add_buddy( gc, ic->acc->user ); } -- cgit v1.2.3 From 16592d8422bbd7acdf39d29525e580fb82d47c36 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 9 Apr 2010 02:11:10 +0100 Subject: If the user leaves the Twitter channel, allow that. Recreate it when new tweets come in. --- protocols/twitter/twitter.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index dcbcfdfb..8502cd6f 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -155,6 +155,15 @@ static void twitter_chat_invite( struct groupchat *c, char *who, char *message ) static void twitter_chat_leave( struct groupchat *c ) { + struct twitter_data *td = c->ic->proto_data; + + if( c != td->home_timeline_gc ) + return; /* WTF? */ + + /* If the user leaves the channel: Fine. Rejoin him/her once new + tweets come in. */ + imcb_chat_free(td->home_timeline_gc); + td->home_timeline_gc = NULL; } static struct groupchat *twitter_chat_with( struct im_connection *ic, char *who ) -- cgit v1.2.3 From 9bf248155cb870be9dce921d58c905f5a5c1dad3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Apr 2010 00:16:38 +0100 Subject: First stab at MSN keepalives. Only kicks in if the user goes offline during a conversation. --- protocols/msn/msn.c | 2 ++ protocols/msn/msn.h | 7 +++++++ protocols/msn/ns.c | 23 +++++++++++++++++++++-- protocols/msn/sb.c | 25 ++++++++++++++++++------- 4 files changed, 48 insertions(+), 9 deletions(-) diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c index 8930847d..2132f3d8 100644 --- a/protocols/msn/msn.c +++ b/protocols/msn/msn.c @@ -40,6 +40,8 @@ static void msn_init( account_t *acc ) s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); + + s = set_add( &acc->set, "switchboard_keepalives", "false", set_eval_bool, acc ); } static void msn_login( account_t *acc ) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 84914bc3..68ca32f8 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -30,6 +30,7 @@ */ #define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r" #define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r" +#define SB_KEEPALIVE_MESSAGE "\r\r\rDONT HANG UP ON ME!\r\r\r" #ifdef DEBUG_MSN #define debug( text... ) imcb_log( ic, text ); @@ -53,6 +54,10 @@ "TypingUser: %s\r\n" \ "\r\n\r\n" +#define SB_KEEPALIVE_HEADERS "MIME-Version: 1.0\r\n" \ + "Content-Type: text/x-ping\r\n" \ + "\r\n\r\n" + #define PROFILE_URL "http://members.msn.com/" struct msn_data @@ -82,6 +87,7 @@ struct msn_switchboard int fd; gint inp; struct msn_handler_data *handler; + gint keepalive; int trId; int ready; @@ -178,5 +184,6 @@ struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ); void msn_sb_destroy( struct msn_switchboard *sb ); gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ); int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); +gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ); #endif //_MSN_H diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index d78d753a..1f6f8c74 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -435,12 +435,25 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "FLN" ) == 0 ) { - if( cmd[1] ) - imcb_buddy_status( ic, cmd[1], 0, NULL, NULL ); + struct msn_switchboard *sb; + + if( cmd[1] == NULL ) + return 1; + + imcb_buddy_status( ic, cmd[1], 0, NULL, NULL ); + + if( ( sb = msn_sb_by_handle( ic, cmd[1] ) ) && + set_getbool( &ic->acc->set, "switchboard_keepalives" ) && + sb->keepalive == 0 ) + { + msn_sb_keepalive( sb, 0, 0 ); + sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb ); + } } else if( strcmp( cmd[0], "NLN" ) == 0 ) { const struct msn_away_state *st; + struct msn_switchboard *sb; if( num_parts != 5 ) { @@ -462,6 +475,12 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) imcb_buddy_status( ic, cmd[2], OPT_LOGGED_IN | ( st != msn_away_state_list ? OPT_AWAY : 0 ), st->name, NULL ); + + if( ( sb = msn_sb_by_handle( ic, cmd[1] ) ) && sb->keepalive > 0 ) + { + b_event_remove( sb->keepalive ); + sb->keepalive = 0; + } } else if( strcmp( cmd[0], "RNG" ) == 0 ) { diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e9526234..b4686e80 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -167,7 +167,18 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) int i, j; /* Build the message. Convert LF to CR-LF for normal messages. */ - if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 ) + if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) == 0 ) + { + i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); + buf = g_new0( char, i ); + i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); + } + else if( strcmp( text, SB_KEEPALIVE_MESSAGE ) == 0 ) + { + buf = g_strdup( SB_KEEPALIVE_HEADERS ); + i = strlen( buf ); + } + else { buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 + 1 ); i = strlen( MSN_MESSAGE_HEADERS ); @@ -181,12 +192,6 @@ int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) buf[i++] = text[j]; } } - else - { - i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->ic->acc->user ); - buf = g_new0( char, i ); - i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->ic->acc->user ); - } /* Build the final packet (MSG command + the message). */ packet = g_strdup_printf( "MSG %d N %d\r\n%s", ++sb->trId, i, buf ); @@ -763,3 +768,9 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int return( 1 ); } + +gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ) +{ + struct msn_switchboard *sb = data; + return sb->ready && msn_sb_sendmessage( sb, SB_KEEPALIVE_MESSAGE ); +} -- cgit v1.2.3 From bb839e8ae5b6228f9dcd8dda96b4e3ac5c0f63ba Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Apr 2010 02:05:39 +0100 Subject: Be more clever with keepalives; detect when a switchboard is opened with someone who's offline already. Still a hack but it eases the pain a little bit. --- protocols/msn/msn.h | 3 ++- protocols/msn/ns.c | 17 ++--------------- protocols/msn/sb.c | 33 ++++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h index 68ca32f8..61101546 100644 --- a/protocols/msn/msn.h +++ b/protocols/msn/msn.h @@ -184,6 +184,7 @@ struct groupchat *msn_sb_to_chat( struct msn_switchboard *sb ); void msn_sb_destroy( struct msn_switchboard *sb ); gboolean msn_sb_connected( gpointer data, gint source, b_input_condition cond ); int msn_sb_write_msg( struct im_connection *ic, struct msn_message *m ); -gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ); +void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ); +void msn_sb_stop_keepalives( struct msn_switchboard *sb ); #endif //_MSN_H diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c index 1f6f8c74..4056bad7 100644 --- a/protocols/msn/ns.c +++ b/protocols/msn/ns.c @@ -435,25 +435,16 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) } else if( strcmp( cmd[0], "FLN" ) == 0 ) { - struct msn_switchboard *sb; - if( cmd[1] == NULL ) return 1; imcb_buddy_status( ic, cmd[1], 0, NULL, NULL ); - if( ( sb = msn_sb_by_handle( ic, cmd[1] ) ) && - set_getbool( &ic->acc->set, "switchboard_keepalives" ) && - sb->keepalive == 0 ) - { - msn_sb_keepalive( sb, 0, 0 ); - sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb ); - } + msn_sb_start_keepalives( msn_sb_by_handle( ic, cmd[1] ), TRUE ); } else if( strcmp( cmd[0], "NLN" ) == 0 ) { const struct msn_away_state *st; - struct msn_switchboard *sb; if( num_parts != 5 ) { @@ -476,11 +467,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts ) ( st != msn_away_state_list ? OPT_AWAY : 0 ), st->name, NULL ); - if( ( sb = msn_sb_by_handle( ic, cmd[1] ) ) && sb->keepalive > 0 ) - { - b_event_remove( sb->keepalive ); - sb->keepalive = 0; - } + msn_sb_stop_keepalives( msn_sb_by_handle( ic, cmd[2] ) ); } else if( strcmp( cmd[0], "RNG" ) == 0 ) { diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index b4686e80..e2ee8570 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -254,6 +254,7 @@ void msn_sb_destroy( struct msn_switchboard *sb ) debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" ); msn_msgq_purge( ic, &sb->msgq ); + msn_sb_stop_keepalives( sb ); if( sb->key ) g_free( sb->key ); if( sb->who ) g_free( sb->who ); @@ -475,6 +476,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) } sb->ready = 1; + + msn_sb_start_keepalives( sb, FALSE ); } else if( strcmp( cmd[0], "CAL" ) == 0 ) { @@ -524,6 +527,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) sb->msgq = g_slist_remove( sb->msgq, m ); } + msn_sb_start_keepalives( sb, FALSE ); + return( st ); } else if( sb->who ) @@ -585,6 +590,8 @@ static int msn_sb_command( gpointer data, char **cmd, int num_parts ) if( sb->who ) { + msn_sb_stop_keepalives( sb ); + /* This is a single-person chat, and the other person is leaving. */ g_free( sb->who ); sb->who = NULL; @@ -769,8 +776,32 @@ static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int return( 1 ); } -gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ) +static gboolean msn_sb_keepalive( gpointer data, gint source, b_input_condition cond ) { struct msn_switchboard *sb = data; return sb->ready && msn_sb_sendmessage( sb, SB_KEEPALIVE_MESSAGE ); } + +void msn_sb_start_keepalives( struct msn_switchboard *sb, gboolean initial ) +{ + struct buddy *b; + + if( sb && sb->who && sb->keepalive == 0 && + ( b = imcb_find_buddy( sb->ic, sb->who ) ) && !b->present && + set_getbool( &sb->ic->acc->set, "switchboard_keepalives" ) ) + { + if( initial ) + msn_sb_keepalive( sb, 0, 0 ); + + sb->keepalive = b_timeout_add( 20000, msn_sb_keepalive, sb ); + } +} + +void msn_sb_stop_keepalives( struct msn_switchboard *sb ) +{ + if( sb && sb->keepalive > 0 ) + { + b_event_remove( sb->keepalive ); + sb->keepalive = 0; + } +} -- cgit v1.2.3 From 4fca1db5144a17f9313db9ee4103182d2c0f22e4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 10 Apr 2010 11:07:11 +0100 Subject: Make compiling on BSD less painful: mktemp really does need a template on some machines, and warn the user about make vs. gmake at configure time. --- Makefile | 5 +++++ configure | 20 +++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 337fcf63..15db5098 100644 --- a/Makefile +++ b/Makefile @@ -125,3 +125,8 @@ endif ctags: ctags `find . -name "*.c"` `find . -name "*.h"` + +# Using this as a bogus Make target to test if a GNU-compatible version of +# make is available. +helloworld: + @echo Hello World diff --git a/configure b/configure index 27fe4c88..24f60d7f 100755 --- a/configure +++ b/configure @@ -268,7 +268,7 @@ EOF detect_ldap() { - TMPFILE=$(mktemp) + TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX) if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then cat<>Makefile.settings EFLAGS+=-lldap @@ -296,7 +296,7 @@ int main() detect_resolv_dynamic() { - TMPFILE=$(mktemp) + TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX) ret=1 echo "$RESOLV_TESTCODE" | $CC -o $TMPFILE -x c - -lresolv >/dev/null 2>/dev/null if [ "$?" = "0" ]; then @@ -310,7 +310,7 @@ detect_resolv_dynamic() detect_resolv_static() { - TMPFILE=$(mktemp) + TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX) ret=1 for i in $systemlibdirs; do if [ -f $i/libresolv.a ]; then @@ -477,6 +477,20 @@ if [ -n "$BITLBEE_VERSION" ]; then echo fi +if ! make helloworld > /dev/null 2>&1; then + echo "WARNING: Your version of make (BSD make?) does not support BitlBee's makefiles." + echo "BitlBee needs GNU make to build properly. On most systems GNU make is available" + echo "under the name 'gmake'." + echo + if gmake helloworld > /dev/null 2>&1; then + echo "gmake seems to be available on your machine, great." + echo + else + echo "gmake is not installed (or not working). Please try to install it." + echo + fi +fi + cat <bitlbee.pc prefix=$prefix includedir=$includedir -- cgit v1.2.3 From ec2ebcc07f4df02aee43712dae1245eb34c48575 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 12 Apr 2010 00:13:19 +0200 Subject: Use 320 replies for status messages in /WHOIS, 333 was a bad idea. Closes Debian bug #576120. --- irc_commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irc_commands.c b/irc_commands.c index a417e0d9..319d549a 100644 --- a/irc_commands.c +++ b/irc_commands.c @@ -497,7 +497,7 @@ static void irc_cmd_whois( irc_t *irc, char **cmd ) else if( u->away ) irc_reply( irc, 301, "%s :%s", u->nick, u->away ); if( u->status_msg ) - irc_reply( irc, 333, "%s :Status: %s", u->nick, u->status_msg ); + irc_reply( irc, 320, "%s :%s", u->nick, u->status_msg ); irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); } -- cgit v1.2.3 From 3e69802454295ffbfbbbe9bca4fcd226b5b63b28 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 14:51:05 +0200 Subject: Use full name information of Twitter buddies. --- protocols/twitter/twitter.c | 1 + protocols/twitter/twitter_lib.c | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 8502cd6f..5027eb74 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -33,6 +33,7 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) { struct im_connection *ic = data; + // Check if we are still logged in... // We are logged in if the flag says so and the connection is still in the connections list. if (!g_slist_find( twitter_connections, ic ) || diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 93f71f3b..e297f6b2 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -97,7 +97,7 @@ static void txl_free(struct twitter_xml_list *txl) /** * Add a buddy if it is not allready added, set the status to logged in. */ -static void twitter_add_buddy(struct im_connection *ic, char *name) +static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname) { struct twitter_data *td = ic->proto_data; @@ -106,6 +106,7 @@ static void twitter_add_buddy(struct im_connection *ic, char *name) { // The buddy is not in the list, add the buddy and set the status to logged in. imcb_add_buddy( ic, name, NULL ); + imcb_rename_buddy( ic, name, fullname ); if (set_getbool( &ic->acc->set, "use_groupchat" )) imcb_chat_add_buddy( td->home_timeline_gc, name ); else @@ -421,7 +422,7 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list) for ( l = list; l ; l = g_slist_next(l) ) { status = l->data; - twitter_add_buddy(ic, status->user->screen_name); + twitter_add_buddy(ic, status->user->screen_name, status->user->name); // Say it! if (g_strcasecmp(td->user, status->user->screen_name) == 0) @@ -536,7 +537,7 @@ static void twitter_http_get_statuses_friends(struct http_request *req) for ( l = txl->list; l ; l = g_slist_next(l) ) { user = l->data; - twitter_add_buddy(ic, user->screen_name); + twitter_add_buddy(ic, user->screen_name, user->name); } // if the next_cursor is set to something bigger then 0 there are more friends to gather. -- cgit v1.2.3 From 3bd4a9327bb2c7c662e6d385b3bf6903ca8ca09a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 19:36:16 +0200 Subject: Suppress HTTP error messages unless we get five or more in a row. --- protocols/twitter/twitter.c | 5 +---- protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_lib.c | 35 +++++++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 5027eb74..727eff91 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -35,9 +35,7 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) struct im_connection *ic = data; // Check if we are still logged in... - // We are logged in if the flag says so and the connection is still in the connections list. - if (!g_slist_find( twitter_connections, ic ) || - (ic->flags & OPT_LOGGED_IN) != OPT_LOGGED_IN) + if (!g_slist_find( twitter_connections, ic )) return 0; // If the user uses multiple private message windows we need to get the @@ -78,7 +76,6 @@ static void twitter_login( account_t *acc ) ic->proto_data = td; imcb_log( ic, "Connecting to Twitter" ); - imcb_connected(ic); // Run this once. After this queue the main loop function. twitter_main_loop(ic, -1, 0); diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index e13deddb..88caa104 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -39,6 +39,7 @@ struct twitter_data guint64 home_timeline_id; gint main_loop_id; struct groupchat *home_timeline_gc; + gint http_fails; }; /** diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index e297f6b2..36871e93 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -179,8 +179,10 @@ static void twitter_http_get_friends_ids(struct http_request *req) struct im_connection *ic; struct xt_parser *parser; struct twitter_xml_list *txl; + struct twitter_data *td; ic = req->data; + td = ic->proto_data; // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) @@ -189,8 +191,12 @@ static void twitter_http_get_friends_ids(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not retrieve friends. HTTP STATUS: %d", req->status_code); + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve friends. HTTP STATUS: %d", req->status_code); + return; + } else { + td->http_fails = 0; } txl = g_new0(struct twitter_xml_list, 1); @@ -461,6 +467,7 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) static void twitter_http_get_home_timeline(struct http_request *req) { struct im_connection *ic = req->data; + struct twitter_data *td = ic->proto_data; struct xt_parser *parser; struct twitter_xml_list *txl; @@ -469,9 +476,24 @@ static void twitter_http_get_home_timeline(struct http_request *req) return; // Check if the HTTP request went well. - if (req->status_code != 200) { + if (req->status_code == 200) + { + td->http_fails = 0; + if (!ic->flags & OPT_LOGGED_IN) + imcb_connected(ic); + } + else if (req->status_code == 401) + { + imcb_error( ic, "Authentication failure" ); + imc_logout( ic, FALSE ); + return; + } + else + { // It didn't go well, output the error and return. - imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ". HTTP STATUS: %d", req->status_code); + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ". HTTP STATUS: %d", req->status_code); + return; } @@ -506,6 +528,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) static void twitter_http_get_statuses_friends(struct http_request *req) { struct im_connection *ic = req->data; + struct twitter_data *td = ic->proto_data; struct xt_parser *parser; struct twitter_xml_list *txl; GSList *l = NULL; @@ -518,8 +541,12 @@ static void twitter_http_get_statuses_friends(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL " HTTP STATUS: %d", req->status_code); + if (++td->http_fails >= 5) + imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL " HTTP STATUS: %d", req->status_code); + return; + } else { + td->http_fails = 0; } txl = g_new0(struct twitter_xml_list, 1); -- cgit v1.2.3 From 3254c12328517b8b4bc12d8ef412c6b878f7b28d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 13 Apr 2010 21:11:28 +0200 Subject: Adding BITLBEE_VERSION_CODE macro to make plugin authors' lifes easier. --- bitlbee.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bitlbee.h b/bitlbee.h index 5f98deef..0b31bbc1 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -34,8 +34,10 @@ #define _WIN32_WINNT 0x0501 #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.2.5" +#define BITLBEE_VERSION "1.2.6" #define VERSION BITLBEE_VERSION +#define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#define BITLBEE_VERSION_CODE BITLBEE_VER(1, 2, 6) #define MAX_STRING 511 -- cgit v1.2.3 From 37aa3172c7e9ec6ba7b5985ed60c41c621e8e7b9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 00:09:40 +0200 Subject: Small Valgrind noise fix. (Check if the conn is still alive before getting its private data.) --- protocols/twitter/twitter_lib.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 36871e93..081612ac 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -182,11 +182,12 @@ static void twitter_http_get_friends_ids(struct http_request *req) struct twitter_data *td; ic = req->data; - td = ic->proto_data; // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) return; + + td = ic->proto_data; // Check if the HTTP request went well. if (req->status_code != 200) { @@ -467,13 +468,15 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) static void twitter_http_get_home_timeline(struct http_request *req) { struct im_connection *ic = req->data; - struct twitter_data *td = ic->proto_data; + struct twitter_data *td; struct xt_parser *parser; struct twitter_xml_list *txl; // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) return; + + td = ic->proto_data; // Check if the HTTP request went well. if (req->status_code == 200) @@ -528,7 +531,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) static void twitter_http_get_statuses_friends(struct http_request *req) { struct im_connection *ic = req->data; - struct twitter_data *td = ic->proto_data; + struct twitter_data *td; struct xt_parser *parser; struct twitter_xml_list *txl; GSList *l = NULL; @@ -537,7 +540,9 @@ static void twitter_http_get_statuses_friends(struct http_request *req) // Check if the connection is still active. if( !g_slist_find( twitter_connections, ic ) ) return; - + + td = ic->proto_data; + // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. -- cgit v1.2.3 From 40bc82df007cb78b155a098dae86b1d5bc7cda9f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 00:20:36 +0200 Subject: Run setsid() when daemonizing. (Closes #581) --- bitlbee.c | 1 + 1 file changed, 1 insertion(+) diff --git a/bitlbee.c b/bitlbee.c index 8f7be698..c53f7216 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -114,6 +114,7 @@ int bitlbee_daemon_init() else if( i != 0 ) exit( 0 ); + setsid(); chdir( "/" ); if( getenv( "_BITLBEE_RESTART_STATE" ) == NULL ) -- cgit v1.2.3 From 3d1481f0c57ade74fb1d888bce8ce4e8e17bc269 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 00:25:40 +0200 Subject: Adding -V flag to show version info from a not-serving binary. (Bug #580) --- conf.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/conf.c b/conf.c index b997fb0a..337d0cfe 100644 --- a/conf.c +++ b/conf.c @@ -78,7 +78,7 @@ conf_t *conf_load( int argc, char *argv[] ) at a *valid* configuration file. */ } - while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:" ) ) >= 0 ) + while( argc > 0 && ( opt = getopt( argc, argv, "i:p:P:nvIDFc:d:hR:u:V" ) ) >= 0 ) /* ^^^^ Just to make sure we skip this step from the REHASH handler. */ { if( opt == 'i' ) @@ -144,7 +144,14 @@ conf_t *conf_load( int argc, char *argv[] ) " -c Load alternative configuration file\n" " -d Specify alternative user configuration directory\n" " -x Command-line interface to password encryption/hashing\n" - " -h Show this help page.\n" ); + " -h Show this help page.\n" + " -V Show version info.\n" ); + return NULL; + } + else if( opt == 'V' ) + { + printf( "BitlBee %s\nAPI version %06x\n", + BITLBEE_VERSION, BITLBEE_VERSION_CODE ); return NULL; } else if( opt == 'R' ) -- cgit v1.2.3 From 542e44ac43d901c9ad70b7f89dde47eff4d9d8db Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 00:35:55 +0200 Subject: fsync() the user configuration file before closing it. (Bug #559) --- storage_xml.c | 1 + 1 file changed, 1 insertion(+) diff --git a/storage_xml.c b/storage_xml.c index b6745c75..8c524ca9 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -495,6 +495,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite ) if( !xml_printf( fd, 0, "\n" ) ) goto write_error; + fsync( fd ); close( fd ); path2 = g_strndup( path, strlen( path ) - 1 ); -- cgit v1.2.3 From c0417e80a965d74944260752f4f58378a4cdd8fd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 00:57:44 +0200 Subject: 1.2.6 changelist. --- doc/CHANGES | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/CHANGES b/doc/CHANGES index 1cac2dc7..62d2d4df 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,22 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.2.6: +- Native (very basic) support for Twitter. +- Fixed format of status messages in /WHOIS to improve IRC client + compatibility. +- Show timestamps of offline messages/channel backlogs. +- Allow saving MSN display names locally since sometimes this stuff breaks + server-side. (Use the local_display_name per-account setting.) +- Suppress empty "Headline:" messages for certain new XMPP broadcast + messages. +- Better handling of XMPP contacts with multiple resources on-line. Default + behaviour now is to write to wherever the last message came from, or to + the bare JID (usually becomes a broadcast) if there wasn't any recent msg. +- The usual misc. bug fixes. + +Finished ... + Version 1.2.5: - Many bug fixes, including a fix for MSN login issues, Jabber login timing issues, Yahoo! crashes at login time with huge contact lists, @@ -22,7 +38,7 @@ Version 1.2.5: routing issues on Jabber (i.e. messages going someone's phone instead of the main client). -Fixed 17 Mar 2010 +Finished 17 Mar 2010 Version 1.2.4: - Most important change (and main reason for releasing now): Upgraded Yahoo! -- cgit v1.2.3 From 156bbd7b66cf29220c2ff6a86217c4dec5e33765 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 01:55:30 +0200 Subject: Log to stderr+syslog until daemonized. Current behaviour is too confusing and annoying. --- bitlbee.c | 10 ++++++++-- log.c | 2 ++ unix.c | 13 ++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index c53f7216..2d543eba 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -41,8 +41,8 @@ int bitlbee_daemon_init() int i; FILE *fp; - log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); - log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); + log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE ); + log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE ); memset( &hints, 0, sizeof( hints ) ); hints.ai_family = PF_UNSPEC; @@ -145,6 +145,12 @@ int bitlbee_daemon_init() } #endif + if( !global.conf->nofork ) + { + log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); + log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); + } + return( 0 ); } diff --git a/log.c b/log.c index 4606fb88..d7d971bd 100644 --- a/log.c +++ b/log.c @@ -171,5 +171,7 @@ static void log_console(int level, char *message) { if(level == LOGLVL_DEBUG) fprintf(stdout, "Debug: %s\n", message); #endif + /* Always log stuff in syslogs too. */ + log_syslog(level, message); return; } diff --git a/unix.c b/unix.c index 7088d0f8..d58395a1 100644 --- a/unix.c +++ b/unix.c @@ -72,19 +72,22 @@ int main( int argc, char *argv[] ) log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); i = bitlbee_inetd_init(); - log_message( LOGLVL_INFO, "Bitlbee %s starting in inetd mode.", BITLBEE_VERSION ); + log_message( LOGLVL_INFO, "BitlBee %s starting in inetd mode.", BITLBEE_VERSION ); } else if( global.conf->runmode == RUNMODE_DAEMON ) { - log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); - log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); + log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE ); + log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE ); i = bitlbee_daemon_init(); - log_message( LOGLVL_INFO, "Bitlbee %s starting in daemon mode.", BITLBEE_VERSION ); + log_message( LOGLVL_INFO, "BitlBee %s starting in daemon mode.", BITLBEE_VERSION ); } else if( global.conf->runmode == RUNMODE_FORKDAEMON ) { + log_link( LOGLVL_ERROR, LOGOUTPUT_CONSOLE ); + log_link( LOGLVL_WARNING, LOGOUTPUT_CONSOLE ); + /* In case the operator requests a restart, we need this. */ old_cwd = g_malloc( 256 ); if( getcwd( old_cwd, 255 ) == NULL ) @@ -95,7 +98,7 @@ int main( int argc, char *argv[] ) } i = bitlbee_daemon_init(); - log_message( LOGLVL_INFO, "Bitlbee %s starting in forking daemon mode.", BITLBEE_VERSION ); + log_message( LOGLVL_INFO, "BitlBee %s starting in forking daemon mode.", BITLBEE_VERSION ); } if( i != 0 ) return( i ); -- cgit v1.2.3 From f9ed3113c4bc5110171295abef9c140e1328aeb1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 14 Apr 2010 21:08:23 +0200 Subject: getaddrinfo(NULL) may return an IPv4 address first but we should definitely prefer IPv6. --- bitlbee.c | 76 ++++++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 28 deletions(-) diff --git a/bitlbee.c b/bitlbee.c index 2d543eba..89e33223 100644 --- a/bitlbee.c +++ b/bitlbee.c @@ -35,6 +35,43 @@ static gboolean bitlbee_io_new_client( gpointer data, gint fd, b_input_condition condition ); +static gboolean try_listen( struct addrinfo *res ) +{ + int i; + + global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); + if( global.listen_socket < 0 ) + { + log_error( "socket" ); + return FALSE; + } + +#ifdef IPV6_V6ONLY + if( res->ai_family == AF_INET6 ) + { + i = 0; + setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY, + (char *) &i, sizeof( i ) ); + } +#endif + + /* TIME_WAIT (?) sucks.. */ + i = 1; + setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) ); + + i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen ); + if( i == -1 ) + { + closesocket( global.listen_socket ); + global.listen_socket = -1; + + log_error( "bind" ); + return FALSE; + } + + return TRUE; +} + int bitlbee_daemon_init() { struct addrinfo *res, hints, *addrinfo_bind; @@ -62,35 +99,18 @@ int bitlbee_daemon_init() } global.listen_socket = -1; - + + /* Try IPv6 first (which will become an IPv6+IPv4 socket). */ for( res = addrinfo_bind; res; res = res->ai_next ) - { - global.listen_socket = socket( res->ai_family, res->ai_socktype, res->ai_protocol ); - if( global.listen_socket < 0 ) - continue; - -#ifdef IPV6_V6ONLY - if( res->ai_family == AF_INET6 ) - { - i = 0; - setsockopt( global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY, - (char *) &i, sizeof( i ) ); - } -#endif - - /* TIME_WAIT (?) sucks.. */ - i = 1; - setsockopt( global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof( i ) ); - - i = bind( global.listen_socket, res->ai_addr, res->ai_addrlen ); - if( i == -1 ) - { - log_error( "bind" ); - return( -1 ); - } - break; - } - + if( res->ai_family == AF_INET6 && try_listen( res ) ) + break; + + /* The rest (so IPv4, I guess). */ + if( res == NULL ) + for( res = addrinfo_bind; res; res = res->ai_next ) + if( res->ai_family != AF_INET6 && try_listen( res ) ) + break; + freeaddrinfo( addrinfo_bind ); i = listen( global.listen_socket, 10 ); -- cgit v1.2.3 From e88fbe272c097066e538d670fe8fa907e9847321 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 16 Apr 2010 01:10:10 +0200 Subject: Added a meta-contact twitter_$username and replaced the "use_groupchat" setting with a "mode" setting which also allows for a mode where everything just comes from the meta-contact. Tweets should now go to that user or to the channel (if available). Messages to others become DMs. --- doc/user-guide/commands.xml | 36 +++++++++++++++++++++++------------- protocols/twitter/twitter.c | 28 ++++++++++++++++++++++------ protocols/twitter/twitter_lib.c | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 24 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 0664aaab..7dd23d0c 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -20,7 +20,7 @@ - Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see help account add <protocol>. + Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ), Yahoo and Twitter. For more information about adding an account, see help account add <protocol>. @@ -72,11 +72,11 @@ - By default all your Twitter contacts will show up in your contact list and their tweets will show up as private messages or in &bitlbee depending on your settings. If you want them in a separate channel, use the use_groupchat setting (see help set use_groupchat). + By default all your Twitter contacts will come from a contact called twitter_(yourusername). You can change this behaviour using the mode setting (see help set mode). - To send tweets yourself, send them to any of your Twitter contacts via /query (doesn't matter who), or just write in the groupchat channel if you enabled that option. + To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option. @@ -658,6 +658,26 @@ + + one, many, chat + one + + + + By default, everything from the Twitter module will come from one nick, twitter_(yourusername). If you prefer to have individual nicks for everyone, you can set this setting to "many" instead. + + + + If you prefer to have all your Twitter things in a separate channel, you can set this setting to "chat". + + + + In the last two modes, you can send direct messages by /msg'ing your contacts directly. Note, however, that incoming DMs are not fetched yet. + + + + + @@ -919,16 +939,6 @@ - - false - - - - By default the Twitter module shows all Twitter contacts and their Tweet in &bitlbee and/or private messages. With this setting enabled the module will show all contacts and their Tweets in a separate channel. - - - - false diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 727eff91..29be8a96 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -40,7 +40,7 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) // If the user uses multiple private message windows we need to get the // users buddies. - if (!set_getbool( &ic->acc->set, "use_groupchat" )) + if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "many") == 0) twitter_get_statuses_friends(ic, -1); // Do stuff.. @@ -50,11 +50,21 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; } +static char *set_eval_mode( set_t *set, char *value ) +{ + if( g_strcasecmp( value, "one" ) == 0 || + g_strcasecmp( value, "many" ) == 0 || + g_strcasecmp( value, "char" ) == 0 ) + return value; + else + return NULL; +} static void twitter_init( account_t *acc ) { set_t *s; - s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, acc ); + + s = set_add( &acc->set, "mode", "one", set_eval_mode, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; } @@ -66,6 +76,7 @@ static void twitter_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); struct twitter_data *td = g_new0( struct twitter_data, 1 ); + char name[strlen(acc->user)+9]; twitter_connections = g_slist_append( twitter_connections, ic ); @@ -83,6 +94,10 @@ static void twitter_login( account_t *acc ) // Queue the main_loop // Save the return value, so we can remove the timeout on logout. td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); + + sprintf( name, "twitter_%s", acc->user ); + imcb_add_buddy( ic, name, NULL ); + imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); } /** @@ -114,11 +129,12 @@ static void twitter_logout( struct im_connection *ic ) */ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { - // Let's just update the status. -// if ( g_strcasecmp(who, ic->acc->user) == 0 ) + if (g_strncasecmp(who, "twitter_", 8) == 0 && + g_strcasecmp(who + 8, ic->acc->user) == 0) twitter_post_status(ic, message); -// else -// twitter_direct_messages_new(ic, who, message); + else + twitter_direct_messages_new(ic, who, message); + return( 0 ); } diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 081612ac..04e8ef95 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -104,12 +104,14 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char * // Check if the buddy is allready in the buddy list. if (!imcb_find_buddy( ic, name )) { + char *mode = set_getstr(&ic->acc->set, "mode"); + // The buddy is not in the list, add the buddy and set the status to logged in. imcb_add_buddy( ic, name, NULL ); imcb_rename_buddy( ic, name, fullname ); - if (set_getbool( &ic->acc->set, "use_groupchat" )) + if (g_strcasecmp(mode, "chat") == 0) imcb_chat_add_buddy( td->home_timeline_gc, name ); - else + else if (g_strcasecmp(mode, "many") == 0) imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); } } @@ -451,14 +453,37 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) struct twitter_data *td = ic->proto_data; GSList *l = NULL; struct twitter_xml_status *status; + char from[MAX_STRING]; + gboolean mode_one; + + mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0; + if( mode_one ) + { + g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user ); + from[MAX_STRING-1] = '\0'; + } + for ( l = list; l ; l = g_slist_next(l) ) { + char *text = NULL; + status = l->data; - imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, status->created_at ); + + if( mode_one ) + text = g_strdup_printf( "\002<\002%s\002>\002 %s", + status->user->screen_name, status->text ); + + imcb_buddy_msg( ic, + mode_one ? from : status->user->screen_name, + mode_one ? text : status->text, + 0, status->created_at ); + // Update the home_timeline_id to hold the highest id, so that by the next request // we won't pick up the updates allready in the list. td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id; + + g_free( text ); } } @@ -511,7 +536,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) xt_free( parser ); // See if the user wants to see the messages in a groupchat window or as private messages. - if (set_getbool( &ic->acc->set, "use_groupchat" )) + if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) twitter_groupchat(ic, txl->list); else twitter_private_message_chat(ic, txl->list); @@ -611,7 +636,7 @@ static void twitter_http_post_status(struct http_request *req) // Check if the HTTP request went well. if (req->status_code != 200) { // It didn't go well, output the error and return. - imcb_error(ic, "Could not post tweet... HTTP STATUS: %d", req->status_code); + imcb_error(ic, "Could not post message... HTTP STATUS: %d", req->status_code); return; } } -- cgit v1.2.3 From 55b1e690c595ac6b027cf4bd1465c6676214bb50 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 16 Apr 2010 01:15:36 +0200 Subject: Since the home/timeline response can (and often does) come in before the user list, call twitter_add_buddy() on all incoming tweets to avoid getting tweets from people who aren't listed yet. --- protocols/twitter/twitter_lib.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index 04e8ef95..d58afd73 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -473,6 +473,8 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list) if( mode_one ) text = g_strdup_printf( "\002<\002%s\002>\002 %s", status->user->screen_name, status->text ); + else + twitter_add_buddy(ic, status->user->screen_name, status->user->name); imcb_buddy_msg( ic, mode_one ? from : status->user->screen_name, -- cgit v1.2.3 From b6190ca72d44523bc775ee3fc89b4bdbffe2cff4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 16 Apr 2010 01:34:41 +0200 Subject: Changelog update: Credit where credit is due! --- doc/CHANGES | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/CHANGES b/doc/CHANGES index 62d2d4df..a79be7e5 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -4,7 +4,9 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on Version 1.2.6: -- Native (very basic) support for Twitter. +- Native (very basic) support for Twitter, implemented by Geert Mulders. + Currently supported are posting tweets, reading the ones of people you + follow, and sending (not yet receiving!) direct messages. - Fixed format of status messages in /WHOIS to improve IRC client compatibility. - Show timestamps of offline messages/channel backlogs. -- cgit v1.2.3 From 0f8423414dffedb0a9e8e1e9a041cac356808911 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 17 Apr 2010 01:44:57 +0200 Subject: Help info for the switchboard_keepalives setting. --- doc/user-guide/commands.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 7dd23d0c..a8f030b2 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -886,6 +886,24 @@ + + false + + + + Turn on this flag if you have difficulties talking to offline/invisible contacts. + + + + With this setting enabled, BitlBee will send keepalives to MSN switchboards with offline/invisible contacts every twenty seconds. This should keep the server and client on the other side from shutting it down. + + + + This is useful because BitlBee doesn't support MSN offline messages yet and the MSN servers won't let the user reopen switchboards to offline users. Once offline messaging is supported, this flag might be removed. + + + + local local, utc, gmt, timezone-spec -- cgit v1.2.3 From a897467549fc75eee8fdd7c255ee5f55c3714ac6 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 18 Apr 2010 00:43:55 +0200 Subject: I should stop doing commits with the debugging stuff still enabled. --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 997b09f7..44a21fae 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -871,7 +871,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + //purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From 57400f4283fa232d31d14b2c0edd44d1c01cb8b8 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Apr 2010 11:26:49 +0200 Subject: Use the $PIDFILE variable in the Debian init script. --- debian/bitlbee.init | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/bitlbee.init b/debian/bitlbee.init index 4c224ffc..be1dcd66 100755 --- a/debian/bitlbee.init +++ b/debian/bitlbee.init @@ -37,8 +37,8 @@ fi # d_start() { # Make sure BitlBee can actually write its PID... - touch /var/run/bitlbee.pid - chown bitlbee: /var/run/bitlbee.pid + touch $PIDFILE + chown bitlbee: $PIDFILE start-stop-daemon --start --quiet --pidfile $PIDFILE \ --exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS -- cgit v1.2.3 From f4bcc223fea70de8555bbc4d2caf48e0476c0e13 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Apr 2010 11:40:32 +0200 Subject: Finalized 1.2.6 changelog. --- doc/CHANGES | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/CHANGES b/doc/CHANGES index a79be7e5..94330bf9 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -17,9 +17,12 @@ Version 1.2.6: - Better handling of XMPP contacts with multiple resources on-line. Default behaviour now is to write to wherever the last message came from, or to the bare JID (usually becomes a broadcast) if there wasn't any recent msg. +- Added a switchboard_keepalives setting which should solve some issues with + talking to offline MSN contacts. (Although full support for offline + messages is not ready yet!) - The usual misc. bug fixes. -Finished ... +Finished 19 Apr 2010 Version 1.2.5: - Many bug fixes, including a fix for MSN login issues, Jabber login timing -- cgit v1.2.3 From db57e7c0d4a88e2a2b0862afa20a1255dfffc58d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Apr 2010 15:08:34 +0200 Subject: 1.2.6a. I fail. :-( The 1.2.6 I just released has a groupchat mode in the Twitter module that doesn't actually work... --- bitlbee.h | 2 +- doc/CHANGES | 6 ++++++ protocols/twitter/twitter.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bitlbee.h b/bitlbee.h index 0b31bbc1..f4e03d76 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -34,7 +34,7 @@ #define _WIN32_WINNT 0x0501 #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.2.6" +#define BITLBEE_VERSION "1.2.6a" #define VERSION BITLBEE_VERSION #define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #define BITLBEE_VERSION_CODE BITLBEE_VER(1, 2, 6) diff --git a/doc/CHANGES b/doc/CHANGES index 94330bf9..45b16e28 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,12 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.2.6a: +- Fixed a typo that renders the Twitter groupchat mode unusable. A last- + minute change that came a few minutes late. + +Finished 19 Apr 2010 + Version 1.2.6: - Native (very basic) support for Twitter, implemented by Geert Mulders. Currently supported are posting tweets, reading the ones of people you diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 29be8a96..726c7cb1 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -54,7 +54,7 @@ static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || g_strcasecmp( value, "many" ) == 0 || - g_strcasecmp( value, "char" ) == 0 ) + g_strcasecmp( value, "chat" ) == 0 ) return value; else return NULL; -- cgit v1.2.3 From dd0dcac7a5814a4358e4a11388bd9155eeca9c53 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 12:51:08 +0100 Subject: Debian package changelog entry. --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index f969b410..56d0a551 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +bitlbee (1.2.6a-1) unstable; urgency=low + + * New upstream version. + * Native support for Twitter. + * Fixed /WHOIS response format. (Closes: #576120) + * Problems with bitlbee-skype are solved by now. (Closes: #575572) + + -- Wilmer van der Gaast Tue, 20 Apr 2010 00:34:51 +0200 + bitlbee (1.2.5-1) unstable; urgency=low * New upstream version. -- cgit v1.2.3 From f1b7711f566163ff27a8f13ae3ccc7214a24fe70 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 15:15:54 +0100 Subject: Also override package version number for bitlbee-dev when $BITLBEE_VERSION is set. --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 788e5006..0c656fd8 100755 --- a/debian/rules +++ b/debian/rules @@ -98,7 +98,7 @@ binary-indep: install-indep cd debian/bitlbee-dev; \ find usr -type f -exec md5sum {} \; > DEBIAN/md5sums - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev + dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0 dpkg --build debian/bitlbee-dev .. -- cgit v1.2.3 From b5b40ffd38e315223c6e38e4a291cbd58e471062 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 17:57:34 +0100 Subject: Added BITLBEE_CONFIGURE_FLAGS variable so configure flags can be overridden when generating debs. --- debian/rules | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 788e5006..56a02fbb 100755 --- a/debian/rules +++ b/debian/rules @@ -1,5 +1,6 @@ #!/usr/bin/make -f +BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 ifdef BITLBEE_VERSION @@ -13,7 +14,7 @@ endif build-arch: build-arch-stamp build-arch-stamp: [ -d debian ] - ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent + ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) # $(MAKE) -C doc/ all touch build-arch-stamp -- cgit v1.2.3 From c5213622819e578c577158be6b0610ef9486fe45 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 18:04:28 +0100 Subject: libpurple-dev build dependency. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index 86488c8a..8f861aeb 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,7 @@ Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.8.0 -Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf +Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ DM-Upload-Allowed: yes -- cgit v1.2.3 From 0f64ca78c8ec9ee9be2ffa8ec203e138f7a00804 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 13:56:20 +0100 Subject: Make http_encode() RFC3986-compliant. (Escape everything except alphanumeric characters plus [-_~.].) --- lib/misc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/misc.c b/lib/misc.c index 1d36d639..c56b31f3 100644 --- a/lib/misc.c +++ b/lib/misc.c @@ -305,8 +305,7 @@ void http_encode( char *s ) for( i = j = 0; t[i]; i ++, j ++ ) { - /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */ - if( !isalnum( t[i] ) ) + if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) ) { sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); j += 2; -- cgit v1.2.3 From be28fe7da3e197d91ec00a6c1017c053041f5a85 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 16:12:13 +0100 Subject: Code to calculate OAuth signatures. I hope that after wrapping my mind around all of this the rest is going to be easier.. --- lib/Makefile | 2 +- lib/oauth.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/oauth.h | 22 +++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 lib/oauth.c create mode 100644 lib/oauth.h diff --git a/lib/Makefile b/lib/Makefile index 03fef1ab..441634cd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ -include ../Makefile.settings # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o CFLAGS += -Wall LFLAGS += -r diff --git a/lib/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..ef303f14 --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,106 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +\***************************************************************************/ + +#include +#include +#include +#include +#include "base64.h" +#include "misc.h" +#include "sha1.h" + +#define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" +#define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" +/* How can it be a secret if it's right here in the source code? No clue... */ + +#define HMAC_BLOCK_SIZE 64 + +struct oauth_state +{ +}; + +static char *oauth_sign( const char *method, const char *url, + const char *params, const char *token_secret ) +{ + sha1_state_t sha1; + uint8_t hash[sha1_hash_size]; + uint8_t key[HMAC_BLOCK_SIZE+1]; + char *s; + int i; + + /* Create K. If our current key is >64 chars we have to hash it, + otherwise just pad. */ + memset( key, 0, HMAC_BLOCK_SIZE ); + i = strlen( CONSUMER_SECRET ) + 1 + token_secret ? strlen( token_secret ) : 0; + if( i > HMAC_BLOCK_SIZE ) + { + sha1_init( &sha1 ); + sha1_append( &sha1, CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); + sha1_append( &sha1, "&", 1 ); + if( token_secret ) + sha1_append( &sha1, token_secret, strlen( token_secret ) ); + sha1_finish( &sha1, key ); + } + else + { + g_snprintf( key, HMAC_BLOCK_SIZE + 1, "%s&%s", + CONSUMER_SECRET, token_secret ? : "" ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + + /* OAuth: text = method&url¶ms, all http_encoded. */ + sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + + s = g_new0( char, strlen( url ) * 3 + 1 ); + strcpy( s, url ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + g_free( s ); + + s = g_new0( char, strlen( params ) * 3 + 1 ); + strcpy( s, params ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + g_free( s ); + + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, hash ); + + /* base64_encode it and we're done. */ + return base64_encode( hash, sha1_hash_size ); +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..160e30f1 --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,22 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ -- cgit v1.2.3 From e9eaee6eb50cd566d699b61a4d643f64800b488b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 19:56:53 +0100 Subject: constified. --- lib/url.c | 2 +- lib/url.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/url.c b/lib/url.c index de9966b4..9e330f8c 100644 --- a/lib/url.c +++ b/lib/url.c @@ -26,7 +26,7 @@ #include "url.h" /* Convert an URL to a url_t structure */ -int url_set( url_t *url, char *set_url ) +int url_set( url_t *url, const char *set_url ) { char s[MAX_STRING+1]; char *i; diff --git a/lib/url.h b/lib/url.h index 8c038c91..55107ad2 100644 --- a/lib/url.h +++ b/lib/url.h @@ -41,4 +41,4 @@ typedef struct url char pass[MAX_STRING+1]; } url_t; -int url_set( url_t *url, char *set_url ); +int url_set( url_t *url, const char *set_url ); -- cgit v1.2.3 From da2efd4eaaf6a87afc35402d96d98d8001e43af7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 19:57:06 +0100 Subject: Some HTTP stuff. Via gdb I can make this request a token. --- lib/oauth.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 6 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index ef303f14..6240d514 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -25,9 +25,11 @@ #include #include #include +#include "http_client.h" #include "base64.h" #include "misc.h" #include "sha1.h" +#include "url.h" #define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" #define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" @@ -35,8 +37,18 @@ #define HMAC_BLOCK_SIZE 64 -struct oauth_state +struct oauth_info; +typedef void (*oauth_cb)( struct oauth_info * ); + +struct oauth_info { + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_params; + char *token; }; static char *oauth_sign( const char *method, const char *url, @@ -51,19 +63,19 @@ static char *oauth_sign( const char *method, const char *url, /* Create K. If our current key is >64 chars we have to hash it, otherwise just pad. */ memset( key, 0, HMAC_BLOCK_SIZE ); - i = strlen( CONSUMER_SECRET ) + 1 + token_secret ? strlen( token_secret ) : 0; + i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 ); if( i > HMAC_BLOCK_SIZE ) { sha1_init( &sha1 ); - sha1_append( &sha1, CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); - sha1_append( &sha1, "&", 1 ); + sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); + sha1_append( &sha1, (uint8_t*) "&", 1 ); if( token_secret ) - sha1_append( &sha1, token_secret, strlen( token_secret ) ); + sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) ); sha1_finish( &sha1, key ); } else { - g_snprintf( key, HMAC_BLOCK_SIZE + 1, "%s&%s", + g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", CONSUMER_SECRET, token_secret ? : "" ); } @@ -104,3 +116,197 @@ static char *oauth_sign( const char *method, const char *url, /* base64_encode it and we're done. */ return base64_encode( hash, sha1_hash_size ); } + +static char *oauth_nonce() +{ + unsigned char bytes[9]; + + random_bytes( bytes, sizeof( bytes ) ); + return base64_encode( bytes, sizeof( bytes ) ); +} + +void oauth_params_add( GSList **params, const char *key, const char *value ) +{ + char *item; + + item = g_strdup_printf( "%s=%s", key, value ); + *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); +} + +void oauth_params_del( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l, *n; + + for( l = *params; l; l = n ) + { + n = l->next; + + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + { + g_free( l->data ); + *params = g_slist_remove( *params, l ); + } + } +} + +void oauth_params_set( GSList **params, const char *key, const char *value ) +{ + oauth_params_del( params, key ); + oauth_params_add( params, key, value ); +} + +const char *oauth_params_get( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l; + + for( l = *params; l; l = l->next ) + { + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + return (const char*) l->data + key_len + 1; + } + + return NULL; +} + +GSList *oauth_params_parse( char *in ) +{ + GSList *ret = NULL; + char *amp, *eq; + + while( in && *in ) + { + eq = strchr( in, '=' ); + if( !eq ) + break; + + *eq = '\0'; + if( ( amp = strchr( eq + 1, '&' ) ) ) + *amp = '\0'; + + oauth_params_add( &ret, in, eq + 1 ); + + *eq = '='; + if( amp == NULL ) + break; + + *amp = '&'; + in = amp + 1; + } + + return ret; +} + +void oauth_params_free( GSList **params ) +{ + while( params && *params ) + { + g_free( (*params)->data ); + *params = g_slist_remove( *params, (*params)->data ); + } +} + +char *oauth_params_string( GSList *params ) +{ + GSList *l; + GString *str = g_string_new( "" ); + + for( l = params; l; l = l->next ) + { + g_string_append( str, l->data ); + if( l->next ) + g_string_append_c( str, '&' ); + } + + return g_string_free( str, FALSE ); +} + +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data ) +{ + GSList *params = NULL; + char *s, *params_s, *post; + void *req; + url_t url_p; + + if( !url_set( &url_p, url ) ) + { + oauth_params_free( params_ ); + return NULL; + } + + if( params_ ) + params = *params_; + + oauth_params_set( ¶ms, "oauth_consumer_key", CONSUMER_KEY ); + oauth_params_set( ¶ms, "oauth_signature_method", "HMAC-SHA1" ); + + s = g_strdup_printf( "%d", (int) time( NULL ) ); + oauth_params_set( ¶ms, "oauth_timestamp", s ); + g_free( s ); + + s = oauth_nonce(); + oauth_params_set( ¶ms, "oauth_nonce", s ); + g_free( s ); + + oauth_params_set( ¶ms, "oauth_version", "1.0" ); + oauth_params_set( ¶ms, "oauth_callback", "oob" ); + + params_s = oauth_params_string( params ); + oauth_params_free( params_ ); + + s = oauth_sign( "POST", url, params_s, NULL ); + s = g_realloc( s, strlen( s ) * 3 + 1 ); + http_encode( s ); + + post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); + g_free( params_s ); + g_free( s ); + + s = g_strdup_printf( "POST %s HTTP/1.0\r\n" + "Host: %s\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: %zd\r\n" + "\r\n" + "%s", url_p.file, url_p.host, strlen( post ), post ); + g_free( post ); + + req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, + s, func, data ); + g_free( s ); + + return req; +} + +void oauth_request_token_done( struct http_request *req ); + +void *oauth_request_token( const char *url, oauth_cb func, void *data ) +{ + struct oauth_info *st = g_new0( struct oauth_info, 1 ); + + st->func = func; + st->data = data; + + return oauth_post_request( url, NULL, oauth_request_token_done, st ); +} + +void oauth_request_token_done( struct http_request *req ) +{ + struct oauth_info *st = req->data; + + st->http = req; + + if( req->status_code == 200 ) + { + GSList *params; + + st->auth_params = g_strdup( req->reply_body ); + params = oauth_params_parse( st->auth_params ); + st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oauth_params_free( ¶ms ); + } + + st->func( st ); +} -- cgit v1.2.3 From 346dfd940c1439f6113c3cf80340791567de5d25 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 20:22:02 +0100 Subject: oauth_access_token() added. I managed to increase the counter on http://twitter.com/oauth_clients/details/127170 . \o/ --- lib/oauth.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 6240d514..bc3974c1 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -252,7 +252,6 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f g_free( s ); oauth_params_set( ¶ms, "oauth_version", "1.0" ); - oauth_params_set( ¶ms, "oauth_callback", "oob" ); params_s = oauth_params_string( params ); oauth_params_free( params_ ); @@ -280,19 +279,22 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f return req; } -void oauth_request_token_done( struct http_request *req ); +static void oauth_request_token_done( struct http_request *req ); void *oauth_request_token( const char *url, oauth_cb func, void *data ) { struct oauth_info *st = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; st->func = func; st->data = data; + oauth_params_add( ¶ms, "oauth_callback", "oob" ); + return oauth_post_request( url, NULL, oauth_request_token_done, st ); } -void oauth_request_token_done( struct http_request *req ) +static void oauth_request_token_done( struct http_request *req ) { struct oauth_info *st = req->data; @@ -308,5 +310,21 @@ void oauth_request_token_done( struct http_request *req ) oauth_params_free( ¶ms ); } - st->func( st ); + //st->func( st ); +} + +static void oauth_access_token_done( struct http_request *req ); + +void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st ) +{ + GSList *params = NULL; + + oauth_params_add( ¶ms, "oauth_token", st->token ); + oauth_params_add( ¶ms, "oauth_verifier", pin ); + + return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); +} + +static void oauth_access_token_done( struct http_request *req ) +{ } -- cgit v1.2.3 From b2bc25c4483f4c2b419d91d479f38c3f07ef4226 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 00:21:00 +0100 Subject: Added a function that generates an OAuth Authorization: HTTP header. --- lib/oauth.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index bc3974c1..c166d96a 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -49,6 +49,8 @@ struct oauth_info char *auth_params; char *token; + + char *access_token; }; static char *oauth_sign( const char *method, const char *url, @@ -146,7 +148,7 @@ void oauth_params_del( GSList **params, const char *key ) ((char*)l->data)[key_len] == '=' ) { g_free( l->data ); - *params = g_slist_remove( *params, l ); + *params = g_slist_remove( *params, l->data ); } } } @@ -224,6 +226,24 @@ char *oauth_params_string( GSList *params ) return g_string_free( str, FALSE ); } +static void oauth_add_default_params( GSList **params ) +{ + char *s; + + oauth_params_set( params, "oauth_consumer_key", CONSUMER_KEY ); + oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); + + s = g_strdup_printf( "%d", (int) time( NULL ) ); + oauth_params_set( params, "oauth_timestamp", s ); + g_free( s ); + + s = oauth_nonce(); + oauth_params_set( params, "oauth_nonce", s ); + g_free( s ); + + oauth_params_set( params, "oauth_version", "1.0" ); +} + static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data ) { GSList *params = NULL; @@ -240,18 +260,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f if( params_ ) params = *params_; - oauth_params_set( ¶ms, "oauth_consumer_key", CONSUMER_KEY ); - oauth_params_set( ¶ms, "oauth_signature_method", "HMAC-SHA1" ); - - s = g_strdup_printf( "%d", (int) time( NULL ) ); - oauth_params_set( ¶ms, "oauth_timestamp", s ); - g_free( s ); - - s = oauth_nonce(); - oauth_params_set( ¶ms, "oauth_nonce", s ); - g_free( s ); - - oauth_params_set( ¶ms, "oauth_version", "1.0" ); + oauth_add_default_params( ¶ms ); params_s = oauth_params_string( params ); oauth_params_free( params_ ); @@ -327,4 +336,61 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s static void oauth_access_token_done( struct http_request *req ) { + struct oauth_info *st = req->data; + + if( req->status_code == 200 ) + st->access_token = g_strdup( req->reply_body ); + + //st->func( st ); +} + +char *oauth_http_header( char *access_token, const char *method, const char *url ) +{ + GSList *params, *l; + char *token_secret, *sig, *params_s; + GString *ret = NULL; + + params = oauth_params_parse( access_token ); + if( params == NULL ) + goto err; + token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); + if( token_secret == NULL ) + goto err; + oauth_params_del( ¶ms, "oauth_token_secret" ); + + oauth_add_default_params( ¶ms ); + + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, token_secret ); + g_free( params_s ); + sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); + http_encode( sig ); + + ret = g_string_new( "OAuth " ); + for( l = params; l; l = l->next ) + { + char *kv = l->data; + char *eq = strchr( kv, '=' ); + char esc[strlen(kv)*3+1]; + + if( eq == NULL ) + break; /* WTF */ + + strcpy( esc, eq + 1 ); + http_encode( esc ); + + g_string_append_len( ret, kv, eq - kv + 1 ); + g_string_append_c( ret, '"' ); + g_string_append( ret, esc ); + g_string_append( ret, "\", " ); + } + + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + +err: + oauth_params_free( ¶ms ); + g_free( sig ); + g_free( token_secret ); + + return ret ? g_string_free( ret, FALSE ) : NULL; } -- cgit v1.2.3 From 508c340d1d12d3ca932001d7ee1dea1a4c81bf02 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 01:42:37 +0100 Subject: Successfully posted a tweet! Twitter's tricky. It returns vars (user_id, screen_name) in the access token that, the way I read the spec, should be included in all subsequent queries. However, stuff only started to work when I dropped those vars. This code's definitely not pretty ATM. Need to clean up now that it actually works. --- lib/oauth.c | 40 +++++++++++++++++++++++----------------- lib/oauth.h | 2 ++ protocols/twitter/twitter.c | 7 ++++++- protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_http.c | 18 ++++++++++++++++-- protocols/twitter/twitter_http.h | 2 +- protocols/twitter/twitter_lib.c | 10 +++++----- 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index c166d96a..6731fe7e 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -174,9 +174,8 @@ const char *oauth_params_get( GSList **params, const char *key ) return NULL; } -GSList *oauth_params_parse( char *in ) +static void oauth_params_parse( GSList **params, char *in ) { - GSList *ret = NULL; char *amp, *eq; while( in && *in ) @@ -189,7 +188,7 @@ GSList *oauth_params_parse( char *in ) if( ( amp = strchr( eq + 1, '&' ) ) ) *amp = '\0'; - oauth_params_add( &ret, in, eq + 1 ); + oauth_params_add( params, in, eq + 1 ); *eq = '='; if( amp == NULL ) @@ -198,8 +197,6 @@ GSList *oauth_params_parse( char *in ) *amp = '&'; in = amp + 1; } - - return ret; } void oauth_params_free( GSList **params ) @@ -311,10 +308,10 @@ static void oauth_request_token_done( struct http_request *req ) if( req->status_code == 200 ) { - GSList *params; + GSList *params = NULL; st->auth_params = g_strdup( req->reply_body ); - params = oauth_params_parse( st->auth_params ); + oauth_params_parse( ¶ms, st->auth_params ); st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); oauth_params_free( ¶ms ); } @@ -344,13 +341,13 @@ static void oauth_access_token_done( struct http_request *req ) //st->func( st ); } -char *oauth_http_header( char *access_token, const char *method, const char *url ) +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) { - GSList *params, *l; - char *token_secret, *sig, *params_s; + GSList *params = NULL, *l; + char *token_secret, *sig, *params_s, *s; GString *ret = NULL; - params = oauth_params_parse( access_token ); + oauth_params_parse( ¶ms, access_token ); if( params == NULL ) goto err; token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); @@ -360,12 +357,6 @@ char *oauth_http_header( char *access_token, const char *method, const char *url oauth_add_default_params( ¶ms ); - params_s = oauth_params_string( params ); - sig = oauth_sign( method, url, params_s, token_secret ); - g_free( params_s ); - sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); - http_encode( sig ); - ret = g_string_new( "OAuth " ); for( l = params; l; l = l->next ) { @@ -385,6 +376,21 @@ char *oauth_http_header( char *access_token, const char *method, const char *url g_string_append( ret, "\", " ); } + if( args ) + oauth_params_parse( ¶ms, args ); + if( ( s = strchr( url, '?' ) ) ) + { + s = g_strdup( s + 1 ); + oauth_params_parse( ¶ms, s + 1 ); + g_free( s ); + } + + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, token_secret ); + g_free( params_s ); + sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); + http_encode( sig ); + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); err: diff --git a/lib/oauth.h b/lib/oauth.h index 160e30f1..98e58c64 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -20,3 +20,5 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * \***************************************************************************/ + +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 726c7cb1..ead03c56 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -81,7 +81,10 @@ static void twitter_login( account_t *acc ) twitter_connections = g_slist_append( twitter_connections, ic ); td->user = acc->user; - td->pass = acc->pass; + if( strstr( acc->pass, "oauth_token=" ) == NULL ) + td->pass = g_strdup( acc->pass ); + else + td->oauth = g_strdup( acc->pass ); td->home_timeline_id = 0; ic->proto_data = td; @@ -118,6 +121,8 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { + g_free( td->pass ); + g_free( td->oauth ); g_free( td ); } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 88caa104..16620e1d 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,6 +36,7 @@ struct twitter_data { char* user; char* pass; + char* oauth; guint64 home_timeline_id; gint main_loop_id; struct groupchat *home_timeline_gc; diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 3632140f..93e315fa 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -34,6 +34,7 @@ #include "url.h" #include "misc.h" #include "base64.h" +#include "oauth.h" #include #include @@ -44,7 +45,7 @@ char *twitter_url_append(char *url, char *key, char* value); * Do a request. * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c */ -void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char** arguments, int arguments_len) +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char* oauth_token, char** arguments, int arguments_len) { url_t *url = g_new0( url_t, 1 ); char *tmp; @@ -109,7 +110,20 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in is_post ? "POST" : "GET", url->file, url->host ); // If a pass and user are given we append them to the request. - if (userpass_base64) + if (oauth_token) + { + char *full_header; + + full_header = oauth_http_header(oauth_token, + is_post ? "POST" : "GET", + url_string, url_arguments); + + tmp = g_strdup_printf("%sAuthorization: %s\r\n", request, full_header); + g_free(request); + g_free(full_header); + request = tmp; + } + else if (userpass_base64) { tmp = g_strdup_printf("%sAuthorization: Basic %s\r\n", request, userpass_base64); g_free(request); diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h index ec4a0b7c..1e29fb99 100644 --- a/protocols/twitter/twitter_http.h +++ b/protocols/twitter/twitter_http.h @@ -28,7 +28,7 @@ #include "http_client.h" void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, - char* user, char* pass, char** arguments, int arguments_len); + char* user, char* pass, char *oauth_token, char** arguments, int arguments_len); #endif //_TWITTER_HTTP_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d58afd73..c91f8bbf 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -129,7 +129,7 @@ void twitter_get_friends_ids(struct im_connection *ic, int next_cursor) char* args[2]; args[0] = "cursor"; args[1] = g_strdup_printf ("%d", next_cursor); - twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2); + twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth, args, 2); g_free(args[1]); } @@ -395,7 +395,7 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor) args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id); } - twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2); + twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, td->oauth, args, td->home_timeline_id ? 4 : 2); g_free(args[1]); if (td->home_timeline_id) { @@ -619,7 +619,7 @@ void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor) args[0] = "cursor"; args[1] = g_strdup_printf ("%d", next_cursor); - twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2); + twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth, args, 2); g_free(args[1]); } @@ -653,7 +653,7 @@ void twitter_post_status(struct im_connection *ic, char* msg) char* args[2]; args[0] = "status"; args[1] = msg; - twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2); + twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 2); // g_free(args[1]); } @@ -671,7 +671,7 @@ void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) args[2] = "text"; args[3] = msg; // Use the same callback as for twitter_post_status, since it does basically the same. - twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4); + twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 4); // g_free(args[1]); // g_free(args[3]); } -- cgit v1.2.3 From acba168df90f28fdaee26bc09d621364878dd099 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 22:20:09 +0100 Subject: Moving two public OAuth functions into the header file. --- lib/oauth.c | 17 +---------------- lib/oauth.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 6731fe7e..7897b760 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -30,6 +30,7 @@ #include "misc.h" #include "sha1.h" #include "url.h" +#include "oauth.h" #define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" #define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" @@ -37,22 +38,6 @@ #define HMAC_BLOCK_SIZE 64 -struct oauth_info; -typedef void (*oauth_cb)( struct oauth_info * ); - -struct oauth_info -{ - oauth_cb func; - void *data; - - struct http_request *http; - - char *auth_params; - char *token; - - char *access_token; -}; - static char *oauth_sign( const char *method, const char *url, const char *params, const char *token_secret ) { diff --git a/lib/oauth.h b/lib/oauth.h index 98e58c64..91696d83 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -21,4 +21,39 @@ * * \***************************************************************************/ +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; +typedef void (*oauth_cb)( struct oauth_info * ); + +struct oauth_info +{ + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_params; + char *token; + + char *access_token; +}; + +/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1) + Request an initial anonymous token which can be used to construct an + authorization URL for the user. This is passed to the callback function + in a struct oauth_info. */ +void *oauth_request_token( const char *url, oauth_cb func, void *data ); + +/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) + The user gets a PIN or so which we now exchange for the final access + token. This is passed to the callback function in the same + struct oauth_info. */ +void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st ); + +/* http://oauth.net/core/1.0a/#anchor12 (section 7) + Generate an OAuth Authorization: HTTP header. access_token should be + saved/fetched using the functions above. args can be a string with + whatever's going to be in the POST body of the request. GET args will + automatically be grabbed from url. */ char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); -- cgit v1.2.3 From 713d6111cd754ff9eae4cfa1e6de82c9824d16db Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 22:50:48 +0100 Subject: Twitter module now generates authorize URLs. --- lib/oauth.c | 8 +++--- lib/oauth.h | 2 +- protocols/twitter/twitter.c | 64 +++++++++++++++++++++++++++++++++++---------- protocols/twitter/twitter.h | 4 +++ 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 7897b760..828d713d 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -297,11 +297,11 @@ static void oauth_request_token_done( struct http_request *req ) st->auth_params = g_strdup( req->reply_body ); oauth_params_parse( ¶ms, st->auth_params ); - st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); oauth_params_free( ¶ms ); } - //st->func( st ); + st->func( st ); } static void oauth_access_token_done( struct http_request *req ); @@ -310,7 +310,7 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s { GSList *params = NULL; - oauth_params_add( ¶ms, "oauth_token", st->token ); + oauth_params_add( ¶ms, "oauth_token", st->request_token ); oauth_params_add( ¶ms, "oauth_verifier", pin ); return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); @@ -323,7 +323,7 @@ static void oauth_access_token_done( struct http_request *req ) if( req->status_code == 200 ) st->access_token = g_strdup( req->reply_body ); - //st->func( st ); + st->func( st ); } char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) diff --git a/lib/oauth.h b/lib/oauth.h index 91696d83..4151d73f 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -34,7 +34,7 @@ struct oauth_info struct http_request *http; char *auth_params; - char *token; + char *request_token; char *access_token; }; diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index ead03c56..e2a5986b 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -22,6 +22,7 @@ ****************************************************************************/ #include "nogaim.h" +#include "oauth.h" #include "twitter.h" #include "twitter_http.h" #include "twitter_lib.h" @@ -50,6 +51,44 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond) return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN; } +static void twitter_main_loop_start( struct im_connection *ic ) +{ + struct twitter_data *td = ic->proto_data; + + imcb_log( ic, "Connecting to Twitter" ); + + // Run this once. After this queue the main loop function. + twitter_main_loop(ic, -1, 0); + + // Queue the main_loop + // Save the return value, so we can remove the timeout on logout. + td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); +} + +static void twitter_oauth_callback( struct oauth_info *info ); + +static void twitter_oauth_start( struct im_connection *ic ) +{ + oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); +} + +static void twitter_oauth_callback( struct oauth_info *info ) +{ + struct im_connection *ic = info->data; + + if( info->request_token && info->access_token == NULL ) + { + char name[strlen(ic->acc->user)+9], *msg; + + sprintf( name, "twitter_%s", ic->acc->user ); + msg = g_strdup_printf( "To finish OAuth authentication, please visit " + "%s?%s and respond with the resulting PIN code.", + TWITTER_OAUTH_AUTHORIZE, info->auth_params ); + imcb_buddy_msg( ic, name, msg, 0, 0 ); + g_free( msg ); + } +} + static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || @@ -66,6 +105,8 @@ static void twitter_init( account_t *acc ) s = set_add( &acc->set, "mode", "one", set_eval_mode, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; + + s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc ); } /** @@ -79,28 +120,23 @@ static void twitter_login( account_t *acc ) char name[strlen(acc->user)+9]; twitter_connections = g_slist_append( twitter_connections, ic ); - + ic->proto_data = td; + td->user = acc->user; - if( strstr( acc->pass, "oauth_token=" ) == NULL ) + if( !set_getbool( &acc->set, "oauth" ) ) td->pass = g_strdup( acc->pass ); - else + else if( strstr( acc->pass, "oauth_token=" ) ) td->oauth = g_strdup( acc->pass ); td->home_timeline_id = 0; - - ic->proto_data = td; - - imcb_log( ic, "Connecting to Twitter" ); - - // Run this once. After this queue the main loop function. - twitter_main_loop(ic, -1, 0); - - // Queue the main_loop - // Save the return value, so we can remove the timeout on logout. - td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); sprintf( name, "twitter_%s", acc->user ); imcb_add_buddy( ic, name, NULL ); imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); + + if( td->pass || td->oauth ) + twitter_main_loop_start( ic ); + else + twitter_oauth_start( ic ); } /** diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 16620e1d..f1937bd0 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -50,4 +50,8 @@ struct twitter_data */ GSList *twitter_connections; +#define TWITTER_OAUTH_REQUEST_TOKEN "http://api.twitter.com/oauth/request_token" +#define TWITTER_OAUTH_ACCESS_TOKEN "http://api.twitter.com/oauth/access_token" +#define TWITTER_OAUTH_AUTHORIZE "http://api.twitter.com/oauth/authorize" + #endif //_TWITTER_H -- cgit v1.2.3 From c42e8b907817fc76df4dc3b48d85858555102654 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:40:11 +0100 Subject: OAuth, it lives! --- lib/oauth.c | 14 +++++++++++++- lib/oauth.h | 9 +++++++++ protocols/twitter/twitter.c | 39 ++++++++++++++++++++++++++++++++++++--- protocols/twitter/twitter.h | 1 + 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 828d713d..7f60f4f5 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -301,6 +301,7 @@ static void oauth_request_token_done( struct http_request *req ) oauth_params_free( ¶ms ); } + st->stage = OAUTH_REQUEST_TOKEN; st->func( st ); } @@ -321,8 +322,19 @@ static void oauth_access_token_done( struct http_request *req ) struct oauth_info *st = req->data; if( req->status_code == 200 ) - st->access_token = g_strdup( req->reply_body ); + { + GSList *params; + const char *token, *token_secret; + + oauth_params_parse( ¶ms, req->reply_body ); + token = oauth_params_get( ¶ms, "oauth_token" ); + token_secret = oauth_params_get( ¶ms, "oauth_token_secret" ); + st->access_token = g_strdup_printf( + "oauth_token=%s&oauth_token_secret=%s", token, token_secret ); + oauth_params_free( ¶ms ); + } + st->stage = OAUTH_ACCESS_TOKEN; st->func( st ); } diff --git a/lib/oauth.h b/lib/oauth.h index 4151d73f..30486b98 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -26,8 +26,17 @@ struct oauth_info; typedef void (*oauth_cb)( struct oauth_info * ); +typedef enum +{ + OAUTH_INIT, + OAUTH_REQUEST_TOKEN, + OAUTH_ACCESS_TOKEN, +} oauth_stage_t; + struct oauth_info { + oauth_stage_t stage; + oauth_cb func; void *data; diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index e2a5986b..eb05d9d5 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -69,17 +69,29 @@ static void twitter_oauth_callback( struct oauth_info *info ); static void twitter_oauth_start( struct im_connection *ic ) { + imcb_log( ic, "Requesting OAuth request token" ); + oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); } static void twitter_oauth_callback( struct oauth_info *info ) { struct im_connection *ic = info->data; + struct twitter_data *td = ic->proto_data; - if( info->request_token && info->access_token == NULL ) + if( info->stage == OAUTH_REQUEST_TOKEN ) { char name[strlen(ic->acc->user)+9], *msg; + if( info->request_token == NULL ) + { + imcb_error( ic, "OAuth error: %s", info->http->status_string ); + imc_logout( ic, TRUE ); + return; + } + + td->oauth_info = info; + sprintf( name, "twitter_%s", ic->acc->user ); msg = g_strdup_printf( "To finish OAuth authentication, please visit " "%s?%s and respond with the resulting PIN code.", @@ -87,6 +99,19 @@ static void twitter_oauth_callback( struct oauth_info *info ) imcb_buddy_msg( ic, name, msg, 0, 0 ); g_free( msg ); } + else if( info->stage == OAUTH_ACCESS_TOKEN ) + { + if( info->access_token == NULL ) + { + imcb_error( ic, "OAuth error: %s", info->http->status_string ); + imc_logout( ic, TRUE ); + return; + } + + td->oauth = g_strdup( info->access_token ); + + twitter_main_loop_start( ic ); + } } static char *set_eval_mode( set_t *set, char *value ) @@ -170,12 +195,20 @@ static void twitter_logout( struct im_connection *ic ) */ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { + struct twitter_data *td = ic->proto_data; + if (g_strncasecmp(who, "twitter_", 8) == 0 && g_strcasecmp(who + 8, ic->acc->user) == 0) - twitter_post_status(ic, message); + { + if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth == NULL ) + oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); + else + twitter_post_status(ic, message); + } else + { twitter_direct_messages_new(ic, who, message); - + } return( 0 ); } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index f1937bd0..9c046b66 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -37,6 +37,7 @@ struct twitter_data char* user; char* pass; char* oauth; + struct oauth_info *oauth_info; guint64 home_timeline_id; gint main_loop_id; struct groupchat *home_timeline_gc; -- cgit v1.2.3 From 78a2f1e4ac9aeb2c041059eb7a9578d34bd60d50 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:47:35 +0100 Subject: Escaping in oauth_nonce(). Not sure if the escaping is entirely right ATM... :-( --- lib/oauth.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 7f60f4f5..9a372082 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -107,9 +107,14 @@ static char *oauth_sign( const char *method, const char *url, static char *oauth_nonce() { unsigned char bytes[9]; + char *ret; random_bytes( bytes, sizeof( bytes ) ); - return base64_encode( bytes, sizeof( bytes ) ); + ret = base64_encode( bytes, sizeof( bytes ) ); + ret = g_realloc( ret, strlen( ret ) * 3 + 1 ); + http_encode( ret ); + + return ret; } void oauth_params_add( GSList **params, const char *key, const char *value ) -- cgit v1.2.3 From 288b215ca64d09ea6a49cf9ff1fcc7682b7607ec Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:50:25 +0100 Subject: Save the OAuth token in the acct structs so it doesn't have to be rerequested every time. --- protocols/twitter/twitter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index eb05d9d5..54bee2f1 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -110,6 +110,11 @@ static void twitter_oauth_callback( struct oauth_info *info ) td->oauth = g_strdup( info->access_token ); + /* IM mods didn't do this so far and it's ugly but I should + be able to get away with it... */ + g_free( ic->acc->pass ); + ic->acc->pass = g_strdup( info->access_token ); + twitter_main_loop_start( ic ); } } -- cgit v1.2.3 From ee84bdbc2510fa09bd0f67e14d06e69c2626d0f1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:11:11 +0100 Subject: The escaping, I fixed it for you. More expensive code this way and most of the vars don't need escaping. But this shouldn't be so fragile anymore. --- lib/oauth.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 9a372082..0ab94c96 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -100,21 +100,21 @@ static char *oauth_sign( const char *method, const char *url, sha1_append( &sha1, hash, sha1_hash_size ); sha1_finish( &sha1, hash ); - /* base64_encode it and we're done. */ - return base64_encode( hash, sha1_hash_size ); + /* base64_encode + HTTP escape it (both consumers + need it that away) and we're done. */ + s = base64_encode( hash, sha1_hash_size ); + s = g_realloc( s, strlen( s ) * 3 + 1 ); + http_encode( s ); + + return s; } static char *oauth_nonce() { unsigned char bytes[9]; - char *ret; random_bytes( bytes, sizeof( bytes ) ); - ret = base64_encode( bytes, sizeof( bytes ) ); - ret = g_realloc( ret, strlen( ret ) * 3 + 1 ); - http_encode( ret ); - - return ret; + return base64_encode( bytes, sizeof( bytes ) ); } void oauth_params_add( GSList **params, const char *key, const char *value ) @@ -166,7 +166,7 @@ const char *oauth_params_get( GSList **params, const char *key ) static void oauth_params_parse( GSList **params, char *in ) { - char *amp, *eq; + char *amp, *eq, *s; while( in && *in ) { @@ -178,7 +178,10 @@ static void oauth_params_parse( GSList **params, char *in ) if( ( amp = strchr( eq + 1, '&' ) ) ) *amp = '\0'; - oauth_params_add( params, in, eq + 1 ); + s = g_strdup( eq + 1 ); + http_decode( s ); + oauth_params_add( params, in, s ); + g_free( s ); *eq = '='; if( amp == NULL ) @@ -205,7 +208,15 @@ char *oauth_params_string( GSList *params ) for( l = params; l; l = l->next ) { - g_string_append( str, l->data ); + char *s, *eq; + + s = g_malloc( strlen( l->data ) * 3 + 1 ); + strcpy( s, l->data ); + if( ( eq = strchr( s, '=' ) ) ) + http_encode( eq + 1 ); + g_string_append( str, s ); + g_free( s ); + if( l->next ) g_string_append_c( str, '&' ); } @@ -253,9 +264,6 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f oauth_params_free( params_ ); s = oauth_sign( "POST", url, params_s, NULL ); - s = g_realloc( s, strlen( s ) * 3 + 1 ); - http_encode( s ); - post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); g_free( params_s ); g_free( s ); @@ -389,11 +397,8 @@ char *oauth_http_header( char *access_token, const char *method, const char *url params_s = oauth_params_string( params ); sig = oauth_sign( method, url, params_s, token_secret ); - g_free( params_s ); - sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); - http_encode( sig ); - g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + g_free( params_s ); err: oauth_params_free( ¶ms ); -- cgit v1.2.3 From 18dbb20242719f7537ad1a18a9a3befa2eefd502 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:42:07 +0100 Subject: Valgrind cleanup. --- lib/oauth.c | 40 ++++++++++++++++++++++++++++++++++------ lib/oauth.h | 12 +++++++++--- protocols/twitter/twitter.c | 30 +++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 0ab94c96..97017043 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -224,6 +224,17 @@ char *oauth_params_string( GSList *params ) return g_string_free( str, FALSE ); } +void oauth_info_free( struct oauth_info *info ) +{ + if( info ) + { + g_free( info->auth_params ); + g_free( info->request_token ); + g_free( info->access_token ); + g_free( info ); + } +} + static void oauth_add_default_params( GSList **params ) { char *s; @@ -285,7 +296,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f static void oauth_request_token_done( struct http_request *req ); -void *oauth_request_token( const char *url, oauth_cb func, void *data ) +struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ) { struct oauth_info *st = g_new0( struct oauth_info, 1 ); GSList *params = NULL; @@ -295,7 +306,13 @@ void *oauth_request_token( const char *url, oauth_cb func, void *data ) oauth_params_add( ¶ms, "oauth_callback", "oob" ); - return oauth_post_request( url, NULL, oauth_request_token_done, st ); + if( !oauth_post_request( url, ¶ms, oauth_request_token_done, st ) ) + { + oauth_info_free( st ); + return NULL; + } + + return st; } static void oauth_request_token_done( struct http_request *req ) @@ -315,19 +332,21 @@ static void oauth_request_token_done( struct http_request *req ) } st->stage = OAUTH_REQUEST_TOKEN; - st->func( st ); + if( !st->func( st ) ) + oauth_info_free( st ); } static void oauth_access_token_done( struct http_request *req ); -void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st ) +void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ) { GSList *params = NULL; oauth_params_add( ¶ms, "oauth_token", st->request_token ); oauth_params_add( ¶ms, "oauth_verifier", pin ); - return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); + if( !oauth_post_request( url, ¶ms, oauth_access_token_done, st ) ) + oauth_info_free( st ); } static void oauth_access_token_done( struct http_request *req ) @@ -336,7 +355,7 @@ static void oauth_access_token_done( struct http_request *req ) if( req->status_code == 200 ) { - GSList *params; + GSList *params = NULL; const char *token, *token_secret; oauth_params_parse( ¶ms, req->reply_body ); @@ -349,6 +368,7 @@ static void oauth_access_token_done( struct http_request *req ) st->stage = OAUTH_ACCESS_TOKEN; st->func( st ); + oauth_info_free( st ); } char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) @@ -357,9 +377,12 @@ char *oauth_http_header( char *access_token, const char *method, const char *url char *token_secret, *sig, *params_s, *s; GString *ret = NULL; + /* First, get the two pieces of info from the access token that we need. */ oauth_params_parse( ¶ms, access_token ); if( params == NULL ) goto err; + + /* Pick out the token secret, we shouldn't include it but use it for signing. */ token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); if( token_secret == NULL ) goto err; @@ -367,6 +390,7 @@ char *oauth_http_header( char *access_token, const char *method, const char *url oauth_add_default_params( ¶ms ); + /* Start building the OAuth header. 'key="value", '... */ ret = g_string_new( "OAuth " ); for( l = params; l; l = l->next ) { @@ -386,6 +410,9 @@ char *oauth_http_header( char *access_token, const char *method, const char *url g_string_append( ret, "\", " ); } + /* Now, before generating the signature, add GET/POST arguments to params + since they should be included in the base signature string (but not in + the HTTP header). */ if( args ) oauth_params_parse( ¶ms, args ); if( ( s = strchr( url, '?' ) ) ) @@ -395,6 +422,7 @@ char *oauth_http_header( char *access_token, const char *method, const char *url g_free( s ); } + /* Append the signature and we're done! */ params_s = oauth_params_string( params ); sig = oauth_sign( method, url, params_s, token_secret ); g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); diff --git a/lib/oauth.h b/lib/oauth.h index 30486b98..a61650cc 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -24,7 +24,10 @@ /* http://oauth.net/core/1.0a/ */ struct oauth_info; -typedef void (*oauth_cb)( struct oauth_info * ); + +/* Callback function called twice during the access token request process. + Return FALSE if something broke and the process must be aborted. */ +typedef gboolean (*oauth_cb)( struct oauth_info * ); typedef enum { @@ -52,13 +55,13 @@ struct oauth_info Request an initial anonymous token which can be used to construct an authorization URL for the user. This is passed to the callback function in a struct oauth_info. */ -void *oauth_request_token( const char *url, oauth_cb func, void *data ); +struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ); /* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) The user gets a PIN or so which we now exchange for the final access token. This is passed to the callback function in the same struct oauth_info. */ -void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st ); +void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ); /* http://oauth.net/core/1.0a/#anchor12 (section 7) Generate an OAuth Authorization: HTTP header. access_token should be @@ -66,3 +69,6 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s whatever's going to be in the POST body of the request. GET args will automatically be grabbed from url. */ char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); + +/* Shouldn't normally be required unless the process is aborted by the user. */ +void oauth_info_free( struct oauth_info *info ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 54bee2f1..8ea1a56e 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -65,20 +65,27 @@ static void twitter_main_loop_start( struct im_connection *ic ) td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); } -static void twitter_oauth_callback( struct oauth_info *info ); +static gboolean twitter_oauth_callback( struct oauth_info *info ); static void twitter_oauth_start( struct im_connection *ic ) { + struct twitter_data *td = ic->proto_data; + imcb_log( ic, "Requesting OAuth request token" ); - oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); + td->oauth_info = oauth_request_token( + TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); } -static void twitter_oauth_callback( struct oauth_info *info ) +static gboolean twitter_oauth_callback( struct oauth_info *info ) { struct im_connection *ic = info->data; - struct twitter_data *td = ic->proto_data; + struct twitter_data *td; + if( !g_slist_find( twitter_connections, ic ) ) + return FALSE; + + td = ic->proto_data; if( info->stage == OAUTH_REQUEST_TOKEN ) { char name[strlen(ic->acc->user)+9], *msg; @@ -87,11 +94,9 @@ static void twitter_oauth_callback( struct oauth_info *info ) { imcb_error( ic, "OAuth error: %s", info->http->status_string ); imc_logout( ic, TRUE ); - return; + return FALSE; } - td->oauth_info = info; - sprintf( name, "twitter_%s", ic->acc->user ); msg = g_strdup_printf( "To finish OAuth authentication, please visit " "%s?%s and respond with the resulting PIN code.", @@ -105,7 +110,7 @@ static void twitter_oauth_callback( struct oauth_info *info ) { imcb_error( ic, "OAuth error: %s", info->http->status_string ); imc_logout( ic, TRUE ); - return; + return FALSE; } td->oauth = g_strdup( info->access_token ); @@ -117,6 +122,8 @@ static void twitter_oauth_callback( struct oauth_info *info ) twitter_main_loop_start( ic ); } + + return TRUE; } static char *set_eval_mode( set_t *set, char *value ) @@ -187,6 +194,8 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { + oauth_info_free( td->oauth_info ); + g_free( td->pass ); g_free( td->oauth ); g_free( td ); @@ -205,8 +214,11 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message if (g_strncasecmp(who, "twitter_", 8) == 0 && g_strcasecmp(who + 8, ic->acc->user) == 0) { - if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth == NULL ) + if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth_info ) + { oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); + td->oauth_info = NULL; + } else twitter_post_status(ic, message); } -- cgit v1.2.3 From 0bff877cf81754283eac52ee49fedc3872cd0d4c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:49:58 +0100 Subject: Valgrind-clean now. And decent handling of errors (wrong PIN). --- lib/oauth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 97017043..9fb83922 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -272,7 +272,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f oauth_add_default_params( ¶ms ); params_s = oauth_params_string( params ); - oauth_params_free( params_ ); + oauth_params_free( ¶ms ); s = oauth_sign( "POST", url, params_s, NULL ); post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); @@ -353,6 +353,8 @@ static void oauth_access_token_done( struct http_request *req ) { struct oauth_info *st = req->data; + st->http = req; + if( req->status_code == 200 ) { GSList *params = NULL; -- cgit v1.2.3 From 3f668e478bbaa062cf6b01e627cb39885200e5ff Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 28 Apr 2010 08:38:25 +0100 Subject: OAuth documentation update. --- doc/user-guide/commands.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index a8f030b2..9fcc91da 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -78,6 +78,10 @@ To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option. + + + Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See help set oauth.) + @@ -702,6 +706,25 @@ + + true + + + + This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory. + + + + With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type account on. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process. + + + + The resulting access token will be saved permanently, so you have to do this only once. + + + + + both both, root, user, none -- cgit v1.2.3 From a7c6d0e9a9d985534dc18d450399d6a7ece6c592 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 30 Apr 2010 23:51:18 +0100 Subject: Set HTML-flag on Twitter accounts so </> and all get converted back. (Probably also fixes potential problems with outgoing messages.) --- protocols/twitter/twitter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 8ea1a56e..f5baeeea 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -158,6 +158,7 @@ static void twitter_login( account_t *acc ) twitter_connections = g_slist_append( twitter_connections, ic ); ic->proto_data = td; + ic->flags |= OPT_DOES_HTML; td->user = acc->user; if( !set_getbool( &acc->set, "oauth" ) ) -- cgit v1.2.3 From 85ef57f94436f23447c0d8603b52977824381854 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 30 Apr 2010 23:53:29 +0100 Subject: NULL-initialize two vars that weren't and should. --- lib/oauth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 9fb83922..4a465a2f 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -376,7 +376,7 @@ static void oauth_access_token_done( struct http_request *req ) char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) { GSList *params = NULL, *l; - char *token_secret, *sig, *params_s, *s; + char *token_secret = NULL, *sig = NULL, *params_s, *s; GString *ret = NULL; /* First, get the two pieces of info from the access token that we need. */ -- cgit v1.2.3 From c2ecadc08daa5163f4c90aef36de0e33d0d44f16 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 14:53:59 +0100 Subject: Cleaned up OAuth stuff: consumer key/secret should *not* be in lib/oauth.c. Keep it in the Twitter module, and use the oauth_info struct through the whole session to keep all this together. --- lib/oauth.c | 91 +++++++++++++++++----------------------- lib/oauth.h | 22 +++++++--- protocols/twitter/twitter.c | 46 ++++++++++++-------- protocols/twitter/twitter.h | 5 --- protocols/twitter/twitter_http.c | 10 ++--- protocols/twitter/twitter_http.h | 4 +- protocols/twitter/twitter_lib.c | 12 +++--- 7 files changed, 98 insertions(+), 92 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 4a465a2f..4c93cbf4 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -32,14 +32,10 @@ #include "url.h" #include "oauth.h" -#define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" -#define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" -/* How can it be a secret if it's right here in the source code? No clue... */ - #define HMAC_BLOCK_SIZE 64 static char *oauth_sign( const char *method, const char *url, - const char *params, const char *token_secret ) + const char *params, struct oauth_info *oi ) { sha1_state_t sha1; uint8_t hash[sha1_hash_size]; @@ -50,20 +46,20 @@ static char *oauth_sign( const char *method, const char *url, /* Create K. If our current key is >64 chars we have to hash it, otherwise just pad. */ memset( key, 0, HMAC_BLOCK_SIZE ); - i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 ); + i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 ); if( i > HMAC_BLOCK_SIZE ) { sha1_init( &sha1 ); - sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); + sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); sha1_append( &sha1, (uint8_t*) "&", 1 ); - if( token_secret ) - sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) ); + if( oi->token_secret ) + sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) ); sha1_finish( &sha1, key ); } else { g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", - CONSUMER_SECRET, token_secret ? : "" ); + oi->sp->consumer_secret, oi->token_secret ? : "" ); } /* Inner part: H(K XOR 0x36, text) */ @@ -228,18 +224,19 @@ void oauth_info_free( struct oauth_info *info ) { if( info ) { - g_free( info->auth_params ); + g_free( info->auth_url ); g_free( info->request_token ); - g_free( info->access_token ); + g_free( info->token ); + g_free( info->token_secret ); g_free( info ); } } -static void oauth_add_default_params( GSList **params ) +static void oauth_add_default_params( GSList **params, struct oauth_service *sp ) { char *s; - oauth_params_set( params, "oauth_consumer_key", CONSUMER_KEY ); + oauth_params_set( params, "oauth_consumer_key", sp->consumer_key ); oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); s = g_strdup_printf( "%d", (int) time( NULL ) ); @@ -253,7 +250,7 @@ static void oauth_add_default_params( GSList **params ) oauth_params_set( params, "oauth_version", "1.0" ); } -static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data ) +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi ) { GSList *params = NULL; char *s, *params_s, *post; @@ -269,12 +266,12 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f if( params_ ) params = *params_; - oauth_add_default_params( ¶ms ); + oauth_add_default_params( ¶ms, oi->sp ); params_s = oauth_params_string( params ); oauth_params_free( ¶ms ); - s = oauth_sign( "POST", url, params_s, NULL ); + s = oauth_sign( "POST", url, params_s, oi ); post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); g_free( params_s ); g_free( s ); @@ -288,7 +285,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f g_free( post ); req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, - s, func, data ); + s, func, oi ); g_free( s ); return req; @@ -296,17 +293,18 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f static void oauth_request_token_done( struct http_request *req ); -struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ) +struct oauth_info *oauth_request_token( struct oauth_service *sp, oauth_cb func, void *data ) { struct oauth_info *st = g_new0( struct oauth_info, 1 ); GSList *params = NULL; st->func = func; st->data = data; + st->sp = sp; oauth_params_add( ¶ms, "oauth_callback", "oob" ); - if( !oauth_post_request( url, ¶ms, oauth_request_token_done, st ) ) + if( !oauth_post_request( sp->url_request_token, ¶ms, oauth_request_token_done, st ) ) { oauth_info_free( st ); return NULL; @@ -325,28 +323,26 @@ static void oauth_request_token_done( struct http_request *req ) { GSList *params = NULL; - st->auth_params = g_strdup( req->reply_body ); - oauth_params_parse( ¶ms, st->auth_params ); + st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body ); + oauth_params_parse( ¶ms, req->reply_body ); st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); oauth_params_free( ¶ms ); } st->stage = OAUTH_REQUEST_TOKEN; - if( !st->func( st ) ) - oauth_info_free( st ); + st->func( st ); } static void oauth_access_token_done( struct http_request *req ); -void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ) +gboolean oauth_access_token( const char *pin, struct oauth_info *st ) { GSList *params = NULL; oauth_params_add( ¶ms, "oauth_token", st->request_token ); oauth_params_add( ¶ms, "oauth_verifier", pin ); - if( !oauth_post_request( url, ¶ms, oauth_access_token_done, st ) ) - oauth_info_free( st ); + return oauth_post_request( st->sp->url_access_token, ¶ms, oauth_access_token_done, st ) != NULL; } static void oauth_access_token_done( struct http_request *req ) @@ -358,39 +354,32 @@ static void oauth_access_token_done( struct http_request *req ) if( req->status_code == 200 ) { GSList *params = NULL; - const char *token, *token_secret; oauth_params_parse( ¶ms, req->reply_body ); - token = oauth_params_get( ¶ms, "oauth_token" ); - token_secret = oauth_params_get( ¶ms, "oauth_token_secret" ); - st->access_token = g_strdup_printf( - "oauth_token=%s&oauth_token_secret=%s", token, token_secret ); + st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + st->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); oauth_params_free( ¶ms ); } st->stage = OAUTH_ACCESS_TOKEN; - st->func( st ); - oauth_info_free( st ); + if( st->func( st ) ) + { + /* Don't need these anymore, but keep the rest. */ + g_free( st->auth_url ); + st->auth_url = NULL; + g_free( st->request_token ); + st->request_token = NULL; + } } -char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ) { GSList *params = NULL, *l; - char *token_secret = NULL, *sig = NULL, *params_s, *s; + char *sig = NULL, *params_s, *s; GString *ret = NULL; - /* First, get the two pieces of info from the access token that we need. */ - oauth_params_parse( ¶ms, access_token ); - if( params == NULL ) - goto err; - - /* Pick out the token secret, we shouldn't include it but use it for signing. */ - token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); - if( token_secret == NULL ) - goto err; - oauth_params_del( ¶ms, "oauth_token_secret" ); - - oauth_add_default_params( ¶ms ); + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_add_default_params( ¶ms, oi->sp ); /* Start building the OAuth header. 'key="value", '... */ ret = g_string_new( "OAuth " ); @@ -420,20 +409,18 @@ char *oauth_http_header( char *access_token, const char *method, const char *url if( ( s = strchr( url, '?' ) ) ) { s = g_strdup( s + 1 ); - oauth_params_parse( ¶ms, s + 1 ); + oauth_params_parse( ¶ms, s ); g_free( s ); } /* Append the signature and we're done! */ params_s = oauth_params_string( params ); - sig = oauth_sign( method, url, params_s, token_secret ); + sig = oauth_sign( method, url, params_s, oi ); g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); g_free( params_s ); -err: oauth_params_free( ¶ms ); g_free( sig ); - g_free( token_secret ); return ret ? g_string_free( ret, FALSE ) : NULL; } diff --git a/lib/oauth.h b/lib/oauth.h index a61650cc..849375be 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -39,36 +39,48 @@ typedef enum struct oauth_info { oauth_stage_t stage; + struct oauth_service *sp; oauth_cb func; void *data; struct http_request *http; - char *auth_params; + char *auth_url; char *request_token; - char *access_token; + char *token; + char *token_secret; +}; + +struct oauth_service +{ + char *url_request_token; + char *url_access_token; + char *url_authorize; + + char *consumer_key; + char *consumer_secret; }; /* http://oauth.net/core/1.0a/#auth_step1 (section 6.1) Request an initial anonymous token which can be used to construct an authorization URL for the user. This is passed to the callback function in a struct oauth_info. */ -struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ); +struct oauth_info *oauth_request_token( struct oauth_service *sp, oauth_cb func, void *data ); /* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) The user gets a PIN or so which we now exchange for the final access token. This is passed to the callback function in the same struct oauth_info. */ -void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ); +gboolean oauth_access_token( const char *pin, struct oauth_info *st ); /* http://oauth.net/core/1.0a/#anchor12 (section 7) Generate an OAuth Authorization: HTTP header. access_token should be saved/fetched using the functions above. args can be a string with whatever's going to be in the POST body of the request. GET args will automatically be grabbed from url. */ -char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ); /* Shouldn't normally be required unless the process is aborted by the user. */ void oauth_info_free( struct oauth_info *info ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index f5baeeea..6d43d819 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -27,7 +27,6 @@ #include "twitter_http.h" #include "twitter_lib.h" - /** * Main loop function */ @@ -65,6 +64,16 @@ static void twitter_main_loop_start( struct im_connection *ic ) td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); } + +static struct oauth_service twitter_oauth = +{ + "http://api.twitter.com/oauth/request_token", + "http://api.twitter.com/oauth/access_token", + "http://api.twitter.com/oauth/authorize", + .consumer_key = "xsDNKJuNZYkZyMcu914uEA", + .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo", +}; + static gboolean twitter_oauth_callback( struct oauth_info *info ); static void twitter_oauth_start( struct im_connection *ic ) @@ -73,8 +82,7 @@ static void twitter_oauth_start( struct im_connection *ic ) imcb_log( ic, "Requesting OAuth request token" ); - td->oauth_info = oauth_request_token( - TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); + td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic ); } static gboolean twitter_oauth_callback( struct oauth_info *info ) @@ -99,26 +107,24 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) sprintf( name, "twitter_%s", ic->acc->user ); msg = g_strdup_printf( "To finish OAuth authentication, please visit " - "%s?%s and respond with the resulting PIN code.", - TWITTER_OAUTH_AUTHORIZE, info->auth_params ); + "%s and respond with the resulting PIN code.", + info->auth_url ); imcb_buddy_msg( ic, name, msg, 0, 0 ); g_free( msg ); } else if( info->stage == OAUTH_ACCESS_TOKEN ) { - if( info->access_token == NULL ) + if( info->token == NULL ) { imcb_error( ic, "OAuth error: %s", info->http->status_string ); imc_logout( ic, TRUE ); return FALSE; } - td->oauth = g_strdup( info->access_token ); - /* IM mods didn't do this so far and it's ugly but I should be able to get away with it... */ - g_free( ic->acc->pass ); - ic->acc->pass = g_strdup( info->access_token ); + //g_free( ic->acc->pass ); + //ic->acc->pass = g_strdup( info->access_token ); twitter_main_loop_start( ic ); } @@ -126,6 +132,7 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) return TRUE; } + static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || @@ -163,15 +170,15 @@ static void twitter_login( account_t *acc ) td->user = acc->user; if( !set_getbool( &acc->set, "oauth" ) ) td->pass = g_strdup( acc->pass ); - else if( strstr( acc->pass, "oauth_token=" ) ) - td->oauth = g_strdup( acc->pass ); + //else if( strstr( acc->pass, "oauth_token=" ) ) + // td->oauth = g_strdup( acc->pass ); td->home_timeline_id = 0; sprintf( name, "twitter_%s", acc->user ); imcb_add_buddy( ic, name, NULL ); imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); - if( td->pass || td->oauth ) + if( td->pass || td->oauth_info ) twitter_main_loop_start( ic ); else twitter_oauth_start( ic ); @@ -196,9 +203,7 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { oauth_info_free( td->oauth_info ); - g_free( td->pass ); - g_free( td->oauth ); g_free( td ); } @@ -215,10 +220,15 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message if (g_strncasecmp(who, "twitter_", 8) == 0 && g_strcasecmp(who + 8, ic->acc->user) == 0) { - if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth_info ) + if( set_getbool( &ic->acc->set, "oauth" ) && + td->oauth_info && td->oauth_info->token == NULL ) { - oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); - td->oauth_info = NULL; + if( !oauth_access_token( message, td->oauth_info ) ) + { + imcb_error( ic, "OAuth error: %s", "Failed to send access token request" ); + imc_logout( ic, TRUE ); + return FALSE; + } } else twitter_post_status(ic, message); diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 9c046b66..24f61e42 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,7 +36,6 @@ struct twitter_data { char* user; char* pass; - char* oauth; struct oauth_info *oauth_info; guint64 home_timeline_id; gint main_loop_id; @@ -51,8 +50,4 @@ struct twitter_data */ GSList *twitter_connections; -#define TWITTER_OAUTH_REQUEST_TOKEN "http://api.twitter.com/oauth/request_token" -#define TWITTER_OAUTH_ACCESS_TOKEN "http://api.twitter.com/oauth/access_token" -#define TWITTER_OAUTH_AUTHORIZE "http://api.twitter.com/oauth/authorize" - #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 93e315fa..51f437df 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -28,7 +28,6 @@ * * ****************************************************************************/ -#include "twitter_http.h" #include "twitter.h" #include "bitlbee.h" #include "url.h" @@ -38,6 +37,8 @@ #include #include +#include "twitter_http.h" + char *twitter_url_append(char *url, char *key, char* value); @@ -45,7 +46,7 @@ char *twitter_url_append(char *url, char *key, char* value); * Do a request. * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c */ -void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char* oauth_token, char** arguments, int arguments_len) +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, struct oauth_info* oi, char** arguments, int arguments_len) { url_t *url = g_new0( url_t, 1 ); char *tmp; @@ -110,12 +111,11 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in is_post ? "POST" : "GET", url->file, url->host ); // If a pass and user are given we append them to the request. - if (oauth_token) + if (oi) { char *full_header; - full_header = oauth_http_header(oauth_token, - is_post ? "POST" : "GET", + full_header = oauth_http_header(oi, is_post ? "POST" : "GET", url_string, url_arguments); tmp = g_strdup_printf("%sAuthorization: %s\r\n", request, full_header); diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h index 1e29fb99..5ef2530f 100644 --- a/protocols/twitter/twitter_http.h +++ b/protocols/twitter/twitter_http.h @@ -27,8 +27,10 @@ #include "nogaim.h" #include "http_client.h" +struct oauth_info; + void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, - char* user, char* pass, char *oauth_token, char** arguments, int arguments_len); + char* user, char* pass, struct oauth_info *oi, char** arguments, int arguments_len); #endif //_TWITTER_HTTP_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c91f8bbf..ee6e39fe 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -129,7 +129,7 @@ void twitter_get_friends_ids(struct im_connection *ic, int next_cursor) char* args[2]; args[0] = "cursor"; args[1] = g_strdup_printf ("%d", next_cursor); - twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth, args, 2); + twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth_info, args, 2); g_free(args[1]); } @@ -395,7 +395,7 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor) args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id); } - twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, td->oauth, args, td->home_timeline_id ? 4 : 2); + twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, td->oauth_info, args, td->home_timeline_id ? 4 : 2); g_free(args[1]); if (td->home_timeline_id) { @@ -509,7 +509,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) if (req->status_code == 200) { td->http_fails = 0; - if (!ic->flags & OPT_LOGGED_IN) + if (!(ic->flags & OPT_LOGGED_IN)) imcb_connected(ic); } else if (req->status_code == 401) @@ -619,7 +619,7 @@ void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor) args[0] = "cursor"; args[1] = g_strdup_printf ("%d", next_cursor); - twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth, args, 2); + twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth_info, args, 2); g_free(args[1]); } @@ -653,7 +653,7 @@ void twitter_post_status(struct im_connection *ic, char* msg) char* args[2]; args[0] = "status"; args[1] = msg; - twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 2); + twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, args, 2); // g_free(args[1]); } @@ -671,7 +671,7 @@ void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) args[2] = "text"; args[3] = msg; // Use the same callback as for twitter_post_status, since it does basically the same. - twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 4); + twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, args, 4); // g_free(args[1]); // g_free(args[3]); } -- cgit v1.2.3 From f4b0911d037c02f8b9190518b5efda4368dcc25b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 15:10:32 +0100 Subject: Save the credentials again. --- lib/oauth.c | 27 +++++++++++++++++++++++++++ lib/oauth.h | 4 ++++ protocols/twitter/twitter.c | 8 ++++---- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 4c93cbf4..8012c37a 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -424,3 +424,30 @@ char *oauth_http_header( struct oauth_info *oi, const char *method, const char * return ret ? g_string_free( ret, FALSE ) : NULL; } + +char *oauth_to_string( struct oauth_info *oi ) +{ + GSList *params = NULL; + char *ret; + + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_params_add( ¶ms, "oauth_token_secret", oi->token_secret ); + ret = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + return ret; +} + +struct oauth_info *oauth_from_string( char *in, struct oauth_service *sp ) +{ + struct oauth_info *oi = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + oauth_params_parse( ¶ms, in ); + oi->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oi->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); + oauth_params_free( ¶ms ); + oi->sp = sp; + + return oi; +} diff --git a/lib/oauth.h b/lib/oauth.h index 849375be..6b51dc2c 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -84,3 +84,7 @@ char *oauth_http_header( struct oauth_info *oi, const char *method, const char * /* Shouldn't normally be required unless the process is aborted by the user. */ void oauth_info_free( struct oauth_info *info ); + +/* Convert to and back from strings, for easier saving. */ +char *oauth_to_string( struct oauth_info *oi ); +struct oauth_info *oauth_from_string( char *in, struct oauth_service *sp ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 6d43d819..589f2088 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -123,8 +123,8 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) /* IM mods didn't do this so far and it's ugly but I should be able to get away with it... */ - //g_free( ic->acc->pass ); - //ic->acc->pass = g_strdup( info->access_token ); + g_free( ic->acc->pass ); + ic->acc->pass = oauth_to_string( info ); twitter_main_loop_start( ic ); } @@ -170,8 +170,8 @@ static void twitter_login( account_t *acc ) td->user = acc->user; if( !set_getbool( &acc->set, "oauth" ) ) td->pass = g_strdup( acc->pass ); - //else if( strstr( acc->pass, "oauth_token=" ) ) - // td->oauth = g_strdup( acc->pass ); + else if( strstr( acc->pass, "oauth_token=" ) ) + td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); td->home_timeline_id = 0; sprintf( name, "twitter_%s", acc->user ); -- cgit v1.2.3 From 4273158e2bd219602e502d593040cccb234d4805 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 15:16:26 +0100 Subject: Oops. rt_mon start at 0=Jan, causing off-by-a-month timestamps shown to the user. Temporary work-around: set timezone to +720 or so. :-P --- protocols/nogaim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index fca8b302..98325c89 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -1193,7 +1193,7 @@ static char *format_timestamp( irc_t *irc, time_t msg_ts ) else return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d " "%02d:%02d:%02d\x02]\x02 ", - msg.tm_year + 1900, msg.tm_mon, msg.tm_mday, + msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday, msg.tm_hour, msg.tm_min, msg.tm_sec ); } -- cgit v1.2.3 From f4850088ea8527d8cfd46dedea678bb0d5d93471 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 00:55:48 +0100 Subject: Support at least incoming groupchats. Not sure yet how starting them is going to work. --- protocols/purple/purple.c | 78 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 44a21fae..b81415ea 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -364,6 +364,13 @@ static int purple_send_typing( struct im_connection *ic, char *who, int flags ) } } +static void purple_chat_msg( struct groupchat *gc, char *message, int flags ) +{ + PurpleConversation *pc = gc->data; + + purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -505,6 +512,64 @@ static PurpleBlistUiOps bee_blist_uiops = prplcb_blist_remove, }; +void prplcb_conv_new( PurpleConversation *conv ) +{ + if( conv->type == PURPLE_CONV_TYPE_CHAT ) + { + struct im_connection *ic = purple_ic_by_pa( conv->account ); + struct groupchat *gc; + + gc = imcb_chat_new( ic, conv->name ); + conv->ui_data = gc; + gc->data = conv; + } +} + +void prplcb_conv_free( PurpleConversation *conv ) +{ + struct groupchat *gc = conv->ui_data; + + imcb_chat_free( gc ); +} + +void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals ) +{ + struct groupchat *gc = conv->ui_data; + GList *b; + + for( b = cbuddies; b; b = b->next ) + { + PurpleConvChatBuddy *pcb = b->data; + + imcb_chat_add_buddy( gc, pcb->name ); + } +} + +void prplcb_conv_del_users( PurpleConversation *conv, GList *cbuddies ) +{ + struct groupchat *gc = conv->ui_data; + GList *b; + + for( b = cbuddies; b; b = b->next ) + imcb_chat_remove_buddy( gc, b->data, "" ); +} + +void prplcb_conv_chat_msg( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) +{ + struct groupchat *gc = conv->ui_data; + PurpleBuddy *buddy; + + /* ..._SEND means it's an outgoing message, no need to echo those. */ + if( flags & PURPLE_MESSAGE_SEND ) + return; + + buddy = purple_find_buddy( conv->account, who ); + if( buddy != NULL ) + who = purple_buddy_get_name( buddy ); + + imcb_chat_msg( gc, who, (char*) message, 0, mtime ); +} + static void prplcb_conv_im( PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime ) { struct im_connection *ic = purple_ic_by_pa( conv->account ); @@ -523,14 +588,14 @@ static void prplcb_conv_im( PurpleConversation *conv, const char *who, const cha static PurpleConversationUiOps bee_conv_uiops = { - NULL, /* create_conversation */ - NULL, /* destroy_conversation */ - NULL, /* write_chat */ + prplcb_conv_new, /* create_conversation */ + prplcb_conv_free, /* destroy_conversation */ + prplcb_conv_chat_msg, /* write_chat */ prplcb_conv_im, /* write_im */ NULL, /* write_conv */ - NULL, /* chat_add_users */ + prplcb_conv_add_users, /* chat_add_users */ NULL, /* chat_rename_user */ - NULL, /* chat_remove_users */ + prplcb_conv_del_users, /* chat_remove_users */ NULL, /* chat_update_user */ NULL, /* present */ NULL, /* has_focus */ @@ -917,7 +982,8 @@ void purple_initmodule() funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; - /* TODO(wilmer): Set this one only for protocols that support it? */ + /* TODO(wilmer): Set these only for protocols that support them? */ + funcs.chat_msg = purple_chat_msg; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From a348d00feb203641a3c2e8d2e3596f86d4bdbe0b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 16:49:01 +0100 Subject: Although probably the Twitter module won't interfere with libpurple, for now I'll let BitlBee-libpurple be just that. --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 3799e2da..6b950464 100755 --- a/configure +++ b/configure @@ -532,6 +532,7 @@ EOF jabber=0 oscar=0 yahoo=0 + twitter=0 fi if [ "$msn" = 0 ]; then -- cgit v1.2.3 From 8ad5c34dfc1eff0112f73414fd4b566fae0c9ce3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 16:53:18 +0100 Subject: Added support for creating groupchats. This can only be done in a horribly broken way which is surely going to break somehow someday. --- protocols/purple/purple.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b81415ea..0f60f630 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -52,6 +52,22 @@ static struct im_connection *purple_ic_by_gc( PurpleConnection *gc ) return purple_ic_by_pa( purple_connection_get_account( gc ) ); } +static gboolean purple_menu_cmp( const char *a, const char *b ) +{ + while( *a && *b ) + { + while( *a == '_' ) a ++; + while( *b == '_' ) b ++; + if( tolower( *a ) != tolower( *b ) ) + return FALSE; + + a ++; + b ++; + } + + return ( *a == '\0' && *b == '\0' ); +} + static void purple_init( account_t *acc ) { PurplePlugin *prpl = purple_plugins_find_with_id( (char*) acc->prpl->data ); @@ -371,6 +387,67 @@ static void purple_chat_msg( struct groupchat *gc, char *message, int flags ) purple_conv_chat_send( purple_conversation_get_chat_data( pc ), message ); } +struct groupchat *purple_chat_with( struct im_connection *ic, char *who ) +{ + /* No, "of course" this won't work this way. Or in fact, it almost + does, but it only lets you send msgs to it, you won't receive + any. Instead, we have to click the virtual menu item. + PurpleAccount *pa = ic->proto_data; + PurpleConversation *pc; + PurpleConvChat *pcc; + struct groupchat *gc; + + gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" ); + gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" ); + pc->ui_data = gc; + + pcc = PURPLE_CONV_CHAT( pc ); + purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE ); + purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE ); + //purple_conv_chat_add_user( pcc, who, "", 0, TRUE ); + */ + + /* There went my nice afternoon. :-( */ + + PurpleAccount *pa = ic->proto_data; + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + PurpleBuddy *pb = purple_find_buddy( (PurpleAccount*) ic->proto_data, who ); + PurpleMenuAction *mi; + GList *menu; + void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */ + + if( !pb || !pi || !pi->blist_node_menu ) + return NULL; + + menu = pi->blist_node_menu( &pb->node ); + while( menu ) + { + mi = menu->data; + if( purple_menu_cmp( mi->label, "initiate chat" ) || + purple_menu_cmp( mi->label, "initiate conference" ) ) + break; + menu = menu->next; + } + + if( menu == NULL ) + return NULL; + + /* Call the fucker. */ + callback = (void*) mi->callback; + callback( &pb->node, menu->data ); + + return NULL; +} + +void purple_chat_invite( struct groupchat *gc, char *who, char *message ) +{ + PurpleConversation *pc = gc->data; + PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc ); + + purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -537,6 +614,14 @@ void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean struct groupchat *gc = conv->ui_data; GList *b; + if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 ) + { + /* Work around the broken MSN module which fucks up the user's + handle completely when informing him/her that he just + successfully joined the room s/he just created (v2.6.6). */ + imcb_chat_add_buddy( gc, gc->ic->acc->user ); + } + for( b = cbuddies; b; b = b->next ) { PurpleConvChatBuddy *pcb = b->data; @@ -984,6 +1069,8 @@ void purple_initmodule() funcs.handle_cmp = g_strcasecmp; /* TODO(wilmer): Set these only for protocols that support them? */ funcs.chat_msg = purple_chat_msg; + funcs.chat_with = purple_chat_with; + funcs.chat_invite = purple_chat_invite; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From 15794dcafac99b2be1c400bc54a510fe61c4ebac Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 17:03:41 +0100 Subject: Groupchat support "finished". Named chatrooms are not supported yet. This only adds support for the "chat with" command and for getting pulled into other people's chats. --- protocols/purple/purple.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 0f60f630..90312d0d 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -448,6 +448,13 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); } +void purple_chat_leave( struct groupchat *gc, char *who ) +{ + PurpleConversation *pc = gc->data; + + purple_conversation_destroy( pc ); +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -1071,6 +1078,7 @@ void purple_initmodule() funcs.chat_msg = purple_chat_msg; funcs.chat_with = purple_chat_with; funcs.chat_invite = purple_chat_invite; + funcs.chat_leave = purple_chat_leave; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From bda2975f43df2fd2409243082f6d98dc8c9e6fde Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 17:14:14 +0100 Subject: Warning on the libpurple+libevent combination. --- configure | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure b/configure index 6b950464..69d59d0d 100755 --- a/configure +++ b/configure @@ -533,6 +533,13 @@ EOF oscar=0 yahoo=0 twitter=0 + + if [ "$events" = "libevent" ]; then + echo + echo 'Warning: Some libpurple modules (including msn-pecan) do their event handling' + echo 'outside libpurple, talking to GLib directly. At least for now the combination' + echo 'libpurple + libevent is *not* recommended!' + fi fi if [ "$msn" = 0 ]; then -- cgit v1.2.3 From 839189b091ee82937666bcaa328a7d3ecd1c573b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 22:06:25 +0100 Subject: Applied show-offline patch from Florian E.J. Fruth, adapted for a few changes that happened since 1.2.4. --- doc/user-guide/commands.xml | 10 +++++ irc.c | 1 + protocols/nogaim.c | 90 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 9fcc91da..af2c5eb2 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -860,6 +860,16 @@ + + true + + + + If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect. + + + + true diff --git a/irc.c b/irc.c index aa8bc140..22bb9fa6 100644 --- a/irc.c +++ b/irc.c @@ -200,6 +200,7 @@ irc_t *irc_new( int fd ) s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "show_offline", "false", set_eval_bool, irc ); s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); s->flags |= SET_NULL_OK; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 98325c89..2248d11e 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -656,7 +656,18 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( u->status_msg ); u->away = u->status_msg = NULL; - if( ( flags & OPT_LOGGED_IN ) && !u->online ) + if( set_getbool( &ic->irc->set, "show_offline" ) && !u->online ) + { + /* always set users as online */ + irc_spawn( ic->irc, u ); + u->online = 1; + if( !( flags & OPT_LOGGED_IN ) ) + { + /* set away message if user isn't really online */ + u->away = g_strdup( "User is offline" ); + } + } + else if( ( flags & OPT_LOGGED_IN ) && !u->online ) { irc_spawn( ic->irc, u ); u->online = 1; @@ -665,14 +676,30 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, { struct groupchat *c; - irc_kill( ic->irc, u ); - u->online = 0; - - /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ - for( c = ic->groupchats; c; c = c->next ) - remove_chat_buddy_silent( c, handle ); + if( set_getbool( &ic->irc->set, "show_offline" ) ) + { + /* keep offline users in channel and set away message to "offline" */ + u->away = g_strdup( "User is offline" ); + + /* Keep showing him/her in the control channel but not in groupchats. */ + for( c = ic->groupchats; c; c = c->next ) + { + if( remove_chat_buddy_silent( c, handle ) && c->joined ) + irc_part( c->ic->irc, u, c->channel ); + } + } + else + { + /* kill offline users */ + irc_kill( ic->irc, u ); + u->online = 0; + + /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ + for( c = ic->groupchats; c; c = c->next ) + remove_chat_buddy_silent( c, handle ); + } } - + if( flags & OPT_AWAY ) { if( state && message ) @@ -697,11 +724,8 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, u->status_msg = g_strdup( message ); } - /* LISPy... */ - if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ - ( u->online ) && /* Don't touch offline people */ - ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ - ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + /* early if-clause for show_offline even if there is some redundant code here because this isn't LISP but C ;) */ + if( set_getbool( &ic->irc->set, "show_offline" ) && set_getbool( &ic->irc->set, "away_devoice" ) ) { char *from; @@ -714,9 +738,43 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, ic->irc->myhost ); } - irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, - u->away?'-':'+', u->nick ); - g_free( from ); + + /* if we use show_offline, we op online users, voice away users, and devoice/deop offline users */ + if( flags & OPT_LOGGED_IN ) + { + /* user is "online" (either really online or away) */ + irc_write( ic->irc, ":%s MODE %s %cv%co %s %s", from, ic->irc->channel, + u->away?'+':'-', u->away?'-':'+', u->nick, u->nick ); + } + else + { + /* user is offline */ + irc_write( ic->irc, ":%s MODE %s -vo %s %s", from, ic->irc->channel, u->nick, u->nick ); + } + } + else + { + /* LISPy... */ + if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + ( u->online ) && /* Don't touch offline people */ + ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ + ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + { + char *from; + + if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) + { + from = g_strdup( ic->irc->myhost ); + } + else + { + from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, + ic->irc->myhost ); + } + irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + g_free( from ); + } } } -- cgit v1.2.3 From 6824fb355a31ed46c22740ef184332440d3981ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 23:00:46 +0100 Subject: Fixed show_offline documentation. (The default is off, not on.) --- doc/user-guide/commands.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index af2c5eb2..b84e87aa 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -861,7 +861,7 @@ - true + false -- cgit v1.2.3 From 999769119e85518cc46b3ed64cb8781695fefbdc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 22:36:43 +0100 Subject: Check Tweet length on the BitlBee side already. --- doc/user-guide/commands.xml | 15 +++++++++++++++ protocols/twitter/twitter.c | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index b84e87aa..ba2b4e70 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -662,6 +662,21 @@ + + 140 + + + + Since Twitter rejects messages longer than 140 characters, BitlBee can count message length and emit a warning instead of waiting for Twitter to reject it. + + + + You can change this limit here but this won't disable length checks on Twitter's side. You can also set it to 0 to disable the check in case you believe BitlBee doesn't count the characters correctly. + + + + + one, many, chat one diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 9c7b060c..98e85641 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -143,10 +143,24 @@ static char *set_eval_mode( set_t *set, char *value ) return NULL; } +static gboolean twitter_length_check( struct im_connection *ic, gchar *msg ) +{ + int max = set_getint( &ic->acc->set, "message_length" ), len; + + if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max ) + return TRUE; + + imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max ); + + return FALSE; +} + static void twitter_init( account_t *acc ) { set_t *s; + s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); + s = set_add( &acc->set, "mode", "one", set_eval_mode, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -230,7 +244,7 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message return FALSE; } } - else + else if( twitter_length_check(ic, message) ) twitter_post_status(ic, message); } else @@ -261,7 +275,7 @@ static void twitter_remove_buddy( struct im_connection *ic, char *who, char *gro static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) { - if( c && message ) + if( c && message && twitter_length_check(c->ic, message)) twitter_post_status(c->ic, message); } -- cgit v1.2.3 From aa7ce1b3dd961e948e907460bd42bf1592ea1717 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 00:50:12 +0100 Subject: Yahoo! seems to echo conference room invitations now, huh? No idea what this is but let's ignore them. --- protocols/yahoo/yahoo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index b61f6ff9..922ac17b 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -827,6 +827,10 @@ void ext_yahoo_got_conf_invite( int id, const char *ignored, char txt[1024]; YList *m; + if( g_strcasecmp( who, ic->acc->user ) == 0 ) + /* WTF, Yahoo! seems to echo these now? */ + return; + inv = g_malloc( sizeof( struct byahoo_conf_invitation ) ); memset( inv, 0, sizeof( struct byahoo_conf_invitation ) ); inv->name = g_strdup( room ); -- cgit v1.2.3 From 5a599a1550c670649dba681e702864d55d2e3795 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 01:40:03 +0100 Subject: Room names on OSCAR can't start with digits, this broke the "chat with" command on ICQ so far. Just prepend "icq_" and it'll work. --- protocols/oscar/oscar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index e0c32257..5d23c36a 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2650,7 +2650,8 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) static int chat_id = 0; char * chatname; - chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++); + chatname = g_strdup_printf("%s%s_%d", isdigit(*ic->acc->user) ? "icq_" : "", + ic->acc->user, chat_id++); ret = oscar_chat_join(ic, chatname, NULL, NULL); -- cgit v1.2.3 From 7ee07c3191d1eb21d1d8317f7bfc3d9d98359ab1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 17:06:19 +0100 Subject: It looks like AIM mem requests (implemented as damn_you() and straight_to_hell() back in the days - and still more or less the same in libpurple it seems) are no longer necessary; the script no longer exists at the location used in the source and nobody complained. Nice try, AOL. And hats of to the Gaim developer who thought of this clever way around it. --- protocols/oscar/oscar.c | 136 ------------------------------------------------ 1 file changed, 136 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 5d23c36a..00c5e5ef 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -204,7 +204,6 @@ static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...); static int gaim_parse_locerr (aim_session_t *, aim_frame_t *, ...); static int gaim_icbm_param_info (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...); -static int gaim_memrequest (aim_session_t *, aim_frame_t *, ...); static int gaim_selfinfo (aim_session_t *, aim_frame_t *, ...); static int gaim_offlinemsg (aim_session_t *, aim_frame_t *, ...); static int gaim_offlinemsgdone (aim_session_t *, aim_frame_t *, ...); @@ -569,7 +568,6 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); @@ -603,143 +601,9 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { return 1; } -struct pieceofcrap { - struct im_connection *ic; - unsigned long offset; - unsigned long len; - char *modname; - int fd; - aim_conn_t *conn; - unsigned int inpa; -}; - -static gboolean damn_you(gpointer data, gint source, b_input_condition c) -{ - struct pieceofcrap *pos = data; - struct oscar_data *od = pos->ic->proto_data; - char in = '\0'; - int x = 0; - unsigned char m[17]; - - while (read(pos->fd, &in, 1) == 1) { - if (in == '\n') - x++; - else if (in != '\r') - x = 0; - if (x == 2) - break; - in = '\0'; - } - if (in != '\n') { - imcb_error(pos->ic, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - b_event_remove(pos->inpa); - closesocket(pos->fd); - g_free(pos); - return FALSE; - } - /* [WvG] Wheeeee! Who needs error checking anyway? ;-) */ - read(pos->fd, m, 16); - m[16] = '\0'; - b_event_remove(pos->inpa); - closesocket(pos->fd); - aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH); - g_free(pos); - - return FALSE; -} - -static gboolean straight_to_hell(gpointer data, gint source, b_input_condition cond) { - struct pieceofcrap *pos = data; - char buf[BUF_LONG]; - - if (source < 0) { - imcb_error(pos->ic, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - if (pos->modname) - g_free(pos->modname); - g_free(pos); - return FALSE; - } - - g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA - "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", - pos->offset, pos->len, pos->modname ? pos->modname : ""); - write(pos->fd, buf, strlen(buf)); - if (pos->modname) - g_free(pos->modname); - pos->inpa = b_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); - return FALSE; -} - /* size of icbmui.ocm, the largest module in AIM 3.5 */ #define AIM_MAX_FILE_SIZE 98304 -int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) { - va_list ap; - struct pieceofcrap *pos; - guint32 offset, len; - char *modname; - int fd; - - va_start(ap, fr); - offset = (guint32)va_arg(ap, unsigned long); - len = (guint32)va_arg(ap, unsigned long); - modname = va_arg(ap, char *); - va_end(ap); - - if (len == 0) { - aim_sendmemblock(sess, fr->conn, offset, len, NULL, - AIM_SENDMEMBLOCK_FLAG_ISREQUEST); - return 1; - } - /* uncomment this when you're convinced it's right. remember, it's been wrong before. - if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) { - char *buf; - int i = 8; - if (modname) - i += strlen(modname); - buf = g_malloc(i); - i = 0; - if (modname) { - memcpy(buf, modname, strlen(modname)); - i += strlen(modname); - } - buf[i++] = offset & 0xff; - buf[i++] = (offset >> 8) & 0xff; - buf[i++] = (offset >> 16) & 0xff; - buf[i++] = (offset >> 24) & 0xff; - buf[i++] = len & 0xff; - buf[i++] = (len >> 8) & 0xff; - buf[i++] = (len >> 16) & 0xff; - buf[i++] = (len >> 24) & 0xff; - aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); - g_free(buf); - return 1; - } - */ - - pos = g_new0(struct pieceofcrap, 1); - pos->ic = sess->aux_data; - pos->conn = fr->conn; - - pos->offset = offset; - pos->len = len; - pos->modname = modname ? g_strdup(modname) : NULL; - - fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos); - if (fd < 0) { - if (pos->modname) - g_free(pos->modname); - g_free(pos); - imcb_error(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - } - pos->fd = fd; - - return 1; -} - static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) { #if 0 struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b}; -- cgit v1.2.3 From d8acfd3ba84e018554d8564f08e9a50cde56b4a4 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 10 May 2010 00:23:34 +0100 Subject: Purple lists mix up key and value; key == what the user sees, *value* is what the module understands. This should hopefully resolve QQ issues. --- protocols/purple/purple.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 90312d0d..edd10219 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -140,8 +140,11 @@ static void purple_init( account_t *acc ) for( io = purple_account_option_get_list( o ); io; io = io->next ) { PurpleKeyValuePair *kv = io->data; - opts = g_slist_append( opts, kv->key ); - g_string_append_printf( help, "%s, ", kv->key ); + opts = g_slist_append( opts, kv->value ); + if( strcmp( kv->value, kv->key ) != 0 ) + g_string_append_printf( help, "%s (%s), ", kv->value, kv->key ); + else + g_string_append_printf( help, "%s, ", kv->value ); } g_string_truncate( help, help->len - 2 ); eval = set_eval_list; -- cgit v1.2.3 From e73a501ce0cf49d8073d7d25656561400f2aca79 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 11 May 2010 00:35:58 +0100 Subject: Fixed a long-standing bug where non-anonymous Jabber chatrooms could cause full JIDs to be added to the local contact list, but not removed when leaving the room, eventually triggering errors like the one in #499. People kept sending me these dumps and I finally took time to make what turned out to be this one-line fix. This error should never appear again unless people are doing really crazy stuff. --- protocols/jabber/conference.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index f434c58a..affe8aef 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -271,8 +271,10 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu bud->flags |= JBFLAG_IS_ANONYMOUS; } - if( bud != jc->me ) + if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) { + /* If JIDs are anonymized, add them to the local + list for the duration of this chat. */ imcb_add_buddy( ic, bud->ext_jid, NULL ); imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource ); } -- cgit v1.2.3 From f8cb76dea3554fd82c01c5cae61f35d2b6cb0936 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 11 May 2010 22:33:37 +0100 Subject: Stop those "Many switchboard failures" messages. No point in showing them unless the lost switchboard actually had something queued. --- protocols/msn/sb.c | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e2ee8570..49eed601 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -327,9 +327,13 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c struct im_connection *ic = sb->ic; struct msn_data *md = ic->proto_data; - if( msn_handler( sb->handler ) == -1 ) + if( msn_handler( sb->handler ) != -1 ) + return TRUE; + + if( sb->msgq != NULL ) { time_t now = time( NULL ); + char buf[1024]; if( now - md->first_sb_failure > 600 ) { @@ -346,37 +350,28 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c imcb_log( ic, "Warning: Many switchboard failures on MSN connection. " "There might be problems delivering your messages." ); - if( sb->msgq != NULL ) + if( md->msgq == NULL ) { - char buf[1024]; - - if( md->msgq == NULL ) - { - md->msgq = sb->msgq; - } - else - { - GSList *l; - - for( l = md->msgq; l->next; l = l->next ); - l->next = sb->msgq; - } - sb->msgq = NULL; + md->msgq = sb->msgq; + } + else + { + GSList *l; - debug( "Moved queued messages back to the main queue, creating a new switchboard to retry." ); - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) - return FALSE; + for( l = md->msgq; l->next; l = l->next ); + l->next = sb->msgq; } + sb->msgq = NULL; - msn_sb_destroy( sb ); - - return FALSE; - } - else - { - return TRUE; + debug( "Moved queued messages back to the main queue, " + "creating a new switchboard to retry." ); + g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); + if( !msn_write( ic, buf, strlen( buf ) ) ) + return FALSE; } + + msn_sb_destroy( sb ); + return FALSE; } static int msn_sb_command( gpointer data, char **cmd, int num_parts ) -- cgit v1.2.3 From 3742fb6ae99c2e5e25cff272a154b9834866e8f9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 12 May 2010 00:27:11 +0100 Subject: Implement some kind of ignorant awareness of XML namespaces: Enough to not break backward compatibility (hopefully) but be able to pick up inappropriate uses of XML namespace prefixes. Main reason for this change: Fix XMPP typing notification compatibility with GMail. --- lib/xmltree.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/xmltree.c b/lib/xmltree.c index 67fe46e1..31f8ee9c 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -448,7 +448,11 @@ struct xt_node *xt_find_node( struct xt_node *node, const char *name ) { while( node ) { - if( g_strcasecmp( node->name, name ) == 0 ) + char *colon; + + if( g_strcasecmp( node->name, name ) == 0 || + ( ( colon = strchr( node->name, ':' ) ) && + g_strcasecmp( colon + 1, name ) == 0 ) ) break; node = node->next; @@ -460,6 +464,7 @@ struct xt_node *xt_find_node( struct xt_node *node, const char *name ) char *xt_find_attr( struct xt_node *node, const char *key ) { int i; + char *colon; if( !node ) return NULL; @@ -468,6 +473,21 @@ char *xt_find_attr( struct xt_node *node, const char *key ) if( g_strcasecmp( node->attr[i].key, key ) == 0 ) break; + /* This is an awful hack that only takes care of namespace prefixes + inside a tag. Since IMHO excessive namespace usage in XMPP is + massive overkill anyway (this code exists for almost four years + now and never really missed it): Meh. */ + if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 && + ( colon = strchr( node->name, ':' ) ) ) + { + *colon = '\0'; + for( i = 0; node->attr[i].key; i ++ ) + if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 && + strcmp( node->attr[i].key + 6, node->name ) == 0 ) + break; + *colon = ':'; + } + return node->attr[i].value; } -- cgit v1.2.3 From ca0981ad1e427644a33fc31fe78d63ea834f0fa0 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 14 May 2010 00:05:07 +0100 Subject: As long as this is a separate branch, enabling purple by default's not a bad idea. --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 69d59d0d..12e79633 100755 --- a/configure +++ b/configure @@ -26,7 +26,7 @@ jabber=1 oscar=1 yahoo=1 twitter=1 -purple=0 +purple=1 debug=0 strip=1 -- cgit v1.2.3 From 6e6b3d7c7300c5cf5cf7f1538154925fd2fe3953 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 14 May 2010 01:09:29 +0100 Subject: Add a warning when the user included a domain part in his/her Yahoo! username. --- protocols/yahoo/yahoo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 922ac17b..e4d541d5 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -137,10 +137,15 @@ static void byahoo_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); struct byahoo_data *yd = ic->proto_data = g_new0( struct byahoo_data, 1 ); + char *s; yd->logged_in = FALSE; yd->current_status = YAHOO_STATUS_AVAILABLE; + if( ( s = strchr( acc->user, '@' ) ) && g_strcasecmp( s, "@yahoo.com" ) == 0 ) + imcb_error( ic, "Your Yahoo! username should just be a username. " + "Do not include any @domain part." ); + imcb_log( ic, "Connecting" ); yd->y2_id = yahoo_init( acc->user, acc->pass ); yahoo_login( yd->y2_id, yd->current_status ); -- cgit v1.2.3 From 2309152972f1d52af6adbb0e7e5d9c5ad3ae80f9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 17 May 2010 01:14:14 +0100 Subject: Split off the file transfer stuff into a separate file. What a mess. --- protocols/purple/Makefile | 2 +- protocols/purple/ft.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++ protocols/purple/purple.c | 199 +-------------------------------------- 3 files changed, 237 insertions(+), 198 deletions(-) create mode 100644 protocols/purple/ft.c diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index 15460529..403db799 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -9,7 +9,7 @@ -include ../../Makefile.settings # [SH] Program variables -objects = purple.o +objects = ft.o purple.o CFLAGS += -Wall $(PURPLE_CFLAGS) LFLAGS += -r diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c new file mode 100644 index 00000000..62a1092a --- /dev/null +++ b/protocols/purple/ft.c @@ -0,0 +1,234 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - File transfer stuff * +* * +* Copyright 2009-2010 Wilmer van der Gaast * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +#include "bitlbee.h" + +#include + +#include +#include + +struct prpl_xfer_data +{ + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; + char *buf; + int buf_len; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); + return TRUE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; + + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; + + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; +} + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, +}; + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index edd10219..b01fb991 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -36,7 +36,7 @@ GSList *purple_connections; libpurple in daemon mode anyway. */ static irc_t *local_irc; -static struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -826,202 +826,7 @@ static PurpleNotifyUiOps bee_notify_uiops = prplcb_notify_email, }; - -struct prpl_xfer_data -{ - PurpleXfer *xfer; - file_transfer_t *ft; - gint ready_timer; - char *buf; - int buf_len; -}; - -static file_transfer_t *next_ft; - -/* Glorious hack: We seem to have to remind at least some libpurple plugins - that we're ready because this info may get lost if we give it too early. - So just do it ten times a second. :-/ */ -static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - purple_xfer_ui_ready( px->xfer ); - - return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; -} - -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); - return TRUE; -} - -static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) -{ - struct prpl_xfer_data *px = xfer->ui_data; - gboolean st; - - b_event_remove( px->ready_timer ); - px->ready_timer = 0; - - st = px->ft->write( px->ft, (char*) buffer, size ); - - if( st && xfer->bytes_remaining == size ) - imcb_file_finished( px->ft ); - - return st ? size : 0; -} - -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) -{ - struct prpl_xfer_data *px = ft->data; - - px->buf = g_memdup( buffer, len ); - px->buf_len = len; - - //purple_xfer_ui_ready( px->xfer ); - px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); - - return TRUE; -} - -static void prpl_xfer_accept( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - purple_xfer_request_accepted( px->xfer, NULL ); - prpl_xfer_write_request( ft ); -} - -static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) -{ - struct prpl_xfer_data *px = ft->data; - purple_xfer_request_denied( px->xfer ); -} - -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - PurpleXfer *xfer = data; - struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - PurpleBuddy *buddy; - const char *who; - - buddy = purple_find_buddy( xfer->account, xfer->who ); - who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - - /* TODO(wilmer): After spreading some more const goodness in BitlBee, - remove the evil cast below. */ - px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); - px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; - - px->ft->accept = prpl_xfer_accept; - px->ft->canceled = prpl_xfer_canceled; - px->ft->write_request = prpl_xfer_write_request; - - return FALSE; -} - -static void prplcb_xfer_new( PurpleXfer *xfer ) -{ - if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) - { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); - - /* Sadly the xfer struct is still empty ATM so come back after - the caller is done. */ - b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); - } - else - { - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - - px->ft = next_ft; - px->ft->data = px; - px->xfer = xfer; - px->xfer->ui_data = px; - - purple_xfer_set_filename( xfer, px->ft->file_name ); - purple_xfer_set_size( xfer, px->ft->file_size ); - - next_ft = NULL; - } -} - -static void prplcb_xfer_dbg( PurpleXfer *xfer ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); -} - -gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) -{ - struct prpl_xfer_data *px = xfer->ui_data; - - fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); - - if( px->buf ) - { - *buffer = px->buf; - px->buf = NULL; - - px->ft->write_request( px->ft ); - - return px->buf_len; - } - - return 0; -} - -static PurpleXferUiOps bee_xfer_uiops = -{ - prplcb_xfer_new, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_dbg, - prplcb_xfer_write, - prplcb_xfer_read, - prplcb_xfer_dbg, -}; - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); - -void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) -{ - PurpleAccount *pa = ic->proto_data; - struct prpl_xfer_data *px; - - /* xfer_new() will pick up this variable. It's a hack but we're not - multi-threaded anyway. */ - next_ft = ft; - serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); - - ft->write = prpl_xfer_write; - - px = ft->data; - imcb_file_recv_start( ft ); - - px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); -} - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - if( px->ft->status & FT_STATUS_TRANSFERRING ) - { - fprintf( stderr, "The ft, it is ready...\n" ); - px->ft->write_request( px->ft ); - - return FALSE; - } - - return TRUE; -} +extern PurpleXferUiOps bee_xfer_uiops; static void purple_ui_init() { -- cgit v1.2.3 From 553767cacf43e1a4a1038530c47dc0af5fdd0369 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 17 May 2010 21:30:45 +0100 Subject: Move direct ft stuff to an unused file: This gets too hairy and too fragile. I don't have time to work out all the details, I doubt if this is supposed to work reliably yet at all. Let's go for the simple via-disk approach for now. --- protocols/purple/ft-direct.c | 239 +++++++++++++++++++++++++++++++++++++++++++ protocols/purple/ft.c | 137 +++++++------------------ 2 files changed, 275 insertions(+), 101 deletions(-) create mode 100644 protocols/purple/ft-direct.c diff --git a/protocols/purple/ft-direct.c b/protocols/purple/ft-direct.c new file mode 100644 index 00000000..98a16d75 --- /dev/null +++ b/protocols/purple/ft-direct.c @@ -0,0 +1,239 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* libpurple module - File transfer stuff * +* * +* Copyright 2009-2010 Wilmer van der Gaast * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +/* This code tries to do direct file transfers, i.e. without caching the file + locally on disk first. Since libpurple can only do this since version 2.6.0 + and even then very unreliably (and not with all IM modules), I'm canning + this code for now. */ + +#include "bitlbee.h" + +#include + +#include +#include + +struct prpl_xfer_data +{ + PurpleXfer *xfer; + file_transfer_t *ft; + gint ready_timer; + char *buf; + int buf_len; +}; + +static file_transfer_t *next_ft; + +struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); + +/* Glorious hack: We seem to have to remind at least some libpurple plugins + that we're ready because this info may get lost if we give it too early. + So just do it ten times a second. :-/ */ +static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + purple_xfer_ui_ready( px->xfer ); + + return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; +} + +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); + return TRUE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + px->buf = g_memdup( buffer, len ); + px->buf_len = len; + + //purple_xfer_ui_ready( px->xfer ); + px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); + + return TRUE; +} + +static void prpl_xfer_accept( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_accepted( px->xfer, NULL ); + prpl_xfer_write_request( ft ); +} + +static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) +{ + struct prpl_xfer_data *px = ft->data; + purple_xfer_request_denied( px->xfer ); +} + +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; + + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; + + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; + + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; + + return FALSE; +} + +static void prplcb_xfer_new( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) + { + /* This should suppress the stupid file dialog. */ + purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + + /* Sadly the xfer struct is still empty ATM so come back after + the caller is done. */ + b_timeout_add( 0, prplcb_xfer_new_send_cb, xfer ); + } + else + { + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + px->ft = next_ft; + px->ft->data = px; + px->xfer = xfer; + px->xfer->ui_data = px; + + purple_xfer_set_filename( xfer, px->ft->file_name ); + purple_xfer_set_size( xfer, px->ft->file_size ); + + next_ft = NULL; + } +} + +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} + +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); +} + +static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + gboolean st; + + fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + + b_event_remove( px->ready_timer ); + px->ready_timer = 0; + + st = px->ft->write( px->ft, (char*) buffer, size ); + + if( st && xfer->bytes_remaining == size ) + imcb_file_finished( px->ft ); + + return st ? size : 0; +} + +gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + + if( px->buf ) + { + *buffer = px->buf; + px->buf = NULL; + + px->ft->write_request( px->ft ); + + return px->buf_len; + } + + return 0; +} + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_dbg, + prplcb_xfer_write, + prplcb_xfer_read, + prplcb_xfer_dbg, +}; + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +{ + PurpleAccount *pa = ic->proto_data; + struct prpl_xfer_data *px; + + /* xfer_new() will pick up this variable. It's a hack but we're not + multi-threaded anyway. */ + next_ft = ft; + serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + + ft->write = prpl_xfer_write; + + px = ft->data; + imcb_file_recv_start( ft ); + + px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); +} + +static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) +{ + struct prpl_xfer_data *px = data; + + if( px->ft->status & FT_STATUS_TRANSFERRING ) + { + fprintf( stderr, "The ft, it is ready...\n" ); + px->ft->write_request( px->ft ); + + return FALSE; + } + + return TRUE; +} diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 62a1092a..4b5a3f49 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -21,6 +21,11 @@ * * \***************************************************************************/ +/* Do file transfers via disk for now, since libpurple was really designed + for straight-to/from disk fts and is only just learning how to pass the + file contents the the UI instead (2.6.0 and higher it seems, and with + varying levels of success). */ + #include "bitlbee.h" #include @@ -41,36 +46,16 @@ static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); -/* Glorious hack: We seem to have to remind at least some libpurple plugins - that we're ready because this info may get lost if we give it too early. - So just do it ten times a second. :-/ */ -static gboolean prplcb_xfer_write_request_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - purple_xfer_ui_ready( px->xfer ); - - return purple_xfer_get_type( px->xfer ) == PURPLE_XFER_RECEIVE; -} - static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { - struct prpl_xfer_data *px = ft->data; - px->ready_timer = b_timeout_add( 100, prplcb_xfer_write_request_cb, px ); - return TRUE; + return FALSE; } static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) { struct prpl_xfer_data *px = ft->data; - px->buf = g_memdup( buffer, len ); - px->buf_len = len; - - //purple_xfer_ui_ready( px->xfer ); - px->ready_timer = b_timeout_add( 0, prplcb_xfer_write_request_cb, px ); - - return TRUE; + return FALSE; } static void prpl_xfer_accept( struct file_transfer *ft ) @@ -86,30 +71,7 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - PurpleXfer *xfer = data; - struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); - PurpleBuddy *buddy; - const char *who; - - buddy = purple_find_buddy( xfer->account, xfer->who ); - who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - - /* TODO(wilmer): After spreading some more const goodness in BitlBee, - remove the evil cast below. */ - px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); - px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; - - px->ft->accept = prpl_xfer_accept; - px->ft->canceled = prpl_xfer_canceled; - px->ft->write_request = prpl_xfer_write_request; - - return FALSE; -} +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); static void prplcb_xfer_new( PurpleXfer *xfer ) { @@ -124,6 +86,7 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) } else { + /* struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); px->ft = next_ft; @@ -135,54 +98,43 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) purple_xfer_set_size( xfer, px->ft->file_size ); next_ft = NULL; + */ } } -static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); -} - -static void prplcb_xfer_dbg( PurpleXfer *xfer ) -{ - fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); -} - -static gssize prplcb_xfer_write( PurpleXfer *xfer, const guchar *buffer, gssize size ) +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ) { - struct prpl_xfer_data *px = xfer->ui_data; - gboolean st; - - fprintf( stderr, "xfer_write %d %d\n", size, px->buf_len ); + PurpleXfer *xfer = data; + struct im_connection *ic = purple_ic_by_pa( xfer->account ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + PurpleBuddy *buddy; + const char *who; - b_event_remove( px->ready_timer ); - px->ready_timer = 0; + buddy = purple_find_buddy( xfer->account, xfer->who ); + who = buddy ? purple_buddy_get_name( buddy ) : xfer->who; - st = px->ft->write( px->ft, (char*) buffer, size ); + /* TODO(wilmer): After spreading some more const goodness in BitlBee, + remove the evil cast below. */ + px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); + px->ft->data = px; + px->xfer = data; + px->xfer->ui_data = px; - if( st && xfer->bytes_remaining == size ) - imcb_file_finished( px->ft ); + px->ft->accept = prpl_xfer_accept; + px->ft->canceled = prpl_xfer_canceled; + px->ft->write_request = prpl_xfer_write_request; - return st ? size : 0; + return FALSE; } -gssize prplcb_xfer_read( PurpleXfer *xfer, guchar **buffer, gssize size ) +static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { - struct prpl_xfer_data *px = xfer->ui_data; - - fprintf( stderr, "xfer_read %d %d\n", size, px->buf_len ); + fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); +} - if( px->buf ) - { - *buffer = px->buf; - px->buf = NULL; - - px->ft->write_request( px->ft ); - - return px->buf_len; - } - - return 0; +static void prplcb_xfer_dbg( PurpleXfer *xfer ) +{ + fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); } PurpleXferUiOps bee_xfer_uiops = @@ -193,8 +145,8 @@ PurpleXferUiOps bee_xfer_uiops = prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_dbg, - prplcb_xfer_write, - prplcb_xfer_read, + NULL, + NULL, prplcb_xfer_dbg, }; @@ -214,21 +166,4 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha px = ft->data; imcb_file_recv_start( ft ); - - px->ready_timer = b_timeout_add( 100, prplcb_xfer_send_cb, px ); -} - -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ) -{ - struct prpl_xfer_data *px = data; - - if( px->ft->status & FT_STATUS_TRANSFERRING ) - { - fprintf( stderr, "The ft, it is ready...\n" ); - px->ft->write_request( px->ft ); - - return FALSE; - } - - return TRUE; } -- cgit v1.2.3 From 8822d23385bf7c35d67eaff1d0ce81470ba73f4f Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 00:23:20 +0100 Subject: This receives files but is very fragile if anything unusual happens (like a cancellation/timeout/etc). --- protocols/purple/ft.c | 117 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 102 insertions(+), 15 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 4b5a3f49..57d45beb 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -37,24 +37,74 @@ struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; - gint ready_timer; - char *buf; - int buf_len; + int fd; + char *fn; + gboolean ui_wants_data; }; static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) { + struct file_transfer *ft = data; + struct prpl_xfer_data *px = ft->data; + struct stat fs; + off_t tx_bytes; + + fprintf( stderr, "write_to_ui\n" ); + + /* If we don't have the file opened yet, there's no data so wait. */ + if( px->fd < 0 || !px->ui_wants_data ) + return FALSE; + + tx_bytes = lseek( px->fd, 0, SEEK_CUR ); + fstat( px->fd, &fs ); + + fprintf( stderr, "write_to_ui %zd %zd %zd\n", fs.st_size, tx_bytes, px->xfer->size ); + + if( fs.st_size > tx_bytes ) + { + char buf[1024]; + size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); + + if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) + { + fprintf( stderr, "Wrote %zd bytes\n", n ); + px->ui_wants_data = FALSE; + } + else + { + purple_xfer_cancel_local( px->xfer ); + imcb_file_canceled( ft, "Read error" ); + } + } + + if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) + { + purple_xfer_end( px->xfer ); + imcb_file_finished( ft ); + } + return FALSE; } -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +/* UI calls this when its buffer is empty and wants more data to send to the user. */ +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; + fprintf( stderr, "wrq\n" ); + + px->ui_wants_data = TRUE; + try_write_to_ui( ft, 0, 0 ); + + return FALSE; +} + +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ return FALSE; } @@ -77,8 +127,14 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) { if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) { - /* This should suppress the stupid file dialog. */ - purple_xfer_set_local_filename( xfer, "/tmp/wtf123" ); + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + xfer->ui_data = px; + px->xfer = xfer; + px->fn = mktemp( g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ) ); + px->fd = -1; + + purple_xfer_set_local_filename( xfer, px->fn ); /* Sadly the xfer struct is still empty ATM so come back after the caller is done. */ @@ -89,6 +145,7 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) /* struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + px->fd = -1; px->ft = next_ft; px->ft->data = px; px->xfer = xfer; @@ -106,7 +163,7 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi { PurpleXfer *xfer = data; struct im_connection *ic = purple_ic_by_pa( xfer->account ); - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + struct prpl_xfer_data *px = xfer->ui_data; PurpleBuddy *buddy; const char *who; @@ -117,8 +174,6 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi remove the evil cast below. */ px->ft = imcb_file_send_start( ic, (char*) who, xfer->filename, xfer->size ); px->ft->data = px; - px->xfer = data; - px->xfer->ui_data = px; px->ft->accept = prpl_xfer_accept; px->ft->canceled = prpl_xfer_canceled; @@ -127,9 +182,43 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi return FALSE; } +static void prplcb_xfer_destroy( PurpleXfer *xfer ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + g_free( px->fn ); + if( px->fd >= 0 ) + close( px->fd ); + g_free( px ); +} + static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { - fprintf( stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent ); + struct prpl_xfer_data *px = xfer->ui_data; + + fprintf( stderr, "prplcb_xfer_progress 0x%p %f\n", xfer, percent ); + + if( px->fd == -1 && percent > 0 ) + { + /* Weeeeeeeee, we're getting data! That means the file exists + by now so open it and start sending to the UI. */ + px->fd = open( px->fn, O_RDONLY ); + + /* Unlink it now, because we don't need it after this. */ + //unlink( px->fn ); + } + + if( percent < 1 ) + try_write_to_ui( px->ft, 0, 0 ); + else + b_timeout_add( 0, try_write_to_ui, px->ft ); +} + +static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) +{ + struct prpl_xfer_data *px = xfer->ui_data; + + imcb_file_canceled( px->ft, "Canceled by remote end" ); } static void prplcb_xfer_dbg( PurpleXfer *xfer ) @@ -140,18 +229,16 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, - prplcb_xfer_dbg, + prplcb_xfer_destroy, prplcb_xfer_dbg, prplcb_xfer_progress, prplcb_xfer_dbg, - prplcb_xfer_dbg, + prplcb_xfer_cancel_remote, NULL, NULL, prplcb_xfer_dbg, }; -static gboolean prplcb_xfer_send_cb( gpointer data, gint fd, b_input_condition cond ); - void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { PurpleAccount *pa = ic->proto_data; -- cgit v1.2.3 From 5d1b3a9529f7aadab62026be0eb9f4ca0f6311ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 00:38:39 +0100 Subject: purple_conv_chat_invite_user() is libpurple >= 2.6.0, so use serv_chat_invite() instead. --- protocols/purple/purple.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b01fb991..eb3a4eb5 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -448,7 +448,10 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) PurpleConversation *pc = gc->data; PurpleConvChat *pcc = PURPLE_CONV_CHAT( pc ); - purple_conv_chat_invite_user( pcc, who, message && *message ? message : "Please join my chat", FALSE ); + serv_chat_invite( purple_account_get_connection( gc->ic->proto_data ), + purple_conv_chat_get_id( pcc ), + message && *message ? message : "Please join my chat", + who ); } void purple_chat_leave( struct groupchat *gc, char *who ) -- cgit v1.2.3 From c96c72f16ea48ca769400ff91bd2eb434da19f6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 01:08:17 +0100 Subject: Little cleanup. Less compiler warnings, and removing tempfile at the beginning of the download already to make sure it doesn't stick around. --- protocols/purple/ft.c | 158 ++++++++++++++++++++++++---------------------- protocols/purple/purple.c | 7 +- 2 files changed, 85 insertions(+), 80 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 57d45beb..8cfa60dd 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -45,69 +45,11 @@ struct prpl_xfer_data static file_transfer_t *next_ft; struct im_connection *purple_ic_by_pa( PurpleAccount *pa ); +static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); +static gboolean prpl_xfer_write_request( struct file_transfer *ft ); -gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) -{ - struct file_transfer *ft = data; - struct prpl_xfer_data *px = ft->data; - struct stat fs; - off_t tx_bytes; - - fprintf( stderr, "write_to_ui\n" ); - - /* If we don't have the file opened yet, there's no data so wait. */ - if( px->fd < 0 || !px->ui_wants_data ) - return FALSE; - - tx_bytes = lseek( px->fd, 0, SEEK_CUR ); - fstat( px->fd, &fs ); - - fprintf( stderr, "write_to_ui %zd %zd %zd\n", fs.st_size, tx_bytes, px->xfer->size ); - - if( fs.st_size > tx_bytes ) - { - char buf[1024]; - size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); - - if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) - { - fprintf( stderr, "Wrote %zd bytes\n", n ); - px->ui_wants_data = FALSE; - } - else - { - purple_xfer_cancel_local( px->xfer ); - imcb_file_canceled( ft, "Read error" ); - } - } - - if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) - { - purple_xfer_end( px->xfer ); - imcb_file_finished( ft ); - } - - return FALSE; -} - -/* UI calls this when its buffer is empty and wants more data to send to the user. */ -static gboolean prpl_xfer_write_request( struct file_transfer *ft ) -{ - struct prpl_xfer_data *px = ft->data; - - fprintf( stderr, "wrq\n" ); - - px->ui_wants_data = TRUE; - try_write_to_ui( ft, 0, 0 ); - - return FALSE; -} - -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) -{ - return FALSE; -} +/* Receiving files (IM->UI): */ static void prpl_xfer_accept( struct file_transfer *ft ) { struct prpl_xfer_data *px = ft->data; @@ -121,8 +63,6 @@ static void prpl_xfer_canceled( struct file_transfer *ft, char *reason ) purple_xfer_request_denied( px->xfer ); } -static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_condition cond ); - static void prplcb_xfer_new( PurpleXfer *xfer ) { if( purple_xfer_get_type( xfer ) == PURPLE_XFER_RECEIVE ) @@ -182,6 +122,58 @@ static gboolean prplcb_xfer_new_send_cb( gpointer data, gint fd, b_input_conditi return FALSE; } +gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) +{ + struct file_transfer *ft = data; + struct prpl_xfer_data *px = ft->data; + struct stat fs; + off_t tx_bytes; + + /* If we don't have the file opened yet, there's no data so wait. */ + if( px->fd < 0 || !px->ui_wants_data ) + return FALSE; + + tx_bytes = lseek( px->fd, 0, SEEK_CUR ); + fstat( px->fd, &fs ); + + if( fs.st_size > tx_bytes ) + { + char buf[1024]; + size_t n = MIN( fs.st_size - tx_bytes, sizeof( buf ) ); + + if( read( px->fd, buf, n ) == n && ft->write( ft, buf, n ) ) + { + px->ui_wants_data = FALSE; + } + else + { + purple_xfer_cancel_local( px->xfer ); + imcb_file_canceled( ft, "Read error" ); + } + } + + if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) + { + purple_xfer_end( px->xfer ); + imcb_file_finished( ft ); + } + + return FALSE; +} + +/* UI calls this when its buffer is empty and wants more data to send to the user. */ +static gboolean prpl_xfer_write_request( struct file_transfer *ft ) +{ + struct prpl_xfer_data *px = ft->data; + + px->ui_wants_data = TRUE; + try_write_to_ui( ft, 0, 0 ); + + return FALSE; +} + + +/* Generic (IM<>UI): */ static void prplcb_xfer_destroy( PurpleXfer *xfer ) { struct prpl_xfer_data *px = xfer->ui_data; @@ -196,8 +188,6 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { struct prpl_xfer_data *px = xfer->ui_data; - fprintf( stderr, "prplcb_xfer_progress 0x%p %f\n", xfer, percent ); - if( px->fd == -1 && percent > 0 ) { /* Weeeeeeeee, we're getting data! That means the file exists @@ -205,12 +195,16 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) px->fd = open( px->fn, O_RDONLY ); /* Unlink it now, because we don't need it after this. */ - //unlink( px->fn ); + unlink( px->fn ); } if( percent < 1 ) try_write_to_ui( px->ft, 0, 0 ); else + /* Another nice problem: If we have the whole file, it only + gets closed when we return. Problem: There may still be + stuff buffered and not written, we'll only see it after + the caller close()s the file. So poll the file after that. */ b_timeout_add( 0, try_write_to_ui, px->ft ); } @@ -226,18 +220,12 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); } -PurpleXferUiOps bee_xfer_uiops = + +/* Sending files (UI->IM): */ +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) { - prplcb_xfer_new, - prplcb_xfer_destroy, - prplcb_xfer_dbg, - prplcb_xfer_progress, - prplcb_xfer_dbg, - prplcb_xfer_cancel_remote, - NULL, - NULL, - prplcb_xfer_dbg, -}; + return FALSE; +} void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { @@ -254,3 +242,19 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha px = ft->data; imcb_file_recv_start( ft ); } + + + + +PurpleXferUiOps bee_xfer_uiops = +{ + prplcb_xfer_new, + prplcb_xfer_destroy, + prplcb_xfer_dbg, + prplcb_xfer_progress, + prplcb_xfer_dbg, + prplcb_xfer_cancel_remote, + NULL, + NULL, + prplcb_xfer_dbg, +}; diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index eb3a4eb5..2507bfc2 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -141,10 +141,11 @@ static void purple_init( account_t *acc ) { PurpleKeyValuePair *kv = io->data; opts = g_slist_append( opts, kv->value ); + /* TODO: kv->value is not a char*, WTF? */ if( strcmp( kv->value, kv->key ) != 0 ) - g_string_append_printf( help, "%s (%s), ", kv->value, kv->key ); + g_string_append_printf( help, "%s (%s), ", (char*) kv->value, kv->key ); else - g_string_append_printf( help, "%s, ", kv->value ); + g_string_append_printf( help, "%s, ", (char*) kv->value ); } g_string_truncate( help, help->len - 2 ); eval = set_eval_list; @@ -454,7 +455,7 @@ void purple_chat_invite( struct groupchat *gc, char *who, char *message ) who ); } -void purple_chat_leave( struct groupchat *gc, char *who ) +void purple_chat_leave( struct groupchat *gc ) { PurpleConversation *pc = gc->data; -- cgit v1.2.3 From 31fc06fbb48b6217186ca441eb9b95add5f783ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 18 May 2010 01:42:02 +0100 Subject: Suppress auto-reconnect when required (auth errors and concurrent logins probably, not sure what sets the wants_to_die flag). --- protocols/purple/purple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 2507bfc2..fee93b27 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -497,7 +497,7 @@ static void prplcb_conn_disconnected( PurpleConnection *gc ) if( ic != NULL ) { - imc_logout( ic, TRUE ); + imc_logout( ic, !gc->wants_to_die ); } } -- cgit v1.2.3 From e7dc02a89d846d27b63719a5093c2e2a295fc232 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 19 May 2010 01:57:58 +0100 Subject: Similar hacky code to send files. This indirect sending stuff sucks badly for numerous reasons. Maybe libpurple 2.7.0 is less crappy and will eventually allow (working) direct ft's again. This somewhat works, but filename info is lost with some protocols. --- protocols/purple/ft.c | 113 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 93 insertions(+), 20 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index 8cfa60dd..ab637a94 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -37,8 +37,9 @@ struct prpl_xfer_data { PurpleXfer *xfer; file_transfer_t *ft; + struct im_connection *ic; int fd; - char *fn; + char *fn, *orig_fn, *handle; gboolean ui_wants_data; }; @@ -82,20 +83,16 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) } else { - /* - struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + struct file_transfer *ft = next_ft; + struct prpl_xfer_data *px = ft->data; - px->fd = -1; - px->ft = next_ft; - px->ft->data = px; + xfer->ui_data = px; px->xfer = xfer; - px->xfer->ui_data = px; - purple_xfer_set_filename( xfer, px->ft->file_name ); - purple_xfer_set_size( xfer, px->ft->file_size ); + purple_xfer_set_filename( xfer, px->orig_fn ); + purple_xfer_set_local_filename( xfer, px->fn ); next_ft = NULL; - */ } } @@ -179,6 +176,8 @@ static void prplcb_xfer_destroy( PurpleXfer *xfer ) struct prpl_xfer_data *px = xfer->ui_data; g_free( px->fn ); + g_free( px->orig_fn ); + g_free( px->handle ); if( px->fd >= 0 ) close( px->fd ); g_free( px ); @@ -188,6 +187,20 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { struct prpl_xfer_data *px = xfer->ui_data; + if( px == NULL ) + return; + + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) + { + if( *px->fn ) + { + //unlink( px->fn ); + *px->fn = '\0'; + } + + return; + } + if( px->fd == -1 && percent > 0 ) { /* Weeeeeeeee, we're getting data! That means the file exists @@ -215,6 +228,16 @@ static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) imcb_file_canceled( px->ft, "Canceled by remote end" ); } +static void prplcb_xfer_add( PurpleXfer *xfer ) +{ + if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) + { + struct prpl_xfer_data *px = xfer->ui_data; + + purple_xfer_set_filename( xfer, px->orig_fn ); + } +} + static void prplcb_xfer_dbg( PurpleXfer *xfer ) { fprintf( stderr, "prplcb_xfer_dbg 0x%p\n", xfer ); @@ -222,27 +245,77 @@ static void prplcb_xfer_dbg( PurpleXfer *xfer ) /* Sending files (UI->IM): */ -static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ); +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ); + +void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { - return FALSE; + struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + + ft->data = px; + px->ft = ft; + px->fn = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); + px->fd = mkstemp( px->fn ); + + px->ic = ic; + px->handle = g_strdup( handle ); + px->orig_fn = g_strdup( ft->file_name ); + + imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." ); + + b_timeout_add( 0, purple_transfer_request_cb, ft ); } -void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) +static void purple_transfer_forward( struct file_transfer *ft ) { - PurpleAccount *pa = ic->proto_data; - struct prpl_xfer_data *px; + struct prpl_xfer_data *px = ft->data; + PurpleAccount *pa = px->ic->proto_data; /* xfer_new() will pick up this variable. It's a hack but we're not multi-threaded anyway. */ next_ft = ft; - serv_send_file( purple_account_get_connection( pa ), handle, ft->file_name ); + serv_send_file( purple_account_get_connection( pa ), px->handle, px->fn ); +} + +static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_condition cond ) +{ + file_transfer_t *ft = data; + + if( ft->write == NULL ) + { + ft->write = prpl_xfer_write; + imcb_file_recv_start( ft ); + } - ft->write = prpl_xfer_write; + ft->write_request( ft ); - px = ft->data; - imcb_file_recv_start( ft ); + return FALSE; } +static gboolean prpl_xfer_write( struct file_transfer *ft, char *buffer, unsigned int len ) +{ + struct prpl_xfer_data *px = ft->data; + + if( write( px->fd, buffer, len ) != len ) + { + imcb_file_canceled( ft, "Error while writing temporary file" ); + return FALSE; + } + + if( lseek( px->fd, 0, SEEK_CUR ) >= ft->file_size ) + { + close( px->fd ); + px->fd = -1; + + purple_transfer_forward( ft ); + imcb_file_finished( ft ); + px->ft = NULL; + } + else + b_timeout_add( 0, purple_transfer_request_cb, ft ); + + return TRUE; +} @@ -250,7 +323,7 @@ PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, prplcb_xfer_destroy, - prplcb_xfer_dbg, + prplcb_xfer_add, prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_cancel_remote, -- cgit v1.2.3 From 75c3ff711698ea069f33ffc460c2e7aec650e875 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 21 May 2010 01:09:29 +0100 Subject: Fixed sending with proper filenames by creating a temporary directory with the file in it; protocol modules are mostly hardcoded to use the filename from the filesystem with no way to override this. Also improved robustness a little bit. --- protocols/purple/ft.c | 64 +++++++++++++++++++++++++++++++---------------- protocols/purple/purple.c | 2 +- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/protocols/purple/ft.c b/protocols/purple/ft.c index ab637a94..e3a89524 100644 --- a/protocols/purple/ft.c +++ b/protocols/purple/ft.c @@ -39,7 +39,7 @@ struct prpl_xfer_data file_transfer_t *ft; struct im_connection *ic; int fd; - char *fn, *orig_fn, *handle; + char *fn, *handle; gboolean ui_wants_data; }; @@ -89,9 +89,6 @@ static void prplcb_xfer_new( PurpleXfer *xfer ) xfer->ui_data = px; px->xfer = xfer; - purple_xfer_set_filename( xfer, px->orig_fn ); - purple_xfer_set_local_filename( xfer, px->fn ); - next_ft = NULL; } } @@ -151,7 +148,7 @@ gboolean try_write_to_ui( gpointer data, gint fd, b_input_condition cond ) if( lseek( px->fd, 0, SEEK_CUR ) == px->xfer->size ) { - purple_xfer_end( px->xfer ); + /*purple_xfer_end( px->xfer );*/ imcb_file_finished( ft ); } @@ -176,7 +173,6 @@ static void prplcb_xfer_destroy( PurpleXfer *xfer ) struct prpl_xfer_data *px = xfer->ui_data; g_free( px->fn ); - g_free( px->orig_fn ); g_free( px->handle ); if( px->fd >= 0 ) close( px->fd ); @@ -194,7 +190,14 @@ static void prplcb_xfer_progress( PurpleXfer *xfer, double percent ) { if( *px->fn ) { - //unlink( px->fn ); + char *slash; + + unlink( px->fn ); + if( ( slash = strrchr( px->fn, '/' ) ) ) + { + *slash = '\0'; + rmdir( px->fn ); + } *px->fn = '\0'; } @@ -225,17 +228,11 @@ static void prplcb_xfer_cancel_remote( PurpleXfer *xfer ) { struct prpl_xfer_data *px = xfer->ui_data; - imcb_file_canceled( px->ft, "Canceled by remote end" ); -} - -static void prplcb_xfer_add( PurpleXfer *xfer ) -{ - if( purple_xfer_get_type( xfer ) == PURPLE_XFER_SEND ) - { - struct prpl_xfer_data *px = xfer->ui_data; - - purple_xfer_set_filename( xfer, px->orig_fn ); - } + if( px->ft ) + imcb_file_canceled( px->ft, "Canceled by remote end" ); + else + /* px->ft == NULL for sends, because of the two stages. :-/ */ + imcb_error( px->ic, "File transfer cancelled by remote end" ); } static void prplcb_xfer_dbg( PurpleXfer *xfer ) @@ -251,15 +248,38 @@ static gboolean purple_transfer_request_cb( gpointer data, gint fd, b_input_cond void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ) { struct prpl_xfer_data *px = g_new0( struct prpl_xfer_data, 1 ); + char *dir, *basename; ft->data = px; px->ft = ft; - px->fn = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); - px->fd = mkstemp( px->fn ); + + dir = g_strdup( "/tmp/bitlbee-purple-ft.XXXXXX" ); + if( !mkdtemp( dir ) ) + { + imcb_error( ic, "Could not create temporary file for file transfer" ); + g_free( px ); + g_free( dir ); + return; + } + + if( ( basename = strrchr( ft->file_name, '/' ) ) ) + basename++; + else + basename = ft->file_name; + px->fn = g_strdup_printf( "%s/%s", dir, basename ); + px->fd = open( px->fn, O_WRONLY | O_CREAT, 0600 ); + g_free( dir ); + + if( px->fd < 0 ) + { + imcb_error( ic, "Could not create temporary file for file transfer" ); + g_free( px ); + g_free( px->fn ); + return; + } px->ic = ic; px->handle = g_strdup( handle ); - px->orig_fn = g_strdup( ft->file_name ); imcb_log( ic, "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait..." ); @@ -323,7 +343,7 @@ PurpleXferUiOps bee_xfer_uiops = { prplcb_xfer_new, prplcb_xfer_destroy, - prplcb_xfer_add, + NULL, /* prplcb_xfer_add, */ prplcb_xfer_progress, prplcb_xfer_dbg, prplcb_xfer_cancel_remote, diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index fee93b27..799a8a80 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -840,7 +840,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - //purple_debug_set_ui_ops( &bee_debug_uiops ); + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From 2c5fabcf9066752eed45ad0334fca232a7123629 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 00:26:21 +0100 Subject: Sigh. Enable debugging only if the BITLBEE_DEBUG variable is set. --- protocols/purple/purple.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 799a8a80..f11cefcb 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -840,7 +840,9 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); - purple_debug_set_ui_ops( &bee_debug_uiops ); + + if( getenv( "BITLBEE_DEBUG" ) ) + purple_debug_set_ui_ops( &bee_debug_uiops ); } void purple_initmodule() -- cgit v1.2.3 From e77c2647c3f4d8d6518239f070f3989444003a08 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 01:58:59 +0100 Subject: Added support for the info command. --- protocols/purple/purple.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index f11cefcb..1d17b012 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -358,6 +358,11 @@ static void purple_remove_buddy( struct im_connection *ic, char *who, char *grou } } +static void purple_get_info( struct im_connection *ic, char *who ) +{ + serv_get_info( purple_account_get_connection( ic->proto_data ), who ); +} + static void purple_keepalive( struct im_connection *ic ) { } @@ -824,10 +829,66 @@ static void *prplcb_notify_email( PurpleConnection *gc, const char *subject, con return NULL; } +static void *prplcb_notify_userinfo( PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info ) +{ + struct im_connection *ic = purple_ic_by_gc( gc ); + GString *info = g_string_new( "" ); + GList *l = purple_notify_user_info_get_entries( user_info ); + char *key; + const char *value; + int n; + + while( l ) + { + PurpleNotifyUserInfoEntry *e = l->data; + + switch( purple_notify_user_info_entry_get_type( e ) ) + { + case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR: + case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER: + key = g_strdup( purple_notify_user_info_entry_get_label( e ) ); + value = purple_notify_user_info_entry_get_value( e ); + + if( key ) + { + strip_html( key ); + g_string_append_printf( info, "%s: ", key ); + + if( value ) + { + n = strlen( value ) - 1; + while( isspace( value[n] ) ) + n --; + g_string_append_len( info, value, n + 1 ); + } + g_string_append_c( info, '\n' ); + g_free( key ); + } + + break; + case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK: + g_string_append( info, "------------------------\n" ); + break; + } + + l = l->next; + } + + imcb_log( ic, "User %s info:\n%s", who, info->str ); + g_string_free( info, TRUE ); + + return NULL; +} + static PurpleNotifyUiOps bee_notify_uiops = { NULL, prplcb_notify_email, + NULL, + NULL, + NULL, + NULL, + prplcb_notify_userinfo, }; extern PurpleXferUiOps bee_xfer_uiops; @@ -885,6 +946,7 @@ void purple_initmodule() funcs.set_away = purple_set_away; funcs.add_buddy = purple_add_buddy; funcs.remove_buddy = purple_remove_buddy; + funcs.get_info = purple_get_info; funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; funcs.handle_cmp = g_strcasecmp; -- cgit v1.2.3 From dca8effbf732557e28a6a03d2fcf785d69a5a1bd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 02:05:58 +0100 Subject: Return ui_info so jabber:iq:version responses will not say just libpurple. --- protocols/purple/purple.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 1d17b012..7b020cf7 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -471,12 +471,27 @@ void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, cha static void purple_ui_init(); +GHashTable *prplcb_ui_info() +{ + static GHashTable *ret; + + if( ret == NULL ) + { + ret = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert( ret, "name", "BitlBee" ); + g_hash_table_insert( ret, "version", BITLBEE_VERSION ); + } + + return ret; +} + static PurpleCoreUiOps bee_core_uiops = { NULL, NULL, purple_ui_init, NULL, + prplcb_ui_info, }; static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t step, size_t step_count ) -- cgit v1.2.3 From 05a8932daa3e8f6d3dd4c5177fa04d1f17254000 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 13:21:27 +0100 Subject: Enable changing and viewing of block/allow lists. --- protocols/purple/purple.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index 7b020cf7..c9588d7a 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -358,6 +358,34 @@ static void purple_remove_buddy( struct im_connection *ic, char *who, char *grou } } +static void purple_add_permit( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_permit_add( pa, who, FALSE ); +} + +static void purple_add_deny( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_deny_add( pa, who, FALSE ); +} + +static void purple_rem_permit( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_permit_remove( pa, who, FALSE ); +} + +static void purple_rem_deny( struct im_connection *ic, char *who ) +{ + PurpleAccount *pa = ic->proto_data; + + purple_privacy_deny_remove( pa, who, FALSE ); +} + static void purple_get_info( struct im_connection *ic, char *who ) { serv_get_info( purple_account_get_connection( ic->proto_data ), who ); @@ -800,6 +828,48 @@ static PurpleRequestUiOps bee_request_uiops = NULL, }; +static void prplcb_privacy_permit_added( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + + if( !g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) + ic->permit = g_slist_prepend( ic->permit, g_strdup( name ) ); +} + +static void prplcb_privacy_permit_removed( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + void *n; + + n = g_slist_find_custom( ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); + ic->permit = g_slist_remove( ic->permit, n ); +} + +static void prplcb_privacy_deny_added( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + + if( !g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ) ) + ic->deny = g_slist_prepend( ic->deny, g_strdup( name ) ); +} + +static void prplcb_privacy_deny_removed( PurpleAccount *account, const char *name ) +{ + struct im_connection *ic = purple_ic_by_pa( account ); + void *n; + + n = g_slist_find_custom( ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp ); + ic->deny = g_slist_remove( ic->deny, n ); +} + +static PurplePrivacyUiOps bee_privacy_uiops = +{ + prplcb_privacy_permit_added, + prplcb_privacy_permit_removed, + prplcb_privacy_deny_added, + prplcb_privacy_deny_removed, +}; + static void prplcb_debug_print( PurpleDebugLevel level, const char *category, const char *arg_s ) { fprintf( stderr, "DEBUG %s: %s", category, arg_s ); @@ -916,6 +986,7 @@ static void purple_ui_init() purple_request_set_ui_ops( &bee_request_uiops ); purple_notify_set_ui_ops( &bee_notify_uiops ); purple_xfers_set_ui_ops( &bee_xfer_uiops ); + purple_privacy_set_ui_ops( &bee_privacy_uiops ); if( getenv( "BITLBEE_DEBUG" ) ) purple_debug_set_ui_ops( &bee_debug_uiops ); @@ -961,6 +1032,10 @@ void purple_initmodule() funcs.set_away = purple_set_away; funcs.add_buddy = purple_add_buddy; funcs.remove_buddy = purple_remove_buddy; + funcs.add_permit = purple_add_permit; + funcs.add_deny = purple_add_deny; + funcs.rem_permit = purple_rem_permit; + funcs.rem_deny = purple_rem_deny; funcs.get_info = purple_get_info; funcs.keepalive = purple_keepalive; funcs.send_typing = purple_send_typing; -- cgit v1.2.3 From c3caa46bc5c5ac0a372a13c5fe0de79845a7dabf Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 22 May 2010 14:46:25 +0100 Subject: Support for named groupchats, although not very solid. --- protocols/purple/purple.c | 52 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index c9588d7a..b91b7aca 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -495,6 +495,44 @@ void purple_chat_leave( struct groupchat *gc ) purple_conversation_destroy( pc ); } +struct groupchat *purple_chat_join( struct im_connection *ic, const char *room, const char *nick, const char *password ) +{ + PurpleAccount *pa = ic->proto_data; + PurplePlugin *prpl = purple_plugins_find_with_id( pa->protocol_id ); + PurplePluginProtocolInfo *pi = prpl->info->extra_info; + GHashTable *chat_hash; + PurpleConversation *conv; + GList *info, *l; + + if( !pi->chat_info || !pi->chat_info_defaults || + !( info = pi->chat_info( purple_account_get_connection( pa ) ) ) ) + { + imcb_error( ic, "Joining chatrooms not supported by this protocol" ); + return NULL; + } + + if( ( conv = purple_find_conversation_with_account( PURPLE_CONV_TYPE_CHAT, room, pa ) ) ) + purple_conversation_destroy( conv ); + + chat_hash = pi->chat_info_defaults( purple_account_get_connection( pa ), room ); + + for( l = info; l; l = l->next ) + { + struct proto_chat_entry *pce = l->data; + + if( strcmp( pce->identifier, "handle" ) == 0 ) + g_hash_table_replace( chat_hash, "handle", g_strdup( nick ) ); + else if( strcmp( pce->identifier, "password" ) == 0 ) + g_hash_table_replace( chat_hash, "password", g_strdup( password ) ); + else if( strcmp( pce->identifier, "passwd" ) == 0 ) + g_hash_table_replace( chat_hash, "passwd", g_strdup( password ) ); + } + + serv_join_chat( purple_account_get_connection( pa ), chat_hash ); + + return NULL; +} + void purple_transfer_request( struct im_connection *ic, file_transfer_t *ft, char *handle ); static void purple_ui_init(); @@ -661,6 +699,11 @@ void prplcb_conv_new( PurpleConversation *conv ) gc = imcb_chat_new( ic, conv->name ); conv->ui_data = gc; gc->data = conv; + + /* libpurple brokenness: Whatever. Show that we join right away, + there's no clear "This is you!" signaling in _add_users so + don't even try. */ + imcb_chat_add_buddy( gc, gc->ic->acc->user ); } } @@ -676,14 +719,6 @@ void prplcb_conv_add_users( PurpleConversation *conv, GList *cbuddies, gboolean struct groupchat *gc = conv->ui_data; GList *b; - if( !gc->joined && strcmp( conv->account->protocol_id, "prpl-msn" ) == 0 ) - { - /* Work around the broken MSN module which fucks up the user's - handle completely when informing him/her that he just - successfully joined the room s/he just created (v2.6.6). */ - imcb_chat_add_buddy( gc, gc->ic->acc->user ); - } - for( b = cbuddies; b; b = b->next ) { PurpleConvChatBuddy *pcb = b->data; @@ -1045,6 +1080,7 @@ void purple_initmodule() funcs.chat_with = purple_chat_with; funcs.chat_invite = purple_chat_invite; funcs.chat_leave = purple_chat_leave; + funcs.chat_join = purple_chat_join; funcs.transfer_request = purple_transfer_request; help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n"); -- cgit v1.2.3 From f85e9d6846d7b4ec0109180e62ae8677e2421fa3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 24 May 2010 22:24:53 +0100 Subject: Read display names. Setting them is going to be an awesome hack. --- protocols/purple/purple.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/protocols/purple/purple.c b/protocols/purple/purple.c index b91b7aca..98cd2241 100644 --- a/protocols/purple/purple.c +++ b/protocols/purple/purple.c @@ -36,6 +36,8 @@ GSList *purple_connections; libpurple in daemon mode anyway. */ static irc_t *local_irc; +static char *set_eval_display_name( set_t *set, char *value ); + struct im_connection *purple_ic_by_pa( PurpleAccount *pa ) { GSList *i; @@ -172,6 +174,9 @@ static void purple_init( account_t *acc ) help_add_mem( &global.help, help_title, help->str ); g_string_free( help, TRUE ); + s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc ); + s->flags |= ACC_SET_ONLINE_ONLY; + if( pi->options & OPT_PROTO_MAIL_CHECK ) { s = set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc ); @@ -337,6 +342,14 @@ static void purple_set_away( struct im_connection *ic, char *state_txt, char *me g_list_free( args ); } +static char *set_eval_display_name( set_t *set, char *value ) +{ + account_t *acc = set->data; + struct im_connection *ic = acc->ic; + + return NULL; +} + static void purple_add_buddy( struct im_connection *ic, char *who, char *group ) { PurpleBuddy *pb; @@ -570,9 +583,18 @@ static void prplcb_conn_progress( PurpleConnection *gc, const char *text, size_t static void prplcb_conn_connected( PurpleConnection *gc ) { struct im_connection *ic = purple_ic_by_gc( gc ); + const char *dn; + set_t *s; imcb_connected( ic ); + if( ( dn = purple_connection_get_display_name( gc ) ) && + ( s = set_find( &ic->acc->set, "display_name" ) ) ) + { + g_free( s->value ); + s->value = g_strdup( dn ); + } + if( gc->flags & PURPLE_CONNECTION_HTML ) ic->flags |= OPT_DOES_HTML; } -- cgit v1.2.3 From d25ebea73aef7d6b2bbc17212af5109383277e6e Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:04:55 +0100 Subject: GAIM_INPUT_* were renamed, at last. --- protocols/jabber/s5bytestream.c | 14 +++++++------- protocols/msn/invitation.c | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/protocols/jabber/s5bytestream.c b/protocols/jabber/s5bytestream.c index 58a6c2e4..14611a6f 100644 --- a/protocols/jabber/s5bytestream.c +++ b/protocols/jabber/s5bytestream.c @@ -405,7 +405,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_out = b_input_add( fd, GAIM_INPUT_WRITE, jabber_bs_recv_handshake, bt ); + bt->tf->watch_out = b_input_add( fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt ); /* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */ bt->connect_timeout = b_timeout_add( JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt ); @@ -432,7 +432,7 @@ gboolean jabber_bs_recv_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_REQUEST; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_recv_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt ); bt->tf->watch_out = 0; return FALSE; @@ -588,7 +588,7 @@ void jabber_bs_recv_answer_request( struct bs_transfer *bt ) bt->sh->port ); tf->ft->data = tf; - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); tf->ft->write_request = jabber_bs_recv_write_request; reply = xt_new_node( "streamhost-used", NULL, NULL ); @@ -630,7 +630,7 @@ gboolean jabber_bs_recv_read( gpointer data, gint fd, b_input_condition cond ) if( ( ret == -1 ) && ( errno == EAGAIN ) ) { - tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_recv_read, bt ); + tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt ); return FALSE; } } @@ -706,7 +706,7 @@ gboolean jabber_bs_send_write( file_transfer_t *ft, char *buffer, unsigned int l if( tf->byteswritten >= ft->file_size ) imcb_file_finished( ft ); else - bt->tf->watch_out = b_input_add( tf->fd, GAIM_INPUT_WRITE, jabber_bs_send_can_write, bt ); + bt->tf->watch_out = b_input_add( tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt ); return TRUE; } @@ -917,7 +917,7 @@ void jabber_si_set_proxies( struct bs_transfer *bt ) strcpy( sh->port, port ); bt->streamhosts = g_slist_append( bt->streamhosts, sh ); - bt->tf->watch_in = b_input_add( tf->fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); bt->connect_timeout = b_timeout_add( JABBER_BS_LISTEN_TIMEOUT * 1000, jabber_bs_connect_timeout, bt ); } else { imcb_log( tf->ic, "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s", @@ -1054,7 +1054,7 @@ gboolean jabber_bs_send_handshake( gpointer data, gint fd, b_input_condition con bt->phase = BS_PHASE_CONNECTED; - bt->tf->watch_in = b_input_add( fd, GAIM_INPUT_READ, jabber_bs_send_handshake, bt ); + bt->tf->watch_in = b_input_add( fd, B_EV_IO_READ, jabber_bs_send_handshake, bt ); return FALSE; } case BS_PHASE_CONNECTED: diff --git a/protocols/msn/invitation.c b/protocols/msn/invitation.c index d2b2a5c8..9f8b9a6e 100644 --- a/protocols/msn/invitation.c +++ b/protocols/msn/invitation.c @@ -208,7 +208,7 @@ gboolean msn_ftps_connected( gpointer data, gint fd, b_input_condition cond ) fd = msn_file->fd; sock_make_nonblocking( fd ); - msn_file->r_event_id = b_input_add( fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -229,7 +229,7 @@ void msn_invitations_accept( msn_filetransfer_t *msn_file, struct msn_switchboar return; } - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftps_connected, file ); + msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftps_connected, file ); g_snprintf( buf, sizeof( buf ), "IP-Address: %s\r\n" @@ -317,7 +317,7 @@ gboolean msn_ftp_connected( gpointer data, gint fd, b_input_condition cond ) return FALSE; sock_make_nonblocking( msn_file->fd ); - msn_file->r_event_id = b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + msn_file->r_event_id = b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return FALSE; } @@ -414,7 +414,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) if( ( msn_file->sbufpos < MSNFTP_PSIZE ) && ( msn_file->data_sent + msn_file->sbufpos - 3 < file->file_size ) ) { if( !msn_file->w_event_id ) - msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); return TRUE; } @@ -451,7 +451,7 @@ gboolean msn_ftps_write( file_transfer_t *file, char *buffer, unsigned int len ) } else { /* we might already be listening if this is data from an overflow */ if( !msn_file->w_event_id ) - msn_file->w_event_id = b_input_add( msn_file->fd, GAIM_INPUT_WRITE, msn_ftp_send, file ); + msn_file->w_event_id = b_input_add( msn_file->fd, B_EV_IO_WRITE, msn_ftp_send, file ); } return TRUE; @@ -616,7 +616,7 @@ gboolean msn_ftpr_write_request( file_transfer_t *file ) } msn_file->r_event_id = - b_input_add( msn_file->fd, GAIM_INPUT_READ, msn_ftp_read, file ); + b_input_add( msn_file->fd, B_EV_IO_READ, msn_ftp_read, file ); return TRUE; } -- cgit v1.2.3 From f60079b74053a53da3720b992c603fddf75ff1dd Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:26:54 +0100 Subject: Allow one to run the configure script from a different directory and put all build files in there. I need this to properly make Debian package variants (i.e. libpurple and native). --- Makefile | 2 +- bitlbee.h | 2 +- configure | 24 +++++++++++++++++++++++- doc/Makefile | 3 +++ doc/user-guide/Makefile | 4 ++++ lib/Makefile | 5 ++++- protocols/Makefile | 5 ++++- protocols/jabber/Makefile | 5 ++++- protocols/msn/Makefile | 5 ++++- protocols/oscar/Makefile | 6 +++++- protocols/purple/Makefile | 5 ++++- protocols/twitter/Makefile | 5 ++++- protocols/yahoo/Makefile | 5 ++++- tests/Makefile | 5 ++++- 14 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Makefile b/Makefile index 916f551a..2bbafc57 100644 --- a/Makefile +++ b/Makefile @@ -109,7 +109,7 @@ tar: $(subdirs): @$(MAKE) -C $@ $(MAKECMDGOALS) -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/bitlbee.h b/bitlbee.h index f4e03d76..1dc434ec 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -42,7 +42,7 @@ #define MAX_STRING 511 #if HAVE_CONFIG_H -#include "config.h" +#include #endif #include diff --git a/configure b/configure index e0cf6f18..54156a61 100755 --- a/configure +++ b/configure @@ -123,6 +123,28 @@ LFLAGS= EFLAGS= EOF +srcdir="$(dirname $0)" +if [ "$srcdir" != "." ]; then + echo + echo "configure script run from a different directory. Will create some symlinks..." + if [ ! -e Makefile -o -L Makefile ]; then + mkdir -p $(cd "$srcdir"; find . -type d) + find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null + dst="$PWD" + cd "$srcdir" + for i in $(find . -name Makefile); do + ln -s "$PWD${i#.}" "$dst/$i"; + done + cd "$dst" + rm -rf .bzr + fi + + echo "SRCDIR=$srcdir/" >> Makefile.settings + CFLAGS="$CFLAGS -I${dst}" +else + srcdir=$PWD +fi + cat<config.h /* BitlBee settings, generated by configure @@ -160,7 +182,7 @@ else fi echo CFLAGS=$CFLAGS >> Makefile.settings -echo CFLAGS+=-I`pwd` -I`pwd`/lib -I`pwd`/protocols -I. >> Makefile.settings +echo CFLAGS+=-I${srcdir} -I${srcdir}/lib -I${srcdir}/protocols -I. >> Makefile.settings echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings diff --git a/doc/Makefile b/doc/Makefile index 9b473df3..aeebd901 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -1,4 +1,7 @@ -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)doc/ +endif all: # Only build the docs if this is a bzr checkout diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile index 9841de8d..eaf2a5ff 100644 --- a/doc/user-guide/Makefile +++ b/doc/user-guide/Makefile @@ -1,4 +1,8 @@ -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)doc/user-guide/ +endif + EXTRAPARANEWLINE = 1 # EXTRAPARANEWLINE = 0 diff --git a/lib/Makefile b/lib/Makefile index b686f886..8fd9b19e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)lib/ +endif # [SH] Program variables objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o @@ -36,6 +39,6 @@ lib.o: $(objects) $(subdirs) $(objects): ../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/Makefile b/protocols/Makefile index 18d79e8d..57fcd7eb 100644 --- a/protocols/Makefile +++ b/protocols/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/ +endif # [SH] Program variables objects = nogaim.o @@ -48,6 +51,6 @@ protocols.o: $(objects) $(subdirs) $(objects): ../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile index 78a02696..912ea702 100644 --- a/protocols/jabber/Makefile +++ b/protocols/jabber/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/jabber/ +endif # [SH] Program variables objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile index 5d199b9e..1de755a8 100644 --- a/protocols/msn/Makefile +++ b/protocols/msn/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/msn/ +endif # [SH] Program variables objects = invitation.o msn.o msn_util.o ns.o passport.o sb.o tables.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile index 2792f22a..0ec7436b 100644 --- a/protocols/oscar/Makefile +++ b/protocols/oscar/Makefile @@ -7,6 +7,10 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/oscar/ +CFLAGS += -I$(SRCDIR) +endif # [SH] Program variables objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o @@ -32,7 +36,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/purple/Makefile b/protocols/purple/Makefile index 403db799..97a5bb6a 100644 --- a/protocols/purple/Makefile +++ b/protocols/purple/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/purple/ +endif # [SH] Program variables objects = ft.o purple.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/twitter/Makefile b/protocols/twitter/Makefile index ca1e4695..8a4b97f9 100644 --- a/protocols/twitter/Makefile +++ b/protocols/twitter/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/twitter/ +endif # [SH] Program variables objects = twitter.o twitter_http.o twitter_lib.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile index b4fe56e2..20ecce71 100644 --- a/protocols/yahoo/Makefile +++ b/protocols/yahoo/Makefile @@ -7,6 +7,9 @@ ### DEFINITIONS -include ../../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)protocols/yahoo/ +endif # [SH] Program variables objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_util.o @@ -32,7 +35,7 @@ distclean: clean $(objects): ../../Makefile.settings Makefile -$(objects): %.o: %.c +$(objects): %.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/tests/Makefile b/tests/Makefile index 1bcf8f72..7c876cec 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,7 @@ -include ../Makefile.settings +ifdef SRCDIR +SRCDIR := $(SRCDIR)tests/ +endif LFLAGS +=-lcheck @@ -18,6 +21,6 @@ check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../l @echo '*' Linking $@ @$(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(EFLAGS) -%.o: %.c +%.o: $(SRCDIR)%.c @echo '*' Compiling $< @$(CC) -c $(CFLAGS) $< -o $@ -- cgit v1.2.3 From 4af305032ba6913449cbd9160db1b6ab228f0a9a Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 25 May 2010 23:41:52 +0100 Subject: install-* targets should also work now. Let's see how this works out in a deb. --- Makefile | 9 +++++---- doc/Makefile | 4 ++-- doc/user-guide/Makefile | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 2bbafc57..222737b2 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ # Program variables objects = account.o bitlbee.o chat.o dcc.o help.o ipc.o irc.o irc_commands.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) user.o -headers = account.h bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h +headers = account.h bitlbee.h commands.h conf.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h user.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/ft.h protocols/nogaim.h subdirs = lib protocols ifeq ($(TARGET),i586-mingw32msvc) @@ -81,7 +81,8 @@ uninstall-bin: install-dev: mkdir -p $(DESTDIR)$(INCLUDEDIR) - install -m 0644 $(headers) $(DESTDIR)$(INCLUDEDIR) + install -m 0644 config.h $(DESTDIR)$(INCLUDEDIR) + for i in $(headers); do install -m 0644 $(SRCDIR)$$i $(DESTDIR)$(INCLUDEDIR); done mkdir -p $(DESTDIR)$(PCDIR) install -m 0644 bitlbee.pc $(DESTDIR)$(PCDIR) @@ -92,8 +93,8 @@ uninstall-dev: install-etc: mkdir -p $(DESTDIR)$(ETCDIR) - install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt - install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf + install -m 0644 $(SRCDIR)motd.txt $(DESTDIR)$(ETCDIR)/motd.txt + install -m 0644 $(SRCDIR)bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf uninstall-etc: rm -f $(DESTDIR)$(ETCDIR)/motd.txt diff --git a/doc/Makefile b/doc/Makefile index aeebd901..5f59879e 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -9,8 +9,8 @@ all: install: mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(DESTDIR)$(MANDIR)/man5/ - install -m 0644 bitlbee.8 $(DESTDIR)$(MANDIR)/man8/ - install -m 0644 bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/ + install -m 0644 $(SRCDIR)bitlbee.8 $(DESTDIR)$(MANDIR)/man8/ + install -m 0644 $(SRCDIR)bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/ $(MAKE) -C user-guide $@ uninstall: diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile index eaf2a5ff..2a80ea6c 100644 --- a/doc/user-guide/Makefile +++ b/doc/user-guide/Makefile @@ -41,7 +41,7 @@ install: mkdir -p $(DESTDIR)$(DATADIR) chmod 0755 $(DESTDIR)$(DATADIR) rm -f $(DESTDIR)$(DATADIR)/help.txt # Prevent help function from breaking in running sessions - install -m 0644 help.txt $(DESTDIR)$(DATADIR)/help.txt + install -m 0644 $(SRCDIR)help.txt $(DESTDIR)$(DATADIR)/help.txt uninstall: rm -f $(DESTDIR)$(DATADIR)/help.txt -- cgit v1.2.3 From 04dc563f2a30476bf96e72ce3e66cb5a8b2606d3 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 11:32:02 +0100 Subject: A few more configure script tweaks: Don't enable libpurple by default anymore (in preparation for a merge), and limit the directory replication stuff. --- configure | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 54156a61..232e0cdc 100755 --- a/configure +++ b/configure @@ -26,7 +26,7 @@ jabber=1 oscar=1 yahoo=1 twitter=1 -purple=1 +purple=0 debug=0 strip=1 @@ -128,11 +128,12 @@ if [ "$srcdir" != "." ]; then echo echo "configure script run from a different directory. Will create some symlinks..." if [ ! -e Makefile -o -L Makefile ]; then - mkdir -p $(cd "$srcdir"; find . -type d) + COPYDIRS="doc lib protocols tests utils" + mkdir -p $(cd "$srcdir"; find $COPYDIRS -type d) find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null dst="$PWD" cd "$srcdir" - for i in $(find . -name Makefile); do + for i in $(find . -name Makefile -type f); do ln -s "$PWD${i#.}" "$dst/$i"; done cd "$dst" -- cgit v1.2.3 From 095a5f046c36c4cff689305fef81533a9e9603fc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 15:47:54 +0100 Subject: Redid debian/rules using debhelper, with good results. This creates mostly fine debs. Need to do some more checks, and make sure bitlbee-libpurple gets the same maintainer scripts as bitlbee. --- debian/bitlbee-common.docs | 6 ++ debian/bitlbee-common.examples | 1 + debian/changelog | 9 +++ debian/compat | 1 + debian/conffiles | 3 - debian/control | 29 ++++++- debian/patches/bitlbee.conf.diff | 4 +- debian/rules | 163 ++++++++++++++++++--------------------- 8 files changed, 120 insertions(+), 96 deletions(-) create mode 100644 debian/bitlbee-common.docs create mode 100644 debian/bitlbee-common.examples create mode 100644 debian/compat delete mode 100644 debian/conffiles diff --git a/debian/bitlbee-common.docs b/debian/bitlbee-common.docs new file mode 100644 index 00000000..72ff657c --- /dev/null +++ b/debian/bitlbee-common.docs @@ -0,0 +1,6 @@ +doc/user-guide/user-guide.txt +doc/user-guide/user-guide.html +doc/AUTHORS +doc/CREDITS +doc/FAQ +doc/README diff --git a/debian/bitlbee-common.examples b/debian/bitlbee-common.examples new file mode 100644 index 00000000..81562b9e --- /dev/null +++ b/debian/bitlbee-common.examples @@ -0,0 +1 @@ +utils/* diff --git a/debian/changelog b/debian/changelog index 56d0a551..ffb23ed8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +bitlbee (1.3-0) unstable; urgency=low + + * Setting some bogus version number, fix that later. + * Now using debhelper to improve maintainability. + * Added a bitlbee-libpurple package, and split off docs and stuff into + bitlbee-common. + + -- Wilmer van der Gaast Sat, 05 Jun 2010 15:16:38 +0100 + bitlbee (1.2.6a-1) unstable; urgency=low * New upstream version. diff --git a/debian/compat b/debian/compat new file mode 100644 index 00000000..7f8f011e --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/conffiles b/debian/conffiles deleted file mode 100644 index dcb4078e..00000000 --- a/debian/conffiles +++ /dev/null @@ -1,3 +0,0 @@ -/etc/bitlbee/motd.txt -/etc/bitlbee/bitlbee.conf -/etc/init.d/bitlbee diff --git a/debian/control b/debian/control index 8f861aeb..dfef8e39 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: net Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij -Standards-Version: 3.8.0 +Standards-Version: 3.8.4 Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ @@ -11,11 +11,36 @@ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, ${debconf-depends}, debianutils (>= 1.16) +Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. +Package: bitlbee-libpurple +Architecture: any +Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Description: An IRC to other chat networks gateway + This program can be used as an IRC server which forwards everything you + say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. + . + This package contains a version of BitlBee that uses the libpurple instant + messaging library instead of built-in code, which adds support for more IM + protocols (all protocols supported by Pidgin/Finch) and features (like file + transfers), at the price of being less lightweight. + . + This variant may not be very suitable for BitlBee instances used by many + (tens or hundreds) of clients. + +Package: bitlbee-common +Architecture: all +Replaces: bitlbee (<= 1.3) +Description: An IRC to other chat networks gateway + This program can be used as an IRC server which forwards everything you + say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. + . + This package contains common files (mostly documentation) for bitlbee and + bitlbee-libpurple. + Package: bitlbee-dev Architecture: all Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) diff --git a/debian/patches/bitlbee.conf.diff b/debian/patches/bitlbee.conf.diff index c98fa546..339ccd4a 100644 --- a/debian/patches/bitlbee.conf.diff +++ b/debian/patches/bitlbee.conf.diff @@ -1,5 +1,5 @@ ---- debian/bitlbee/etc/bitlbee/bitlbee.conf 2009-06-01 00:20:24.000000000 +0100 -+++ debian/bitlbee/etc/bitlbee/bitlbee.conf 2009-06-07 21:16:19.000000000 +0100 +--- bitlbee.conf 2009-06-01 00:20:24.000000000 +0100 ++++ bitlbee.conf 2009-06-07 21:16:19.000000000 +0100 @@ -23,13 +23,18 @@ ## If BitlBee is started by root as a daemon, it can drop root privileges, ## and change to the specified user. diff --git a/debian/rules b/debian/rules index df129f98..bbb368f9 100755 --- a/debian/rules +++ b/debian/rules @@ -1,4 +1,11 @@ #!/usr/bin/make -f +# +# Finally switching to debhelper. +# +# Not using debhelper was an exercise suggested to me by my AM (Gergely +# Nagy). It was educating at the time but I finally decided that the +# exercise is over now. +# BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 @@ -7,104 +14,82 @@ ifdef BITLBEE_VERSION BITLBEE_FORCE_VERSION=1 else # Want to use the full package version number instead of just the release. -BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')" -export BITLBEE_VERSION +BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION=\"$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')\" endif -build-arch: build-arch-stamp -build-arch-stamp: - [ -d debian ] - ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) - $(MAKE) -# $(MAKE) -C doc/ all - touch build-arch-stamp +build: build-stamp +build-stamp: + dh_testdir -clean: - [ "`whoami`" = "root" -a -d debian ] - rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files debian/bitlbee-dev - $(MAKE) distclean -# -$(MAKE) -C doc/ clean - - -install-arch: build-arch - [ "`whoami`" = "root" -a -d debian ] - mkdir -p debian/bitlbee/DEBIAN/ - $(MAKE) install install-etc DESTDIR=`pwd`/debian/bitlbee - - mkdir -p debian/bitlbee/usr/share/doc/bitlbee/ - cp doc/user-guide/user-guide.txt debian/bitlbee/usr/share/doc/bitlbee/ - cp doc/user-guide/user-guide.html debian/bitlbee/usr/share/doc/bitlbee/ - -install-indep: install-arch - [ "`whoami`" = "root" -a -d debian ] - mkdir -p debian/bitlbee-dev/DEBIAN/ - $(MAKE) install-dev DESTDIR=`pwd`/debian/bitlbee-dev - - mkdir -p debian/bitlbee-dev/usr/share/doc/bitlbee-dev/ - -binary-arch: build-arch install-arch - [ "`whoami`" = "root" -a -d debian ] - - chmod 755 debian/post* debian/pre* debian/config debian/bitlbee.init - - mkdir -p debian/bitlbee/usr/share/doc/bitlbee/examples/ debian/bitlbee/etc/init.d/ - -cp doc/RELEASE-SPEECH* debian/bitlbee/usr/share/doc/bitlbee/ && gzip -9 debian/bitlbee/usr/share/doc/bitlbee/RELEASE-SPEECH* - cp doc/CREDITS doc/AUTHORS doc/README doc/FAQ debian/README.Debian debian/bitlbee/usr/share/doc/bitlbee/ - cp debian/changelog debian/bitlbee/usr/share/doc/bitlbee/changelog.Debian - cp debian/copyright debian/bitlbee/usr/share/doc/bitlbee/copyright - cp doc/CHANGES debian/bitlbee/usr/share/doc/bitlbee/changelog - cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/ - cp debian/bitlbee.init debian/bitlbee/etc/init.d/bitlbee - patch -p0 < debian/patches/bitlbee.conf.diff - cd debian/bitlbee/usr/share/; \ - gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \ - doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5 - - chown -R root:root debian/bitlbee/ - find debian/bitlbee/usr/share/ -type d -exec chmod 755 {} \; - find debian/bitlbee/usr/share/ -type f -exec chmod 644 {} \; - - cp debian/prerm debian/bitlbee/DEBIAN/ - cp debian/postinst debian/bitlbee/DEBIAN/ - cp debian/postrm debian/bitlbee/DEBIAN/ - cp debian/config debian/bitlbee/DEBIAN/ - - po2debconf debian/templates > debian/bitlbee/DEBIAN/templates - cp debian/conffiles debian/bitlbee/DEBIAN/ - - if [ "$(DEBUG)" = "0" ]; then strip -R .comment -R .note debian/bitlbee/usr/sbin/bitlbee; fi - - cd debian/bitlbee; \ - find usr -type f -exec md5sum {} \; > DEBIAN/md5sums - dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee -ifdef BITLBEE_FORCE_VERSION - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0' -else - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0' -endif + mkdir debian/build-native + ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) + $(MAKE) -C debian/build-native - dpkg --build debian/bitlbee .. + mkdir debian/build-libpurple + ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS) + $(MAKE) -C debian/build-libpurple -binary-indep: install-indep - [ "`whoami`" = "root" -a -d debian ] + touch build-stamp - chown -R root.root debian/bitlbee-dev/ - find debian/bitlbee-dev/usr/share/ -type d -exec chmod 755 {} \; - find debian/bitlbee-dev/usr/share/ -type f -exec chmod 644 {} \; +clean: + dh_testdir + dh_testroot + rm -f build-stamp - cp debian/changelog debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian - gzip -9 debian/bitlbee-dev/usr/share/doc/bitlbee-dev/changelog.Debian - cp debian/copyright debian/bitlbee-dev/usr/share/doc/bitlbee-dev/copyright + rm -rf build-arch-stamp debian/build-* + $(MAKE) distclean - cd debian/bitlbee-dev; \ - find usr -type f -exec md5sum {} \; > DEBIAN/md5sums + dh_clean + +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + + $(MAKE) -C debian/build-native install install-etc DESTDIR=`pwd`/debian/bitlbee + $(MAKE) -C debian/build-libpurple install install-etc DESTDIR=`pwd`/debian/bitlbee-libpurple + $(MAKE) -C debian/build-native install-dev DESTDIR=`pwd`/debian/bitlbee-dev + + patch debian/bitlbee/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff + patch debian/bitlbee-libpurple/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff + + mkdir -p debian/bitlbee-common/usr + mv debian/bitlbee/usr/share debian/bitlbee-common/usr + rm -rf debian/bitlbee-libpurple/usr/share + +binary-common: + dh_testdir + dh_testroot + + dh_installdocs --link-doc=bitlbee-common + dh_installchangelogs + dh_installexamples + dh_installdebconf + dh_installinit + dh_installman + dh_strip + dh_link + dh_compress + dh_fixperms + dh_installdeb + dh_shlibdeps +ifdef BITLBEE_FORCE_VERSION + dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 +else + dh_gencontrol +endif + dh_md5sums + dh_builddeb - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0 +binary-indep: build install + $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common - dpkg --build debian/bitlbee-dev .. +binary-arch: build install + $(MAKE) -f debian/rules DH_OPTIONS=-a binary-common -binary: binary-arch binary-indep -build: build-arch -install: install-arch install-indep +binary-%: build install + make -f debian/rules binary-common DH_OPTIONS=-p$* -.PHONY: build-arch build clean binary-arch binary install-arch install binary-indep install-indep +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary-common binary install -- cgit v1.2.3 From 4c0388134ff88b72e343bd07013554480c2a4ef7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 17:39:58 +0100 Subject: Remaining fixes: All looks good now, apt and dpkg seem to do the right thing with the -common transition, etc. --- debian/bitlbee-common.config | 25 ++++++++++ debian/bitlbee-common.templates | 8 ++++ debian/bitlbee.init | 0 debian/bitlbee.postinst | 102 ++++++++++++++++++++++++++++++++++++++++ debian/bitlbee.postrm | 14 ++++++ debian/bitlbee.prerm | 17 +++++++ debian/config | 25 ---------- debian/control | 15 ++++-- debian/postinst | 102 ---------------------------------------- debian/postrm | 14 ------ debian/prerm | 17 ------- debian/rules | 12 +++-- debian/templates | 8 ---- 13 files changed, 184 insertions(+), 175 deletions(-) create mode 100644 debian/bitlbee-common.config create mode 100644 debian/bitlbee-common.templates mode change 100755 => 100644 debian/bitlbee.init create mode 100644 debian/bitlbee.postinst create mode 100644 debian/bitlbee.postrm create mode 100644 debian/bitlbee.prerm delete mode 100755 debian/config delete mode 100755 debian/postinst delete mode 100755 debian/postrm delete mode 100755 debian/prerm delete mode 100644 debian/templates diff --git a/debian/bitlbee-common.config b/debian/bitlbee-common.config new file mode 100644 index 00000000..9bb78237 --- /dev/null +++ b/debian/bitlbee-common.config @@ -0,0 +1,25 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule +[ -f /etc/default/bitlbee ] && . /etc/default/bitlbee + +db_title BitlBee + +if [ -n "$BITLBEE_PORT" ]; then + db_set bitlbee/serveport "$BITLBEE_PORT" +else + db_get bitlbee/serveport + if [ "$RET" = "stillhavetoask" ]; then + listens=$(netstat -ltn | awk '{print $4}') + for port in 6667 6666 6668 6669; do + if [ $(expr "$listens " : ".*:$port\s") = "0" ]; then + break + fi + done + db_set bitlbee/serveport $port; + fi +fi + +if db_input medium bitlbee/serveport; then + db_go; +fi diff --git a/debian/bitlbee-common.templates b/debian/bitlbee-common.templates new file mode 100644 index 00000000..0cd04426 --- /dev/null +++ b/debian/bitlbee-common.templates @@ -0,0 +1,8 @@ +Template: bitlbee/serveport +Type: string +Default: stillhavetoask +_Description: On what TCP port should BitlBee listen for connections? + BitlBee normally listens on the regular IRC port, 6667. This might not be + a very good idea when you're running a real IRC daemon as well. 6668 might + be a good alternative. Leaving this value blank means that BitlBee will not + be run automatically. diff --git a/debian/bitlbee.init b/debian/bitlbee.init old mode 100755 new mode 100644 diff --git a/debian/bitlbee.postinst b/debian/bitlbee.postinst new file mode 100644 index 00000000..db541f6c --- /dev/null +++ b/debian/bitlbee.postinst @@ -0,0 +1,102 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule + +db_get bitlbee/serveport +PORT="$RET" + +CONFDIR=/var/lib/bitlbee/ + +update-rc.d bitlbee defaults > /dev/null 2>&1 + +## Load default option. Don't want to put this in debconf (yet?) +BITLBEE_OPTS=-F +BITLBEE_DISABLED=0 +BITLBEE_UPGRADE_DONT_RESTART=0 +[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee + +if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null && + ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then + ## Make sure the inetd entry is gone (can still be there from a + ## previous version. + update-inetd --remove '.*/usr/sbin/bitlbee' + if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then + # Thanks for breaking update-inetd! (bugs.debian.org/311111) + # I hope that it works at least with xinetd, because this + # emergency hack doesn't: + perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf + killall -HUP inetd || true + fi +fi + +cat</etc/default/bitlbee +## /etc/default/bitlbee: Auto-generated/updated script. +## +## If running in (fork)daemon mode, listen on this TCP port. +BITLBEE_PORT="$PORT" + +## Use single-process or forking daemon mode? Can't be changed from debconf, +## but maintainer scripts will save your changes here. +BITLBEE_OPTS="$BITLBEE_OPTS" + +## In case you want to stick with inetd mode (or if you just want to disable +## the init scripts for some other reason), you can disable the init script +## here. (Just set it to 1) +BITLBEE_DISABLED=$BITLBEE_DISABLED + +## As a server operator, you can use the RESTART command to restart only the +## master process while keeping all the child processes and their IPC +## connections. By enabling this, the maintainer scripts won't restart +## BitlBee during upgrades so you can restart the master process by hand. +BITLBEE_UPGRADE_DONT_RESTART=$BITLBEE_UPGRADE_DONT_RESTART +EOF + +## Bye-bye DebConf, we don't need you anymore. +db_stop + +## Restore the helpfile in case we weren't upgrading but just reconfiguring: +if [ -e /usr/share/bitlbee/help.upgrading ]; then + if [ -e /usr/share/bitlbee/help.txt ]; then + rm -f /usr/share/bitlbee/help.upgrading + else + mv /usr/share/bitlbee/help.upgrading /usr/share/bitlbee/help.txt + fi +fi + +if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee restart + else + /etc/init.d/bitlbee restart + fi +fi + +## If we're upgrading, we'll probably skip this next part +if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then + echo 'BitlBee (probably) already installed, skipping user/configdir installation' + exit 0 +fi + +adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee +chmod 700 /var/lib/bitlbee/ + +## Can't do this in packaging phase: Don't know the UID yet. Access to +## the file should be limited, now that it stores passwords. Added +## --group later for a little more security, but have to see if I can +## apply this change to existing installations on upgrades. Will think +## about that later. +if getent group bitlbee > /dev/null; then + chmod 640 /etc/bitlbee/bitlbee.conf + chown root:bitlbee /etc/bitlbee/bitlbee.conf +else + chmod 600 /etc/bitlbee/bitlbee.conf + chown bitlbee /etc/bitlbee/bitlbee.conf +fi + +if [ -z "$2" ]; then + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee start + else + /etc/init.d/bitlbee start + fi +fi diff --git a/debian/bitlbee.postrm b/debian/bitlbee.postrm new file mode 100644 index 00000000..5c3b4b2e --- /dev/null +++ b/debian/bitlbee.postrm @@ -0,0 +1,14 @@ +#!/bin/sh -e + +[ "$1" = "purge" ] || exit 0 + +if [ -e /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule; + db_purge; +fi + +update-rc.d bitlbee remove > /dev/null 2>&1 || true +rm -f /etc/default/bitlbee + +deluser --system --remove-home bitlbee || true +rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var diff --git a/debian/bitlbee.prerm b/debian/bitlbee.prerm new file mode 100644 index 00000000..687c2cc1 --- /dev/null +++ b/debian/bitlbee.prerm @@ -0,0 +1,17 @@ +#!/bin/sh -e + +if [ "$1" = "upgrade" ]; then + ## To prevent the help function from breaking in currently running + ## BitlBee processes. Have to do it like this because dpkg-reconfigure + ## looks a lot like an upgrade and we don't want to lose help.txt... + if [ -e /usr/share/bitlbee/help.txt ]; then + rm -f /usr/share/bitlbee/help.upgrading + mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading + fi +else + if which invoke-rc.d >/dev/null 2>&1; then + invoke-rc.d bitlbee stop || exit 0 + else + /etc/init.d/bitlbee stop || exit 0 + fi +fi diff --git a/debian/config b/debian/config deleted file mode 100755 index 9bb78237..00000000 --- a/debian/config +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule -[ -f /etc/default/bitlbee ] && . /etc/default/bitlbee - -db_title BitlBee - -if [ -n "$BITLBEE_PORT" ]; then - db_set bitlbee/serveport "$BITLBEE_PORT" -else - db_get bitlbee/serveport - if [ "$RET" = "stillhavetoask" ]; then - listens=$(netstat -ltn | awk '{print $4}') - for port in 6667 6666 6668 6669; do - if [ $(expr "$listens " : ".*:$port\s") = "0" ]; then - break - fi - done - db_set bitlbee/serveport $port; - fi -fi - -if db_input medium bitlbee/serveport; then - db_go; -fi diff --git a/debian/control b/debian/control index dfef8e39..62cb2ec0 100644 --- a/debian/control +++ b/debian/control @@ -4,21 +4,25 @@ Priority: optional Maintainer: Wilmer van der Gaast Uploaders: Jelmer Vernooij Standards-Version: 3.8.4 -Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf, libpurple-dev +Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), po-debconf, libpurple-dev, debhelper (>= 7) Homepage: http://www.bitlbee.org/ Vcs-Bzr: http://code.bitlbee.org/bitlbee/ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Conflicts: bitlbee-libpurple +Replaces: bitlbee-libpurple Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. Package: bitlbee-libpurple Architecture: any -Depends: ${shlibs:Depends}, adduser, net-tools, debianutils (>= 1.16), bitlbee-common (>= ${source:Version}), bitlbee-common (<< ${source:Version}.1~) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Conflicts: bitlbee +Replaces: bitlbee Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -33,7 +37,8 @@ Description: An IRC to other chat networks gateway Package: bitlbee-common Architecture: all -Replaces: bitlbee (<= 1.3) +Depends: ${misc:Depends}, net-tools +Replaces: bitlbee Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -43,7 +48,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all -Depends: bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) +Depends: ${misc:Depends}, bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. diff --git a/debian/postinst b/debian/postinst deleted file mode 100755 index db541f6c..00000000 --- a/debian/postinst +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/sh -e - -. /usr/share/debconf/confmodule - -db_get bitlbee/serveport -PORT="$RET" - -CONFDIR=/var/lib/bitlbee/ - -update-rc.d bitlbee defaults > /dev/null 2>&1 - -## Load default option. Don't want to put this in debconf (yet?) -BITLBEE_OPTS=-F -BITLBEE_DISABLED=0 -BITLBEE_UPGRADE_DONT_RESTART=0 -[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee - -if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null && - ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then - ## Make sure the inetd entry is gone (can still be there from a - ## previous version. - update-inetd --remove '.*/usr/sbin/bitlbee' - if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then - # Thanks for breaking update-inetd! (bugs.debian.org/311111) - # I hope that it works at least with xinetd, because this - # emergency hack doesn't: - perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf - killall -HUP inetd || true - fi -fi - -cat</etc/default/bitlbee -## /etc/default/bitlbee: Auto-generated/updated script. -## -## If running in (fork)daemon mode, listen on this TCP port. -BITLBEE_PORT="$PORT" - -## Use single-process or forking daemon mode? Can't be changed from debconf, -## but maintainer scripts will save your changes here. -BITLBEE_OPTS="$BITLBEE_OPTS" - -## In case you want to stick with inetd mode (or if you just want to disable -## the init scripts for some other reason), you can disable the init script -## here. (Just set it to 1) -BITLBEE_DISABLED=$BITLBEE_DISABLED - -## As a server operator, you can use the RESTART command to restart only the -## master process while keeping all the child processes and their IPC -## connections. By enabling this, the maintainer scripts won't restart -## BitlBee during upgrades so you can restart the master process by hand. -BITLBEE_UPGRADE_DONT_RESTART=$BITLBEE_UPGRADE_DONT_RESTART -EOF - -## Bye-bye DebConf, we don't need you anymore. -db_stop - -## Restore the helpfile in case we weren't upgrading but just reconfiguring: -if [ -e /usr/share/bitlbee/help.upgrading ]; then - if [ -e /usr/share/bitlbee/help.txt ]; then - rm -f /usr/share/bitlbee/help.upgrading - else - mv /usr/share/bitlbee/help.upgrading /usr/share/bitlbee/help.txt - fi -fi - -if [ -n "$2" -a "$BITLBEE_UPGRADE_DONT_RESTART" != "1" ]; then - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee restart - else - /etc/init.d/bitlbee restart - fi -fi - -## If we're upgrading, we'll probably skip this next part -if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then - echo 'BitlBee (probably) already installed, skipping user/configdir installation' - exit 0 -fi - -adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee -chmod 700 /var/lib/bitlbee/ - -## Can't do this in packaging phase: Don't know the UID yet. Access to -## the file should be limited, now that it stores passwords. Added -## --group later for a little more security, but have to see if I can -## apply this change to existing installations on upgrades. Will think -## about that later. -if getent group bitlbee > /dev/null; then - chmod 640 /etc/bitlbee/bitlbee.conf - chown root:bitlbee /etc/bitlbee/bitlbee.conf -else - chmod 600 /etc/bitlbee/bitlbee.conf - chown bitlbee /etc/bitlbee/bitlbee.conf -fi - -if [ -z "$2" ]; then - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee start - else - /etc/init.d/bitlbee start - fi -fi diff --git a/debian/postrm b/debian/postrm deleted file mode 100755 index 5c3b4b2e..00000000 --- a/debian/postrm +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh -e - -[ "$1" = "purge" ] || exit 0 - -if [ -e /usr/share/debconf/confmodule ]; then - . /usr/share/debconf/confmodule; - db_purge; -fi - -update-rc.d bitlbee remove > /dev/null 2>&1 || true -rm -f /etc/default/bitlbee - -deluser --system --remove-home bitlbee || true -rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var diff --git a/debian/prerm b/debian/prerm deleted file mode 100755 index 687c2cc1..00000000 --- a/debian/prerm +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -e - -if [ "$1" = "upgrade" ]; then - ## To prevent the help function from breaking in currently running - ## BitlBee processes. Have to do it like this because dpkg-reconfigure - ## looks a lot like an upgrade and we don't want to lose help.txt... - if [ -e /usr/share/bitlbee/help.txt ]; then - rm -f /usr/share/bitlbee/help.upgrading - mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading - fi -else - if which invoke-rc.d >/dev/null 2>&1; then - invoke-rc.d bitlbee stop || exit 0 - else - /etc/init.d/bitlbee stop || exit 0 - fi -fi diff --git a/debian/rules b/debian/rules index bbb368f9..8ccb181b 100755 --- a/debian/rules +++ b/debian/rules @@ -10,9 +10,7 @@ BITLBEE_CONFIGURE_FLAGS ?= DEBUG ?= 0 -ifdef BITLBEE_VERSION -BITLBEE_FORCE_VERSION=1 -else +ifndef BITLBEE_VERSION # Want to use the full package version number instead of just the release. BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION=\"$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')\" endif @@ -67,14 +65,20 @@ binary-common: dh_installexamples dh_installdebconf dh_installinit +ifeq ($(DH_OPTIONS),-a) + cp -a debian/bitlbee/etc debian/bitlbee-libpurple +endif dh_installman dh_strip dh_link dh_compress dh_fixperms dh_installdeb +ifeq ($(DH_OPTIONS),-a) + cp -a debian/bitlbee/DEBIAN/{post,pre}* debian/bitlbee-libpurple/DEBIAN +endif dh_shlibdeps -ifdef BITLBEE_FORCE_VERSION +ifdef BITLBEE_VERSION dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 else dh_gencontrol diff --git a/debian/templates b/debian/templates deleted file mode 100644 index 0cd04426..00000000 --- a/debian/templates +++ /dev/null @@ -1,8 +0,0 @@ -Template: bitlbee/serveport -Type: string -Default: stillhavetoask -_Description: On what TCP port should BitlBee listen for connections? - BitlBee normally listens on the regular IRC port, 6667. This might not be - a very good idea when you're running a real IRC daemon as well. 6668 might - be a good alternative. Leaving this value blank means that BitlBee will not - be run automatically. -- cgit v1.2.3 From 477250038a91c6877838cbd7f0d32b839f64824d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 18:35:39 +0100 Subject: Put in the necessary hacks to make version number spoofing work again. --- debian/control | 6 +++--- debian/rules | 11 ++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/debian/control b/debian/control index 62cb2ec0..d197dcdd 100644 --- a/debian/control +++ b/debian/control @@ -11,7 +11,7 @@ DM-Upload-Allowed: yes Package: bitlbee Architecture: any -Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee-libpurple Replaces: bitlbee-libpurple Description: An IRC to other chat networks gateway @@ -20,7 +20,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-libpurple Architecture: any -Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${source:Version}) +Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee Replaces: bitlbee Description: An IRC to other chat networks gateway @@ -48,7 +48,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all -Depends: ${misc:Depends}, bitlbee (>= ${source:Version}), bitlbee (<< ${source:Version}.1~) +Depends: ${misc:Depends}, bitlbee (>= ${bee:Version}), bitlbee (<< ${bee:Version}.1~) Description: An IRC to other chat networks gateway This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. diff --git a/debian/rules b/debian/rules index 8ccb181b..f2ede2cf 100755 --- a/debian/rules +++ b/debian/rules @@ -19,11 +19,11 @@ build: build-stamp build-stamp: dh_testdir - mkdir debian/build-native + mkdir -p debian/build-native ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) -C debian/build-native - mkdir debian/build-libpurple + mkdir -p debian/build-libpurple ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --purple=1 $(BITLBEE_CONFIGURE_FLAGS) $(MAKE) -C debian/build-libpurple @@ -61,7 +61,7 @@ binary-common: dh_testroot dh_installdocs --link-doc=bitlbee-common - dh_installchangelogs + dh_installchangelogs doc/CHANGES dh_installexamples dh_installdebconf dh_installinit @@ -79,9 +79,10 @@ ifeq ($(DH_OPTIONS),-a) endif dh_shlibdeps ifdef BITLBEE_VERSION - dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 + echo source:Version=1:$(BITLBEE_VERSION)-0 > debian/substvars + dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 -Vbee:Version=1:$(BITLBEE_VERSION)-0 else - dh_gencontrol + dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+[^+]*$$//') endif dh_md5sums dh_builddeb -- cgit v1.2.3 From e774815bc621af90bb64ca314b84367659c5a005 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 5 Jun 2010 18:43:38 +0100 Subject: Updated short descriptions and fixed po-debconf files. --- debian/control | 8 ++++---- debian/po/POTFILES.in | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/debian/control b/debian/control index d197dcdd..05689940 100644 --- a/debian/control +++ b/debian/control @@ -14,7 +14,7 @@ Architecture: any Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee-libpurple Replaces: bitlbee-libpurple -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (default version) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. @@ -23,7 +23,7 @@ Architecture: any Depends: ${shlibs:Depends}, adduser, debianutils (>= 1.16), bitlbee-common (= ${bee:Version}) Conflicts: bitlbee Replaces: bitlbee -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (using libpurple) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . @@ -39,7 +39,7 @@ Package: bitlbee-common Architecture: all Depends: ${misc:Depends}, net-tools Replaces: bitlbee -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (common files/docs) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . @@ -49,7 +49,7 @@ Description: An IRC to other chat networks gateway Package: bitlbee-dev Architecture: all Depends: ${misc:Depends}, bitlbee (>= ${bee:Version}), bitlbee (<< ${bee:Version}.1~) -Description: An IRC to other chat networks gateway +Description: An IRC to other chat networks gateway (dev files) This program can be used as an IRC server which forwards everything you say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. . diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in index cef83a34..8d2b570f 100644 --- a/debian/po/POTFILES.in +++ b/debian/po/POTFILES.in @@ -1 +1 @@ -[type: gettext/rfc822deb] templates +[type: gettext/rfc822deb] bitlbee-common.templates -- cgit v1.2.3