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