]> andersk Git - libfaim.git/blob - aim_im.c
54adc738a84709319a00d89bcbd616f9a4e774d4
[libfaim.git] / aim_im.c
1 /*
2  *  aim_im.c
3  *
4  *  The routines for sending/receiving Instant Messages.
5  *
6  */
7
8 #include "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_conn_t *conn, char *destsn, int flags, char *msg)
21 {   
22
23   int curbyte,i;
24   struct command_tx_struct newpacket;
25   
26   newpacket.lock = 1; /* lock struct */
27   newpacket.type = 0x02; /* IMs are always family 0x02 */
28   if (conn)
29     newpacket.conn = conn;
30   else
31     newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
32
33   /*
34    * Its simplest to set this arbitrarily large and waste
35    * space.  Precalculating is costly here.
36    */
37   newpacket.commandlen = 1152;
38
39   newpacket.data = (char *) calloc(1, newpacket.commandlen);
40
41   curbyte  = 0;
42   curbyte += aim_putsnac(newpacket.data+curbyte, 
43                          0x0004, 0x0006, 0x0000, aim_snac_nextid);
44
45   /* 
46    * Generate a random message cookie 
47    */
48   for (i=0;i<8;i++)
49     curbyte += aimutil_put8(newpacket.data+curbyte, (u_char) random());
50
51   /*
52    * Channel ID
53    */
54   curbyte += aimutil_put16(newpacket.data+curbyte,0x0001);
55
56   /* 
57    * Destination SN (prepended with byte length)
58    */
59   curbyte += aimutil_put8(newpacket.data+curbyte,strlen(destsn));
60   curbyte += aimutil_putstr(newpacket.data+curbyte, destsn, strlen(destsn));
61
62   /*
63    * metaTLV start.
64    */
65   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0002);
66   curbyte += aimutil_put16(newpacket.data+curbyte, strlen(msg) + 0x0d);
67
68   /*
69    * Flag data?
70    */
71   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0501);
72   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0001);
73   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0101);
74   curbyte += aimutil_put8 (newpacket.data+curbyte, 0x01);
75
76   /* 
77    * Message block length.
78    */
79   curbyte += aimutil_put16(newpacket.data+curbyte, strlen(msg) + 0x04);
80
81   /*
82    * Character set data? 
83    */
84   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
85   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
86
87   /*
88    * Message.  Not terminated.
89    */
90   curbyte += aimutil_putstr(newpacket.data+curbyte,msg, strlen(msg));
91
92   /*
93    * Set the Request Acknowledge flag.  
94    */
95   if (flags & AIM_IMFLAGS_ACK)
96     {
97       curbyte += aimutil_put16(newpacket.data+curbyte,0x0003);
98       curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
99     }
100   
101   /*
102    * Set the Autoresponse flag.
103    */
104   if (flags & AIM_IMFLAGS_AWAY)
105     {
106       curbyte += aimutil_put16(newpacket.data+curbyte,0x0004);
107       curbyte += aimutil_put16(newpacket.data+curbyte,0x0000);
108     }
109   
110   newpacket.commandlen = curbyte;
111
112   aim_tx_enqueue(&newpacket);
113
114 #ifdef USE_SNAC_FOR_IMS
115  {
116     struct aim_snac_t snac;
117
118     snac.id = aim_snac_nextid;
119     snac.family = 0x0004;
120     snac.type = 0x0006;
121     snac.flags = 0x0000;
122
123     snac.data = malloc(strlen(destsn)+1);
124     memcpy(snac.data, destsn, strlen(destsn)+1);
125
126     aim_newsnac(&snac);
127   }
128
129  aim_cleansnacs(60); /* clean out all SNACs over 60sec old */
130 #endif
131
132   return (aim_snac_nextid++);
133 }
134
135 /*
136  * It can easily be said that parsing ICBMs is THE single
137  * most difficult thing to do in the in AIM protocol.  In
138  * fact, I think I just did say that.
139  *
140  * Below is the best damned solution I've come up with
141  * over the past sixteen months of battling with it. This
142  * can parse both away and normal messages from every client
143  * I have access to.  Its not fast, its not clean.  But it works.
144  *
145  * We should also support at least minimal parsing of 
146  * Channel 2, so that we can at least know the name of the
147  * room we're invited to, but obviously can't attend...
148  *
149  */
150 int aim_parse_incoming_im_middle(struct command_rx_struct *command)
151 {
152   struct aim_userinfo_s userinfo;
153   u_int i = 0, j = 0, y = 0, z = 0;
154   char *msg = NULL;
155   int isautoresponse = 0;
156   rxcallback_t userfunc = NULL;
157   u_char cookie[8];
158   int channel;
159   struct aim_tlvlist_t *tlvlist;
160   struct aim_tlv_t *msgblocktlv, *tmptlv;
161   u_char *msgblock;
162   u_short wastebits;
163   u_short flag1,flag2;
164
165   memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
166   
167   i = 10; /* Skip SNAC header */
168
169   /*
170    * Read ICBM Cookie.  And throw away.
171    */
172   for (z=0; z<8; z++,i++)
173     cookie[z] = command->data[i];
174   
175   /*
176    * Channel ID.
177    *
178    * Channel 0x0001 is the message channel.  There are 
179    * other channels for things called "rendevous"
180    * which represent chat and some of the other new
181    * features of AIM2/3/3.5.  We only support 
182    * standard messages; those on channel 0x0001.
183    */
184   channel = aimutil_get16(command->data+i);
185   i += 2;
186   if (channel != 0x0001)
187     {
188       printf("faim: icbm: ICBM received on an unsupported channel.  Ignoring.\n (chan = %04x)", channel);
189       return 1;
190     }
191
192   /*
193    * Source screen name.
194    */
195   memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]);
196   userinfo.sn[(int)command->data[i]] = '\0';
197   i += 1 + (int)command->data[i];
198
199   /*
200    * Unknown bits.
201    */
202   wastebits = aimutil_get16(command->data+i);
203   i += 2;
204   wastebits = aimutil_get16(command->data+i);
205   i += 2;
206
207   /*
208    * Read block of TLVs.  All further data is derived
209    * from what is parsed here.
210    */
211   tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
212
213   /*
214    * Check Autoresponse status.  If it is an autoresponse,
215    * it will contain a second type 0x0004 TLV, with zero length.
216    */
217   if (aim_gettlv(tlvlist, 0x0004, 2))
218     isautoresponse = 1;
219
220   /*
221    * Extract the various pieces of the userinfo struct.
222    */
223   /* Class. */
224   if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
225     userinfo.class = aimutil_get16(tmptlv->value);
226   /* Member-since date. */
227   if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1)))
228     {
229       /* If this is larger than 4, its probably the message block, skip */
230       if (tmptlv->length <= 4)
231         userinfo.membersince = aimutil_get32(tmptlv->value);
232     }
233   /* On-since date */
234   if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
235     userinfo.onlinesince = aimutil_get32(tmptlv->value);
236   /* Idle-time */
237   if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
238     userinfo.idletime = aimutil_get16(tmptlv->value);
239   /* Session Length (AIM) */
240   if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
241     userinfo.sessionlen = aimutil_get16(tmptlv->value);
242   /* Session Length (AOL) */
243   if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
244     userinfo.sessionlen = aimutil_get16(tmptlv->value);
245
246   /*
247    * Message block.
248    *
249    * XXX: Will the msgblock always be the second 0x0002? 
250    */
251   msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
252   if (!msgblocktlv)
253     {
254       printf("faim: icbm: major error! no message block TLV found!\n");
255       aim_freetlvchain(&tlvlist);
256     }
257
258   /*
259    * Extracting the message from the unknown cruft.
260    * 
261    * This is a bit messy, and I'm not really qualified,
262    * even as the author, to comment on it.  At least
263    * its not as bad as a while loop shooting into infinity.
264    *
265    * "Do you believe in magic?"
266    *
267    */
268   msgblock = msgblocktlv->value;
269   j = 0;
270
271   wastebits = aimutil_get8(msgblock+j++);
272   wastebits = aimutil_get8(msgblock+j++);
273   
274   y = aimutil_get16(msgblock+j);
275   j += 2;
276   for (z = 0; z < y; z++)
277     wastebits = aimutil_get8(msgblock+j++);
278   wastebits = aimutil_get8(msgblock+j++);
279   wastebits = aimutil_get8(msgblock+j++);
280  
281   /* 
282    * Message string length, including flag words.
283    */
284   i = aimutil_get16(msgblock+j);
285   j += 2;
286
287   /*
288    * Flag words.
289    *
290    * Its rumored that these can kick in some funky
291    * 16bit-wide char stuff that used to really kill
292    * libfaim.  Hopefully the latter is no longer true.
293    *
294    * Though someone should investiagte the former.
295    *
296    */
297   flag1 = aimutil_get16(msgblock+j);
298   j += 2;
299   flag2 = aimutil_get16(msgblock+j);
300   j += 2;
301
302   if (flag1 || flag2)
303     printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2);
304
305   /* 
306    * Message string. 
307    */
308   i -= 4;
309   msg = (char *)malloc(i+1);
310   memcpy(msg, msgblock+j, i);
311   msg[i] = '\0';
312
313   /*
314    * Free up the TLV chain.
315    */
316   aim_freetlvchain(&tlvlist);
317
318   /*
319    * Call client.
320    */
321   userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
322   if (userfunc)
323     i = userfunc(command, &userinfo, msg, isautoresponse, flag1, flag2);
324   else 
325     i = 0;
326
327   free(msg);
328
329   return 1;
330 }
331
332 /*
333  * Not real sure what this does, nor does anyone I've talk to.
334  *
335  * Didn't use to send it.  But now I think it might be a good
336  * idea. 
337  *
338  */
339 u_long aim_seticbmparam(struct aim_conn_t *conn)
340 {
341   struct command_tx_struct newpacket;
342   int curbyte;
343
344   newpacket.lock = 1;
345   if (conn)
346     newpacket.conn = conn;
347   else
348     newpacket.conn = aim_getconn_type(AIM_CONN_TYPE_BOS);
349   newpacket.type = 0x02;
350
351   newpacket.commandlen = 10 + 16;
352   newpacket.data = (u_char *) malloc (newpacket.commandlen);
353
354   curbyte = aim_putsnac(newpacket.data, 0x0004, 0x0002, 0x0000, aim_snac_nextid);
355   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
356   curbyte += aimutil_put32(newpacket.data+curbyte, 0x00000003);
357   curbyte += aimutil_put8(newpacket.data+curbyte,  0x1f);
358   curbyte += aimutil_put8(newpacket.data+curbyte,  0x40);
359   curbyte += aimutil_put8(newpacket.data+curbyte,  0x03);
360   curbyte += aimutil_put8(newpacket.data+curbyte,  0xe7);
361   curbyte += aimutil_put8(newpacket.data+curbyte,  0x03);
362   curbyte += aimutil_put8(newpacket.data+curbyte,  0xe7);
363   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
364   curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
365
366   aim_tx_enqueue(&newpacket);
367
368   return (aim_snac_nextid++);
369 }
This page took 2.030909 seconds and 3 git commands to generate.