aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--bitlbee.h4
-rw-r--r--debian/changelog7
-rwxr-xr-xdebian/rules2
-rw-r--r--doc/CHANGES16
-rw-r--r--doc/bitlbee.xinetd2
-rw-r--r--doc/user-guide/commands.xml32
-rw-r--r--init/bitlbee@.service.in2
-rw-r--r--irc_commands.c2
-rw-r--r--irc_im.c4
-rw-r--r--lib/http_client.c1
-rw-r--r--lib/misc.c8
-rw-r--r--lib/oauth.c1
-rw-r--r--lib/proxy.c1
-rw-r--r--lib/ssl_gnutls.c1
-rw-r--r--otr.c8
-rw-r--r--protocols/twitter/twitter.c573
-rw-r--r--protocols/twitter/twitter.h8
-rw-r--r--protocols/twitter/twitter_http.c65
-rw-r--r--protocols/twitter/twitter_lib.c653
-rw-r--r--protocols/twitter/twitter_lib.h8
21 files changed, 689 insertions, 711 deletions
diff --git a/Makefile b/Makefile
index e62d47cb..eea4f1c1 100644
--- a/Makefile
+++ b/Makefile
@@ -155,7 +155,7 @@ $(subdirs):
$(OTR_PI): %.so: $(SRCDIR)%.c
@echo '*' Building plugin $@
- @$(CC) $(CFLAGS) $(OTRFLAGS) -fPIC -shared $< -o $@
+ @$(CC) $(CFLAGS) $(OTRFLAGS) -fPIC -shared $(LDFLAGS) $< -o $@
$(SKYPE_PI): $(SRCDIR)protocols/skype/skype.c
@echo '*' Building plugin skype
diff --git a/bitlbee.h b/bitlbee.h
index 6aff082f..e5cfa6ee 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -34,10 +34,10 @@
#define _WIN32_WINNT 0x0501
#define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "3.0.2"
+#define BITLBEE_VERSION "3.0.3"
#define VERSION BITLBEE_VERSION
#define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c))
-#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 0, 2)
+#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 0, 3)
#define MAX_STRING 511
diff --git a/debian/changelog b/debian/changelog
index 343918e7..a7d008f3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+bitlbee (3.0.3-1) unstable; urgency=low
+
+ * New upstream release. (Skipped 3.0.2, sorry!)
+ * Fixes Twitter issues.
+
+ -- Wilmer van der Gaast <wilmer@gaast.net> Tue, 14 Jun 2011 22:39:22 +0100
+
bitlbee (3.0.1-1) unstable; urgency=low
* New upstream release.
diff --git a/debian/rules b/debian/rules
index 221587ad..2afadd90 100755
--- a/debian/rules
+++ b/debian/rules
@@ -94,7 +94,7 @@ binary-common:
dh_installdeb
dh_shlibdeps
ifdef BITLBEE_VERSION
- dh_gencontrol -- -v1:$(BITLBEE_VERSION)-0 -Vbee:Version=1:$(BITLBEE_VERSION)-0
+ dh_gencontrol -- -v$(BITLBEE_VERSION) -Vbee:Version=$(BITLBEE_VERSION)
else
dh_gencontrol -- -Vbee:Version=$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}' | sed -e 's/+b[0-9]\+$$//')
endif
diff --git a/doc/CHANGES b/doc/CHANGES
index 38e4054d..31be73f4 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 3.0.3:
+- Fixed Twitter compatibility. (The API call used to get the following list
+ was deprecated.)
+- Twitter: Enable the show_ids setting to assign a two-digit short ID to
+ recent tweets to use for retweets and replies (so you can RT/reply to more
+ than just a person's last message).
+- Some other Twitter fixes/improvements.
+- "otr reconnect" command and some other fixes.
+- GnuTLS 2.12 compatibility fix.
+- Include "FLOOD=0/9999" in the 005/ISUPPORT line at login to hint the IRC
+ client that rate limiting is not required. (Next step: Get IRC clients to
+ parse it.)
+- Other stuff too small to mention.
+
+Finished 12 Jun 2011
+
Version 3.0.2:
- Fixed MSN login issues with @msn.com accounts.
- /CTCP support: You can CTCP VERSION Jabber contacts, and CTCP NUDGE MSN
diff --git a/doc/bitlbee.xinetd b/doc/bitlbee.xinetd
index 88d02013..b01e8005 100644
--- a/doc/bitlbee.xinetd
+++ b/doc/bitlbee.xinetd
@@ -11,7 +11,7 @@ service ircd
## You most likely want to change these two
user = nobody
- server = /usr/local/sbin/bitlbee
+ server = /usr/local/sbin/bitlbee -I
## You might want to limit access to localhost only:
# bind = 127.0.0.1
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml
index 85003f24..74310d5e 100644
--- a/doc/user-guide/commands.xml
+++ b/doc/user-guide/commands.xml
@@ -392,7 +392,7 @@
<description>
<para>
- Available subcommands: connect, disconnect, smp, smpq, trust, info, keygen, and forget. See <emphasis>help otr &lt;subcommand&gt;</emphasis> for more information.
+ Available subcommands: connect, disconnect, reconnect, smp, smpq, trust, info, keygen, and forget. See <emphasis>help otr &lt;subcommand&gt;</emphasis> for more information.
</para>
</description>
@@ -423,6 +423,23 @@
</bitlbee-command>
+ <bitlbee-command name="reconnect">
+ <syntax>otr reconnect &lt;nick&gt;</syntax>
+
+ <description>
+
+ <para>
+ Breaks and re-establishes the encrypted connection with the specified user. Useful if something got desynced.
+ </para>
+
+ <para>
+ Equivalent to <emphasis>otr disconnect</emphasis> followed by <emphasis>otr connect</emphasis>.
+ </para>
+
+ </description>
+
+ </bitlbee-command>
+
<bitlbee-command name="smp">
<syntax>otr smp &lt;nick&gt; &lt;secret&gt;</syntax>
@@ -753,7 +770,7 @@
</bitlbee-setting>
<bitlbee-setting name="base_url" type="string" scope="account">
- <default>http://twitter.com</default>
+ <default>http://api.twitter.com/1</default>
<description>
<para>
@@ -836,6 +853,7 @@
<variablelist>
<varlistentry><term>undo [&lt;id&gt;]</term><listitem><para>Delete your last Tweet (or one with the given ID)</para></listitem></varlistentry>
<varlistentry><term>rt &lt;screenname|id&gt;</term><listitem><para>Retweet someone's last Tweet (or one with the given ID)</para></listitem></varlistentry>
+ <varlistentry><term>reply &lt;screenname|id&gt;</term><listitem><para>Reply to a Tweet (with a reply-to reference)</para></listitem></varlistentry>
<varlistentry><term>follow &lt;screenname&gt;</term><listitem><para>Start following a person</para></listitem></varlistentry>
<varlistentry><term>unfollow &lt;screenname&gt;</term><listitem><para>Stop following a person</para></listitem></varlistentry>
<varlistentry><term>post &lt;message&gt;</term><listitem><para>Post a tweet</para></listitem></varlistentry>
@@ -1312,6 +1330,16 @@
</description>
</bitlbee-setting>
+ <bitlbee-setting name="show_ids" type="boolean" scope="account">
+ <default>false</default>
+
+ <description>
+ <para>
+ 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.
+ </para>
+ </description>
+ </bitlbee-setting>
+
<bitlbee-setting name="show_offline" type="boolean" scope="global">
<default>false</default>
diff --git a/init/bitlbee@.service.in b/init/bitlbee@.service.in
index 8b576a4f..fa0e62f9 100644
--- a/init/bitlbee@.service.in
+++ b/init/bitlbee@.service.in
@@ -3,6 +3,6 @@ Description=BitlBee Per-Connection Server
After=syslog.target
[Service]
-ExecStart=@sbindir@/bitlbee
+ExecStart=@sbindir@/bitlbee -I
StandardInput=socket
User=bitlbee
diff --git a/irc_commands.c b/irc_commands.c
index cf41d323..a1933fa6 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -636,7 +636,7 @@ static void irc_cmd_away( irc_t *irc, char **cmd )
/* Copy away string, but skip control chars. Mainly because
Jabber really doesn't like them. */
for( i = j = 0; cmd[1][i]; i ++ )
- if( ( away[j] = cmd[1][i] ) >= ' ' )
+ if( (unsigned char) ( away[j] = cmd[1][i] ) >= ' ' )
j ++;
away[j] = '\0';
diff --git a/irc_im.c b/irc_im.c
index 15c5783d..6add08dd 100644
--- a/irc_im.c
+++ b/irc_im.c
@@ -599,6 +599,7 @@ static gboolean bee_irc_chat_free( bee_t *bee, struct groupchat *c )
irc_channel_printf( ic, "Cleaning up channel, bye!" );
ic->data = NULL;
+ c->ui_data = NULL;
irc_channel_del_user( ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server" );
return TRUE;
@@ -904,7 +905,10 @@ static gboolean bee_irc_channel_chat_part( irc_channel_t *ic, const char *msg )
if( c && c->ic->acc->prpl->chat_leave )
c->ic->acc->prpl->chat_leave( c );
+ /* Remove references in both directions now. We don't need each other anymore. */
ic->data = NULL;
+ if( c )
+ c->ui_data = NULL;
return TRUE;
}
diff --git a/lib/http_client.c b/lib/http_client.c
index 69f06ec5..8b045414 100644
--- a/lib/http_client.c
+++ b/lib/http_client.c
@@ -92,6 +92,7 @@ struct http_request *http_dorequest_url( char *url_string, http_input_function f
request = g_strdup_printf( "GET %s HTTP/1.0\r\n"
"Host: %s\r\n"
+ "Connection: close\r\n"
"User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
"\r\n", url->file, url->host );
diff --git a/lib/misc.c b/lib/misc.c
index 05192d9c..711b927c 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -399,7 +399,7 @@ signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t si
cd = g_iconv_open( to_cs, from_cs );
if( cd == (GIConv) -1 )
- return( -1 );
+ return -1;
inbytesleft = size ? size : strlen( src );
outbytesleft = maxbuf - 1;
@@ -407,10 +407,10 @@ signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t si
*outbuf = '\0';
g_iconv_close( cd );
- if( res == (size_t) -1 )
- return( -1 );
+ if( res != 0 )
+ return -1;
else
- return( outbuf - dst );
+ return outbuf - dst;
}
/* A pretty reliable random number generator. Tries to use the /dev/random
diff --git a/lib/oauth.c b/lib/oauth.c
index 9c67363a..372a62d3 100644
--- a/lib/oauth.c
+++ b/lib/oauth.c
@@ -285,6 +285,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f
"Host: %s\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %zd\r\n"
+ "Connection: close\r\n"
"\r\n"
"%s", url_p.file, url_p.host, strlen( post ), post );
g_free( post );
diff --git a/lib/proxy.c b/lib/proxy.c
index b79afea4..3e5c9d49 100644
--- a/lib/proxy.c
+++ b/lib/proxy.c
@@ -84,6 +84,7 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition
b_event_remove(phb->inpa);
closesocket(source);
dup2(new_fd, source);
+ closesocket(new_fd);
phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb);
return FALSE;
}
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
index cdc7c498..72517e72 100644
--- a/lib/ssl_gnutls.c
+++ b/lib/ssl_gnutls.c
@@ -134,6 +134,7 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con
gnutls_certificate_allocate_credentials( &conn->xcred );
gnutls_init( &conn->session, GNUTLS_CLIENT );
+ gnutls_transport_set_lowat( conn->session, 1 );
gnutls_set_default_priority( conn->session );
gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
diff --git a/otr.c b/otr.c
index b034eae8..1bea9c44 100644
--- a/otr.c
+++ b/otr.c
@@ -85,6 +85,7 @@ const char *op_account_name(void *opdata, const char *account, const char *proto
static void cmd_otr(irc_t *irc, char **args);
void cmd_otr_connect(irc_t *irc, char **args);
void cmd_otr_disconnect(irc_t *irc, char **args);
+void cmd_otr_reconnect(irc_t *irc, char **args);
void cmd_otr_smp(irc_t *irc, char **args);
void cmd_otr_smpq(irc_t *irc, char **args);
void cmd_otr_trust(irc_t *irc, char **args);
@@ -95,6 +96,7 @@ void cmd_otr_forget(irc_t *irc, char **args);
const command_t otr_commands[] = {
{ "connect", 1, &cmd_otr_connect, 0 },
{ "disconnect", 1, &cmd_otr_disconnect, 0 },
+ { "reconnect", 1, &cmd_otr_reconnect, 0 },
{ "smp", 2, &cmd_otr_smp, 0 },
{ "smpq", 3, &cmd_otr_smpq, 0 },
{ "trust", 6, &cmd_otr_trust, 0 },
@@ -693,6 +695,12 @@ const char *op_account_name(void *opdata, const char *account, const char *proto
/*** OTR sub-command handlers ***/
+void cmd_otr_reconnect(irc_t *irc, char **args)
+{
+ cmd_otr_disconnect(irc, args);
+ cmd_otr_connect(irc, args);
+}
+
void cmd_otr_disconnect(irc_t *irc, char **args)
{
irc_user_t *u;
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c
index 50bf6cd2..57a1ed80 100644
--- a/protocols/twitter/twitter.c
+++ b/protocols/twitter/twitter.c
@@ -36,7 +36,7 @@
else \
imcb_log( ic, fmt ); \
} while( 0 );
-
+
GSList *twitter_connections = NULL;
/**
@@ -45,9 +45,9 @@ GSList *twitter_connections = NULL;
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 (!g_slist_find( twitter_connections, ic ))
+ if (!g_slist_find(twitter_connections, ic))
return 0;
// Do stuff..
@@ -57,11 +57,11 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN;
}
-static void twitter_main_loop_start( struct im_connection *ic )
+static void twitter_main_loop_start(struct im_connection *ic)
{
struct twitter_data *td = ic->proto_data;
-
- imcb_log( ic, "Getting initial statuses" );
+
+ imcb_log(ic, "Getting initial statuses");
// Run this once. After this queue the main loop function.
twitter_main_loop(ic, -1, 0);
@@ -71,26 +71,24 @@ static void twitter_main_loop_start( struct im_connection *ic )
td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic);
}
-static void twitter_oauth_start( struct im_connection *ic );
+static void twitter_oauth_start(struct im_connection *ic);
-void twitter_login_finish( struct im_connection *ic )
+void twitter_login_finish(struct im_connection *ic)
{
struct twitter_data *td = ic->proto_data;
-
- if( set_getbool( &ic->acc->set, "oauth" ) && !td->oauth_info )
- twitter_oauth_start( ic );
- else if( g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) != 0 &&
- !( td->flags & TWITTER_HAVE_FRIENDS ) )
- {
- imcb_log( ic, "Getting contact list" );
- twitter_get_statuses_friends( ic, -1 );
- }
- else
- twitter_main_loop_start( ic );
+
+ if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info)
+ twitter_oauth_start(ic);
+ else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") != 0 &&
+ !(td->flags & TWITTER_HAVE_FRIENDS)) {
+ imcb_log(ic, "Getting contact list");
+ twitter_get_friends_ids(ic, -1);
+ //twitter_get_statuses_friends(ic, -1);
+ } else
+ twitter_main_loop_start(ic);
}
-static const struct oauth_service twitter_oauth =
-{
+static const struct oauth_service twitter_oauth = {
"http://api.twitter.com/oauth/request_token",
"http://api.twitter.com/oauth/access_token",
"https://api.twitter.com/oauth/authorize",
@@ -98,8 +96,7 @@ static const struct oauth_service twitter_oauth =
.consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo",
};
-static const struct oauth_service identica_oauth =
-{
+static const struct oauth_service identica_oauth = {
"http://identi.ca/api/oauth/request_token",
"http://identi.ca/api/oauth/access_token",
"https://identi.ca/api/oauth/authorize",
@@ -107,501 +104,477 @@ static const struct oauth_service identica_oauth =
.consumer_secret = "c596267f277457ec0ce1ab7bb788d828",
};
-static gboolean twitter_oauth_callback( struct oauth_info *info );
+static gboolean twitter_oauth_callback(struct oauth_info *info);
-static const struct oauth_service *get_oauth_service( struct im_connection *ic )
+static const struct oauth_service *get_oauth_service(struct im_connection *ic)
{
struct twitter_data *td = ic->proto_data;
-
- if( strstr( td->url_host, "identi.ca" ) )
+
+ if (strstr(td->url_host, "identi.ca"))
return &identica_oauth;
else
return &twitter_oauth;
-
+
/* Could add more services, or allow configuring your own base URL +
API keys. */
}
-static void twitter_oauth_start( struct im_connection *ic )
+static void twitter_oauth_start(struct im_connection *ic)
{
struct twitter_data *td = ic->proto_data;
-
- imcb_log( ic, "Requesting OAuth request token" );
- td->oauth_info = oauth_request_token( get_oauth_service( ic ), twitter_oauth_callback, ic );
-
+ imcb_log(ic, "Requesting OAuth request token");
+
+ td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic);
+
/* We need help from the user to complete OAuth login, so don't time
out on this login. */
ic->flags |= OPT_SLOW_LOGIN;
}
-static gboolean twitter_oauth_callback( struct oauth_info *info )
+static gboolean twitter_oauth_callback(struct oauth_info *info)
{
struct im_connection *ic = info->data;
struct twitter_data *td;
-
- if( !g_slist_find( twitter_connections, ic ) )
+
+ if (!g_slist_find(twitter_connections, ic))
return FALSE;
-
+
td = ic->proto_data;
- if( info->stage == OAUTH_REQUEST_TOKEN )
- {
- char name[strlen(ic->acc->user)+9], *msg;
-
- if( info->request_token == NULL )
- {
- imcb_error( ic, "OAuth error: %s", info->http->status_string );
- imc_logout( ic, TRUE );
+ if (info->stage == OAUTH_REQUEST_TOKEN) {
+ char name[strlen(ic->acc->user) + 9], *msg;
+
+ if (info->request_token == NULL) {
+ imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
+ imc_logout(ic, TRUE);
return FALSE;
}
-
- sprintf( name, "%s_%s", td->prefix, ic->acc->user );
- msg = g_strdup_printf( "To finish OAuth authentication, please visit "
- "%s and respond with the resulting PIN code.",
- info->auth_url );
- imcb_buddy_msg( ic, name, msg, 0, 0 );
- g_free( msg );
- }
- else if( info->stage == OAUTH_ACCESS_TOKEN )
- {
- if( info->token == NULL || info->token_secret == NULL )
- {
- imcb_error( ic, "OAuth error: %s", info->http->status_string );
- imc_logout( ic, TRUE );
+
+ sprintf(name, "%s_%s", td->prefix, ic->acc->user);
+ msg = g_strdup_printf("To finish OAuth authentication, please visit "
+ "%s and respond with the resulting PIN code.",
+ info->auth_url);
+ imcb_buddy_msg(ic, name, msg, 0, 0);
+ g_free(msg);
+ } else if (info->stage == OAUTH_ACCESS_TOKEN) {
+ if (info->token == NULL || info->token_secret == NULL) {
+ imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
+ imc_logout(ic, TRUE);
return FALSE;
- }
- else
- {
- const char *sn = oauth_params_get( &info->params, "screen_name" );
-
- if( sn != NULL && ic->acc->prpl->handle_cmp( sn, ic->acc->user ) != 0 )
- {
- imcb_log( ic, "Warning: You logged in via OAuth as %s "
- "instead of %s.", sn, ic->acc->user );
+ } else {
+ const char *sn = oauth_params_get(&info->params, "screen_name");
+
+ if (sn != NULL && ic->acc->prpl->handle_cmp(sn, ic->acc->user) != 0) {
+ imcb_log(ic, "Warning: You logged in via OAuth as %s "
+ "instead of %s.", sn, ic->acc->user);
}
+ g_free(td->user);
+ td->user = g_strdup(sn);
}
-
+
/* IM mods didn't do this so far and it's ugly but I should
be able to get away with it... */
- g_free( ic->acc->pass );
- ic->acc->pass = oauth_to_string( info );
-
- twitter_login_finish( ic );
+ g_free(ic->acc->pass);
+ ic->acc->pass = oauth_to_string(info);
+
+ twitter_login_finish(ic);
}
-
+
return TRUE;
}
-static char *set_eval_mode( set_t *set, char *value )
+static char *set_eval_mode(set_t * set, char *value)
{
- if( g_strcasecmp( value, "one" ) == 0 ||
- g_strcasecmp( value, "many" ) == 0 ||
- g_strcasecmp( value, "chat" ) == 0 )
+ if (g_strcasecmp(value, "one") == 0 ||
+ g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0)
return value;
else
return NULL;
}
-static gboolean twitter_length_check( struct im_connection *ic, gchar *msg )
+static gboolean twitter_length_check(struct im_connection *ic, gchar * msg)
{
- int max = set_getint( &ic->acc->set, "message_length" ), len;
-
- if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max )
+ int max = set_getint(&ic->acc->set, "message_length"), len;
+
+ if (max == 0 || (len = g_utf8_strlen(msg, -1)) <= max)
return TRUE;
-
- imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max );
-
+
+ imcb_error(ic, "Maximum message length exceeded: %d > %d", len, max);
+
return FALSE;
}
-static void twitter_init( account_t *acc )
+static void twitter_init(account_t * acc)
{
set_t *s;
char *def_url;
char *def_oauth;
-
- if( strcmp( acc->prpl->name, "twitter" ) == 0 )
- {
+
+ if (strcmp(acc->prpl->name, "twitter") == 0) {
def_url = TWITTER_API_URL;
def_oauth = "true";
- }
- else /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
- {
+ } else { /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */
+
def_url = IDENTICA_API_URL;
def_oauth = "false";
}
-
- s = set_add( &acc->set, "auto_reply_timeout", "10800", set_eval_int, acc );
-
- s = set_add( &acc->set, "base_url", def_url, NULL, acc );
+
+ s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc);
+
+ s = set_add(&acc->set, "base_url", def_url, NULL, acc);
s->flags |= ACC_SET_OFFLINE_ONLY;
-
- s = set_add( &acc->set, "commands", "true", set_eval_bool, acc );
-
- s = set_add( &acc->set, "message_length", "140", set_eval_int, acc );
-
- s = set_add( &acc->set, "mode", "chat", set_eval_mode, acc );
+
+ s = set_add(&acc->set, "commands", "true", set_eval_bool, acc);
+
+ s = set_add(&acc->set, "message_length", "140", set_eval_int, acc);
+
+ s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc);
s->flags |= ACC_SET_OFFLINE_ONLY;
-
- s = set_add( &acc->set, "show_ids", "false", set_eval_bool, acc );
+
+ s = set_add(&acc->set, "show_ids", "false", set_eval_bool, acc);
s->flags |= ACC_SET_OFFLINE_ONLY;
-
- s = set_add( &acc->set, "oauth", def_oauth, set_eval_bool, acc );
+
+ s = set_add(&acc->set, "oauth", def_oauth, set_eval_bool, 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 )
+static void twitter_login(account_t * acc)
{
- struct im_connection *ic = imcb_new( acc );
+ struct im_connection *ic = imcb_new(acc);
struct twitter_data *td;
- char name[strlen(acc->user)+9];
+ char name[strlen(acc->user) + 9];
url_t url;
-
- if( !url_set( &url, set_getstr( &ic->acc->set, "base_url" ) ) ||
- ( url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS ) )
- {
- imcb_error( ic, "Incorrect API base URL: %s", set_getstr( &ic->acc->set, "base_url" ) );
- imc_logout( ic, FALSE );
+ char *s;
+
+ if (!url_set(&url, set_getstr(&ic->acc->set, "base_url")) ||
+ (url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS)) {
+ imcb_error(ic, "Incorrect API base URL: %s", set_getstr(&ic->acc->set, "base_url"));
+ imc_logout(ic, FALSE);
return;
}
-
- twitter_connections = g_slist_append( twitter_connections, ic );
- td = g_new0( struct twitter_data, 1 );
+
+ imcb_log(ic, "Connecting");
+
+ twitter_connections = g_slist_append(twitter_connections, ic);
+ td = g_new0(struct twitter_data, 1);
ic->proto_data = td;
-
+ td->user = g_strdup(acc->user);
+
td->url_ssl = url.proto == PROTO_HTTPS;
td->url_port = url.port;
- td->url_host = g_strdup( url.host );
- if( strcmp( url.file, "/" ) != 0 )
- td->url_path = g_strdup( url.file );
- else
- td->url_path = g_strdup( "" );
- if( g_str_has_suffix( url.host, ".com" ) )
- td->prefix = g_strndup( url.host, strlen( url.host ) - 4 );
- else
- td->prefix = g_strdup( url.host );
-
- td->user = acc->user;
- if( strstr( acc->pass, "oauth_token=" ) )
- td->oauth_info = oauth_from_string( acc->pass, get_oauth_service( ic ) );
-
- sprintf( name, "%s_%s", td->prefix, acc->user );
- imcb_add_buddy( ic, name, NULL );
- imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
-
- if( set_getbool( &acc->set, "show_ids" ) )
- td->log = g_new0( struct twitter_log_data, TWITTER_LOG_LENGTH );
+ td->url_host = g_strdup(url.host);
+ if (strcmp(url.file, "/") != 0)
+ td->url_path = g_strdup(url.file);
+ else {
+ td->url_path = g_strdup("");
+ if (g_str_has_suffix(url.host, "twitter.com"))
+ /* May fire for people who turned on HTTPS. */
+ imcb_error(ic, "Warning: Twitter requires a version number in API calls "
+ "now. Try resetting the base_url account setting.");
+ }
- imcb_log( ic, "Connecting" );
+ /* Hacky string mangling: Turn identi.ca into identi.ca and api.twitter.com
+ into twitter, and try to be sensible if we get anything else. */
+ td->prefix = g_strdup(url.host);
+ if (g_str_has_suffix(td->prefix, ".com"))
+ td->prefix[strlen(url.host) - 4] = '\0';
+ if ((s = strrchr(td->prefix, '.')) && strlen(s) > 4) {
+ /* If we have at least 3 chars after the last dot, cut off the rest.
+ (mostly a www/api prefix or sth) */
+ s = g_strdup(s + 1);
+ g_free(td->prefix);
+ td->prefix = s;
+ }
- twitter_login_finish( ic );
+ if (strstr(acc->pass, "oauth_token="))
+ td->oauth_info = oauth_from_string(acc->pass, get_oauth_service(ic));
+
+ sprintf(name, "%s_%s", td->prefix, acc->user);
+ imcb_add_buddy(ic, name, NULL);
+ imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
+
+ if (set_getbool(&acc->set, "show_ids"))
+ td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
+
+ twitter_login_finish(ic);
}
/**
* Logout method. Just free the twitter_data.
*/
-static void twitter_logout( struct im_connection *ic )
+static void twitter_logout(struct im_connection *ic)
{
struct twitter_data *td = ic->proto_data;
-
+
// Set the status to logged out.
- ic->flags &= ~ OPT_LOGGED_IN;
+ ic->flags &= ~OPT_LOGGED_IN;
// Remove the main_loop function from the function queue.
b_event_remove(td->main_loop_id);
- if(td->home_timeline_gc)
+ if (td->home_timeline_gc)
imcb_chat_free(td->home_timeline_gc);
- if( td )
- {
- oauth_info_free( td->oauth_info );
- g_free( td->prefix );
- g_free( td->url_host );
- g_free( td->url_path );
- g_free( td->pass );
- g_free( td->log );
- g_free( td );
+ if (td) {
+ oauth_info_free(td->oauth_info);
+ g_free(td->user);
+ g_free(td->prefix);
+ g_free(td->url_host);
+ g_free(td->url_path);
+ g_free(td->log);
+ g_free(td);
}
- twitter_connections = g_slist_remove( twitter_connections, ic );
+ twitter_connections = g_slist_remove(twitter_connections, ic);
}
-static void twitter_handle_command( struct im_connection *ic, char *message );
+static void twitter_handle_command(struct im_connection *ic, char *message);
/**
*
*/
-static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
+static int twitter_buddy_msg(struct im_connection *ic, char *who, char *message, int away)
{
struct twitter_data *td = ic->proto_data;
- int plen = strlen( td->prefix );
-
+ int plen = strlen(td->prefix);
+
if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' &&
- g_strcasecmp(who + plen + 1, ic->acc->user) == 0)
- {
- if( set_getbool( &ic->acc->set, "oauth" ) &&
- td->oauth_info && td->oauth_info->token == NULL )
- {
- char pin[strlen(message)+1], *s;
-
- strcpy( pin, message );
- for( s = pin + sizeof( pin ) - 2; s > pin && isspace( *s ); s -- )
+ g_strcasecmp(who + plen + 1, ic->acc->user) == 0) {
+ if (set_getbool(&ic->acc->set, "oauth") &&
+ td->oauth_info && td->oauth_info->token == NULL) {
+ char pin[strlen(message) + 1], *s;
+
+ strcpy(pin, message);
+ for (s = pin + sizeof(pin) - 2; s > pin && isspace(*s); s--)
*s = '\0';
- for( s = pin; *s && isspace( *s ); s ++ ) {}
-
- if( !oauth_access_token( s, td->oauth_info ) )
- {
- imcb_error( ic, "OAuth error: %s", "Failed to send access token request" );
- imc_logout( ic, TRUE );
+ for (s = pin; *s && isspace(*s); s++) {
+ }
+
+ if (!oauth_access_token(s, td->oauth_info)) {
+ imcb_error(ic, "OAuth error: %s",
+ "Failed to send access token request");
+ imc_logout(ic, TRUE);
return FALSE;
}
- }
- else
+ } else
twitter_handle_command(ic, message);
- }
- else
- {
+ } else {
twitter_direct_messages_new(ic, who, message);
}
- return( 0 );
+ return (0);
}
/**
*
*/
-static void twitter_set_my_name( struct im_connection *ic, char *info )
+static void twitter_set_my_name(struct im_connection *ic, char *info)
{
}
-static void twitter_get_info(struct im_connection *ic, char *who)
+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_add_buddy(struct im_connection *ic, char *who, char *group)
{
twitter_friendships_create_destroy(ic, who, 1);
}
-static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group )
+static void twitter_remove_buddy(struct im_connection *ic, char *who, char *group)
{
twitter_friendships_create_destroy(ic, who, 0);
}
-static void twitter_chat_msg( struct groupchat *c, char *message, int flags )
+static void twitter_chat_msg(struct groupchat *c, char *message, int flags)
{
- if( c && message )
- twitter_handle_command( c->ic, message );
+ if (c && message)
+ twitter_handle_command(c->ic, message);
}
-static void twitter_chat_invite( struct groupchat *c, char *who, char *message )
+static void twitter_chat_invite(struct groupchat *c, char *who, char *message)
{
}
-static void twitter_chat_leave( struct groupchat *c )
+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 (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 void twitter_keepalive( struct im_connection *ic )
+static void twitter_keepalive(struct im_connection *ic)
{
}
-static void twitter_add_permit( struct im_connection *ic, char *who )
+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_rem_permit(struct im_connection *ic, char *who)
{
}
-static void twitter_add_deny( 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 void twitter_rem_deny(struct im_connection *ic, char *who)
{
}
//static char *twitter_set_display_name( set_t *set, char *value )
//{
-// return value;
+// return value;
//}
-static void twitter_buddy_data_add( struct bee_user *bu )
+static void twitter_buddy_data_add(struct bee_user *bu)
{
- bu->data = g_new0( struct twitter_user_data, 1 );
+ bu->data = g_new0(struct twitter_user_data, 1);
}
-static void twitter_buddy_data_free( struct bee_user *bu )
+static void twitter_buddy_data_free(struct bee_user *bu)
{
- g_free( bu->data );
+ g_free(bu->data);
}
-static void twitter_handle_command( struct im_connection *ic, char *message )
+static void twitter_handle_command(struct im_connection *ic, char *message)
{
struct twitter_data *td = ic->proto_data;
char *cmds, **cmd, *new = NULL;
guint64 in_reply_to = 0;
-
- cmds = g_strdup( message );
- cmd = split_command_parts( cmds );
-
- if( cmd[0] == NULL )
- {
- g_free( cmds );
+
+ cmds = g_strdup(message);
+ cmd = split_command_parts(cmds);
+
+ if (cmd[0] == NULL) {
+ g_free(cmds);
return;
- }
- else if( !set_getbool( &ic->acc->set, "commands" ) )
- {
+ } else if (!set_getbool(&ic->acc->set, "commands")) {
/* Not supporting commands. */
- }
- else if( g_strcasecmp( cmd[0], "undo" ) == 0 )
- {
+ } else if (g_strcasecmp(cmd[0], "undo") == 0) {
guint64 id;
-
- if( cmd[1] )
- id = g_ascii_strtoull( cmd[1], NULL, 10 );
+
+ if (cmd[1])
+ id = g_ascii_strtoull(cmd[1], NULL, 10);
else
id = td->last_status_id;
-
+
/* TODO: User feedback. */
- if( id )
- twitter_status_destroy( ic, id );
+ if (id)
+ twitter_status_destroy(ic, id);
else
- twitter_msg( ic, "Could not undo last action" );
-
- g_free( cmds );
+ twitter_msg(ic, "Could not undo last action");
+
+ g_free(cmds);
return;
- }
- else if( g_strcasecmp( cmd[0], "follow" ) == 0 && cmd[1] )
- {
- twitter_add_buddy( ic, cmd[1], NULL );
- g_free( cmds );
+ } else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) {
+ twitter_add_buddy(ic, cmd[1], NULL);
+ g_free(cmds);
return;
- }
- else if( g_strcasecmp( cmd[0], "unfollow" ) == 0 && cmd[1] )
- {
- twitter_remove_buddy( ic, cmd[1], NULL );
- g_free( cmds );
+ } else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
+ twitter_remove_buddy(ic, cmd[1], NULL);
+ g_free(cmds);
return;
- }
- else if( g_strcasecmp( cmd[0], "rt" ) == 0 && cmd[1] )
- {
+ } else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) {
struct twitter_user_data *tud;
bee_user_t *bu;
guint64 id;
-
- if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] ) ) &&
- ( tud = bu->data ) && tud->last_id )
+
+ if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) &&
+ (tud = bu->data) && tud->last_id)
id = tud->last_id;
- else
- {
- id = g_ascii_strtoull( cmd[1], NULL, 10 );
- if( id < TWITTER_LOG_LENGTH && td->log )
+ else {
+ id = g_ascii_strtoull(cmd[1], NULL, 10);
+ if (id < TWITTER_LOG_LENGTH && td->log)
id = td->log[id].id;
}
-
+
td->last_status_id = 0;
- if( id )
- twitter_status_retweet( ic, id );
+ if (id)
+ twitter_status_retweet(ic, id);
else
- twitter_msg( ic, "User `%s' does not exist or didn't "
- "post any statuses recently", cmd[1] );
-
- g_free( cmds );
+ twitter_msg(ic, "User `%s' does not exist or didn't "
+ "post any statuses recently", cmd[1]);
+
+ g_free(cmds);
return;
- }
- else if( g_strcasecmp( cmd[0], "reply" ) == 0 && cmd[1] && cmd[2] )
- {
+ } else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) {
struct twitter_user_data *tud;
bee_user_t *bu = NULL;
guint64 id = 0;
-
- if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[1] ) ) &&
- ( tud = bu->data ) && tud->last_id )
- {
+
+ if ((bu = bee_user_by_handle(ic->bee, ic, cmd[1])) &&
+ (tud = bu->data) && tud->last_id) {
id = tud->last_id;
- }
- else if( ( id = g_ascii_strtoull( cmd[1], NULL, 10 ) ) &&
- ( id < TWITTER_LOG_LENGTH ) && td->log )
- {
+ } else if (sscanf(cmd[1], "%" G_GUINT64_FORMAT, &id) == 1 &&
+ (id < TWITTER_LOG_LENGTH) && td->log) {
bu = td->log[id].bu;
- if( g_slist_find( ic->bee->users, bu ) )
+ if (g_slist_find(ic->bee->users, bu))
id = td->log[id].id;
else
bu = NULL;
}
- if( !id || !bu )
- {
- twitter_msg( ic, "User `%s' does not exist or didn't "
- "post any statuses recently", cmd[1] );
+ if (!id || !bu) {
+ twitter_msg(ic, "User `%s' does not exist or didn't "
+ "post any statuses recently", cmd[1]);
return;
}
- message = new = g_strdup_printf( "@%s %s", bu->handle,
- message + ( cmd[2] - cmd[0] ) );
+ message = new = g_strdup_printf("@%s %s", bu->handle, message + (cmd[2] - cmd[0]));
in_reply_to = id;
- }
- else if( g_strcasecmp( cmd[0], "post" ) == 0 )
- {
+ } else if (g_strcasecmp(cmd[0], "post") == 0) {
message += 5;
}
-
+
{
char *s;
bee_user_t *bu;
-
- if( !twitter_length_check( ic, message ) )
- {
- g_free( new );
- g_free( cmds );
- return;
+
+ if (!twitter_length_check(ic, message)) {
+ g_free(new);
+ g_free(cmds);
+ return;
}
-
- s = cmd[0] + strlen( cmd[0] ) - 1;
- if( !new && s > cmd[0] && ( *s == ':' || *s == ',' ) )
- {
+
+ s = cmd[0] + strlen(cmd[0]) - 1;
+ if (!new && s > cmd[0] && (*s == ':' || *s == ',')) {
*s = '\0';
-
- if( ( bu = bee_user_by_handle( ic->bee, ic, cmd[0] ) ) )
- {
+
+ if ((bu = bee_user_by_handle(ic->bee, ic, cmd[0]))) {
struct twitter_user_data *tud = bu->data;
-
- new = g_strdup_printf( "@%s %s", bu->handle,
- message + ( s - cmd[0] ) + 2 );
+
+ new = g_strdup_printf("@%s %s", bu->handle,
+ message + (s - cmd[0]) + 2);
message = new;
-
- if( time( NULL ) < tud->last_time +
- set_getint( &ic->acc->set, "auto_reply_timeout" ) )
+
+ if (time(NULL) < tud->last_time +
+ set_getint(&ic->acc->set, "auto_reply_timeout"))
in_reply_to = tud->last_id;
}
}
-
+
/* If the user runs undo between this request and its response
this would delete the second-last Tweet. Prevent that. */
td->last_status_id = 0;
- twitter_post_status( ic, message, in_reply_to );
- g_free( new );
+ twitter_post_status(ic, message, in_reply_to);
+ g_free(new);
}
- g_free( cmds );
+ g_free(cmds);
}
void twitter_initmodule()
{
struct prpl *ret = g_new0(struct prpl, 1);
-
+
ret->options = OPT_NOOTR;
ret->name = "twitter";
ret->login = twitter_login;
@@ -623,7 +596,7 @@ void twitter_initmodule()
ret->buddy_data_add = twitter_buddy_data_add;
ret->buddy_data_free = twitter_buddy_data_free;
ret->handle_cmp = g_strcasecmp;
-
+
register_protocol(ret);
/* And an identi.ca variant: */
diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h
index 5bce97d4..c38d9b86 100644
--- a/protocols/twitter/twitter.h
+++ b/protocols/twitter/twitter.h
@@ -42,8 +42,9 @@ struct twitter_log_data;
struct twitter_data
{
char* user;
- char* pass;
struct oauth_info *oauth_info;
+ GSList *follow_ids;
+
guint64 home_timeline_id;
guint64 last_status_id; /* For undo */
gint main_loop_id;
@@ -51,6 +52,7 @@ struct twitter_data
gint http_fails;
twitter_flags_t flags;
+ /* set base_url */
gboolean url_ssl;
int url_port;
char *url_host;
@@ -58,6 +60,7 @@ struct twitter_data
char *prefix; /* Used to generate contact + channel name. */
+ /* set show_ids */
struct twitter_log_data *log;
int log_id;
};
@@ -84,4 +87,7 @@ extern GSList *twitter_connections;
void twitter_login_finish( struct im_connection *ic );
+struct http_request;
+char *twitter_parse_error( struct http_request *req );
+
#endif //_TWITTER_H
diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c
index ff17f5f4..dbac5461 100644
--- a/protocols/twitter/twitter_http.c
+++ b/protocols/twitter/twitter_http.c
@@ -26,7 +26,7 @@
* Some funtions within this file have been copied from other files within *
* BitlBee. *
* *
-****************************************************************************/
+****************************************************************************/
#include "twitter.h"
#include "bitlbee.h"
@@ -40,13 +40,14 @@
#include "twitter_http.h"
-static char *twitter_url_append(char *url, char *key, char* value);
+static char *twitter_url_append(char *url, char *key, char *value);
/**
* Do a request.
* This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
*/
-void *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, gpointer data, int is_post, char** arguments, int arguments_len)
+void *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
+ gpointer data, int is_post, char **arguments, int arguments_len)
{
struct twitter_data *td = ic->proto_data;
char *tmp;
@@ -57,59 +58,51 @@ void *twitter_http(struct im_connection *ic, char *url_string, http_input_functi
url_arguments = g_strdup("");
// Construct the url arguments.
- if (arguments_len != 0)
- {
+ if (arguments_len != 0) {
int i;
- for (i=0; i<arguments_len; i+=2)
- {
- tmp = twitter_url_append(url_arguments, arguments[i], arguments[i+1]);
+ for (i = 0; i < arguments_len; i += 2) {
+ tmp = twitter_url_append(url_arguments, arguments[i], arguments[i + 1]);
g_free(url_arguments);
url_arguments = tmp;
}
}
-
// Make the request.
g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n"
- "Host: %s\r\n"
- "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
- is_post ? "POST" : "GET",
- td->url_path, url_string,
- is_post ? "" : "?", is_post ? "" : url_arguments,
- td->url_host);
+ "Host: %s\r\n"
+ "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
+ is_post ? "POST" : "GET",
+ td->url_path, url_string,
+ is_post ? "" : "?", is_post ? "" : url_arguments, td->url_host);
// If a pass and user are given we append them to the request.
- if (td->oauth_info)
- {
+ if (td->oauth_info) {
char *full_header;
char *full_url;
-
- full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url" ), url_string, NULL);
+
+ full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL);
full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET",
- full_url, url_arguments);
-
+ full_url, url_arguments);
+
g_string_append_printf(request, "Authorization: %s\r\n", full_header);
g_free(full_header);
g_free(full_url);
- }
- else
- {
- char userpass[strlen(ic->acc->user)+2+strlen(ic->acc->pass)];
+ } else {
+ char userpass[strlen(ic->acc->user) + 2 + strlen(ic->acc->pass)];
char *userpass_base64;
-
+
g_snprintf(userpass, sizeof(userpass), "%s:%s", ic->acc->user, ic->acc->pass);
- userpass_base64 = base64_encode((unsigned char*)userpass, strlen(userpass));
+ userpass_base64 = base64_encode((unsigned char *) userpass, strlen(userpass));
g_string_append_printf(request, "Authorization: Basic %s\r\n", userpass_base64);
- g_free( userpass_base64 );
+ g_free(userpass_base64);
}
// Do POST stuff..
- if (is_post)
- {
+ if (is_post) {
// Append the Content-Type and url-encoded arguments.
g_string_append_printf(request,
- "Content-Type: application/x-www-form-urlencoded\r\n"
- "Content-Length: %zd\r\n\r\n%s",
- strlen(url_arguments), url_arguments);
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: %zd\r\n\r\n%s",
+ strlen(url_arguments), url_arguments);
} else {
// Append an extra \r\n to end the request...
g_string_append(request, "\r\n");
@@ -117,12 +110,12 @@ void *twitter_http(struct im_connection *ic, char *url_string, http_input_functi
ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data);
- g_free( url_arguments );
- g_string_free( request, TRUE );
+ g_free(url_arguments);
+ g_string_free(request, TRUE);
return ret;
}
-static char *twitter_url_append(char *url, char *key, char* value)
+static char *twitter_url_append(char *url, char *key, char *value)
{
char *key_encoded = g_strndup(key, 3 * strlen(key));
http_encode(key_encoded);
diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c
index e8fb3530..14e98c53 100644
--- a/protocols/twitter/twitter_lib.c
+++ b/protocols/twitter/twitter_lib.c
@@ -54,7 +54,6 @@ struct twitter_xml_list {
int type;
gint64 next_cursor;
GSList *list;
- gpointer data;
};
struct twitter_xml_user {
@@ -103,11 +102,13 @@ static void txl_free(struct twitter_xml_list *txl)
GSList *l;
if (txl == NULL)
return;
- for ( l = txl->list; l ; l = g_slist_next(l) )
+ for (l = txl->list; l; l = g_slist_next(l))
if (txl->type == TXL_STATUS)
- txs_free((struct twitter_xml_status *)l->data);
+ txs_free((struct twitter_xml_status *) l->data);
else if (txl->type == TXL_ID)
g_free(l->data);
+ else if (txl->type == TXL_USER)
+ txu_free(l->data);
g_slist_free(txl->list);
g_free(txl);
}
@@ -119,55 +120,48 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *
{
struct twitter_data *td = ic->proto_data;
- // Check if the buddy is allready in the buddy list.
- if (!bee_user_by_handle( ic->bee, ic, name ))
- {
+ // Check if the buddy is already in the buddy list.
+ if (!bee_user_by_handle(ic->bee, ic, name)) {
char *mode = set_getstr(&ic->acc->set, "mode");
-
+
// The buddy is not in the list, add the buddy and set the status to logged in.
- imcb_add_buddy( ic, name, NULL );
- imcb_rename_buddy( ic, name, fullname );
- if (g_strcasecmp(mode, "chat") == 0)
- {
+ imcb_add_buddy(ic, name, NULL);
+ imcb_rename_buddy(ic, name, fullname);
+ if (g_strcasecmp(mode, "chat") == 0) {
/* Necessary so that nicks always get translated to the
exact Twitter username. */
- imcb_buddy_nick_hint( ic, name, name );
- imcb_chat_add_buddy( td->home_timeline_gc, name );
- }
- else if (g_strcasecmp(mode, "many") == 0)
- imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
+ imcb_buddy_nick_hint(ic, name, name);
+ imcb_chat_add_buddy(td->home_timeline_gc, name);
+ } else if (g_strcasecmp(mode, "many") == 0)
+ imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
}
}
/* Warning: May return a malloc()ed value, which will be free()d on the next
- call. Only for short-term use. */
-static char *twitter_parse_error(struct http_request *req)
+ call. Only for short-term use. NOT THREADSAFE! */
+char *twitter_parse_error(struct http_request *req)
{
static char *ret = NULL;
struct xt_parser *xp = NULL;
- struct xt_node *node;
-
+ struct xt_node *node, *err;
+
g_free(ret);
ret = NULL;
-
- if (req->body_size > 0)
- {
+
+ if (req->body_size > 0) {
xp = xt_new(NULL, NULL);
xt_feed(xp, req->reply_body, req->body_size);
- if ((node = xt_find_node(xp->root, "hash")) &&
- (node = xt_find_node(node->children, "error")) &&
- node->text_len > 0)
- {
- ret = g_strdup_printf("%s (%s)", req->status_string, node->text);
- xt_free(xp);
- return ret;
- }
-
+ for (node = xp->root; node; node = node->next)
+ if ((err = xt_find_node(node->children, "error")) && err->text_len > 0) {
+ ret = g_strdup_printf("%s (%s)", req->status_string, err->text);
+ break;
+ }
+
xt_free(xp);
}
-
- return req->status_string;
+
+ return ret ? ret : req->status_string;
}
static void twitter_http_get_friends_ids(struct http_request *req);
@@ -177,10 +171,10 @@ static void twitter_http_get_friends_ids(struct http_request *req);
*/
void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
{
- // Primitive, but hey! It works...
- char* args[2];
+ // Primitive, but hey! It works...
+ char *args[2];
args[0] = "cursor";
- args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
+ args[1] = g_strdup_printf("%lld", (long long) next_cursor);
twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
g_free(args[1]);
@@ -189,13 +183,13 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
/**
* Function to help fill a list.
*/
-static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
+static xt_status twitter_xt_next_cursor(struct xt_node *node, struct twitter_xml_list *txl)
{
char *end = NULL;
-
- if( node->text )
- txl->next_cursor = g_ascii_strtoll( node->text, &end, 10 );
- if( end == NULL )
+
+ if (node->text)
+ txl->next_cursor = g_ascii_strtoll(node->text, &end, 10);
+ if (end == NULL)
txl->next_cursor = -1;
return XT_HANDLED;
@@ -204,24 +198,23 @@ static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xm
/**
* Fill a list of ids.
*/
-static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
+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 <statuses> node should hold the list of statuses <status>
// 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( child->text, child->text_len + 1 ));
- }
- else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
- {
+ for (child = node->children; child; child = child->next) {
+ if (g_strcasecmp("ids", child->name) == 0) {
+ struct xt_node *idc;
+ for (idc = child->children; idc; idc = idc->next)
+ if (g_strcasecmp(idc->name, "id") == 0)
+ txl->list = g_slist_prepend(txl->list,
+ g_memdup(idc->text, idc->text_len + 1));
+ } else if (g_strcasecmp("next_cursor", child->name) == 0) {
twitter_xt_next_cursor(child, txl);
}
}
@@ -229,6 +222,8 @@ static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct tw
return XT_HANDLED;
}
+static void twitter_get_users_lookup(struct im_connection *ic);
+
/**
* Callback for getting the friends ids.
*/
@@ -242,34 +237,137 @@ 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 ) )
+ if (!g_slist_find(twitter_connections, ic))
return;
-
+
td = ic->proto_data;
- // Check if the HTTP request went well.
- if (req->status_code != 200) {
+ // Check if the HTTP request went well. More strict checks as this is
+ // the first request we do in a session.
+ if (req->status_code == 401) {
+ imcb_error(ic, "Authentication failure");
+ imc_logout(ic, FALSE);
+ return;
+ } else if (req->status_code != 200) {
// It didn't go well, output the error and return.
- if (++td->http_fails >= 5)
- imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));
-
+ imcb_error(ic, "Could not retrieve %s: %s",
+ TWITTER_FRIENDS_IDS_URL, twitter_parse_error(req));
+ imc_logout(ic, TRUE);
return;
} else {
td->http_fails = 0;
}
+ /* Create the room now that we "logged in". */
+ if (!td->home_timeline_gc && g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
+ twitter_groupchat_init(ic);
+
txl = g_new0(struct twitter_xml_list, 1);
+ txl->list = td->follow_ids;
// Parse the data.
- parser = xt_new( NULL, txl );
- xt_feed( parser, req->reply_body, req->body_size );
+ 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 );
+ xt_free(parser);
+ td->follow_ids = txl->list;
if (txl->next_cursor)
+ /* These were just numbers. Up to 4000 in a response AFAIK so if we get here
+ we may be using a spammer account. \o/ */
twitter_get_friends_ids(ic, txl->next_cursor);
+ else
+ /* Now to convert all those numbers into names.. */
+ twitter_get_users_lookup(ic);
+
+ txl->list = NULL;
+ txl_free(txl);
+}
+
+static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl);
+static void twitter_http_get_users_lookup(struct http_request *req);
+
+static void twitter_get_users_lookup(struct im_connection *ic)
+{
+ struct twitter_data *td = ic->proto_data;
+ char *args[2] = {
+ "user_id",
+ NULL,
+ };
+ GString *ids = g_string_new("");
+ int i;
+
+ /* We can request up to 100 users at a time. */
+ for (i = 0; i < 100 && td->follow_ids; i ++) {
+ g_string_append_printf(ids, ",%s", (char*) td->follow_ids->data);
+ g_free(td->follow_ids->data);
+ td->follow_ids = g_slist_remove(td->follow_ids, td->follow_ids->data);
+ }
+ if (ids->len > 0) {
+ args[1] = ids->str + 1;
+ /* POST, because I think ids can be up to 1KB long. */
+ twitter_http(ic, TWITTER_USERS_LOOKUP_URL, twitter_http_get_users_lookup, ic, 1, args, 2);
+ } else {
+ /* We have all users. Continue with login. (Get statuses.) */
+ td->flags |= TWITTER_HAVE_FRIENDS;
+ twitter_login_finish(ic);
+ }
+ g_string_free(ids, TRUE);
+}
+
+/**
+ * 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!
+ */
+static void twitter_http_get_users_lookup(struct http_request *req)
+{
+ struct im_connection *ic = req->data;
+ struct twitter_data *td;
+ 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))
+ return;
+
+ td = ic->proto_data;
+
+ if (req->status_code != 200) {
+ // It didn't go well, output the error and return.
+ imcb_error(ic, "Could not retrieve %s: %s",
+ TWITTER_USERS_LOOKUP_URL, twitter_parse_error(req));
+ imc_logout(ic, TRUE);
+ return;
+ } else {
+ td->http_fails = 0;
+ }
+
+ 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_users(parser->root, txl);
+ xt_free(parser);
+
+ // Add the users as buddies.
+ for (l = txl->list; l; l = g_slist_next(l)) {
+ user = l->data;
+ twitter_add_buddy(ic, user->screen_name, user->name);
+ }
+
+ // Free the structure.
txl_free(txl);
+
+ twitter_get_users_lookup(ic);
}
/**
@@ -278,20 +376,16 @@ static void twitter_http_get_friends_ids(struct http_request *req)
* - the name and
* - the screen_name.
*/
-static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu )
+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 );
+ 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;
@@ -302,7 +396,7 @@ static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_u
* It sets:
* - all <user>s from the <users> element.
*/
-static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl )
+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;
@@ -312,44 +406,12 @@ static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_
// The root <users> node should hold the list of users <user>
// Walk over the nodes children.
- for( child = node->children ; child ; child = child->next )
- {
- if ( g_strcasecmp( "user", child->name ) == 0)
- {
+ 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 <user>s from a <users> 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 <user_list> node should hold a users <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);
+ txl->list = g_slist_prepend(txl->list, txu);
}
}
@@ -370,66 +432,50 @@ static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_
* - 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 )
+static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs)
{
struct xt_node *child, *rt = NULL;
gboolean truncated = FALSE;
// 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( "truncated", child->name ) == 0 && child->text)
- {
+ 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("truncated", child->name) == 0 && child->text) {
truncated = bool2int(child->text);
- }
- else if (g_strcasecmp( "retweeted_status", child->name ) == 0)
- {
+ } else if (g_strcasecmp("retweeted_status", child->name) == 0) {
rt = child;
- }
- else if (g_strcasecmp( "created_at", child->name ) == 0)
- {
+ } else if (g_strcasecmp("created_at", child->name) == 0) {
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, TWITTER_TIME_FORMAT, &parsed ) != NULL )
- txs->created_at = mktime_utc( &parsed );
- }
- else if (g_strcasecmp( "user", child->name ) == 0)
- {
+ if (strptime(child->text, TWITTER_TIME_FORMAT, &parsed) != NULL)
+ txs->created_at = mktime_utc(&parsed);
+ } 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);
- }
- else if (g_strcasecmp( "in_reply_to_status_id", child->name ) == 0)
- {
- txs->reply_to = g_ascii_strtoull (child->text, NULL, 10);
+ twitter_xt_get_user(child, txs->user);
+ } else if (g_strcasecmp("id", child->name) == 0) {
+ txs->id = g_ascii_strtoull(child->text, NULL, 10);
+ } else if (g_strcasecmp("in_reply_to_status_id", child->name) == 0) {
+ txs->reply_to = g_ascii_strtoull(child->text, NULL, 10);
}
}
-
+
/* If it's a truncated retweet, get the original because dots suck. */
- if (truncated && rt)
- {
+ if (truncated && rt) {
struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1);
- if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED)
- {
+ if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) {
txs_free(rtxs);
return XT_HANDLED;
}
-
+
g_free(txs->text);
txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
txs_free(rtxs);
}
-
+
return XT_HANDLED;
}
@@ -439,7 +485,8 @@ static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml
* - all <status>es within the <status> element and
* - the next_cursor.
*/
-static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt_node *node, struct twitter_xml_list *txl )
+static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node,
+ struct twitter_xml_list *txl)
{
struct twitter_xml_status *txs;
struct xt_node *child;
@@ -450,29 +497,23 @@ static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt
// The root <statuses> node should hold the list of statuses <status>
// Walk over the nodes children.
- for( child = node->children ; child ; child = child->next )
- {
- if ( g_strcasecmp( "status", child->name ) == 0)
- {
+ 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);
-
+ txl->list = g_slist_prepend(txl->list, txs);
+
if (txs->user && txs->user->screen_name &&
- (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name)))
- {
+ (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
struct twitter_user_data *tud = bu->data;
-
- if (txs->id > tud->last_id)
- {
+
+ if (txs->id > tud->last_id) {
tud->last_id = txs->id;
tud->last_time = txs->created_at;
}
}
- }
- else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
- {
+ } else if (g_strcasecmp("next_cursor", child->name) == 0) {
twitter_xt_next_cursor(child, txl);
}
}
@@ -489,15 +530,16 @@ void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
{
struct twitter_data *td = ic->proto_data;
- char* args[4];
+ char *args[4];
args[0] = "cursor";
- args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
+ args[1] = g_strdup_printf("%lld", (long long) next_cursor);
if (td->home_timeline_id) {
args[2] = "since_id";
- args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);
+ args[3] = g_strdup_printf("%llu", (long long unsigned int) td->home_timeline_id);
}
- twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2);
+ twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
+ td->home_timeline_id ? 4 : 2);
g_free(args[1]);
if (td->home_timeline_id) {
@@ -506,37 +548,33 @@ void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
}
static char *twitter_msg_add_id(struct im_connection *ic,
- struct twitter_xml_status *txs, const char *prefix)
+ struct twitter_xml_status *txs, const char *prefix)
{
struct twitter_data *td = ic->proto_data;
char *ret = NULL;
-
- if (!set_getbool(&ic->acc->set, "show_ids"))
- {
+
+ if (!set_getbool(&ic->acc->set, "show_ids")) {
if (*prefix)
return g_strconcat(prefix, txs->text, NULL);
else
return NULL;
}
-
+
td->log[td->log_id].id = txs->id;
td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
- if (txs->reply_to)
- {
+ if (txs->reply_to) {
int i;
- for (i = 0; i < TWITTER_LOG_LENGTH; i ++)
- if (td->log[i].id == txs->reply_to)
- {
- ret = g_strdup_printf( "\002[\002%02d->%02d\002]\002 %s%s",
- td->log_id, i, prefix, txs->text);
+ for (i = 0; i < TWITTER_LOG_LENGTH; i++)
+ if (td->log[i].id == txs->reply_to) {
+ ret = g_strdup_printf("\002[\002%02d->%02d\002]\002 %s%s",
+ td->log_id, i, prefix, txs->text);
break;
}
}
if (ret == NULL)
- ret = g_strdup_printf( "\002[\002%02d\002]\002 %s%s",
- td->log_id, prefix, txs->text);
+ ret = g_strdup_printf("\002[\002%02d\002]\002 %s%s", td->log_id, prefix, txs->text);
td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
-
+
return ret;
}
@@ -546,25 +584,24 @@ static void twitter_groupchat_init(struct im_connection *ic)
struct groupchat *gc;
struct twitter_data *td = ic->proto_data;
GSList *l;
-
- td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
-
- name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user );
- imcb_chat_name_hint( gc, name_hint );
- g_free( name_hint );
-
- for( l = ic->bee->users; l; l = l->next )
- {
+
+ td->home_timeline_gc = gc = imcb_chat_new(ic, "home/timeline");
+
+ name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
+ imcb_chat_name_hint(gc, name_hint);
+ g_free(name_hint);
+
+ for (l = ic->bee->users; l; l = l->next) {
bee_user_t *bu = l->data;
- if( bu->ic == ic )
- imcb_chat_add_buddy( td->home_timeline_gc, bu->handle );
+ if (bu->ic == ic)
+ imcb_chat_add_buddy(td->home_timeline_gc, bu->handle);
}
}
/**
* Function that is called to see the statuses in a groupchat window.
*/
-static void twitter_groupchat(struct im_connection *ic, GSList *list)
+static void twitter_groupchat(struct im_connection *ic, GSList * list)
{
struct twitter_data *td = ic->proto_data;
GSList *l = NULL;
@@ -574,33 +611,32 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list)
// Create a new groupchat if it does not exsist.
if (!td->home_timeline_gc)
twitter_groupchat_init(ic);
-
+
gc = td->home_timeline_gc;
if (!gc->joined)
- imcb_chat_add_buddy( gc, ic->acc->user );
+ imcb_chat_add_buddy(gc, ic->acc->user);
- for ( l = list; l ; l = g_slist_next(l) )
- {
+ for (l = list; l; l = g_slist_next(l)) {
char *msg;
-
+
status = l->data;
if (status->user == NULL || status->text == NULL)
continue;
twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-
+
strip_html(status->text);
msg = twitter_msg_add_id(ic, status, "");
-
+
// Say it!
if (g_strcasecmp(td->user, status->user->screen_name) == 0)
imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
else
imcb_chat_msg(gc, status->user->screen_name,
- msg ? msg : status->text, 0, status->created_at );
-
+ msg ? msg : status->text, 0, status->created_at);
+
g_free(msg);
-
+
// Update the home_timeline_id to hold the highest id, so that by the next request
// we won't pick up the updates already in the list.
td->home_timeline_id = MAX(td->home_timeline_id, status->id);
@@ -610,48 +646,45 @@ static void twitter_groupchat(struct im_connection *ic, GSList *list)
/**
* Function that is called to see statuses as private messages.
*/
-static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
+static void twitter_private_message_chat(struct im_connection *ic, GSList * list)
{
struct twitter_data *td = ic->proto_data;
GSList *l = NULL;
struct twitter_xml_status *status;
char from[MAX_STRING];
gboolean mode_one;
-
- mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
- if( mode_one )
- {
- g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user );
- from[MAX_STRING-1] = '\0';
+ mode_one = g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "one") == 0;
+
+ if (mode_one) {
+ g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user);
+ from[MAX_STRING - 1] = '\0';
}
-
- for ( l = list; l ; l = g_slist_next(l) )
- {
+
+ for (l = list; l; l = g_slist_next(l)) {
char *prefix = NULL, *text = NULL;
-
+
status = l->data;
-
- strip_html( status->text );
- if( mode_one )
+
+ strip_html(status->text);
+ if (mode_one)
prefix = g_strdup_printf("\002<\002%s\002>\002 ",
- status->user->screen_name);
+ status->user->screen_name);
else
twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-
+
text = twitter_msg_add_id(ic, status, prefix ? prefix : "");
-
- imcb_buddy_msg( ic,
- mode_one ? from : status->user->screen_name,
- text ? text : status->text,
- 0, status->created_at );
-
+
+ imcb_buddy_msg(ic,
+ mode_one ? from : status->user->screen_name,
+ text ? text : status->text, 0, status->created_at);
+
// Update the home_timeline_id to hold the highest id, so that by the next request
// we won't pick up the updates already in the list.
- td->home_timeline_id = MAX(td->home_timeline_id, status->id);
-
- g_free( text );
- g_free( prefix );
+ td->home_timeline_id = MAX(td->home_timeline_id, status->id);
+
+ g_free(text);
+ g_free(prefix);
}
}
@@ -666,30 +699,26 @@ static void twitter_http_get_home_timeline(struct http_request *req)
struct twitter_xml_list *txl;
// Check if the connection is still active.
- if( !g_slist_find( twitter_connections, ic ) )
+ if (!g_slist_find(twitter_connections, ic))
return;
-
+
td = ic->proto_data;
// 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 );
+ } else if (req->status_code == 401) {
+ imcb_error(ic, "Authentication failure");
+ imc_logout(ic, FALSE);
return;
- }
- else
- {
+ } else {
// It didn't go well, output the error and return.
if (++td->http_fails >= 5)
- imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req));
-
+ imcb_error(ic, "Could not retrieve %s: %s",
+ TWITTER_HOME_TIMELINE_URL, twitter_parse_error(req));
+
return;
}
@@ -697,114 +726,26 @@ static void twitter_http_get_home_timeline(struct http_request *req)
txl->list = NULL;
// Parse the data.
- parser = xt_new( NULL, txl );
- xt_feed( parser, req->reply_body, req->body_size );
+ parser = xt_new(NULL, txl);
+ xt_feed(parser, req->reply_body, req->body_size);
// The root <statuses> node should hold the list of statuses <status>
twitter_xt_get_status_list(ic, parser->root, txl);
- xt_free( parser );
+ xt_free(parser);
// See if the user wants to see the messages in a groupchat window or as private messages.
- if (txl->list == NULL)
- ;
+ if (txl->list == NULL);
else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
twitter_groupchat(ic, txl->list);
else
twitter_private_message_chat(ic, txl->list);
- // Free the structure.
+ // Free the structure.
txl_free(txl);
}
/**
- * 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!
- */
-static void twitter_http_get_statuses_friends(struct http_request *req)
-{
- struct im_connection *ic = req->data;
- struct twitter_data *td;
- 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 ) )
- return;
-
- td = ic->proto_data;
-
- // Check if the HTTP request went well.
- if (req->status_code == 401)
- {
- imcb_error( ic, "Authentication failure" );
- imc_logout( ic, FALSE );
- return;
- } else 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 ": %s", twitter_parse_error(req));
- imc_logout( ic, TRUE );
- return;
- } else {
- td->http_fails = 0;
- }
-
- if( !td->home_timeline_gc &&
- g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "chat" ) == 0 )
- twitter_groupchat_init( ic );
-
- 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 );
-
- // Add the users as buddies.
- for ( l = txl->list; l ; l = g_slist_next(l) )
- {
- user = l->data;
- 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.
- if (txl->next_cursor > 0)
- {
- twitter_get_statuses_friends(ic, txl->next_cursor);
- }
- else
- {
- td->flags |= TWITTER_HAVE_FRIENDS;
- twitter_login_finish(ic);
- }
-
- // Free the structure.
- txl_free(txl);
-}
-
-/**
- * Get the friends.
- */
-void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor)
-{
- char* args[2];
- args[0] = "cursor";
- args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
-
- twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args, 2);
-
- g_free(args[1]);
-}
-
-/**
- * Callback to use after sending a post request to twitter.
+ * Callback to use after sending a POST request to twitter.
+ * (Generic, used for a few kinds of queries.)
*/
static void twitter_http_post(struct http_request *req)
{
@@ -812,47 +753,46 @@ static void twitter_http_post(struct http_request *req)
struct twitter_data *td;
// Check if the connection is still active.
- if( !g_slist_find( twitter_connections, ic ) )
+ if (!g_slist_find(twitter_connections, ic))
return;
td = ic->proto_data;
td->last_status_id = 0;
-
+
// 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, "HTTP error: %s", twitter_parse_error(req));
return;
}
-
- if (req->body_size > 0)
- {
+
+ if (req->body_size > 0) {
struct xt_parser *xp = NULL;
struct xt_node *node;
-
+
xp = xt_new(NULL, NULL);
xt_feed(xp, req->reply_body, req->body_size);
-
+
if ((node = xt_find_node(xp->root, "status")) &&
(node = xt_find_node(node->children, "id")) && node->text)
- td->last_status_id = g_ascii_strtoull( node->text, NULL, 10 );
-
+ td->last_status_id = g_ascii_strtoull(node->text, NULL, 10);
+
xt_free(xp);
}
}
/**
* Function to POST a new status to twitter.
- */
+ */
void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to)
{
- char* args[4] = {
+ char *args[4] = {
"status", msg,
"in_reply_to_status_id",
g_strdup_printf("%llu", (unsigned long long) in_reply_to)
};
twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
- args, in_reply_to ? 4 : 2);
+ args, in_reply_to ? 4 : 2);
g_free(args[3]);
}
@@ -862,29 +802,29 @@ void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_t
*/
void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
{
- char* args[4];
+ 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(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
-// g_free(args[1]);
-// g_free(args[3]);
}
void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
{
- char* args[2];
+ char *args[2];
args[0] = "screen_name";
args[1] = who;
- twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, twitter_http_post, ic, 1, args, 2);
+ twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL,
+ twitter_http_post, ic, 1, args, 2);
}
void twitter_status_destroy(struct im_connection *ic, guint64 id)
{
char *url;
- url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".xml");
+ url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL,
+ (unsigned long long) id, ".xml");
twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
g_free(url);
}
@@ -892,7 +832,8 @@ void twitter_status_destroy(struct im_connection *ic, guint64 id)
void twitter_status_retweet(struct im_connection *ic, guint64 id)
{
char *url;
- url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".xml");
+ url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL,
+ (unsigned long long) id, ".xml");
twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
g_free(url);
}
diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h
index 24b4a089..c33b2dfc 100644
--- a/protocols/twitter/twitter_lib.h
+++ b/protocols/twitter/twitter_lib.h
@@ -28,8 +28,8 @@
#include "nogaim.h"
#include "twitter_http.h"
-#define TWITTER_API_URL "http://twitter.com"
-#define IDENTICA_API_URL "http://identi.ca/api"
+#define TWITTER_API_URL "http://api.twitter.com/1"
+#define IDENTICA_API_URL "https://identi.ca/api"
/* Status URLs */
#define TWITTER_STATUS_UPDATE_URL "/statuses/update.xml"
@@ -46,9 +46,7 @@
#define TWITTER_USER_TIMELINE_URL "/statuses/user_timeline.xml"
/* Users URLs */
-#define TWITTER_SHOW_USERS_URL "/users/show.xml"
-#define TWITTER_SHOW_FRIENDS_URL "/statuses/friends.xml"
-#define TWITTER_SHOW_FOLLOWERS_URL "/statuses/followers.xml"
+#define TWITTER_USERS_LOOKUP_URL "/users/lookup.xml"
/* Direct messages URLs */
#define TWITTER_DIRECT_MESSAGES_URL "/direct_messages.xml"