source: libfaim/chat.c @ 601733d

barnowl_perlaimdebianowlrelease-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 601733d was cf02dd6, checked in by James M. Kretchmar <kretch@mit.edu>, 17 years ago
*** empty log message ***
  • Property mode set to 100644
File size: 15.6 KB
Line 
1/*
2 * Family 0x000e - Routines for the Chat service.
3 *
4 */
5
6#define FAIM_INTERNAL
7#include <aim.h>
8
9#include <string.h>
10
11/* Stored in the ->priv of chat connections */
12struct chatconnpriv {
13        fu16_t exchange;
14        char *name;
15        fu16_t instance;
16};
17
18faim_internal void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn)
19{
20        struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv;
21
22        if (ccp)
23                free(ccp->name);
24        free(ccp);
25
26        return;
27}
28
29faim_export char *aim_chat_getname(aim_conn_t *conn)
30{
31        struct chatconnpriv *ccp;
32
33        if (!conn)
34                return NULL;
35
36        if (conn->type != AIM_CONN_TYPE_CHAT)
37                return NULL;
38
39        ccp = (struct chatconnpriv *)conn->priv;
40
41        return ccp->name;
42}
43
44/* XXX get this into conn.c -- evil!! */
45faim_export aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name)
46{
47        aim_conn_t *cur;
48
49        for (cur = sess->connlist; cur; cur = cur->next) {
50                struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv;
51
52                if (cur->type != AIM_CONN_TYPE_CHAT)
53                        continue;
54                if (!cur->priv) {
55                        faimdprintf(sess, 0, "faim: chat: chat connection with no name! (fd = %d)\n", cur->fd);
56                        continue;
57                }
58
59                if (strcmp(ccp->name, name) == 0)
60                        break;
61        }
62
63        return cur;
64}
65
66faim_export int aim_chat_attachname(aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
67{
68        struct chatconnpriv *ccp;
69
70        if (!conn || !roomname)
71                return -EINVAL;
72
73        if (conn->priv)
74                free(conn->priv);
75
76        if (!(ccp = malloc(sizeof(struct chatconnpriv))))
77                return -ENOMEM;
78
79        ccp->exchange = exchange;
80        ccp->name = strdup(roomname);
81        ccp->instance = instance;
82
83        conn->priv = (void *)ccp;
84
85        return 0;
86}
87
88static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, fu16_t type, fu16_t exchange, const char *roomname, fu16_t instance)
89{
90        fu8_t *buf;
91        int buflen;
92        aim_bstream_t bs;
93
94        buflen = 2 + 1 + strlen(roomname) + 2;
95       
96        if (!(buf = malloc(buflen)))
97                return 0;
98
99        aim_bstream_init(&bs, buf, buflen);
100
101        aimbs_put16(&bs, exchange);
102        aimbs_put8(&bs, strlen(roomname));
103        aimbs_putraw(&bs, roomname, strlen(roomname));
104        aimbs_put16(&bs, instance);
105
106        aim_tlvlist_add_raw(list, type, aim_bstream_curpos(&bs), buf);
107
108        free(buf);
109
110        return 0;
111}
112
113/*
114 * Join a room of name roomname.  This is the first step to joining an
115 * already created room.  It's basically a Service Request for
116 * family 0x000e, with a little added on to specify the exchange and room
117 * name.
118 */
119faim_export int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, fu16_t exchange, const char *roomname, fu16_t instance)
120{
121        aim_frame_t *fr;
122        aim_snacid_t snacid;
123        aim_tlvlist_t *tl = NULL;
124        struct chatsnacinfo csi;
125       
126        if (!sess || !conn || !roomname || !strlen(roomname))
127                return -EINVAL;
128
129        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512)))
130                return -ENOMEM;
131
132        memset(&csi, 0, sizeof(csi));
133        csi.exchange = exchange;
134        strncpy(csi.name, roomname, sizeof(csi.name));
135        csi.instance = instance;
136
137        snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi));
138        aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid);
139
140        /*
141         * Requesting service chat (0x000e)
142         */
143        aimbs_put16(&fr->data, 0x000e);
144
145        aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance);
146        aim_tlvlist_write(&fr->data, &tl);
147        aim_tlvlist_free(&tl);
148
149        aim_tx_enqueue(sess, fr);
150
151        return 0; 
152}
153
154faim_internal int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo)
155{
156        int namelen;
157
158        if (!bs || !outinfo)
159                return 0;
160
161        outinfo->exchange = aimbs_get16(bs);
162        namelen = aimbs_get8(bs);
163        outinfo->name = aimbs_getstr(bs, namelen);
164        outinfo->instance = aimbs_get16(bs);
165
166        return 0;
167}
168
169faim_export int aim_chat_leaveroom(aim_session_t *sess, const char *name)
170{
171        aim_conn_t *conn;
172
173        if (!(conn = aim_chat_getconn(sess, name)))
174                return -ENOENT;
175
176        aim_conn_close(conn);
177
178        return 0;
179}
180
181/*
182 * conn must be a BOS connection!
183 */
184faim_export int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, fu16_t exchange, const char *roomname, fu16_t instance)
185{
186        int i;
187        aim_frame_t *fr;
188        aim_msgcookie_t *cookie;
189        struct aim_invite_priv *priv;
190        fu8_t ckstr[8];
191        aim_snacid_t snacid;
192        aim_tlvlist_t *otl = NULL, *itl = NULL;
193        fu8_t *hdr;
194        int hdrlen;
195        aim_bstream_t hdrbs;
196       
197        if (!sess || !conn || !sn || !msg || !roomname)
198                return -EINVAL;
199
200        if (conn->type != AIM_CONN_TYPE_BOS)
201                return -EINVAL;
202
203        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
204                return -ENOMEM;
205
206        snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1);
207        aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid);
208
209        /*
210         * Cookie
211         */
212        for (i = 0; i < 8; i++)
213                ckstr[i] = (fu8_t)rand();
214
215        /* XXX should be uncached by an unwritten 'invite accept' handler */
216        if ((priv = malloc(sizeof(struct aim_invite_priv)))) {
217                priv->sn = strdup(sn);
218                priv->roomname = strdup(roomname);
219                priv->exchange = exchange;
220                priv->instance = instance;
221        }
222
223        if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv)))
224                aim_cachecookie(sess, cookie);
225        else
226                free(priv);
227
228        /* ICBM Header */
229        aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
230        aimbs_put16(&fr->data, 0x0002); /* Channel */
231        aimbs_put8(&fr->data, strlen(sn)); /* Screename length */
232        aimbs_putraw(&fr->data, sn, strlen(sn)); /* Screenname */
233
234        /*
235         * TLV t(0005)
236         *
237         * Everything else is inside this TLV.
238         *
239         * Sigh.  AOL was rather inconsistent right here.  So we have
240         * to play some minor tricks.  Right inside the type 5 is some
241         * raw data, followed by a series of TLVs. 
242         *
243         */
244        hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2;
245        hdr = malloc(hdrlen);
246        aim_bstream_init(&hdrbs, hdr, hdrlen);
247       
248        aimbs_put16(&hdrbs, 0x0000); /* Unknown! */
249        aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */
250        aim_putcap(&hdrbs, AIM_CAPS_CHAT);
251
252        aim_tlvlist_add_16(&itl, 0x000a, 0x0001);
253        aim_tlvlist_add_noval(&itl, 0x000f);
254        aim_tlvlist_add_raw(&itl, 0x000c, strlen(msg), msg);
255        aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance);
256        aim_tlvlist_write(&hdrbs, &itl);
257       
258        aim_tlvlist_add_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr);
259
260        aim_tlvlist_write(&fr->data, &otl);
261
262        free(hdr);
263        aim_tlvlist_free(&itl);
264        aim_tlvlist_free(&otl);
265       
266        aim_tx_enqueue(sess, fr);
267
268        return 0;
269}
270
271/*
272 * Subtype 0x0002 - General room information.  Lots of stuff.
273 *
274 * Values I know are in here but I havent attached
275 * them to any of the 'Unknown's:
276 *      - Language (English)
277 *
278 */
279static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
280{
281        aim_userinfo_t *userinfo = NULL;
282        aim_rxcallback_t userfunc;
283        int ret = 0;
284        int usercount = 0;
285        fu8_t detaillevel = 0;
286        char *roomname = NULL;
287        struct aim_chat_roominfo roominfo;
288        fu16_t tlvcount = 0;
289        aim_tlvlist_t *tlvlist;
290        char *roomdesc = NULL;
291        fu16_t flags = 0;
292        fu32_t creationtime = 0;
293        fu16_t maxmsglen = 0, maxvisiblemsglen = 0;
294        fu16_t unknown_d2 = 0, unknown_d5 = 0;
295
296        aim_chat_readroominfo(bs, &roominfo);
297
298        detaillevel = aimbs_get8(bs);
299
300        if (detaillevel != 0x02) {
301                faimdprintf(sess, 0, "faim: chat_roomupdateinfo: detail level %d not supported\n", detaillevel);
302                return 1;
303        }
304
305        tlvcount = aimbs_get16(bs);
306
307        /*
308         * Everything else are TLVs.
309         */ 
310        tlvlist = aim_tlvlist_read(bs);
311
312        /*
313         * TLV type 0x006a is the room name in Human Readable Form.
314         */
315        if (aim_tlv_gettlv(tlvlist, 0x006a, 1))
316                roomname = aim_tlv_getstr(tlvlist, 0x006a, 1);
317
318        /*
319         * Type 0x006f: Number of occupants.
320         */
321        if (aim_tlv_gettlv(tlvlist, 0x006f, 1))
322                usercount = aim_tlv_get16(tlvlist, 0x006f, 1);
323
324        /*
325         * Type 0x0073:  Occupant list.
326         */
327        if (aim_tlv_gettlv(tlvlist, 0x0073, 1)) {       
328                int curoccupant = 0;
329                aim_tlv_t *tmptlv;
330                aim_bstream_t occbs;
331
332                tmptlv = aim_tlv_gettlv(tlvlist, 0x0073, 1);
333
334                /* Allocate enough userinfo structs for all occupants */
335                userinfo = calloc(usercount, sizeof(aim_userinfo_t));
336
337                aim_bstream_init(&occbs, tmptlv->value, tmptlv->length);
338
339                while (curoccupant < usercount)
340                        aim_info_extract(sess, &occbs, &userinfo[curoccupant++]);
341        }
342
343        /*
344         * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG)
345         */
346        if (aim_tlv_gettlv(tlvlist, 0x00c9, 1))
347                flags = aim_tlv_get16(tlvlist, 0x00c9, 1);
348
349        /*
350         * Type 0x00ca: Creation time (4 bytes)
351         */
352        if (aim_tlv_gettlv(tlvlist, 0x00ca, 1))
353                creationtime = aim_tlv_get32(tlvlist, 0x00ca, 1);
354
355        /*
356         * Type 0x00d1: Maximum Message Length
357         */
358        if (aim_tlv_gettlv(tlvlist, 0x00d1, 1))
359                maxmsglen = aim_tlv_get16(tlvlist, 0x00d1, 1);
360
361        /*
362         * Type 0x00d2: Unknown. (2 bytes)
363         */
364        if (aim_tlv_gettlv(tlvlist, 0x00d2, 1))
365                unknown_d2 = aim_tlv_get16(tlvlist, 0x00d2, 1);
366
367        /*
368         * Type 0x00d3: Room Description
369         */
370        if (aim_tlv_gettlv(tlvlist, 0x00d3, 1))
371                roomdesc = aim_tlv_getstr(tlvlist, 0x00d3, 1);
372
373        /*
374         * Type 0x000d4: Unknown (flag only)
375         */
376        if (aim_tlv_gettlv(tlvlist, 0x000d4, 1))
377                ;
378
379        /*
380         * Type 0x00d5: Unknown. (1 byte)
381         */
382        if (aim_tlv_gettlv(tlvlist, 0x00d5, 1))
383                unknown_d5 = aim_tlv_get8(tlvlist, 0x00d5, 1);
384
385
386        /*
387         * Type 0x00d6: Encoding 1 ("us-ascii")
388         */
389        if (aim_tlv_gettlv(tlvlist, 0x000d6, 1))
390                ;
391       
392        /*
393         * Type 0x00d7: Language 1 ("en")
394         */
395        if (aim_tlv_gettlv(tlvlist, 0x000d7, 1))
396                ;
397
398        /*
399         * Type 0x00d8: Encoding 2 ("us-ascii")
400         */
401        if (aim_tlv_gettlv(tlvlist, 0x000d8, 1))
402                ;
403       
404        /*
405         * Type 0x00d9: Language 2 ("en")
406         */
407        if (aim_tlv_gettlv(tlvlist, 0x000d9, 1))
408                ;
409
410        /*
411         * Type 0x00da: Maximum visible message length
412         */
413        if (aim_tlv_gettlv(tlvlist, 0x000da, 1))
414                maxvisiblemsglen = aim_tlv_get16(tlvlist, 0x00da, 1);
415
416        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) {
417                ret = userfunc(sess,
418                                rx,
419                                &roominfo,
420                                roomname,
421                                usercount,
422                                userinfo,
423                                roomdesc,
424                                flags,
425                                creationtime,
426                                maxmsglen,
427                                unknown_d2,
428                                unknown_d5,
429                                maxvisiblemsglen);
430        }
431
432        free(roominfo.name);
433
434        while (usercount > 0)
435                aim_info_free(&userinfo[--usercount]);
436
437        free(userinfo);
438        free(roomname);
439        free(roomdesc);
440        aim_tlvlist_free(&tlvlist);
441
442        return ret;
443}
444
445/* Subtypes 0x0003 and 0x0004 */
446static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
447{
448        aim_userinfo_t *userinfo = NULL;
449        aim_rxcallback_t userfunc;
450        int curcount = 0, ret = 0;
451
452        while (aim_bstream_empty(bs)) {
453                curcount++;
454                userinfo = realloc(userinfo, curcount * sizeof(aim_userinfo_t));
455                aim_info_extract(sess, bs, &userinfo[curcount-1]);
456        }
457
458        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
459                ret = userfunc(sess, rx, curcount, userinfo);
460
461        aim_info_free(userinfo);
462        free(userinfo);
463
464        return ret;
465}
466
467/*
468 * Subtype 0x0005 - Send a Chat Message.
469 *
470 * Possible flags:
471 *   AIM_CHATFLAGS_NOREFLECT   --  Unset the flag that requests messages
472 *                                 should be sent to their sender.
473 *   AIM_CHATFLAGS_AWAY        --  Mark the message as an autoresponse
474 *                                 (Note that WinAIM does not honor this,
475 *                                 and displays the message as normal.)
476 *
477 * XXX convert this to use tlvchains
478 */
479faim_export int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, fu16_t flags, const char *msg, int msglen)
480{   
481        int i;
482        aim_frame_t *fr;
483        aim_msgcookie_t *cookie;
484        aim_snacid_t snacid;
485        fu8_t ckstr[8];
486        aim_tlvlist_t *otl = NULL, *itl = NULL;
487
488        if (!sess || !conn || !msg || (msglen <= 0))
489                return 0;
490
491        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152)))
492                return -ENOMEM;
493
494        snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0);
495        aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid);
496
497        /*
498         * Cookie
499         *
500         * XXX mkcookie should generate the cookie and cache it in one
501         * operation to preserve uniqueness.
502         */
503        for (i = 0; i < 8; i++)
504                ckstr[i] = (fu8_t)rand();
505
506        cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL);
507        cookie->data = NULL; /* XXX store something useful here */
508
509        aim_cachecookie(sess, cookie);
510
511        /* ICBM Header */
512        aimbs_putraw(&fr->data, ckstr, 8); /* Cookie */
513        aimbs_put16(&fr->data, 0x0003); /* Channel */
514
515        /*
516         * Type 1: Flag meaning this message is destined to the room.
517         */
518        aim_tlvlist_add_noval(&otl, 0x0001);
519
520        /*
521         * Type 6: Reflect
522         */
523        if (!(flags & AIM_CHATFLAGS_NOREFLECT))
524                aim_tlvlist_add_noval(&otl, 0x0006);
525
526        /*
527         * Type 7: Autoresponse
528         */
529        if (flags & AIM_CHATFLAGS_AWAY)
530                aim_tlvlist_add_noval(&otl, 0x0007);
531
532        /*
533         * SubTLV: Type 1: Message
534         */
535        aim_tlvlist_add_raw(&itl, 0x0001, msglen, msg);
536
537        /*
538         * Type 5: Message block.  Contains more TLVs.
539         *
540         * This could include other information... We just
541         * put in a message TLV however. 
542         *
543         */
544        aim_tlvlist_add_frozentlvlist(&otl, 0x0005, &itl);
545
546        aim_tlvlist_write(&fr->data, &otl);
547       
548        aim_tlvlist_free(&itl);
549        aim_tlvlist_free(&otl);
550       
551        aim_tx_enqueue(sess, fr);
552
553        return 0;
554}
555
556/*
557 * Subtype 0x0006
558 *
559 * We could probably include this in the normal ICBM parsing
560 * code as channel 0x0003, however, since only the start
561 * would be the same, we might as well do it here.
562 *
563 * General outline of this SNAC:
564 *   snac
565 *   cookie
566 *   channel id
567 *   tlvlist
568 *     unknown
569 *     source user info
570 *       name
571 *       evility
572 *       userinfo tlvs
573 *         online time
574 *         etc
575 *     message metatlv
576 *       message tlv
577 *         message string
578 *       possibly others
579 * 
580 */
581static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
582{
583        aim_userinfo_t userinfo;
584        aim_rxcallback_t userfunc;     
585        int ret = 0;
586        fu8_t *cookie;
587        fu16_t channel;
588        aim_tlvlist_t *otl;
589        char *msg = NULL;
590        aim_msgcookie_t *ck;
591
592        memset(&userinfo, 0, sizeof(aim_userinfo_t));
593
594        /*
595         * ICBM Cookie.  Uncache it.
596         */
597        cookie = aimbs_getraw(bs, 8);
598
599        if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
600                free(ck->data);
601                free(ck);
602        }
603
604        /*
605         * Channel ID
606         *
607         * Channels 1 and 2 are implemented in the normal ICBM
608         * parser.
609         *
610         * We only do channel 3 here.
611         *
612         */
613        channel = aimbs_get16(bs);
614
615        if (channel != 0x0003) {
616                faimdprintf(sess, 0, "faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
617                return 0;
618        }
619
620        /*
621         * Start parsing TLVs right away.
622         */
623        otl = aim_tlvlist_read(bs);
624
625        /*
626         * Type 0x0003: Source User Information
627         */
628        if (aim_tlv_gettlv(otl, 0x0003, 1)) {
629                aim_tlv_t *userinfotlv;
630                aim_bstream_t tbs;
631
632                userinfotlv = aim_tlv_gettlv(otl, 0x0003, 1);
633
634                aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length);
635                aim_info_extract(sess, &tbs, &userinfo);
636        }
637
638        /*
639         * Type 0x0001: If present, it means it was a message to the
640         * room (as opposed to a whisper).
641         */
642        if (aim_tlv_gettlv(otl, 0x0001, 1))
643                ;
644
645        /*
646         * Type 0x0005: Message Block.  Conains more TLVs.
647         */
648        if (aim_tlv_gettlv(otl, 0x0005, 1)) {
649                aim_tlvlist_t *itl;
650                aim_tlv_t *msgblock;
651                aim_bstream_t tbs;
652
653                msgblock = aim_tlv_gettlv(otl, 0x0005, 1);
654                aim_bstream_init(&tbs, msgblock->value, msgblock->length);
655                itl = aim_tlvlist_read(&tbs);
656
657                /*
658                 * Type 0x0001: Message.
659                 */     
660                if (aim_tlv_gettlv(itl, 0x0001, 1))
661                        msg = aim_tlv_getstr(itl, 0x0001, 1);
662
663                aim_tlvlist_free(&itl); 
664        }
665
666        if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype)))
667                ret = userfunc(sess, rx, &userinfo, msg);
668
669        aim_info_free(&userinfo);
670        free(cookie);
671        free(msg);
672        aim_tlvlist_free(&otl);
673
674        return ret;
675}
676
677static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs)
678{
679
680        if (snac->subtype == 0x0002)
681                return infoupdate(sess, mod, rx, snac, bs);
682        else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004))
683                return userlistchange(sess, mod, rx, snac, bs);
684        else if (snac->subtype == 0x0006)
685                return incomingmsg(sess, mod, rx, snac, bs);
686
687        return 0;
688}
689
690faim_internal int chat_modfirst(aim_session_t *sess, aim_module_t *mod)
691{
692
693        mod->family = 0x000e;
694        mod->version = 0x0001;
695        mod->toolid = 0x0010;
696        mod->toolversion = 0x0629;
697        mod->flags = 0;
698        strncpy(mod->name, "chat", sizeof(mod->name));
699        mod->snachandler = snachandler;
700
701        return 0;
702}
Note: See TracBrowser for help on using the repository browser.