source: protocols/oscar/chat.c @ 36533bf

Last change on this file since 36533bf was d18db32f, checked in by Miklos Vajna <vmiklos@…>, at 2011-12-06T00:53:26Z

oscar: unused-but-set-variables

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