source: libfaim/im.c @ a8938c7

barnowl_perlaimdebianowlrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since a8938c7 was 5e53c4a, checked in by James M. Kretchmar <kretch@mit.edu>, 21 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 47.6 KB
Line 
1/*
2 *  aim_im.c
3 *
4 *  The routines for sending/receiving Instant Messages.
5 *
6 *  Note the term ICBM (Inter-Client Basic Message) which blankets
7 *  all types of genericly routed through-server messages.  Within
8 *  the ICBM types (family 4), a channel is defined.  Each channel
9 *  represents a different type of message.  Channel 1 is used for
10 *  what would commonly be called an "instant message".  Channel 2
11 *  is used for negotiating "rendezvous".  These transactions end in
12 *  something more complex happening, such as a chat invitation, or
13 *  a file transfer.
14 *
15 *  In addition to the channel, every ICBM contains a cookie.  For
16 *  standard IMs, these are only used for error messages.  However,
17 *  the more complex rendezvous messages make suitably more complex
18 *  use of this field.
19 *
20 */
21
22#define FAIM_INTERNAL
23#include <aim.h>
24
25/*
26 * Takes a msghdr (and a length) and returns a client type
27 * code.  Note that this is *only a guess* and has a low likelihood
28 * of actually being accurate.
29 *
30 * Its based on experimental data, with the help of Eric Warmenhoven
31 * who seems to have collected a wide variety of different AIM clients.
32 *
33 *
34 * Heres the current collection:
35 *  0501 0003 0101 0101 01       AOL Mobile Communicator, WinAIM 1.0.414
36 *  0501 0003 0101 0201 01       WinAIM 2.0.847, 2.1.1187, 3.0.1464,
37 *                                      4.3.2229, 4.4.2286
38 *  0501 0004 0101 0102 0101     WinAIM 4.1.2010, libfaim (right here)
39 *  0501 0001 0101 01            AOL v6.0, CompuServe 2000 v6.0, any
40 *                                      TOC client
41 *
42 * Note that in this function, only the feature bytes are tested, since
43 * the rest will always be the same.
44 *
45 */
46faim_export fu16_t aim_fingerprintclient(fu8_t *msghdr, int len)
47{
48        static const struct {
49                fu16_t clientid;
50                int len;
51                fu8_t data[10];
52        } fingerprints[] = {
53                /* AOL Mobile Communicator, WinAIM 1.0.414 */
54                { AIM_CLIENTTYPE_MC, 
55                  3, {0x01, 0x01, 0x01}},
56
57                /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */
58                { AIM_CLIENTTYPE_WINAIM, 
59                  3, {0x01, 0x01, 0x02}},
60
61                /* WinAIM 4.1.2010, libfaim */
62                { AIM_CLIENTTYPE_WINAIM41,
63                  4, {0x01, 0x01, 0x01, 0x02}},
64
65                /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */
66                { AIM_CLIENTTYPE_AOL_TOC,
67                  1, {0x01}},
68
69                { 0, 0}
70        };
71        int i;
72
73        if (!msghdr || (len <= 0))
74                return AIM_CLIENTTYPE_UNKNOWN;
75
76        for (i = 0; fingerprints[i].len; i++) {
77                if (fingerprints[i].len != len)
78                        continue;
79                if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0)
80                        return fingerprints[i].clientid;
81        }
82
83        return AIM_CLIENTTYPE_UNKNOWN;
84}
85
86/* This should be endian-safe now... but who knows... */
87faim_export fu16_t aim_iconsum(const fu8_t *buf, int buflen)
88{
89        fu32_t sum;
90        int i;
91
92        for (i = 0, sum = 0; i + 1 < buflen; i += 2)
93                sum += (buf[i+1] << 8) + buf[i];
94        if (i < buflen)
95                sum += buf[i];
96
97        sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff);
98
99        return (fu16_t)sum;
100}
101
102/*
103 * Send an ICBM (instant message). 
104 *
105 *
106 * Possible flags:
107 *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
108 *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
109 *                        when the message is received (of type 0x0004/0x000c)
110 *   AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are
111 *                        online (probably ICQ only).
112 *   AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is
113 *                        made up of UNICODE duples.  If you set
114 *                        this, you'd better be damn sure you know
115 *                        what you're doing.
116 *   AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset
117 *                        known as ISO-8859-1. 
118 *
119 * Generally, you should use the lowest encoding possible to send
120 * your message.  If you only use basic punctuation and the generic
121 * Latin alphabet, use ASCII7 (no flags).  If you happen to use non-ASCII7
122 * characters, but they are all clearly defined in ISO-8859-1, then
123 * use that.  Keep in mind that not all characters in the PC ASCII8
124 * character set are defined in the ISO standard. For those cases (most
125 * notably when the (r) symbol is used), you must use the full UNICODE
126 * encoding for your message.  In UNICODE mode, _all_ characters must
127 * occupy 16bits, including ones that are not special.  (Remember that
128 * the first 128 UNICODE symbols are equivelent to ASCII7, however they
129 * must be prefixed with a zero high order byte.)
130 *
131 * I strongly discourage the use of UNICODE mode, mainly because none
132 * of the clients I use can parse those messages (and besides that,
133 * wchars are difficult and non-portable to handle in most UNIX environments).
134 * If you really need to include special characters, use the HTML UNICODE
135 * entities.  These are of the form &#2026; where 2026 is the hex
136 * representation of the UNICODE index (in this case, UNICODE
137 * "Horizontal Ellipsis", or 133 in in ASCII8).
138 *
139 * Implementation note:  Since this is one of the most-used functions
140 * in all of libfaim, it is written with performance in mind.  As such,
141 * it is not as clear as it could be in respect to how this message is
142 * supposed to be layed out. Most obviously, tlvlists should be used
143 * instead of writing out the bytes manually.
144 *
145 * XXX more precise verification that we never send SNACs larger than 8192
146 * XXX check SNAC size for multipart
147 *
148 */
149faim_export int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args)
150{
151        static const fu8_t deffeatures[] = {
152                0x01, 0x01, 0x01, 0x02
153        };
154        aim_conn_t *conn;
155        int i, msgtlvlen;
156        aim_frame_t *fr;
157        aim_snacid_t snacid;
158
159        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
160                return -EINVAL;
161
162        if (!args)
163                return -EINVAL;
164
165        if (args->flags & AIM_IMFLAGS_MULTIPART) {
166                if (args->mpmsg->numparts <= 0)
167                        return -EINVAL;
168        } else {
169                if (!args->msg || (args->msglen <= 0))
170                        return -EINVAL;
171
172                if (args->msglen >= MAXMSGLEN)
173                        return -E2BIG;
174        }
175
176        /* Painfully calculate the size of the message TLV */
177        msgtlvlen = 1 + 1; /* 0501 */
178
179        if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES)
180                msgtlvlen += 2 + args->featureslen;
181        else
182                msgtlvlen += 2 + sizeof(deffeatures);
183
184        if (args->flags & AIM_IMFLAGS_MULTIPART) {
185                aim_mpmsg_section_t *sec;
186
187                for (sec = args->mpmsg->parts; sec; sec = sec->next) {
188                        msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
189                        msgtlvlen += 4 /* charset */ + sec->datalen;
190                }
191
192        } else {
193                msgtlvlen += 2 /* 0101 */ + 2 /* block len */;
194                msgtlvlen += 4 /* charset */ + args->msglen;
195        }
196
197
198        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128)))
199                return -ENOMEM;
200
201        /* XXX should be optional */   
202        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1);
203        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
204
205        /*
206         * Generate a random message cookie
207         *
208         * We could cache these like we do SNAC IDs.  (In fact, it
209         * might be a good idea.)  In the message error functions,
210         * the 8byte message cookie is returned as well as the
211         * SNAC ID.
212         *
213         */
214        for (i = 0; i < 8; i++)
215                aimbs_put8(&fr->data, (fu8_t) rand());
216
217        /*
218         * Channel ID
219         */
220        aimbs_put16(&fr->data, 0x0001);
221
222        /*
223         * Destination SN (prepended with byte length)
224         */
225        aimbs_put8(&fr->data, strlen(args->destsn));
226        aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
227
228        /*
229         * Message TLV (type 2).
230         */
231        aimbs_put16(&fr->data, 0x0002);
232        aimbs_put16(&fr->data, msgtlvlen);
233
234        /*
235         * Features
236         *
237         */
238        aimbs_put8(&fr->data, 0x05);
239        aimbs_put8(&fr->data, 0x01);
240
241        if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) {
242                aimbs_put16(&fr->data, args->featureslen);
243                aimbs_putraw(&fr->data, args->features, args->featureslen);
244        } else {
245                aimbs_put16(&fr->data, sizeof(deffeatures));
246                aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures));
247        }
248
249        if (args->flags & AIM_IMFLAGS_MULTIPART) {
250                aim_mpmsg_section_t *sec;
251
252                for (sec = args->mpmsg->parts; sec; sec = sec->next) {
253                        aimbs_put16(&fr->data, 0x0101);
254                        aimbs_put16(&fr->data, sec->datalen + 4);
255                        aimbs_put16(&fr->data, sec->charset);
256                        aimbs_put16(&fr->data, sec->charsubset);
257                        aimbs_putraw(&fr->data, sec->data, sec->datalen);
258                }
259
260        } else {
261
262                aimbs_put16(&fr->data, 0x0101);
263
264                /*
265                 * Message block length.
266                 */
267                aimbs_put16(&fr->data, args->msglen + 0x04);
268
269                /*
270                 * Character set.
271                 */
272                if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) {
273
274                        aimbs_put16(&fr->data, args->charset);
275                        aimbs_put16(&fr->data, args->charsubset);
276
277                } else {
278                        if (args->flags & AIM_IMFLAGS_UNICODE)
279                                aimbs_put16(&fr->data, 0x0002);
280                        else if (args->flags & AIM_IMFLAGS_ISO_8859_1)
281                                aimbs_put16(&fr->data, 0x0003);
282                        else
283                                aimbs_put16(&fr->data, 0x0000);
284
285                        aimbs_put16(&fr->data, 0x0000);
286                }
287
288                /*
289                 * Message.  Not terminated.
290                 */
291                aimbs_putraw(&fr->data, args->msg, args->msglen);
292        }
293
294        /*
295         * Set the Request Acknowledge flag. 
296         */
297        if (args->flags & AIM_IMFLAGS_ACK) {
298                aimbs_put16(&fr->data, 0x0003);
299                aimbs_put16(&fr->data, 0x0000);
300        }
301
302        /*
303         * Set the Autoresponse flag.
304         */
305        if (args->flags & AIM_IMFLAGS_AWAY) {
306                aimbs_put16(&fr->data, 0x0004);
307                aimbs_put16(&fr->data, 0x0000);
308        }
309
310        if (args->flags & AIM_IMFLAGS_OFFLINE) {
311                aimbs_put16(&fr->data, 0x0006);
312                aimbs_put16(&fr->data, 0x0000);
313        }
314
315        /*
316         * Set the I HAVE A REALLY PURTY ICON flag.
317         */
318        if (args->flags & AIM_IMFLAGS_HASICON) {
319                aimbs_put16(&fr->data, 0x0008);
320                aimbs_put16(&fr->data, 0x000c);
321                aimbs_put32(&fr->data, args->iconlen);
322                aimbs_put16(&fr->data, 0x0001);
323                aimbs_put16(&fr->data, args->iconsum);
324                aimbs_put32(&fr->data, args->iconstamp);
325        }
326
327        /*
328         * Set the Buddy Icon Requested flag.
329         */
330        if (args->flags & AIM_IMFLAGS_BUDDYREQ) {
331                aimbs_put16(&fr->data, 0x0009);
332                aimbs_put16(&fr->data, 0x0000);
333        }
334
335        aim_tx_enqueue(sess, fr);
336
337        if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM))
338                aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */
339
340        return 0;
341}
342
343/*
344 * Simple wrapper for aim_send_im_ext()
345 *
346 * You cannot use aim_send_im if you need the HASICON flag.  You must
347 * use aim_send_im_ext directly for that.
348 *
349 * aim_send_im also cannot be used if you require UNICODE messages, because
350 * that requires an explicit message length.  Use aim_send_im_ext().
351 *
352 */
353faim_export int aim_send_im(aim_session_t *sess, const char *destsn, fu16_t flags, const char *msg)
354{
355        struct aim_sendimext_args args;
356
357        args.destsn = destsn;
358        args.flags = flags;
359        args.msg = msg;
360        args.msglen = strlen(msg);
361
362        /* Make these don't get set by accident -- they need aim_send_im_ext */
363        args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART);
364
365        return aim_send_im_ext(sess, &args);
366}
367
368/*
369 * This is also performance sensitive. (If you can believe it...)
370 *
371 */
372faim_export int aim_send_icon(aim_session_t *sess, const char *sn, const fu8_t *icon, int iconlen, time_t stamp, fu16_t iconsum)
373{
374        aim_conn_t *conn;
375        int i;
376        fu8_t ck[8];
377        aim_frame_t *fr;
378        aim_snacid_t snacid;
379
380        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
381                return -EINVAL;
382
383        if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN))
384                return -EINVAL;
385
386        for (i = 0; i < 8; i++)
387                aimutil_put8(ck+i, (fu8_t) rand());
388
389        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2)))
390                return -ENOMEM;
391
392        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
393        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
394
395        /*
396         * Cookie
397         */
398        aimbs_putraw(&fr->data, ck, 8);
399
400        /*
401         * Channel (2)
402         */
403        aimbs_put16(&fr->data, 0x0002);
404
405        /*
406         * Dest sn
407         */
408        aimbs_put8(&fr->data, strlen(sn));
409        aimbs_putraw(&fr->data, sn, strlen(sn));
410
411        /*
412         * TLV t(0005)
413         *
414         * Encompasses everything below.
415         */
416        aimbs_put16(&fr->data, 0x0005);
417        aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT));
418
419        aimbs_put16(&fr->data, 0x0000);
420        aimbs_putraw(&fr->data, ck, 8);
421        aim_putcap(&fr->data, AIM_CAPS_BUDDYICON);
422
423        /* TLV t(000a) */
424        aimbs_put16(&fr->data, 0x000a);
425        aimbs_put16(&fr->data, 0x0002);
426        aimbs_put16(&fr->data, 0x0001);
427
428        /* TLV t(000f) */
429        aimbs_put16(&fr->data, 0x000f);
430        aimbs_put16(&fr->data, 0x0000);
431
432        /* TLV t(2711) */
433        aimbs_put16(&fr->data, 0x2711);
434        aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT));
435        aimbs_put16(&fr->data, 0x0000);
436        aimbs_put16(&fr->data, iconsum);
437        aimbs_put32(&fr->data, iconlen);
438        aimbs_put32(&fr->data, stamp);
439        aimbs_putraw(&fr->data, icon, iconlen);
440        aimbs_putraw(&fr->data, AIM_ICONIDENT, strlen(AIM_ICONIDENT));
441
442        /* TLV t(0003) */
443        aimbs_put16(&fr->data, 0x0003);
444        aimbs_put16(&fr->data, 0x0000);
445
446        aim_tx_enqueue(sess, fr);
447
448        return 0;
449}
450
451/*
452 * This only works for ICQ 2001b (thats 2001 not 2000).  Better, only
453 * send it to clients advertising the RTF capability.  In fact, if you send
454 * it to a client that doesn't support that capability, the server will gladly
455 * bounce it back to you.
456 *
457 * You'd think this would be in icq.c, but, well, I'm trying to stick with
458 * the one-group-per-file scheme as much as possible.  This could easily
459 * be an exception, since Rendezvous IMs are external of the Oscar core,
460 * and therefore are undefined.  Really I just need to think of a good way to
461 * make an interface similar to what AOL actually uses.  But I'm not using COM.
462 *
463 */
464faim_export int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args)
465{
466        const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */
467        aim_conn_t *conn;
468        int i;
469        fu8_t ck[8];
470        aim_frame_t *fr;
471        aim_snacid_t snacid;
472        int servdatalen;
473
474        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
475                return -EINVAL;
476
477        if (!args || !args->destsn || !args->rtfmsg)
478                return -EINVAL;
479
480        servdatalen = 2+2+16+2+4+1+2  +  2+2+4+4+4  +  2+4+2+strlen(args->rtfmsg)+1  +  4+4+4+strlen(rtfcap)+1;
481
482        for (i = 0; i < 8; i++)
483                aimutil_put8(ck+i, (fu8_t) rand());
484
485        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen)))
486                return -ENOMEM;
487
488        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
489        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
490
491        /*
492         * Cookie
493         */
494        aimbs_putraw(&fr->data, ck, 8);
495
496        /*
497         * Channel (2)
498         */
499        aimbs_put16(&fr->data, 0x0002);
500
501        /*
502         * Dest sn
503         */
504        aimbs_put8(&fr->data, strlen(args->destsn));
505        aimbs_putraw(&fr->data, args->destsn, strlen(args->destsn));
506
507        /*
508         * TLV t(0005)
509         *
510         * Encompasses everything below.
511         */
512        aimbs_put16(&fr->data, 0x0005);
513        aimbs_put16(&fr->data, 2+8+16  +  2+2+2  +  2+2  +  2+2+servdatalen);
514
515        aimbs_put16(&fr->data, 0x0000);
516        aimbs_putraw(&fr->data, ck, 8);
517        aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY);
518
519        /*
520         * t(000a) l(0002) v(0001)
521         */
522        aimbs_put16(&fr->data, 0x000a);
523        aimbs_put16(&fr->data, 0x0002);
524        aimbs_put16(&fr->data, 0x0001);
525
526        /*
527         * t(000f) l(0000) v()
528         */
529        aimbs_put16(&fr->data, 0x000f);
530        aimbs_put16(&fr->data, 0x0000);
531
532        /*
533         * Service Data TLV
534         */
535        aimbs_put16(&fr->data, 0x2711);
536        aimbs_put16(&fr->data, servdatalen);
537
538        aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */);
539        aimbs_putle16(&fr->data, 9);
540        aim_putcap(&fr->data, AIM_CAPS_EMPTY);
541        aimbs_putle16(&fr->data, 0);
542        aimbs_putle32(&fr->data, 0);
543        aimbs_putle8(&fr->data, 0);
544        aimbs_putle16(&fr->data, 0x03ea); /* trid1 */
545
546        aimbs_putle16(&fr->data, 14);
547        aimbs_putle16(&fr->data, 0x03eb); /* trid2 */
548        aimbs_putle32(&fr->data, 0);
549        aimbs_putle32(&fr->data, 0);
550        aimbs_putle32(&fr->data, 0);
551
552        aimbs_putle16(&fr->data, 0x0001);
553        aimbs_putle32(&fr->data, 0);
554        aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1);
555        aimbs_putraw(&fr->data, args->rtfmsg, strlen(args->rtfmsg)+1);
556
557        aimbs_putle32(&fr->data, args->fgcolor);
558        aimbs_putle32(&fr->data, args->bgcolor);
559        aimbs_putle32(&fr->data, strlen(rtfcap)+1);
560        aimbs_putraw(&fr->data, rtfcap, strlen(rtfcap)+1);
561
562        aim_tx_enqueue(sess, fr);
563
564        return 0;
565}
566
567faim_internal int aim_request_directim(aim_session_t *sess, const char *destsn, fu8_t *ip, fu16_t port, fu8_t *ckret)
568{
569        aim_conn_t *conn;
570        fu8_t ck[8];
571        aim_frame_t *fr;
572        aim_snacid_t snacid;
573        aim_tlvlist_t *tl = NULL, *itl = NULL;
574        int hdrlen, i;
575        fu8_t *hdr;
576        aim_bstream_t hdrbs;
577
578        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
579                return -EINVAL;
580
581        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn))))
582                return -ENOMEM;
583
584        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
585        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
586
587        /*
588         * Generate a random message cookie
589         *
590         * This cookie needs to be alphanumeric and NULL-terminated to be
591         * TOC-compatible.
592         *
593         * XXX have I mentioned these should be generated in msgcookie.c?
594         *
595         */
596        for (i = 0; i < 7; i++)
597                ck[i] = 0x30 + ((fu8_t) rand() % 10);
598        ck[7] = '\0';
599
600        if (ckret)
601                memcpy(ckret, ck, 8);
602
603        /* Cookie */
604        aimbs_putraw(&fr->data, ck, 8);
605
606        /* Channel */
607        aimbs_put16(&fr->data, 0x0002);
608
609        /* Destination SN */
610        aimbs_put8(&fr->data, strlen(destsn));
611        aimbs_putraw(&fr->data, destsn, strlen(destsn));
612
613        aim_addtlvtochain_noval(&tl, 0x0003);
614
615        hdrlen = 2+8+16+6+8+6+4;
616        hdr = malloc(hdrlen);
617        aim_bstream_init(&hdrbs, hdr, hdrlen);
618
619        aimbs_put16(&hdrbs, 0x0000);
620        aimbs_putraw(&hdrbs, ck, 8);
621        aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE);
622
623        aim_addtlvtochain16(&itl, 0x000a, 0x0001);
624        aim_addtlvtochain_raw(&itl, 0x0003, 4, ip);
625        aim_addtlvtochain16(&itl, 0x0005, port);
626        aim_addtlvtochain_noval(&itl, 0x000f);
627       
628        aim_writetlvchain(&hdrbs, &itl);
629
630        aim_addtlvtochain_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
631
632        aim_writetlvchain(&fr->data, &tl);
633
634        free(hdr);
635        aim_freetlvchain(&itl);
636        aim_freetlvchain(&tl);
637
638        aim_tx_enqueue(sess, fr);
639
640        return 0;
641}
642
643faim_internal int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, fu16_t numfiles, fu32_t totsize, fu8_t *ip, fu16_t port, fu8_t *ckret)
644{
645        aim_conn_t *conn;
646        int i;
647        fu8_t ck[8];
648        aim_frame_t *fr;
649        aim_snacid_t snacid;
650
651        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
652                return -EINVAL;
653
654        if (!sn || !filename)
655                return -EINVAL;
656
657        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4)))
658                return -ENOMEM;
659
660        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0);
661        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
662
663        for (i = 0; i < 7; i++)
664                aimutil_put8(ck+i, 0x30 + ((fu8_t) rand() % 10));
665        ck[7] = '\0';
666
667        if (ckret)
668                memcpy(ckret, ck, 8);
669
670        /*
671         * Cookie
672         */
673        aimbs_putraw(&fr->data, ck, 8);
674
675        /*
676         * Channel (2)
677         */
678        aimbs_put16(&fr->data, 0x0002);
679
680        /*
681         * Dest sn
682         */
683        aimbs_put8(&fr->data, strlen(sn));
684        aimbs_putraw(&fr->data, sn, strlen(sn));
685
686        /*
687         * TLV t(0005)
688         *
689         * Encompasses everything below. Gee.
690         */
691        aimbs_put16(&fr->data, 0x0005);
692        aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4);
693
694        aimbs_put16(&fr->data, 0x0000);
695        aimbs_putraw(&fr->data, ck, 8);
696        aim_putcap(&fr->data, AIM_CAPS_SENDFILE);
697
698        /* TLV t(000a) */
699        aimbs_put16(&fr->data, 0x000a);
700        aimbs_put16(&fr->data, 0x0002);
701        aimbs_put16(&fr->data, 0x0001);
702
703        /* TLV t(0003) (IP) */
704        aimbs_put16(&fr->data, 0x0003);
705        aimbs_put16(&fr->data, 0x0004);
706        aimbs_putraw(&fr->data, ip, 4);
707
708        /* TLV t(0005) (port) */
709        aimbs_put16(&fr->data, 0x0005);
710        aimbs_put16(&fr->data, 0x0002);
711        aimbs_put16(&fr->data, port);
712
713        /* TLV t(000f) */
714        aimbs_put16(&fr->data, 0x000f);
715        aimbs_put16(&fr->data, 0x0000);
716
717        /* TLV t(2711) */
718        aimbs_put16(&fr->data, 0x2711);
719        aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4);
720
721        /* ? */
722        aimbs_put16(&fr->data, 0x0001);
723        aimbs_put16(&fr->data, numfiles);
724        aimbs_put32(&fr->data, totsize);
725        aimbs_putraw(&fr->data, filename, strlen(filename));
726
727        /* ? */
728        aimbs_put32(&fr->data, 0x00000000);
729
730        aim_tx_enqueue(sess, fr);
731
732        return 0;
733}
734
735static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
736{
737        int i, ret = 0;
738        aim_rxcallback_t userfunc;
739        fu8_t cookie[8];
740        fu16_t channel;
741        aim_tlvlist_t *tlvlist;
742        char *sn;
743        int snlen;
744        fu16_t icbmflags = 0;
745        fu8_t flag1 = 0, flag2 = 0;
746        fu8_t *msg = NULL;
747        aim_tlv_t *msgblock;
748
749        /* ICBM Cookie. */
750        for (i = 0; i < 8; i++)
751                cookie[i] = aimbs_get8(bs);
752
753        /* Channel ID */
754        channel = aimbs_get16(bs);
755
756        if (channel != 0x01) {
757                faimdprintf(sess, 0, "icbm: ICBM recieved on unsupported channel.  Ignoring. (chan = %04x)\n", channel);
758                return 0;
759        }
760
761        snlen = aimbs_get8(bs);
762        sn = aimbs_getstr(bs, snlen);
763
764        tlvlist = aim_readtlvchain(bs);
765
766        if (aim_gettlv(tlvlist, 0x0003, 1))
767                icbmflags |= AIM_IMFLAGS_ACK;
768        if (aim_gettlv(tlvlist, 0x0004, 1))
769                icbmflags |= AIM_IMFLAGS_AWAY;
770
771        if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) {
772                aim_bstream_t mbs;
773                int featurelen, msglen;
774
775                aim_bstream_init(&mbs, msgblock->value, msgblock->length);
776
777                aimbs_get8(&mbs);
778                aimbs_get8(&mbs);
779                for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--)
780                        aimbs_get8(&mbs);
781                aimbs_get8(&mbs);
782                aimbs_get8(&mbs);
783
784                msglen = aimbs_get16(&mbs) - 4; /* final block length */
785
786                flag1 = aimbs_get16(&mbs);
787                flag2 = aimbs_get16(&mbs);
788
789                msg = aimbs_getstr(&mbs, msglen);
790        }
791
792        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
793                ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2);
794
795        free(sn);
796        aim_freetlvchain(&tlvlist);
797
798        return ret;
799}
800
801/*
802 * Ahh, the joys of nearly ridiculous over-engineering.
803 *
804 * Not only do AIM ICBM's support multiple channels.  Not only do they
805 * support multiple character sets.  But they support multiple character
806 * sets / encodings within the same ICBM.
807 *
808 * These multipart messages allow for complex space savings techniques, which
809 * seem utterly unnecessary by today's standards.  In fact, there is only
810 * one client still in popular use that still uses this method: AOL for the
811 * Macintosh, Version 5.0.  Obscure, yes, I know. 
812 *
813 * In modern (non-"legacy") clients, if the user tries to send a character
814 * that is not ISO-8859-1 or ASCII, the client will send the entire message
815 * as UNICODE, meaning that every character in the message will occupy the
816 * full 16 bit UNICODE field, even if the high order byte would be zero.
817 * Multipart messages prevent this wasted space by allowing the client to
818 * only send the characters in UNICODE that need to be sent that way, and
819 * the rest of the message can be sent in whatever the native character
820 * set is (probably ASCII).
821 *
822 * An important note is that sections will be displayed in the order that
823 * they appear in the ICBM.  There is no facility for merging or rearranging
824 * sections at run time.  So if you have, say, ASCII then UNICODE then ASCII,
825 * you must supply two ASCII sections with a UNICODE in the middle, and incur
826 * the associated overhead.
827 *
828 * Normally I would have laughed and given a firm 'no' to supporting this
829 * seldom-used feature, but something is attracting me to it.  In the future,
830 * it may be possible to abuse this to send mixed-media messages to other
831 * open source clients (like encryption or something) -- see faimtest for
832 * examples of how to do this.
833 *
834 * I would definitly recommend avoiding this feature unless you really
835 * know what you are doing, and/or you have something neat to do with it.
836 *
837 */
838faim_export int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm)
839{
840
841        memset(mpm, 0, sizeof(aim_mpmsg_t));
842
843        return 0;
844}
845
846static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, fu16_t charset, fu16_t charsubset, fu8_t *data, fu16_t datalen)
847{
848        aim_mpmsg_section_t *sec; 
849       
850        if (!(sec = malloc(sizeof(aim_mpmsg_section_t))))
851                return -1;
852
853        sec->charset = charset;
854        sec->charsubset = charsubset;
855        sec->data = data;
856        sec->datalen = datalen;
857        sec->next = NULL;
858
859        if (!mpm->parts)
860                mpm->parts = sec;
861        else {
862                aim_mpmsg_section_t *cur;
863
864                for (cur = mpm->parts; cur->next; cur = cur->next)
865                        ;
866                cur->next = sec;
867        }
868
869        mpm->numparts++;
870
871        return 0;
872}
873
874faim_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)
875{
876        fu8_t *dup;
877
878        if (!(dup = malloc(datalen)))
879                return -1;
880        memcpy(dup, data, datalen);
881
882        if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) {
883                free(dup);
884                return -1;
885        }
886
887        return 0;
888}
889
890/* XXX should provide a way of saying ISO-8859-1 specifically */
891faim_export int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii)
892{
893        fu8_t *dup;
894
895        if (!(dup = strdup(ascii))) 
896                return -1;
897
898        if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, dup, strlen(ascii)) == -1) {
899                free(dup);
900                return -1;
901        }
902
903        return 0;
904}
905
906faim_export int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const fu16_t *unicode, fu16_t unicodelen)
907{
908        fu8_t *buf;
909        aim_bstream_t bs;
910        int i;
911
912        if (!(buf = malloc(unicodelen * 2)))
913                return -1;
914
915        aim_bstream_init(&bs, buf, unicodelen * 2);
916
917        /* We assume unicode is in /host/ byte order -- convert to network */
918        for (i = 0; i < unicodelen; i++)
919                aimbs_put16(&bs, unicode[i]);
920
921        if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) {
922                free(buf);
923                return -1;
924        }
925       
926        return 0;
927}
928
929faim_export void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm)
930{
931        aim_mpmsg_section_t *cur;
932
933        for (cur = mpm->parts; cur; ) {
934                aim_mpmsg_section_t *tmp;
935               
936                tmp = cur->next;
937                free(cur->data);
938                free(cur);
939                cur = tmp;
940        }
941       
942        mpm->numparts = 0;
943        mpm->parts = NULL;
944
945        return;
946}
947
948/*
949 * Start by building the multipart structures, then pick the first
950 * human-readable section and stuff it into args->msg so no one gets
951 * suspicious.
952 *
953 */
954static int incomingim_ch1_parsemsgs(aim_session_t *sess, fu8_t *data, int len, struct aim_incomingim_ch1_args *args)
955{
956        static const fu16_t charsetpri[] = {
957                0x0000, /* ASCII first */
958                0x0003, /* then ISO-8859-1 */
959                0x0002, /* UNICODE as last resort */
960        };
961        static const int charsetpricount = 3;
962        int i;
963        aim_bstream_t mbs;
964        aim_mpmsg_section_t *sec;
965
966        aim_bstream_init(&mbs, data, len);
967
968        while (aim_bstream_empty(&mbs)) {
969                fu16_t msglen, flag1, flag2;
970                fu8_t *msgbuf;
971
972                aimbs_get8(&mbs); /* 01 */
973                aimbs_get8(&mbs); /* 01 */
974
975                /* Message string length, including character set info. */
976                msglen = aimbs_get16(&mbs);
977
978                /* Character set info */
979                flag1 = aimbs_get16(&mbs);
980                flag2 = aimbs_get16(&mbs);
981
982                /* Message. */
983                msglen -= 4;
984
985                /*
986                 * For now, we don't care what the encoding is.  Just copy
987                 * it into a multipart struct and deal with it later. However,
988                 * always pad the ending with a NULL.  This makes it easier
989                 * to treat ASCII sections as strings.  It won't matter for
990                 * UNICODE or binary data, as you should never read past
991                 * the specified data length, which will not include the pad.
992                 *
993                 * XXX There's an API bug here.  For sending, the UNICODE is
994                 * given in host byte order (aim_mpmsg_addunicode), but here
995                 * the received messages are given in network byte order.
996                 *
997                 */
998                msgbuf = aimbs_getstr(&mbs, msglen);
999                mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, msgbuf, msglen);
1000
1001        } /* while */
1002
1003        args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */
1004
1005        /*
1006         * Clients that support multiparts should never use args->msg, as it
1007         * will point to an arbitrary section.
1008         *
1009         * Here, we attempt to provide clients that do not support multipart
1010         * messages with something to look at -- hopefully a human-readable
1011         * string.  But, failing that, a UNICODE message, or nothing at all.
1012         *
1013         * Which means that even if args->msg is NULL, it does not mean the
1014         * message was blank.
1015         *
1016         */
1017        for (i = 0; i < charsetpricount; i++) {
1018                for (sec = args->mpmsg.parts; sec; sec = sec->next) {
1019
1020                        if (sec->charset != charsetpri[i])
1021                                continue;
1022
1023                        /* Great. We found one.  Fill it in. */
1024                        args->charset = sec->charset;
1025                        args->charsubset = sec->charsubset;
1026                        args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET;
1027
1028                        /* Set up the simple flags */
1029                        if (args->charset == 0x0000)
1030                                ; /* ASCII */
1031                        else if (args->charset == 0x0002)
1032                                args->icbmflags |= AIM_IMFLAGS_UNICODE;
1033                        else if (args->charset == 0x0003)
1034                                args->icbmflags |= AIM_IMFLAGS_ISO_8859_1;
1035                        else if (args->charset == 0xffff)
1036                                ; /* no encoding (yeep!) */
1037
1038                        if (args->charsubset == 0x0000)
1039                                ; /* standard subencoding? */
1040                        else if (args->charsubset == 0x000b)
1041                                args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH;
1042                        else if (args->charsubset == 0xffff)
1043                                ; /* no subencoding */
1044#if 0
1045                        /* XXX this isn't really necesary... */
1046                        if (    ((args.flag1 != 0x0000) &&
1047                                 (args.flag1 != 0x0002) &&
1048                                 (args.flag1 != 0x0003) &&
1049                                 (args.flag1 != 0xffff)) ||
1050                                ((args.flag2 != 0x0000) &&
1051                                 (args.flag2 != 0x000b) &&
1052                                 (args.flag2 != 0xffff))) {
1053                                faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2);
1054                        }
1055#endif
1056
1057                        args->msg = sec->data;
1058                        args->msglen = sec->datalen;
1059
1060                        return 0;
1061                }
1062        }
1063
1064        /* No human-readable sections found.  Oh well. */
1065        args->charset = args->charsubset = 0xffff;
1066        args->msg = NULL;
1067        args->msglen = 0;
1068
1069        return 0;
1070}
1071
1072static 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)
1073{
1074        fu16_t type, length;
1075        aim_rxcallback_t userfunc;
1076        int ret = 0;
1077        struct aim_incomingim_ch1_args args;
1078        int endpos;
1079
1080        memset(&args, 0, sizeof(args));
1081
1082        aim_mpmsg_init(sess, &args.mpmsg);
1083
1084        /*
1085         * This used to be done using tlvchains.  For performance reasons,
1086         * I've changed it to process the TLVs in-place.  This avoids lots
1087         * of per-IM memory allocations.
1088         */
1089        while (aim_bstream_empty(bs)) {
1090
1091                type = aimbs_get16(bs);
1092                length = aimbs_get16(bs);
1093
1094                endpos = aim_bstream_curpos(bs) + length;
1095
1096                if (type == 0x0002) { /* Message Block */
1097
1098                        /*
1099                         * This TLV consists of the following:
1100                         *   - 0501 -- Unknown
1101                         *   - Features: Don't know how to interpret these
1102                         *   - 0101 -- Unknown
1103                         *   - Message
1104                         *
1105                         */
1106
1107                        aimbs_get8(bs); /* 05 */
1108                        aimbs_get8(bs); /* 01 */
1109
1110                        args.featureslen = aimbs_get16(bs);
1111                        /* XXX XXX this is all evil! */
1112                        args.features = bs->data + bs->offset;
1113                        aim_bstream_advance(bs, args.featureslen);
1114                        args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES;
1115
1116                        /*
1117                         * The rest of the TLV contains one or more message
1118                         * blocks...
1119                         */
1120                        incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args);
1121
1122                } else if (type == 0x0003) { /* Server Ack Requested */
1123
1124                        args.icbmflags |= AIM_IMFLAGS_ACK;
1125
1126                } else if (type == 0x0004) { /* Message is Auto Response */
1127
1128                        args.icbmflags |= AIM_IMFLAGS_AWAY;
1129
1130                } else if (type == 0x0006) { /* Message was received offline. */
1131
1132                        /* XXX not sure if this actually gets sent. */
1133                        args.icbmflags |= AIM_IMFLAGS_OFFLINE;
1134
1135                } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */
1136
1137                        args.iconlen = aimbs_get32(bs);
1138                        aimbs_get16(bs); /* 0x0001 */
1139                        args.iconsum = aimbs_get16(bs);
1140                        args.iconstamp = aimbs_get32(bs);
1141
1142                        /*
1143                         * This looks to be a client bug.  MacAIM 4.3 will
1144                         * send this tag, but with all zero values, in the
1145                         * first message of a conversation. This makes no
1146                         * sense whatsoever, so I'm going to say its a bug.
1147                         *
1148                         * You really shouldn't advertise a zero-length icon
1149                         * anyway.
1150                         *
1151                         */
1152                        if (args.iconlen)
1153                                args.icbmflags |= AIM_IMFLAGS_HASICON;
1154
1155                } else if (type == 0x0009) {
1156
1157                        args.icbmflags |= AIM_IMFLAGS_BUDDYREQ;
1158
1159                } else if (type == 0x0017) {
1160
1161                        args.extdatalen = length;
1162                        args.extdata = aimbs_getraw(bs, args.extdatalen);
1163
1164                } else {
1165                        faimdprintf(sess, 0, "incomingim_ch1: unknown TLV 0x%04x (len %d)\n", type, length);
1166                }
1167
1168                /*
1169                 * This is here to protect ourselves from ourselves.  That
1170                 * is, if something above doesn't completly parse its value
1171                 * section, or, worse, overparses it, this will set the
1172                 * stream where it needs to be in order to land on the next
1173                 * TLV when the loop continues.
1174                 *
1175                 */
1176                aim_bstream_setpos(bs, endpos);
1177        }
1178
1179
1180        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1181                ret = userfunc(sess, rx, channel, userinfo, &args);
1182
1183        aim_mpmsg_free(sess, &args.mpmsg);
1184        free(args.extdata);
1185
1186        return ret;
1187}
1188
1189static void 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_bstream_t *servdata)
1190{
1191
1192        /*
1193         * This goes like this...
1194         *
1195         *   group name length
1196         *   group name
1197         *     num of buddies in group
1198         *     buddy name length
1199         *     buddy name
1200         *     buddy name length
1201         *     buddy name
1202         *     ...
1203         *   group name length
1204         *   group name
1205         *     num of buddies in group
1206         *     buddy name length
1207         *     buddy name
1208         *     ...
1209         *   ...
1210         */
1211        while (servdata && aim_bstream_empty(servdata)) {
1212                fu16_t gnlen, numb;
1213                int i;
1214                char *gn;
1215
1216                gnlen = aimbs_get16(servdata);
1217                gn = aimbs_getstr(servdata, gnlen);
1218                numb = aimbs_get16(servdata);
1219
1220                for (i = 0; i < numb; i++) {
1221                        fu16_t bnlen;
1222                        char *bn;
1223
1224                        bnlen = aimbs_get16(servdata);
1225                        bn = aimbs_getstr(servdata, bnlen);
1226
1227                        faimdprintf(sess, 0, "got a buddy list from %s: group %s, buddy %s\n", userinfo->sn, gn, bn);
1228
1229                        free(bn);
1230                }
1231
1232                free(gn);
1233        }
1234
1235        return;
1236}
1237
1238static void incomingim_ch2_buddyicon_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
1239{
1240
1241        free(args->info.icon.icon);
1242
1243        return;
1244}
1245
1246static void 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_bstream_t *servdata)
1247{
1248
1249        if (servdata) {
1250                args->info.icon.checksum = aimbs_get32(servdata);
1251                args->info.icon.length = aimbs_get32(servdata);
1252                args->info.icon.timestamp = aimbs_get32(servdata);
1253                args->info.icon.icon = aimbs_getraw(servdata, args->info.icon.length);
1254        }
1255
1256        args->destructor = (void *)incomingim_ch2_buddyicon_free;
1257
1258        return;
1259}
1260
1261static void incomingim_ch2_chat_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
1262{
1263
1264        /* XXX aim_chat_roominfo_free() */
1265        free(args->info.chat.roominfo.name);
1266
1267        return;
1268}
1269
1270static void 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_bstream_t *servdata)
1271{
1272
1273        /*
1274         * Chat room info.
1275         */
1276        if (servdata)
1277                aim_chat_readroominfo(servdata, &args->info.chat.roominfo);
1278
1279        args->destructor = (void *)incomingim_ch2_chat_free;
1280
1281        return;
1282}
1283
1284static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args)
1285{
1286
1287        free((char *)args->info.rtfmsg.rtfmsg);
1288
1289        return;
1290}
1291
1292/*
1293 * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is
1294 * kind of odd. This sends the client ICQRTF since that is all that I've seen
1295 * SERVERRELAY used for.
1296 *
1297 * Note that this is all little-endian.  Cringe.
1298 *
1299 */
1300static void incomingim_ch2_icqserverrelay(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_bstream_t *servdata)
1301{
1302        fu16_t hdrlen, anslen, msglen;
1303        fu16_t msgtype;
1304
1305        hdrlen = aimbs_getle16(servdata);
1306        aim_bstream_advance(servdata, hdrlen);
1307
1308        hdrlen = aimbs_getle16(servdata);
1309        aim_bstream_advance(servdata, hdrlen);
1310
1311        msgtype = aimbs_getle16(servdata);
1312       
1313        anslen = aimbs_getle32(servdata);
1314        aim_bstream_advance(servdata, anslen);
1315
1316        msglen = aimbs_getle16(servdata);
1317        args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen);
1318
1319        args->info.rtfmsg.fgcolor = aimbs_getle32(servdata);
1320        args->info.rtfmsg.bgcolor = aimbs_getle32(servdata);
1321
1322        hdrlen = aimbs_getle32(servdata);
1323        aim_bstream_advance(servdata, hdrlen);
1324
1325        /* XXX This is such a hack. */
1326        args->reqclass = AIM_CAPS_ICQRTF;
1327
1328        args->destructor = (void *)incomingim_ch2_icqserverrelay_free;
1329
1330        return;
1331}
1332
1333typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args);
1334
1335static 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)
1336{
1337        aim_rxcallback_t userfunc;
1338        aim_tlv_t *block1, *servdatatlv;
1339        aim_tlvlist_t *list2;
1340        struct aim_incomingim_ch2_args args;
1341        aim_bstream_t bbs, sdbs, *sdbsptr = NULL;
1342        fu8_t *cookie2;
1343        int ret = 0;
1344
1345        char clientip1[30] = {""};
1346        char clientip2[30] = {""};
1347        char verifiedip[30] = {""};
1348
1349        memset(&args, 0, sizeof(args));
1350
1351        /*
1352         * There's another block of TLVs embedded in the type 5 here.
1353         */
1354        block1 = aim_gettlv(tlvlist, 0x0005, 1);
1355        aim_bstream_init(&bbs, block1->value, block1->length);
1356
1357        /*
1358         * First two bytes represent the status of the connection.
1359         *
1360         * 0 is a request, 1 is a deny (?), 2 is an accept
1361         */ 
1362        args.status = aimbs_get16(&bbs);
1363
1364        /*
1365         * Next comes the cookie.  Should match the ICBM cookie.
1366         */
1367        cookie2 = aimbs_getraw(&bbs, 8);
1368        if (memcmp(cookie, cookie2, 8) != 0) 
1369                faimdprintf(sess, 0, "rend: warning cookies don't match!\n");
1370        memcpy(args.cookie, cookie2, 8);
1371        free(cookie2);
1372
1373        /*
1374         * The next 16bytes are a capability block so we can
1375         * identify what type of rendezvous this is.
1376         */
1377        args.reqclass = aim_getcap(sess, &bbs, 0x10);
1378
1379        /*
1380         * What follows may be TLVs or nothing, depending on the
1381         * purpose of the message.
1382         *
1383         * Ack packets for instance have nothing more to them.
1384         */
1385        list2 = aim_readtlvchain(&bbs);
1386
1387        /*
1388         * IP address from the perspective of the client.
1389         */
1390        if (aim_gettlv(list2, 0x0002, 1)) {
1391                aim_tlv_t *iptlv;
1392
1393                iptlv = aim_gettlv(list2, 0x0002, 1);
1394
1395                snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d",
1396                                aimutil_get8(iptlv->value+0),
1397                                aimutil_get8(iptlv->value+1),
1398                                aimutil_get8(iptlv->value+2),
1399                                aimutil_get8(iptlv->value+3));
1400        }
1401
1402        /*
1403         * Secondary IP address from the perspective of the client.
1404         */
1405        if (aim_gettlv(list2, 0x0003, 1)) {
1406                aim_tlv_t *iptlv;
1407
1408                iptlv = aim_gettlv(list2, 0x0003, 1);
1409
1410                snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d",
1411                                aimutil_get8(iptlv->value+0),
1412                                aimutil_get8(iptlv->value+1),
1413                                aimutil_get8(iptlv->value+2),
1414                                aimutil_get8(iptlv->value+3));
1415        }
1416
1417        /*
1418         * Verified IP address (from the perspective of Oscar).
1419         *
1420         * This is added by the server.
1421         */
1422        if (aim_gettlv(list2, 0x0004, 1)) {
1423                aim_tlv_t *iptlv;
1424
1425                iptlv = aim_gettlv(list2, 0x0004, 1);
1426
1427                snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d",
1428                                aimutil_get8(iptlv->value+0),
1429                                aimutil_get8(iptlv->value+1),
1430                                aimutil_get8(iptlv->value+2),
1431                                aimutil_get8(iptlv->value+3));
1432        }
1433
1434        /*
1435         * Port number for something.
1436         */
1437        if (aim_gettlv(list2, 0x0005, 1))
1438                args.port = aim_gettlv16(list2, 0x0005, 1);
1439
1440        /*
1441         * Error code.
1442         */
1443        if (aim_gettlv(list2, 0x000b, 1))
1444                args.errorcode = aim_gettlv16(list2, 0x000b, 1);
1445
1446        /*
1447         * Invitation message / chat description.
1448         */
1449        if (aim_gettlv(list2, 0x000c, 1))
1450                args.msg = aim_gettlv_str(list2, 0x000c, 1);
1451
1452        /*
1453         * Character set.
1454         */
1455        if (aim_gettlv(list2, 0x000d, 1))
1456                args.encoding = aim_gettlv_str(list2, 0x000d, 1);
1457       
1458        /*
1459         * Language.
1460         */
1461        if (aim_gettlv(list2, 0x000e, 1))
1462                args.language = aim_gettlv_str(list2, 0x000e, 1);
1463
1464        /* Unknown -- two bytes = 0x0001 */
1465        if (aim_gettlv(list2, 0x000a, 1))
1466                ;
1467
1468        /* Unknown -- no value */
1469        if (aim_gettlv(list2, 0x000f, 1))
1470                ;
1471
1472        if (strlen(clientip1))
1473                args.clientip = (char *)clientip1;
1474        if (strlen(clientip2))
1475                args.clientip2 = (char *)clientip2;
1476        if (strlen(verifiedip))
1477                args.verifiedip = (char *)verifiedip;
1478
1479        /*
1480         * This is must be present in PROPOSALs, but will probably not
1481         * exist in CANCELs and ACCEPTs.
1482         *
1483         * Service Data blocks are module-specific in format.
1484         */
1485        if ((servdatatlv = aim_gettlv(list2, 0x2711 /* 10001 */, 1))) {
1486
1487                aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length);
1488                sdbsptr = &sdbs;
1489        }
1490
1491        /*
1492         * The rest of the handling depends on what type it is.
1493         *
1494         * Not all of them have special handling (yet).
1495         */
1496        if (args.reqclass & AIM_CAPS_BUDDYICON)
1497                incomingim_ch2_buddyicon(sess, mod, rx, snac, userinfo, &args, sdbsptr);
1498        else if (args.reqclass & AIM_CAPS_SENDBUDDYLIST)
1499                incomingim_ch2_buddylist(sess, mod, rx, snac, userinfo, &args, sdbsptr);
1500        else if (args.reqclass & AIM_CAPS_CHAT)
1501                incomingim_ch2_chat(sess, mod, rx, snac, userinfo, &args, sdbsptr);
1502        else if (args.reqclass & AIM_CAPS_ICQSERVERRELAY)
1503                incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr);
1504
1505
1506        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1507                ret = userfunc(sess, rx, channel, userinfo, &args);
1508
1509
1510        if (args.destructor)
1511                ((ch2_args_destructor_t)args.destructor)(sess, &args);
1512
1513        free((char *)args.msg);
1514        free((char *)args.encoding);
1515        free((char *)args.language);
1516
1517        aim_freetlvchain(&list2);
1518
1519        return ret;
1520}
1521
1522/*
1523 * It can easily be said that parsing ICBMs is THE single
1524 * most difficult thing to do in the in AIM protocol.  In
1525 * fact, I think I just did say that.
1526 *
1527 * Below is the best damned solution I've come up with
1528 * over the past sixteen months of battling with it. This
1529 * can parse both away and normal messages from every client
1530 * I have access to.  Its not fast, its not clean.  But it works.
1531 *
1532 */
1533static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1534{
1535        int i, ret = 0;
1536        fu8_t cookie[8];
1537        fu16_t channel;
1538        aim_userinfo_t userinfo;
1539
1540        memset(&userinfo, 0x00, sizeof(aim_userinfo_t));
1541
1542        /*
1543         * Read ICBM Cookie.  And throw away.
1544         */
1545        for (i = 0; i < 8; i++)
1546                cookie[i] = aimbs_get8(bs);
1547
1548        /*
1549         * Channel ID.
1550         *
1551         * Channel 0x0001 is the message channel.  There are
1552         * other channels for things called "rendevous"
1553         * which represent chat and some of the other new
1554         * features of AIM2/3/3.5.
1555         *
1556         * Channel 0x0002 is the Rendevous channel, which
1557         * is where Chat Invitiations and various client-client
1558         * connection negotiations come from.
1559         *
1560         */
1561        channel = aimbs_get16(bs);
1562
1563        /*
1564         * Extract the standard user info block.
1565         *
1566         * Note that although this contains TLVs that appear contiguous
1567         * with the TLVs read below, they are two different pieces.  The
1568         * userinfo block contains the number of TLVs that contain user
1569         * information, the rest are not even though there is no seperation.
1570         * aim_extractuserinfo() returns the number of bytes used by the
1571         * userinfo tlvs, so you can start reading the rest of them right
1572         * afterward. 
1573         *
1574         * That also means that TLV types can be duplicated between the
1575         * userinfo block and the rest of the message, however there should
1576         * never be two TLVs of the same type in one block.
1577         *
1578         */
1579        aim_extractuserinfo(sess, bs, &userinfo);
1580
1581        /*
1582         * From here on, its depends on what channel we're on.
1583         *
1584         * Technically all channels have a TLV list have this, however,
1585         * for the common channel 1 case, in-place parsing is used for
1586         * performance reasons (less memory allocation).
1587         */
1588        if (channel == 1) {
1589
1590                ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie);
1591
1592        } else if (channel == 2) {
1593                aim_tlvlist_t *tlvlist;
1594
1595                /*
1596                 * Read block of TLVs (not including the userinfo data).  All
1597                 * further data is derived from what is parsed here.
1598                 */
1599                tlvlist = aim_readtlvchain(bs);
1600
1601                ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie);
1602
1603                aim_freetlvchain(&tlvlist);
1604
1605        } else {
1606
1607                faimdprintf(sess, 0, "icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
1608
1609                return 0;
1610        }
1611
1612        return ret;
1613}
1614
1615/*
1616 * Possible codes:
1617 *    AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
1618 *    AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
1619 *    AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
1620 *
1621 */
1622faim_export int aim_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code)
1623{
1624        aim_conn_t *conn;
1625        aim_frame_t *fr;
1626        aim_snacid_t snacid;
1627        aim_tlvlist_t *tl = NULL;
1628       
1629        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
1630                return -EINVAL;
1631
1632        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6)))
1633                return -ENOMEM;
1634
1635        snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0);
1636        aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid);
1637       
1638        aimbs_putraw(&fr->data, cookie, 8);
1639
1640        aimbs_put16(&fr->data, 0x0002); /* channel */
1641        aimbs_put8(&fr->data, strlen(sender));
1642        aimbs_putraw(&fr->data, sender, strlen(sender));
1643
1644        aim_addtlvtochain16(&tl, 0x0003, code);
1645        aim_writetlvchain(&fr->data, &tl);
1646        aim_freetlvchain(&tl);
1647
1648        aim_tx_enqueue(sess, fr);
1649
1650        return 0;
1651}
1652
1653/*
1654 * aim_reqicbmparaminfo()
1655 *
1656 * Request ICBM parameter information.
1657 *
1658 */
1659faim_export int aim_reqicbmparams(aim_session_t *sess)
1660{
1661        aim_conn_t *conn;
1662
1663        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
1664                return -EINVAL;
1665
1666        return aim_genericreq_n(sess, conn, 0x0004, 0x0004);
1667}
1668
1669/*
1670 *
1671 * I definitly recommend sending this.  If you don't, you'll be stuck
1672 * with the rather unreasonable defaults.  You don't want those.  Send this.
1673 *
1674 */
1675faim_export int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params)
1676{
1677        aim_conn_t *conn;
1678        aim_frame_t *fr;
1679        aim_snacid_t snacid;
1680
1681        if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)))
1682                return -EINVAL;
1683
1684        if (!params)
1685                return -EINVAL;
1686
1687        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16)))
1688                return -ENOMEM;
1689
1690        snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0);
1691        aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid);
1692
1693        /* This is read-only (see Parameter Reply). Must be set to zero here. */
1694        aimbs_put16(&fr->data, 0x0000);
1695
1696        /* These are all read-write */
1697        aimbs_put32(&fr->data, params->flags); 
1698        aimbs_put16(&fr->data, params->maxmsglen);
1699        aimbs_put16(&fr->data, params->maxsenderwarn); 
1700        aimbs_put16(&fr->data, params->maxrecverwarn); 
1701        aimbs_put32(&fr->data, params->minmsginterval);
1702
1703        aim_tx_enqueue(sess, fr);
1704
1705        return 0;
1706}
1707
1708static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1709{
1710        struct aim_icbmparameters params;
1711        aim_rxcallback_t userfunc;
1712
1713        params.maxchan = aimbs_get16(bs);
1714        params.flags = aimbs_get32(bs);
1715        params.maxmsglen = aimbs_get16(bs);
1716        params.maxsenderwarn = aimbs_get16(bs);
1717        params.maxrecverwarn = aimbs_get16(bs);
1718        params.minmsginterval = aimbs_get32(bs);
1719       
1720        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1721                return userfunc(sess, rx, &params);
1722
1723        return 0;
1724}
1725
1726static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1727{
1728        int ret = 0;
1729        aim_rxcallback_t userfunc;
1730        fu16_t channel, nummissed, reason;
1731        aim_userinfo_t userinfo;
1732
1733        while (aim_bstream_empty(bs)) { 
1734
1735                channel = aimbs_get16(bs);
1736                aim_extractuserinfo(sess, bs, &userinfo);
1737                nummissed = aimbs_get16(bs);
1738                reason = aimbs_get16(bs);
1739
1740                if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1741                         ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason);
1742        }
1743
1744        return ret;
1745}
1746
1747static int clienterr(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1748{
1749        int ret = 0;
1750        aim_rxcallback_t userfunc;
1751        fu16_t channel, reason;
1752        char *sn;
1753        fu8_t *ck, snlen;
1754
1755        ck = aimbs_getraw(bs, 8);
1756        channel = aimbs_get16(bs);
1757        snlen = aimbs_get8(bs);
1758        sn = aimbs_getstr(bs, snlen);
1759        reason = aimbs_get16(bs);
1760
1761        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1762                 ret = userfunc(sess, rx, channel, sn, reason);
1763
1764        free(ck);
1765        free(sn);
1766
1767        return ret;
1768}
1769
1770static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1771{
1772        aim_rxcallback_t userfunc;
1773        fu16_t type;
1774        fu8_t snlen, *ck;
1775        char *sn;
1776        int ret = 0;
1777
1778        ck = aimbs_getraw(bs, 8);
1779        type = aimbs_get16(bs);
1780        snlen = aimbs_get8(bs);
1781        sn = aimbs_getstr(bs, snlen);
1782
1783        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
1784                ret = userfunc(sess, rx, type, sn);
1785
1786        free(sn);
1787        free(ck);
1788
1789        return ret;
1790}
1791
1792static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
1793{
1794
1795        if (snac->subtype == 0x0005)
1796                return paraminfo(sess, mod, rx, snac, bs);
1797        else if (snac->subtype == 0x0006)
1798                return outgoingim(sess, mod, rx, snac, bs);
1799        else if (snac->subtype == 0x0007)
1800                return incomingim(sess, mod, rx, snac, bs);
1801        else if (snac->subtype == 0x000a)
1802                return missedcall(sess, mod, rx, snac, bs);
1803        else if (snac->subtype == 0x000b)
1804                return clienterr(sess, mod, rx, snac, bs);
1805        else if (snac->subtype == 0x000c)
1806                return msgack(sess, mod, rx, snac, bs);
1807
1808        return 0;
1809}
1810
1811faim_internal int msg_modfirst(aim_session_t *sess, aim_module_t *mod)
1812{
1813
1814        mod->family = 0x0004;
1815        mod->version = 0x0001;
1816        mod->toolid = 0x0110;
1817        mod->toolversion = 0x047b;
1818        mod->flags = 0;
1819        strncpy(mod->name, "messaging", sizeof(mod->name));
1820        mod->snachandler = snachandler;
1821
1822        return 0;
1823}
Note: See TracBrowser for help on using the repository browser.