4 * The routines for sending/receiving Instant Messages.
11 * Send an ICBM (instant message).
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)
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)
26 struct command_tx_struct *newpacket;
28 if (strlen(msg) >= MAXMSGLEN)
31 if (!(newpacket = aim_tx_new(0x0002, conn, strlen(msg)+256)))
34 newpacket->lock = 1; /* lock struct */
37 curbyte += aim_putsnac(newpacket->data+curbyte,
38 0x0004, 0x0006, 0x0000, sess->snac_nextid);
41 * Generate a random message cookie
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
50 curbyte += aimutil_put8(newpacket->data+curbyte, (u_char) random());
55 curbyte += aimutil_put16(newpacket->data+curbyte,0x0001);
58 * Destination SN (prepended with byte length)
60 curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
61 curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
66 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
67 curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x0d);
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);
78 * Message block length.
80 curbyte += aimutil_put16(newpacket->data+curbyte, strlen(msg) + 0x04);
85 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
86 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
89 * Message. Not terminated.
91 curbyte += aimutil_putstr(newpacket->data+curbyte,msg, strlen(msg));
94 * Set the Request Acknowledge flag.
96 if (flags & AIM_IMFLAGS_ACK) {
97 curbyte += aimutil_put16(newpacket->data+curbyte,0x0003);
98 curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
102 * Set the Autoresponse flag.
104 if (flags & AIM_IMFLAGS_AWAY) {
105 curbyte += aimutil_put16(newpacket->data+curbyte,0x0004);
106 curbyte += aimutil_put16(newpacket->data+curbyte,0x0000);
109 newpacket->commandlen = curbyte;
112 aim_tx_enqueue(sess, newpacket);
114 #ifdef USE_SNAC_FOR_IMS
116 struct aim_snac_t snac;
118 snac.id = sess->snac_nextid;
119 snac.family = 0x0004;
123 snac.data = malloc(strlen(destsn)+1);
124 memcpy(snac.data, destsn, strlen(destsn)+1);
126 aim_newsnac(sess, &snac);
129 aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
132 return (sess->snac_nextid++);
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.
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.
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...
150 int aim_parse_incoming_im_middle(struct aim_session_t *sess,
151 struct command_rx_struct *command)
154 rxcallback_t userfunc = NULL;
157 struct aim_tlvlist_t *tlvlist;
158 struct aim_userinfo_s userinfo;
161 memset(&userinfo, 0x00, sizeof(struct aim_userinfo_s));
163 i = 10; /* Skip SNAC header */
166 * Read ICBM Cookie. And throw away.
168 for (z=0; z<8; z++,i++)
169 cookie[z] = command->data[i];
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
177 * features of AIM2/3/3.5.
179 * Channel 0x0002 is the Rendevous channel, which
180 * is where Chat Invitiations and various client-client
181 * connection negotiations come from.
184 channel = aimutil_get16(command->data+i);
190 if ((channel != 0x01) && (channel != 0x02))
192 printf("faim: icbm: ICBM received on an unsupported channel. Ignoring.\n (chan = %04x)", channel);
197 * Source screen name.
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];
206 userinfo.warnlevel = aimutil_get16(command->data+i); /* guess */
210 * Number of TLVs that follow. Not needed.
212 wastebits = aimutil_get16(command->data+i);
216 * Read block of TLVs. All further data is derived
217 * from what is parsed here.
219 tlvlist = aim_readtlvchain(command->data+i, command->commandlen-i);
222 * From here on, its depends on what channel we're on.
226 u_int j = 0, y = 0, z = 0;
229 struct aim_tlv_t *msgblocktlv, *tmptlv;
234 * Check Autoresponse status. If it is an autoresponse,
235 * it will contain a second type 0x0004 TLV, with zero length.
237 if (aim_gettlv(tlvlist, 0x0004, 2))
238 icbmflags |= AIM_IMFLAGS_AWAY;
241 * Check Ack Request status.
243 if (aim_gettlv(tlvlist, 0x0003, 2))
244 icbmflags |= AIM_IMFLAGS_ACK;
247 * Extract the various pieces of the userinfo struct.
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)))
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);
260 if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
261 userinfo.onlinesince = aimutil_get32(tmptlv->value);
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);
275 * XXX: Will the msgblock always be the second 0x0002?
277 msgblocktlv = aim_gettlv(tlvlist, 0x0002, 1);
280 printf("faim: icbm: major error! no message block TLV found!\n");
281 aim_freetlvchain(&tlvlist);
286 * Extracting the message from the unknown cruft.
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.
292 * "Do you believe in magic?"
295 msgblock = msgblocktlv->value;
298 wastebits = aimutil_get8(msgblock+j++);
299 wastebits = aimutil_get8(msgblock+j++);
301 y = aimutil_get16(msgblock+j);
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++);
309 * Message string length, including flag words.
311 i = aimutil_get16(msgblock+j);
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.
321 * Though someone should investiagte the former.
324 flag1 = aimutil_get16(msgblock+j);
326 flag2 = aimutil_get16(msgblock+j);
330 printf("faim: icbm: **warning: encoding flags are being used! {%04x, %04x}\n", flag1, flag2);
336 msg = (char *)malloc(i+1);
337 memcpy(msg, msgblock+j, i);
343 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
345 i = userfunc(sess, command, channel, &userinfo, msg, icbmflags, flag1, flag2);
351 else if (channel == 0x0002)
353 struct aim_tlv_t *block1;
354 struct aim_tlvlist_t *list2;
355 struct aim_tlv_t *tmptlv;
356 struct aim_tlv_t *miscinfo;
357 unsigned short reqclass = 0;
361 if ((tmptlv = aim_gettlv(tlvlist, 0x0001, 1)))
362 userinfo.class = aimutil_get16(tmptlv->value);
364 if ((tmptlv = aim_gettlv(tlvlist, 0x0003, 1)))
365 userinfo.onlinesince = aimutil_get32(tmptlv->value);
367 if ((tmptlv = aim_gettlv(tlvlist, 0x0004, 1)))
368 userinfo.idletime = aimutil_get16(tmptlv->value);
369 /* Session Length (AIM) */
370 if ((tmptlv = aim_gettlv(tlvlist, 0x000f, 1)))
371 userinfo.sessionlen = aimutil_get16(tmptlv->value);
372 /* Session Length (AOL) */
373 if ((tmptlv = aim_gettlv(tlvlist, 0x0010, 1)))
374 userinfo.sessionlen = aimutil_get16(tmptlv->value);
377 * There's another block of TLVs embedded in the type 5 here.
379 block1 = aim_gettlv(tlvlist, 0x0005, 1);
381 printf("faim: no tlv 0x0005 in rendezvous transaction!\n");
382 aim_freetlvchain(&tlvlist);
383 return 1; /* major problem */
386 a = 0x1a; /* skip -- not sure what this information is! */
389 * XXX: Ignore if there's no data, only cookie information.
391 * Its probably just an accepted invitation or something.
394 if (block1->length <= 0x1a) {
395 aim_freetlvchain(&tlvlist);
399 list2 = aim_readtlvchain(block1->value+a, block1->length-a);
401 if (!(miscinfo = aim_gettlv(list2, 0x2711, 1))) {
402 struct aim_msgcookie_t *cook;
404 if ((cook = aim_uncachecookie(sess, cookie)) == NULL) {
405 printf("faim: no 0x2711 info TLV in rendezvous and its not in cache!\n");
406 aim_freetlvchain(&list2);
407 aim_freetlvchain(&tlvlist);
411 if (cook->type == 1) {
412 struct aim_filetransfer_t *ft;
415 struct aim_tlv_t *errortlv;
418 ft = (struct aim_filetransfer_t *)cook->data;
420 if ((errortlv = aim_gettlv(list2, 0x000b, 1))) {
421 errorcode = aimutil_get16(errortlv->value);
423 printf("faim: transfer from %s (%s) for %s cancelled (error code %d)\n", ft->sender, ft->ip, ft->filename, errorcode);
426 printf("faim: not data attached to file transfer\n");
430 printf("faim: unknown cookie cache type %d\n", cook->type);
439 * Parse the first two bytes of the 0x2711 TLV.
441 * This can be interpretted in a couple ways.
443 * It could be said that its a service type or some such and
444 * that voice is 0, file transfer is 1, and chat is 4 (and 5).
446 * Or it could be said that its an exchange value. Exchanges
447 * four and five are for chat, voice is on exchange zero and
448 * file transfer is done on exchange 1.
450 * It should work out the same no how its thought of.
453 reqclass = aimutil_get16(miscinfo->value+0);
456 case AIM_RENDEZVOUS_VOICE: {
458 /* XXX: implement all this */
463 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
464 if (userfunc || (i = 0)) {
473 case AIM_RENDEZVOUS_FILETRANSFER: {
476 struct aim_msgcookie_t cachedcook;
477 struct aim_filetransfer_t *ft;
479 memset(ip, 0, sizeof(ip));
481 if (aim_gettlv(list2, 0x0003, 1) && aim_gettlv(list2, 0x0003, 1)) {
482 struct aim_tlv_t *iptlv, *porttlv;
484 iptlv = aim_gettlv(list2, 0x0003, 1);
485 porttlv = aim_gettlv(list2, 0x0005, 1);
487 snprintf(ip, sizeof(ip)-1, "%d.%d.%d.%d:%d",
488 aimutil_get8(iptlv->value+0),
489 aimutil_get8(iptlv->value+1),
490 aimutil_get8(iptlv->value+2),
491 aimutil_get8(iptlv->value+3),
492 aimutil_get16(porttlv->value));
495 if (aim_gettlv(list2, 0x000c, 1)) {
496 desc = aim_gettlv_str(list2, 0x000c, 1);
499 printf("faim: rend: file transfer request from %s for %s: %s (%s)\n",
505 memcpy(cachedcook.cookie, cookie, 8);
506 memcpy(cachedcook.extended, block1->value+10, 16);
508 ft = malloc(sizeof(struct aim_filetransfer_t));
509 strncpy(ft->sender, userinfo.sn, sizeof(ft->sender));
510 strncpy(ft->ip, ip, sizeof(ft->ip));
511 ft->filename = strdup(miscinfo->value+8);
513 cachedcook.data = ft;
515 if (aim_cachecookie(sess, &cachedcook) != 0)
516 printf("faim: ERROR caching message cookie\n");
519 aim_denytransfer(sess, command->conn, ft->sender, cookie, 0);
526 case AIM_RENDEZVOUS_FILETRANSFER_GET: {
527 printf("faim: file get request (unsupported)\n");
531 case AIM_RENDEZVOUS_CHAT_EX3:
532 case AIM_RENDEZVOUS_CHAT_EX4:
533 case AIM_RENDEZVOUS_CHAT_EX5: {
534 struct aim_chat_roominfo roominfo;
535 char *msg=NULL,*encoding=NULL,*lang=NULL;
537 aim_chat_readroominfo(miscinfo->value, &roominfo);
539 if (aim_gettlv(list2, 0x000c, 1))
540 msg = aim_gettlv_str(list2, 0x000c, 1);
542 if (aim_gettlv(list2, 0x000d, 1))
543 encoding = aim_gettlv_str(list2, 0x000d, 1);
545 if (aim_gettlv(list2, 0x000e, 1))
546 lang = aim_gettlv_str(list2, 0x000e, 1);
551 userfunc = aim_callhandler(command->conn, 0x0004, 0x0007);
552 if (userfunc || (i = 0))
556 reqclass, /* == roominfo->exchange */
560 encoding?encoding+1:NULL,
569 printf("faim: rendezvous: unknown rend class 0x%04x\n", reqclass);
575 aim_freetlvchain(&list2);
579 * Free up the TLV chain.
581 aim_freetlvchain(&tlvlist);
589 * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support"
590 * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer"
591 * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers"
594 u_long aim_denytransfer(struct aim_session_t *sess,
595 struct aim_conn_t *conn,
600 struct command_tx_struct *newpacket;
603 if(!(newpacket = aim_tx_new(0x0002, conn, 10+8+2+1+strlen(sender)+6)))
608 curbyte = aim_putsnac(newpacket->data, 0x0004, 0x000b, 0x0000, sess->snac_nextid);
609 for (i = 0; i < 8; i++)
610 curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]);
611 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
612 curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sender));
613 curbyte += aimutil_putstr(newpacket->data+curbyte, sender, strlen(sender));
614 curbyte += aim_puttlv_16(newpacket->data+curbyte, 0x0003, code);
617 aim_tx_enqueue(sess, newpacket);
619 return (sess->snac_nextid++);
623 * Not real sure what this does, nor does anyone I've talk to.
625 * Didn't use to send it. But now I think it might be a good
629 u_long aim_seticbmparam(struct aim_session_t *sess,
630 struct aim_conn_t *conn)
632 struct command_tx_struct *newpacket;
635 if(!(newpacket = aim_tx_new(0x0002, conn, 10+16)))
640 curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0002, 0x0000, sess->snac_nextid);
641 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
642 curbyte += aimutil_put32(newpacket->data+curbyte, 0x00000003);
643 curbyte += aimutil_put8(newpacket->data+curbyte, 0x1f);
644 curbyte += aimutil_put8(newpacket->data+curbyte, 0x40);
645 curbyte += aimutil_put8(newpacket->data+curbyte, 0x03);
646 curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7);
647 curbyte += aimutil_put8(newpacket->data+curbyte, 0x03);
648 curbyte += aimutil_put8(newpacket->data+curbyte, 0xe7);
649 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
650 curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
653 aim_tx_enqueue(sess, newpacket);
655 return (sess->snac_nextid++);
658 int aim_parse_msgerror_middle(struct aim_session_t *sess,
659 struct command_rx_struct *command)
661 u_long snacid = 0x000000000;
662 struct aim_snac_t *snac = NULL;
664 rxcallback_t userfunc = NULL;
667 * Get SNAC from packet and look it up
668 * the list of unrepliedto/outstanding
671 * After its looked up, the SN that the
672 * message should've gone to will be
673 * in the ->data element of the snac struct.
676 snacid = aimutil_get32(command->data+6);
677 snac = aim_remsnac(sess, snacid);
680 printf("faim: msgerr: got an ICBM-failed error on an unknown SNAC ID! (%08lx)\n", snacid);
686 userfunc = aim_callhandler(command->conn, 0x0004, 0x0001);
688 ret = userfunc(sess, command, (snac)?snac->data:"(UNKNOWN)");