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