]> andersk Git - libfaim.git/blame_incremental - aim_im.c
Stupid offgoing buddy middle handler bug.
[libfaim.git] / aim_im.c
... / ...
CommitLineData
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 */
20u_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 */
158int aim_parse_incoming_im_middle(struct aim_session_t *sess,
159 struct command_rx_struct *command)
160{
161 struct aim_userinfo_s userinfo;
162 u_int i = 0, j = 0, y = 0, z = 0;
163 char *msg = NULL;
164 u_int icbmflags = 0;
165 rxcallback_t userfunc = NULL;
166 u_char cookie[8];
167 int channel;
168 struct aim_tlvlist_t *tlvlist;
169 struct aim_tlv_t *msgblocktlv, *tmptlv;
170 u_char *msgblock;
171 u_short wastebits;
172 u_short flag1,flag2;
173
174 memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
175
176 i = 10; /* Skip SNAC header */
177
178 /*
179 * Read ICBM Cookie. And throw away.
180 */
181 for (z=0; z<8; z++,i++)
182 cookie[z] = command->data[i];
183
184 /*
185 * Channel ID.
186 *
187 * Channel 0x0001 is the message channel. There are
188 * other channels for things called "rendevous"
189 * which represent chat and some of the other new
190 * features of AIM2/3/3.5. We only support
191 * standard messages; those on channel 0x0001.
192 */
193 channel = aimutil_get16(command->data+i);
194 i += 2;
195 if (channel != 0x0001)
196 {
197 printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel);
198 return 1;
199 }
200
201 /*
202 * Source screen name.
203 */
204 memcpy(userinfo.sn, command->data+i+1, (int)command->data[i]);
205 userinfo.sn[(int)command->data[i]] = '\0';
206 i += 1 + (int)command->data[i];
207
208 /*
209 * Unknown bits.
210 */
211 wastebits = aimutil_get16(command->data+i);
212 i += 2;
213 wastebits = aimutil_get16(command->data+i);
214 i += 2;
215
216 /*
217 * Read block of TLVs. All further data is derived
218 * from what is parsed here.
219 */
220 tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
221
222 /*
223 * Check Autoresponse status. If it is an autoresponse,
224 * it will contain a second type 0x0004 TLV, with zero length.
225 */
226 if (aim_gettlv(tlvlist, 0x0004, 2))
227 icbmflags |= AIM_IMFLAGS_AWAY;
228
229 /*
230 * Check Ack Request status.
231 */
232 if (aim_gettlv(tlvlist, 0x0003, 2))
233 icbmflags |= AIM_IMFLAGS_ACK;
234
235 /*
236 * Extract the various pieces of the userinfo struct.
237 */
238 /* Class. */
239 if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
240 userinfo.class = aimutil_get16(tmptlv->value);
241 /* Member-since date. */
242 if ((tmptlv = aim_gettlv(tlvlist, 0x0002, 1)))
243 {
244 /* If this is larger than 4, its probably the message block, skip */
245 if (tmptlv->length <= 4)
246 userinfo.membersince = aimutil_get32(tmptlv->value);
247 }
248 /* On-since date */
249 if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
250 userinfo.onlinesince = aimutil_get32(tmptlv->value);
251 /* Idle-time */
252 if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
253 userinfo.idletime = aimutil_get16(tmptlv->value);
254 /* Session Length (AIM) */
255 if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
256 userinfo.sessionlen = aimutil_get16(tmptlv->value);
257 /* Session Length (AOL) */
258 if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
259 userinfo.sessionlen = aimutil_get16(tmptlv->value);
260
261 /*
262 * Message block.
263 *
264 * XXX: Will the msgblock always be the second 0x0002?
265 */
266 msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
267 if (!msgblocktlv)
268 {
269 printf("faim: icbm: major error! no message block TLV found!\n");
270 aim_freetlvchain(&tlvlist);
271 }
272
273 /*
274 * Extracting the message from the unknown cruft.
275 *
276 * This is a bit messy, and I'm not really qualified,
277 * even as the author, to comment on it. At least
278 * its not as bad as a while loop shooting into infinity.
279 *
280 * "Do you believe in magic?"
281 *
282 */
283 msgblock = msgblocktlv->value;
284 j = 0;
285
286 wastebits = aimutil_get8(msgblock+j++);
287 wastebits = aimutil_get8(msgblock+j++);
288
289 y = aimutil_get16(msgblock+j);
290 j += 2;
291 for (z = 0; z < y; z++)
292 wastebits = aimutil_get8(msgblock+j++);
293 wastebits = aimutil_get8(msgblock+j++);
294 wastebits = aimutil_get8(msgblock+j++);
295
296 /*
297 * Message string length, including flag words.
298 */
299 i = aimutil_get16(msgblock+j);
300 j += 2;
301
302 /*
303 * Flag words.
304 *
305 * Its rumored that these can kick in some funky
306 * 16bit-wide char stuff that used to really kill
307 * libfaim. Hopefully the latter is no longer true.
308 *
309 * Though someone should investiagte the former.
310 *
311 */
312 flag1 = aimutil_get16(msgblock+j);
313 j += 2;
314 flag2 = aimutil_get16(msgblock+j);
315 j += 2;
316
317 if (flag1 || flag2)
318 printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2);
319
320 /*
321 * Message string.
322 */
323 i -= 4;
324 msg = (char *)malloc(i+1);
325 memcpy(msg, msgblock+j, i);
326 msg[i] = '\0';
327
328 /*
329 * Free up the TLV chain.
330 */
331 aim_freetlvchain(&tlvlist);
332
333 /*
334 * Call client.
335 */
336 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
337 if (userfunc)
338 i = userfunc(sess, command, &userinfo, msg, icbmflags, flag1, flag2);
339 else
340 i = 0;
341
342 free(msg);
343
344 return 1;
345}
346
347/*
348 * Not real sure what this does, nor does anyone I've talk to.
349 *
350 * Didn't use to send it. But now I think it might be a good
351 * idea.
352 *
353 */
354u_long aim_seticbmparam(struct aim_session_t *sess,
355 struct aim_conn_t *conn)
356{
357 struct command_tx_struct newpacket;
358 int curbyte;
359
360 newpacket.lock = 1;
361 if (conn)
362 newpacket.conn = conn;
363 else
364 newpacket.conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
365 newpacket.type = 0x02;
366
367 newpacket.commandlen = 10 + 16;
368 newpacket.data = (u_char *) malloc (newpacket.commandlen);
369
370 curbyte = aim_putsnac(newpacket.data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
371 curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
372 curbyte += aimutil_put32(newpacket.data+curbyte, 0x00000003);
373 curbyte += aimutil_put8(newpacket.data+curbyte, 0x1f);
374 curbyte += aimutil_put8(newpacket.data+curbyte, 0x40);
375 curbyte += aimutil_put8(newpacket.data+curbyte, 0x03);
376 curbyte += aimutil_put8(newpacket.data+curbyte, 0xe7);
377 curbyte += aimutil_put8(newpacket.data+curbyte, 0x03);
378 curbyte += aimutil_put8(newpacket.data+curbyte, 0xe7);
379 curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
380 curbyte += aimutil_put16(newpacket.data+curbyte, 0x0000);
381
382 aim_tx_enqueue(sess, &newpacket);
383
384 return (sess->snac_nextid++);
385}
386
387int aim_parse_msgerror_middle(struct aim_session_t *sess,
388 struct command_rx_struct *command)
389{
390 u_long snacid = 0x000000000;
391 struct aim_snac_t *snac = NULL;
392 int ret = 0;
393 rxcallback_t userfunc = NULL;
394
395 /*
396 * Get SNAC from packet and look it up
397 * the list of unrepliedto/outstanding
398 * SNACs.
399 *
400 * After its looked up, the SN that the
401 * message should've gone to will be
402 * in the ->data element of the snac struct.
403 *
404 */
405 snacid = aimutil_get32(command->data+6);
406 snac = aim_remsnac(sess, snacid);
407
408 if (!snac)
409 {
410 printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
411 }
412
413 /*
414 * Call client.
415 */
416 userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
417 if (userfunc)
418 ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)");
419 else
420 ret = 0;
421
422 free(snac->data);
423 free(snac);
424
425 return ret;
426}
This page took 0.154223 seconds and 5 git commands to generate.