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