source: libfaim/im.c @ de03334

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