File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / family_icbm.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Mon Jan 27 19:48:25 2025 UTC (6 months ago) by snw
Branches: MAIN, CoherentLogicDevelopment
CVS tags: test-tag, start, HEAD
Pidgin AIM Plugin for ChivaNet

    1: /*
    2:  * Purple's oscar protocol plugin
    3:  * This file is the legal property of its developers.
    4:  * Please see the AUTHORS file distributed alongside this file.
    5:  *
    6:  * This library is free software; you can redistribute it and/or
    7:  * modify it under the terms of the GNU Lesser General Public
    8:  * License as published by the Free Software Foundation; either
    9:  * version 2 of the License, or (at your option) any later version.
   10:  *
   11:  * This library is distributed in the hope that it will be useful,
   12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14:  * Lesser General Public License for more details.
   15:  *
   16:  * You should have received a copy of the GNU Lesser General Public
   17:  * License along with this library; if not, write to the Free Software
   18:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   19: */
   20: 
   21: /*
   22:  * Family 0x0004 - Routines for sending/receiving Instant Messages.
   23:  *
   24:  * Note the term ICBM (Inter-Client Basic Message) which blankets
   25:  * all types of generically routed through-server messages.  Within
   26:  * the ICBM types (family 4), a channel is defined.  Each channel
   27:  * represents a different type of message.  Channel 1 is used for
   28:  * what would commonly be called an "instant message".  Channel 2
   29:  * is used for negotiating "rendezvous".  These transactions end in
   30:  * something more complex happening, such as a chat invitation, or
   31:  * a file transfer.  Channel 3 is used for chat messages (not in
   32:  * the same family as these channels).  Channel 4 is used for
   33:  * various ICQ messages.  Examples are normal messages, URLs, and
   34:  * old-style authorization.
   35:  *
   36:  * In addition to the channel, every ICBM contains a cookie.  For
   37:  * standard IMs, these are only used for error messages.  However,
   38:  * the more complex rendezvous messages make suitably more complex
   39:  * use of this field.
   40:  *
   41:  * TODO: Split this up into an im.c file an an icbm.c file.  It
   42:  *       will be beautiful, you'll see.
   43:  *
   44:  *       Make sure flap_connection_findbygroup is used by all functions.
   45:  */
   46: 
   47: #include "encoding.h"
   48: #include "oscar.h"
   49: #include "peer.h"
   50: 
   51: #ifdef _WIN32
   52: #include "win32dep.h"
   53: #endif
   54: 
   55: #include "util.h"
   56: 
   57: static const char * const errcodereason[] = {
   58: 	N_("Invalid error"),
   59: 	N_("Not logged in"),
   60: 	N_("Cannot receive IM due to parental controls"),
   61: 	N_("Cannot send SMS without accepting terms"),
   62: 	N_("Cannot send SMS"), /* SMS_WITHOUT_DISCLAIMER is weird */
   63: 	N_("Cannot send SMS to this country"),
   64: 	N_("Unknown error"), /* Undocumented */
   65: 	N_("Unknown error"), /* Undocumented */
   66: 	N_("Cannot send SMS to unknown country"),
   67: 	N_("Bot accounts cannot initiate IMs"),
   68: 	N_("Bot account cannot IM this user"),
   69: 	N_("Bot account reached IM limit"),
   70: 	N_("Bot account reached daily IM limit"),
   71: 	N_("Bot account reached monthly IM limit"),
   72: 	N_("Unable to receive offline messages"),
   73: 	N_("Offline message store full")
   74: };
   75: static const int errcodereasonlen = G_N_ELEMENTS(errcodereason);
   76: 
   77: /**
   78:  * Add a standard ICBM header to the given bstream with the given
   79:  * information.
   80:  *
   81:  * @param bs The bstream to write the ICBM header to.
   82:  * @param c c is for cookie, and cookie is for me.
   83:  * @param channel The ICBM channel (1 through 4).
   84:  * @param bn Null-terminated scrizeen nizame.
   85:  * @return The number of bytes written.  It's really not useful.
   86:  */
   87: static int aim_im_puticbm(ByteStream *bs, const guchar *c, guint16 channel, const char *bn)
   88: {
   89: 	byte_stream_putraw(bs, c, 8);
   90: 	byte_stream_put16(bs, channel);
   91: 	byte_stream_put8(bs, strlen(bn));
   92: 	byte_stream_putstr(bs, bn);
   93: 	return 8+2+1+strlen(bn);
   94: }
   95: 
   96: /**
   97:  * Generates a random ICBM cookie in a character array of length 8
   98:  * and copies it into the variable passed as cookie
   99:  * TODO: Maybe we should stop limiting our characters to the visible range?
  100:  */
  101: void aim_icbm_makecookie(guchar *cookie)
  102: {
  103: 	int i;
  104: 
  105: 	/* Should be like "21CBF95" and null terminated */
  106: 	for (i = 0; i < 7; i++)
  107: 		cookie[i] = 0x30 + ((guchar)rand() % 10);
  108: 	cookie[7] = '\0';
  109: }
  110: 
  111: /*
  112:  * Subtype 0x0001 - Error
  113:  */
  114: static int
  115: error(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  116: {
  117: 	aim_snac_t *snac2;
  118: 	guint16 reason, errcode = 0;
  119: 	const char *bn;
  120: 	GSList *tlvlist;
  121: 	PurpleConnection *gc = od->gc;
  122: #ifdef TODOFT
  123: 	PurpleXfer *xfer;
  124: #endif
  125: 	const char *reason_str;
  126: 	char *buf;
  127: 
  128: 	snac2 = aim_remsnac(od, snac->id);
  129: 	if (!snac2) {
  130: 		purple_debug_misc("oscar", "icbm error: received response from unknown request!\n");
  131: 		return 1;
  132: 	}
  133: 
  134: 	if (snac2->family != SNAC_FAMILY_ICBM) {
  135: 		purple_debug_misc("oscar", "icbm error: received response from invalid request! %d\n", snac2->family);
  136: 		g_free(snac2->data);
  137: 		g_free(snac2);
  138: 		return 1;
  139: 	}
  140: 
  141: 	/* Data is assumed to be the destination bn */
  142: 	bn = snac2->data;
  143: 	if (!bn || bn[0] == '\0') {
  144: 		purple_debug_misc("oscar", "icbm error: received response from request without a buddy name!\n");
  145: 		g_free(snac2->data);
  146: 		g_free(snac2);
  147: 		return 1;
  148: 	}
  149: 
  150: 	reason = byte_stream_get16(bs);
  151: 
  152: 	tlvlist = aim_tlvlist_read(bs);
  153: 	if (aim_tlv_gettlv(tlvlist, 0x0008, 1))
  154: 		errcode = aim_tlv_get16(tlvlist, 0x0008, 1);
  155: 	aim_tlvlist_free(tlvlist);
  156: 
  157: 	purple_debug_error("oscar",
  158: 			   "Message error with bn %s and reason %hu and errcode %hu\n",
  159: 				bn, reason, errcode);
  160: 
  161: #ifdef TODOFT
  162: 	/* If this was a file transfer request, bn is a cookie */
  163: 	if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, bn))) {
  164: 		purple_xfer_cancel_remote(xfer);
  165: 		return 1;
  166: 	}
  167: #endif
  168: 
  169: 	/* Notify the user that the message wasn't delivered */
  170: 	reason_str = oscar_get_msgerr_reason(reason);
  171: 	if (errcode != 0 && errcode < errcodereasonlen)
  172: 		buf = g_strdup_printf(_("Unable to send message: %s (%s)"), reason_str,
  173: 		                      _(errcodereason[errcode]));
  174: 	else
  175: 		buf = g_strdup_printf(_("Unable to send message: %s"), reason_str);
  176: 
  177: 	if (!purple_conv_present_error(bn, purple_connection_get_account(gc), buf)) {
  178: 		g_free(buf);
  179: 		if (errcode != 0 && errcode < errcodereasonlen)
  180: 			buf = g_strdup_printf(_("Unable to send message to %s: %s (%s)"),
  181: 			                      bn ? bn : "(unknown)", reason_str,
  182: 			                      _(errcodereason[errcode]));
  183: 		else
  184: 			buf = g_strdup_printf(_("Unable to send message to %s: %s"),
  185: 			                      bn ? bn : "(unknown)", reason_str);
  186: 		purple_notify_error(od->gc, NULL, buf, reason_str);
  187: 	}
  188: 	g_free(buf);
  189: 
  190: 	g_free(snac2->data);
  191: 	g_free(snac2);
  192: 
  193: 	return 1;
  194: }
  195: 
  196: /**
  197:  * Subtype 0x0002 - Set ICBM parameters.
  198:  *
  199:  * I definitely recommend sending this.  If you don't, you'll be stuck
  200:  * with the rather unreasonable defaults.
  201:  *
  202:  */
  203: int aim_im_setparams(OscarData *od, struct aim_icbmparameters *params)
  204: {
  205: 	FlapConnection *conn;
  206: 	ByteStream bs;
  207: 	aim_snacid_t snacid;
  208: 
  209: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  210: 		return -EINVAL;
  211: 
  212: 	if (!params)
  213: 		return -EINVAL;
  214: 
  215: 	byte_stream_new(&bs, 16);
  216: 
  217: 	/* This is read-only (see Parameter Reply). Must be set to zero here. */
  218: 	byte_stream_put16(&bs, 0x0000);
  219: 
  220: 	/* These are all read-write */
  221: 	byte_stream_put32(&bs, params->flags);
  222: 	byte_stream_put16(&bs, params->maxmsglen);
  223: 	byte_stream_put16(&bs, params->maxsenderwarn);
  224: 	byte_stream_put16(&bs, params->maxrecverwarn);
  225: 	byte_stream_put32(&bs, params->minmsginterval);
  226: 
  227: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0002, 0x0000, NULL, 0);
  228: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0002, snacid, &bs);
  229: 
  230: 	byte_stream_destroy(&bs);
  231: 
  232: 	return 0;
  233: }
  234: 
  235: /**
  236:  * Subtype 0x0004 - Request ICBM parameter information.
  237:  *
  238:  */
  239: int aim_im_reqparams(OscarData *od)
  240: {
  241: 	FlapConnection *conn;
  242: 
  243: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  244: 		return -EINVAL;
  245: 
  246: 	aim_genericreq_n_snacid(od, conn, SNAC_FAMILY_ICBM, 0x0004);
  247: 
  248: 	return 0;
  249: }
  250: 
  251: /**
  252:  * Subtype 0x0005 - Receive parameter information.
  253:  *
  254:  */
  255: static int aim_im_paraminfo(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
  256: {
  257: 	struct aim_icbmparameters params;
  258: 
  259: 	params.maxchan = byte_stream_get16(bs);
  260: 	params.flags = byte_stream_get32(bs);
  261: 	params.maxmsglen = byte_stream_get16(bs);
  262: 	params.maxsenderwarn = byte_stream_get16(bs);
  263: 	params.maxrecverwarn = byte_stream_get16(bs);
  264: 	params.minmsginterval = byte_stream_get32(bs);
  265: 
  266: 	params.flags = AIM_IMPARAM_FLAG_CHANNEL_MSGS_ALLOWED
  267: 			| AIM_IMPARAM_FLAG_MISSED_CALLS_ENABLED
  268: 			| AIM_IMPARAM_FLAG_EVENTS_ALLOWED
  269: 			| AIM_IMPARAM_FLAG_SMS_SUPPORTED
  270: 			| AIM_IMPARAM_FLAG_OFFLINE_MSGS_ALLOWED
  271: 			| AIM_IMPARAM_FLAG_USE_HTML_FOR_ICQ;
  272: 	params.maxmsglen = 8000;
  273: 	params.minmsginterval = 0;
  274: 
  275: 	aim_im_setparams(od, &params);
  276: 
  277: 	return 0;
  278: }
  279: 
  280: /**
  281:  * Subtype 0x0006 - Send an ICBM (instant message).
  282:  *
  283:  *
  284:  * Possible flags:
  285:  *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
  286:  *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
  287:  *                        online (probably ICQ only).
  288:  *
  289:  * Implementation note:  Since this is one of the most-used functions
  290:  * in all of libfaim, it is written with performance in mind.  As such,
  291:  * it is not as clear as it could be in respect to how this message is
  292:  * supposed to be layed out. Most obviously, tlvlists should be used
  293:  * instead of writing out the bytes manually.
  294:  */
  295: int aim_im_sendch1_ext(OscarData *od, struct aim_sendimext_args *args)
  296: {
  297: 	FlapConnection *conn;
  298: 	aim_snacid_t snacid;
  299: 	ByteStream data;
  300: 	guchar cookie[8];
  301: 	int msgtlvlen;
  302: 
  303: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  304: 		return -EINVAL;
  305: 
  306: 	if (!args)
  307: 		return -EINVAL;
  308: 
  309: 	if (!args->msg || (args->msglen <= 0))
  310: 		return -EINVAL;
  311: 
  312: 	if (args->msglen > MAXMSGLEN)
  313: 		return -E2BIG;
  314: 
  315: 	/* Painfully calculate the size of the message TLV */
  316: 	msgtlvlen = 1 + 1; /* 0501 */
  317: 	msgtlvlen += 2 + args->featureslen;
  318: 	msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
  319: 	msgtlvlen += 4 /* charset */ + args->msglen;
  320: 
  321: 	byte_stream_new(&data, msgtlvlen + 128);
  322: 
  323: 	/* Generate an ICBM cookie */
  324: 	aim_icbm_makecookie(cookie);
  325: 
  326: 	/* ICBM header */
  327: 	aim_im_puticbm(&data, cookie, 0x0001, args->destbn);
  328: 
  329: 	/* Message TLV (type 0x0002) */
  330: 	byte_stream_put16(&data, 0x0002);
  331: 	byte_stream_put16(&data, msgtlvlen);
  332: 
  333: 	/* Features TLV (type 0x0501) */
  334: 	byte_stream_put16(&data, 0x0501);
  335: 	byte_stream_put16(&data, args->featureslen);
  336: 	byte_stream_putraw(&data, args->features, args->featureslen);
  337: 
  338: 	/* Insert message text in a TLV (type 0x0101) */
  339: 	byte_stream_put16(&data, 0x0101);
  340: 
  341: 	/* Message block length */
  342: 	byte_stream_put16(&data, args->msglen + 0x04);
  343: 
  344: 	/* Character set */
  345: 	byte_stream_put16(&data, args->charset);
  346: 	/* Character subset -- we always use 0 here */
  347: 	byte_stream_put16(&data, 0x0);
  348: 
  349: 	/* Message.  Not terminated */
  350: 	byte_stream_putraw(&data, (guchar *)args->msg, args->msglen);
  351: 
  352: 	/* Set the Autoresponse flag */
  353: 	if (args->flags & AIM_IMFLAGS_AWAY) {
  354: 		byte_stream_put16(&data, 0x0004);
  355: 		byte_stream_put16(&data, 0x0000);
  356: 	} else {
  357: 		/* Set the Request Acknowledge flag */
  358: 		byte_stream_put16(&data, 0x0003);
  359: 		byte_stream_put16(&data, 0x0000);
  360: 
  361: 		if (args->flags & AIM_IMFLAGS_OFFLINE) {
  362: 			/* Allow this message to be queued as an offline message */
  363: 			byte_stream_put16(&data, 0x0006);
  364: 			byte_stream_put16(&data, 0x0000);
  365: 		}
  366: 	}
  367: 
  368: 	/*
  369: 	 * Set the I HAVE A REALLY PURTY ICON flag.
  370: 	 * XXX - This should really only be sent on initial
  371: 	 * IMs and when you change your icon.
  372: 	 */
  373: 	if (args->flags & AIM_IMFLAGS_HASICON) {
  374: 		byte_stream_put16(&data, 0x0008);
  375: 		byte_stream_put16(&data, 0x000c);
  376: 		byte_stream_put32(&data, args->iconlen);
  377: 		byte_stream_put16(&data, 0x0001);
  378: 		byte_stream_put16(&data, args->iconsum);
  379: 		byte_stream_put32(&data, args->iconstamp);
  380: 	}
  381: 
  382: 	/*
  383: 	 * Set the Buddy Icon Requested flag.
  384: 	 * XXX - Every time?  Surely not...
  385: 	 */
  386: 	if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
  387: 		byte_stream_put16(&data, 0x0009);
  388: 		byte_stream_put16(&data, 0x0000);
  389: 	}
  390: 
  391: 	/* XXX - should be optional */
  392: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, args->destbn, strlen(args->destbn)+1);
  393: 
  394: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &data);
  395: 	byte_stream_destroy(&data);
  396: 
  397: 	/* clean out SNACs over 60sec old */
  398: 	aim_cleansnacs(od, 60);
  399: 
  400: 	return 0;
  401: }
  402: 
  403: /*
  404:  * Subtype 0x0006 - Send a chat invitation.
  405:  */
  406: int aim_im_sendch2_chatinvite(OscarData *od, const char *bn, const char *msg, guint16 exchange, const char *roomname, guint16 instance)
  407: {
  408: 	FlapConnection *conn;
  409: 	ByteStream bs;
  410: 	aim_snacid_t snacid;
  411: 	IcbmCookie *msgcookie;
  412: 	struct aim_invite_priv *priv;
  413: 	guchar cookie[8];
  414: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  415: 	ByteStream hdrbs;
  416: 
  417: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  418: 		return -EINVAL;
  419: 
  420: 	if (!bn || !msg || !roomname)
  421: 		return -EINVAL;
  422: 
  423: 	aim_icbm_makecookie(cookie);
  424: 
  425: 	byte_stream_new(&bs, 1142+strlen(bn)+strlen(roomname)+strlen(msg));
  426: 
  427: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, bn, strlen(bn)+1);
  428: 
  429: 	/* XXX should be uncached by an unwritten 'invite accept' handler */
  430: 	priv = g_malloc(sizeof(struct aim_invite_priv));
  431: 	priv->bn = g_strdup(bn);
  432: 	priv->roomname = g_strdup(roomname);
  433: 	priv->exchange = exchange;
  434: 	priv->instance = instance;
  435: 
  436: 	if ((msgcookie = aim_mkcookie(cookie, AIM_COOKIETYPE_INVITE, priv)))
  437: 		aim_cachecookie(od, msgcookie);
  438: 	else
  439: 		g_free(priv);
  440: 
  441: 	/* ICBM Header */
  442: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  443: 
  444: 	/*
  445: 	 * TLV t(0005)
  446: 	 *
  447: 	 * Everything else is inside this TLV.
  448: 	 *
  449: 	 * Sigh.  AOL was rather inconsistent right here.  So we have
  450: 	 * to play some minor tricks.  Right inside the type 5 is some
  451: 	 * raw data, followed by a series of TLVs.
  452: 	 *
  453: 	 */
  454: 	byte_stream_new(&hdrbs, 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2);
  455: 
  456: 	byte_stream_put16(&hdrbs, 0x0000); /* Unknown! */
  457: 	byte_stream_putraw(&hdrbs, cookie, sizeof(cookie)); /* I think... */
  458: 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_CHAT);
  459: 
  460: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
  461: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
  462: 	aim_tlvlist_add_str(&inner_tlvlist, 0x000c, msg);
  463: 	aim_tlvlist_add_chatroom(&inner_tlvlist, 0x2711, exchange, roomname, instance);
  464: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  465: 
  466: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  467: 	byte_stream_destroy(&hdrbs);
  468: 
  469: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  470: 
  471: 	aim_tlvlist_free(inner_tlvlist);
  472: 	aim_tlvlist_free(outer_tlvlist);
  473: 
  474: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  475: 
  476: 	byte_stream_destroy(&bs);
  477: 
  478: 	return 0;
  479: }
  480: 
  481: /**
  482:  * Subtype 0x0006 - Send your icon to a given user.
  483:  *
  484:  * This is also performance sensitive. (If you can believe it...)
  485:  *
  486:  */
  487: int aim_im_sendch2_icon(OscarData *od, const char *bn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum)
  488: {
  489: 	FlapConnection *conn;
  490: 	ByteStream bs;
  491: 	aim_snacid_t snacid;
  492: 	guchar cookie[8];
  493: 
  494: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
  495: 		return -EINVAL;
  496: 
  497: 	if (!bn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
  498: 		return -EINVAL;
  499: 
  500: 	aim_icbm_makecookie(cookie);
  501: 
  502: 	byte_stream_new(&bs, 8+2+1+strlen(bn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2);
  503: 
  504: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  505: 
  506: 	/* ICBM header */
  507: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  508: 
  509: 	/*
  510: 	 * TLV t(0005)
  511: 	 *
  512: 	 * Encompasses everything below.
  513: 	 */
  514: 	byte_stream_put16(&bs, 0x0005);
  515: 	byte_stream_put16(&bs, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
  516: 
  517: 	byte_stream_put16(&bs, 0x0000);
  518: 	byte_stream_putraw(&bs, cookie, 8);
  519: 	byte_stream_putcaps(&bs, OSCAR_CAPABILITY_BUDDYICON);
  520: 
  521: 	/* TLV t(000a) */
  522: 	byte_stream_put16(&bs, 0x000a);
  523: 	byte_stream_put16(&bs, 0x0002);
  524: 	byte_stream_put16(&bs, 0x0001);
  525: 
  526: 	/* TLV t(000f) */
  527: 	byte_stream_put16(&bs, 0x000f);
  528: 	byte_stream_put16(&bs, 0x0000);
  529: 
  530: 	/* TLV t(2711) */
  531: 	byte_stream_put16(&bs, 0x2711);
  532: 	byte_stream_put16(&bs, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
  533: 	byte_stream_put16(&bs, 0x0000);
  534: 	byte_stream_put16(&bs, iconsum);
  535: 	byte_stream_put32(&bs, iconlen);
  536: 	byte_stream_put32(&bs, stamp);
  537: 	byte_stream_putraw(&bs, icon, iconlen);
  538: 	byte_stream_putstr(&bs, AIM_ICONIDENT);
  539: 
  540: 	/* TLV t(0003) */
  541: 	byte_stream_put16(&bs, 0x0003);
  542: 	byte_stream_put16(&bs, 0x0000);
  543: 
  544: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  545: 
  546: 	byte_stream_destroy(&bs);
  547: 
  548: 	return 0;
  549: }
  550: 
  551: /**
  552:  * Cancel a rendezvous invitation.  It could be an invitation to
  553:  * establish a direct connection, or a file-send, or a chat invite.
  554:  */
  555: void
  556: aim_im_sendch2_cancel(PeerConnection *peer_conn)
  557: {
  558: 	OscarData *od;
  559: 	FlapConnection *conn;
  560: 	ByteStream bs;
  561: 	aim_snacid_t snacid;
  562: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  563: 	ByteStream hdrbs;
  564: 
  565: 	od = peer_conn->od;
  566: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  567: 	if (conn == NULL)
  568: 		return;
  569: 
  570: 	byte_stream_new(&bs, 118+strlen(peer_conn->bn));
  571: 
  572: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  573: 
  574: 	/* ICBM header */
  575: 	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
  576: 
  577: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
  578: 
  579: 	byte_stream_new(&hdrbs, 64);
  580: 
  581: 	byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_CANCEL);
  582: 	byte_stream_putraw(&hdrbs, peer_conn->cookie, 8);
  583: 	byte_stream_putcaps(&hdrbs, peer_conn->type);
  584: 
  585: 	/* This TLV means "cancel!" */
  586: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000b, 0x0001);
  587: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  588: 
  589: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  590: 	byte_stream_destroy(&hdrbs);
  591: 
  592: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  593: 
  594: 	aim_tlvlist_free(inner_tlvlist);
  595: 	aim_tlvlist_free(outer_tlvlist);
  596: 
  597: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  598: 
  599: 	byte_stream_destroy(&bs);
  600: }
  601: 
  602: /**
  603:  * Subtype 0x0006 - Send an "I accept and I've connected to
  604:  * you" message.
  605:  */
  606: void
  607: aim_im_sendch2_connected(PeerConnection *peer_conn)
  608: {
  609: 	OscarData *od;
  610: 	FlapConnection *conn;
  611: 	ByteStream bs;
  612: 	aim_snacid_t snacid;
  613: 
  614: 	od = peer_conn->od;
  615: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  616: 	if (conn == NULL)
  617: 		return;
  618: 
  619: 	byte_stream_new(&bs, 11+strlen(peer_conn->bn) + 4+2+8+16);
  620: 
  621: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  622: 
  623: 	/* ICBM header */
  624: 	aim_im_puticbm(&bs, peer_conn->cookie, 0x0002, peer_conn->bn);
  625: 
  626: 	byte_stream_put16(&bs, 0x0005);
  627: 	byte_stream_put16(&bs, 0x001a);
  628: 	byte_stream_put16(&bs, AIM_RENDEZVOUS_CONNECTED);
  629: 	byte_stream_putraw(&bs, peer_conn->cookie, 8);
  630: 	byte_stream_putcaps(&bs, peer_conn->type);
  631: 
  632: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  633: 
  634: 	byte_stream_destroy(&bs);
  635: }
  636: 
  637: /**
  638:  * Subtype 0x0006 - Send a direct connect rendezvous ICBM.  This
  639:  * could have a number of meanings, depending on the content:
  640:  * "I want you to connect to me"
  641:  * "I want to connect to you"
  642:  * "I want to connect through a proxy server"
  643:  */
  644: void
  645: aim_im_sendch2_odc_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber)
  646: {
  647: 	FlapConnection *conn;
  648: 	ByteStream bs;
  649: 	aim_snacid_t snacid;
  650: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  651: 	ByteStream hdrbs;
  652: 
  653: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  654: 	if (conn == NULL)
  655: 		return;
  656: 
  657: 	byte_stream_new(&bs, 246+strlen(bn));
  658: 
  659: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  660: 
  661: 	/* ICBM header */
  662: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  663: 
  664: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
  665: 
  666: 	byte_stream_new(&hdrbs, 128);
  667: 
  668: 	byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
  669: 	byte_stream_putraw(&hdrbs, cookie, 8);
  670: 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
  671: 
  672: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
  673: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
  674: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
  675: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
  676: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
  677: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  678: 
  679: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  680: 	byte_stream_destroy(&hdrbs);
  681: 
  682: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  683: 
  684: 	aim_tlvlist_free(inner_tlvlist);
  685: 	aim_tlvlist_free(outer_tlvlist);
  686: 
  687: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  688: 
  689: 	byte_stream_destroy(&bs);
  690: }
  691: 
  692: /**
  693:  * Subtype 0x0006 - Send a direct connect rendezvous ICBM asking the
  694:  * remote user to connect to us via a proxy server.
  695:  */
  696: void
  697: aim_im_sendch2_odc_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber)
  698: {
  699: 	FlapConnection *conn;
  700: 	ByteStream bs;
  701: 	aim_snacid_t snacid;
  702: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  703: 	ByteStream hdrbs;
  704: 	guint8 ip_comp[4];
  705: 
  706: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  707: 	if (conn == NULL)
  708: 		return;
  709: 
  710: 	byte_stream_new(&bs, 246+strlen(bn));
  711: 
  712: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  713: 
  714: 	/* ICBM header */
  715: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  716: 
  717: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
  718: 
  719: 	byte_stream_new(&hdrbs, 128);
  720: 
  721: 	byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
  722: 	byte_stream_putraw(&hdrbs, cookie, 8);
  723: 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_DIRECTIM);
  724: 
  725: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
  726: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
  727: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
  728: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
  729: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
  730: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
  731: 
  732: 	/* Send the bitwise complement of the port and ip.  As a check? */
  733: 	ip_comp[0] = ~ip[0];
  734: 	ip_comp[1] = ~ip[1];
  735: 	ip_comp[2] = ~ip[2];
  736: 	ip_comp[3] = ~ip[3];
  737: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
  738: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
  739: 
  740: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  741: 
  742: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  743: 	byte_stream_destroy(&hdrbs);
  744: 
  745: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  746: 
  747: 	aim_tlvlist_free(inner_tlvlist);
  748: 	aim_tlvlist_free(outer_tlvlist);
  749: 
  750: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  751: 
  752: 	byte_stream_destroy(&bs);
  753: }
  754: 
  755: /**
  756:  * Subtype 0x0006 - Send an "I want to send you this file" message
  757:  *
  758:  */
  759: void
  760: aim_im_sendch2_sendfile_requestdirect(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 port, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
  761: {
  762: 	FlapConnection *conn;
  763: 	ByteStream bs;
  764: 	aim_snacid_t snacid;
  765: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  766: 	ByteStream hdrbs;
  767: 
  768: 	g_return_if_fail(bn != NULL);
  769: 	g_return_if_fail(ip != NULL);
  770: 
  771: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  772: 	if (conn == NULL)
  773: 		return;
  774: 
  775: 	byte_stream_new(&bs, 1014);
  776: 
  777: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  778: 
  779: 	/* ICBM header */
  780: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  781: 
  782: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
  783: 
  784: 	byte_stream_new(&hdrbs, 512);
  785: 
  786: 	byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
  787: 	byte_stream_putraw(&hdrbs, cookie, 8);
  788: 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
  789: 
  790: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
  791: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
  792: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, port);
  793: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
  794: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
  795: 	/* TODO: Send 0x0016 and 0x0017 */
  796: 
  797: 	if (filename != NULL)
  798: 	{
  799: 		ByteStream inner_bs;
  800: 
  801: 		/* Begin TLV t(2711) */
  802: 		byte_stream_new(&inner_bs, 2+2+4+strlen(filename)+1);
  803: 		byte_stream_put16(&inner_bs, (numfiles > 1) ? 0x0002 : 0x0001);
  804: 		byte_stream_put16(&inner_bs, numfiles);
  805: 		byte_stream_put32(&inner_bs, size);
  806: 
  807: 		/* Filename - NULL terminated, for some odd reason */
  808: 		byte_stream_putstr(&inner_bs, filename);
  809: 		byte_stream_put8(&inner_bs, 0x00);
  810: 
  811: 		aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, inner_bs.len, inner_bs.data);
  812: 		byte_stream_destroy(&inner_bs);
  813: 		/* End TLV t(2711) */
  814: 	}
  815: 
  816: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  817: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  818: 	byte_stream_destroy(&hdrbs);
  819: 
  820: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  821: 
  822: 	aim_tlvlist_free(inner_tlvlist);
  823: 	aim_tlvlist_free(outer_tlvlist);
  824: 
  825: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  826: 
  827: 	byte_stream_destroy(&bs);
  828: }
  829: 
  830: /**
  831:  * Subtype 0x0006 - Send a sendfile connect rendezvous ICBM asking the
  832:  * remote user to connect to us via a proxy server.
  833:  */
  834: void
  835: aim_im_sendch2_sendfile_requestproxy(OscarData *od, guchar *cookie, const char *bn, const guint8 *ip, guint16 pin, guint16 requestnumber, const gchar *filename, guint32 size, guint16 numfiles)
  836: {
  837: 	FlapConnection *conn;
  838: 	ByteStream bs;
  839: 	aim_snacid_t snacid;
  840: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
  841: 	ByteStream hdrbs;
  842: 	guint8 ip_comp[4];
  843: 
  844: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  845: 	if (conn == NULL)
  846: 		return;
  847: 
  848: 	byte_stream_new(&bs, 1014);
  849: 
  850: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0006, 0x0000, NULL, 0);
  851: 
  852: 	/* ICBM header */
  853: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
  854: 
  855: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003);
  856: 
  857: 	byte_stream_new(&hdrbs, 512);
  858: 
  859: 	byte_stream_put16(&hdrbs, AIM_RENDEZVOUS_PROPOSE);
  860: 	byte_stream_putraw(&hdrbs, cookie, 8);
  861: 	byte_stream_putcaps(&hdrbs, OSCAR_CAPABILITY_SENDFILE);
  862: 
  863: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0002, 4, ip);
  864: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0003, 4, ip);
  865: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0005, pin);
  866: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, requestnumber);
  867: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
  868: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x0010);
  869: 
  870: 	/* Send the bitwise complement of the port and ip.  As a check? */
  871: 	ip_comp[0] = ~ip[0];
  872: 	ip_comp[1] = ~ip[1];
  873: 	ip_comp[2] = ~ip[2];
  874: 	ip_comp[3] = ~ip[3];
  875: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x0016, 4, ip_comp);
  876: 	aim_tlvlist_add_16(&inner_tlvlist, 0x0017, ~pin);
  877: 
  878: 	if (filename != NULL)
  879: 	{
  880: 		ByteStream filename_bs;
  881: 
  882: 		/* Begin TLV t(2711) */
  883: 		byte_stream_new(&filename_bs, 2+2+4+strlen(filename)+1);
  884: 		byte_stream_put16(&filename_bs, (numfiles > 1) ? 0x0002 : 0x0001);
  885: 		byte_stream_put16(&filename_bs, numfiles);
  886: 		byte_stream_put32(&filename_bs, size);
  887: 
  888: 		/* Filename - NULL terminated, for some odd reason */
  889: 		byte_stream_putstr(&filename_bs, filename);
  890: 		byte_stream_put8(&filename_bs, 0x00);
  891: 
  892: 		aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, filename_bs.len, filename_bs.data);
  893: 		byte_stream_destroy(&filename_bs);
  894: 		/* End TLV t(2711) */
  895: 	}
  896: 
  897: 	aim_tlvlist_write(&hdrbs, &inner_tlvlist);
  898: 
  899: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&hdrbs), hdrbs.data);
  900: 	byte_stream_destroy(&hdrbs);
  901: 
  902: 	aim_tlvlist_write(&bs, &outer_tlvlist);
  903: 
  904: 	aim_tlvlist_free(inner_tlvlist);
  905: 	aim_tlvlist_free(outer_tlvlist);
  906: 
  907: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0006, snacid, &bs);
  908: 
  909: 	byte_stream_destroy(&bs);
  910: }
  911: 
  912: static void
  913: incomingim_ch1_parsemsg(OscarData *od, aim_userinfo_t *userinfo, ByteStream *message, struct aim_incomingim_ch1_args *args)
  914: {
  915: 	PurpleAccount *account = purple_connection_get_account(od->gc);
  916: 	/*
  917: 	 * We're interested in the inner TLV 0x101, which contains precious, precious message.
  918: 	 */
  919: 	while (byte_stream_bytes_left(message) >= 4) {
  920: 		guint16 type = byte_stream_get16(message);
  921: 		guint16 length = byte_stream_get16(message);
  922: 		if (type == 0x101) {
  923: 			gchar *msg;
  924: 			guint16 msglen = length - 4; /* charset + charsubset */
  925: 			guint16 charset = byte_stream_get16(message);
  926: 			byte_stream_advance(message, 2); /* charsubset */
  927: 
  928: 			msg = byte_stream_getstr(message, msglen);
  929: 			args->msg = oscar_decode_im(account, userinfo->bn, charset, msg, msglen);
  930: 			g_free(msg);
  931: 		} else {
  932: 			byte_stream_advance(message, length);
  933: 		}
  934: 	}
  935: }
  936: 
  937: static int
  938: incomingim_ch1(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, ByteStream *bs, guint8 *cookie)
  939: {
  940: 	guint16 type, length;
  941: 	aim_rxcallback_t userfunc;
  942: 	int ret = 0;
  943: 	struct aim_incomingim_ch1_args args;
  944: 	unsigned int endpos;
  945: 
  946: 	memset(&args, 0, sizeof(args));
  947: 
  948: 	/*
  949: 	 * This used to be done using tlvchains.  For performance reasons,
  950: 	 * I've changed it to process the TLVs in-place.  This avoids lots
  951: 	 * of per-IM memory allocations.
  952: 	 */
  953: 	while (byte_stream_bytes_left(bs) >= 4)
  954: 	{
  955: 		type = byte_stream_get16(bs);
  956: 		length = byte_stream_get16(bs);
  957: 
  958: 		if (length > byte_stream_bytes_left(bs))
  959: 		{
  960: 			purple_debug_misc("oscar", "Received an IM containing an invalid message part from %s.  They are probably trying to do something malicious.\n", userinfo->bn);
  961: 			break;
  962: 		}
  963: 
  964: 		endpos = byte_stream_curpos(bs) + length;
  965: 
  966: 		if (type == 0x0002) { /* Message Block */
  967: 			ByteStream tlv02;
  968: 			byte_stream_init(&tlv02, bs->data + bs->offset, length);
  969: 			incomingim_ch1_parsemsg(od, userinfo, &tlv02, &args);
  970: 		} else if (type == 0x0003) { /* Server Ack Requested */
  971: 			args.icbmflags |= AIM_IMFLAGS_ACK;
  972: 		} else if (type == 0x0004) { /* Message is Auto Response */
  973: 			args.icbmflags |= AIM_IMFLAGS_AWAY;
  974: 		} else if (type == 0x0006) { /* Message was received offline. */
  975: 			/*
  976: 			 * This flag is set on incoming offline messages for both
  977: 			 * AIM and ICQ accounts.
  978: 			 */
  979: 			args.icbmflags |= AIM_IMFLAGS_OFFLINE;
  980: 		} else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
  981: 			args.iconlen = byte_stream_get32(bs);
  982: 			byte_stream_get16(bs); /* 0x0001 */
  983: 			args.iconsum = byte_stream_get16(bs);
  984: 			args.iconstamp = byte_stream_get32(bs);
  985: 
  986: 			/*
  987: 			 * This looks to be a client bug.  MacAIM 4.3 will
  988: 			 * send this tag, but with all zero values, in the
  989: 			 * first message of a conversation. This makes no
  990: 			 * sense whatsoever, so I'm going to say its a bug.
  991: 			 *
  992: 			 * You really shouldn't advertise a zero-length icon
  993: 			 * anyway.
  994: 			 *
  995: 			 */
  996: 			if (args.iconlen)
  997: 				args.icbmflags |= AIM_IMFLAGS_HASICON;
  998: 		} else if (type == 0x0009) {
  999: 			args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
 1000: 		} else if (type == 0x000b) { /* Non-direct connect typing notification */
 1001: 			args.icbmflags |= AIM_IMFLAGS_TYPINGNOT;
 1002: 		} else if (type == 0x0016) {
 1003: 			/*
 1004: 			 * UTC timestamp for when the message was sent.  Only
 1005: 			 * provided for offline messages.
 1006: 			 */
 1007: 			args.timestamp = byte_stream_get32(bs);
 1008: 		}
 1009: 
 1010: 		/*
 1011: 		 * This is here to protect ourselves from ourselves.  That
 1012: 		 * is, if something above doesn't completely parse its value
 1013: 		 * section, or, worse, overparses it, this will set the
 1014: 		 * stream where it needs to be in order to land on the next
 1015: 		 * TLV when the loop continues.
 1016: 		 *
 1017: 		 */
 1018: 		byte_stream_setpos(bs, endpos);
 1019: 	}
 1020: 
 1021: 
 1022: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1023: 		ret = userfunc(od, conn, frame, channel, userinfo, &args);
 1024: 
 1025: 	g_free(args.msg);
 1026: 	return ret;
 1027: }
 1028: 
 1029: static void
 1030: incomingim_ch2_buddylist(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
 1031: {
 1032: 	/*
 1033: 	 * This goes like this...
 1034: 	 *
 1035: 	 *   group name length
 1036: 	 *   group name
 1037: 	 *     num of buddies in group
 1038: 	 *     buddy name length
 1039: 	 *     buddy name
 1040: 	 *     buddy name length
 1041: 	 *     buddy name
 1042: 	 *     ...
 1043: 	 *   group name length
 1044: 	 *   group name
 1045: 	 *     num of buddies in group
 1046: 	 *     buddy name length
 1047: 	 *     buddy name
 1048: 	 *     ...
 1049: 	 *   ...
 1050: 	 */
 1051: 	while (byte_stream_bytes_left(servdata))
 1052: 	{
 1053: 		guint16 gnlen, numb;
 1054: 		int i;
 1055: 		char *gn;
 1056: 
 1057: 		gnlen = byte_stream_get16(servdata);
 1058: 		gn = byte_stream_getstr(servdata, gnlen);
 1059: 		numb = byte_stream_get16(servdata);
 1060: 
 1061: 		for (i = 0; i < numb; i++) {
 1062: 			guint16 bnlen;
 1063: 			char *bn;
 1064: 
 1065: 			bnlen = byte_stream_get16(servdata);
 1066: 			bn = byte_stream_getstr(servdata, bnlen);
 1067: 
 1068: 			purple_debug_misc("oscar", "got a buddy list from %s: group %s, buddy %s\n", userinfo->bn, gn, bn);
 1069: 
 1070: 			g_free(bn);
 1071: 		}
 1072: 
 1073: 		g_free(gn);
 1074: 	}
 1075: 
 1076: 	return;
 1077: }
 1078: 
 1079: static void
 1080: incomingim_ch2_buddyicon_free(OscarData *od, IcbmArgsCh2 *args)
 1081: {
 1082: 	g_free(args->info.icon.icon);
 1083: 
 1084: 	return;
 1085: }
 1086: 
 1087: static void
 1088: incomingim_ch2_buddyicon(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
 1089: {
 1090: 	args->info.icon.checksum = byte_stream_get32(servdata);
 1091: 	args->info.icon.length = byte_stream_get32(servdata);
 1092: 	args->info.icon.timestamp = byte_stream_get32(servdata);
 1093: 	args->info.icon.icon = byte_stream_getraw(servdata, args->info.icon.length);
 1094: 
 1095: 	args->destructor = (void *)incomingim_ch2_buddyicon_free;
 1096: 
 1097: 	return;
 1098: }
 1099: 
 1100: static void
 1101: incomingim_ch2_chat_free(OscarData *od, IcbmArgsCh2 *args)
 1102: {
 1103: 	/* XXX - aim_chat_roominfo_free() */
 1104: 	g_free(args->info.chat.roominfo.name);
 1105: 
 1106: 	return;
 1107: }
 1108: 
 1109: static void
 1110: incomingim_ch2_chat(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
 1111: {
 1112: 	/*
 1113: 	 * Chat room info.
 1114: 	 */
 1115: 	aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
 1116: 
 1117: 	args->destructor = (void *)incomingim_ch2_chat_free;
 1118: }
 1119: 
 1120: static void
 1121: incomingim_ch2_icqserverrelay_free(OscarData *od, IcbmArgsCh2 *args)
 1122: {
 1123: 	g_free((char *)args->info.rtfmsg.msg);
 1124: }
 1125: 
 1126: /*
 1127:  * The relationship between OSCAR_CAPABILITY_ICQSERVERRELAY and OSCAR_CAPABILITY_ICQRTF is
 1128:  * kind of odd. This sends the client ICQRTF since that is all that I've seen
 1129:  * SERVERRELAY used for.
 1130:  *
 1131:  * Note that this is all little-endian.  Cringe.
 1132:  *
 1133:  */
 1134: static void
 1135: incomingim_ch2_icqserverrelay(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
 1136: {
 1137: 	guint16 hdrlen, msglen;
 1138: 
 1139: 	args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
 1140: 
 1141: #define SKIP_HEADER(expected_hdrlen) \
 1142: 	hdrlen = byte_stream_getle16(servdata); \
 1143: 	if (hdrlen != expected_hdrlen) { \
 1144: 		purple_debug_warning("oscar", "Expected to find a header with length " #expected_hdrlen "; ignoring message"); \
 1145: 		return; \
 1146: 	} \
 1147: 	byte_stream_advance(servdata, hdrlen);
 1148: 
 1149: 	SKIP_HEADER(0x001b);
 1150: 	SKIP_HEADER(0x000e);
 1151: 
 1152: 	args->info.rtfmsg.msgtype = byte_stream_get8(servdata);
 1153: 	/*
 1154: 	 * Copied from http://iserverd.khstu.ru/oscar/message.html:
 1155: 	 * xx      byte       message flags
 1156: 	 * xx xx   word (LE)  status code
 1157: 	 * xx xx   word (LE)  priority code
 1158: 	 *
 1159: 	 * We don't need any of these, so just skip them.
 1160: 	 */
 1161: 	byte_stream_advance(servdata, 1 + 2 + 2);
 1162: 
 1163: 	msglen = byte_stream_getle16(servdata);
 1164: 	args->info.rtfmsg.msg = byte_stream_getstr(servdata, msglen);
 1165: }
 1166: 
 1167: static void
 1168: incomingim_ch2_sendfile_free(OscarData *od, IcbmArgsCh2 *args)
 1169: {
 1170: 	g_free(args->info.sendfile.filename);
 1171: }
 1172: 
 1173: /* Someone is sending us a file */
 1174: static void
 1175: incomingim_ch2_sendfile(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, aim_userinfo_t *userinfo, IcbmArgsCh2 *args, ByteStream *servdata)
 1176: {
 1177: 	int flen;
 1178: 
 1179: 	args->destructor = (void *)incomingim_ch2_sendfile_free;
 1180: 
 1181: 	/* Maybe there is a better way to tell what kind of sendfile
 1182: 	 * this is?  Maybe TLV t(000a)? */
 1183: 
 1184: 	/* subtype is one of AIM_OFT_SUBTYPE_* */
 1185: 	args->info.sendfile.subtype = byte_stream_get16(servdata);
 1186: 	args->info.sendfile.totfiles = byte_stream_get16(servdata);
 1187: 	args->info.sendfile.totsize = byte_stream_get32(servdata);
 1188: 
 1189: 	/*
 1190: 	 * I hope to God I'm right when I guess that there is a
 1191: 	 * 32 char max filename length for single files.  I think
 1192: 	 * OFT tends to do that.  Gotta love inconsistency.  I saw
 1193: 	 * a 26 byte filename?
 1194: 	 */
 1195: 	/* AAA - create an byte_stream_getnullstr function (don't anymore)(maybe) */
 1196: 	/* Use an inelegant way of getting the null-terminated filename,
 1197: 	 * since there's no easy bstream routine. */
 1198: 	for (flen = 0; byte_stream_get8(servdata); flen++);
 1199: 	byte_stream_advance(servdata, -flen -1);
 1200: 	args->info.sendfile.filename = byte_stream_getstr(servdata, flen);
 1201: 
 1202: 	/* There is sometimes more after the null-terminated filename,
 1203: 	 * but I'm unsure of its format. */
 1204: 	/* I don't believe him. */
 1205: 	/* There is sometimes a null byte inside a unicode filename,
 1206: 	 * but as far as I can tell the filename is the last
 1207: 	 * piece of data that will be in this message. --Jonathan */
 1208: }
 1209: 
 1210: typedef void (*ch2_args_destructor_t)(OscarData *od, IcbmArgsCh2 *args);
 1211: 
 1212: static int incomingim_ch2(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
 1213: {
 1214: 	aim_rxcallback_t userfunc;
 1215: 	aim_tlv_t *block1, *servdatatlv;
 1216: 	GSList *list2;
 1217: 	aim_tlv_t *tlv;
 1218: 	IcbmArgsCh2 args;
 1219: 	ByteStream bbs, sdbs, *sdbsptr = NULL;
 1220: 	guint8 *cookie2;
 1221: 	int ret = 0;
 1222: 
 1223: 	char proxyip[30] = {""};
 1224: 	char clientip[30] = {""};
 1225: 	char verifiedip[30] = {""};
 1226: 
 1227: 	memset(&args, 0, sizeof(args));
 1228: 
 1229: 	/*
 1230: 	 * There's another block of TLVs embedded in the type 5 here.
 1231: 	 */
 1232: 	block1 = aim_tlv_gettlv(tlvlist, 0x0005, 1);
 1233: 	if (block1 == NULL)
 1234: 	{
 1235: 		/* The server sent us ch2 ICBM without ch2 info?  Weird. */
 1236: 		return 1;
 1237: 	}
 1238: 	byte_stream_init(&bbs, block1->value, block1->length);
 1239: 
 1240: 	/*
 1241: 	 * First two bytes represent the status of the connection.
 1242: 	 * One of the AIM_RENDEZVOUS_ defines.
 1243: 	 *
 1244: 	 * 0 is a request, 1 is a cancel, 2 is an accept
 1245: 	 */
 1246: 	args.status = byte_stream_get16(&bbs);
 1247: 
 1248: 	/*
 1249: 	 * Next comes the cookie.  Should match the ICBM cookie.
 1250: 	 */
 1251: 	cookie2 = byte_stream_getraw(&bbs, 8);
 1252: 	if (memcmp(cookie, cookie2, 8) != 0)
 1253: 	{
 1254: 		purple_debug_warning("oscar",
 1255: 				"Cookies don't match in rendezvous ICBM, bailing out.\n");
 1256: 		g_free(cookie2);
 1257: 		return 1;
 1258: 	}
 1259: 	memcpy(args.cookie, cookie2, 8);
 1260: 	g_free(cookie2);
 1261: 
 1262: 	/*
 1263: 	 * The next 16bytes are a capability block so we can
 1264: 	 * identify what type of rendezvous this is.
 1265: 	 */
 1266: 	args.type = aim_locate_getcaps(od, &bbs, 0x10);
 1267: 
 1268: 	/*
 1269: 	 * What follows may be TLVs or nothing, depending on the
 1270: 	 * purpose of the message.
 1271: 	 *
 1272: 	 * Ack packets for instance have nothing more to them.
 1273: 	 */
 1274: 	list2 = aim_tlvlist_read(&bbs);
 1275: 
 1276: 	/*
 1277: 	 * IP address to proxy the file transfer through.
 1278: 	 *
 1279: 	 * TODO: I don't like this.  Maybe just read in an int?  Or inet_ntoa...
 1280: 	 */
 1281: 	tlv = aim_tlv_gettlv(list2, 0x0002, 1);
 1282: 	if ((tlv != NULL) && (tlv->length == 4))
 1283: 		snprintf(proxyip, sizeof(proxyip), "%hhu.%hhu.%hhu.%hhu",
 1284: 				tlv->value[0], tlv->value[1],
 1285: 				tlv->value[2], tlv->value[3]);
 1286: 
 1287: 	/*
 1288: 	 * IP address from the perspective of the client.
 1289: 	 */
 1290: 	tlv = aim_tlv_gettlv(list2, 0x0003, 1);
 1291: 	if ((tlv != NULL) && (tlv->length == 4))
 1292: 		snprintf(clientip, sizeof(clientip), "%hhu.%hhu.%hhu.%hhu",
 1293: 				tlv->value[0], tlv->value[1],
 1294: 				tlv->value[2], tlv->value[3]);
 1295: 
 1296: 	/*
 1297: 	 * Verified IP address (from the perspective of Oscar).
 1298: 	 *
 1299: 	 * This is added by the server.
 1300: 	 */
 1301: 	tlv = aim_tlv_gettlv(list2, 0x0004, 1);
 1302: 	if ((tlv != NULL) && (tlv->length == 4))
 1303: 		snprintf(verifiedip, sizeof(verifiedip), "%hhu.%hhu.%hhu.%hhu",
 1304: 				tlv->value[0], tlv->value[1],
 1305: 				tlv->value[2], tlv->value[3]);
 1306: 
 1307: 	/*
 1308: 	 * Port number for something.
 1309: 	 */
 1310: 	if (aim_tlv_gettlv(list2, 0x0005, 1))
 1311: 		args.port = aim_tlv_get16(list2, 0x0005, 1);
 1312: 
 1313: 	/*
 1314: 	 * File transfer "request number":
 1315: 	 * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
 1316: 	 * 0x0002 - "Reply request" for a stage 2 proxy (receiver wants to use proxy)
 1317: 	 * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
 1318: 	 */
 1319: 	if (aim_tlv_gettlv(list2, 0x000a, 1))
 1320: 		args.requestnumber = aim_tlv_get16(list2, 0x000a, 1);
 1321: 
 1322: 	/*
 1323: 	 * Terminate connection/error code.  0x0001 means the other user
 1324: 	 * cancelled the connection.
 1325: 	 */
 1326: 	if (aim_tlv_gettlv(list2, 0x000b, 1))
 1327: 		args.errorcode = aim_tlv_get16(list2, 0x000b, 1);
 1328: 
 1329: 	/*
 1330: 	 * Invitation message / chat description.
 1331: 	 */
 1332: 	if (aim_tlv_gettlv(list2, 0x000c, 1)) {
 1333: 		args.msg = aim_tlv_getstr(list2, 0x000c, 1);
 1334: 		args.msglen = aim_tlv_getlength(list2, 0x000c, 1);
 1335: 	}
 1336: 
 1337: 	/*
 1338: 	 * Character set.
 1339: 	 */
 1340: 	if (aim_tlv_gettlv(list2, 0x000d, 1))
 1341: 		args.encoding = aim_tlv_getstr(list2, 0x000d, 1);
 1342: 
 1343: 	/*
 1344: 	 * Language.
 1345: 	 */
 1346: 	if (aim_tlv_gettlv(list2, 0x000e, 1))
 1347: 		args.language = aim_tlv_getstr(list2, 0x000e, 1);
 1348: 
 1349: 	/*
 1350: 	 * Flag meaning we should proxy the file transfer through an AIM server
 1351: 	 */
 1352: 	if (aim_tlv_gettlv(list2, 0x0010, 1))
 1353: 		args.use_proxy = TRUE;
 1354: 
 1355: 	if (strlen(proxyip))
 1356: 		args.proxyip = (char *)proxyip;
 1357: 	if (strlen(clientip))
 1358: 		args.clientip = (char *)clientip;
 1359: 	if (strlen(verifiedip))
 1360: 		args.verifiedip = (char *)verifiedip;
 1361: 
 1362: 	/*
 1363: 	 * This must be present in PROPOSALs, but will probably not
 1364: 	 * exist in CANCELs and ACCEPTs.  Also exists in ICQ Lite
 1365: 	 * Beta 4.0 URLs (OSCAR_CAPABILITY_ICQSERVERRELAY).
 1366: 	 *
 1367: 	 * Service Data blocks are module-specific in format.
 1368: 	 */
 1369: 	if ((servdatatlv = aim_tlv_gettlv(list2, 0x2711 /* 10001 */, 1))) {
 1370: 
 1371: 		byte_stream_init(&sdbs, servdatatlv->value, servdatatlv->length);
 1372: 		sdbsptr = &sdbs;
 1373: 
 1374: 		/*
 1375: 		 * The rest of the handling depends on what type it is.
 1376: 		 *
 1377: 		 * Not all of them have special handling (yet).
 1378: 		 */
 1379: 		if (args.type & OSCAR_CAPABILITY_BUDDYICON)
 1380: 			incomingim_ch2_buddyicon(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
 1381: 		else if (args.type & OSCAR_CAPABILITY_SENDBUDDYLIST)
 1382: 			incomingim_ch2_buddylist(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
 1383: 		else if (args.type & OSCAR_CAPABILITY_CHAT)
 1384: 			incomingim_ch2_chat(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
 1385: 		else if (args.type & OSCAR_CAPABILITY_ICQSERVERRELAY)
 1386: 			incomingim_ch2_icqserverrelay(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
 1387: 		else if (args.type & OSCAR_CAPABILITY_SENDFILE)
 1388: 			incomingim_ch2_sendfile(od, conn, mod, frame, snac, userinfo, &args, sdbsptr);
 1389: 	}
 1390: 
 1391: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1392: 		ret = userfunc(od, conn, frame, channel, userinfo, &args);
 1393: 
 1394: 
 1395: 	if (args.destructor)
 1396: 		((ch2_args_destructor_t)args.destructor)(od, &args);
 1397: 
 1398: 	g_free((char *)args.msg);
 1399: 	g_free((char *)args.encoding);
 1400: 	g_free((char *)args.language);
 1401: 
 1402: 	aim_tlvlist_free(list2);
 1403: 
 1404: 	return ret;
 1405: }
 1406: 
 1407: static int incomingim_ch4(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, GSList *tlvlist, guint8 *cookie)
 1408: {
 1409: 	ByteStream meat;
 1410: 	aim_rxcallback_t userfunc;
 1411: 	aim_tlv_t *block;
 1412: 	struct aim_incomingim_ch4_args args;
 1413: 	int ret = 0;
 1414: 
 1415: 	/*
 1416: 	 * Make a bstream for the meaty part.  Yum.  Meat.
 1417: 	 */
 1418: 	if (!(block = aim_tlv_gettlv(tlvlist, 0x0005, 1)))
 1419: 		return -1;
 1420: 	byte_stream_init(&meat, block->value, block->length);
 1421: 
 1422: 	args.uin = byte_stream_getle32(&meat);
 1423: 	args.type = byte_stream_getle8(&meat);
 1424: 	args.flags = byte_stream_getle8(&meat);
 1425: 	if (args.type == 0x1a)
 1426: 		/* There seems to be a problem with the length in SMS msgs from server, this fixed it */
 1427: 		args.msglen = block->length - 6;
 1428: 	else
 1429: 		args.msglen = byte_stream_getle16(&meat);
 1430: 	args.msg = (gchar *)byte_stream_getraw(&meat, args.msglen);
 1431: 
 1432: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1433: 		ret = userfunc(od, conn, frame, channel, userinfo, &args);
 1434: 
 1435: 	g_free(args.msg);
 1436: 
 1437: 	return ret;
 1438: }
 1439: 
 1440: /*
 1441:  * Subtype 0x0007
 1442:  *
 1443:  * It can easily be said that parsing ICBMs is THE single
 1444:  * most difficult thing to do in the in AIM protocol.  In
 1445:  * fact, I think I just did say that.
 1446:  *
 1447:  * Below is the best damned solution I've come up with
 1448:  * over the past sixteen months of battling with it. This
 1449:  * can parse both away and normal messages from every client
 1450:  * I have access to.  Its not fast, its not clean.  But it works.
 1451:  *
 1452:  */
 1453: static int incomingim(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1454: {
 1455: 	int ret = 0;
 1456: 	guchar *cookie;
 1457: 	guint16 channel;
 1458: 	aim_userinfo_t userinfo;
 1459: 
 1460: 	memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
 1461: 
 1462: 	/*
 1463: 	 * Read ICBM Cookie.
 1464: 	 */
 1465: 	cookie = byte_stream_getraw(bs, 8);
 1466: 
 1467: 	/*
 1468: 	 * Channel ID.
 1469: 	 *
 1470: 	 * Channel 0x0001 is the message channel.  It is
 1471: 	 * used to send basic ICBMs.
 1472: 	 *
 1473: 	 * Channel 0x0002 is the Rendezvous channel, which
 1474: 	 * is where Chat Invitiations and various client-client
 1475: 	 * connection negotiations come from.
 1476: 	 *
 1477: 	 * Channel 0x0003 is used for chat messages.
 1478: 	 *
 1479: 	 * Channel 0x0004 is used for ICQ authorization, or
 1480: 	 * possibly any system notice.
 1481: 	 *
 1482: 	 */
 1483: 	channel = byte_stream_get16(bs);
 1484: 
 1485: 	/*
 1486: 	 * Extract the standard user info block.
 1487: 	 *
 1488: 	 * Note that although this contains TLVs that appear contiguous
 1489: 	 * with the TLVs read below, they are two different pieces.  The
 1490: 	 * userinfo block contains the number of TLVs that contain user
 1491: 	 * information, the rest are not even though there is no separation.
 1492: 	 * You can start reading the message TLVs after aim_info_extract()
 1493: 	 * parses out the standard userinfo block.
 1494: 	 *
 1495: 	 * That also means that TLV types can be duplicated between the
 1496: 	 * userinfo block and the rest of the message, however there should
 1497: 	 * never be two TLVs of the same type in one block.
 1498: 	 *
 1499: 	 */
 1500: 	aim_info_extract(od, bs, &userinfo);
 1501: 
 1502: 	/*
 1503: 	 * From here on, its depends on what channel we're on.
 1504: 	 *
 1505: 	 * Technically all channels have a TLV list have this, however,
 1506: 	 * for the common channel 1 case, in-place parsing is used for
 1507: 	 * performance reasons (less memory allocation).
 1508: 	 */
 1509: 	if (channel == 1) {
 1510: 
 1511: 		ret = incomingim_ch1(od, conn, mod, frame, snac, channel, &userinfo, bs, cookie);
 1512: 
 1513: 	} else if (channel == 2) {
 1514: 		GSList *tlvlist;
 1515: 
 1516: 		/*
 1517: 		 * Read block of TLVs (not including the userinfo data).  All
 1518: 		 * further data is derived from what is parsed here.
 1519: 		 */
 1520: 		tlvlist = aim_tlvlist_read(bs);
 1521: 
 1522: 		ret = incomingim_ch2(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
 1523: 
 1524: 		aim_tlvlist_free(tlvlist);
 1525: 
 1526: 	} else if (channel == 4) {
 1527: 		GSList *tlvlist;
 1528: 
 1529: 		tlvlist = aim_tlvlist_read(bs);
 1530: 		ret = incomingim_ch4(od, conn, mod, frame, snac, channel, &userinfo, tlvlist, cookie);
 1531: 		aim_tlvlist_free(tlvlist);
 1532: 
 1533: 	} else {
 1534: 		purple_debug_misc("oscar", "icbm: ICBM received on an unsupported channel.  Ignoring.  (chan = %04x)\n", channel);
 1535: 	}
 1536: 
 1537: 	aim_info_free(&userinfo);
 1538: 	g_free(cookie);
 1539: 
 1540: 	return ret;
 1541: }
 1542: 
 1543: /* Subtype 0x000a */
 1544: static int missedcall(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1545: {
 1546: 	int ret = 0;
 1547: 	aim_rxcallback_t userfunc;
 1548: 	guint16 channel, nummissed, reason;
 1549: 	aim_userinfo_t userinfo;
 1550: 
 1551: 	while (byte_stream_bytes_left(bs)) {
 1552: 
 1553: 		channel = byte_stream_get16(bs);
 1554: 		aim_info_extract(od, bs, &userinfo);
 1555: 		nummissed = byte_stream_get16(bs);
 1556: 		reason = byte_stream_get16(bs);
 1557: 
 1558: 		if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1559: 			 ret = userfunc(od, conn, frame, channel, &userinfo, nummissed, reason);
 1560: 
 1561: 		aim_info_free(&userinfo);
 1562: 	}
 1563: 
 1564: 	return ret;
 1565: }
 1566: 
 1567: /*
 1568:  * Subtype 0x000b
 1569:  *
 1570:  * Possible codes:
 1571:  *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
 1572:  *
 1573:  */
 1574: int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code)
 1575: {
 1576: 	FlapConnection *conn;
 1577: 	ByteStream bs;
 1578: 	aim_snacid_t snacid;
 1579: 	GSList *tlvlist = NULL;
 1580: 
 1581: 	if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM)))
 1582: 		return -EINVAL;
 1583: 
 1584: 	byte_stream_new(&bs, 8+2+1+strlen(bn)+6);
 1585: 
 1586: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
 1587: 
 1588: 	byte_stream_putraw(&bs, cookie, 8);
 1589: 
 1590: 	byte_stream_put16(&bs, 0x0002); /* channel */
 1591: 	byte_stream_put8(&bs, strlen(bn));
 1592: 	byte_stream_putstr(&bs, bn);
 1593: 
 1594: 	aim_tlvlist_add_16(&tlvlist, 0x0003, code);
 1595: 	aim_tlvlist_write(&bs, &tlvlist);
 1596: 	aim_tlvlist_free(tlvlist);
 1597: 
 1598: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b, snacid, &bs);
 1599: 
 1600: 	byte_stream_destroy(&bs);
 1601: 
 1602: 	return 0;
 1603: }
 1604: 
 1605: /*
 1606:  * Subtype 0x000b.
 1607:  * Send confirmation for a channel 2 message (Miranda wants it by default).
 1608:  */
 1609: void
 1610: aim_im_send_icq_confirmation(OscarData *od, const char *bn, const guchar *cookie)
 1611: {
 1612: 	FlapConnection *conn;
 1613: 	ByteStream bs;
 1614: 	aim_snacid_t snacid;
 1615: 	guint32 header_size, data_size;
 1616: 	guint16 cookie2 = (guint16)g_random_int();
 1617: 
 1618: 	purple_debug_misc("oscar", "Sending message ack to %s\n", bn);
 1619: 
 1620: 	header_size = 8 + 2 + 1 + strlen(bn) + 2;
 1621: 	data_size = 2 + 1 + 16 + 4*2 + 2*3 + 4*3 + 1*2 + 2*3 + 1;
 1622: 	byte_stream_new(&bs, header_size + data_size);
 1623: 
 1624: 	/* The message header. */
 1625: 	aim_im_puticbm(&bs, cookie, 0x0002, bn);
 1626: 	byte_stream_put16(&bs, 0x0003);	/* reason */
 1627: 
 1628: 	/* The actual message. */
 1629: 	byte_stream_putle16(&bs, 0x1b);	/* subheader #1 length */
 1630: 	byte_stream_put8(&bs, 0x08);	/* protocol version */
 1631: 	byte_stream_putcaps(&bs, OSCAR_CAPABILITY_EMPTY);
 1632: 	byte_stream_put32(&bs, 0x3);	/* client features */
 1633: 	byte_stream_put32(&bs, 0x0004);	/* DC type */
 1634: 	byte_stream_put16(&bs, cookie2);	/* a cookie, chosen by fair dice roll */
 1635: 	byte_stream_putle16(&bs, 0x0e);	/* header #2 len? */
 1636: 	byte_stream_put16(&bs, cookie2);	/* the same cookie again */
 1637: 	byte_stream_put32(&bs, 0);	/* unknown */
 1638: 	byte_stream_put32(&bs, 0);	/* unknown */
 1639: 	byte_stream_put32(&bs, 0);	/* unknown */
 1640: 	byte_stream_put8(&bs, 0x01);	/* plain text message */
 1641: 	byte_stream_put8(&bs, 0x00);	/* no message flags */
 1642: 	byte_stream_put16(&bs, 0x0000);	/* no icq status */
 1643: 	byte_stream_put16(&bs, 0x0100);	/* priority */
 1644: 	byte_stream_putle16(&bs, 1);	/* query message len */
 1645: 	byte_stream_put8(&bs, 0x00);	/* empty query message */
 1646: 
 1647: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x000b, 0x0000, NULL, 0);
 1648: 	conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
 1649: 	g_warn_if_fail(conn);
 1650: 	if (conn) {
 1651: 		flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x000b,
 1652: 			snacid, &bs);
 1653: 	}
 1654: 	byte_stream_destroy(&bs);
 1655: }
 1656: 
 1657: /*
 1658:  * Subtype 0x000b - Receive the response from an ICQ status message
 1659:  * request (in which case this contains the ICQ status message) or
 1660:  * a file transfer or direct IM request was declined.
 1661:  */
 1662: static int clientautoresp(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1663: {
 1664: 	int ret = 0;
 1665: 	aim_rxcallback_t userfunc;
 1666: 	guint16 channel, reason;
 1667: 	char *bn;
 1668: 	guchar *cookie;
 1669: 	guint8 bnlen;
 1670: 	char *xml = NULL;
 1671: 	guint16 hdrlen;
 1672: 	int curpos;
 1673: 	guint16 num1, num2;
 1674: 	PurpleAccount *account;
 1675: 	PurpleBuddy *buddy;
 1676: 	PurplePresence *presence;
 1677: 	PurpleStatus *status;
 1678: 
 1679: 	cookie = byte_stream_getraw(bs, 8);
 1680: 	channel = byte_stream_get16(bs);
 1681: 	bnlen = byte_stream_get8(bs);
 1682: 	bn = byte_stream_getstr(bs, bnlen);
 1683: 	reason = byte_stream_get16(bs);
 1684: 
 1685: 	if (channel == 0x0002)
 1686: 	{
 1687: 		hdrlen = byte_stream_getle16(bs);
 1688: 		if (hdrlen == 27 && bs->len > (27 + 51)) {
 1689: 			byte_stream_advance(bs, 51);
 1690: 			num1 = byte_stream_getle16(bs);
 1691: 			num2 = byte_stream_getle16(bs);
 1692: 			purple_debug_misc("oscar", "X-Status: num1 %hu, num2 %hu\n", num1, num2);
 1693: 
 1694: 			if (num1 == 0x4f00 && num2 == 0x3b00) {
 1695: 				byte_stream_advance(bs, 86);
 1696: 				curpos = byte_stream_curpos(bs);
 1697: 				xml = byte_stream_getstr(bs, bs->len - curpos);
 1698: 				purple_debug_misc("oscar", "X-Status: Received XML reply\n");
 1699: 				if (xml) {
 1700: 					GString *xstatus;
 1701: 					char *tmp1, *tmp2, *unescaped_xstatus;
 1702: 
 1703: 					/* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", xml); */
 1704: 
 1705: 					xstatus = g_string_new(NULL);
 1706: 
 1707: 					tmp1 = strstr(xml, "&lt;title&gt;");
 1708: 					if (tmp1 != NULL) {
 1709: 						tmp1 += 13;
 1710: 						tmp2 = strstr(tmp1, "&lt;/title&gt;");
 1711: 						if (tmp2 != NULL)
 1712: 							g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
 1713: 					}
 1714: 					tmp1 = strstr(xml, "&lt;desc&gt;");
 1715: 					if (tmp1 != NULL) {
 1716: 						tmp1 += 12;
 1717: 						tmp2 = strstr(tmp1, "&lt;/desc&gt;");
 1718: 						if (tmp2 != NULL) {
 1719: 							if (xstatus->len > 0 && tmp2 > tmp1)
 1720: 								g_string_append(xstatus, " - ");
 1721: 							g_string_append_len(xstatus, tmp1, tmp2 - tmp1);
 1722: 						}
 1723: 					}
 1724: 					unescaped_xstatus = purple_unescape_text(xstatus->str);
 1725: 					g_string_free(xstatus, TRUE);
 1726: 					if (*unescaped_xstatus) {
 1727: 						purple_debug_misc("oscar", "X-Status reply: %s\n", unescaped_xstatus);
 1728: 						account = purple_connection_get_account(od->gc);
 1729: 						buddy = purple_find_buddy(account, bn);
 1730: 						presence = purple_buddy_get_presence(buddy);
 1731: 						status = purple_presence_get_status(presence, "mood");
 1732: 						if (status) {
 1733: 							purple_prpl_got_user_status(account, bn,
 1734: 									"mood",
 1735: 									PURPLE_MOOD_NAME, purple_status_get_attr_string(status, PURPLE_MOOD_NAME),
 1736: 									PURPLE_MOOD_COMMENT, unescaped_xstatus, NULL);
 1737: 						}
 1738: 					}
 1739: 					g_free(unescaped_xstatus);
 1740: 				} else {
 1741: 					purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n");
 1742: 				}
 1743: 			} else {
 1744: 				purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n");
 1745: 			}
 1746: 
 1747: 		}
 1748: 
 1749: 	} else if (channel == 0x0004) { /* ICQ message */
 1750: 		switch (reason) {
 1751: 			case 0x0003: { /* ICQ status message.  Maybe other stuff too, you never know with these people. */
 1752: 				guint8 statusmsgtype, *msg;
 1753: 				guint16 len;
 1754: 				guint32 state;
 1755: 
 1756: 				len = byte_stream_getle16(bs); /* Should be 0x001b */
 1757: 				byte_stream_advance(bs, len); /* Unknown */
 1758: 
 1759: 				len = byte_stream_getle16(bs); /* Should be 0x000e */
 1760: 				byte_stream_advance(bs, len); /* Unknown */
 1761: 
 1762: 				statusmsgtype = byte_stream_getle8(bs);
 1763: 				switch (statusmsgtype) {
 1764: 					case 0xe8:
 1765: 						state = AIM_ICQ_STATE_AWAY;
 1766: 						break;
 1767: 					case 0xe9:
 1768: 						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY;
 1769: 						break;
 1770: 					case 0xea:
 1771: 						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT;
 1772: 						break;
 1773: 					case 0xeb:
 1774: 						state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY;
 1775: 						break;
 1776: 					case 0xec:
 1777: 						state = AIM_ICQ_STATE_CHAT;
 1778: 						break;
 1779: 					default:
 1780: 						state = 0;
 1781: 						break;
 1782: 				}
 1783: 
 1784: 				byte_stream_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */
 1785: 				byte_stream_getle16(bs); /* Unknown - 0x0000 */
 1786: 				byte_stream_getle16(bs); /* Unknown - 0x0000 */
 1787: 
 1788: 				len = byte_stream_getle16(bs);
 1789: 				msg = byte_stream_getraw(bs, len);
 1790: 
 1791: 				if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1792: 					ret = userfunc(od, conn, frame, channel, bn, reason, state, msg);
 1793: 
 1794: 				g_free(msg);
 1795: 			} break;
 1796: 
 1797: 			default: {
 1798: 				if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 1799: 					ret = userfunc(od, conn, frame, channel, bn, reason);
 1800: 			} break;
 1801: 		} /* end switch */
 1802: 	}
 1803: 
 1804: 	g_free(cookie);
 1805: 	g_free(bn);
 1806: 	g_free(xml);
 1807: 
 1808: 	return ret;
 1809: }
 1810: 
 1811: /*
 1812:  * Subtype 0x000c - Receive an ack after sending an ICBM. The ack contains the ICBM header of the message you sent.
 1813:  */
 1814: static int msgack(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 1815: {
 1816: 	guchar *cookie;
 1817: 	char *bn;
 1818: 	int ret = 0;
 1819: 
 1820: 	cookie = byte_stream_getraw(bs, 8);
 1821: 	byte_stream_get16(bs); /* ch */
 1822: 	bn = byte_stream_getstr(bs, byte_stream_get8(bs));
 1823: 
 1824: 	purple_debug_info("oscar", "Sent message to %s.\n", bn);
 1825: 
 1826: 	g_free(bn);
 1827: 	g_free(cookie);
 1828: 
 1829: 	return ret;
 1830: }
 1831: 
 1832: /*
 1833:  * Subtype 0x0010 - Request any offline messages that are waiting for
 1834:  * us.  This is the "new" way of handling offline messages which is
 1835:  * used for both AIM and ICQ.  The old way is to use the ugly
 1836:  * aim_icq_reqofflinemsgs() function, but that is no longer necessary.
 1837:  *
 1838:  * We set the 0x00000100 flag on the ICBM message parameters, which
 1839:  * tells the oscar servers that we support offline messages.  When we
 1840:  * set that flag the servers do not automatically send us offline
 1841:  * messages.  Instead we must request them using this function.  This
 1842:  * should happen after sending the 0x0001/0x0002 "client online" SNAC.
 1843:  */
 1844: int aim_im_reqofflinemsgs(OscarData *od)
 1845: {
 1846: 	FlapConnection *conn;
 1847: 
 1848: 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 1849: 		return -EINVAL;
 1850: 
 1851: 	aim_genericreq_n(od, conn, SNAC_FAMILY_ICBM, 0x0010);
 1852: 
 1853: 	return 0;
 1854: }
 1855: 
 1856: /*
 1857:  * Subtype 0x0014 - Send a mini typing notification (mtn) packet.
 1858:  *
 1859:  * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
 1860:  * and Purple 0.60 and newer.
 1861:  *
 1862:  */
 1863: int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event)
 1864: {
 1865: 	FlapConnection *conn;
 1866: 	ByteStream bs;
 1867: 	aim_snacid_t snacid;
 1868: 
 1869: 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 1870: 		return -EINVAL;
 1871: 
 1872: 	if (!bn)
 1873: 		return -EINVAL;
 1874: 
 1875: 	byte_stream_new(&bs, 11 + strlen(bn) + 2);
 1876: 
 1877: 	snacid = aim_cachesnac(od, SNAC_FAMILY_ICBM, 0x0014, 0x0000, NULL, 0);
 1878: 
 1879: 	/* ICBM cookie */
 1880: 	byte_stream_put32(&bs, 0x00000000);
 1881: 	byte_stream_put32(&bs, 0x00000000);
 1882: 
 1883: 	/*
 1884: 	 * Channel (should be 0x0001 for mtn)
 1885: 	 */
 1886: 	byte_stream_put16(&bs, channel);
 1887: 
 1888: 	/*
 1889: 	 * Dest buddy name
 1890: 	 */
 1891: 	byte_stream_put8(&bs, strlen(bn));
 1892: 	byte_stream_putstr(&bs, bn);
 1893: 
 1894: 	/*
 1895: 	 * Event (should be 0x0000, 0x0001, or 0x0002 for mtn)
 1896: 	 */
 1897: 	byte_stream_put16(&bs, event);
 1898: 
 1899: 	flap_connection_send_snac(od, conn, SNAC_FAMILY_ICBM, 0x0014, snacid, &bs);
 1900: 
 1901: 	byte_stream_destroy(&bs);
 1902: 
 1903: 	return 0;
 1904: }
 1905: 
 1906: /*
 1907:  * Subtype 0x0006 - Send eXtra Status request
 1908:  */
 1909: int icq_im_xstatus_request(OscarData *od, const char *sn)
 1910: {
 1911: 	FlapConnection *conn;
 1912: 	aim_snacid_t snacid;
 1913: 	guchar cookie[8];
 1914: 	GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL;
 1915: 	ByteStream bs, header, plugindata;
 1916: 	PurpleAccount *account;
 1917: 	const char *fmt;
 1918: 	char *statxml;
 1919: 	int xmllen;
 1920: 
 1921: 	static const guint8 pluginid[] = {
 1922: 		0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1,
 1923: 		0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00
 1924: 	};
 1925: 
 1926: 	static const guint8 c_plugindata[] = {
 1927: 		0x1B, 0x00, 0x0A,
 1928: 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 1929: 		0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00,
 1930: 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00,
 1931: 		0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C,
 1932: 		0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70,
 1933: 		0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
 1934: 		0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41,
 1935: 		0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 1936: 		0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00
 1937: 	};
 1938: 
 1939: 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0004)))
 1940: 		return -EINVAL;
 1941: 
 1942: 	if (!sn)
 1943: 		return -EINVAL;
 1944: 
 1945: 	fmt = "<N><QUERY>&lt;Q&gt;&lt;PluginID&gt;srvMng&lt;/PluginID&gt;&lt;/Q&gt;</QUERY><NOTIFY>&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;req&gt;&lt;id&gt;AwayStat&lt;/id&gt;&lt;trans&gt;2&lt;/trans&gt;&lt;senderId&gt;%s&lt;/senderId&gt;&lt;/req&gt;&lt;/srv&gt;</NOTIFY></N>\r\n";
 1946: 
 1947: 	account = purple_connection_get_account(od->gc);
 1948: 
 1949: 	statxml = g_strdup_printf(fmt, account->username);
 1950: 	xmllen = strlen(statxml);
 1951: 
 1952: 	aim_icbm_makecookie(cookie);
 1953: 
 1954: 	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2
 1955: 					  + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2
 1956: 					  + 2 + 2 + sizeof(c_plugindata) + xmllen
 1957: 					  + 2 + 2);
 1958: 
 1959: 	snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0);
 1960: 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
 1961: 
 1962: 	byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */
 1963: 	byte_stream_put16(&header, 0x0000); /* Message Type: Request */
 1964: 	byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */
 1965: 	byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */
 1966: 
 1967: 	aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001);
 1968: 	aim_tlvlist_add_noval(&inner_tlvlist, 0x000f);
 1969: 
 1970: 	/* Add Plugin Specific Data */
 1971: 	byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen));
 1972: 	byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */
 1973: 	byte_stream_putraw(&plugindata, (const guint8*)statxml, xmllen);
 1974: 
 1975: 	aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data);
 1976: 
 1977: 	aim_tlvlist_write(&header, &inner_tlvlist);
 1978: 	aim_tlvlist_free(inner_tlvlist);
 1979: 
 1980: 	aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data);
 1981: 	aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */
 1982: 
 1983: 	aim_tlvlist_write(&bs, &outer_tlvlist);
 1984: 
 1985: 	purple_debug_misc("oscar", "X-Status Request\n");
 1986: 	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, snacid, &bs, TRUE);
 1987: 
 1988: 	aim_tlvlist_free(outer_tlvlist);
 1989: 	byte_stream_destroy(&header);
 1990: 	byte_stream_destroy(&plugindata);
 1991: 	byte_stream_destroy(&bs);
 1992: 	g_free(statxml);
 1993: 
 1994: 	return 0;
 1995: }
 1996: 
 1997: int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie)
 1998: {
 1999: 	FlapConnection *conn;
 2000: 	ByteStream bs;
 2001: 	aim_snacid_t snacid;
 2002: 	PurpleAccount *account;
 2003: 	PurpleStatus *status;
 2004: 	const char *fmt;
 2005: 	const char *formatted_msg;
 2006: 	char *msg;
 2007: 	char *statxml;
 2008: 	const char *title;
 2009: 	int len;
 2010: 
 2011: 	static const guint8 plugindata[] = {
 2012: 		0x1B, 0x00,
 2013: 		0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 2014: 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 2015: 		0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1,
 2016: 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 2017: 		0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F,
 2018: 		0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0,
 2019: 		0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00,
 2020: 		0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75,
 2021: 		0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74,
 2022: 		0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
 2023: 		0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00,
 2024: 		0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 2025: 		0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00
 2026: 	};
 2027: 
 2028: 	fmt = "<NR><RES>&lt;ret event='OnRemoteNotification'&gt;&lt;srv&gt;&lt;id&gt;cAwaySrv&lt;/id&gt;&lt;val srv_id='cAwaySrv'&gt;&lt;Root&gt;&lt;CASXtraSetAwayMessage&gt;&lt;/CASXtraSetAwayMessage&gt;&l t;uin&gt;%s&lt;/uin&gt;&lt;index&gt;1&lt;/index&gt;&lt;title&gt;%s&lt;/title&gt;&lt;desc&gt;%s&lt;/desc&gt;&lt;/Root&gt;&lt;/val&gt;&lt;/srv&gt;&lt;srv&gt;&lt;id&gt;cRandomizerSrv&lt;/id&gt;&lt;val srv_id='cRandomizerSrv'&gt;undefined&lt;/val&gt;&lt;/srv&gt;&lt;/ret&gt;</RES></NR>\r\n";
 2029: 
 2030: 	if (!od || !(conn = flap_connection_findbygroup(od, 0x0002)))
 2031: 		return -EINVAL;
 2032: 
 2033: 	if (!sn)
 2034: 		return -EINVAL;
 2035: 
 2036: 	account = purple_connection_get_account(od->gc);
 2037: 	if (!account)
 2038: 		return -EINVAL;
 2039: 
 2040: 	/* if (purple_strequal(account->username, sn))
 2041: 		icq_im_xstatus_request(od, sn); */
 2042: 
 2043: 	status = purple_presence_get_active_status(account->presence);
 2044: 	if (!status)
 2045: 		return -EINVAL;
 2046: 
 2047: 	title = purple_status_get_name(status);
 2048: 	if (!title)
 2049: 		return -EINVAL;
 2050: 
 2051: 	formatted_msg = purple_status_get_attr_string(status, "message");
 2052: 	if (!formatted_msg)
 2053: 		return -EINVAL;
 2054: 
 2055: 	msg = purple_markup_strip_html(formatted_msg);
 2056: 	if (!msg)
 2057: 		return -EINVAL;
 2058: 
 2059: 	statxml = g_strdup_printf(fmt, account->username, title, msg);
 2060: 	len = strlen(statxml);
 2061: 
 2062: 	purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg);
 2063: 
 2064: 	byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + len); /* 16 extra */
 2065: 
 2066: 	snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0);
 2067: 	aim_im_puticbm(&bs, cookie, 0x0002, sn);
 2068: 	byte_stream_put16(&bs, 0x0003);
 2069: 	byte_stream_putraw(&bs, plugindata, sizeof(plugindata));
 2070: 	byte_stream_putraw(&bs, (const guint8*)statxml, len);
 2071: 
 2072: 	flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, snacid, &bs, TRUE);
 2073: 
 2074: 	g_free(statxml);
 2075: 	g_free(msg);
 2076: 	byte_stream_destroy(&bs);
 2077: 
 2078: 	return 0;
 2079: }
 2080: 
 2081: /*
 2082:  * Subtype 0x0014 - Receive a mini typing notification (mtn) packet.
 2083:  *
 2084:  * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
 2085:  * and Purple 0.60 and newer.
 2086:  *
 2087:  */
 2088: static int mtn_receive(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 2089: {
 2090: 	int ret = 0;
 2091: 	aim_rxcallback_t userfunc;
 2092: 	char *bn;
 2093: 	guint8 bnlen;
 2094: 	guint16 channel, event;
 2095: 
 2096: 	byte_stream_advance(bs, 8); /* ICBM cookie */
 2097: 	channel = byte_stream_get16(bs);
 2098: 	bnlen = byte_stream_get8(bs);
 2099: 	bn = byte_stream_getstr(bs, bnlen);
 2100: 	event = byte_stream_get16(bs);
 2101: 
 2102: 	if ((userfunc = aim_callhandler(od, snac->family, snac->subtype)))
 2103: 		ret = userfunc(od, conn, frame, channel, bn, event);
 2104: 
 2105: 	g_free(bn);
 2106: 
 2107: 	return ret;
 2108: }
 2109: 
 2110: static int
 2111: snachandler(OscarData *od, FlapConnection *conn, aim_module_t *mod, FlapFrame *frame, aim_modsnac_t *snac, ByteStream *bs)
 2112: {
 2113: 	if (snac->subtype == 0x0001)
 2114: 		return error(od, conn, mod, frame, snac, bs);
 2115: 	else if (snac->subtype == 0x0005)
 2116: 		return aim_im_paraminfo(od, conn, mod, frame, snac, bs);
 2117: 	else if (snac->subtype == 0x0007)
 2118: 		return incomingim(od, conn, mod, frame, snac, bs);
 2119: 	else if (snac->subtype == 0x000a)
 2120: 		return missedcall(od, conn, mod, frame, snac, bs);
 2121: 	else if (snac->subtype == 0x000b)
 2122: 		return clientautoresp(od, conn, mod, frame, snac, bs);
 2123: 	else if (snac->subtype == 0x000c)
 2124: 		return msgack(od, conn, mod, frame, snac, bs);
 2125: 	else if (snac->subtype == 0x0014)
 2126: 		return mtn_receive(od, conn, mod, frame, snac, bs);
 2127: 
 2128: 	return 0;
 2129: }
 2130: 
 2131: int
 2132: msg_modfirst(OscarData *od, aim_module_t *mod)
 2133: {
 2134: 	mod->family = SNAC_FAMILY_ICBM;
 2135: 	mod->version = 0x0001;
 2136: 	mod->toolid = 0x0110;
 2137: 	mod->toolversion = 0x0629;
 2138: 	mod->flags = 0;
 2139: 	strncpy(mod->name, "messaging", sizeof(mod->name));
 2140: 	mod->snachandler = snachandler;
 2141: 
 2142: 	return 0;
 2143: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>