Annotation of ChivanetAimPidgin/oscarprpl/src/c/family_icbm.c, revision 1.1.1.1

1.1       snw         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>