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