Changes in / [17f6079:c8b8c83]
- Files:
-
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
Makefile
r17f6079 rc8b8c83 156 156 $(OTR_PI): %.so: $(SRCDIR)%.c 157 157 @echo '*' Building plugin $@ 158 @$(CC) $(CFLAGS) $(OTRFLAGS) -fPIC -shared $ < -o $@158 @$(CC) $(CFLAGS) $(OTRFLAGS) -fPIC -shared $(LDFLAGS) $< -o $@ 159 159 160 160 $(SKYPE_PI): $(SRCDIR)protocols/skype/skype.c -
bitlbee.h
r17f6079 rc8b8c83 35 35 36 36 #define PACKAGE "BitlBee" 37 #define BITLBEE_VERSION "3.0. 2"37 #define BITLBEE_VERSION "3.0.3" 38 38 #define VERSION BITLBEE_VERSION 39 39 #define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c)) 40 #define BITLBEE_VERSION_CODE BITLBEE_VER(3, 0, 2)40 #define BITLBEE_VERSION_CODE BITLBEE_VER(3, 0, 3) 41 41 42 42 #define MAX_STRING 511 -
debian/changelog
r17f6079 rc8b8c83 1 bitlbee (3.0.3-1) unstable; urgency=low 2 3 * New upstream release. (Skipped 3.0.2, sorry!) 4 * Fixes Twitter issues. 5 6 -- Wilmer van der Gaast <wilmer@gaast.net> Tue, 14 Jun 2011 22:39:22 +0100 7 1 8 bitlbee (3.0.1-1) unstable; urgency=low 2 9 -
debian/rules
r17f6079 rc8b8c83 95 95 dh_shlibdeps 96 96 ifdef BITLBEE_VERSION 97 dh_gencontrol -- -v 1:$(BITLBEE_VERSION)-0 -Vbee:Version=1:$(BITLBEE_VERSION)-097 dh_gencontrol -- -v$(BITLBEE_VERSION) -Vbee:Version=$(BITLBEE_VERSION) 98 98 else 99 99 dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+b[0-9]\+$$//') -
doc/CHANGES
r17f6079 rc8b8c83 3 3 4 4 http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on 5 6 Version 3.0.3: 7 - Fixed Twitter compatibility. (The API call used to get the following list 8 was deprecated.) 9 - Twitter: Enable the show_ids setting to assign a two-digit short ID to 10 recent tweets to use for retweets and replies (so you can RT/reply to more 11 than just a person's last message). 12 - Some other Twitter fixes/improvements. 13 - "otr reconnect" command and some other fixes. 14 - GnuTLS 2.12 compatibility fix. 15 - Include "FLOOD=0/9999" in the 005/ISUPPORT line at login to hint the IRC 16 client that rate limiting is not required. (Next step: Get IRC clients to 17 parse it.) 18 - Other stuff too small to mention. 19 20 Finished 12 Jun 2011 5 21 6 22 Version 3.0.2: -
doc/bitlbee.xinetd
r17f6079 rc8b8c83 12 12 ## You most likely want to change these two 13 13 user = nobody 14 server = /usr/local/sbin/bitlbee 14 server = /usr/local/sbin/bitlbee -I 15 15 16 16 ## You might want to limit access to localhost only: -
doc/user-guide/commands.xml
r17f6079 rc8b8c83 393 393 394 394 <para> 395 Available subcommands: connect, disconnect, smp, smpq, trust, info, keygen, and forget. See <emphasis>help otr <subcommand></emphasis> for more information.395 Available subcommands: connect, disconnect, reconnect, smp, smpq, trust, info, keygen, and forget. See <emphasis>help otr <subcommand></emphasis> for more information. 396 396 </para> 397 397 … … 418 418 <para> 419 419 Resets the connection with the specified user to cleartext. 420 </para> 421 422 </description> 423 424 </bitlbee-command> 425 426 <bitlbee-command name="reconnect"> 427 <syntax>otr reconnect <nick></syntax> 428 429 <description> 430 431 <para> 432 Breaks and re-establishes the encrypted connection with the specified user. Useful if something got desynced. 433 </para> 434 435 <para> 436 Equivalent to <emphasis>otr disconnect</emphasis> followed by <emphasis>otr connect</emphasis>. 420 437 </para> 421 438 … … 754 771 755 772 <bitlbee-setting name="base_url" type="string" scope="account"> 756 <default>http:// twitter.com</default>773 <default>http://api.twitter.com/1</default> 757 774 758 775 <description> … … 837 854 <varlistentry><term>undo [<id>]</term><listitem><para>Delete your last Tweet (or one with the given ID)</para></listitem></varlistentry> 838 855 <varlistentry><term>rt <screenname|id></term><listitem><para>Retweet someone's last Tweet (or one with the given ID)</para></listitem></varlistentry> 856 <varlistentry><term>reply <screenname|id></term><listitem><para>Reply to a Tweet (with a reply-to reference)</para></listitem></varlistentry> 839 857 <varlistentry><term>follow <screenname></term><listitem><para>Start following a person</para></listitem></varlistentry> 840 858 <varlistentry><term>unfollow <screenname></term><listitem><para>Stop following a person</para></listitem></varlistentry> … … 1309 1327 <para> 1310 1328 Can be set for Jabber- and OSCAR-connections. For Jabber, you might have to set this if the servername isn't equal to the part after the @ in the Jabber handle. For OSCAR this shouldn't be necessary anymore in recent BitlBee versions. 1329 </para> 1330 </description> 1331 </bitlbee-setting> 1332 1333 <bitlbee-setting name="show_ids" type="boolean" scope="account"> 1334 <default>false</default> 1335 1336 <description> 1337 <para> 1338 Enable this setting on a Twitter account to have BitlBee include a two-digit "id" in front of every message. This id can then be used for replies and retweets. 1311 1339 </para> 1312 1340 </description> -
init/bitlbee@.service.in
r17f6079 rc8b8c83 4 4 5 5 [Service] 6 ExecStart=@sbindir@/bitlbee 6 ExecStart=@sbindir@/bitlbee -I 7 7 StandardInput=socket 8 8 User=bitlbee -
irc_commands.c
r17f6079 rc8b8c83 637 637 Jabber really doesn't like them. */ 638 638 for( i = j = 0; cmd[1][i]; i ++ ) 639 if( ( away[j] = cmd[1][i] ) >= ' ' )639 if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' ) 640 640 j ++; 641 641 away[j] = '\0'; -
irc_im.c
r17f6079 rc8b8c83 600 600 601 601 ic->data = NULL; 602 c->ui_data = NULL; 602 603 irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" ); 603 604 … … 905 906 c->ic->acc->prpl->chat_leave( c ); 906 907 908 /* Remove references in both directions now. We don't need each other anymore. */ 907 909 ic->data = NULL; 910 if( c ) 911 c->ui_data = NULL; 908 912 909 913 return TRUE; -
lib/http_client.c
r17f6079 rc8b8c83 93 93 request = g_strdup_printf( "GET %s HTTP/1.0\r\n" 94 94 "Host: %s\r\n" 95 "Connection: close\r\n" 95 96 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" 96 97 "\r\n", url->file, url->host ); -
lib/misc.c
r17f6079 rc8b8c83 400 400 cd = g_iconv_open( to_cs, from_cs ); 401 401 if( cd == (GIConv) -1 ) 402 return ( -1 );402 return -1; 403 403 404 404 inbytesleft = size ? size : strlen( src ); … … 408 408 g_iconv_close( cd ); 409 409 410 if( res == (size_t) -1)411 return ( -1 );410 if( res != 0 ) 411 return -1; 412 412 else 413 return ( outbuf - dst );413 return outbuf - dst; 414 414 } 415 415 -
lib/oauth.c
r17f6079 rc8b8c83 286 286 "Content-Type: application/x-www-form-urlencoded\r\n" 287 287 "Content-Length: %zd\r\n" 288 "Connection: close\r\n" 288 289 "\r\n" 289 290 "%s", url_p.file, url_p.host, strlen( post ), post ); -
lib/proxy.c
r17f6079 rc8b8c83 85 85 closesocket(source); 86 86 dup2(new_fd, source); 87 closesocket(new_fd); 87 88 phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb); 88 89 return FALSE; -
lib/ssl_gnutls.c
r17f6079 rc8b8c83 135 135 gnutls_certificate_allocate_credentials( &conn->xcred ); 136 136 gnutls_init( &conn->session, GNUTLS_CLIENT ); 137 gnutls_transport_set_lowat( conn->session, 1 ); 137 138 gnutls_set_default_priority( conn->session ); 138 139 gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); -
otr.c
r17f6079 rc8b8c83 86 86 void cmd_otr_connect(irc_t *irc, char **args); 87 87 void cmd_otr_disconnect(irc_t *irc, char **args); 88 void cmd_otr_reconnect(irc_t *irc, char **args); 88 89 void cmd_otr_smp(irc_t *irc, char **args); 89 90 void cmd_otr_smpq(irc_t *irc, char **args); … … 96 97 { "connect", 1, &cmd_otr_connect, 0 }, 97 98 { "disconnect", 1, &cmd_otr_disconnect, 0 }, 99 { "reconnect", 1, &cmd_otr_reconnect, 0 }, 98 100 { "smp", 2, &cmd_otr_smp, 0 }, 99 101 { "smpq", 3, &cmd_otr_smpq, 0 }, … … 693 695 694 696 /*** OTR sub-command handlers ***/ 697 698 void cmd_otr_reconnect(irc_t *irc, char **args) 699 { 700 cmd_otr_disconnect(irc, args); 701 cmd_otr_connect(irc, args); 702 } 695 703 696 704 void cmd_otr_disconnect(irc_t *irc, char **args) -
protocols/twitter/twitter.c
r17f6079 rc8b8c83 37 37 imcb_log( ic, fmt ); \ 38 38 } while( 0 ); 39 39 40 40 GSList *twitter_connections = NULL; 41 41 … … 46 46 { 47 47 struct im_connection *ic = data; 48 48 49 49 // Check if we are still logged in... 50 if (!g_slist_find( twitter_connections, ic))50 if (!g_slist_find(twitter_connections, ic)) 51 51 return 0; 52 52 … … 58 58 } 59 59 60 static void twitter_main_loop_start( struct im_connection *ic)60 static void twitter_main_loop_start(struct im_connection *ic) 61 61 { 62 62 struct twitter_data *td = ic->proto_data; 63 64 imcb_log( ic, "Getting initial statuses");63 64 imcb_log(ic, "Getting initial statuses"); 65 65 66 66 // Run this once. After this queue the main loop function. … … 72 72 } 73 73 74 static void twitter_oauth_start( struct im_connection *ic);75 76 void twitter_login_finish( struct im_connection *ic)74 static void twitter_oauth_start(struct im_connection *ic); 75 76 void twitter_login_finish(struct im_connection *ic) 77 77 { 78 78 struct twitter_data *td = ic->proto_data; 79 80 if( set_getbool( &ic->acc->set, "oauth" ) && !td->oauth_info ) 81 twitter_oauth_start( ic ); 82 else if( g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) != 0 && 83 !( td->flags & TWITTER_HAVE_FRIENDS ) ) 84 { 85 imcb_log( ic, "Getting contact list" ); 86 twitter_get_statuses_friends( ic, -1 ); 87 } 88 else 89 twitter_main_loop_start( ic ); 90 } 91 92 static const struct oauth_service twitter_oauth = 93 { 79 80 if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) 81 twitter_oauth_start(ic); 82 else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0 && 83 !(td->flags & TWITTER_HAVE_FRIENDS)) { 84 imcb_log(ic, "Getting contact list"); 85 twitter_get_friends_ids(ic, -1); 86 //twitter_get_statuses_friends(ic, -1); 87 } else 88 twitter_main_loop_start(ic); 89 } 90 91 static const struct oauth_service twitter_oauth = { 94 92 "http://api.twitter.com/oauth/request_token", 95 93 "http://api.twitter.com/oauth/access_token", … … 99 97 }; 100 98 101 static const struct oauth_service identica_oauth = 102 { 99 static const struct oauth_service identica_oauth = { 103 100 "http://identi.ca/api/oauth/request_token", 104 101 "http://identi.ca/api/oauth/access_token", … … 108 105 }; 109 106 110 static gboolean twitter_oauth_callback( struct oauth_info *info);111 112 static const struct oauth_service *get_oauth_service( struct im_connection *ic)107 static gboolean twitter_oauth_callback(struct oauth_info *info); 108 109 static const struct oauth_service *get_oauth_service(struct im_connection *ic) 113 110 { 114 111 struct twitter_data *td = ic->proto_data; 115 116 if ( strstr( td->url_host, "identi.ca" ))112 113 if (strstr(td->url_host, "identi.ca")) 117 114 return &identica_oauth; 118 115 else 119 116 return &twitter_oauth; 120 117 121 118 /* Could add more services, or allow configuring your own base URL + 122 119 API keys. */ 123 120 } 124 121 125 static void twitter_oauth_start( struct im_connection *ic)122 static void twitter_oauth_start(struct im_connection *ic) 126 123 { 127 124 struct twitter_data *td = ic->proto_data; 128 129 imcb_log( ic, "Requesting OAuth request token");130 131 td->oauth_info = oauth_request_token( get_oauth_service( ic ), twitter_oauth_callback, ic);132 125 126 imcb_log(ic, "Requesting OAuth request token"); 127 128 td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic); 129 133 130 /* We need help from the user to complete OAuth login, so don't time 134 131 out on this login. */ … … 136 133 } 137 134 138 static gboolean twitter_oauth_callback( struct oauth_info *info)135 static gboolean twitter_oauth_callback(struct oauth_info *info) 139 136 { 140 137 struct im_connection *ic = info->data; 141 138 struct twitter_data *td; 142 143 if ( !g_slist_find( twitter_connections, ic ))139 140 if (!g_slist_find(twitter_connections, ic)) 144 141 return FALSE; 145 142 146 143 td = ic->proto_data; 147 if( info->stage == OAUTH_REQUEST_TOKEN ) 148 { 149 char name[strlen(ic->acc->user)+9], *msg; 150 151 if( info->request_token == NULL ) 152 { 153 imcb_error( ic, "OAuth error: %s", info->http->status_string ); 154 imc_logout( ic, TRUE ); 144 if (info->stage == OAUTH_REQUEST_TOKEN) { 145 char name[strlen(ic->acc->user) + 9], *msg; 146 147 if (info->request_token == NULL) { 148 imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http)); 149 imc_logout(ic, TRUE); 155 150 return FALSE; 156 151 } 157 158 sprintf( name, "%s_%s", td->prefix, ic->acc->user ); 159 msg = g_strdup_printf( "To finish OAuth authentication, please visit " 160 "%s and respond with the resulting PIN code.", 161 info->auth_url ); 162 imcb_buddy_msg( ic, name, msg, 0, 0 ); 163 g_free( msg ); 164 } 165 else if( info->stage == OAUTH_ACCESS_TOKEN ) 166 { 167 if( info->token == NULL || info->token_secret == NULL ) 168 { 169 imcb_error( ic, "OAuth error: %s", info->http->status_string ); 170 imc_logout( ic, TRUE ); 152 153 sprintf(name, "%s_%s", td->prefix, ic->acc->user); 154 msg = g_strdup_printf("To finish OAuth authentication, please visit " 155 "%s and respond with the resulting PIN code.", 156 info->auth_url); 157 imcb_buddy_msg(ic, name, msg, 0, 0); 158 g_free(msg); 159 } else if (info->stage == OAUTH_ACCESS_TOKEN) { 160 if (info->token == NULL || info->token_secret == NULL) { 161 imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http)); 162 imc_logout(ic, TRUE); 171 163 return FALSE; 164 } else { 165 const char *sn = oauth_params_get(&info->params, "screen_name"); 166 167 if (sn != NULL && ic->acc->prpl->handle_cmp(sn, ic->acc->user) != 0) { 168 imcb_log(ic, "Warning: You logged in via OAuth as %s " 169 "instead of %s.", sn, ic->acc->user); 170 } 171 g_free(td->user); 172 td->user = g_strdup(sn); 172 173 } 173 else 174 { 175 const char *sn = oauth_params_get( &info->params, "screen_name" ); 176 177 if( sn != NULL && ic->acc->prpl->handle_cmp( sn, ic->acc->user ) != 0 ) 178 { 179 imcb_log( ic, "Warning: You logged in via OAuth as %s " 180 "instead of %s.", sn, ic->acc->user ); 181 } 182 } 183 174 184 175 /* IM mods didn't do this so far and it's ugly but I should 185 176 be able to get away with it... */ 186 g_free( ic->acc->pass);187 ic->acc->pass = oauth_to_string( info);188 189 twitter_login_finish( ic);190 } 191 177 g_free(ic->acc->pass); 178 ic->acc->pass = oauth_to_string(info); 179 180 twitter_login_finish(ic); 181 } 182 192 183 return TRUE; 193 184 } 194 185 195 186 196 static char *set_eval_mode( set_t *set, char *value ) 197 { 198 if( g_strcasecmp( value, "one" ) == 0 || 199 g_strcasecmp( value, "many" ) == 0 || 200 g_strcasecmp( value, "chat" ) == 0 ) 187 static char *set_eval_mode(set_t * set, char *value) 188 { 189 if (g_strcasecmp(value, "one") == 0 || 190 g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0) 201 191 return value; 202 192 else … … 204 194 } 205 195 206 static gboolean twitter_length_check( struct im_connection *ic, gchar *msg)207 { 208 int max = set_getint( &ic->acc->set, "message_length"), len;209 210 if ( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max)196 static gboolean twitter_length_check(struct im_connection *ic, gchar * msg) 197 { 198 int max = set_getint(&ic->acc->set, "message_length"), len; 199 200 if (max == 0 || (len = g_utf8_strlen(msg, -1)) <= max) 211 201 return TRUE; 212 213 imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max);214 202 203 imcb_error(ic, "Maximum message length exceeded: %d > %d", len, max); 204 215 205 return FALSE; 216 206 } 217 207 218 static void twitter_init( account_t *acc)208 static void twitter_init(account_t * acc) 219 209 { 220 210 set_t *s; 221 211 char *def_url; 222 212 char *def_oauth; 223 224 if( strcmp( acc->prpl->name, "twitter" ) == 0 ) 225 { 213 214 if (strcmp(acc->prpl->name, "twitter") == 0) { 226 215 def_url = TWITTER_API_URL; 227 216 def_oauth = "true"; 228 } 229 else /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ 230 { 217 } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ 218 231 219 def_url = IDENTICA_API_URL; 232 220 def_oauth = "false"; 233 221 } 234 235 s = set_add( &acc->set, "auto_reply_timeout", "10800", set_eval_int, acc);236 237 s = set_add( &acc->set, "base_url", def_url, NULL, acc);222 223 s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc); 224 225 s = set_add(&acc->set, "base_url", def_url, NULL, acc); 238 226 s->flags |= ACC_SET_OFFLINE_ONLY; 239 240 s = set_add( &acc->set, "commands", "true", set_eval_bool, acc);241 242 s = set_add( &acc->set, "message_length", "140", set_eval_int, acc);243 244 s = set_add( &acc->set, "mode", "chat", set_eval_mode, acc);227 228 s = set_add(&acc->set, "commands", "true", set_eval_bool, acc); 229 230 s = set_add(&acc->set, "message_length", "140", set_eval_int, acc); 231 232 s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc); 245 233 s->flags |= ACC_SET_OFFLINE_ONLY; 246 247 s = set_add( &acc->set, "show_ids", "false", set_eval_bool, acc);234 235 s = set_add(&acc->set, "show_ids", "false", set_eval_bool, acc); 248 236 s->flags |= ACC_SET_OFFLINE_ONLY; 249 250 s = set_add( &acc->set, "oauth", def_oauth, set_eval_bool, acc);237 238 s = set_add(&acc->set, "oauth", def_oauth, set_eval_bool, acc); 251 239 } 252 240 … … 255 243 * only save the user and pass to the twitter_data object. 256 244 */ 257 static void twitter_login( account_t *acc)258 { 259 struct im_connection *ic = imcb_new( acc);245 static void twitter_login(account_t * acc) 246 { 247 struct im_connection *ic = imcb_new(acc); 260 248 struct twitter_data *td; 261 char name[strlen(acc->user) +9];249 char name[strlen(acc->user) + 9]; 262 250 url_t url; 263 264 if( !url_set( &url, set_getstr( &ic->acc->set, "base_url" ) ) ||265 ( url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS ) )266 {267 imcb_error( ic, "Incorrect API base URL: %s", set_getstr( &ic->acc->set, "base_url" ));268 imc_logout( ic, FALSE);251 char *s; 252 253 if (!url_set(&url, set_getstr(&ic->acc->set, "base_url")) || 254 (url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS)) { 255 imcb_error(ic, "Incorrect API base URL: %s", set_getstr(&ic->acc->set, "base_url")); 256 imc_logout(ic, FALSE); 269 257 return; 270 258 } 271 272 twitter_connections = g_slist_append( twitter_connections, ic ); 273 td = g_new0( struct twitter_data, 1 ); 259 260 imcb_log(ic, "Connecting"); 261 262 twitter_connections = g_slist_append(twitter_connections, ic); 263 td = g_new0(struct twitter_data, 1); 274 264 ic->proto_data = td; 275 265 td->user = g_strdup(acc->user); 266 276 267 td->url_ssl = url.proto == PROTO_HTTPS; 277 268 td->url_port = url.port; 278 td->url_host = g_strdup( url.host ); 279 if( strcmp( url.file, "/" ) != 0 ) 280 td->url_path = g_strdup( url.file ); 281 else 282 td->url_path = g_strdup( "" ); 283 if( g_str_has_suffix( url.host, ".com" ) ) 284 td->prefix = g_strndup( url.host, strlen( url.host ) - 4 ); 285 else 286 td->prefix = g_strdup( url.host ); 269 td->url_host = g_strdup(url.host); 270 if (strcmp(url.file, "/") != 0) 271 td->url_path = g_strdup(url.file); 272 else { 273 td->url_path = g_strdup(""); 274 if (g_str_has_suffix(url.host, "twitter.com")) 275 /* May fire for people who turned on HTTPS. */ 276 imcb_error(ic, "Warning: Twitter requires a version number in API calls " 277 "now. Try resetting the base_url account setting."); 278 } 287 279 288 td->user = acc->user; 289 if( strstr( acc->pass, "oauth_token=" ) ) 290 td->oauth_info = oauth_from_string( acc->pass, get_oauth_service( ic ) ); 280 /* Hacky string mangling: Turn identi.ca into identi.ca and api.twitter.com 281 into twitter, and try to be sensible if we get anything else. */ 282 td->prefix = g_strdup(url.host); 283 if (g_str_has_suffix(td->prefix, ".com")) 284 td->prefix[strlen(url.host) - 4] = '\0'; 285 if ((s = strrchr(td->prefix, '.')) && strlen(s) > 4) { 286 /* If we have at least 3 chars after the last dot, cut off the rest. 287 (mostly a www/api prefix or sth) */ 288 s = g_strdup(s + 1); 289 g_free(td->prefix); 290 td->prefix = s; 291 } 291 292 292 sprintf( name, "%s_%s", td->prefix, acc->user ); 293 imcb_add_buddy( ic, name, NULL ); 294 imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); 295 296 if( set_getbool( &acc->set, "show_ids" ) ) 297 td->log = g_new0( struct twitter_log_data, TWITTER_LOG_LENGTH ); 298 299 imcb_log( ic, "Connecting" ); 300 301 twitter_login_finish( ic ); 293 if (strstr(acc->pass, "oauth_token=")) 294 td->oauth_info = oauth_from_string(acc->pass, get_oauth_service(ic)); 295 296 sprintf(name, "%s_%s", td->prefix, acc->user); 297 imcb_add_buddy(ic, name, NULL); 298 imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); 299 300 if (set_getbool(&acc->set, "show_ids")) 301 td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH); 302 303 twitter_login_finish(ic); 302 304 } 303 305 … … 305 307 * Logout method. Just free the twitter_data. 306 308 */ 307 static void twitter_logout( struct im_connection *ic)309 static void twitter_logout(struct im_connection *ic) 308 310 { 309 311 struct twitter_data *td = ic->proto_data; 310 312 311 313 // Set the status to logged out. 312 ic->flags &= ~ 314 ic->flags &= ~OPT_LOGGED_IN; 313 315 314 316 // Remove the main_loop function from the function queue. 315 317 b_event_remove(td->main_loop_id); 316 318 317 if (td->home_timeline_gc)319 if (td->home_timeline_gc) 318 320 imcb_chat_free(td->home_timeline_gc); 319 321 320 if( td ) 321 { 322 oauth_info_free( td->oauth_info ); 323 g_free( td->prefix ); 324 g_free( td->url_host ); 325 g_free( td->url_path ); 326 g_free( td->pass ); 327 g_free( td->log ); 328 g_free( td ); 329 } 330 331 twitter_connections = g_slist_remove( twitter_connections, ic ); 332 } 333 334 static void twitter_handle_command( struct im_connection *ic, char *message ); 322 if (td) { 323 oauth_info_free(td->oauth_info); 324 g_free(td->user); 325 g_free(td->prefix); 326 g_free(td->url_host); 327 g_free(td->url_path); 328 g_free(td->log); 329 g_free(td); 330 } 331 332 twitter_connections = g_slist_remove(twitter_connections, ic); 333 } 334 335 static void twitter_handle_command(struct im_connection *ic, char *message); 335 336 336 337 /** 337 338 * 338 339 */ 339 static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away)340 static int twitter_buddy_msg(struct im_connection *ic, char *who, char *message, int away) 340 341 { 341 342 struct twitter_data *td = ic->proto_data; 342 int plen = strlen( td->prefix);343 343 int plen = strlen(td->prefix); 344 344 345 if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' && 345 g_strcasecmp(who + plen + 1, ic->acc->user) == 0) 346 { 347 if( set_getbool( &ic->acc->set, "oauth" ) && 348 td->oauth_info && td->oauth_info->token == NULL ) 349 { 350 char pin[strlen(message)+1], *s; 351 352 strcpy( pin, message ); 353 for( s = pin + sizeof( pin ) - 2; s > pin && isspace( *s ); s -- ) 346 g_strcasecmp(who + plen + 1, ic->acc->user) == 0) { 347 if (set_getbool(&ic->acc->set, "oauth") && 348 td->oauth_info && td->oauth_info->token == NULL) { 349 char pin[strlen(message) + 1], *s; 350 351 strcpy(pin, message); 352 for (s = pin + sizeof(pin) - 2; s > pin && isspace(*s); s--) 354 353 *s = '\0'; 355 for( s = pin; *s && isspace( *s ); s ++ ) {} 356 357 if( !oauth_access_token( s, td->oauth_info ) ) 358 { 359 imcb_error( ic, "OAuth error: %s", "Failed to send access token request" ); 360 imc_logout( ic, TRUE ); 354 for (s = pin; *s && isspace(*s); s++) { 355 } 356 357 if (!oauth_access_token(s, td->oauth_info)) { 358 imcb_error(ic, "OAuth error: %s", 359 "Failed to send access token request"); 360 imc_logout(ic, TRUE); 361 361 return FALSE; 362 362 } 363 } 364 else 363 } else 365 364 twitter_handle_command(ic, message); 366 } 367 else 368 { 365 } else { 369 366 twitter_direct_messages_new(ic, who, message); 370 367 } 371 return ( 0);368 return (0); 372 369 } 373 370 … … 375 372 * 376 373 */ 377 static void twitter_set_my_name( struct im_connection *ic, char *info)378 { 379 } 380 381 static void twitter_get_info(struct im_connection *ic, char *who) 382 { 383 } 384 385 static void twitter_add_buddy( struct im_connection *ic, char *who, char *group)374 static void twitter_set_my_name(struct im_connection *ic, char *info) 375 { 376 } 377 378 static void twitter_get_info(struct im_connection *ic, char *who) 379 { 380 } 381 382 static void twitter_add_buddy(struct im_connection *ic, char *who, char *group) 386 383 { 387 384 twitter_friendships_create_destroy(ic, who, 1); 388 385 } 389 386 390 static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group)387 static void twitter_remove_buddy(struct im_connection *ic, char *who, char *group) 391 388 { 392 389 twitter_friendships_create_destroy(ic, who, 0); 393 390 } 394 391 395 static void twitter_chat_msg( struct groupchat *c, char *message, int flags)396 { 397 if ( c && message)398 twitter_handle_command( c->ic, message);399 } 400 401 static void twitter_chat_invite( struct groupchat *c, char *who, char *message)402 { 403 } 404 405 static void twitter_chat_leave( struct groupchat *c)392 static void twitter_chat_msg(struct groupchat *c, char *message, int flags) 393 { 394 if (c && message) 395 twitter_handle_command(c->ic, message); 396 } 397 398 static void twitter_chat_invite(struct groupchat *c, char *who, char *message) 399 { 400 } 401 402 static void twitter_chat_leave(struct groupchat *c) 406 403 { 407 404 struct twitter_data *td = c->ic->proto_data; 408 409 if ( c != td->home_timeline_gc)410 return; 411 405 406 if (c != td->home_timeline_gc) 407 return; /* WTF? */ 408 412 409 /* If the user leaves the channel: Fine. Rejoin him/her once new 413 410 tweets come in. */ … … 416 413 } 417 414 418 static void twitter_keepalive( struct im_connection *ic)419 { 420 } 421 422 static void twitter_add_permit( struct im_connection *ic, char *who)423 { 424 } 425 426 static void twitter_rem_permit( struct im_connection *ic, char *who)427 { 428 } 429 430 static void twitter_add_deny( struct im_connection *ic, char *who)431 { 432 } 433 434 static void twitter_rem_deny( struct im_connection *ic, char *who)415 static void twitter_keepalive(struct im_connection *ic) 416 { 417 } 418 419 static void twitter_add_permit(struct im_connection *ic, char *who) 420 { 421 } 422 423 static void twitter_rem_permit(struct im_connection *ic, char *who) 424 { 425 } 426 427 static void twitter_add_deny(struct im_connection *ic, char *who) 428 { 429 } 430 431 static void twitter_rem_deny(struct im_connection *ic, char *who) 435 432 { 436 433 } … … 438 435 //static char *twitter_set_display_name( set_t *set, char *value ) 439 436 //{ 440 // 437 // return value; 441 438 //} 442 439 443 static void twitter_buddy_data_add( struct bee_user *bu)444 { 445 bu->data = g_new0( struct twitter_user_data, 1);446 } 447 448 static void twitter_buddy_data_free( struct bee_user *bu)449 { 450 g_free( bu->data);451 } 452 453 static void twitter_handle_command( struct im_connection *ic, char *message)440 static void twitter_buddy_data_add(struct bee_user *bu) 441 { 442 bu->data = g_new0(struct twitter_user_data, 1); 443 } 444 445 static void twitter_buddy_data_free(struct bee_user *bu) 446 { 447 g_free(bu->data); 448 } 449 450 static void twitter_handle_command(struct im_connection *ic, char *message) 454 451 { 455 452 struct twitter_data *td = ic->proto_data; 456 453 char *cmds, **cmd, *new = NULL; 457 454 guint64 in_reply_to = 0; 458 459 cmds = g_strdup( message ); 460 cmd = split_command_parts( cmds ); 461 462 if( cmd[0] == NULL ) 463 { 464 g_free( cmds ); 455 456 cmds = g_strdup(message); 457 cmd = split_command_parts(cmds); 458 459 if (cmd[0] == NULL) { 460 g_free(cmds); 465 461 return; 466 } 467 else if( !set_getbool( &ic->acc->set, "commands" ) ) 468 { 462 } else if (!set_getbool(&ic->acc->set, "commands")) { 469 463 /* Not supporting commands. */ 470 } 471 else if( g_strcasecmp( cmd[0], "undo" ) == 0 ) 472 { 464 } else if (g_strcasecmp(cmd[0], "undo") == 0) { 473 465 guint64 id; 474 475 if ( cmd[1])476 id = g_ascii_strtoull( cmd[1], NULL, 10);466 467 if (cmd[1]) 468 id = g_ascii_strtoull(cmd[1], NULL, 10); 477 469 else 478 470 id = td->last_status_id; 479 471 480 472 /* TODO: User feedback. */ 481 if ( id)482 twitter_status_destroy( ic, id);473 if (id) 474 twitter_status_destroy(ic, id); 483 475 else 484 twitter_msg( ic, "Could not undo last action");485 486 g_free( cmds);476 twitter_msg(ic, "Could not undo last action"); 477 478 g_free(cmds); 487 479 return; 488 } 489 else if( g_strcasecmp( cmd[0], "follow" ) == 0 && cmd[1] ) 490 { 491 twitter_add_buddy( ic, cmd[1], NULL ); 492 g_free( cmds ); 480 } else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) { 481 twitter_add_buddy(ic, cmd[1], NULL); 482 g_free(cmds); 493 483 return; 494 } 495 else if( g_strcasecmp( cmd[0], "unfollow" ) == 0 && cmd[1] ) 496 { 497 twitter_remove_buddy( ic, cmd[1], NULL ); 498 g_free( cmds ); 484 } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) { 485 twitter_remove_buddy(ic, cmd[1], NULL); 486 g_free(cmds); 499 487 return; 500 } 501 else if( g_strcasecmp( cmd[0], "rt" ) == 0 && cmd[1] ) 502 { 488 } else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) { 503 489 struct twitter_user_data *tud; 504 490 bee_user_t *bu; 505 491 guint64 id; 506 507 if ( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] )) &&508 ( tud = bu->data ) && tud->last_id)492 493 if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && 494 (tud = bu->data) && tud->last_id) 509 495 id = tud->last_id; 510 else 511 { 512 id = g_ascii_strtoull( cmd[1], NULL, 10 ); 513 if( id < TWITTER_LOG_LENGTH && td->log ) 496 else { 497 id = g_ascii_strtoull(cmd[1], NULL, 10); 498 if (id < TWITTER_LOG_LENGTH && td->log) 514 499 id = td->log[id].id; 515 500 } 516 501 517 502 td->last_status_id = 0; 518 if ( id)519 twitter_status_retweet( ic, id);503 if (id) 504 twitter_status_retweet(ic, id); 520 505 else 521 twitter_msg( 522 "post any statuses recently", cmd[1]);523 524 g_free( cmds);506 twitter_msg(ic, "User `%s' does not exist or didn't " 507 "post any statuses recently", cmd[1]); 508 509 g_free(cmds); 525 510 return; 526 } 527 else if( g_strcasecmp( cmd[0], "reply" ) == 0 && cmd[1] && cmd[2] ) 528 { 511 } else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) { 529 512 struct twitter_user_data *tud; 530 513 bee_user_t *bu = NULL; 531 514 guint64 id = 0; 532 533 if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] ) ) && 534 ( tud = bu->data ) && tud->last_id ) 535 { 515 516 if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) && 517 (tud = bu->data) && tud->last_id) { 536 518 id = tud->last_id; 537 } 538 else if( ( id = g_ascii_strtoull( cmd[1], NULL, 10 ) ) && 539 ( id < TWITTER_LOG_LENGTH ) && td->log ) 540 { 519 } else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1 && 520 (id < TWITTER_LOG_LENGTH) && td->log) { 541 521 bu = td->log[id].bu; 542 if ( g_slist_find( ic->bee->users, bu ))522 if (g_slist_find(ic->bee->users, bu)) 543 523 id = td->log[id].id; 544 524 else 545 525 bu = NULL; 546 526 } 547 if( !id || !bu ) 548 { 549 twitter_msg( ic, "User `%s' does not exist or didn't " 550 "post any statuses recently", cmd[1] ); 527 if (!id || !bu) { 528 twitter_msg(ic, "User `%s' does not exist or didn't " 529 "post any statuses recently", cmd[1]); 551 530 return; 552 531 } 553 message = new = g_strdup_printf( "@%s %s", bu->handle, 554 message + ( cmd[2] - cmd[0] ) ); 532 message = new = g_strdup_printf("@%s %s", bu->handle, message + (cmd[2] - cmd[0])); 555 533 in_reply_to = id; 556 } 557 else if( g_strcasecmp( cmd[0], "post" ) == 0 ) 558 { 534 } else if (g_strcasecmp(cmd[0], "post") == 0) { 559 535 message += 5; 560 536 } 561 537 562 538 { 563 539 char *s; 564 540 bee_user_t *bu; 565 566 if( !twitter_length_check( ic, message ) ) 567 { 568 g_free( new ); 569 g_free( cmds ); 570 return; 541 542 if (!twitter_length_check(ic, message)) { 543 g_free(new); 544 g_free(cmds); 545 return; 571 546 } 572 573 s = cmd[0] + strlen( cmd[0] ) - 1; 574 if( !new && s > cmd[0] && ( *s == ':' || *s == ',' ) ) 575 { 547 548 s = cmd[0] + strlen(cmd[0]) - 1; 549 if (!new && s > cmd[0] && (*s == ':' || *s == ',')) { 576 550 *s = '\0'; 577 578 if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[0] ) ) ) 579 { 551 552 if ((bu = bee_user_by_handle(ic->bee, ic, cmd[0]))) { 580 553 struct twitter_user_data *tud = bu->data; 581 582 new = g_strdup_printf( 583 message + ( s - cmd[0] ) + 2);554 555 new = g_strdup_printf("@%s %s", bu->handle, 556 message + (s - cmd[0]) + 2); 584 557 message = new; 585 586 if ( time( NULL) < tud->last_time +587 set_getint( &ic->acc->set, "auto_reply_timeout" ))558 559 if (time(NULL) < tud->last_time + 560 set_getint(&ic->acc->set, "auto_reply_timeout")) 588 561 in_reply_to = tud->last_id; 589 562 } 590 563 } 591 564 592 565 /* If the user runs undo between this request and its response 593 566 this would delete the second-last Tweet. Prevent that. */ 594 567 td->last_status_id = 0; 595 twitter_post_status( ic, message, in_reply_to);596 g_free( new);597 } 598 g_free( cmds);568 twitter_post_status(ic, message, in_reply_to); 569 g_free(new); 570 } 571 g_free(cmds); 599 572 } 600 573 … … 602 575 { 603 576 struct prpl *ret = g_new0(struct prpl, 1); 604 577 605 578 ret->options = OPT_NOOTR; 606 579 ret->name = "twitter"; … … 624 597 ret->buddy_data_free = twitter_buddy_data_free; 625 598 ret->handle_cmp = g_strcasecmp; 626 599 627 600 register_protocol(ret); 628 601 -
protocols/twitter/twitter.h
r17f6079 rc8b8c83 43 43 { 44 44 char* user; 45 char* pass;46 45 struct oauth_info *oauth_info; 46 GSList *follow_ids; 47 47 48 guint64 home_timeline_id; 48 49 guint64 last_status_id; /* For undo */ … … 52 53 twitter_flags_t flags; 53 54 55 /* set base_url */ 54 56 gboolean url_ssl; 55 57 int url_port; … … 59 61 char *prefix; /* Used to generate contact + channel name. */ 60 62 63 /* set show_ids */ 61 64 struct twitter_log_data *log; 62 65 int log_id; … … 85 88 void twitter_login_finish( struct im_connection *ic ); 86 89 90 struct http_request; 91 char *twitter_parse_error( struct http_request *req ); 92 87 93 #endif //_TWITTER_H -
protocols/twitter/twitter_http.c
r17f6079 rc8b8c83 27 27 * BitlBee. * 28 28 * * 29 ****************************************************************************/ 29 ****************************************************************************/ 30 30 31 31 #include "twitter.h" … … 41 41 42 42 43 static char *twitter_url_append(char *url, char *key, char *value);43 static char *twitter_url_append(char *url, char *key, char *value); 44 44 45 45 /** … … 47 47 * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c 48 48 */ 49 void *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, gpointer data, int is_post, char** arguments, int arguments_len) 49 void *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, 50 gpointer data, int is_post, char **arguments, int arguments_len) 50 51 { 51 52 struct twitter_data *td = ic->proto_data; … … 58 59 59 60 // Construct the url arguments. 60 if (arguments_len != 0) 61 { 61 if (arguments_len != 0) { 62 62 int i; 63 for (i=0; i<arguments_len; i+=2) 64 { 65 tmp = twitter_url_append(url_arguments, arguments[i], arguments[i+1]); 63 for (i = 0; i < arguments_len; i += 2) { 64 tmp = twitter_url_append(url_arguments, arguments[i], arguments[i + 1]); 66 65 g_free(url_arguments); 67 66 url_arguments = tmp; 68 67 } 69 68 } 70 71 69 // Make the request. 72 70 g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n" 73 "Host: %s\r\n" 74 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", 75 is_post ? "POST" : "GET", 76 td->url_path, url_string, 77 is_post ? "" : "?", is_post ? "" : url_arguments, 78 td->url_host); 71 "Host: %s\r\n" 72 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", 73 is_post ? "POST" : "GET", 74 td->url_path, url_string, 75 is_post ? "" : "?", is_post ? "" : url_arguments, td->url_host); 79 76 80 77 // If a pass and user are given we append them to the request. 81 if (td->oauth_info) 82 { 78 if (td->oauth_info) { 83 79 char *full_header; 84 80 char *full_url; 85 86 full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url" 81 82 full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL); 87 83 full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET", 88 89 84 full_url, url_arguments); 85 90 86 g_string_append_printf(request, "Authorization: %s\r\n", full_header); 91 87 g_free(full_header); 92 88 g_free(full_url); 93 } 94 else 95 { 96 char userpass[strlen(ic->acc->user)+2+strlen(ic->acc->pass)]; 89 } else { 90 char userpass[strlen(ic->acc->user) + 2 + strlen(ic->acc->pass)]; 97 91 char *userpass_base64; 98 92 99 93 g_snprintf(userpass, sizeof(userpass), "%s:%s", ic->acc->user, ic->acc->pass); 100 userpass_base64 = base64_encode((unsigned char *)userpass, strlen(userpass));94 userpass_base64 = base64_encode((unsigned char *) userpass, strlen(userpass)); 101 95 g_string_append_printf(request, "Authorization: Basic %s\r\n", userpass_base64); 102 g_free( userpass_base64);96 g_free(userpass_base64); 103 97 } 104 98 105 99 // Do POST stuff.. 106 if (is_post) 107 { 100 if (is_post) { 108 101 // Append the Content-Type and url-encoded arguments. 109 102 g_string_append_printf(request, 110 111 112 103 "Content-Type: application/x-www-form-urlencoded\r\n" 104 "Content-Length: %zd\r\n\r\n%s", 105 strlen(url_arguments), url_arguments); 113 106 } else { 114 107 // Append an extra \r\n to end the request... … … 118 111 ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data); 119 112 120 g_free( url_arguments);121 g_string_free( request, TRUE);113 g_free(url_arguments); 114 g_string_free(request, TRUE); 122 115 return ret; 123 116 } 124 117 125 static char *twitter_url_append(char *url, char *key, char *value)118 static char *twitter_url_append(char *url, char *key, char *value) 126 119 { 127 120 char *key_encoded = g_strndup(key, 3 * strlen(key)); -
protocols/twitter/twitter_lib.c
r17f6079 rc8b8c83 55 55 gint64 next_cursor; 56 56 GSList *list; 57 gpointer data;58 57 }; 59 58 … … 104 103 if (txl == NULL) 105 104 return; 106 for ( l = txl->list; l ; l = g_slist_next(l))105 for (l = txl->list; l; l = g_slist_next(l)) 107 106 if (txl->type == TXL_STATUS) 108 txs_free((struct twitter_xml_status *) l->data);107 txs_free((struct twitter_xml_status *) l->data); 109 108 else if (txl->type == TXL_ID) 110 109 g_free(l->data); 110 else if (txl->type == TXL_USER) 111 txu_free(l->data); 111 112 g_slist_free(txl->list); 112 113 g_free(txl); … … 120 121 struct twitter_data *td = ic->proto_data; 121 122 122 // Check if the buddy is allready in the buddy list. 123 if (!bee_user_by_handle( ic->bee, ic, name )) 124 { 123 // Check if the buddy is already in the buddy list. 124 if (!bee_user_by_handle(ic->bee, ic, name)) { 125 125 char *mode = set_getstr(&ic->acc->set, "mode"); 126 126 127 127 // The buddy is not in the list, add the buddy and set the status to logged in. 128 imcb_add_buddy( ic, name, NULL ); 129 imcb_rename_buddy( ic, name, fullname ); 130 if (g_strcasecmp(mode, "chat") == 0) 131 { 128 imcb_add_buddy(ic, name, NULL); 129 imcb_rename_buddy(ic, name, fullname); 130 if (g_strcasecmp(mode, "chat") == 0) { 132 131 /* Necessary so that nicks always get translated to the 133 132 exact Twitter username. */ 134 imcb_buddy_nick_hint( ic, name, name ); 135 imcb_chat_add_buddy( td->home_timeline_gc, name ); 136 } 137 else if (g_strcasecmp(mode, "many") == 0) 138 imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); 133 imcb_buddy_nick_hint(ic, name, name); 134 imcb_chat_add_buddy(td->home_timeline_gc, name); 135 } else if (g_strcasecmp(mode, "many") == 0) 136 imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); 139 137 } 140 138 } 141 139 142 140 /* Warning: May return a malloc()ed value, which will be free()d on the next 143 call. Only for short-term use. */144 staticchar *twitter_parse_error(struct http_request *req)141 call. Only for short-term use. NOT THREADSAFE! */ 142 char *twitter_parse_error(struct http_request *req) 145 143 { 146 144 static char *ret = NULL; 147 145 struct xt_parser *xp = NULL; 148 struct xt_node *node ;149 146 struct xt_node *node, *err; 147 150 148 g_free(ret); 151 149 ret = NULL; 152 153 if (req->body_size > 0) 154 { 150 151 if (req->body_size > 0) { 155 152 xp = xt_new(NULL, NULL); 156 153 xt_feed(xp, req->reply_body, req->body_size); 157 154 158 if ((node = xt_find_node(xp->root, "hash")) && 159 (node = xt_find_node(node->children, "error")) && 160 node->text_len > 0) 161 { 162 ret = g_strdup_printf("%s (%s)", req->status_string, node->text); 163 xt_free(xp); 164 return ret; 165 } 166 155 for (node = xp->root; node; node = node->next) 156 if ((err = xt_find_node(node->children, "error")) && err->text_len > 0) { 157 ret = g_strdup_printf("%s (%s)", req->status_string, err->text); 158 break; 159 } 160 167 161 xt_free(xp); 168 162 } 169 170 return re q->status_string;163 164 return ret ? ret : req->status_string; 171 165 } 172 166 … … 178 172 void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor) 179 173 { 180 // Primitive, but hey! It works... 181 char *args[2];174 // Primitive, but hey! It works... 175 char *args[2]; 182 176 args[0] = "cursor"; 183 args[1] = g_strdup_printf 177 args[1] = g_strdup_printf("%lld", (long long) next_cursor); 184 178 twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2); 185 179 … … 190 184 * Function to help fill a list. 191 185 */ 192 static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl)186 static xt_status twitter_xt_next_cursor(struct xt_node *node, struct twitter_xml_list *txl) 193 187 { 194 188 char *end = NULL; 195 196 if ( node->text)197 txl->next_cursor = g_ascii_strtoll( node->text, &end, 10);198 if ( end == NULL)189 190 if (node->text) 191 txl->next_cursor = g_ascii_strtoll(node->text, &end, 10); 192 if (end == NULL) 199 193 txl->next_cursor = -1; 200 194 … … 205 199 * Fill a list of ids. 206 200 */ 207 static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl)201 static xt_status twitter_xt_get_friends_id_list(struct xt_node *node, struct twitter_xml_list *txl) 208 202 { 209 203 struct xt_node *child; 210 204 211 205 // Set the list type. 212 206 txl->type = TXL_ID; … … 214 208 // The root <statuses> node should hold the list of statuses <status> 215 209 // Walk over the nodes children. 216 for( child = node->children ; child ; child = child->next ) 217 { 218 if ( g_strcasecmp( "id", child->name ) == 0) 219 { 220 // Add the item to the list. 221 txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 )); 222 } 223 else if ( g_strcasecmp( "next_cursor", child->name ) == 0) 224 { 210 for (child = node->children; child; child = child->next) { 211 if (g_strcasecmp("ids", child->name) == 0) { 212 struct xt_node *idc; 213 for (idc = child->children; idc; idc = idc->next) 214 if (g_strcasecmp(idc->name, "id") == 0) 215 txl->list = g_slist_prepend(txl->list, 216 g_memdup(idc->text, idc->text_len + 1)); 217 } else if (g_strcasecmp("next_cursor", child->name) == 0) { 225 218 twitter_xt_next_cursor(child, txl); 226 219 } … … 229 222 return XT_HANDLED; 230 223 } 224 225 static void twitter_get_users_lookup(struct im_connection *ic); 231 226 232 227 /** … … 243 238 244 239 // Check if the connection is still active. 245 if( !g_slist_find( twitter_connections, ic ) ) 246 return; 240 if (!g_slist_find(twitter_connections, ic)) 241 return; 242 243 td = ic->proto_data; 244 245 // Check if the HTTP request went well. More strict checks as this is 246 // the first request we do in a session. 247 if (req->status_code == 401) { 248 imcb_error(ic, "Authentication failure"); 249 imc_logout(ic, FALSE); 250 return; 251 } else if (req->status_code != 200) { 252 // It didn't go well, output the error and return. 253 imcb_error(ic, "Could not retrieve %s: %s", 254 TWITTER_FRIENDS_IDS_URL, twitter_parse_error(req)); 255 imc_logout(ic, TRUE); 256 return; 257 } else { 258 td->http_fails = 0; 259 } 260 261 /* Create the room now that we "logged in". */ 262 if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 263 twitter_groupchat_init(ic); 264 265 txl = g_new0(struct twitter_xml_list, 1); 266 txl->list = td->follow_ids; 267 268 // Parse the data. 269 parser = xt_new(NULL, txl); 270 xt_feed(parser, req->reply_body, req->body_size); 271 twitter_xt_get_friends_id_list(parser->root, txl); 272 xt_free(parser); 273 274 td->follow_ids = txl->list; 275 if (txl->next_cursor) 276 /* These were just numbers. Up to 4000 in a response AFAIK so if we get here 277 we may be using a spammer account. \o/ */ 278 twitter_get_friends_ids(ic, txl->next_cursor); 279 else 280 /* Now to convert all those numbers into names.. */ 281 twitter_get_users_lookup(ic); 282 283 txl->list = NULL; 284 txl_free(txl); 285 } 286 287 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl); 288 static void twitter_http_get_users_lookup(struct http_request *req); 289 290 static void twitter_get_users_lookup(struct im_connection *ic) 291 { 292 struct twitter_data *td = ic->proto_data; 293 char *args[2] = { 294 "user_id", 295 NULL, 296 }; 297 GString *ids = g_string_new(""); 298 int i; 247 299 300 /* We can request up to 100 users at a time. */ 301 for (i = 0; i < 100 && td->follow_ids; i ++) { 302 g_string_append_printf(ids, ",%s", (char*) td->follow_ids->data); 303 g_free(td->follow_ids->data); 304 td->follow_ids = g_slist_remove(td->follow_ids, td->follow_ids->data); 305 } 306 if (ids->len > 0) { 307 args[1] = ids->str + 1; 308 /* POST, because I think ids can be up to 1KB long. */ 309 twitter_http(ic, TWITTER_USERS_LOOKUP_URL, twitter_http_get_users_lookup, ic, 1, args, 2); 310 } else { 311 /* We have all users. Continue with login. (Get statuses.) */ 312 td->flags |= TWITTER_HAVE_FRIENDS; 313 twitter_login_finish(ic); 314 } 315 g_string_free(ids, TRUE); 316 } 317 318 /** 319 * Callback for getting (twitter)friends... 320 * 321 * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has 322 * hundreds of friends?" you wonder? You probably not, since you are reading the source of 323 * BitlBee... Get a life and meet new people! 324 */ 325 static void twitter_http_get_users_lookup(struct http_request *req) 326 { 327 struct im_connection *ic = req->data; 328 struct twitter_data *td; 329 struct xt_parser *parser; 330 struct twitter_xml_list *txl; 331 GSList *l = NULL; 332 struct twitter_xml_user *user; 333 334 // Check if the connection is still active. 335 if (!g_slist_find(twitter_connections, ic)) 336 return; 337 248 338 td = ic->proto_data; 249 339 250 // Check if the HTTP request went well.251 340 if (req->status_code != 200) { 252 341 // It didn't go well, output the error and return. 253 i f (++td->http_fails >= 5)254 imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));255 342 imcb_error(ic, "Could not retrieve %s: %s", 343 TWITTER_USERS_LOOKUP_URL, twitter_parse_error(req)); 344 imc_logout(ic, TRUE); 256 345 return; 257 346 } else { … … 260 349 261 350 txl = g_new0(struct twitter_xml_list, 1); 351 txl->list = NULL; 262 352 263 353 // Parse the data. 264 parser = xt_new( NULL, txl ); 265 xt_feed( parser, req->reply_body, req->body_size ); 266 twitter_xt_get_friends_id_list(parser->root, txl); 267 xt_free( parser ); 268 269 if (txl->next_cursor) 270 twitter_get_friends_ids(ic, txl->next_cursor); 271 354 parser = xt_new(NULL, txl); 355 xt_feed(parser, req->reply_body, req->body_size); 356 357 // Get the user list from the parsed xml feed. 358 twitter_xt_get_users(parser->root, txl); 359 xt_free(parser); 360 361 // Add the users as buddies. 362 for (l = txl->list; l; l = g_slist_next(l)) { 363 user = l->data; 364 twitter_add_buddy(ic, user->screen_name, user->name); 365 } 366 367 // Free the structure. 272 368 txl_free(txl); 369 370 twitter_get_users_lookup(ic); 273 371 } 274 372 … … 279 377 * - the screen_name. 280 378 */ 281 static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu)379 static xt_status twitter_xt_get_user(struct xt_node *node, struct twitter_xml_user *txu) 282 380 { 283 381 struct xt_node *child; 284 382 285 383 // Walk over the nodes children. 286 for( child = node->children ; child ; child = child->next ) 287 { 288 if ( g_strcasecmp( "name", child->name ) == 0) 289 { 290 txu->name = g_memdup( child->text, child->text_len + 1 ); 291 } 292 else if (g_strcasecmp( "screen_name", child->name ) == 0) 293 { 294 txu->screen_name = g_memdup( child->text, child->text_len + 1 ); 384 for (child = node->children; child; child = child->next) { 385 if (g_strcasecmp("name", child->name) == 0) { 386 txu->name = g_memdup(child->text, child->text_len + 1); 387 } else if (g_strcasecmp("screen_name", child->name) == 0) { 388 txu->screen_name = g_memdup(child->text, child->text_len + 1); 295 389 } 296 390 } … … 303 397 * - all <user>s from the <users> element. 304 398 */ 305 static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl)399 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl) 306 400 { 307 401 struct twitter_xml_user *txu; … … 313 407 // The root <users> node should hold the list of users <user> 314 408 // Walk over the nodes children. 315 for( child = node->children ; child ; child = child->next ) 316 { 317 if ( g_strcasecmp( "user", child->name ) == 0) 318 { 409 for (child = node->children; child; child = child->next) { 410 if (g_strcasecmp("user", child->name) == 0) { 319 411 txu = g_new0(struct twitter_xml_user, 1); 320 412 twitter_xt_get_user(child, txu); 321 413 // Put the item in the front of the list. 322 txl->list = g_slist_prepend (txl->list, txu); 323 } 324 } 325 326 return XT_HANDLED; 327 } 328 329 /** 330 * Function to fill a twitter_xml_list struct. 331 * It calls twitter_xt_get_users to get the <user>s from a <users> element. 332 * It sets: 333 * - the next_cursor. 334 */ 335 static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl ) 336 { 337 struct xt_node *child; 338 339 // Set the type of the list. 340 txl->type = TXL_USER; 341 342 // The root <user_list> node should hold a users <users> element 343 // Walk over the nodes children. 344 for( child = node->children ; child ; child = child->next ) 345 { 346 if ( g_strcasecmp( "users", child->name ) == 0) 347 { 348 twitter_xt_get_users(child, txl); 349 } 350 else if ( g_strcasecmp( "next_cursor", child->name ) == 0) 351 { 352 twitter_xt_next_cursor(child, txl); 414 txl->list = g_slist_prepend(txl->list, txu); 353 415 } 354 416 } … … 371 433 * - the user in a twitter_xml_user struct. 372 434 */ 373 static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs)435 static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs) 374 436 { 375 437 struct xt_node *child, *rt = NULL; … … 377 439 378 440 // Walk over the nodes children. 379 for( child = node->children ; child ; child = child->next ) 380 { 381 if ( g_strcasecmp( "text", child->name ) == 0) 382 { 383 txs->text = g_memdup( child->text, child->text_len + 1 ); 384 } 385 else if (g_strcasecmp( "truncated", child->name ) == 0 && child->text) 386 { 441 for (child = node->children; child; child = child->next) { 442 if (g_strcasecmp("text", child->name) == 0) { 443 txs->text = g_memdup(child->text, child->text_len + 1); 444 } else if (g_strcasecmp("truncated", child->name) == 0 && child->text) { 387 445 truncated = bool2int(child->text); 388 } 389 else if (g_strcasecmp( "retweeted_status", child->name ) == 0) 390 { 446 } else if (g_strcasecmp("retweeted_status", child->name) == 0) { 391 447 rt = child; 392 } 393 else if (g_strcasecmp( "created_at", child->name ) == 0) 394 { 448 } else if (g_strcasecmp("created_at", child->name) == 0) { 395 449 struct tm parsed; 396 450 397 451 /* Very sensitive to changes to the formatting of 398 452 this field. :-( Also assumes the timezone used 399 453 is UTC since C time handling functions suck. */ 400 if( strptime( child->text, TWITTER_TIME_FORMAT, &parsed ) != NULL ) 401 txs->created_at = mktime_utc( &parsed ); 454 if (strptime(child->text, TWITTER_TIME_FORMAT, &parsed) != NULL) 455 txs->created_at = mktime_utc(&parsed); 456 } else if (g_strcasecmp("user", child->name) == 0) { 457 txs->user = g_new0(struct twitter_xml_user, 1); 458 twitter_xt_get_user(child, txs->user); 459 } else if (g_strcasecmp("id", child->name) == 0) { 460 txs->id = g_ascii_strtoull(child->text, NULL, 10); 461 } else if (g_strcasecmp("in_reply_to_status_id", child->name) == 0) { 462 txs->reply_to = g_ascii_strtoull(child->text, NULL, 10); 402 463 } 403 else if (g_strcasecmp( "user", child->name ) == 0) 404 { 405 txs->user = g_new0(struct twitter_xml_user, 1); 406 twitter_xt_get_user( child, txs->user ); 407 } 408 else if (g_strcasecmp( "id", child->name ) == 0) 409 { 410 txs->id = g_ascii_strtoull (child->text, NULL, 10); 411 } 412 else if (g_strcasecmp( "in_reply_to_status_id", child->name ) == 0) 413 { 414 txs->reply_to = g_ascii_strtoull (child->text, NULL, 10); 415 } 416 } 417 464 } 465 418 466 /* If it's a truncated retweet, get the original because dots suck. */ 419 if (truncated && rt) 420 { 467 if (truncated && rt) { 421 468 struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); 422 if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) 423 { 469 if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) { 424 470 txs_free(rtxs); 425 471 return XT_HANDLED; 426 472 } 427 473 428 474 g_free(txs->text); 429 475 txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); 430 476 txs_free(rtxs); 431 477 } 432 478 433 479 return XT_HANDLED; 434 480 } … … 440 486 * - the next_cursor. 441 487 */ 442 static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt_node *node, struct twitter_xml_list *txl ) 488 static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node, 489 struct twitter_xml_list *txl) 443 490 { 444 491 struct twitter_xml_status *txs; … … 451 498 // The root <statuses> node should hold the list of statuses <status> 452 499 // Walk over the nodes children. 453 for( child = node->children ; child ; child = child->next ) 454 { 455 if ( g_strcasecmp( "status", child->name ) == 0) 456 { 500 for (child = node->children; child; child = child->next) { 501 if (g_strcasecmp("status", child->name) == 0) { 457 502 txs = g_new0(struct twitter_xml_status, 1); 458 503 twitter_xt_get_status(child, txs); 459 504 // Put the item in the front of the list. 460 txl->list = g_slist_prepend 461 505 txl->list = g_slist_prepend(txl->list, txs); 506 462 507 if (txs->user && txs->user->screen_name && 463 (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) 464 { 508 (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { 465 509 struct twitter_user_data *tud = bu->data; 466 467 if (txs->id > tud->last_id) 468 { 510 511 if (txs->id > tud->last_id) { 469 512 tud->last_id = txs->id; 470 513 tud->last_time = txs->created_at; 471 514 } 472 515 } 473 } 474 else if ( g_strcasecmp( "next_cursor", child->name ) == 0) 475 { 516 } else if (g_strcasecmp("next_cursor", child->name) == 0) { 476 517 twitter_xt_next_cursor(child, txl); 477 518 } … … 490 531 struct twitter_data *td = ic->proto_data; 491 532 492 char *args[4];533 char *args[4]; 493 534 args[0] = "cursor"; 494 args[1] = g_strdup_printf 535 args[1] = g_strdup_printf("%lld", (long long) next_cursor); 495 536 if (td->home_timeline_id) { 496 537 args[2] = "since_id"; 497 args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id); 498 } 499 500 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2); 538 args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id); 539 } 540 541 twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, 542 td->home_timeline_id ? 4 : 2); 501 543 502 544 g_free(args[1]); … … 507 549 508 550 static char *twitter_msg_add_id(struct im_connection *ic, 509 551 struct twitter_xml_status *txs, const char *prefix) 510 552 { 511 553 struct twitter_data *td = ic->proto_data; 512 554 char *ret = NULL; 513 514 if (!set_getbool(&ic->acc->set, "show_ids")) 515 { 555 556 if (!set_getbool(&ic->acc->set, "show_ids")) { 516 557 if (*prefix) 517 558 return g_strconcat(prefix, txs->text, NULL); … … 519 560 return NULL; 520 561 } 521 562 522 563 td->log[td->log_id].id = txs->id; 523 564 td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name); 524 if (txs->reply_to) 525 { 565 if (txs->reply_to) { 526 566 int i; 527 for (i = 0; i < TWITTER_LOG_LENGTH; i ++) 528 if (td->log[i].id == txs->reply_to) 529 { 530 ret = g_strdup_printf( "\002[\002%02d->%02d\002]\002 %s%s", 531 td->log_id, i, prefix, txs->text); 567 for (i = 0; i < TWITTER_LOG_LENGTH; i++) 568 if (td->log[i].id == txs->reply_to) { 569 ret = g_strdup_printf("\002[\002%02d->%02d\002]\002 %s%s", 570 td->log_id, i, prefix, txs->text); 532 571 break; 533 572 } 534 573 } 535 574 if (ret == NULL) 536 ret = g_strdup_printf( "\002[\002%02d\002]\002 %s%s", 537 td->log_id, prefix, txs->text); 575 ret = g_strdup_printf("\002[\002%02d\002]\002 %s%s", td->log_id, prefix, txs->text); 538 576 td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH; 539 577 540 578 return ret; 541 579 } … … 547 585 struct twitter_data *td = ic->proto_data; 548 586 GSList *l; 549 550 td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" ); 551 552 name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user ); 553 imcb_chat_name_hint( gc, name_hint ); 554 g_free( name_hint ); 555 556 for( l = ic->bee->users; l; l = l->next ) 557 { 587 588 td->home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline"); 589 590 name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user); 591 imcb_chat_name_hint(gc, name_hint); 592 g_free(name_hint); 593 594 for (l = ic->bee->users; l; l = l->next) { 558 595 bee_user_t *bu = l->data; 559 if ( bu->ic == ic)560 imcb_chat_add_buddy( td->home_timeline_gc, bu->handle);596 if (bu->ic == ic) 597 imcb_chat_add_buddy(td->home_timeline_gc, bu->handle); 561 598 } 562 599 } … … 565 602 * Function that is called to see the statuses in a groupchat window. 566 603 */ 567 static void twitter_groupchat(struct im_connection *ic, GSList * list)604 static void twitter_groupchat(struct im_connection *ic, GSList * list) 568 605 { 569 606 struct twitter_data *td = ic->proto_data; … … 575 612 if (!td->home_timeline_gc) 576 613 twitter_groupchat_init(ic); 577 614 578 615 gc = td->home_timeline_gc; 579 616 if (!gc->joined) 580 imcb_chat_add_buddy( gc, ic->acc->user ); 581 582 for ( l = list; l ; l = g_slist_next(l) ) 583 { 617 imcb_chat_add_buddy(gc, ic->acc->user); 618 619 for (l = list; l; l = g_slist_next(l)) { 584 620 char *msg; 585 621 586 622 status = l->data; 587 623 if (status->user == NULL || status->text == NULL) … … 589 625 590 626 twitter_add_buddy(ic, status->user->screen_name, status->user->name); 591 627 592 628 strip_html(status->text); 593 629 msg = twitter_msg_add_id(ic, status, ""); 594 630 595 631 // Say it! 596 632 if (g_strcasecmp(td->user, status->user->screen_name) == 0) … … 598 634 else 599 635 imcb_chat_msg(gc, status->user->screen_name, 600 msg ? msg : status->text, 0, status->created_at);601 636 msg ? msg : status->text, 0, status->created_at); 637 602 638 g_free(msg); 603 639 604 640 // Update the home_timeline_id to hold the highest id, so that by the next request 605 641 // we won't pick up the updates already in the list. … … 611 647 * Function that is called to see statuses as private messages. 612 648 */ 613 static void twitter_private_message_chat(struct im_connection *ic, GSList * list)649 static void twitter_private_message_chat(struct im_connection *ic, GSList * list) 614 650 { 615 651 struct twitter_data *td = ic->proto_data; … … 618 654 char from[MAX_STRING]; 619 655 gboolean mode_one; 620 621 mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0; 622 623 if( mode_one ) 624 { 625 g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user ); 626 from[MAX_STRING-1] = '\0'; 627 } 628 629 for ( l = list; l ; l = g_slist_next(l) ) 630 { 656 657 mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0; 658 659 if (mode_one) { 660 g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user); 661 from[MAX_STRING - 1] = '\0'; 662 } 663 664 for (l = list; l; l = g_slist_next(l)) { 631 665 char *prefix = NULL, *text = NULL; 632 666 633 667 status = l->data; 634 635 strip_html( status->text);636 if ( mode_one)668 669 strip_html(status->text); 670 if (mode_one) 637 671 prefix = g_strdup_printf("\002<\002%s\002>\002 ", 638 672 status->user->screen_name); 639 673 else 640 674 twitter_add_buddy(ic, status->user->screen_name, status->user->name); 641 675 642 676 text = twitter_msg_add_id(ic, status, prefix ? prefix : ""); 643 644 imcb_buddy_msg( ic, 645 mode_one ? from : status->user->screen_name, 646 text ? text : status->text, 647 0, status->created_at ); 648 677 678 imcb_buddy_msg(ic, 679 mode_one ? from : status->user->screen_name, 680 text ? text : status->text, 0, status->created_at); 681 649 682 // Update the home_timeline_id to hold the highest id, so that by the next request 650 683 // we won't pick up the updates already in the list. 651 td->home_timeline_id = MAX(td->home_timeline_id, 652 653 g_free( text);654 g_free( prefix);684 td->home_timeline_id = MAX(td->home_timeline_id, status->id); 685 686 g_free(text); 687 g_free(prefix); 655 688 } 656 689 } … … 667 700 668 701 // Check if the connection is still active. 669 if ( !g_slist_find( twitter_connections, ic ))670 return; 671 702 if (!g_slist_find(twitter_connections, ic)) 703 return; 704 672 705 td = ic->proto_data; 673 706 674 707 // Check if the HTTP request went well. 675 if (req->status_code == 200) 676 { 708 if (req->status_code == 200) { 677 709 td->http_fails = 0; 678 710 if (!(ic->flags & OPT_LOGGED_IN)) 679 711 imcb_connected(ic); 680 } 681 else if (req->status_code == 401) 682 { 683 imcb_error( ic, "Authentication failure" ); 684 imc_logout( ic, FALSE ); 685 return; 686 } 687 else 688 { 712 } else if (req->status_code == 401) { 713 imcb_error(ic, "Authentication failure"); 714 imc_logout(ic, FALSE); 715 return; 716 } else { 689 717 // It didn't go well, output the error and return. 690 718 if (++td->http_fails >= 5) 691 imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req)); 692 719 imcb_error(ic, "Could not retrieve %s: %s", 720 TWITTER_HOME_TIMELINE_URL, twitter_parse_error(req)); 721 693 722 return; 694 723 } … … 698 727 699 728 // Parse the data. 700 parser = xt_new( NULL, txl);701 xt_feed( parser, req->reply_body, req->body_size);729 parser = xt_new(NULL, txl); 730 xt_feed(parser, req->reply_body, req->body_size); 702 731 // The root <statuses> node should hold the list of statuses <status> 703 732 twitter_xt_get_status_list(ic, parser->root, txl); 704 xt_free( parser);733 xt_free(parser); 705 734 706 735 // See if the user wants to see the messages in a groupchat window or as private messages. 707 if (txl->list == NULL) 708 ; 736 if (txl->list == NULL); 709 737 else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0) 710 738 twitter_groupchat(ic, txl->list); … … 712 740 twitter_private_message_chat(ic, txl->list); 713 741 714 // Free the structure. 742 // Free the structure. 715 743 txl_free(txl); 716 744 } 717 745 718 746 /** 719 * Callback for getting (twitter)friends... 720 * 721 * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has 722 * hundreds of friends?" you wonder? You probably not, since you are reading the source of 723 * BitlBee... Get a life and meet new people! 724 */ 725 static void twitter_http_get_statuses_friends(struct http_request *req) 747 * Callback to use after sending a POST request to twitter. 748 * (Generic, used for a few kinds of queries.) 749 */ 750 static void twitter_http_post(struct http_request *req) 726 751 { 727 752 struct im_connection *ic = req->data; 728 753 struct twitter_data *td; 729 struct xt_parser *parser;730 struct twitter_xml_list *txl;731 GSList *l = NULL;732 struct twitter_xml_user *user;733 754 734 755 // Check if the connection is still active. 735 if( !g_slist_find( twitter_connections, ic ) ) 736 return; 737 738 td = ic->proto_data; 739 740 // Check if the HTTP request went well. 741 if (req->status_code == 401) 742 { 743 imcb_error( ic, "Authentication failure" ); 744 imc_logout( ic, FALSE ); 745 return; 746 } else if (req->status_code != 200) { 747 // It didn't go well, output the error and return. 748 imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL ": %s", twitter_parse_error(req)); 749 imc_logout( ic, TRUE ); 750 return; 751 } else { 752 td->http_fails = 0; 753 } 754 755 if( !td->home_timeline_gc && 756 g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "chat" ) == 0 ) 757 twitter_groupchat_init( ic ); 758 759 txl = g_new0(struct twitter_xml_list, 1); 760 txl->list = NULL; 761 762 // Parse the data. 763 parser = xt_new( NULL, txl ); 764 xt_feed( parser, req->reply_body, req->body_size ); 765 766 // Get the user list from the parsed xml feed. 767 twitter_xt_get_user_list(parser->root, txl); 768 xt_free( parser ); 769 770 // Add the users as buddies. 771 for ( l = txl->list; l ; l = g_slist_next(l) ) 772 { 773 user = l->data; 774 twitter_add_buddy(ic, user->screen_name, user->name); 775 } 776 777 // if the next_cursor is set to something bigger then 0 there are more friends to gather. 778 if (txl->next_cursor > 0) 779 { 780 twitter_get_statuses_friends(ic, txl->next_cursor); 781 } 782 else 783 { 784 td->flags |= TWITTER_HAVE_FRIENDS; 785 twitter_login_finish(ic); 786 } 787 788 // Free the structure. 789 txl_free(txl); 790 } 791 792 /** 793 * Get the friends. 794 */ 795 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor) 796 { 797 char* args[2]; 798 args[0] = "cursor"; 799 args[1] = g_strdup_printf ("%lld", (long long) next_cursor); 800 801 twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args, 2); 802 803 g_free(args[1]); 804 } 805 806 /** 807 * Callback to use after sending a post request to twitter. 808 */ 809 static void twitter_http_post(struct http_request *req) 810 { 811 struct im_connection *ic = req->data; 812 struct twitter_data *td; 813 814 // Check if the connection is still active. 815 if( !g_slist_find( twitter_connections, ic ) ) 756 if (!g_slist_find(twitter_connections, ic)) 816 757 return; 817 758 818 759 td = ic->proto_data; 819 760 td->last_status_id = 0; 820 761 821 762 // Check if the HTTP request went well. 822 763 if (req->status_code != 200) { … … 825 766 return; 826 767 } 827 828 if (req->body_size > 0) 829 { 768 769 if (req->body_size > 0) { 830 770 struct xt_parser *xp = NULL; 831 771 struct xt_node *node; 832 772 833 773 xp = xt_new(NULL, NULL); 834 774 xt_feed(xp, req->reply_body, req->body_size); 835 775 836 776 if ((node = xt_find_node(xp->root, "status")) && 837 777 (node = xt_find_node(node->children, "id")) && node->text) 838 td->last_status_id = g_ascii_strtoull( node->text, NULL, 10);839 778 td->last_status_id = g_ascii_strtoull(node->text, NULL, 10); 779 840 780 xt_free(xp); 841 781 } … … 844 784 /** 845 785 * Function to POST a new status to twitter. 846 */ 786 */ 847 787 void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to) 848 788 { 849 char *args[4] = {789 char *args[4] = { 850 790 "status", msg, 851 791 "in_reply_to_status_id", … … 853 793 }; 854 794 twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1, 855 795 args, in_reply_to ? 4 : 2); 856 796 g_free(args[3]); 857 797 } … … 863 803 void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg) 864 804 { 865 char *args[4];805 char *args[4]; 866 806 args[0] = "screen_name"; 867 807 args[1] = who; … … 870 810 // Use the same callback as for twitter_post_status, since it does basically the same. 871 811 twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4); 872 // g_free(args[1]);873 // g_free(args[3]);874 812 } 875 813 876 814 void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create) 877 815 { 878 char *args[2];816 char *args[2]; 879 817 args[0] = "screen_name"; 880 818 args[1] = who; 881 twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, twitter_http_post, ic, 1, args, 2); 819 twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, 820 twitter_http_post, ic, 1, args, 2); 882 821 } 883 822 … … 885 824 { 886 825 char *url; 887 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".xml"); 826 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, 827 (unsigned long long) id, ".xml"); 888 828 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 889 829 g_free(url); … … 893 833 { 894 834 char *url; 895 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".xml"); 835 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, 836 (unsigned long long) id, ".xml"); 896 837 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 897 838 g_free(url); -
protocols/twitter/twitter_lib.h
r17f6079 rc8b8c83 29 29 #include "twitter_http.h" 30 30 31 #define TWITTER_API_URL "http:// twitter.com"32 #define IDENTICA_API_URL "http ://identi.ca/api"31 #define TWITTER_API_URL "http://api.twitter.com/1" 32 #define IDENTICA_API_URL "https://identi.ca/api" 33 33 34 34 /* Status URLs */ … … 47 47 48 48 /* Users URLs */ 49 #define TWITTER_SHOW_USERS_URL "/users/show.xml" 50 #define TWITTER_SHOW_FRIENDS_URL "/statuses/friends.xml" 51 #define TWITTER_SHOW_FOLLOWERS_URL "/statuses/followers.xml" 49 #define TWITTER_USERS_LOOKUP_URL "/users/lookup.xml" 52 50 53 51 /* Direct messages URLs */
Note: See TracChangeset
for help on using the changeset viewer.