source: protocols/oscar/im.c @ a906b77

Last change on this file since a906b77 was 6042a54, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-10-19T23:38:33Z

Massive cleanup in OSCAR.

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