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