changeset 3212:56746b0868db

[gaim-migrate @ 3229] More OSCAR stuff from KingAnt! committer: Tailor Script <tailor@pidgin.im>
author Sean Egan <seanegan@gmail.com>
date Sun, 05 May 2002 19:02:08 +0000
parents 3ef95e625bbc
children 7ed9fa994055
files ChangeLog src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/im.c src/protocols/oscar/oscar.c
diffstat 5 files changed, 330 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun May 05 18:43:20 2002 +0000
+++ b/ChangeLog	Sun May 05 19:02:08 2002 +0000
@@ -15,7 +15,8 @@
 	* Better selection in GtkIMHtml (Thanks Ben Miller)
 	* A warning when your OSCAR buddy list is too long
 	  (Thanks, Mark Doliner)
-
+	* ICQ status messages in OSCAR (Thanks, Mark Doliner)
+	
 version 0.57 (04/25/2002):
 	* New authorization method for Yahoo!
 	* Polish translation updated (Thanks Przemyslaw Sulek)
--- a/src/protocols/oscar/aim.h	Sun May 05 18:43:20 2002 +0000
+++ b/src/protocols/oscar/aim.h	Sun May 05 19:02:08 2002 +0000
@@ -854,6 +854,7 @@
 faim_export aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn);
 faim_export aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const fu8_t *cookie);
 
+faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type);
 faim_export aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, fu16_t numfiles, fu32_t totsize);
 faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message);
 
--- a/src/protocols/oscar/aim_cbtypes.h	Sun May 05 18:43:20 2002 +0000
+++ b/src/protocols/oscar/aim_cbtypes.h	Sun May 05 19:02:08 2002 +0000
@@ -97,7 +97,7 @@
 #define AIM_CB_MSG_INCOMING 0x0007
 #define AIM_CB_MSG_EVIL 0x0009
 #define AIM_CB_MSG_MISSEDCALL 0x000a
-#define AIM_CB_MSG_CLIENTERROR 0x000b
+#define AIM_CB_MSG_CLIENTAUTORESP 0x000b
 #define AIM_CB_MSG_ACK 0x000c
 #define AIM_CB_MSG_DEFAULT 0xffff
 
--- a/src/protocols/oscar/im.c	Sun May 05 18:43:20 2002 +0000
+++ b/src/protocols/oscar/im.c	Sun May 05 19:02:08 2002 +0000
@@ -732,16 +732,127 @@
 	return 0;
 }
 
-/*
+/**
+ * Request the status message of the given ICQ user.
+ *
+ * @param sess The oscar session.
+ * @param sn The UIN of the user of whom you wish to request info.
+ * @param type The type of info you wish to request.  This should be the current 
+ *        state of the user, as one of the AIM_ICQ_STATE_* defines.
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type)
+{
+	aim_conn_t *conn;
+	int i;
+	fu8_t ck[8];
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn)
+		return -EINVAL;
+
+	for (i = 0; i < 8; i++)
+		aimutil_put8(ck+i, (fu8_t) rand());
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
+
+	/* Cookie */
+	aimbs_putraw(&fr->data, ck, 8);
+
+	/* Channel (2) */
+	aimbs_put16(&fr->data, 0x0002);
+
+	/* Dest sn */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
+
+	/* TLV t(0005) - Encompasses almost everything below. */
+	aimbs_put16(&fr->data, 0x0005); /* T */
+	aimbs_put16(&fr->data, 0x005e); /* L */
+	{ /* V */
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* Cookie */
+		aimbs_putraw(&fr->data, ck, 8);
+
+		/* Put the 16 byte server relay capability */
+		aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
+
+		/* TLV t(000a) */
+		aimbs_put16(&fr->data, 0x000a);
+		aimbs_put16(&fr->data, 0x0002);
+		aimbs_put16(&fr->data, 0x0001);
+
+		/* TLV t(000f) */
+		aimbs_put16(&fr->data, 0x000f);
+		aimbs_put16(&fr->data, 0x0000);
+
+		/* TLV t(2711) */
+		aimbs_put16(&fr->data, 0x2711);
+		aimbs_put16(&fr->data, 0x0036);
+		{ /* V */
+			aimbs_putle16(&fr->data, 0x001b); /* L */
+			aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle16(&fr->data, 0x0003); /* Client features? */
+			aimbs_putle16(&fr->data, 0x0000); /* Unknown */
+			aimbs_putle8(&fr->data, 0x00); /* Unkizown */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+
+			aimbs_putle16(&fr->data, 0x000e); /* L */
+			aimbs_putle16(&fr->data, 0xffff); /* Sequence number?  XXX - This should decrement by 1 with each request */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+			aimbs_putle32(&fr->data, 0x00000000); /* Unknown */
+
+			/* The type of status message being requested */
+			if (type & AIM_ICQ_STATE_CHAT)
+				aimbs_putle16(&fr->data, 0x03ec);
+			else if(type & AIM_ICQ_STATE_DND)
+				aimbs_putle16(&fr->data, 0x03eb);
+			else if(type & AIM_ICQ_STATE_OUT)
+				aimbs_putle16(&fr->data, 0x03ea);
+			else if(type & AIM_ICQ_STATE_BUSY)
+				aimbs_putle16(&fr->data, 0x03e9);
+			else if(type & AIM_ICQ_STATE_AWAY)
+				aimbs_putle16(&fr->data, 0x03e8);
+
+			aimbs_putle16(&fr->data, 0x0000); /* Status? */
+			aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */
+			aimbs_putle16(&fr->data, 0x0001); /* L? */
+			aimbs_putle8(&fr->data, 0x00); /* Null termination? */
+		} /* End TLV t(2711) */
+	} /* End TLV t(0005) */
+
+	/* TLV t(0003) */
+	aimbs_put16(&fr->data, 0x0003);
+	aimbs_put16(&fr->data, 0x0000);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/**
  * This can be used to send an ICQ authorization reply (deny or grant).  It is the "old way."  
  * The new way is to use SSI.  I like the new way a lot better.  This seems like such a hack, 
  * mostly because it's in network byte order.  Figuring this stuff out sometimes takes a while, 
  * but thats ok, because it gives me time to try to figure out what kind of drugs the AOL people 
  * were taking when they merged the two protocols.
  *
- * sn is the destination screen name
- * type is the type of message.  0x0007 for authorization denied.  0x0008 for authorization granted
- * message is the message you want to send, it should be null terminated
+ * @param sn The destination screen name.
+ * @param type The type of message.  0x0007 for authorization denied.  0x0008 for authorization granted.
+ * @param message The message you want to send, it should be null terminated.
+ * @return Return 0 if no errors, otherwise return the error number.
  */
 faim_export int aim_send_im_ch4(aim_session_t *sess, char *sn, fu16_t type, fu8_t *message)
 {
@@ -1859,7 +1970,11 @@
 	return ret;
 }
 
-static int clienterr(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
+/*
+ * Receive the response from an ICQ status message request.  This contains the 
+ * ICQ status message.  Go figure.
+ */
+static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 	int ret = 0;
 	aim_rxcallback_t userfunc;
@@ -1873,8 +1988,58 @@
 	sn = aimbs_getstr(bs, snlen);
 	reason = aimbs_get16(bs);
 
-	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-		 ret = userfunc(sess, rx, channel, sn, reason);
+	switch (reason) {
+		case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
+			fu8_t statusmsgtype, *msg;
+			fu16_t len;
+			fu32_t state;
+
+			len = aimbs_getle16(bs); /* Should be 0x001b */
+			free(aimbs_getraw(bs, len)); /* Unknown */
+
+			len = aimbs_getle16(bs); /* Should be 0x000e */
+			free(aimbs_getraw(bs, len)); /* Unknown */
+
+			statusmsgtype = aimbs_getle8(bs);
+			switch (statusmsgtype) {
+				case 0xe8:
+					state = AIM_ICQ_STATE_AWAY;
+					break;
+				case 0xe9:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
+					break;
+				case 0xea:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
+					break;
+				case 0xeb:
+					state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
+					break;
+				case 0xec:
+					state = AIM_ICQ_STATE_CHAT;
+					break;
+				default:
+					state = 0;
+					break;
+			}
+
+			aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
+			aimbs_getle16(bs); /* Unknown - 0x0000 */
+			aimbs_getle16(bs); /* Unknown - 0x0000 */
+
+			len = aimbs_getle16(bs);
+			msg = aimbs_getraw(bs, len);
+
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				ret = userfunc(sess, rx, channel, sn, reason, state, msg);
+
+			free(msg);
+		} break;
+
+		default: {
+			if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+				ret = userfunc(sess, rx, channel, sn, reason);
+		} break;
+	} /* end switch */
 
 	free(ck);
 	free(sn);
@@ -1916,7 +2081,7 @@
 	else if (snac->subtype == 0x000a)
 		return missedcall(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x000b)
-		return clienterr(sess, mod, rx, snac, bs);
+		return clientautoresp(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x000c)
 		return msgack(sess, mod, rx, snac, bs);
 
--- a/src/protocols/oscar/oscar.c	Sun May 05 18:43:20 2002 +0000
+++ b/src/protocols/oscar/oscar.c	Sun May 05 19:02:08 2002 +0000
@@ -243,6 +243,7 @@
 static int gaim_parse_offgoing   (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_misses     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_clientauto (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_user_info  (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_motd       (aim_session_t *, aim_frame_t *, ...);
 static int gaim_chatnav_info     (aim_session_t *, aim_frame_t *, ...);
@@ -691,6 +692,7 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_CLIENTAUTORESP, gaim_parse_clientauto, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_EVIL, gaim_parse_evilnotify, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
@@ -1630,6 +1632,83 @@
 	return 1;
 }
 
+static char *gaim_icq_status(int state) {
+	/* Make a cute little string that shows the status of the dude or dudet */
+	if (state & AIM_ICQ_STATE_CHAT)
+		return g_strdup_printf("Free For Chat");
+	else if (state & AIM_ICQ_STATE_DND)
+		return g_strdup_printf("Do Not Disturb");
+	else if (state & AIM_ICQ_STATE_OUT)
+		return g_strdup_printf("Not Available");
+	else if (state & AIM_ICQ_STATE_BUSY)
+		return g_strdup_printf("Occupied");
+	else if (state & AIM_ICQ_STATE_AWAY)
+		return g_strdup_printf("Away");
+	else if (state & AIM_ICQ_STATE_WEBAWARE)
+		return g_strdup_printf("Web Aware");
+	else if (state & AIM_ICQ_STATE_INVISIBLE)
+		return g_strdup_printf("Invisible");
+	else
+		return g_strdup_printf("Online");
+}
+
+static int gaim_parse_clientauto(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	va_list ap;
+	fu16_t chan, reason;
+	char *who;
+
+	va_start(ap, fr);
+	chan = (fu16_t)va_arg(ap, unsigned int);
+	who = va_arg(ap, char *);
+	reason = (fu16_t)va_arg(ap, unsigned int);
+
+	switch(reason) {
+		case 0x0003: { /* Reply from an ICQ status message request */
+			int state = (int)va_arg(ap, fu32_t);
+			char *msg = va_arg(ap, char *);
+			char *status_msg = gaim_icq_status(state);
+			char *dialog_msg, **splitmsg;
+			struct oscar_data *od = gc->proto_data;
+			GSList *l = od->evilhack;
+			gboolean evilhack = FALSE;
+
+			/* Split at (carriage return/newline)'s, then rejoin later with BRs between. */
+			splitmsg = g_strsplit(msg, "\r\n", 0);
+
+			/* If who is in od->evilhack, then we're just getting the away message, otherwise this 
+			 * will just get appended to the info box (which is already showing). */
+			while (l) {
+				char *x = l->data;
+				if (!strcmp(x, normalize(who))) {
+					evilhack = TRUE;
+					g_free(x);
+					od->evilhack = g_slist_remove(od->evilhack, x);
+					break;
+				}
+				l = l->next;
+			}
+
+			if (evilhack)
+				dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR>%s<BR>"), who, status_msg, g_strjoinv("<BR>", splitmsg));
+			else
+				dialog_msg = g_strdup_printf(_("<B>Status:</B> %s<BR><HR><BR>%s<BR>"), status_msg, g_strjoinv("<BR>", splitmsg));
+			g_show_info_text(gc, who, 2, dialog_msg, NULL);
+
+			g_free(status_msg);
+			g_free(dialog_msg);
+			g_strfreev(splitmsg);
+		} break;
+
+		default: {
+			debug_printf("Received an unknown client auto-response from %s.  Type 0x%04x\n", who, reason);
+		} break;
+	}
+	va_end(ap);
+
+	return 1;
+}
+
 static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	fu16_t reason;
@@ -2397,6 +2476,7 @@
 static int gaim_simpleinfo(aim_session_t *sess, aim_frame_t *fr, ...)
 {
 	struct gaim_connection *gc = sess->aux_data;
+	struct buddy *budlight;
 	va_list ap;
 	struct aim_icq_simpleinfo *info;
 	char buf[16 * 1024];
@@ -2411,13 +2491,31 @@
 		   "<B>UIN:</B> %lu<BR>"
 		   "<B>Nick:</B> %s<BR>"
 		   "<B>Name:</B> %s %s<BR>"
-		   "<B>Email:</B> %s\n",
+		   "<B>Email:</B> %s<BR>\n",
 		   info->uin,
 		   info->nick,
 		   info->first, info->last,
 		   info->email);
 
-	g_show_info_text(gc, who, FALSE, buf, NULL);
+	/* If the contact is away, then we also want to get their status message
+	 * and show it in the same window as info.  g_show_info_text gets the status 
+	 * message if the third arg is 0 (this seems really gross to me).  The 
+	 * parse-icq-status-message function knows if it is putting it's message in 
+	 * an info window because the name will _not_ be in od->evilhack.  For getting 
+	 * only the away message the contact's UIN is put in od->evilhack. */
+	if ((budlight = find_buddy(gc, who)) && ((budlight->uc >> 7) & (AIM_ICQ_STATE_AWAY || AIM_ICQ_STATE_DND || AIM_ICQ_STATE_OUT || AIM_ICQ_STATE_BUSY || AIM_ICQ_STATE_CHAT))) {
+		if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
+			g_show_info_text(gc, who, 0, buf, NULL);
+		else {
+			char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7);
+			g_show_info_text(gc, who, 2, buf, "<B>Status:</B> ", state_msg, "<BR>\n<HR><BR><I>Remote client does not support sending status messages.</I><BR>\n", NULL);
+			free(state_msg);
+		}
+	} else {
+		char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7);
+		g_show_info_text(gc, who, 2, buf, "<B>Status:</B> ", state_msg, NULL);
+		free(state_msg);
+	}
 
 	return 1;
 }
@@ -2459,7 +2557,7 @@
 	at += g_snprintf(buf + at, len - at, "<B>%s has the following screen names:</B><BR>", address);
 	for (i = 0; i < num; i++)
 		at += g_snprintf(buf + at, len - at, "%s<BR>", &SNs[i * (MAXSNLEN + 1)]);
-	g_show_info_text(NULL, NULL, FALSE, buf, NULL);
+	g_show_info_text(NULL, NULL, 2, buf, NULL);
 	g_free(buf);
 
 	return 1;
@@ -2635,10 +2733,22 @@
 		aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE);
 }
 
-static void oscar_get_away(struct gaim_connection *g, char *name) {
+static void oscar_get_away(struct gaim_connection *g, char *who) {
 	struct oscar_data *odata = (struct oscar_data *)g->proto_data;
-	if (!odata->icq)
-		aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO);
+	if (odata->icq) {
+		struct buddy *budlight = find_buddy(g, who);
+		if (budlight)
+			if ((budlight->uc & 0xff80) >> 7)
+				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
+					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
+				else
+					debug_printf("Error: Remote client does not support retrieval of status messages.\n");
+			else
+				debug_printf("Error: The user %s has no status message, therefore not requesting.\n", who);
+		else
+			debug_printf("Error: Could not find %s in local contact list, therefore unable to request status message.\n", who);
+	} else
+		aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_GENERALINFO);
 }
 
 static void oscar_get_caps(struct gaim_connection *g, char *name) {
@@ -2729,13 +2839,13 @@
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY);
 		gc->away = "";
 	} else if (!strcmp(state, "Do Not Disturb")) {
-		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_DND);
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY);
 		gc->away = "";
 	} else if (!strcmp(state, "Not Available")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY);
 		gc->away = "";
 	} else if (!strcmp(state, "Occupied")) {
-		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_BUSY);
+		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY);
 		gc->away = "";
 	} else if (!strcmp(state, "Free For Chat")) {
 		aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT);
@@ -3236,12 +3346,12 @@
 			return icon_offline_xpm;
 		if (uc & AIM_ICQ_STATE_CHAT)
 			return icon_ffc_xpm;
+		if (uc & AIM_ICQ_STATE_DND)
+		 	return icon_dnd_xpm;
+		if (uc & AIM_ICQ_STATE_OUT)
+			return icon_na_xpm;
 		if (uc & AIM_ICQ_STATE_BUSY)
 		 	return icon_occ_xpm;
-		if (uc & AIM_ICQ_STATE_OUT)
-			return icon_na_xpm;
-		if (uc & AIM_ICQ_STATE_DND)
-		 	return icon_dnd_xpm;
 		if (uc & AIM_ICQ_STATE_AWAY)
 			return icon_away_xpm;
 		return icon_online_xpm;
@@ -3433,7 +3543,30 @@
 static void oscar_get_away_msg(struct gaim_connection *gc, char *who) {
 	struct oscar_data *od = gc->proto_data;
 	od->evilhack = g_slist_append(od->evilhack, g_strdup(normalize(who)));
-	oscar_get_info(gc, who);
+	if (od->icq) {
+		struct buddy *budlight = find_buddy(gc, who);
+		if (budlight)
+			if ((budlight->uc >> 7) & (AIM_ICQ_STATE_AWAY || AIM_ICQ_STATE_DND || AIM_ICQ_STATE_OUT || AIM_ICQ_STATE_BUSY || AIM_ICQ_STATE_CHAT))
+				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
+					aim_send_im_ch2_geticqmessage(od->sess, who, (budlight->uc & 0xff80) >> 7);
+				else {
+					char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7);
+					char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR><I>Remote client does not support sending status messages.</I><BR>"), who, state_msg);
+					g_show_info_text(gc, who, 2, dialog_msg, NULL);
+					free(state_msg);
+					free(dialog_msg);
+				}
+			else {
+				char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7);
+				char *dialog_msg = g_strdup_printf(_("<B>UIN:</B> %s<BR><B>Status:</B> %s<BR><HR><BR><I>User has no status message.</I><BR>"), who, state_msg);
+				g_show_info_text(gc, who, 2, dialog_msg, NULL);
+				free(state_msg);
+				free(dialog_msg);
+			}
+		else
+			do_error_dialog("Could not find contact in local list, therefore unable to request status message.\n", "Gaim - Error");
+	} else
+		oscar_get_info(gc, who);
 }
 
 static GList *oscar_buddy_menu(struct gaim_connection *gc, char *who) {
@@ -3448,7 +3581,13 @@
 	pbm->gc = gc;
 	m = g_list_append(m, pbm);
 
-	if (!odata->icq) {
+	if (odata->icq) {
+		pbm = g_new0(struct proto_buddy_menu, 1);
+		pbm->label = _("Get Status Msg");
+		pbm->callback = oscar_get_away_msg;
+		pbm->gc = gc;
+		m = g_list_append(m, pbm);
+	} else {
 		pbm = g_new0(struct proto_buddy_menu, 1);
 		pbm->label = _("Get Away Msg");
 		pbm->callback = oscar_get_away_msg;