source: protocols/oscar/im.c @ 7a9d968

Last change on this file since 7a9d968 was e88fe7da, checked in by Veres Lajos <vlajos@…>, at 2015-08-07T21:53:25Z

typofix - https://github.com/vlajos/misspell_fixer

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