]> andersk Git - libfaim.git/blob - aim_chat.c
1d09b1b168effd29b6a8337beb653259ed621bf9
[libfaim.git] / aim_chat.c
1 /*
2  * aim_chat.c
3  *
4  * Routines for the Chat service.
5  *
6  */
7
8 #include <faim/aim.h> 
9
10 faim_export char *aim_chat_getname(struct aim_conn_t *conn)
11 {
12   if (!conn)
13     return NULL;
14   if (conn->type != AIM_CONN_TYPE_CHAT)
15     return NULL;
16
17   return (char *)conn->priv; /* yuck ! */
18 }
19
20 faim_export struct aim_conn_t *aim_chat_getconn(struct aim_session_t *sess, char *name)
21 {
22   struct aim_conn_t *cur;
23   
24   faim_mutex_lock(&sess->connlistlock);
25   for (cur = sess->connlist; cur; cur = cur->next) {
26     if (cur->type != AIM_CONN_TYPE_CHAT)
27       continue;
28     if (strcmp((char *)cur->priv, name) == 0)
29       break;
30   }
31   faim_mutex_unlock(&sess->connlistlock);
32
33   return cur;
34 }
35
36 faim_export int aim_chat_attachname(struct aim_conn_t *conn, char *roomname)
37 {
38   if (!conn || !roomname)
39     return -1;
40
41   conn->priv = malloc(strlen(roomname)+1);
42   strcpy(conn->priv, roomname);
43
44   return 0;
45 }
46
47 faim_export unsigned long aim_chat_send_im(struct aim_session_t *sess,
48                                            struct aim_conn_t *conn, 
49                                            char *msg)
50 {   
51
52   int curbyte,i;
53   struct command_tx_struct *newpacket;
54
55   if (!sess || !conn || !msg)
56     return 0;
57   
58   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152)))
59     return -1;
60
61   newpacket->lock = 1; /* lock struct */
62
63   curbyte  = 0;
64   curbyte += aim_putsnac(newpacket->data+curbyte, 
65                          0x000e, 0x0005, 0x0000, sess->snac_nextid);
66
67   /* 
68    * Generate a random message cookie 
69    */
70   for (i=0;i<8;i++)
71     curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) rand());
72
73   aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL));
74
75   /*
76    * metaTLV start.  -- i assume this is a metaTLV.  it could be the
77    *                    channel ID though.
78    */
79   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
80
81   /*
82    * Type 1: Unknown.  Blank.
83    */
84   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
85   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
86   
87   /*
88    * Type 6: Unknown.  Blank.
89    */
90   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0006);
91   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
92
93   /*
94    * Type 5: Message block.  Contains more TLVs.
95    *
96    * This could include other information... We just
97    * put in a message TLV however.  
98    * 
99    */
100   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
101   curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg)+4);
102
103   /*
104    * SubTLV: Type 1: Message
105    */
106   curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x0001, strlen(msg), msg);
107   
108   newpacket->commandlen = curbyte;
109
110   newpacket->lock = 0;
111   aim_tx_enqueue(sess, newpacket);
112
113   return (sess->snac_nextid++);
114 }
115
116 /*
117  * Join a room of name roomname.  This is the first
118  * step to joining an already created room.  It's 
119  * basically a Service Request for family 0x000e, 
120  * with a little added on to specify the exchange
121  * and room name.
122  *
123  */
124 faim_export unsigned long aim_chat_join(struct aim_session_t *sess,
125                                         struct aim_conn_t *conn, 
126                                         u_short exchange,
127                                         const char *roomname)
128 {
129   struct command_tx_struct *newpacket;
130   int i;
131
132   if (!sess || !conn || !roomname)
133     return 0;
134   
135   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+9+strlen(roomname)+2)))
136     return -1;
137
138   newpacket->lock = 1;
139   
140   i = aim_putsnac(newpacket->data, 0x0001, 0x0004, 0x0000, sess->snac_nextid);
141
142   i+= aimutil_put16(newpacket->data+i, 0x000e);
143
144   /* 
145    * this is techinally a TLV, but we can't use normal functions
146    * because we need the extraneous nulls and other weird things.
147    */
148   i+= aimutil_put16(newpacket->data+i, 0x0001);
149   i+= aimutil_put16(newpacket->data+i, 2+1+strlen(roomname)+2);
150   i+= aimutil_put16(newpacket->data+i, exchange);
151   i+= aimutil_put8(newpacket->data+i, strlen(roomname));
152   memcpy(newpacket->data+i, roomname, strlen(roomname));
153   i+= strlen(roomname);
154   //i+= aimutil_putstr(newpacket->data+i, roomname, strlen(roomname));
155   i+= aimutil_put16(newpacket->data+i, 0x0000);
156
157   /*
158    * Chat hack.
159    *
160    * XXX: A problem occurs here if we request a channel
161    *      join but it fails....pendingjoin will be nonnull
162    *      even though the channel is never going to get a
163    *      redirect!
164    *
165    */
166   sess->pendingjoin = (char *)malloc(strlen(roomname)+1);
167   strcpy(sess->pendingjoin, roomname);
168
169   newpacket->lock = 0;
170   aim_tx_enqueue(sess, newpacket);
171
172 #if 0
173   {
174     struct aim_snac_t snac;
175     
176     snac.id = sess->snac_nextid;
177     snac.family = 0x0001;
178     snac.type = 0x0004;
179     snac.flags = 0x0000;
180
181     snac.data = malloc(strlen(roomname)+1);
182     strcpy(snac.data, roomname);
183
184     aim_newsnac(sess, &snac);
185   }
186
187 #endif
188   return (sess->snac_nextid++);
189 }
190
191 faim_internal int aim_chat_readroominfo(u_char *buf, struct aim_chat_roominfo *outinfo)
192 {
193   int namelen = 0;
194   int i = 0;
195
196   if (!buf || !outinfo)
197     return 0;
198
199   outinfo->exchange = aimutil_get16(buf+i);
200   i += 2;
201
202   namelen = aimutil_get8(buf+i);
203   i += 1;
204
205   outinfo->name = (char *)malloc(namelen+1);
206   memcpy(outinfo->name, buf+i, namelen);
207   outinfo->name[namelen] = '\0';
208   i += namelen;
209
210   outinfo->instance = aimutil_get16(buf+i);
211   i += 2;
212   
213   return i;
214 }
215
216
217 /*
218  * General room information.  Lots of stuff.
219  *
220  * Values I know are in here but I havent attached
221  * them to any of the 'Unknown's:
222  *      - Language (English)
223  *
224  * SNAC 000e/0002
225  */
226 faim_internal int aim_chat_parse_infoupdate(struct aim_session_t *sess,
227                                             struct command_rx_struct *command)
228 {
229   struct aim_userinfo_s *userinfo = NULL;
230   rxcallback_t userfunc=NULL;   
231   int ret = 1, i = 0;
232   int usercount = 0;
233   u_char detaillevel = 0;
234   char *roomname = NULL;
235   struct aim_chat_roominfo roominfo;
236   u_short tlvcount = 0;
237   struct aim_tlvlist_t *tlvlist;
238   char *roomdesc = NULL;
239   struct aim_tlv_t *tmptlv;
240   unsigned short unknown_c9 = 0;
241   unsigned long creationtime = 0;
242   unsigned short maxmsglen = 0;
243   unsigned short unknown_d2 = 0, unknown_d5 = 0;
244
245   i = 10;
246   i += aim_chat_readroominfo(command->data+i, &roominfo);
247   
248   detaillevel = aimutil_get8(command->data+i);
249   i++;
250
251   if (detaillevel != 0x02) {
252     if (detaillevel == 0x01)
253       printf("faim: chat_roomupdateinfo: detail level 1 not supported\n");
254     else
255       printf("faim: chat_roomupdateinfo: unknown detail level %d\n", detaillevel);
256     return 1;
257   }
258   
259   tlvcount = aimutil_get16(command->data+i);
260   i += 2;
261
262   /*
263    * Everything else are TLVs.
264    */ 
265   tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
266   
267   /*
268    * TLV type 0x006a is the room name in Human Readable Form.
269    */
270   if (aim_gettlv(tlvlist, 0x006a, 1))
271       roomname = aim_gettlv_str(tlvlist, 0x006a, 1);
272
273   /*
274    * Type 0x006f: Number of occupants.
275    */
276   if (aim_gettlv(tlvlist, 0x006f, 1)) {
277     struct aim_tlv_t *tmptlv;
278     tmptlv = aim_gettlv(tlvlist, 0x006f, 1);
279     
280     usercount = aimutil_get16(tmptlv->value);
281   }
282
283   /*
284    * Type 0x0073:  Occupant list.
285    */
286   if (aim_gettlv(tlvlist, 0x0073, 1)) { 
287     int curoccupant = 0;
288     struct aim_tlv_t *tmptlv;
289     
290     tmptlv = aim_gettlv(tlvlist, 0x0073, 1);
291
292     /* Allocate enough userinfo structs for all occupants */
293     userinfo = calloc(usercount, sizeof(struct aim_userinfo_s));
294     
295     i = 0;
296     while (curoccupant < usercount)
297       i += aim_extractuserinfo(tmptlv->value+i, &userinfo[curoccupant++]);
298   }
299   
300   /* 
301    * Type 0x00c9: Unknown. (2 bytes)
302    */
303   if ((tmptlv = aim_gettlv(tlvlist, 0x00c9, 1)))
304     unknown_c9 = aimutil_get16(tmptlv->value);
305   
306   /* 
307    * Type 0x00ca: Creation time (4 bytes)
308    */
309   if ((tmptlv = aim_gettlv(tlvlist, 0x00ca, 1)))
310     creationtime = aimutil_get32(tmptlv->value);
311
312   /* 
313    * Type 0x00d1: Maximum Message Length
314    */
315   if ((tmptlv = aim_gettlv(tlvlist, 0x00d1, 1)))
316     maxmsglen = aimutil_get16(tmptlv->value);
317
318   /* 
319    * Type 0x00d2: Unknown. (2 bytes)
320    */
321   if ((tmptlv = aim_gettlv(tlvlist, 0x00d2, 1)))
322     unknown_d2 = aimutil_get16(tmptlv->value);;
323
324   /* 
325    * Type 0x00d3: Room Description
326    */
327   if (aim_gettlv(tlvlist, 0x00d3, 1))
328     roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1);
329
330   /* 
331    * Type 0x00d5: Unknown. (1 byte)
332    */
333   if ((tmptlv = aim_gettlv(tlvlist, 0x00d5, 1)))
334     unknown_d5 = aimutil_get8(tmptlv->value);;
335
336
337   userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE);
338   if (userfunc) {
339     ret = userfunc(sess,
340                    command, 
341                    &roominfo,
342                    roomname,
343                    usercount,
344                    userinfo,    
345                    roomdesc,
346                    unknown_c9,
347                    creationtime,
348                    maxmsglen,
349                    unknown_d2,
350                    unknown_d5);
351   }     
352   free(roominfo.name);
353   free(userinfo);
354   free(roomname);
355   free(roomdesc);
356   aim_freetlvchain(&tlvlist);
357  
358   return ret;
359 }
360
361 faim_internal int aim_chat_parse_joined(struct aim_session_t *sess,
362                                         struct command_rx_struct *command)
363 {
364   struct aim_userinfo_s *userinfo = NULL;
365   rxcallback_t userfunc=NULL;   
366   int i = 10, curcount = 0, ret = 1;
367
368   while (i < command->commandlen) {
369     curcount++;
370     userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
371     i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
372   }
373
374   userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN);
375   if (userfunc) {       
376     ret = userfunc(sess,
377                    command, 
378                    curcount,
379                    userinfo);
380   }
381
382   free(userinfo);
383
384   return ret;
385 }             
386
387 faim_internal int aim_chat_parse_leave(struct aim_session_t *sess,
388                                        struct command_rx_struct *command)
389 {
390
391   struct aim_userinfo_s *userinfo = NULL;
392   rxcallback_t userfunc=NULL;   
393   int i = 10, curcount = 0, ret = 1;
394
395   while (i < command->commandlen) {
396     curcount++;
397     userinfo = realloc(userinfo, curcount * sizeof(struct aim_userinfo_s));
398     i += aim_extractuserinfo(command->data+i, &userinfo[curcount-1]);
399   }
400
401   userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE);
402   if (userfunc) {
403     ret = userfunc(sess,
404                    command, 
405                    curcount,
406                    userinfo);
407   }
408
409   free(userinfo);
410
411   return ret;
412 }          
413
414 /*
415  * We could probably include this in the normal ICBM parsing 
416  * code as channel 0x0003, however, since only the start
417  * would be the same, we might as well do it here.
418  */
419 faim_internal int aim_chat_parse_incoming(struct aim_session_t *sess,
420                                           struct command_rx_struct *command)
421 {
422   struct aim_userinfo_s userinfo;
423   rxcallback_t userfunc=NULL;   
424   int ret = 1, i = 0, z = 0;
425   unsigned char cookie[8];
426   int channel;
427   struct aim_tlvlist_t *outerlist;
428   char *msg = NULL;
429   struct aim_msgcookie_t *ck;
430
431   memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
432
433   i = 10; /* skip snac */
434
435   /*
436    * ICBM Cookie.  Cache it.
437    */ 
438   for (z=0; z<8; z++,i++)
439     cookie[z] = command->data[i];
440
441   if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) {
442     if (ck->data)
443       free(ck->data);
444     free(ck);
445   }
446
447   /*
448    * Channel ID
449    *
450    * Channels 1 and 2 are implemented in the normal ICBM
451    * parser.
452    *
453    * We only do channel 3 here.
454    *
455    */
456   channel = aimutil_get16(command->data+i);
457   i += 2;
458
459   if (channel != 0x0003) {
460     printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
461     return 1;
462   }
463
464   /*
465    * Start parsing TLVs right away. 
466    */
467   outerlist = aim_readtlvchain(command->data+i, command->commandlen-i);
468   
469   /*
470    * Type 0x0003: Source User Information
471    */
472   if (aim_gettlv(outerlist, 0x0003, 1)) {
473     struct aim_tlv_t *userinfotlv;
474     
475     userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
476     aim_extractuserinfo(userinfotlv->value, &userinfo);
477   }
478
479   /*
480    * Type 0x0001: Unknown.
481    */
482   if (aim_gettlv(outerlist, 0x0001, 1))
483     ;
484
485   /*
486    * Type 0x0005: Message Block.  Conains more TLVs.
487    */
488   if (aim_gettlv(outerlist, 0x0005, 1))
489     {
490       struct aim_tlvlist_t *innerlist;
491       struct aim_tlv_t *msgblock;
492
493       msgblock = aim_gettlv(outerlist, 0x0005, 1);
494       innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
495       
496       /* 
497        * Type 0x0001: Message.
498        */       
499       if (aim_gettlv(innerlist, 0x0001, 1))
500           msg = aim_gettlv_str(innerlist, 0x0001, 1);
501
502       aim_freetlvchain(&innerlist); 
503     }
504
505   userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG);
506   if (userfunc)
507     {
508       ret = userfunc(sess,
509                      command, 
510                      &userinfo,
511                      msg);
512     }
513   free(msg);
514   aim_freetlvchain(&outerlist);
515
516   return ret;
517 }             
518
519 faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess,
520                                                struct aim_conn_t *conn)
521 {
522   struct command_tx_struct *newpacket;
523   int i;
524
525   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x20)))
526     return -1;
527
528   newpacket->lock = 1;
529
530   i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
531
532   i+= aimutil_put16(newpacket->data+i, 0x000e);
533   i+= aimutil_put16(newpacket->data+i, 0x0001);
534
535   i+= aimutil_put16(newpacket->data+i, 0x0004);
536   i+= aimutil_put16(newpacket->data+i, 0x0001);
537
538   i+= aimutil_put16(newpacket->data+i, 0x0001);
539   i+= aimutil_put16(newpacket->data+i, 0x0003);
540
541   i+= aimutil_put16(newpacket->data+i, 0x0004);
542   i+= aimutil_put16(newpacket->data+i, 0x0686);
543
544   newpacket->lock = 0;
545   aim_tx_enqueue(sess, newpacket);
546
547   return (sess->snac_nextid++);
548 }
549
550 faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
551 {
552   struct aim_conn_t *conn;
553
554   if ((conn = aim_chat_getconn(sess, name)))
555     aim_conn_kill(sess, &conn);
556
557   if (!conn)
558     return -1;
559   return 0;
560 }
561
562 /*
563  * conn must be a BOS connection!
564  */
565 faim_export unsigned long aim_chat_invite(struct aim_session_t *sess,
566                                           struct aim_conn_t *conn,
567                                           char *sn,
568                                           char *msg,
569                                           u_short exchange,
570                                           char *roomname,
571                                           u_short instance)
572 {
573   struct command_tx_struct *newpacket;
574   int i,curbyte=0;
575
576   if (!sess || !conn || !sn || !msg || !roomname)
577     return 0;
578
579   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
580     return -1;
581
582   newpacket->lock = 1;
583
584   curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
585
586   /*
587    * Cookie
588    */
589   for (i=0;i<8;i++)
590     curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());
591
592   /* XXX this should get uncached by the unwritten 'invite accept' handler */
593   aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL));
594
595   /*
596    * Channel (2)
597    */
598   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
599
600   /*
601    * Dest sn
602    */
603   curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
604   curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
605
606   /*
607    * TLV t(0005)
608    */
609   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
610   curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02);
611   
612   /* 
613    * Unknown info
614    */
615   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
616   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131);
617   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538);
618   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446);
619   curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100);
620   curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f);
621   curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420);
622   curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287);
623   curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1);
624   curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222);
625   curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445);
626   curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354);
627   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
628   
629   /*
630    * TLV t(000a) -- Unknown
631    */
632   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
633   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
634   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
635   
636   /*
637    * TLV t(000f) -- Unknown
638    */
639   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
640   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
641   
642   /*
643    * TLV t(000c) -- Invitation message
644    */
645   curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg);
646
647   /*
648    * TLV t(2711) -- Container for room information 
649    */
650   curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
651   curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2);
652   curbyte += aimutil_put16(newpacket->data+curbyte, exchange);
653   curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname));
654   curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname));
655   curbyte += aimutil_put16(newpacket->data+curbyte, instance);
656
657   newpacket->commandlen = curbyte;
658   newpacket->lock = 0;
659   aim_tx_enqueue(sess, newpacket);
660
661   return (sess->snac_nextid++);
662 }
This page took 0.07223 seconds and 3 git commands to generate.