diff options
Diffstat (limited to 'protocols/twitter')
| -rw-r--r-- | protocols/twitter/twitter.c | 255 | ||||
| -rw-r--r-- | protocols/twitter/twitter.h | 36 | ||||
| -rw-r--r-- | protocols/twitter/twitter_http.c | 54 | ||||
| -rw-r--r-- | protocols/twitter/twitter_http.h | 3 | ||||
| -rw-r--r-- | protocols/twitter/twitter_lib.c | 421 | 
5 files changed, 452 insertions, 317 deletions
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index eb30187f..f3dcde31 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -90,14 +90,15 @@ static struct twitter_filter *twitter_filter_get(struct groupchat *c,  {  	struct twitter_data *td = c->ic->proto_data;  	struct twitter_filter *tf = NULL; -	struct twitter_filter tfc = {type, (char*) text}; +	struct twitter_filter tfc = { type, (char *) text };  	GSList *l;  	for (l = td->filters; l; l = g_slist_next(l)) {  		tf = l->data; -		if (twitter_filter_cmp(tf, &tfc) == 0) +		if (twitter_filter_cmp(tf, &tfc) == 0) {  			break; +		}  		tf = NULL;  	} @@ -109,15 +110,17 @@ static struct twitter_filter *twitter_filter_get(struct groupchat *c,  		td->filters = g_slist_prepend(td->filters, tf);  	} -	if (!g_slist_find(tf->groupchats, c)) +	if (!g_slist_find(tf->groupchats, c)) {  		tf->groupchats = g_slist_prepend(tf->groupchats, c); +	} -	if (td->filter_update_id > 0) +	if (td->filter_update_id > 0) {  		b_event_remove(td->filter_update_id); +	}  	/* Wait for other possible filter changes to avoid request spam */  	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, -					     twitter_filter_update, c->ic); +	                                     twitter_filter_update, c->ic);  	return tf;  } @@ -148,12 +151,14 @@ static void twitter_filter_remove(struct groupchat *c)  		}  	} -	if (td->filter_update_id > 0) +	if (td->filter_update_id > 0) {  		b_event_remove(td->filter_update_id); +	}  	/* Wait for other possible filter changes to avoid request spam */  	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT, -					     twitter_filter_update, c->ic);} +	                                     twitter_filter_update, c->ic); +}  static void twitter_filter_remove_all(struct im_connection *ic)  { @@ -168,8 +173,9 @@ static void twitter_filter_remove_all(struct im_connection *ic)  		/* Build up a list of groupchats to be freed */  		for (p = tf->groupchats; p; p = g_slist_next(p)) { -			if (!g_slist_find(chats, p->data)) +			if (!g_slist_find(chats, p->data)) {  				chats = g_slist_prepend(chats, p->data); +			}  		}  		p = l; @@ -216,8 +222,9 @@ static GSList *twitter_filter_parse(struct groupchat *c, const char *text)  	};  	for (f = fs; *f; f++) { -		if ((v = strchr(*f, ':')) == NULL) +		if ((v = strchr(*f, ':')) == NULL) {  			continue; +		}  		*(v++) = 0; @@ -228,8 +235,9 @@ static GSList *twitter_filter_parse(struct groupchat *c, const char *text)  			}  		} -		if (t < 0 || strlen(v) == 0) +		if (t < 0 || strlen(v) == 0) {  			continue; +		}  		tf = twitter_filter_get(c, types[t], v);  		ret = g_slist_prepend(ret, tf); @@ -247,8 +255,9 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)  	struct im_connection *ic = data;  	// Check if we are still logged in... -	if (!g_slist_find(twitter_connections, ic)) +	if (!g_slist_find(twitter_connections, ic)) {  		return FALSE; +	}  	// Do stuff..  	return twitter_get_timeline(ic, -1) && @@ -260,24 +269,27 @@ static void twitter_main_loop_start(struct im_connection *ic)  	struct twitter_data *td = ic->proto_data;  	char *last_tweet = set_getstr(&ic->acc->set, "_last_tweet"); -	if (last_tweet) + +	if (last_tweet) {  		td->timeline_id = g_ascii_strtoull(last_tweet, NULL, 0); +	}  	/* Create the room now that we "logged in". */ -	if (td->flags & TWITTER_MODE_CHAT) +	if (td->flags & TWITTER_MODE_CHAT) {  		twitter_groupchat_init(ic); +	}  	imcb_log(ic, "Getting initial statuses");  	// Run this once. After this queue the main loop function (or open the  	// stream if available).  	twitter_main_loop(ic, -1, 0); -	 +  	if (set_getbool(&ic->acc->set, "stream")) {  		/* That fetch was just to get backlog, the stream will give  		   us the rest. \o/ */  		twitter_open_stream(ic); -		 +  		/* Stream sends keepalives (empty lines) or actual data at  		   least twice a minute. Disconnect if this stops. */  		ic->flags |= OPT_PONGS; @@ -285,8 +297,8 @@ static void twitter_main_loop_start(struct im_connection *ic)  		/* Not using the streaming API, so keep polling the old-  		   fashioned way. :-( */  		td->main_loop_id = -		    b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, -		                  twitter_main_loop, ic); +		        b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, +		                      twitter_main_loop, ic);  	}  } @@ -297,8 +309,9 @@ struct groupchat *twitter_groupchat_init(struct im_connection *ic)  	struct twitter_data *td = ic->proto_data;  	GSList *l; -	if (td->timeline_gc) +	if (td->timeline_gc) {  		return td->timeline_gc; +	}  	td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline"); @@ -308,11 +321,12 @@ struct groupchat *twitter_groupchat_init(struct im_connection *ic)  	for (l = ic->bee->users; l; l = l->next) {  		bee_user_t *bu = l->data; -		if (bu->ic == ic) +		if (bu->ic == ic) {  			imcb_chat_add_buddy(gc, bu->handle); +		}  	}  	imcb_chat_add_buddy(gc, ic->acc->user); -	 +  	return gc;  } @@ -324,14 +338,15 @@ void twitter_login_finish(struct im_connection *ic)  	td->flags &= ~TWITTER_DOING_TIMELINE; -	if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) +	if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) {  		twitter_oauth_start(ic); -	else if (!(td->flags & TWITTER_MODE_ONE) && -	         !(td->flags & TWITTER_HAVE_FRIENDS)) { +	} else if (!(td->flags & TWITTER_MODE_ONE) && +	           !(td->flags & TWITTER_HAVE_FRIENDS)) {  		imcb_log(ic, "Getting contact list");  		twitter_get_friends_ids(ic, -1); -	} else +	} else {  		twitter_main_loop_start(ic); +	}  }  static const struct oauth_service twitter_oauth = { @@ -356,10 +371,11 @@ 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 +	} else {  		return &twitter_oauth; +	}  	/* Could add more services, or allow configuring your own base URL +  	   API keys. */ @@ -371,10 +387,11 @@ static void twitter_oauth_start(struct im_connection *ic)  	const char *url = set_getstr(&ic->acc->set, "base_url");  	imcb_log(ic, "Requesting OAuth request token"); -	 -	if (!strstr(url, "twitter.com") && !strstr(url, "identi.ca")) + +	if (!strstr(url, "twitter.com") && !strstr(url, "identi.ca")) {  		imcb_log(ic, "Warning: OAuth only works with identi.ca and " -		             "Twitter."); +		         "Twitter."); +	}  	td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic); @@ -388,8 +405,9 @@ 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) { @@ -403,24 +421,25 @@ static gboolean twitter_oauth_callback(struct oauth_info *info)  		name = g_strdup_printf("%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); +		                      "%s and respond with the resulting PIN code.", +		                      info->auth_url);  		imcb_buddy_msg(ic, name, msg, 0, 0);  		g_free(name);  		g_free(msg);  	} else if (info->stage == OAUTH_ACCESS_TOKEN) {  		const char *sn; -		 +  		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;  		} -		 +  		if ((sn = oauth_params_get(&info->params, "screen_name"))) { -			if (ic->acc->prpl->handle_cmp(sn, ic->acc->user) != 0) +			if (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);  		} @@ -443,16 +462,18 @@ int twitter_url_len_diff(gchar *msg, unsigned int target_len)  	static GRegex *regex = NULL;  	GMatchInfo *match_info; -	if (regex == NULL) +	if (regex == NULL) {  		regex = g_regex_new("(^|\\s)(http(s)?://[^\\s$]+)", 0, 0, NULL); -	 +	} +  	g_regex_match(regex, msg, 0, &match_info);  	while (g_match_info_matches(match_info)) {  		gchar *url = g_match_info_fetch(match_info, 2);  		url_len_diff += target_len - g_utf8_strlen(url, -1);  		/* Add another character for https://t.co/... URLs */ -		if (g_match_info_fetch(match_info, 3) != NULL) +		if (g_match_info_fetch(match_info, 3) != NULL) {  			url_len_diff += 1; +		}  		g_free(url);  		g_match_info_next(match_info, NULL);  	} @@ -467,11 +488,13 @@ static gboolean twitter_length_check(struct im_connection *ic, gchar * msg)  	int target_len = set_getint(&ic->acc->set, "target_url_length");  	int url_len_diff = 0; -	if (target_len > 0) +	if (target_len > 0) {  		url_len_diff = twitter_url_len_diff(msg, target_len); +	} -	if (max == 0 || (len = g_utf8_strlen(msg, -1) + url_len_diff) <= max) +	if (max == 0 || (len = g_utf8_strlen(msg, -1) + url_len_diff) <= max) {  		return TRUE; +	}  	twitter_log(ic, "Maximum message length exceeded: %d > %d", len, max); @@ -480,19 +503,21 @@ static gboolean twitter_length_check(struct im_connection *ic, gchar * msg)  static char *set_eval_commands(set_t * set, char *value)  { -	if (g_strcasecmp(value, "strict") == 0 ) +	if (g_strcasecmp(value, "strict") == 0) {  		return value; -	else +	} else {  		return set_eval_bool(set, 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) +	    g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0) {  		return value; -	else +	} else {  		return NULL; +	}  }  static void twitter_init(account_t * acc) @@ -506,7 +531,7 @@ static void twitter_init(account_t * acc)  		def_url = TWITTER_API_URL;  		def_tul = "22";  		def_mentions = "true"; -	} else {		/* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */ +	} else {                /* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */  		def_url = IDENTICA_API_URL;  		def_tul = "0";  		def_mentions = "false"; @@ -538,7 +563,7 @@ static void twitter_init(account_t * acc)  	s = set_add(&acc->set, "show_old_mentions", "0", set_eval_int, acc);  	s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc); -	 +  	s = set_add(&acc->set, "_last_tweet", "0", NULL, acc);  	s->flags |= SET_HIDDEN | SET_NOSAVE; @@ -559,7 +584,7 @@ static void twitter_login(account_t * acc)  	char name[strlen(acc->user) + 9];  	url_t url;  	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")); @@ -570,7 +595,7 @@ static void twitter_login(account_t * acc)  	if (!strstr(url.host, "twitter.com") &&  	    set_getbool(&ic->acc->set, "stream")) {  		imcb_error(ic, "Warning: The streaming API is only supported by Twitter, " -		               "and you seem to be connecting to a different service."); +		           "and you seem to be connecting to a different service.");  	}  	imcb_log(ic, "Connecting"); @@ -583,21 +608,23 @@ static void twitter_login(account_t * acc)  	td->url_ssl = url.proto == PROTO_HTTPS;  	td->url_port = url.port;  	td->url_host = g_strdup(url.host); -	if (strcmp(url.file, "/") != 0) +	if (strcmp(url.file, "/") != 0) {  		td->url_path = g_strdup(url.file); -	else { +	} else {  		td->url_path = g_strdup(""); -		if (g_str_has_suffix(url.host, "twitter.com")) +		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."); +			           "now. Try resetting the base_url account setting."); +		}  	} -	 +  	/* 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")) +	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) */ @@ -605,9 +632,10 @@ static void twitter_login(account_t * acc)  		g_free(td->prefix);  		td->prefix = s;  	} -	 -	if (strstr(acc->pass, "oauth_token=")) + +	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); @@ -615,14 +643,15 @@ static void twitter_login(account_t * acc)  	td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);  	td->log_id = -1; -	 +  	s = set_getstr(&ic->acc->set, "mode"); -	if (g_strcasecmp(s, "one") == 0) +	if (g_strcasecmp(s, "one") == 0) {  		td->flags |= TWITTER_MODE_ONE; -	else if (g_strcasecmp(s, "many") == 0) +	} else if (g_strcasecmp(s, "many") == 0) {  		td->flags |= TWITTER_MODE_MANY; -	else +	} else {  		td->flags |= TWITTER_MODE_CHAT; +	}  	twitter_login_finish(ic);  } @@ -640,12 +669,14 @@ static void twitter_logout(struct im_connection *ic)  	// Remove the main_loop function from the function queue.  	b_event_remove(td->main_loop_id); -	if (td->timeline_gc) +	if (td->timeline_gc) {  		imcb_chat_free(td->timeline_gc); +	}  	if (td) { -		if (td->filter_update_id > 0) +		if (td->filter_update_id > 0) {  			b_event_remove(td->filter_update_id); +		}  		http_close(td->stream);  		twitter_filter_remove_all(ic); @@ -678,19 +709,21 @@ static int twitter_buddy_msg(struct im_connection *ic, char *who, char *message,  			char pin[strlen(message) + 1], *s;  			strcpy(pin, message); -			for (s = pin + sizeof(pin) - 2; s > pin && g_ascii_isspace(*s); s--) +			for (s = pin + sizeof(pin) - 2; s > pin && g_ascii_isspace(*s); s--) {  				*s = '\0'; +			}  			for (s = pin; *s && g_ascii_isspace(*s); s++) {  			}  			if (!oauth_access_token(s, td->oauth_info)) {  				imcb_error(ic, "OAuth error: %s", -					   "Failed to send access token request"); +				           "Failed to send access token request");  				imc_logout(ic, TRUE);  				return FALSE;  			} -		} else +		} else {  			twitter_handle_command(ic, message); +		}  	} else {  		twitter_direct_messages_new(ic, who, message);  	} @@ -713,8 +746,9 @@ static void twitter_remove_buddy(struct im_connection *ic, char *who, char *grou  static void twitter_chat_msg(struct groupchat *c, char *message, int flags)  { -	if (c && message) +	if (c && message) {  		twitter_handle_command(c->ic, message); +	}  }  static void twitter_chat_invite(struct groupchat *c, char *who, char *message) @@ -736,17 +770,20 @@ static struct groupchat *twitter_chat_join(struct im_connection *ic,  	for (l = fs; l; l = g_slist_next(l)) {  		tf = l->data; -		if (topic->len > 0) +		if (topic->len > 0) {  			g_string_append(topic, ", "); +		} -		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) +		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {  			g_string_append_c(topic, '@'); +		}  		g_string_append(topic, tf->text);  	} -	if (topic->len > 0) +	if (topic->len > 0) {  		g_string_prepend(topic, "Twitter Filter: "); +	}  	imcb_chat_topic(c, NULL, topic->str, 0);  	imcb_chat_add_buddy(c, ic->acc->user); @@ -819,40 +856,48 @@ static void twitter_buddy_data_free(struct bee_user *bu)   *   *  Returns 0 if the user provides garbage.   */ -static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_) { +static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_) +{  	struct twitter_data *td = ic->proto_data;  	struct twitter_user_data *tud;  	bee_user_t *bu = NULL;  	guint64 id = 0; -	 -	if (bu_) + +	if (bu_) {  		*bu_ = NULL; -	if (!arg || !arg[0]) +	} +	if (!arg || !arg[0]) {  		return 0; -	 +	} +  	if (arg[0] != '#' && (bu = bee_user_by_handle(ic->bee, ic, arg))) { -		if ((tud = bu->data)) +		if ((tud = bu->data)) {  			id = tud->last_id; +		}  	} else { -		if (arg[0] == '#') +		if (arg[0] == '#') {  			arg++; +		}  		if (sscanf(arg, "%" G_GINT64_MODIFIER "x", &id) == 1 &&  		    id < TWITTER_LOG_LENGTH) {  			bu = td->log[id].bu;  			id = td->log[id].id;  			/* Beware of dangling pointers! */ -			if (!g_slist_find(ic->bee->users, bu)) +			if (!g_slist_find(ic->bee->users, bu)) {  				bu = NULL; +			}  		} else if (sscanf(arg, "%" G_GINT64_MODIFIER "d", &id) == 1) {  			/* Allow normal tweet IDs as well; not a very useful  			   feature but it's always been there. Just ignore  			   very low IDs to avoid accidents. */ -			if (id < 1000000) +			if (id < 1000000) {  				id = 0; +			}  		}  	} -	if (bu_) +	if (bu_) {  		*bu_ = bu; +	}  	return id;  } @@ -862,7 +907,7 @@ static void twitter_handle_command(struct im_connection *ic, char *message)  	char *cmds, **cmd, *new = NULL;  	guint64 in_reply_to = 0, id;  	gboolean allow_post = -		g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0; +	        g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0;  	bee_user_t *bu = NULL;  	cmds = g_strdup(message); @@ -873,17 +918,18 @@ static void twitter_handle_command(struct im_connection *ic, char *message)  	} else if (!set_getbool(&ic->acc->set, "commands") && allow_post) {  		/* Not supporting commands if "commands" is set to true/strict. */  	} else if (g_strcasecmp(cmd[0], "undo") == 0) { -		if (cmd[1] == NULL) +		if (cmd[1] == NULL) {  			twitter_status_destroy(ic, td->last_status_id); -		else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) +		} else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {  			twitter_status_destroy(ic, id); -		else +		} else {  			twitter_log(ic, "Could not undo last action"); +		}  		goto eof;  	} else if ((g_strcasecmp(cmd[0], "favourite") == 0 || -		    g_strcasecmp(cmd[0], "favorite") == 0 || -		    g_strcasecmp(cmd[0], "fav") == 0) && cmd[1]) { +	            g_strcasecmp(cmd[0], "favorite") == 0 || +	            g_strcasecmp(cmd[0], "fav") == 0) && cmd[1]) {  		if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {  			twitter_favourite_tweet(ic, id);  		} else { @@ -899,33 +945,35 @@ static void twitter_handle_command(struct im_connection *ic, char *message)  	} else if ((g_strcasecmp(cmd[0], "report") == 0 ||  	            g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) {  		char *screen_name; -		 +  		/* Report nominally works on users but look up the user who  		   posted the given ID if the user wants to do it that way */  		twitter_message_id_from_command_arg(ic, cmd[1], &bu); -		if (bu) +		if (bu) {  			screen_name = bu->handle; -		else +		} else {  			screen_name = cmd[1]; -		 +		} +  		twitter_report_spam(ic, screen_name);  		goto eof;  	} else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) {  		id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);  		td->last_status_id = 0; -		if (id) +		if (id) {  			twitter_status_retweet(ic, id); -		else +		} else {  			twitter_log(ic, "User `%s' does not exist or didn't " -				    "post any statuses recently", cmd[1]); +			            "post any statuses recently", cmd[1]); +		}  		goto eof;  	} else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) {  		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);  		if (!id || !bu) {  			twitter_log(ic, "User `%s' does not exist or didn't " -				    "post any statuses recently", cmd[1]); +			            "post any statuses recently", cmd[1]);  			goto eof;  		}  		message = new = g_strdup_printf("@%s %s", bu->handle, cmd[2]); @@ -948,8 +996,9 @@ static void twitter_handle_command(struct im_connection *ic, char *message)  	if (allow_post) {  		char *s; -		if (!twitter_length_check(ic, message)) +		if (!twitter_length_check(ic, message)) {  			goto eof; +		}  		s = cmd[0] + strlen(cmd[0]) - 1;  		if (!new && s > cmd[0] && (*s == ':' || *s == ',')) { @@ -959,12 +1008,13 @@ static void twitter_handle_command(struct im_connection *ic, char *message)  				struct twitter_user_data *tud = bu->data;  				new = g_strdup_printf("@%s %s", bu->handle, -						      message + (s - cmd[0]) + 2); +				                      message + (s - cmd[0]) + 2);  				message = new;  				if (time(NULL) < tud->last_time + -				    set_getint(&ic->acc->set, "auto_reply_timeout")) +				    set_getint(&ic->acc->set, "auto_reply_timeout")) {  					in_reply_to = tud->last_id; +				}  			}  		} @@ -980,21 +1030,22 @@ eof:  	g_free(cmds);  } -void twitter_log(struct im_connection *ic, char *format, ... ) +void twitter_log(struct im_connection *ic, char *format, ...)  {  	struct twitter_data *td = ic->proto_data;  	va_list params;  	char *text; -	 +  	va_start(params, format);  	text = g_strdup_vprintf(format, params);  	va_end(params); -	 -	if (td->timeline_gc) + +	if (td->timeline_gc) {  		imcb_chat_log(td->timeline_gc, "%s", text); -	else +	} else {  		imcb_log(ic, "%s", text); -	 +	} +  	g_free(text);  } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 00230cc0..7cfd9148 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -28,13 +28,12 @@  #define _TWITTER_H  #ifdef DEBUG_TWITTER -#define debug( text... ) imcb_log( ic, text ); +#define debug(text ...) imcb_log(ic, text);  #else -#define debug( text... ) +#define debug(text ...)  #endif -typedef enum -{ +typedef enum {  	TWITTER_HAVE_FRIENDS   = 0x00001,  	TWITTER_MODE_ONE       = 0x00002,  	TWITTER_MODE_MANY      = 0x00004, @@ -44,16 +43,14 @@ typedef enum  	TWITTER_GOT_MENTIONS   = 0x40000,  } twitter_flags_t; -typedef enum -{ +typedef enum {  	TWITTER_FILTER_TYPE_FOLLOW = 0,  	TWITTER_FILTER_TYPE_TRACK  } twitter_filter_type_t;  struct twitter_log_data; -struct twitter_data -{ +struct twitter_data {  	char* user;  	struct oauth_info *oauth_info; @@ -64,7 +61,7 @@ struct twitter_data  	GSList *follow_ids;  	GSList *filters; -	 +  	guint64 last_status_id; /* For undo */  	gint main_loop_id;  	gint filter_update_id; @@ -73,7 +70,7 @@ struct twitter_data  	struct groupchat *timeline_gc;  	gint http_fails;  	twitter_flags_t flags; -	 +  	/* set base_url */  	gboolean url_ssl;  	int url_port; @@ -81,47 +78,44 @@ struct twitter_data  	char *url_path;  	char *prefix; /* Used to generate contact + channel name. */ -	 +  	/* set show_ids */  	struct twitter_log_data *log;  	int log_id;  };  #define TWITTER_FILTER_UPDATE_WAIT 3000 -struct twitter_filter -{ +struct twitter_filter {  	twitter_filter_type_t type;  	char *text;  	guint64 uid;  	GSList *groupchats;  }; -struct twitter_user_data -{ +struct twitter_user_data {  	guint64 last_id;  	time_t last_time;  };  #define TWITTER_LOG_LENGTH 256 -struct twitter_log_data -{ +struct twitter_log_data {  	guint64 id;  	struct bee_user *bu; /* DANGER: can be a dead pointer. Check it first. */  };  /** - * This has the same function as the msn_connections GSList. We use this to  + * This has the same function as the msn_connections GSList. We use this to   * make sure the connection is still alive in callbacks before we do anything   * else.   */  extern GSList *twitter_connections; -void twitter_login_finish( struct im_connection *ic ); +void twitter_login_finish(struct im_connection *ic);  struct http_request; -char *twitter_parse_error( struct http_request *req ); +char *twitter_parse_error(struct http_request *req); -void twitter_log(struct im_connection *ic, char *format, ... ); +void twitter_log(struct im_connection *ic, char *format, ...);  struct groupchat *twitter_groupchat_init(struct im_connection *ic);  #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index f7ab6e18..7a180b5e 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -47,7 +47,7 @@ static char *twitter_url_append(char *url, char *key, char *value);   * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c   */  struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, -		                  gpointer data, int is_post, char **arguments, int arguments_len) +                                  gpointer data, int is_post, char **arguments, int arguments_len)  {  	struct twitter_data *td = ic->proto_data;  	char *tmp; @@ -67,7 +67,7 @@ struct http_request *twitter_http(struct im_connection *ic, char *url_string, ht  			url_arguments = tmp;  		}  	} -	 +  	if (strstr(url_string, "://")) {  		base_url = g_new0(url_t, 1);  		if (!url_set(base_url, url_string)) { @@ -75,28 +75,29 @@ struct http_request *twitter_http(struct im_connection *ic, char *url_string, ht  			return NULL;  		}  	} -	 +  	// Make the request.  	g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n" -			"Host: %s\r\n" -			"User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", -			is_post ? "POST" : "GET", -			base_url ? base_url->file : td->url_path, -			base_url ? "" : url_string, -			is_post ? "" : "?", is_post ? "" : url_arguments, -			base_url ? base_url->host : td->url_host); +	                "Host: %s\r\n" +	                "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", +	                is_post ? "POST" : "GET", +	                base_url ? base_url->file : td->url_path, +	                base_url ? "" : url_string, +	                is_post ? "" : "?", is_post ? "" : url_arguments, +	                base_url ? base_url->host : td->url_host);  	// If a pass and user are given we append them to the request.  	if (td->oauth_info) {  		char *full_header;  		char *full_url; -		if (base_url) +		if (base_url) {  			full_url = g_strdup(url_string); -		else +		} else {  			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); @@ -115,18 +116,20 @@ struct http_request *twitter_http(struct im_connection *ic, char *url_string, ht  	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");  	} -	if (base_url) -		ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func, data); -	else +	if (base_url) { +		ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func, +		                     data); +	} else {  		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); @@ -135,26 +138,31 @@ struct http_request *twitter_http(struct im_connection *ic, char *url_string, ht  }  struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func, -		                    gpointer data, int is_post, char **arguments, int arguments_len, twitter_http_flags_t flags) +                                    gpointer data, int is_post, char **arguments, int arguments_len, +                                    twitter_http_flags_t flags)  {  	struct http_request *ret = twitter_http(ic, url_string, func, data, is_post, arguments, arguments_len); -	if (ret) + +	if (ret) {  		ret->flags |= flags; +	}  	return ret;  }  static char *twitter_url_append(char *url, char *key, char *value)  {  	char *key_encoded = g_strndup(key, 3 * strlen(key)); +  	http_encode(key_encoded);  	char *value_encoded = g_strndup(value, 3 * strlen(value));  	http_encode(value_encoded);  	char *retval; -	if (strlen(url) != 0) +	if (strlen(url) != 0) {  		retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded); -	else +	} else {  		retval = g_strdup_printf("%s=%s", key_encoded, value_encoded); +	}  	g_free(key_encoded);  	g_free(value_encoded); diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h index 09ef350c..d46ee4e5 100644 --- a/protocols/twitter/twitter_http.h +++ b/protocols/twitter/twitter_http.h @@ -38,7 +38,8 @@ struct oauth_info;  struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,                                    gpointer data, int is_post, char** arguments, int arguments_len);  struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func, -                                    gpointer data, int is_post, char** arguments, int arguments_len, twitter_http_flags_t flags); +                                    gpointer data, int is_post, char** arguments, int arguments_len, +                                    twitter_http_flags_t flags);  #endif //_TWITTER_HTTP_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c8956606..05932bef 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -23,7 +23,7 @@  ****************************************************************************/  /* For strptime(): */ -#if(__sun) +#if (__sun)  #else  #define _XOPEN_SOURCE  #endif @@ -69,8 +69,9 @@ struct twitter_xml_status {   */  static void txu_free(struct twitter_xml_user *txu)  { -	if (txu == NULL) +	if (txu == NULL) {  		return; +	}  	g_free(txu->name);  	g_free(txu->screen_name); @@ -82,8 +83,9 @@ static void txu_free(struct twitter_xml_user *txu)   */  static void txs_free(struct twitter_xml_status *txs)  { -	if (txs == NULL) +	if (txs == NULL) {  		return; +	}  	g_free(txs->text);  	txu_free(txs->user); @@ -97,8 +99,10 @@ static void txs_free(struct twitter_xml_status *txs)  static void txl_free(struct twitter_xml_list *txl)  {  	GSList *l; -	if (txl == NULL) + +	if (txl == NULL) {  		return; +	}  	for (l = txl->list; l; l = g_slist_next(l)) {  		if (txl->type == TXL_STATUS) { @@ -147,10 +151,12 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *  			/* Necessary so that nicks always get translated to the  			   exact Twitter username. */  			imcb_buddy_nick_hint(ic, name, name); -			if (td->timeline_gc) +			if (td->timeline_gc) {  				imcb_chat_add_buddy(td->timeline_gc, name); -		} else if (td->flags & TWITTER_MODE_MANY) +			} +		} else if (td->flags & TWITTER_MODE_MANY) {  			imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL); +		}  	}  } @@ -170,8 +176,9 @@ char *twitter_parse_error(struct http_request *req)  		if (err && err->type == json_array && (err = err->u.array.values[0]) &&  		    err->type == json_object) {  			const char *msg = json_o_str(err, "message"); -			if (msg) +			if (msg) {  				ret = g_strdup_printf("%s (%s)", req->status_string, msg); +			}  		}  		json_value_free(root);  	} @@ -188,34 +195,37 @@ static json_value *twitter_parse_response(struct im_connection *ic, struct http_  	struct twitter_data *td = ic->proto_data;  	json_value *ret;  	char path[64] = "", *s; -	 +  	if ((s = strchr(req->request, ' '))) { -		path[sizeof(path)-1] = '\0'; +		path[sizeof(path) - 1] = '\0';  		strncpy(path, s + 1, sizeof(path) - 1); -		if ((s = strchr(path, '?')) || (s = strchr(path, ' '))) +		if ((s = strchr(path, '?')) || (s = strchr(path, ' '))) {  			*s = '\0'; +		}  	} -	 +  	/* Kinda nasty. :-( Trying to suppress error messages, but only  	   for periodic (i.e. mentions/timeline) queries. */  	periodic = strstr(path, "timeline") || strstr(path, "mentions"); -	 +  	if (req->status_code == 401 && logging_in) {  		/* IIRC Twitter once had an outage where they were randomly  		   throwing 401s so I'll keep treating this one as fatal  		   only during login. */  		imcb_error(ic, "Authentication failure (%s)", -		               twitter_parse_error(req)); +		           twitter_parse_error(req));  		imc_logout(ic, FALSE);  		return NULL;  	} else if (req->status_code != 200) {  		// It didn't go well, output the error and return. -		if (!periodic || logging_in || ++td->http_fails >= 5) +		if (!periodic || logging_in || ++td->http_fails >= 5) {  			twitter_log(ic, "Error: Could not retrieve %s: %s", -				    path, twitter_parse_error(req)); -		 -		if (logging_in) +			            path, twitter_parse_error(req)); +		} + +		if (logging_in) {  			imc_logout(ic, TRUE); +		}  		return NULL;  	} else {  		td->http_fails = 0; @@ -223,7 +233,7 @@ static json_value *twitter_parse_response(struct im_connection *ic, struct http_  	if ((ret = json_parse(req->reply_body, req->body_size)) == NULL) {  		imcb_error(ic, "Could not retrieve %s: %s", -			   path, "XML parse error"); +		           path, "XML parse error");  	}  	return ret;  } @@ -237,6 +247,7 @@ void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)  {  	// Primitive, but hey! It works...  	char *args[2]; +  	args[0] = "cursor";  	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);  	twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2); @@ -256,23 +267,26 @@ static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_  	txl->type = TXL_ID;  	c = json_o_get(node, "ids"); -	if (!c || c->type != json_array) +	if (!c || c->type != json_array) {  		return FALSE; +	} -	for (i = 0; i < c->u.array.length; i ++) { -		if (c->u.array.values[i]->type != json_integer) +	for (i = 0; i < c->u.array.length; i++) { +		if (c->u.array.values[i]->type != json_integer) {  			continue; -		 +		} +  		txl->list = g_slist_prepend(txl->list, -			g_strdup_printf("%" PRIu64, c->u.array.values[i]->u.integer)); +		                            g_strdup_printf("%" PRIu64, c->u.array.values[i]->u.integer));  	} -	 +  	c = json_o_get(node, "next_cursor"); -	if (c && c->type == json_integer) +	if (c && c->type == json_integer) {  		txl->next_cursor = c->u.integer; -	else +	} else {  		txl->next_cursor = -1; -	 +	} +  	return TRUE;  } @@ -291,8 +305,9 @@ 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; @@ -300,20 +315,22 @@ static void twitter_http_get_friends_ids(struct http_request *req)  	txl->list = td->follow_ids;  	// Parse the data. -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		return; -	 +	} +  	twitter_xt_get_friends_id_list(parsed, txl);  	json_value_free(parsed);  	td->follow_ids = txl->list; -	if (txl->next_cursor) +	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 +	} else {  		/* Now to convert all those numbers into names.. */  		twitter_get_users_lookup(ic); +	}  	txl->list = NULL;  	txl_free(txl); @@ -331,10 +348,10 @@ static void twitter_get_users_lookup(struct im_connection *ic)  	};  	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); +	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);  	} @@ -353,8 +370,8 @@ static void twitter_get_users_lookup(struct im_connection *ic)  /**   * 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  + * 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) @@ -366,15 +383,17 @@ static void twitter_http_get_users_lookup(struct http_request *req)  	struct twitter_xml_user *user;  	// Check if the connection is still active. -	if (!g_slist_find(twitter_connections, ic)) +	if (!g_slist_find(twitter_connections, ic)) {  		return; +	}  	txl = g_new0(struct twitter_xml_list, 1);  	txl->list = NULL;  	// Get the user list from the parsed xml feed. -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		return; +	}  	twitter_xt_get_users(parsed, txl);  	json_value_free(parsed); @@ -394,14 +413,14 @@ struct twitter_xml_user *twitter_xt_get_user(const json_value *node)  {  	struct twitter_xml_user *txu;  	json_value *jv; -	 +  	txu = g_new0(struct twitter_xml_user, 1);  	txu->name = g_strdup(json_o_str(node, "name"));  	txu->screen_name = g_strdup(json_o_str(node, "screen_name")); -	 +  	jv = json_o_get(node, "id");  	txu->uid = jv->u.integer; -	 +  	return txu;  } @@ -418,15 +437,17 @@ static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *  	// Set the type of the list.  	txl->type = TXL_USER; -	if (!node || node->type != json_array) +	if (!node || node->type != json_array) {  		return FALSE; +	}  	// The root <users> node should hold the list of users <user>  	// Walk over the nodes children. -	for (i = 0; i < node->u.array.length; i ++) { +	for (i = 0; i < node->u.array.length; i++) {  		txu = twitter_xt_get_user(node->u.array.values[i]); -		if (txu) +		if (txu) {  			txl->list = g_slist_prepend(txl->list, txu); +		}  	}  	return TRUE; @@ -452,12 +473,13 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)  {  	struct twitter_xml_status *txs;  	const json_value *rt = NULL, *entities = NULL; -	 -	if (node->type != json_object) + +	if (node->type != json_object) {  		return FALSE; +	}  	txs = g_new0(struct twitter_xml_status, 1); -	JSON_O_FOREACH (node, k, v) { +	JSON_O_FOREACH(node, k, v) {  		if (strcmp("text", k) == 0 && v->type == json_string) {  			txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1);  			strip_html(txs->text); @@ -469,8 +491,9 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)  			/* 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(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) +			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {  				txs->created_at = mktime_utc(&parsed); +			}  		} else if (strcmp("user", k) == 0 && v->type == json_object) {  			txs->user = twitter_xt_get_user(v);  		} else if (strcmp("id", k) == 0 && v->type == json_integer) { @@ -496,9 +519,10 @@ static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)  		txs->text = expand_entities(txs->text, entities);  	} -	if (txs->text && txs->user && txs->id) +	if (txs->text && txs->user && txs->id) {  		return txs; -	 +	} +  	txs_free(txs);  	return NULL;  } @@ -510,12 +534,13 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)  {  	struct twitter_xml_status *txs;  	const json_value *entities = NULL; -	 -	if (node->type != json_object) + +	if (node->type != json_object) {  		return FALSE; +	}  	txs = g_new0(struct twitter_xml_status, 1); -	JSON_O_FOREACH (node, k, v) { +	JSON_O_FOREACH(node, k, v) {  		if (strcmp("text", k) == 0 && v->type == json_string) {  			txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1);  			strip_html(txs->text); @@ -525,8 +550,9 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)  			/* 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(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) +			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {  				txs->created_at = mktime_utc(&parsed); +			}  		} else if (strcmp("sender", k) == 0 && v->type == json_object) {  			txs->user = twitter_xt_get_user(v);  		} else if (strcmp("id", k) == 0 && v->type == json_integer) { @@ -538,42 +564,48 @@ static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)  		txs->text = expand_entities(txs->text, entities);  	} -	if (txs->text && txs->user && txs->id) +	if (txs->text && txs->user && txs->id) {  		return txs; -	 +	} +  	txs_free(txs);  	return NULL;  } -static char* expand_entities(char* text, const json_value *entities) { -	JSON_O_FOREACH (entities, k, v) { +static char* expand_entities(char* text, const json_value *entities) +{ +	JSON_O_FOREACH(entities, k, v) {  		int i; -		 -		if (v->type != json_array) + +		if (v->type != json_array) {  			continue; -		if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) +		} +		if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) {  			continue; -		 -		for (i = 0; i < v->u.array.length; i ++) { -			if (v->u.array.values[i]->type != json_object) +		} + +		for (i = 0; i < v->u.array.length; i++) { +			if (v->u.array.values[i]->type != json_object) {  				continue; -			 +			} +  			const char *kort = json_o_str(v->u.array.values[i], "url");  			const char *disp = json_o_str(v->u.array.values[i], "display_url");  			char *pos, *new; -			 -			if (!kort || !disp || !(pos = strstr(text, kort))) + +			if (!kort || !disp || !(pos = strstr(text, kort))) {  				continue; -			 +			} +  			*pos = '\0';  			new = g_strdup_printf("%s%s <%s>%s", text, kort,  			                      disp, pos + strlen(kort)); -			 +  			g_free(text);  			text = new;  		}  	} -	 +  	return text;  } @@ -591,17 +623,19 @@ static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_  	// Set the type of the list.  	txl->type = TXL_STATUS; -	 -	if (node->type != json_array) + +	if (node->type != json_array) {  		return FALSE; +	}  	// The root <statuses> node should hold the list of statuses <status>  	// Walk over the nodes children. -	for (i = 0; i < node->u.array.length; i ++) { +	for (i = 0; i < node->u.array.length; i++) {  		txs = twitter_xt_get_status(node->u.array.values[i]); -		if (!txs) +		if (!txs) {  			continue; -		 +		} +  		txl->list = g_slist_prepend(txl->list, txs);  	} @@ -611,7 +645,7 @@ static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_  /* Will log messages either way. Need to keep track of IDs for stream deduping.     Plus, show_ids is on by default and I don't see why anyone would disable it. */  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;  	int reply_to = -1; @@ -619,11 +653,12 @@ static char *twitter_msg_add_id(struct im_connection *ic,  	if (txs->reply_to) {  		int i; -		for (i = 0; i < TWITTER_LOG_LENGTH; i++) +		for (i = 0; i < TWITTER_LOG_LENGTH; i++) {  			if (td->log[i].id == txs->reply_to) {  				reply_to = i;  				break;  			} +		}  	}  	if (txs->user && txs->user->screen_name && @@ -635,29 +670,32 @@ static char *twitter_msg_add_id(struct im_connection *ic,  			tud->last_time = txs->created_at;  		}  	} -	 +  	td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;  	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); -	 +  	/* This is all getting hairy. :-( If we RT'ed something ourselves,  	   remember OUR id instead so undo will work. In other cases, the  	   original tweet's id should be remembered for deduplicating. */ -	if (g_strcasecmp(txs->user->screen_name, td->user) == 0) +	if (g_strcasecmp(txs->user->screen_name, td->user) == 0) {  		td->log[td->log_id].id = txs->rt_id; -	 +	} +  	if (set_getbool(&ic->acc->set, "show_ids")) { -		if (reply_to != -1) +		if (reply_to != -1) {  			return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s",  			                       td->log_id, reply_to, prefix, txs->text); -		else +		} else {  			return g_strdup_printf("\002[\002%02x\002]\002 %s%s",  			                       td->log_id, prefix, txs->text); +		}  	} else { -		if (*prefix) +		if (*prefix) {  			return g_strconcat(prefix, txs->text, NULL); -		else +		} else {  			return NULL; +		}  	}  } @@ -677,13 +715,15 @@ static void twitter_status_show_filter(struct im_connection *ic, struct twitter_  		switch (tf->type) {  		case TWITTER_FILTER_TYPE_FOLLOW: -			if (status->user->uid != tf->uid) +			if (status->user->uid != tf->uid) {  				continue; +			}  			break;  		case TWITTER_FILTER_TYPE_TRACK: -			if (strcasestr(status->text, tf->text) == NULL) +			if (strcasestr(status->text, tf->text) == NULL) {  				continue; +			}  			break;  		default: @@ -692,7 +732,7 @@ static void twitter_status_show_filter(struct im_connection *ic, struct twitter_  		for (l = tf->groupchats; l; l = g_slist_next(l)) {  			imcb_chat_msg(l->data, status->user->screen_name, -				      msg ? msg : status->text, 0, 0); +			              msg ? msg : status->text, 0, 0);  		}  	} @@ -712,17 +752,18 @@ static void twitter_status_show_chat(struct im_connection *ic, struct twitter_xm  	// Create a new groupchat if it does not exsist.  	gc = twitter_groupchat_init(ic); -	if (!me) +	if (!me) {  		/* MUST be done before twitter_msg_add_id() to avoid #872. */  		twitter_add_buddy(ic, status->user->screen_name, status->user->name); +	}  	msg = twitter_msg_add_id(ic, status, ""); -	 +  	// Say it!  	if (me) {  		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); @@ -743,13 +784,14 @@ static void twitter_status_show_msg(struct im_connection *ic, struct twitter_xml  		from[MAX_STRING - 1] = '\0';  	} -	if (td->flags & TWITTER_MODE_ONE) +	if (td->flags & TWITTER_MODE_ONE) {  		prefix = g_strdup_printf("\002<\002%s\002>\002 ",  		                         status->user->screen_name); -	else if (!me) +	} else if (!me) {  		twitter_add_buddy(ic, status->user->screen_name, status->user->name); -	else +	} else {  		prefix = g_strdup("You: "); +	}  	text = twitter_msg_add_id(ic, status, prefix ? prefix : ""); @@ -765,21 +807,24 @@ static void twitter_status_show(struct im_connection *ic, struct twitter_xml_sta  {  	struct twitter_data *td = ic->proto_data;  	char *last_id_str; -	 -	if (status->user == NULL || status->text == NULL) + +	if (status->user == NULL || status->text == NULL) {  		return; -	 +	} +  	/* Grrrr. Would like to do this during parsing, but can't access  	   settings from there. */ -	if (set_getbool(&ic->acc->set, "strip_newlines")) +	if (set_getbool(&ic->acc->set, "strip_newlines")) {  		strip_newlines(status->text); -	 -	if (status->from_filter) +	} + +	if (status->from_filter) {  		twitter_status_show_filter(ic, status); -	else if (td->flags & TWITTER_MODE_CHAT) +	} else if (td->flags & TWITTER_MODE_CHAT) {  		twitter_status_show_chat(ic, status); -	else +	} else {  		twitter_status_show_msg(ic, status); +	}  	// Update the timeline_id to hold the highest id, so that by the next request  	// we won't pick up the updates already in the list. @@ -800,34 +845,37 @@ static void twitter_http_stream(struct http_request *req)  	int len = 0;  	char c, *nl;  	gboolean from_filter; -	 -	if (!g_slist_find(twitter_connections, ic)) + +	if (!g_slist_find(twitter_connections, ic)) {  		return; -	 +	} +  	ic->flags |= OPT_PONGED;  	td = ic->proto_data; -	 +  	if ((req->flags & HTTPC_EOF) || !req->reply_body) { -		if (req == td->stream) +		if (req == td->stream) {  			td->stream = NULL; -		else if (req == td->filter_stream) +		} else if (req == td->filter_stream) {  			td->filter_stream = NULL; +		}  		imcb_error(ic, "Stream closed (%s)", req->status_string);  		imc_logout(ic, TRUE);  		return;  	} -	 +  	/* MUST search for CRLF, not just LF:  	   https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */ -	if (!(nl = strstr(req->reply_body, "\r\n"))) +	if (!(nl = strstr(req->reply_body, "\r\n"))) {  		return; -	 +	} +  	len = nl - req->reply_body;  	if (len > 0) {  		c = req->reply_body[len];  		req->reply_body[len] = '\0'; -		 +  		if ((parsed = json_parse(req->reply_body, req->body_size))) {  			from_filter = (req == td->filter_stream);  			twitter_stream_handle_object(ic, parsed, from_filter); @@ -835,12 +883,13 @@ static void twitter_http_stream(struct http_request *req)  		json_value_free(parsed);  		req->reply_body[len] = c;  	} -	 +  	http_flush_bytes(req, len + 2); -	 +  	/* One notification might bring multiple events! */ -	if (req->body_size > 0) +	if (req->body_size > 0) {  		twitter_http_stream(req); +	}  }  static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o); @@ -851,7 +900,7 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu  	struct twitter_data *td = ic->proto_data;  	struct twitter_xml_status *txs;  	json_value *c; -	 +  	if ((txs = twitter_xt_get_status(o))) {  		txs->from_filter = from_filter;  		gboolean ret = twitter_stream_handle_status(ic, txs); @@ -859,9 +908,10 @@ static gboolean twitter_stream_handle_object(struct im_connection *ic, json_valu  		return ret;  	} else if ((c = json_o_get(o, "direct_message")) &&  	           (txs = twitter_xt_get_dm(c))) { -		if (g_strcasecmp(txs->user->screen_name, td->user) != 0) +		if (g_strcasecmp(txs->user->screen_name, td->user) != 0) {  			imcb_buddy_msg(ic, txs->user->screen_name, -				       txs->text, 0, txs->created_at); +			               txs->text, 0, txs->created_at); +		}  		txs_free(txs);  		return TRUE;  	} else if ((c = json_o_get(o, "event")) && c->type == json_string) { @@ -885,14 +935,14 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw  {  	struct twitter_data *td = ic->proto_data;  	int i; -	 +  	for (i = 0; i < TWITTER_LOG_LENGTH; i++) {  		if (td->log[i].id == txs->id) {  			/* Got a duplicate (RT, probably). Drop it. */  			return TRUE;  		}  	} -	 +  	if (!(g_strcasecmp(txs->user->screen_name, td->user) == 0 ||  	      set_getbool(&ic->acc->set, "fetch_mentions") ||  	      bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { @@ -904,9 +954,9 @@ static gboolean twitter_stream_handle_status(struct im_connection *ic, struct tw  		   @Wilmer. But meh. You want spam, you get spam. */  		return TRUE;  	} -	 +  	twitter_status_show(ic, txs); -	 +  	return TRUE;  } @@ -916,12 +966,12 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value  	json_value *source = json_o_get(o, "source");  	json_value *target = json_o_get(o, "target");  	const char *type = json_o_str(o, "event"); -	 +  	if (!type || !source || source->type != json_object -	          || !target || target->type != json_object) { +	    || !target || target->type != json_object) {  		return FALSE;  	} -	 +  	if (strcmp(type, "follow") == 0) {  		struct twitter_xml_user *us = twitter_xt_get_user(source);  		struct twitter_xml_user *ut = twitter_xt_get_user(target); @@ -931,15 +981,15 @@ static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value  		txu_free(us);  		txu_free(ut);  	} -	 +  	return TRUE;  }  gboolean twitter_open_stream(struct im_connection *ic)  {  	struct twitter_data *td = ic->proto_data; -	char *args[2] = {"with", "followings"}; -	 +	char *args[2] = { "with", "followings" }; +  	if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL,  	                               twitter_http_stream, ic, 0, args, 2))) {  		/* This flag must be enabled or we'll get no data until EOF @@ -947,14 +997,14 @@ gboolean twitter_open_stream(struct im_connection *ic)  		td->stream->flags |= HTTPC_STREAMING;  		return TRUE;  	} -	 +  	return FALSE;  }  static gboolean twitter_filter_stream(struct im_connection *ic)  {  	struct twitter_data *td = ic->proto_data; -	char *args[4] = {"follow", NULL, "track", NULL}; +	char *args[4] = { "follow", NULL, "track", NULL };  	GString *followstr = g_string_new("");  	GString *trackstr = g_string_new("");  	gboolean ret = FALSE; @@ -966,16 +1016,18 @@ static gboolean twitter_filter_stream(struct im_connection *ic)  		switch (tf->type) {  		case TWITTER_FILTER_TYPE_FOLLOW: -			if (followstr->len > 0) +			if (followstr->len > 0) {  				g_string_append_c(followstr, ','); +			}  			g_string_append_printf(followstr, "%" G_GUINT64_FORMAT, -					       tf->uid); +			                       tf->uid);  			break;  		case TWITTER_FILTER_TYPE_TRACK: -			if (trackstr->len > 0) +			if (trackstr->len > 0) {  				g_string_append_c(trackstr, ','); +			}  			g_string_append(trackstr, tf->text);  			break; @@ -988,12 +1040,13 @@ static gboolean twitter_filter_stream(struct im_connection *ic)  	args[1] = followstr->str;  	args[3] = trackstr->str; -	if (td->filter_stream) +	if (td->filter_stream) {  		http_close(td->filter_stream); +	}  	if ((td->filter_stream = twitter_http(ic, TWITTER_FILTER_STREAM_URL,  	                                      twitter_http_stream, ic, 0, -					      args, 4))) { +	                                      args, 4))) {  		/* This flag must be enabled or we'll get no data until EOF  		   (which err, kind of, defeats the purpose of a streaming API). */  		td->filter_stream->flags |= HTTPC_STREAMING; @@ -1021,30 +1074,35 @@ static void twitter_filter_users_post(struct http_request *req)  	int i;  	// 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; -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		return; +	}  	for (l = td->filters; l; l = g_slist_next(l)) {  		tf = l->data; -		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) +		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {  			users = g_list_prepend(users, tf); +		}  	} -	if (parsed->type != json_array) +	if (parsed->type != json_array) {  		goto finish; +	}  	for (i = 0; i < parsed->u.array.length; i++) {  		id = json_o_get(parsed->u.array.values[i], "id");  		name = json_o_str(parsed->u.array.values[i], "screen_name"); -		if (!name || !id || id->type != json_integer) +		if (!name || !id || id->type != json_integer) {  			continue; +		}  		for (u = users; u; u = g_list_next(u)) {  			tf = u->data; @@ -1061,14 +1119,16 @@ finish:  	json_value_free(parsed);  	twitter_filter_stream(ic); -	if (!users) +	if (!users) {  		return; +	}  	fstr = g_string_new("");  	for (u = users; u; u = g_list_next(u)) { -		if (fstr->len > 0) +		if (fstr->len > 0) {  			g_string_append(fstr, ", "); +		}  		g_string_append(fstr, tf->text);  	} @@ -1082,7 +1142,7 @@ finish:  gboolean twitter_open_filter_stream(struct im_connection *ic)  {  	struct twitter_data *td = ic->proto_data; -	char *args[2] = {"screen_name", NULL}; +	char *args[2] = { "screen_name", NULL };  	GString *ustr = g_string_new("");  	struct twitter_filter *tf;  	struct http_request *req; @@ -1091,11 +1151,13 @@ gboolean twitter_open_filter_stream(struct im_connection *ic)  	for (l = td->filters; l; l = g_slist_next(l)) {  		tf = l->data; -		if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) +		if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) {  			continue; +		} -		if (ustr->len > 0) +		if (ustr->len > 0) {  			g_string_append_c(ustr, ','); +		}  		g_string_append(ustr, tf->text);  	} @@ -1107,8 +1169,8 @@ gboolean twitter_open_filter_stream(struct im_connection *ic)  	args[1] = ustr->str;  	req = twitter_http(ic, TWITTER_USERS_LOOKUP_URL, -			   twitter_filter_users_post, -			   ic, 0, args, 2); +	                   twitter_filter_users_post, +	                   ic, 0, args, 2);  	g_string_free(ustr, TRUE);  	return req != NULL; @@ -1140,7 +1202,7 @@ gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor)  	if (include_mentions) {  		twitter_get_mentions(ic, next_cursor);  	} -	 +  	return TRUE;  } @@ -1160,7 +1222,7 @@ void twitter_flush_timeline(struct im_connection *ic)  	GSList *l;  	imcb_connected(ic); -	 +  	if (!(td->flags & TWITTER_GOT_TIMELINE)) {  		return;  	} @@ -1188,8 +1250,9 @@ void twitter_flush_timeline(struct im_connection *ic)  	// See if the user wants to see the messages in a groupchat window or as private messages.  	while (output) {  		struct twitter_xml_status *txs = output->data; -		if (txs->id != last_id) +		if (txs->id != last_id) {  			twitter_status_show(ic, txs); +		}  		last_id = txs->id;  		output = g_slist_remove(output, txs);  	} @@ -1226,10 +1289,11 @@ static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_curs  	}  	if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, -		     td->timeline_id ? 6 : 4) == NULL) { -		if (++td->http_fails >= 5) +	                 td->timeline_id ? 6 : 4) == NULL) { +		if (++td->http_fails >= 5) {  			imcb_error(ic, "Could not retrieve %s: %s",  			           TWITTER_HOME_TIMELINE_URL, "connection failed"); +		}  		td->flags |= TWITTER_GOT_TIMELINE;  		twitter_flush_timeline(ic);  	} @@ -1266,9 +1330,10 @@ static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)  	if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions,  	                 ic, 0, args, 6) == NULL) { -		if (++td->http_fails >= 5) +		if (++td->http_fails >= 5) {  			imcb_error(ic, "Could not retrieve %s: %s",  			           TWITTER_MENTIONS_URL, "connection failed"); +		}  		td->flags |= TWITTER_GOT_MENTIONS;  		twitter_flush_timeline(ic);  	} @@ -1288,8 +1353,9 @@ 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; @@ -1297,16 +1363,18 @@ static void twitter_http_get_home_timeline(struct http_request *req)  	txl->list = NULL;  	// The root <statuses> node should hold the list of statuses <status> -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		goto end; +	}  	twitter_xt_get_status_list(ic, parsed, txl);  	json_value_free(parsed);  	td->home_timeline_obj = txl; -      end: -	if (!g_slist_find(twitter_connections, ic)) +end: +	if (!g_slist_find(twitter_connections, ic)) {  		return; +	}  	td->flags |= TWITTER_GOT_TIMELINE; @@ -1324,8 +1392,9 @@ static void twitter_http_get_mentions(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; @@ -1333,16 +1402,18 @@ static void twitter_http_get_mentions(struct http_request *req)  	txl->list = NULL;  	// The root <statuses> node should hold the list of statuses <status> -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		goto end; +	}  	twitter_xt_get_status_list(ic, parsed, txl);  	json_value_free(parsed);  	td->mentions_obj = txl; -      end: -	if (!g_slist_find(twitter_connections, ic)) +end: +	if (!g_slist_find(twitter_connections, ic)) {  		return; +	}  	td->flags |= TWITTER_GOT_MENTIONS; @@ -1360,23 +1431,26 @@ static void twitter_http_post(struct http_request *req)  	json_value *parsed, *id;  	// 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; -	if (!(parsed = twitter_parse_response(ic, req))) +	if (!(parsed = twitter_parse_response(ic, req))) {  		return; -	 +	} +  	if ((id = json_o_get(parsed, "id")) && id->type == json_integer) {  		td->last_status_id = id->u.integer;  	} -	 +  	json_value_free(parsed); -	 -	if (req->flags & TWITTER_HTTP_USER_ACK) + +	if (req->flags & TWITTER_HTTP_USER_ACK) {  		twitter_log(ic, "Command processed successfully"); +	}  }  /** @@ -1389,8 +1463,9 @@ void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_t  		"in_reply_to_status_id",  		g_strdup_printf("%" G_GUINT64_FORMAT, 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]);  } @@ -1401,6 +1476,7 @@ 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]; +  	args[0] = "screen_name";  	args[1] = who;  	args[2] = "text"; @@ -1412,15 +1488,17 @@ void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)  void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)  {  	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_post, ic, 1, args, 2);  }  void twitter_status_destroy(struct im_connection *ic, guint64 id)  {  	char *url; +  	url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s",  	                      TWITTER_STATUS_DESTROY_URL, id, ".json");  	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0, @@ -1431,6 +1509,7 @@ 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%" G_GUINT64_FORMAT "%s",  	                      TWITTER_STATUS_RETWEET_URL, id, ".json");  	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0, @@ -1447,6 +1526,7 @@ void twitter_report_spam(struct im_connection *ic, char *screen_name)  		"screen_name",  		NULL,  	}; +  	args[1] = screen_name;  	twitter_http_f(ic, TWITTER_REPORT_SPAM_URL, twitter_http_post,  	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK); @@ -1461,6 +1541,7 @@ void twitter_favourite_tweet(struct im_connection *ic, guint64 id)  		"id",  		NULL,  	}; +  	args[1] = g_strdup_printf("%" G_GUINT64_FORMAT, id);  	twitter_http_f(ic, TWITTER_FAVORITE_CREATE_URL, twitter_http_post,  	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK);  | 
