]> andersk Git - libfaim.git/blobdiff - src/im.c
- Wed Oct 3 11:07:22 PDT 2001
[libfaim.git] / src / im.c
index e2070bc062c512422ac2ad7c0adacd8dd4585eb3..b47b14c66fcb39ff8a9b31c13ce931db96e0eecf 100644 (file)
--- a/src/im.c
+++ b/src/im.c
@@ -138,13 +138,14 @@ faim_export fu32_t aim_iconsum(const fu8_t *buf, int buflen)
  * supposed to be layed out. Most obviously, tlvlists should be used 
  * instead of writing out the bytes manually. 
  *
- * XXX support multipart
+ * XXX more precise verification that we never send SNACs larger than 8192
+ * XXX check SNAC size for multipart
  *
  */
 faim_export int aim_send_im_ext(aim_session_t *sess, aim_conn_t *conn, struct aim_sendimext_args *args)
 {
        static const fu8_t deffeatures[] = {
-               0x01, 0x01, 0x01, 0x02, 0x42,
+               0x01, 0x01, 0x01, 0x02
        };
        int i, msgtlvlen;
        aim_frame_t *fr;
@@ -153,19 +154,40 @@ faim_export int aim_send_im_ext(aim_session_t *sess, aim_conn_t *conn, struct ai
        if (!sess || !conn || !args)
                return -EINVAL;
 
-       if (!args->msg || (args->msglen <= 0))
-               return -EINVAL;
+       if (args->flags & AIM_IMFLAGS_MULTIPART) {
+               if (args->mpmsg->numparts <= 0)
+                       return -EINVAL;
+       } else {
+               if (!args->msg || (args->msglen <= 0))
+                       return -EINVAL;
+
+               if (args->msglen >= MAXMSGLEN)
+                       return -E2BIG;
+       }
 
-       if (args->msglen >= MAXMSGLEN)
-               return -E2BIG;
+       /* Painfully calculate the size of the message TLV */
+       msgtlvlen = 1 + 1; /* 0501 */
 
-       msgtlvlen = 12 + args->msglen;
        if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
-               msgtlvlen += args->featureslen;
+               msgtlvlen += 2 + args->featureslen;
        else
-               msgtlvlen += sizeof(deffeatures);
-               
-       if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, args->msglen+512)))
+               msgtlvlen += 2 + sizeof(deffeatures);
+
+       if (args->flags & AIM_IMFLAGS_MULTIPART) {
+               aim_mpmsg_section_t *sec;
+
+               for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+                       msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+                       msgtlvlen += 4 /* charset */ + sec->datalen;
+               }
+
+       } else {
+               msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
+               msgtlvlen += 4 /* charset */ + args->msglen;
+       }
+
+
+       if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
                return -ENOMEM;
 
        /* XXX should be optional */    
@@ -216,29 +238,50 @@ faim_export int aim_send_im_ext(aim_session_t *sess, aim_conn_t *conn, struct ai
                aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
        }
 
-       aimbs_put16(&fr->data, 0x0101);
+       if (args->flags & AIM_IMFLAGS_MULTIPART) {
+               aim_mpmsg_section_t *sec;
 
-       /* 
-        * Message block length.
-        */
-       aimbs_put16(&fr->data, args->msglen + 0x04);
+               for (sec = args->mpmsg->parts; sec; sec = sec->next) {
+                       aimbs_put16(&fr->data, 0x0101);
+                       aimbs_put16(&fr->data, sec->datalen + 4);
+                       aimbs_put16(&fr->data, sec->charset);
+                       aimbs_put16(&fr->data, sec->charsubset);
+                       aimbs_putraw(&fr->data, sec->data, sec->datalen);
+               }
 
-       /*
-        * Character set.
-        */
-       if (args->flags & AIM_IMFLAGS_UNICODE)
-               aimbs_put16(&fr->data, 0x0002);
-       else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
-               aimbs_put16(&fr->data, 0x0003);
-       else
-               aimbs_put16(&fr->data, 0x0000);
+       } else {
 
-       aimbs_put16(&fr->data, 0x0000);
+               aimbs_put16(&fr->data, 0x0101);
 
-       /*
-        * Message.  Not terminated.
-        */
-       aimbs_putraw(&fr->data, args->msg, args->msglen);
+               /* 
+                * Message block length.
+                */
+               aimbs_put16(&fr->data, args->msglen + 0x04);
+
+               /*
+                * Character set.
+                */
+               if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
+
+                       aimbs_put16(&fr->data, args->charset);
+                       aimbs_put16(&fr->data, args->charsubset);
+
+               } else {
+                       if (args->flags & AIM_IMFLAGS_UNICODE)
+                               aimbs_put16(&fr->data, 0x0002);
+                       else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
+                               aimbs_put16(&fr->data, 0x0003);
+                       else
+                               aimbs_put16(&fr->data, 0x0000);
+
+                       aimbs_put16(&fr->data, 0x0000);
+               }
+
+               /*
+                * Message.  Not terminated.
+                */
+               aimbs_putraw(&fr->data, args->msg, args->msglen);
+       }
 
        /*
         * Set the Request Acknowledge flag.  
@@ -304,13 +347,13 @@ faim_export int aim_send_im(aim_session_t *sess, aim_conn_t *conn, const char *d
        args.msglen = strlen(msg);
 
        /* Make these don't get set by accident -- they need aim_send_im_ext */
-       args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON);
+       args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
 
        return aim_send_im_ext(sess, conn, &args);
 }
 
 /*
- * This is also performance sensative. (If you can believe it...)
+ * This is also performance sensitive. (If you can believe it...)
  *
  */
 faim_export int aim_send_icon(aim_session_t *sess, aim_conn_t *conn, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu32_t iconsum)
@@ -456,6 +499,277 @@ static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, a
        return ret;
 }
 
+/*
+ * Ahh, the joys of nearly ridiculous over-engineering.
+ *
+ * Not only do AIM ICBM's support multiple channels.  Not only do they
+ * support multiple character sets.  But they support multiple character 
+ * sets / encodings within the same ICBM.
+ *
+ * These multipart messages allow for complex space savings techniques, which
+ * seem utterly unnecessary by today's standards.  In fact, there is only
+ * one client still in popular use that still uses this method: AOL for the
+ * Macintosh, Version 5.0.  Obscure, yes, I know.  
+ *
+ * In modern (non-"legacy") clients, if the user tries to send a character
+ * that is not ISO-8859-1 or ASCII, the client will send the entire message
+ * as UNICODE, meaning that every character in the message will occupy the
+ * full 16 bit UNICODE field, even if the high order byte would be zero.
+ * Multipart messages prevent this wasted space by allowing the client to
+ * only send the characters in UNICODE that need to be sent that way, and
+ * the rest of the message can be sent in whatever the native character 
+ * set is (probably ASCII).
+ *
+ * An important note is that sections will be displayed in the order that
+ * they appear in the ICBM.  There is no facility for merging or rearranging
+ * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
+ * you must supply two ASCII sections with a UNICODE in the middle, and incur
+ * the associated overhead.
+ *
+ * Normally I would have laughed and given a firm 'no' to supporting this
+ * seldom-used feature, but something is attracting me to it.  In the future,
+ * it may be possible to abuse this to send mixed-media messages to other
+ * open source clients (like encryption or something) -- see faimtest for
+ * examples of how to do this.
+ *
+ * I would definitly recommend avoiding this feature unless you really
+ * know what you are doing, and/or you have something neat to do with it.
+ *
+ */
+faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+
+       memset(mpm, 0, sizeof(aim_mpmsg_t));
+
+       return 0;
+}
+
+static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, fu8_t *data, fu16_t datalen)
+{
+       aim_mpmsg_section_t *sec; 
+       
+       if (!(sec = malloc(sizeof(aim_mpmsg_section_t))))
+               return -1;
+
+       sec->charset = charset;
+       sec->charsubset = charsubset;
+       sec->data = data;
+       sec->datalen = datalen;
+       sec->next = NULL;
+
+       if (!mpm->parts)
+               mpm->parts = sec;
+       else {
+               aim_mpmsg_section_t *cur;
+
+               for (cur = mpm->parts; cur->next; cur = cur->next)
+                       ;
+               cur->next = sec;
+       }
+
+       mpm->numparts++;
+
+       return 0;
+}
+
+faim_export int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, const fu8_t *data, fu16_t datalen)
+{
+       fu8_t *dup;
+
+       if (!(dup = malloc(datalen)))
+               return -1;
+       memcpy(dup, data, datalen);
+
+       if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
+               free(dup);
+               return -1;
+       }
+
+       return 0;
+}
+
+/* XXX should provide a way of saying ISO-8859-1 specifically */
+faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
+{
+       fu8_t *dup;
+
+       if (!(dup = strdup(ascii))) 
+               return -1;
+
+       if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) {
+               free(dup);
+               return -1;
+       }
+
+       return 0;
+}
+
+faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen)
+{
+       fu8_t *buf;
+       aim_bstream_t bs;
+       int i;
+
+       if (!(buf = malloc(unicodelen * 2)))
+               return -1;
+
+       aim_bstream_init(&bs, buf, unicodelen * 2);
+
+       /* We assume unicode is in /host/ byte order -- convert to network */
+       for (i = 0; i < unicodelen; i++)
+               aimbs_put16(&bs, unicode[i]);
+
+       if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
+               free(buf);
+               return -1;
+       }
+       
+       return 0;
+}
+
+faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
+{
+       aim_mpmsg_section_t *cur;
+
+       for (cur = mpm->parts; cur; ) {
+               aim_mpmsg_section_t *tmp;
+               
+               tmp = cur->next;
+               free(cur->data);
+               free(cur);
+               cur = tmp;
+       }
+       
+       mpm->numparts = 0;
+       mpm->parts = NULL;
+
+       return;
+}
+
+/*
+ * Start by building the multipart structures, then pick the first 
+ * human-readable section and stuff it into args->msg so no one gets
+ * suspicious.
+ *
+ */
+static int incomingim_ch1_parsemsgs(aim_session_t *sess, fu8_t *data, int len, struct aim_incomingim_ch1_args *args)
+{
+       static const fu16_t charsetpri[] = {
+               0x0000, /* ASCII first */
+               0x0003, /* then ISO-8859-1 */
+               0x0002, /* UNICODE as last resort */
+       };
+       static const int charsetpricount = 3;
+       int i;
+       aim_bstream_t mbs;
+       aim_mpmsg_section_t *sec;
+
+       aim_bstream_init(&mbs, data, len);
+
+       while (aim_bstream_empty(&mbs)) {
+               fu16_t msglen, flag1, flag2;
+               fu8_t *msgbuf;
+
+               aimbs_get8(&mbs); /* 01 */
+               aimbs_get8(&mbs); /* 01 */
+
+               /* Message string length, including character set info. */
+               msglen = aimbs_get16(&mbs);
+
+               /* Character set info */
+               flag1 = aimbs_get16(&mbs);
+               flag2 = aimbs_get16(&mbs);
+
+               /* Message. */
+               msglen -= 4;
+
+               /*
+                * For now, we don't care what the encoding is.  Just copy
+                * it into a multipart struct and deal with it later. However,
+                * always pad the ending with a NULL.  This makes it easier
+                * to treat ASCII sections as strings.  It won't matter for
+                * UNICODE or binary data, as you should never read past
+                * the specified data length, which will not include the pad.
+                *
+                * XXX There's an API bug here.  For sending, the UNICODE is
+                * given in host byte order (aim_mpmsg_addunicode), but here
+                * the received messages are given in network byte order.
+                *
+                */
+               msgbuf = aimbs_getstr(&mbs, msglen);
+               mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen);
+
+       } /* while */
+
+       args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
+
+       /*
+        * Clients that support multiparts should never use args->msg, as it
+        * will point to an arbitrary section.
+        *
+        * Here, we attempt to provide clients that do not support multipart
+        * messages with something to look at -- hopefully a human-readable
+        * string.  But, failing that, a UNICODE message, or nothing at all.
+        *
+        * Which means that even if args->msg is NULL, it does not mean the
+        * message was blank.
+        *
+        */
+       for (i = 0; i < charsetpricount; i++) {
+               for (sec = args->mpmsg.parts; sec; sec = sec->next) {
+
+                       if (sec->charset != charsetpri[i])
+                               continue;
+
+                       /* Great. We found one.  Fill it in. */
+                       args->charset = sec->charset;
+                       args->charsubset = sec->charsubset;
+                       args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET;
+
+                       /* Set up the simple flags */
+                       if (args->charset == 0x0000)
+                               ; /* ASCII */
+                       else if (args->charset == 0x0002)
+                               args->icbmflags |= AIM_IMFLAGS_UNICODE;
+                       else if (args->charset == 0x0003)
+                               args->icbmflags |= AIM_IMFLAGS_ISO_8859_1;
+                       else if (args->charset == 0xffff)
+                               ; /* no encoding (yeep!) */
+
+                       if (args->charsubset == 0x0000)
+                               ; /* standard subencoding? */
+                       else if (args->charsubset == 0x000b)
+                               args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
+                       else if (args->charsubset == 0xffff)
+                               ; /* no subencoding */
+#if 0
+                       /* XXX this isn't really necesary... */ 
+                       if (    ((args.flag1 != 0x0000) &&
+                                (args.flag1 != 0x0002) &&
+                                (args.flag1 != 0x0003) &&
+                                (args.flag1 != 0xffff)) ||
+                               ((args.flag2 != 0x0000) &&
+                                (args.flag2 != 0x000b) &&
+                                (args.flag2 != 0xffff))) {
+                               faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
+                       }
+#endif
+
+                       args->msg = sec->data;
+                       args->msglen = sec->datalen;
+
+                       return 0;
+               }
+       }
+
+       /* No human-readable sections found.  Oh well. */
+       args->charset = args->charsubset = 0xffff;
+       args->msg = NULL;
+       args->msglen = 0;
+
+       return 0;
+}
+
 /*
  *
  * This should use tlvlists, but doesn't for performance reasons.
@@ -480,7 +794,7 @@ static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, a
  *             0101 000b 0000 0000 3c2f 4854 4d4c 3e   another ASCII part
  *
  */
-static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, struct aim_userinfo_s *userinfo, aim_bstream_t *bs, fu8_t *cookie)
+static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, fu8_t *cookie)
 {
        fu16_t type, length;
        aim_rxcallback_t userfunc;
@@ -490,6 +804,8 @@ static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
 
        memset(&args, 0, sizeof(args));
 
+       aim_mpmsg_init(sess, &args.mpmsg);
+
        /*
         * This used to be done using tlvchains.  For performance reasons,
         * I've changed it to process the TLVs in-place.  This avoids lots
@@ -522,58 +838,11 @@ static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
                        aim_bstream_advance(bs, args.featureslen);
                        args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
 
-                       aimbs_get8(bs); /* 01 */
-                       aimbs_get8(bs); /* 01 */
-
-                       /* Message string length, including flag words. */
-                       args.msglen = aimbs_get16(bs);
-
-                       /* Flag words. */
-                       args.flag1 = aimbs_get16(bs);
-                       if (args.flag1 == 0x0000)
-                               ; /* ASCII */
-                       else if (args.flag1 == 0x0002)
-                               args.icbmflags |= AIM_IMFLAGS_UNICODE;
-                       else if (args.flag1 == 0x0003)
-                               args.icbmflags |= AIM_IMFLAGS_ISO_8859_1;
-                       else if (args.flag1 == 0xffff)
-                               ; /* no encoding (yeep!) */
-
-                       args.flag2 = aimbs_get16(bs);
-                       if (args.flag2 == 0x0000)
-                               ; /* standard subencoding? */
-                       else if (args.flag2 == 0x000b)
-                               args.icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
-                       else if (args.flag2 == 0xffff)
-                               ; /* no subencoding */
-
-                       /* XXX this isn't really necesary... */ 
-                       if (    ((args.flag1 != 0x0000) &&
-                                (args.flag1 != 0x0002) &&
-                                (args.flag1 != 0x0003) &&
-                                (args.flag1 != 0xffff)) ||
-                               ((args.flag2 != 0x0000) &&
-                                (args.flag2 != 0x000b) &&
-                                (args.flag2 != 0xffff))) {
-                               faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
-                       }
-
-                       /* Message. */
-                       args.msglen -= 4;
-                       if (args.icbmflags & AIM_IMFLAGS_UNICODE) {
-                               fu8_t *umsg;
-
-                               /* Can't use getstr because of wide null */
-                               umsg = aimbs_getraw(bs, args.msglen);
-                               args.msg = malloc(args.msglen+2);
-                               memcpy(args.msg, umsg, args.msglen);
-                               args.msg[args.msglen] = '\0'; /* wide NULL */
-                               args.msg[args.msglen+1] = '\0';
-
-                               free(umsg);
-
-                       } else
-                               args.msg = aimbs_getstr(bs, args.msglen);
+                       /*
+                        * The rest of the TLV contains one or more message
+                        * blocks...
+                        */
+                       incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
 
                } else if (type == 0x0003) { /* Server Ack Requested */
 
@@ -618,16 +887,325 @@ static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
                ret = userfunc(sess, rx, channel, userinfo, &args);
 
+       aim_mpmsg_free(sess, &args.mpmsg);
        free(args.extdata);
-       free(args.msg);
 
        return ret;
 }
 
-/* XXX Ugh.  I think its obvious. */
-static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, struct aim_userinfo_s *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
+static int incomingim_ch2_buddylist(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+       aim_rxcallback_t userfunc;
+       int ret = 0;
+       aim_tlv_t *tse;
+       aim_bstream_t tbs;
+
+       if (args->status != 0x0000)
+               return 1; /* ignore it -- not sure what it means */
+
+       tse = aim_gettlv(list2, 0x2711, 1);
+       aim_bstream_init(&tbs, tse->value, tse->length);
+
+       /*
+        * This goes like this...
+        *
+        *   group name length
+        *   group name
+        *     num of buddies in group
+        *     buddy name length
+        *     buddy name
+        *     buddy name length
+        *     buddy name
+        *     ...
+        *   group name length
+        *   group name
+        *     num of buddies in group
+        *     buddy name length
+        *     buddy name
+        *     ...
+        *   ...
+        */
+       while (aim_bstream_empty(&tbs)) {
+               fu16_t gnlen, numb;
+               int i;
+               char *gn;
+
+               gnlen = aimbs_get16(&tbs);
+               gn = aimbs_getstr(&tbs, gnlen);
+               numb = aimbs_get16(&tbs);
+
+               for (i = 0; i < numb; i++) {
+                       fu16_t bnlen;
+                       char *bn;
+
+                       bnlen = aimbs_get16(&tbs);
+                       bn = aimbs_getstr(&tbs, bnlen);
+
+                       faimdprintf(sess, 0, "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
+
+                       free(bn);
+               }
+
+               free(gn);
+       }
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       return ret;
+}
+
+static int incomingim_ch2_buddyicon(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+       aim_rxcallback_t userfunc;
+       int ret = 0;
+       aim_tlv_t *miscinfo;
+       aim_bstream_t tbs;
+
+       if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)))
+               return 0;
+       aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
+
+       args->info.icon.checksum = aimbs_get32(&tbs);
+       args->info.icon.length = aimbs_get32(&tbs);
+       args->info.icon.timestamp = aimbs_get32(&tbs);
+       args->info.icon.icon = aimbs_getraw(&tbs, args->info.icon.length);
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       free(args->info.icon.icon);
+
+       return ret;
+}
+
+static int incomingim_ch2_voice(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+       aim_msgcookie_t *cachedcook;
+       int ret = 0;
+       aim_rxcallback_t userfunc;
+
+       faimdprintf(sess, 1, "rend: voice!\n");
+
+       if (!(cachedcook = (aim_msgcookie_t*)calloc(1, sizeof(aim_msgcookie_t))))
+               return 0;
+
+       memcpy(cachedcook->cookie, args->cookie, 8);
+       cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
+       cachedcook->data = NULL;
+
+       if (aim_cachecookie(sess, cachedcook) == -1)
+               faimdprintf(sess, 0, "ERROR caching message cookie\n");
+
+       /* XXX: implement all this */
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) 
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       return ret;
+}
+
+static int incomingim_ch2_chat(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+       aim_tlv_t *miscinfo;
+       aim_bstream_t tbs;
+       aim_rxcallback_t userfunc;
+       int ret = 0;
+
+       miscinfo = aim_gettlv(list2, 0x2711, 1);
+       aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
+
+       aim_chat_readroominfo(&tbs, &args->info.chat.roominfo);
+                 
+       if (aim_gettlv(list2, 0x000c, 1))
+               args->info.chat.msg = aim_gettlv_str(list2, 0x000c, 1);
+       
+       if (aim_gettlv(list2, 0x000d, 1))
+               args->info.chat.encoding = aim_gettlv_str(list2, 0x000d, 1);
+       
+       if (aim_gettlv(list2, 0x000e, 1))
+               args->info.chat.lang = aim_gettlv_str(list2, 0x000e, 1);
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       /* XXX free_roominfo */
+       free(args->info.chat.roominfo.name);
+       free(args->info.chat.msg);
+       free(args->info.chat.encoding);
+       free(args->info.chat.lang);
+
+       return ret;
+}
+
+static int incomingim_ch2_getfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+       char ip[30];
+       aim_msgcookie_t *cachedcook;
+       aim_tlv_t *miscinfo;
+       aim_tlv_t *iptlv, *porttlv;
+       int ret = 0;
+       aim_rxcallback_t userfunc;
+
+       memset(ip, 0, 30);
+
+       if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) {
+               aim_freetlvchain(&list2);
+               return 0;
+       }
+
+       if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
+               !(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
+               !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
+               
+               faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
+               aim_cookie_free(sess, cachedcook);
+               aim_freetlvchain(&list2);
+               
+               return 0;
+       }
+
+       snprintf(ip, 30, "%d.%d.%d.%d:%d",
+               aimutil_get8(iptlv->value+0),
+               aimutil_get8(iptlv->value+1),
+               aimutil_get8(iptlv->value+2),
+               aimutil_get8(iptlv->value+3),
+               aimutil_get16(porttlv->value));
+
+       faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo->sn, ip);
+
+       args->info.getfile.ip = ip;
+       memcpy(args->info.getfile.cookie, args->cookie, 8);
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       return ret;
+}
+
+static int incomingim_ch2_sendfile(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
+{
+#if 0
+       char ip[30];
+       aim_msgcookie_t *cachedcook;
+       aim_tlv_t *miscinfo;
+       aim_tlv_t *iptlv, *porttlv;
+       int ret =0;
+       aim_rxcallback_t userfunc;
+       char *desc = NULL;
+
+       memset(ip, 0, 30);
+
+       if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) 
+               return 0;
+
+       if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
+               !(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
+               !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
+       
+               faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
+               aim_cookie_free(sess, cachedcook);
+
+               return 0;
+       }
+
+       snprintf(ip, 30, "%d.%d.%d.%d:%d",
+               aimutil_get8(iptlv->value+0),
+               aimutil_get8(iptlv->value+1),
+               aimutil_get8(iptlv->value+2),
+               aimutil_get8(iptlv->value+3),
+               aimutil_get16(porttlv->value));
+
+       if (aim_gettlv(list2, 0x000c, 1))
+               desc = aim_gettlv_str(list2, 0x000c, 1);
+
+       faimdprintf(sess, 0, "rend: file transfer request from %s: %s (%s)\n",
+               userinfo->sn, desc, ip);
+
+       memcpy(cachedcook->cookie, args->cookie, 8);
+
+       ft = malloc(sizeof(struct aim_filetransfer_priv)); /* XXX */
+       strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
+       strncpy(ft->ip, ip, sizeof(ft->ip));
+       strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
+       cachedcook->type = AIM_COOKIETYPE_OFTSEND;
+       cachedcook->data = ft;
+
+       if (aim_cachecookie(sess, cachedcook) == -1)
+               faimdprintf(sess, 0, "ERROR caching message cookie\n");
+
+       aim_accepttransfer(sess, rx->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
+
+       if (desc)
+               free(desc);
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, &args);
+#endif
+       return 0;
+}
+
+static int incomingim_ch2_imimage(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_tlvlist_t *list2)
 {
        aim_rxcallback_t userfunc;
+       int ret = 0;
+
+       /* Primary IP address */
+       if (aim_gettlv(list2, 0x0003, 1)) {
+               aim_tlv_t *tlv;
+
+               tlv = aim_gettlv(list2, 0x0003, 1);
+
+               snprintf(args->info.imimage.ip, sizeof(args->info.imimage.ip),
+                                       "%d.%d.%d.%d:4443",
+                                       tlv->value[0],
+                                       tlv->value[1],
+                                       tlv->value[2],
+                                       tlv->value[3]);
+       }
+
+       /* 
+        * Alternate IP address
+        *
+        * Sort of.  The peer doesn't send this -- the OSCAR
+        * server does.  So it will be the IP address that the
+        * peer is directly connected to the internet with, which 
+        * may not be the same as the IP above.  If these two
+        * values differ, it's rather unlikely that this
+        * rendezvous is going to happen...
+        *
+        */
+       if (aim_gettlv(list2, 0x0004, 1))
+               ;
+               
+       /* Port number (not correct -- ignore) */
+       if (aim_gettlv(list2, 0x0005, 1)) 
+               ;
+
+       /* Unknown -- two bytes = 0x0001 */
+       if (aim_gettlv(list2, 0x000a, 1))
+               ;
+
+       /* Unknown -- no value */
+       if (aim_gettlv(list2, 0x000f, 1))
+               ;
+
+       faimdprintf(sess, 1, "rend: directIM request from %s (%s)\n", userinfo->sn, args->info.imimage.ip);
+
+       /* 
+        * XXX: there are a couple of different request packets for
+        *          different things 
+        */
+
+       if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
+               ret = userfunc(sess, rx, 0x0002, userinfo, args);
+
+       return ret;
+}
+
+/* XXX Ugh.  I think its obvious. */
+static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, fu16_t channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, fu8_t *cookie)
+{
        aim_tlv_t *block1;
        aim_tlvlist_t *list2;
        int ret = 0;
@@ -660,6 +1238,7 @@ static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
        cookie2 = aimbs_getraw(&bbs, 8);
        if (memcmp(cookie, cookie2, 8) != 0) 
                faimdprintf(sess, 0, "rend: warning cookies don't match!\n");
+       memcpy(args.cookie, cookie2, 8);
        free(cookie2);
 
        /*
@@ -687,6 +1266,7 @@ static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
         */
        list2 = aim_readtlvchain(&bbs);
 
+#if 0 /* this should be in the per-type blocks */
        if (!list2 || ((args.reqclass != AIM_CAPS_IMIMAGE) && !(aim_gettlv(list2, 0x2711, 1)))) {
                aim_msgcookie_t *cook;
                int type;
@@ -734,211 +1314,26 @@ static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *r
 
                return 1;
        }
+#endif
 
        /*
         * The rest of the handling depends on what type it is.
         */
-       if (args.reqclass & AIM_CAPS_BUDDYICON) {
-               aim_tlv_t *miscinfo;
-               aim_bstream_t tbs;
-
-               miscinfo = aim_gettlv(list2, 0x2711, 1);
-               aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
-
-               args.info.icon.checksum = aimbs_get32(&tbs);
-               args.info.icon.length = aimbs_get32(&tbs);
-               args.info.icon.timestamp = aimbs_get32(&tbs);
-               args.info.icon.icon = aimbs_getraw(&tbs, args.info.icon.length);
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-               free(args.info.icon.icon);
-
-       } else if (args.reqclass & AIM_CAPS_VOICE) {
-               aim_msgcookie_t *cachedcook;
-
-               faimdprintf(sess, 1, "rend: voice!\n");
-
-               if(!(cachedcook = (aim_msgcookie_t*)calloc(1, sizeof(aim_msgcookie_t)))) {
-                       aim_freetlvchain(&list2);
-                       return 0;
-               }
-
-               memcpy(cachedcook->cookie, cookie, 8);
-               cachedcook->type = AIM_COOKIETYPE_OFTVOICE;
-               cachedcook->data = NULL;
-
-               if (aim_cachecookie(sess, cachedcook) == -1)
-                       faimdprintf(sess, 0, "ERROR caching message cookie\n");
-
-               /* XXX: implement all this */
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) 
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-       } else if (args.reqclass & AIM_CAPS_IMIMAGE) {
-               char ip[30];
-               struct aim_directim_priv *priv;
-
-               memset(ip, 0, sizeof(ip));
-
-               if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0005, 1)) {
-                       aim_tlv_t *iptlv, *porttlv;
-                         
-                       iptlv = aim_gettlv(list2, 0x0003, 1);
-                       porttlv = aim_gettlv(list2, 0x0005, 1);
-
-                       snprintf(ip, 30, "%d.%d.%d.%d:%d", 
-                               aimutil_get8(iptlv->value+0),
-                               aimutil_get8(iptlv->value+1),
-                               aimutil_get8(iptlv->value+2),
-                               aimutil_get8(iptlv->value+3),
-                               4443 /*aimutil_get16(porttlv->value)*/);
-               }
-
-               faimdprintf(sess, 1, "rend: directIM request from %s (%s)\n",
-                               userinfo->sn, ip);
-
-               /* 
-                * XXX: there are a couple of different request packets for
-                *          different things 
-                */
-
-               args.info.directim = priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv)); /* XXX error */
-               memcpy(priv->ip, ip, sizeof(priv->ip));
-               memcpy(priv->sn, userinfo->sn, sizeof(priv->sn));
-               memcpy(priv->cookie, cookie, sizeof(priv->cookie));
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-       } else if (args.reqclass & AIM_CAPS_CHAT) {
-               aim_tlv_t *miscinfo;
-               aim_bstream_t tbs;
-
-               miscinfo = aim_gettlv(list2, 0x2711, 1);
-
-               aim_bstream_init(&tbs, miscinfo->value, miscinfo->length);
-
-               aim_chat_readroominfo(&tbs, &args.info.chat.roominfo);
-                         
-               if (aim_gettlv(list2, 0x000c, 1))
-                       args.info.chat.msg = aim_gettlv_str(list2, 0x000c, 1);
-               
-               if (aim_gettlv(list2, 0x000d, 1))
-                       args.info.chat.encoding = aim_gettlv_str(list2, 0x000d, 1);
-               
-               if (aim_gettlv(list2, 0x000e, 1))
-                       args.info.chat.lang = aim_gettlv_str(list2, 0x000e, 1);
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-               free(args.info.chat.roominfo.name);
-               free(args.info.chat.msg);
-               free(args.info.chat.encoding);
-               free(args.info.chat.lang);
-
-       } else if (args.reqclass & AIM_CAPS_GETFILE) {
-               char ip[30];
-               aim_msgcookie_t *cachedcook;
-               aim_tlv_t *miscinfo;
-               aim_tlv_t *iptlv, *porttlv;
-
-               memset(ip, 0, 30);
-
-               if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) {
-                       aim_freetlvchain(&list2);
-                       return 0;
-               }
-
-               if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
-                       !(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
-                       !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
-                       
-                       faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
-                       aim_cookie_free(sess, cachedcook);
-                       aim_freetlvchain(&list2);
-                       
-                       return 0;
-               }
-
-               snprintf(ip, 30, "%d.%d.%d.%d:%d",
-                       aimutil_get8(iptlv->value+0),
-                       aimutil_get8(iptlv->value+1),
-                       aimutil_get8(iptlv->value+2),
-                       aimutil_get8(iptlv->value+3),
-                       aimutil_get16(porttlv->value));
-
-               faimdprintf(sess, 0, "rend: file get request from %s (%s)\n", userinfo->sn, ip);
-
-               args.info.getfile.ip = ip;
-               args.info.getfile.cookie = cookie;
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-       } else if (args.reqclass & AIM_CAPS_SENDFILE) {
-#if 0 
-               char ip[30];
-               aim_msgcookie_t *cachedcook;
-               aim_tlv_t *miscinfo;
-               aim_tlv_t *iptlv, *porttlv;
-
-               memset(ip, 0, 30);
-
-               if (!(cachedcook = calloc(1, sizeof(aim_msgcookie_t)))) {
-                       aim_freetlvchain(&list2);
-                       return 0;
-               }
-
-               if (!(miscinfo = aim_gettlv(list2, 0x2711, 1)) || 
-                       !(iptlv = aim_gettlv(list2, 0x0003, 1)) || 
-                       !(porttlv = aim_gettlv(list2, 0x0005, 1))) {
-               
-                       faimdprintf(sess, 0, "rend: badly damaged file get request from %s...\n", userinfo->sn);
-                       aim_cookie_free(sess, cachedcook);
-                       aim_freetlvchain(&list2);
-
-                       return 0;
-               }
-
-               snprintf(ip, 30, "%d.%d.%d.%d:%d",
-                       aimutil_get8(iptlv->value+0),
-                       aimutil_get8(iptlv->value+1),
-                       aimutil_get8(iptlv->value+2),
-                       aimutil_get8(iptlv->value+3),
-                       aimutil_get16(porttlv->value));
-
-               if (aim_gettlv(list2, 0x000c, 1))
-                       desc = aim_gettlv_str(list2, 0x000c, 1);
-
-               faimdprintf(sess, 0, "rend: file transfer request from %s: %s (%s)\n",
-                       userinfo->sn, desc, ip);
-
-               memcpy(cachedcook->cookie, cookie, 8);
-
-               ft = malloc(sizeof(struct aim_filetransfer_priv)); /* XXX */
-               strncpy(ft->sn, userinfo.sn, sizeof(ft->sn));
-               strncpy(ft->ip, ip, sizeof(ft->ip));
-               strncpy(ft->fh.name, miscinfo->value+8, sizeof(ft->fh.name));
-               cachedcook->type = AIM_COOKIETYPE_OFTSEND;
-               cachedcook->data = ft;
-
-               if (aim_cachecookie(sess, cachedcook) == -1)
-                       faimdprintf(sess, 0, "ERROR caching message cookie\n");
-
-               aim_accepttransfer(sess, rx->conn, ft->sn, cookie, AIM_CAPS_SENDFILE);
-
-               if (desc)
-                       free(desc);
-
-               if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-                       ret = userfunc(sess, rx, channel, userinfo, &args);
-
-#endif 
-       } else
+       if (args.reqclass & AIM_CAPS_BUDDYICON)
+               ret = incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST)
+               ret = incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_VOICE)
+               ret = incomingim_ch2_voice(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_IMIMAGE)
+               ret = incomingim_ch2_imimage(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_CHAT)
+               ret = incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_GETFILE)
+               ret = incomingim_ch2_getfile(sess, mod, rx, snac, userinfo, &args, list2);
+       else if (args.reqclass & AIM_CAPS_SENDFILE)
+               ret = incomingim_ch2_sendfile(sess, mod, rx, snac, userinfo, &args, list2);
+       else
                faimdprintf(sess, 0, "rend: unknown rendezvous 0x%04x\n", args.reqclass);
 
        aim_freetlvchain(&list2);
@@ -962,9 +1357,9 @@ static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, a
        int i, ret = 0;
        fu8_t cookie[8];
        fu16_t channel;
-       struct aim_userinfo_s userinfo;
+       aim_userinfo_t userinfo;
 
-       memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
+       memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
 
        /*
         * Read ICBM Cookie.  And throw away.
@@ -1147,7 +1542,7 @@ static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, a
        int ret = 0;
        aim_rxcallback_t userfunc;
        fu16_t channel, nummissed, reason;
-       struct aim_userinfo_s userinfo;
+       aim_userinfo_t userinfo;
 
        while (aim_bstream_empty(bs)) { 
 
@@ -1180,6 +1575,9 @@ static int clienterr(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, ai
        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
                 ret = userfunc(sess, rx, channel, sn, reason);
 
+       free(ck);
+       free(sn);
+
        return ret;
 }
 
@@ -1189,6 +1587,7 @@ static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_m
        fu16_t type;
        fu8_t snlen, *ck;
        char *sn;
+       int ret = 0;
 
        ck = aimbs_getraw(bs, 8);
        type = aimbs_get16(bs);
@@ -1196,12 +1595,12 @@ static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_m
        sn = aimbs_getstr(bs, snlen);
 
        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
-               return userfunc(sess, rx, type, sn);
+               ret = userfunc(sess, rx, type, sn);
 
        free(sn);
        free(ck);
 
-       return 0;
+       return ret;
 }
 
 static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
This page took 0.106524 seconds and 4 git commands to generate.