]> andersk Git - libfaim.git/blob - aim_chat.c
- Wed Nov 8 02:23:25 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 (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 2 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   u_char cookie[8];
426   int channel;
427   struct aim_tlvlist_t *outerlist;
428   char *msg = NULL;
429
430   memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
431
432   i = 10; /* skip snac */
433
434   /*
435    * ICBM Cookie.  Cache it.
436    */ 
437   for (z=0; z<8; z++,i++)
438     cookie[z] = command->data[i];
439
440   aim_cachecookie(sess, aim_mkcookie(cookie, AIM_COOKIETYPE_ICBM, NULL));
441
442   /*
443    * Channel ID
444    *
445    * Channels 1 and 2 are implemented in the normal ICBM
446    * parser.
447    *
448    * We only do channel 3 here.
449    *
450    */
451   channel = aimutil_get16(command->data+i);
452   i += 2;
453
454   if (channel != 0x0003) {
455     printf("faim: chat_incoming: unknown channel! (0x%04x)\n", channel);
456     return 1;
457   }
458
459   /*
460    * Start parsing TLVs right away. 
461    */
462   outerlist = aim_readtlvchain(command->data+i, command->commandlen-i);
463   
464   /*
465    * Type 0x0003: Source User Information
466    */
467   if (aim_gettlv(outerlist, 0x0003, 1)) {
468     struct aim_tlv_t *userinfotlv;
469     
470     userinfotlv = aim_gettlv(outerlist, 0x0003, 1);
471     aim_extractuserinfo(userinfotlv->value, &userinfo);
472   }
473
474   /*
475    * Type 0x0001: Unknown.
476    */
477   if (aim_gettlv(outerlist, 0x0001, 1))
478     ;
479
480   /*
481    * Type 0x0005: Message Block.  Conains more TLVs.
482    */
483   if (aim_gettlv(outerlist, 0x0005, 1))
484     {
485       struct aim_tlvlist_t *innerlist;
486       struct aim_tlv_t *msgblock;
487
488       msgblock = aim_gettlv(outerlist, 0x0005, 1);
489       innerlist = aim_readtlvchain(msgblock->value, msgblock->length);
490       
491       /* 
492        * Type 0x0001: Message.
493        */       
494       if (aim_gettlv(innerlist, 0x0001, 1))
495           msg = aim_gettlv_str(innerlist, 0x0001, 1);
496
497       aim_freetlvchain(&innerlist); 
498     }
499
500   userfunc = aim_callhandler(command->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG);
501   if (userfunc)
502     {
503       ret = userfunc(sess,
504                      command, 
505                      &userinfo,
506                      msg);
507     }
508   free(msg);
509   aim_freetlvchain(&outerlist);
510
511   return ret;
512 }             
513
514 faim_export unsigned long aim_chat_clientready(struct aim_session_t *sess,
515                                                struct aim_conn_t *conn)
516 {
517   struct command_tx_struct *newpacket;
518   int i;
519
520   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 0x20)))
521     return -1;
522
523   newpacket->lock = 1;
524
525   i = aim_putsnac(newpacket->data, 0x0001, 0x0002, 0x0000, sess->snac_nextid);
526
527   i+= aimutil_put16(newpacket->data+i, 0x000e);
528   i+= aimutil_put16(newpacket->data+i, 0x0001);
529
530   i+= aimutil_put16(newpacket->data+i, 0x0004);
531   i+= aimutil_put16(newpacket->data+i, 0x0001);
532
533   i+= aimutil_put16(newpacket->data+i, 0x0001);
534   i+= aimutil_put16(newpacket->data+i, 0x0003);
535
536   i+= aimutil_put16(newpacket->data+i, 0x0004);
537   i+= aimutil_put16(newpacket->data+i, 0x0686);
538
539   newpacket->lock = 0;
540   aim_tx_enqueue(sess, newpacket);
541
542   return (sess->snac_nextid++);
543 }
544
545 faim_export int aim_chat_leaveroom(struct aim_session_t *sess, char *name)
546 {
547   struct aim_conn_t *conn;
548
549   if ((conn = aim_chat_getconn(sess, name)))
550     aim_conn_kill(sess, &conn);
551
552   if (!conn)
553     return -1;
554   return 0;
555 }
556
557 /*
558  * conn must be a BOS connection!
559  */
560 faim_export unsigned long aim_chat_invite(struct aim_session_t *sess,
561                                           struct aim_conn_t *conn,
562                                           char *sn,
563                                           char *msg,
564                                           u_short exchange,
565                                           char *roomname,
566                                           u_short instance)
567 {
568   struct command_tx_struct *newpacket;
569   int i,curbyte=0;
570
571   if (!sess || !conn || !sn || !msg || !roomname)
572     return 0;
573
574   if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 1152+strlen(sn)+strlen(roomname)+strlen(msg))))
575     return -1;
576
577   newpacket->lock = 1;
578
579   curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
580
581   /*
582    * Cookie
583    */
584   for (i=0;i<8;i++)
585     curbyte += aimutil_put8(newpacket->data+curbyte, (u_char)rand());
586   aim_cachecookie(sess, aim_mkcookie(newpacket->data+curbyte-8, AIM_COOKIETYPE_CHAT, NULL));
587
588   /*
589    * Channel (2)
590    */
591   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
592
593   /*
594    * Dest sn
595    */
596   curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn));
597   curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn));
598
599   /*
600    * TLV t(0005)
601    */
602   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
603   curbyte += aimutil_put16(newpacket->data+curbyte, 0x28+strlen(msg)+0x04+0x03+strlen(roomname)+0x02);
604   
605   /* 
606    * Unknown info
607    */
608   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
609   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3131);
610   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3538);
611   curbyte += aimutil_put16(newpacket->data+curbyte, 0x3446);
612   curbyte += aimutil_put16(newpacket->data+curbyte, 0x4100);
613   curbyte += aimutil_put16(newpacket->data+curbyte, 0x748f);
614   curbyte += aimutil_put16(newpacket->data+curbyte, 0x2420);
615   curbyte += aimutil_put16(newpacket->data+curbyte, 0x6287);
616   curbyte += aimutil_put16(newpacket->data+curbyte, 0x11d1);
617   curbyte += aimutil_put16(newpacket->data+curbyte, 0x8222);
618   curbyte += aimutil_put16(newpacket->data+curbyte, 0x4445);
619   curbyte += aimutil_put16(newpacket->data+curbyte, 0x5354);
620   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
621   
622   /*
623    * TLV t(000a) -- Unknown
624    */
625   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
626   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
627   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
628   
629   /*
630    * TLV t(000f) -- Unknown
631    */
632   curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
633   curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
634   
635   /*
636    * TLV t(000c) -- Invitation message
637    */
638   curbyte += aim_puttlv_str(newpacket->data+curbyte, 0x000c, strlen(msg), msg);
639
640   /*
641    * TLV t(2711) -- Container for room information 
642    */
643   curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
644   curbyte += aimutil_put16(newpacket->data+curbyte, 3+strlen(roomname)+2);
645   curbyte += aimutil_put16(newpacket->data+curbyte, exchange);
646   curbyte += aimutil_put8(newpacket->data+curbyte, strlen(roomname));
647   curbyte += aimutil_putstr(newpacket->data+curbyte, roomname, strlen(roomname));
648   curbyte += aimutil_put16(newpacket->data+curbyte, instance);
649
650   newpacket->commandlen = curbyte;
651   newpacket->lock = 0;
652   aim_tx_enqueue(sess, newpacket);
653
654   return (sess->snac_nextid++);
655 }
This page took 0.347445 seconds and 5 git commands to generate.