changeset 3595:1e60a05c7482

[gaim-migrate @ 3697] This adds le non-direct connect typing notification for oscar. How it works, in a nutshell: -AIM servers append a flag telling remote clients you support typing notification -You tell the AIM servers you support typing notification when you sign on (by changing the "flags" passed to aim_seticbmparam()) -Clients send little typing notification messages to other clients, but only when they know that the other client supports it (so after you've received an IM from the other person) I also fixed a compile warning or two dealing with SSI stuff. Things to look out for: -ICQ states doing weird things -Typing notification works with MacAIM clients (iChat and the official one) -Typing notification doesn't try to send stuff to clients that don't support (I have no idea how you're supposed to look out for this. It should be ok) committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sun, 06 Oct 2002 03:08:47 +0000
parents 17e6dd05666a
children bb966d68f9e3
files src/protocols/oscar/aim.h src/protocols/oscar/aim_cbtypes.h src/protocols/oscar/im.c src/protocols/oscar/oscar.c src/protocols/oscar/ssi.c
diffstat 5 files changed, 182 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/src/protocols/oscar/aim.h	Sun Oct 06 03:07:59 2002 +0000
+++ b/src/protocols/oscar/aim.h	Sun Oct 06 03:08:47 2002 +0000
@@ -168,6 +168,17 @@
 	  "en", \
 }
 
+#define CLIENTINFO_AIM_5_0_2938 { \
+	  "AOL Instant Messenger, version 5.0.2938/WIN32", \
+	  0x0109, \
+	  0x0005, \
+	  0x0000, \
+	  0x0000, \
+	  0x0b7a, \
+	  "us", \
+	  "en", \
+}
+
 #define CLIENTINFO_ICQ_4_65_3281 { \
 	"ICQ Inc. - Product of ICQ (TM) 2000b.4.65.1.3281.85", \
 	0x010a, \
@@ -715,6 +726,7 @@
 #define AIM_IMFLAGS_CUSTOMCHARSET	0x0200 /* charset fields set */
 #define AIM_IMFLAGS_MULTIPART		0x0400 /* ->mpmsg section valid */
 #define AIM_IMFLAGS_OFFLINE		0x0800 /* send to offline user */
+#define AIM_IMFLAGS_TYPINGNOT		0x1000 /* typing notification */
 
 /*
  * Multipart message structures.
@@ -884,6 +896,8 @@
 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);
 
+faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2);
+
 faim_export aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn);
 faim_export int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size);
 faim_export int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn);
@@ -1072,7 +1086,7 @@
 faim_export int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn);
 faim_export int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, const char *gn, const char **sn, unsigned int num);
 faim_export int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn);
-faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num);
+faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, const char **gn, unsigned int num);
 faim_export int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, const char **sn, unsigned int num, fu16_t type);
 faim_export int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn);
 faim_export int aim_ssi_rename_group(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn);
--- a/src/protocols/oscar/aim_cbtypes.h	Sun Oct 06 03:07:59 2002 +0000
+++ b/src/protocols/oscar/aim_cbtypes.h	Sun Oct 06 03:08:47 2002 +0000
@@ -99,6 +99,7 @@
 #define AIM_CB_MSG_MISSEDCALL 0x000a
 #define AIM_CB_MSG_CLIENTAUTORESP 0x000b
 #define AIM_CB_MSG_ACK 0x000c
+#define AIM_CB_MSG_MTN 0x0014
 #define AIM_CB_MSG_DEFAULT 0xffff
 
 /*
--- a/src/protocols/oscar/im.c	Sun Oct 06 03:07:59 2002 +0000
+++ b/src/protocols/oscar/im.c	Sun Oct 06 03:08:47 2002 +0000
@@ -1,7 +1,5 @@
 /*
- *  aim_im.c
- *
- *  The routines for sending/receiving Instant Messages.
+ *  Family 0x0004 - Routines for sending/receiving Instant Messages.
  *
  *  Note the term ICBM (Inter-Client Basic Message) which blankets
  *  all types of genericly routed through-server messages.  Within
@@ -10,7 +8,8 @@
  *  what would commonly be called an "instant message".  Channel 2
  *  is used for negotiating "rendezvous".  These transactions end in
  *  something more complex happening, such as a chat invitation, or
- *  a file transfer.
+ *  a file transfer.  Channel 4 is used for various ICQ messages.  
+ *  Examples are normal messages, URLs, and old-style authorization.
  *
  *  In addition to the channel, every ICBM contains a cookie.  For
  *  standard IMs, these are only used for error messages.  However,
@@ -36,6 +35,8 @@
  *  0501 0003 0101 0201 01       WinAIM 2.0.847, 2.1.1187, 3.0.1464, 
  *                                      4.3.2229, 4.4.2286
  *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
+ *  0501 0003 0101 02            WinAIM 5
+ *  0501 0001 01                 iChat x.x
  *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
  *                                      TOC client
  *
@@ -194,7 +195,6 @@
 		msgtlvlen += 4 /* charset */ + args->msglen;
 	}
 
-
 	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
 		return -ENOMEM;
 
@@ -314,6 +314,8 @@
 
 	/*
 	 * Set the I HAVE A REALLY PURTY ICON flag.
+	 * XXX - This should really only be sent on initial 
+	 * IMs and when you change your icon.
 	 */
 	if (args->flags & AIM_IMFLAGS_HASICON) {
 		aimbs_put16(&fr->data, 0x0008);
@@ -326,6 +328,7 @@
 
 	/*
 	 * Set the Buddy Icon Requested flag.
+	 * XXX - Everytime?  Surely not...
 	 */
 	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
 		aimbs_put16(&fr->data, 0x0009);
@@ -1345,6 +1348,10 @@
 
 			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
 
+		} else if (type == 0x000b) { /* Non-direct connect typing notification */
+
+			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
+
 		} else if (type == 0x0017) {
 
 			args.extdatalen = length;
@@ -2069,6 +2076,82 @@
 	return ret;
 }
 
+/*
+ * Send a mini typing notification (mtn) packet.  This is supported by winaim 5 and up.
+ */
+faim_export int aim_mtn_send(aim_session_t *sess, fu16_t type1, char *sn, fu16_t type2)
+{
+	aim_conn_t *conn;
+	aim_frame_t *fr;
+	aim_snacid_t snacid;
+
+	if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0002)))
+		return -EINVAL;
+
+	if (!sn)
+		return -EINVAL;
+
+	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+11+strlen(sn)+2)))
+		return -ENOMEM;
+
+	snacid = aim_cachesnac(sess, 0x0004, 0x0014, 0x0000, NULL, 0);
+	aim_putsnac(&fr->data, 0x0004, 0x0014, 0x0000, snacid);
+
+	/*
+	 * 8 days of light
+	 * Er, that is to say, 8 bytes of 0's
+	 */
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+	aimbs_put16(&fr->data, 0x0000);
+
+	/*
+	 * Type 1 (should be 0x0001 for mtn)
+	 */
+	aimbs_put16(&fr->data, type1);
+
+	/*
+	 * Dest sn
+	 */
+	aimbs_put8(&fr->data, strlen(sn));
+	aimbs_putraw(&fr->data, sn, strlen(sn));
+
+	/*
+	 * Type 2 (should be 0x0000, 0x0001, or 0x0002 for mtn)
+	 */
+	aimbs_put16(&fr->data, type2);
+
+	aim_tx_enqueue(sess, fr);
+
+	return 0;
+}
+
+/*
+ * Receive a mini typing notification (mtn) packet.  This is supported by winaim5 and up.
+ */
+static int mtn_receive(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;
+	char *sn;
+	fu8_t snlen;
+	fu16_t type1, type2;
+
+	aim_bstream_advance(bs, 8); /* Unknown - All 0's */
+	type1 = aimbs_get16(bs);
+	snlen = aimbs_get8(bs);
+	sn = aimbs_getstr(bs, snlen);
+	type2 = aimbs_get16(bs);
+
+	if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+		ret = userfunc(sess, rx, type1, sn, type2);
+
+	free(sn);
+
+	return ret;
+}
+
 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
 {
 
@@ -2084,6 +2167,8 @@
 		return clientautoresp(sess, mod, rx, snac, bs);
 	else if (snac->subtype == 0x000c)
 		return msgack(sess, mod, rx, snac, bs);
+	else if (snac->subtype == 0x0014)
+		return mtn_receive(sess, mod, rx, snac, bs);
 
 	return 0;
 }
--- a/src/protocols/oscar/oscar.c	Sun Oct 06 03:07:59 2002 +0000
+++ b/src/protocols/oscar/oscar.c	Sun Oct 06 03:08:47 2002 +0000
@@ -70,16 +70,17 @@
 #define UC_NORMAL	0x10
 #define UC_AB		0x20
 #define UC_WIRELESS	0x40
+#define UC_TYPINGNOT	0x80
 
 #define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3"
 
 static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_IMIMAGE;
-/* What does AIM_CAPS_ICQ actually mean? -SE */
-/*   static int caps_icq = AIM_CAPS_ICQ; */
 
 /* Set AIM caps, because Gaim can still do them over ICQ and 
  * Winicq doesn't mind. */
 static int caps_icq = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_IMIMAGE;
+/* static int caps_icq = AIM_CAPS_ICQ; */
+/* What does AIM_CAPS_ICQ actually mean? -SE */
 
 static fu8_t gaim_features[] = {0x01, 0x01, 0x01, 0x02};
 
@@ -269,6 +270,7 @@
 static int conninitdone_chat     (aim_session_t *, aim_frame_t *, ...);
 static int conninitdone_chatnav  (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_msgerr     (aim_session_t *, aim_frame_t *, ...);
+static int gaim_parse_mtn        (aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...);
 static int gaim_parse_locerr     (aim_session_t *, aim_frame_t *, ...);
@@ -708,6 +710,7 @@
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, AIM_CB_LOK_ERROR, gaim_parse_searcherror, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOK, 0x0003, gaim_parse_searchreply, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0);
+	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MTN, gaim_parse_mtn, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parse_user_info, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ACK, gaim_parse_msgack, 0);
 	aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0);
@@ -1186,7 +1189,7 @@
 				type |= UC_WIRELESS;
 	}
 	if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) {
-		type = (info->icqinfo.status << 7);
+		type = (info->icqinfo.status << 16);
 		if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) &&
 		      (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) {
 			type |= UC_UNAVAILABLE;
@@ -1405,6 +1408,12 @@
 	} else
 		g_snprintf(tmp, BUF_LONG, "%s", args->msg);
 
+	if (args->icbmflags & AIM_IMFLAGS_TYPINGNOT) {
+		struct buddy *b = find_buddy(gc, userinfo->sn);
+		if (b)
+			b->uc |= UC_TYPINGNOT;
+	}
+
 	strip_linefeed(tmp);
 	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
 	g_free(tmp);
@@ -1828,6 +1837,41 @@
 	return 1;
 }
 
+static int gaim_parse_mtn(aim_session_t *sess, aim_frame_t *fr, ...) {
+	struct gaim_connection *gc = sess->aux_data;
+	va_list ap;
+	fu16_t type1, type2;
+	char *sn;
+
+	va_start(ap, fr);
+	type1 = (fu16_t)va_arg(ap, unsigned int);
+	sn = va_arg(ap, char *);
+	type2 = (fu16_t)va_arg(ap, unsigned int);
+	va_end(ap);
+
+	debug_printf("Received an mtn from %s.  Type1 is 0x%04d and type2 is 0x%04d.\n", sn, type1, type2);
+
+	switch (type2) {
+		case 0x0000: { /* Text has been cleared */
+			serv_got_typing_stopped(gc, sn);
+		} break;
+
+		case 0x0001: { /* Paused typing */
+			serv_got_typing_stopped(gc, sn);
+		} break;
+
+		case 0x0002: { /* Typing */
+			serv_got_typing(gc, sn, 0);
+		} break;
+
+		default: {
+			printf("Received unknown typing notification type.\n");
+		} break;
+	}
+
+	return 1;
+}
+
 static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *destn;
@@ -2413,6 +2457,7 @@
 	*/
 
 	/* Maybe senderwarn and recverwarn should be user preferences... */
+	params->flags = 0x0000000b;
 	params->maxmsglen = 8000;
 	params->minmsginterval = 0;
 
@@ -2591,16 +2636,16 @@
 	 * 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))) {
-		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->uc >> 16) & (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);
+				char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
 				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);
+			char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
 			g_show_info_text(gc, who, 2, buf, "<B>Status:</B> ", state_msg, NULL);
 			free(state_msg);
 		}
@@ -2726,9 +2771,17 @@
 static int oscar_send_typing(struct gaim_connection *gc, char *name, int typing) {
 	struct oscar_data *odata = (struct oscar_data *)gc->proto_data;
 	struct direct_im *dim = find_direct_im(odata, name);
-	if (!dim)
-		return 0;
-	aim_send_typing(odata->sess, dim->conn, typing);
+	if (dim)
+		aim_send_typing(odata->sess, dim->conn, typing);
+	else {
+		struct buddy *b = find_buddy(gc, name);
+		if (b && (b->uc & UC_TYPINGNOT)) {
+			if (typing)
+				aim_mtn_send(odata->sess, 0x0001, name, 0x0002);
+			else
+				aim_mtn_send(odata->sess, 0x0001, name, 0x0000);
+		}
+	}
 	return 0;
 }
 static void oscar_ask_direct_im(struct gaim_connection *gc, char *name);
@@ -2825,9 +2878,9 @@
 	if (odata->icq) {
 		struct buddy *budlight = find_buddy(g, who);
 		if (budlight)
-			if ((budlight->uc & 0xff80) >> 7)
+			if ((budlight->uc & 0xffff0000) >> 16)
 				if (budlight->caps & AIM_CAPS_ICQSERVERRELAY)
-					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7);
+					aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xffff0000) >> 16);
 				else
 					debug_printf("Error: Remote client does not support retrieval of status messages.\n");
 			else
@@ -3026,7 +3079,7 @@
 							sns[tmp] = (char *)((struct buddy*)curbud->data)->name;
 							tmp++;
 						}
-					aim_ssi_addbuddies(odata->sess, odata->conn, ((struct group*)curgrp->data)->name, sns, tmp);
+					aim_ssi_addbuddies(odata->sess, odata->conn, ((struct group*)curgrp->data)->name, (const char**)sns, tmp);
 					free(sns);
 				}
 			}
@@ -3448,8 +3501,8 @@
 static char **oscar_list_icon(int uc) {
 	if (uc == 0)
 		return (char **)icon_online_xpm;
-	if (uc & 0xff80) {
-		uc >>= 7;
+	if (uc & 0xffff0000) {
+		uc >>= 16;
 		if (uc & AIM_ICQ_STATE_INVISIBLE)
 			return icon_offline_xpm;
 		if (uc & AIM_ICQ_STATE_CHAT)
@@ -3654,18 +3707,18 @@
 	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->uc >> 16) & (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);
+					aim_send_im_ch2_geticqmessage(od->sess, who, (budlight->uc & 0xffff0000) >> 16);
 				else {
-					char *state_msg = gaim_icq_status((budlight->uc & 0xff80) >> 7);
+					char *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
 					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 *state_msg = gaim_icq_status((budlight->uc & 0xffff0000) >> 16);
 				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);
@@ -3813,7 +3866,7 @@
 	} else {
 		debug_printf("ssi: About to delete a permit\n");
 		if (od->sess->ssi.received_data)
-			aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT);
+			aim_ssi_delpord(od->sess, od->conn, (const char **) &who, 1, AIM_SSI_TYPE_PERMIT);
 	}
 }
 
@@ -3825,7 +3878,7 @@
 	} else {
 		debug_printf("ssi: About to delete a deny\n");
 		if (od->sess->ssi.received_data)
-			aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY);
+			aim_ssi_delpord(od->sess, od->conn, (const char **) &who, 1, AIM_SSI_TYPE_DENY);
 	}
 }
 
--- a/src/protocols/oscar/ssi.c	Sun Oct 06 03:07:59 2002 +0000
+++ b/src/protocols/oscar/ssi.c	Sun Oct 06 03:08:47 2002 +0000
@@ -416,7 +416,7 @@
 			char *newgroup;
 			newgroup = (char*)malloc(strlen("Unknown")*sizeof(char));
 			strcpy(newgroup, "Unknown");
-			aim_ssi_addgroups(sess, conn, &newgroup, 1);
+			aim_ssi_addgroups(sess, conn, (const char**)&newgroup, 1);
 		}
 	}
 
@@ -581,7 +581,7 @@
  * @param num The number of groups names you are adding (size of the sn array).
  * @return Return 0 if no errors, otherwise return the error number.
  */
-faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num)
+faim_export int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, const char **gn, unsigned int num)
 {
 	struct aim_ssi_item *parentgroup, **newitems;
 	fu16_t i;
@@ -714,7 +714,7 @@
 
 	/* Look up the new parent group */
 	if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
-		aim_ssi_addgroups(sess, conn, &newgn, 1);
+		aim_ssi_addgroups(sess, conn, (const char**)&newgn, 1);
 		if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) {
 			free(groups);
 			return -ENOMEM;