]> andersk Git - libfaim.git/blob - aim_im.c
- Thu Mar 23 08:45:40 UTC 2000
[libfaim.git] / aim_im.c
1 /*
2  *  aim_im.c
3  *
4  *  The routines for sending/receiving Instant Messages.
5  *
6  */
7
8 #include <faim/aim.h>
9
10 /*
11  * Send an ICBM (instant message).  
12  *
13  *
14  * Possible flags:
15  *   AIM_IMFLAGS_AWAY  -- Marks the message as an autoresponse
16  *   AIM_IMFLAGS_ACK   -- Requests that the server send an ack
17  *                        when the message is received (of type 0x0004/0x000c)
18  *
19  */
20 u_long aim_send_im(struct aim_session_t *sess,
21                    struct aim_conn_t *conn, 
22                    char *destsn, u_int flags, char *msg)
23 {   
24
25   int curbyte,i;
26   struct command_tx_struct newpacket;
27   
28   newpacket.lock = 1; /* lock struct */
29   newpacket.type = 0x02; /* IMs are always family 0x02 */
30   if (conn)
31     newpacket.conn = conn;
32   else
33     newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
34
35   /*
36    * Its simplest to set this arbitrarily large and waste
37    * space.  Precalculating is costly here.
38    */
39   newpacket.commandlen = 1152;
40
41   newpacket.data = (u_char *) calloc(1, newpacket.commandlen);
42
43   curbyte  = 0;
44   curbyte += aim_putsnac(newpacket.data+curbyte, 
45                          0x0004, 0x0006, 0x0000, sess->snac_nextid);
46
47   /* 
48    * Generate a random message cookie 
49    *
50    * We could cache these like we do SNAC IDs.  (In fact, it 
51    * might be a good idea.)  In the message error functions, 
52    * the 8byte message cookie is returned as well as the 
53    * SNAC ID.
54    *
55    */
56   for (i=0;i<8;i++)
57     curbyte += aimutil_put8(newpacket.data+curbyte, (u_char) random());
58
59   /*
60    * Channel ID
61    */
62   curbyte += aimutil_put16(newpacket.data+curbyte,0x0001);
63
64   /* 
65    * Destination SN (prepended with byte length)
66    */
67   curbyte += aimutil_put8(newpacket.data+curbyte,strlen(destsn));
68   curbyte += aimutil_putstr(newpacket.data+curbyte, destsn, strlen(destsn));
69
70   /*
71    * metaTLV start.
72    */
73   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0002);
74   curbyte += aimutil_put16(newpacket.data+curbyte, strlen(msg) + 0x0d);
75
76   /*
77    * Flag data?
78    */
79   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0501);
80   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
81   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0101);
82   curbyte += aimutil_put8 (newpacket.data+curbyte, 0x01);
83
84   /* 
85    * Message block length.
86    */
87   curbyte += aimutil_put16(newpacket.data+curbyte, strlen(msg) + 0x04);
88
89   /*
90    * Character set data? 
91    */
92   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
93   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
94
95   /*
96    * Message.  Not terminated.
97    */
98   curbyte += aimutil_putstr(newpacket.data+curbyte,msg, strlen(msg));
99
100   /*
101    * Set the Request Acknowledge flag.  
102    */
103   if (flags & AIM_IMFLAGS_ACK)
104     {
105       curbyte += aimutil_put16(newpacket.data+curbyte,0x0003);
106       curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
107     }
108   
109   /*
110    * Set the Autoresponse flag.
111    */
112   if (flags & AIM_IMFLAGS_AWAY)
113     {
114       curbyte += aimutil_put16(newpacket.data+curbyte,0x0004);
115       curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
116     }
117   
118   newpacket.commandlen = curbyte;
119
120   aim_tx_enqueue(sess, &newpacket);
121
122 #ifdef USE_SNAC_FOR_IMS
123  {
124     struct aim_snac_t snac;
125
126     snac.id = sess->snac_nextid;
127     snac.family = 0x0004;
128     snac.type = 0x0006;
129     snac.flags = 0x0000;
130
131     snac.data = malloc(strlen(destsn)+1);
132     memcpy(snac.data, destsn, strlen(destsn)+1);
133
134     aim_newsnac(sess, &snac);
135   }
136
137  aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
138 #endif
139
140   return (sess->snac_nextid++);
141 }
142
143 /*
144  * It can easily be said that parsing ICBMs is THE single
145  * most difficult thing to do in the in AIM protocol.  In
146  * fact, I think I just did say that.
147  *
148  * Below is the best damned solution I've come up with
149  * over the past sixteen months of battling with it. This
150  * can parse both away and normal messages from every client
151  * I have access to.  Its not fast, its not clean.  But it works.
152  *
153  * We should also support at least minimal parsing of 
154  * Channel 2, so that we can at least know the name of the
155  * room we're invited to, but obviously can't attend...
156  *
157  */
158 int aim_parse_incoming_im_middle(struct aim_session_t *sess,
159                                  struct command_rx_struct *command)
160 {
161   u_int i = 0,z;
162   rxcallback_t userfunc = NULL;
163   u_char cookie[8];
164   int channel;
165   struct aim_tlvlist_t *tlvlist;
166   struct aim_userinfo_s userinfo;
167   u_short wastebits;
168
169   memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
170  
171   i = 10; /* Skip SNAC header */
172
173   /*
174    * Read ICBM Cookie.  And throw away.
175    */
176   for (z=0; z<8; z++,i++)
177     cookie[z] = command->data[i];
178   
179   /*
180    * Channel ID.
181    *
182    * Channel 0x0001 is the message channel.  There are 
183    * other channels for things called "rendevous"
184    * which represent chat and some of the other new
185    * features of AIM2/3/3.5. 
186    *
187    * Channel 0x0002 is the Rendevous channel, which
188    * is where Chat Invitiations and various client-client
189    * connection negotiations come from.
190    * 
191    */
192   channel = aimutil_get16(command->data+i);
193   i += 2;
194   
195   /*
196    *
197    */
198   if ((channel != 0x01) && (channel != 0x02))
199     {
200       printf("faim: icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
201       return 1;
202     }
203
204   /*
205    * Source screen name.
206    */
207   memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]);
208   userinfo.sn[(int)command->data[i]] = '\0';
209   i += 1 + (int)command->data[i];
210
211   /*
212    * Warning Level
213    */
214   userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */
215   i += 2;
216
217   /*
218    * Number of TLVs that follow.  Not needed.
219    */
220   wastebits = aimutil_get16(command->data+i);
221   i += 2;
222   
223   /*
224    * Read block of TLVs.  All further data is derived
225    * from what is parsed here.
226    */
227   tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
228
229   /*
230    * From here on, its depends on what channel we're on.
231    */
232   if (channel == 1)
233     {
234      u_int j = 0, y = 0, z = 0;
235       char *msg = NULL;
236       u_int icbmflags = 0;
237       struct aim_tlv_t *msgblocktlv, *tmptlv;
238       u_char *msgblock;
239       u_short flag1,flag2;
240            
241       /*
242        * Check Autoresponse status.  If it is an autoresponse,
243        * it will contain a second type 0x0004 TLV, with zero length.
244        */
245       if (aim_gettlv(tlvlist, 0x0004, 2))
246         icbmflags |= AIM_IMFLAGS_AWAY;
247       
248       /*
249        * Check Ack Request status.
250        */
251       if (aim_gettlv(tlvlist, 0x0003, 2))
252         icbmflags |= AIM_IMFLAGS_ACK;
253       
254       /*
255        * Extract the various pieces of the userinfo struct.
256        */
257       /* Class. */
258       if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
259         userinfo.class = aimutil_get16(tmptlv->value);
260       /* Member-since date. */
261       if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1)))
262         {
263           /* If this is larger than 4, its probably the message block, skip */
264           if (tmptlv->length <= 4)
265             userinfo.membersince = aimutil_get32(tmptlv->value);
266         }
267       /* On-since date */
268       if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
269         userinfo.onlinesince = aimutil_get32(tmptlv->value);
270       /* Idle-time */
271       if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
272         userinfo.idletime = aimutil_get16(tmptlv->value);
273       /* Session Length (AIM) */
274       if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
275         userinfo.sessionlen = aimutil_get16(tmptlv->value);
276       /* Session Length (AOL) */
277       if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
278         userinfo.sessionlen = aimutil_get16(tmptlv->value);
279       
280       /*
281        * Message block.
282        *
283        * XXX: Will the msgblock always be the second 0x0002? 
284        */
285       msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
286       if (!msgblocktlv)
287         {
288           printf("faim: icbm: major error! no message block TLV found!\n");
289           aim_freetlvchain(&tlvlist);
290           return 1;
291         }
292       
293       /*
294        * Extracting the message from the unknown cruft.
295        * 
296        * This is a bit messy, and I'm not really qualified,
297        * even as the author, to comment on it.  At least
298        * its not as bad as a while loop shooting into infinity.
299        *
300        * "Do you believe in magic?"
301        *
302        */
303       msgblock = msgblocktlv->value;
304       j = 0;
305       
306       wastebits = aimutil_get8(msgblock+j++);
307       wastebits = aimutil_get8(msgblock+j++);
308       
309       y = aimutil_get16(msgblock+j);
310       j += 2;
311       for (z = 0; z < y; z++)
312         wastebits = aimutil_get8(msgblock+j++);
313       wastebits = aimutil_get8(msgblock+j++);
314       wastebits = aimutil_get8(msgblock+j++);
315       
316       /* 
317        * Message string length, including flag words.
318        */
319       i = aimutil_get16(msgblock+j);
320       j += 2;
321       
322       /*
323        * Flag words.
324        *
325        * Its rumored that these can kick in some funky
326        * 16bit-wide char stuff that used to really kill
327        * libfaim.  Hopefully the latter is no longer true.
328        *
329        * Though someone should investiagte the former.
330        *
331        */
332       flag1 = aimutil_get16(msgblock+j);
333       j += 2;
334       flag2 = aimutil_get16(msgblock+j);
335       j += 2;
336       
337       if (flag1 || flag2)
338         printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2);
339       
340       /* 
341        * Message string. 
342        */
343       i -= 4;
344       msg = (char *)malloc(i+1);
345       memcpy(msg, msgblock+j, i);
346       msg[i] = '\0';
347       
348       /*
349        * Call client.
350        */
351       userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
352       if (userfunc)
353         i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2);
354       else 
355         i = 0;
356       
357       free(msg);
358     }
359   else if (channel == 0x0002)
360     {   
361       int rendtype;
362       struct aim_tlv_t *block1;
363       struct aim_tlvlist_t *list2;
364       struct aim_tlv_t *tmptlv;
365       int a;
366       
367       /* Class. */
368       if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
369         userinfo.class = aimutil_get16(tmptlv->value);
370       /* On-since date */
371       if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
372         userinfo.onlinesince = aimutil_get32(tmptlv->value);
373       /* Idle-time */
374       if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
375         userinfo.idletime = aimutil_get16(tmptlv->value);
376       /* Session Length (AIM) */
377       if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
378         userinfo.sessionlen = aimutil_get16(tmptlv->value);
379       /* Session Length (AOL) */
380       if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
381         userinfo.sessionlen = aimutil_get16(tmptlv->value);
382
383       /*
384        * There's another block of TLVs embedded in the type 5 here. 
385        */
386       block1 = aim_gettlv(tlvlist, 0x0005, 1);
387       if (!block1)
388         return 1; /* major problem */
389
390       a = 0x1a; /* skip -- not sure what this information is! */
391
392       /*
393        * XXX: Ignore if there's no data, only cookie information.
394        *
395        * Its probably just an accepted invitation or something.
396        *  
397        */
398       if (block1->length <= 0x1a)
399         {
400           aim_freetlvchain(&tlvlist);
401           return 1;
402         }
403
404       list2 = aim_readtlvchain(block1->value+a, block1->length-a);
405
406       if (aim_gettlv(list2, 0x0004, 1) /* start connection */ ||  
407           aim_gettlv(list2, 0x000b, 1) /* close conncetion */)
408         {
409           rendtype = 1; /* voice request */
410
411           /*
412            * Call client.
413            */
414           userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
415           if (userfunc)
416             i = userfunc(sess, 
417                          command, 
418                          channel, 
419                          rendtype,
420                          &userinfo);
421           else 
422             i = 0;
423         }
424       else
425         {
426           struct aim_chat_roominfo roominfo;
427           char *msg=NULL,*encoding=NULL,*lang=NULL;
428
429           rendtype = 0; /* chat invite */
430           if (aim_gettlv(list2, 0x2711, 1))
431             {
432               struct aim_tlv_t *nametlv;
433               
434               nametlv = aim_gettlv(list2, 0x2711, 1);
435               aim_chat_readroominfo(nametlv->value, &roominfo);
436             }
437           
438           if (aim_gettlv(list2, 0x000c, 1))
439             msg = aim_gettlv_str(list2, 0x000c, 1);
440           
441           if (aim_gettlv(list2, 0x000d, 1))
442             encoding = aim_gettlv_str(list2, 0x000d, 1);
443           
444           if (aim_gettlv(list2, 0x000e, 1))
445             lang = aim_gettlv_str(list2, 0x000e, 1);
446       
447           /*
448            * Call client.
449            */
450           userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
451           if (userfunc)
452             i = userfunc(sess, 
453                          command, 
454                          channel, 
455                          rendtype,
456                          &userinfo, 
457                          &roominfo, 
458                          msg, 
459                          encoding?encoding+1:NULL, 
460                          lang?lang+1:NULL);
461           else 
462             i = 0;
463       
464           free(roominfo.name);
465           free(msg);
466           free(encoding);
467           free(lang);
468         }
469       aim_freetlvchain(&list2);
470     }
471
472   /*
473    * Free up the TLV chain.
474    */
475   aim_freetlvchain(&tlvlist);
476   
477
478   return i;
479 }
480
481 /*
482  * Not real sure what this does, nor does anyone I've talk to.
483  *
484  * Didn't use to send it.  But now I think it might be a good
485  * idea. 
486  *
487  */
488 u_long aim_seticbmparam(struct aim_session_t *sess,
489                         struct aim_conn_t *conn)
490 {
491   struct command_tx_struct newpacket;
492   int curbyte;
493
494   newpacket.lock = 1;
495   if (conn)
496     newpacket.conn = conn;
497   else
498     newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
499   newpacket.type = 0x02;
500
501   newpacket.commandlen = 10 + 16;
502   newpacket.data = (u_char *) malloc (newpacket.commandlen);
503
504   curbyte = aim_putsnac(newpacket.data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
505   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
506   curbyte += aimutil_put32(newpacket.data+curbyte, 0x00000003);
507   curbyte += aimutil_put8(newpacket.data+curbyte,  0x1f);
508   curbyte += aimutil_put8(newpacket.data+curbyte,  0x40);
509   curbyte += aimutil_put8(newpacket.data+curbyte,  0x03);
510   curbyte += aimutil_put8(newpacket.data+curbyte,  0xe7);
511   curbyte += aimutil_put8(newpacket.data+curbyte,  0x03);
512   curbyte += aimutil_put8(newpacket.data+curbyte,  0xe7);
513   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
514   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
515
516   aim_tx_enqueue(sess, &newpacket);
517
518   return (sess->snac_nextid++);
519 }
520
521 int aim_parse_msgerror_middle(struct aim_session_t *sess,
522                               struct command_rx_struct *command)
523 {
524   u_long snacid = 0x000000000;
525   struct aim_snac_t *snac = NULL;
526   int ret = 0;
527   rxcallback_t userfunc = NULL;
528
529   /*
530    * Get SNAC from packet and look it up 
531    * the list of unrepliedto/outstanding
532    * SNACs.
533    *
534    * After its looked up, the SN that the
535    * message should've gone to will be 
536    * in the ->data element of the snac struct.
537    *
538    */
539   snacid = aimutil_get32(command->data+6);
540   snac = aim_remsnac(sess, snacid);
541
542   if (!snac)
543     {
544       printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
545     }
546
547   /*
548    * Call client.
549    */
550   userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
551   if (userfunc)
552     ret =  userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)");
553   else
554     ret = 0;
555   
556   free(snac->data);
557   free(snac);
558
559   return ret;
560 }
561
562
This page took 0.125784 seconds and 5 git commands to generate.