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