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 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 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