changeset 30795:879baaf87aa2

Added an error handler for family_icq.c. So far it works for icq fullinfo and alias requests. This was originally motivated by me getting "Server rate limit exceeded" while testing authorization requests. This error completely prevented the authorization request dialog from showing up.
author ivan.komarov@soc.pidgin.im
date Sun, 30 May 2010 19:01:30 +0000
parents 9881f18b95b1
children b7b399914d21
files libpurple/protocols/oscar/family_icbm.c libpurple/protocols/oscar/family_icq.c libpurple/protocols/oscar/oscar.h libpurple/protocols/oscar/userinfo.c
diffstat 4 files changed, 126 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/oscar/family_icbm.c	Sun May 30 13:53:45 2010 +0000
+++ b/libpurple/protocols/oscar/family_icbm.c	Sun May 30 19:01:30 2010 +0000
@@ -2495,181 +2495,6 @@
 	return 0;
 }
 
-static void parse_status_note_text(OscarData *od, guchar *cookie, char *bn, ByteStream *bs)
-{
-	struct aim_icq_info *info;
-	struct aim_icq_info *prev_info;
-	char *response;
-	char *encoding;
-	char *stripped_encoding;
-	char *status_note_title;
-	char *status_note_text;
-	char *stripped_status_note_text;
-	char *status_note;
-	guint32 length;
-	guint16 version;
-	guint32 capability;
-	guint8 message_type;
-	guint16 status_code;
-	guint16 text_length;
-	guint32 request_length;
-	guint32 response_length;
-	guint32 encoding_length;
-	PurpleAccount *account;
-	PurpleBuddy *buddy;
-	PurplePresence *presence;
-	PurpleStatus *status;
-
-	for (prev_info = NULL, info = od->icq_info; info != NULL; prev_info = info, info = info->next)
-	{
-		if (memcmp(&info->icbm_cookie, cookie, 8) == 0)
-		{
-			if (prev_info == NULL)
-				od->icq_info = info->next;
-			else
-				prev_info->next = info->next;
-
-			break;
-		}
-	}
-
-	if (info == NULL)
-		return;
-
-	status_note_title = info->status_note_title;
-	g_free(info);
-
-	length = byte_stream_getle16(bs);
-	if (length != 27) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect header "
-				"size; expected 27, received %u.\n", length);
-		g_free(status_note_title);
-		return;
-	}
-
-	version = byte_stream_getle16(bs);
-	if (version != 9) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect version; "
-				"expected 9, received %u.\n", version);
-		g_free(status_note_title);
-		return;
-	}
-
-	capability = aim_locate_getcaps(od, bs, 0x10);
-	if (capability != OSCAR_CAPABILITY_EMPTY) {
-		purple_debug_misc("oscar", "clientautoresp: plugin ID is not null.\n");
-		g_free(status_note_title);
-		return;
-	}
-
-	byte_stream_advance(bs, 2); /* unknown */
-	byte_stream_advance(bs, 4); /* client capabilities flags */
-	byte_stream_advance(bs, 1); /* unknown */
-	byte_stream_advance(bs, 2); /* downcouner? */
-
-	length = byte_stream_getle16(bs);
-	if (length != 14) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect header "
-				"size; expected 14, received %u.\n", length);
-		g_free(status_note_title);
-		return;
-	}
-
-	byte_stream_advance(bs, 2); /* downcounter? */
-	byte_stream_advance(bs, 12); /* unknown */
-
-	message_type = byte_stream_get8(bs);
-	if (message_type != 0x1a) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect message "
-				"type; expected 0x1a, received 0x%x.\n", message_type);
-		g_free(status_note_title);
-		return;
-	}
-
-	byte_stream_advance(bs, 1); /* message flags */
-
-	status_code = byte_stream_getle16(bs);
-	if (status_code != 0) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect status "
-				"code; expected 0, received %u.\n", status_code);
-		g_free(status_note_title);
-		return;
-	}
-
-	byte_stream_advance(bs, 2); /* priority code */
-
-	text_length = byte_stream_getle16(bs);
-	byte_stream_advance(bs, text_length); /* text */
-
-	length = byte_stream_getle16(bs);
-	byte_stream_advance(bs, 18); /* unknown */
-
-	request_length = byte_stream_getle32(bs);
-	if (length != 18 + 4 + request_length + 17) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect block; "
-				"expected length is %u, got %u.\n",
-				18 + 4 + request_length + 17, length);
-		g_free(status_note_title);
-		return;
-	}
-
-	byte_stream_advance(bs, request_length); /* x request */
-	byte_stream_advance(bs, 17); /* unknown */
-
-	length = byte_stream_getle32(bs);
-	response_length = byte_stream_getle32(bs);
-	response = byte_stream_getstr(bs, response_length);
-	encoding_length = byte_stream_getle32(bs);
-	if (length != 4 + response_length + 4 + encoding_length) {
-		purple_debug_misc("oscar", "clientautoresp: incorrect block; "
-				"expected length is %u, got %u.\n",
-				4 + response_length + 4 + encoding_length, length);
-		g_free(status_note_title);
-		g_free(response);
-		return;
-	}
-
-	encoding = byte_stream_getstr(bs, encoding_length);
-
-	account = purple_connection_get_account(od->gc);
-
-	stripped_encoding = oscar_encoding_extract(encoding);
-	status_note_text = oscar_encoding_to_utf8(account, stripped_encoding, response, response_length);
-	stripped_status_note_text = purple_markup_strip_html(status_note_text);
-
-	if (stripped_status_note_text != NULL && stripped_status_note_text[0] != 0)
-		status_note = g_strdup_printf("%s: %s", status_note_title, stripped_status_note_text);
-	else
-		status_note = g_strdup(status_note_title);
-
-	g_free(status_note_title);
-	g_free(response);
-	g_free(encoding);
-	g_free(stripped_encoding);
-	g_free(status_note_text);
-	g_free(stripped_status_note_text);
-
-	buddy = purple_find_buddy(account, bn);
-	if (buddy == NULL)
-	{
-		purple_debug_misc("oscar", "clientautoresp: buddy %s was not found.\n", bn);
-		g_free(status_note);
-		return;
-	}
-
-	purple_debug_misc("oscar", "clientautoresp: setting status "
-			"message to \"%s\".\n", status_note);
-
-	presence = purple_buddy_get_presence(buddy);
-	status = purple_presence_get_active_status(presence);
-
-	purple_prpl_got_user_status(account, bn,
-			purple_status_get_id(status),
-			"message", status_note, NULL);
-
-	g_free(status_note);
-}
-
 /*
  * Subtype 0x000b - Receive the response from an ICQ status message
  * request (in which case this contains the ICQ status message) or
--- a/libpurple/protocols/oscar/family_icq.c	Sun May 30 13:53:45 2010 +0000
+++ b/libpurple/protocols/oscar/family_icq.c	Sun May 30 19:01:30 2010 +0000
@@ -25,6 +25,102 @@
 
 #include "oscar.h"
 
+#define AIM_ICQ_INFO_REQUEST 0x04b2
+#define AIM_ICQ_ALIAS_REQUEST 0x04ba
+
+static
+int compare_icq_infos(gconstpointer a, gconstpointer b)
+{
+	const struct aim_icq_info* aa = a;
+	const guint16* bb = b;
+	return aa->reqid - *bb;
+}
+
+static void aim_icq_freeinfo(struct aim_icq_info *info) {
+	int i;
+
+	if (!info)
+		return;
+	g_free(info->nick);
+	g_free(info->first);
+	g_free(info->last);
+	g_free(info->email);
+	g_free(info->homecity);
+	g_free(info->homestate);
+	g_free(info->homephone);
+	g_free(info->homefax);
+	g_free(info->homeaddr);
+	g_free(info->mobile);
+	g_free(info->homezip);
+	g_free(info->personalwebpage);
+	if (info->email2)
+		for (i = 0; i < info->numaddresses; i++)
+			g_free(info->email2[i]);
+	g_free(info->email2);
+	g_free(info->workcity);
+	g_free(info->workstate);
+	g_free(info->workphone);
+	g_free(info->workfax);
+	g_free(info->workaddr);
+	g_free(info->workzip);
+	g_free(info->workcompany);
+	g_free(info->workdivision);
+	g_free(info->workposition);
+	g_free(info->workwebpage);
+	g_free(info->info);
+	g_free(info->status_note_title);
+	g_free(info->auth_request_reason);
+}
+
+static
+int error(OscarData *od, aim_modsnac_t *error_snac, ByteStream *bs)
+{
+	aim_snac_t *original_snac = aim_remsnac(od, error_snac->id);
+	guint16 *request_type;
+	GSList *original_info_ptr;
+	struct aim_icq_info *original_info;
+	guint16 reason;
+	gchar *uin;
+
+	if (!original_snac || (original_snac->family != SNAC_FAMILY_ICQ) || !original_snac->data) {
+		purple_debug_misc("oscar", "icq: the original snac for the error packet was not found");
+		g_free(original_snac);
+		return 0;
+	}
+	
+	request_type = original_snac->data;
+	original_info_ptr = g_slist_find_custom(od->icq_info, &original_snac->id, compare_icq_infos);
+	original_info = original_info_ptr->data;
+	
+	if (!original_info_ptr) {
+		purple_debug_misc("oscar", "icq: the request info for the error packet was not found");
+		g_free(original_snac);
+		return 0;
+	}
+	
+	reason = byte_stream_get16(bs);
+	uin = g_strdup_printf("%u", original_info->uin);
+	switch (*request_type) {
+		case AIM_ICQ_INFO_REQUEST:
+			oscar_user_info_display_error(od, reason, uin);
+			break;
+		case AIM_ICQ_ALIAS_REQUEST:
+			/* Couldn't retrieve an alias for the buddy requesting authorization; have to make do with UIN only. */
+			if (original_info->for_auth_request)
+				oscar_auth_recvrequest(od->gc, uin, NULL, original_info->auth_request_reason);
+			break;
+		default:
+			purple_debug_misc("oscar", "icq: got an error packet with unknown request type %u", *request_type);
+			break;
+	}
+
+	aim_icq_freeinfo(original_info);
+	od->icq_info = g_slist_remove(od->icq_info, original_info_ptr);
+	g_free(original_snac->data);
+	g_free(original_snac);
+	return 1;
+}
+
 int
 aim_icq_setsecurity(OscarData *od, gboolean auth_required, gboolean webaware)
 {
@@ -124,6 +220,7 @@
 	aim_snacid_t snacid;
 	int bslen;
 	struct aim_icq_info *info;
+	guint16 request_type = AIM_ICQ_INFO_REQUEST;
 
 	if (!uin || uin[0] < '0' || uin[0] > '9')
 		return -EINVAL;
@@ -135,7 +232,7 @@
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -145,7 +242,7 @@
 	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
-	byte_stream_putle16(&bs, 0x04b2); /* shrug. */
+	byte_stream_putle16(&bs, request_type); /* shrug. */
 	byte_stream_putle32(&bs, atoi(uin));
 
 	flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE);
@@ -156,8 +253,7 @@
 	info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
 	info->reqid = snacid;
 	info->uin = atoi(uin);
-	info->next = od->icq_info;
-	od->icq_info = info;
+	od->icq_info = g_slist_prepend(od->icq_info, info);
 
 	return 0;
 }
@@ -169,6 +265,7 @@
 	aim_snacid_t snacid;
 	int bslen;
 	struct aim_icq_info *info;
+	guint16 request_type = AIM_ICQ_ALIAS_REQUEST;
 
 	if (!uin || uin[0] < '0' || uin[0] > '9')
 		return -EINVAL;
@@ -182,7 +279,7 @@
 
 	byte_stream_new(&bs, 4 + bslen);
 
-	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, NULL, 0);
+	snacid = aim_cachesnac(od, SNAC_FAMILY_ICQ, 0x0002, 0x0000, &request_type, sizeof(request_type));
 
 	/* For simplicity, don't bother using a tlvlist */
 	byte_stream_put16(&bs, 0x0001);
@@ -192,7 +289,7 @@
 	byte_stream_putuid(&bs, od);
 	byte_stream_putle16(&bs, 0x07d0); /* I command thee. */
 	byte_stream_putle16(&bs, snacid); /* eh. */
-	byte_stream_putle16(&bs, 0x04ba); /* shrug. */
+	byte_stream_putle16(&bs, request_type); /* shrug. */
 	byte_stream_putle32(&bs, atoi(uin));
 
 	flap_connection_send_snac_with_priority(od, conn, SNAC_FAMILY_ICQ, 0x0002, 0x0000, snacid, &bs, FALSE);
@@ -203,10 +300,9 @@
 	info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
 	info->reqid = snacid;
 	info->uin = atoi(uin);
-	info->next = od->icq_info;
 	info->for_auth_request = for_auth_request;
 	info->auth_request_reason = g_strdup(auth_request_reason);
-	od->icq_info = info;
+	od->icq_info = g_slist_prepend(od->icq_info, info);
 
 	return 0;
 }
@@ -331,54 +427,15 @@
 			g_free(utf8);
 		}
 	}
-
-	g_free(info->auth_request_reason);
 	return 1;
 }
 
-static void aim_icq_freeinfo(struct aim_icq_info *info) {
-	int i;
-
-	if (!info)
-		return;
-	g_free(info->nick);
-	g_free(info->first);
-	g_free(info->last);
-	g_free(info->email);
-	g_free(info->homecity);
-	g_free(info->homestate);
-	g_free(info->homephone);
-	g_free(info->homefax);
-	g_free(info->homeaddr);
-	g_free(info->mobile);
-	g_free(info->homezip);
-	g_free(info->personalwebpage);
-	if (info->email2)
-		for (i = 0; i < info->numaddresses; i++)
-			g_free(info->email2[i]);
-	g_free(info->email2);
-	g_free(info->workcity);
-	g_free(info->workstate);
-	g_free(info->workphone);
-	g_free(info->workfax);
-	g_free(info->workaddr);
-	g_free(info->workzip);
-	g_free(info->workcompany);
-	g_free(info->workdivision);
-	g_free(info->workposition);
-	g_free(info->workwebpage);
-	g_free(info->info);
-	g_free(info->status_note_title);
-	g_free(info);
-}
-
 /**
  * Subtype 0x0003 - Response to SNAC_FAMILY_ICQ/0x002, contains an ICQesque packet.
  */
 static int
-icqresponse(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
+icqresponse(OscarData *od, aim_modsnac_t *snac, ByteStream *bs)
 {
-	int ret = 0;
 	GSList *tlvlist;
 	aim_tlv_t *datatlv;
 	ByteStream qbs;
@@ -402,20 +459,21 @@
 
 	if (cmd == 0x07da) { /* information */
 		guint16 subtype;
+		GSList *info_ptr;
 		struct aim_icq_info *info;
 
 		subtype = byte_stream_getle16(&qbs);
 		byte_stream_advance(&qbs, 1); /* 0x0a */
 
 		/* find other data from the same request */
-		for (info = od->icq_info; info && (info->reqid != reqid); info = info->next);
-		if (!info) {
-			info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
-			info->reqid = reqid;
-			info->next = od->icq_info;
-			od->icq_info = info;
+		info_ptr = g_slist_find_custom(od->icq_info, &reqid, compare_icq_infos);
+		if (!info_ptr) {
+			struct aim_icq_info *new_info = (struct aim_icq_info *)g_new0(struct aim_icq_info, 1);
+			new_info->reqid = reqid;
+			info_ptr = od->icq_info = g_slist_prepend(od->icq_info, new_info);
 		}
 
+		info = info_ptr->data;
 		switch (subtype) {
 		case 0x00a0: { /* hide ip status */
 			/* nothing */
@@ -667,8 +725,7 @@
 
 				memcpy(&info->icbm_cookie, cookie, 8);
 
-				info->next = od->icq_info;
-				od->icq_info = info;
+				od->icq_info = g_slist_prepend(od->icq_info, info);
 
 				flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, FALSE);
 
@@ -688,28 +745,23 @@
 			if (info->uin && info->nick)
 				gotalias(od, info);
 
-			if (od->icq_info == info) {
-				od->icq_info = info->next;
-			} else {
-				struct aim_icq_info *cur;
-				for (cur=od->icq_info; (cur->next && (cur->next!=info)); cur=cur->next);
-				if (cur->next)
-					cur->next = cur->next->next;
-			}
 			aim_icq_freeinfo(info);
+			od->icq_info = g_slist_remove(od->icq_info, info);
 		}
 	}
 
 	aim_tlvlist_free(tlvlist);
 
-	return ret;
+	return 1;
 }
 
 static int
 snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 {
-	if (snac->subtype == 0x0003)
-		return icqresponse(od, conn, mod, frame, snac, bs);
+	if (snac->subtype == 0x0001)
+		return error(od, snac, bs);
+	else if (snac->subtype == 0x0003)
+		return icqresponse(od, snac, bs);
 
 	return 0;
 }
@@ -717,15 +769,10 @@
 static void
 icq_shutdown(OscarData *od, aim_module_t *mod)
 {
-	struct aim_icq_info *del;
-
-	while (od->icq_info) {
-		del = od->icq_info;
-		od->icq_info = od->icq_info->next;
-		aim_icq_freeinfo(del);
-	}
-
-	return;
+	GSList *cur;
+	for (cur = od->icq_info; cur; cur = cur->next)
+		aim_icq_freeinfo(cur->data);
+	g_slist_free(od->icq_info);
 }
 
 int
--- a/libpurple/protocols/oscar/oscar.h	Sun May 30 13:53:45 2010 +0000
+++ b/libpurple/protocols/oscar/oscar.h	Sun May 30 19:01:30 2010 +0000
@@ -542,7 +542,7 @@
 	 */
 
 	IcbmCookie *msgcookies;
-	struct aim_icq_info *icq_info;
+	GSList *icq_info;
 
 	/** Only used when connecting with the old-style BUCP login. */
 	struct aim_authresp_info *authinfo;
@@ -1414,9 +1414,6 @@
 	guint16 numaddresses;
 	char **email2;
 
-	/* we keep track of these in a linked list because we're 1337 */
-	struct aim_icq_info *next;
-
 	/* status note info */
 	guint8 icbm_cookie[8];
 	char *status_note_title;
--- a/libpurple/protocols/oscar/userinfo.c	Sun May 30 13:53:45 2010 +0000
+++ b/libpurple/protocols/oscar/userinfo.c	Sun May 30 19:01:30 2010 +0000
@@ -369,7 +369,7 @@
 }
 
 void
-oscar_user_info_display_error(OscarData *od, guint16 error_reason, char *buddy)
+oscar_user_info_display_error(OscarData *od, guint16 error_reason, gchar *buddy)
 {
 	PurpleNotifyUserInfo *user_info = purple_notify_user_info_new();
 	gchar *buf = g_strdup_printf(_("User information not available: %s"), oscar_get_msgerr_reason(error_reason));
@@ -395,7 +395,7 @@
 
 	if (!info->uin)
 		return;
-	
+
 	user_info = purple_notify_user_info_new();
 
 	g_snprintf(who, sizeof(who), "%u", info->uin);