source: libfaim/im.c @ 601733d

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