4 * This file contains most all of the incoming packet handlers, along
5 * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is
6 * actually done in aim_rxqueue.c.
13 * Bleck functions get called when there's no non-bleck functions
14 * around to cleanup the mess...
16 int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
24 /* XXX: this is ugly. and big just for debugging. */
25 char *literals[14][25] = {
36 "Rate Information Request",
38 "Rate Information Ack",
40 "Rate Information Change",
44 "Request Personal User Information",
45 "Personal User Information",
59 "Set user information",
60 "Request User Information",
62 "Watcher Sub Request",
63 "Watcher Notification"
65 {"Buddy List Management",
73 "Watcher List Response",
75 "Watcher Notification",
76 "Reject Notification",
84 "Remove ICBM Parameter",
85 "Request Parameter Information",
86 "Parameter Information",
101 {"Invitation / Client-to-Client",
110 "Information Request",
112 "Information Change Request",
113 "Information Chat Reply",
114 "Account Confirm Request",
115 "Account Confirm Reply",
116 "Account Delete Request",
117 "Account Delete Reply"
129 "Set group permission mask",
130 "Add permission list entries",
131 "Delete permission list entries",
132 "Add deny list entries",
133 "Delete deny list entries",
145 "Set minimum report interval",
158 "Request Exchange Information",
159 "Request Room Information",
160 "Request Occupant List",
170 maxf = sizeof(literals) / sizeof(literals[0]);
171 maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
173 family = aimutil_get16(workingPtr->data+0);
174 subtype= aimutil_get16(workingPtr->data+2);
176 if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
177 printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
179 printf("bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
184 int aim_conn_addhandler(struct aim_session_t *sess,
185 struct aim_conn_t *conn,
188 rxcallback_t newhandler,
191 struct aim_rxcblist_t *new,*cur;
196 faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
198 new = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
199 new->family = family;
203 new->handler = &bleck;
205 new->handler = newhandler;
208 cur = conn->handlerlist;
210 conn->handlerlist = new;
221 int aim_clearhandlers(struct aim_conn_t *conn)
223 struct aim_rxcblist_t *cur,*tmp;
227 cur = conn->handlerlist;
237 rxcallback_t aim_callhandler(struct aim_conn_t *conn,
241 struct aim_rxcblist_t *cur;
246 faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
248 cur = conn->handlerlist;
251 if ( (cur->family == family) && (cur->type == type) )
258 return aim_callhandler(conn, family, 0xffff);
261 int aim_callhandler_noparam(struct aim_session_t *sess,
262 struct aim_conn_t *conn,
265 struct command_rx_struct *ptr)
267 rxcallback_t userfunc = NULL;
268 userfunc = aim_callhandler(conn, family, type);
270 return userfunc(sess, ptr);
277 Basically, heres what this should do:
278 1) Determine correct packet handler for this packet
279 2) Mark the packet handled (so it can be dequeued in purge_queue())
280 3) Send the packet to the packet handler
281 4) Go to next packet in the queue and start over
282 5) When done, run purge_queue() to purge handled commands
284 Note that any unhandlable packets should probably be left in the
285 queue. This is the best way to prevent data loss. This means
286 that a single packet may get looked at by this function multiple
287 times. This is more good than bad! This behavior may change.
291 TODO: Get rid of all the ugly if's.
293 TODO: More support for mid-level handlers.
294 TODO: Allow for NULL handlers.
297 int aim_rxdispatch(struct aim_session_t *sess)
300 struct command_rx_struct *workingPtr = NULL;
302 if (sess->queue_incoming == NULL) {
303 faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
306 workingPtr = sess->queue_incoming;
307 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
309 * XXX: This is still fairly ugly.
311 if (workingPtr->handled)
315 * This is a debugging/sanity check only and probably could/should be removed
318 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
319 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
320 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
321 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
322 printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
323 workingPtr->handled = 1;
327 switch(workingPtr->conn->type) {
330 * This can happen if we have a queued command
331 * that was recieved after a connection has
332 * been terminated. In which case, the handler
333 * list has been cleared, and there's nothing we
334 * can do for it. We can only cancel it.
336 workingPtr->handled = 1;
338 case AIM_CONN_TYPE_AUTH: {
341 head = aimutil_get32(workingPtr->data);
342 if (head == 0x00000001) {
343 faimdprintf(1, "got connection ack on auth line\n");
344 workingPtr->handled = 1;
345 } else if (workingPtr->hdr.oscar.type == 0x0004) {
346 workingPtr->handled = aim_authparse(sess, workingPtr);
348 u_short family,subtype;
350 family = aimutil_get16(workingPtr->data);
351 subtype = aimutil_get16(workingPtr->data+2);
354 /* New login protocol */
357 if (subtype == 0x0001)
358 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
359 else if (subtype == 0x0003)
360 workingPtr->handled = aim_authparse(sess, workingPtr);
361 else if (subtype == 0x0007)
362 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0007, workingPtr);
364 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
367 /* XXX: this isnt foolproof */
369 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, workingPtr);
372 if (subtype == 0x0005)
373 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
375 case AIM_CB_FAM_SPECIAL:
376 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
377 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
379 } /* others fall through */
381 /* Old login protocol */
382 /* any user callbacks will be called from here */
383 workingPtr->handled = aim_authparse(sess, workingPtr);
389 case AIM_CONN_TYPE_BOS: {
393 if (workingPtr->hdr.oscar.type == 0x04) {
394 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
398 family = aimutil_get16(workingPtr->data);
399 subtype = aimutil_get16(workingPtr->data+2);
402 case 0x0000: /* not really a family, but it works */
403 if (subtype == 0x0001)
404 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
406 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
408 case 0x0001: /* Family: General */
411 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
414 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
417 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
420 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
423 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
426 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
429 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
432 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
435 case 0x0002: /* Family: Location */
438 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0001, workingPtr);
441 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
444 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
447 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
450 case 0x0003: /* Family: Buddy List */
453 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
456 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0003, 0x0003, workingPtr);
458 case 0x000b: /* oncoming buddy */
459 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
461 case 0x000c: /* offgoing buddy */
462 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
465 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
468 case 0x0004: /* Family: Messeging */
471 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
474 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
477 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
480 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
483 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x000a, workingPtr);
486 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
489 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
493 if (subtype == 0x0001)
494 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
495 else if (subtype == 0x0003)
496 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0009, 0x0003, workingPtr);
498 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
500 case 0x000a: /* Family: User lookup */
503 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
506 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
509 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
513 if (subtype == 0x0001)
514 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
515 else if (subtype == 0x0002)
516 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
518 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
520 case AIM_CB_FAM_SPECIAL:
521 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
524 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
529 case AIM_CONN_TYPE_CHATNAV: {
532 family = aimutil_get16(workingPtr->data);
533 subtype= aimutil_get16(workingPtr->data+2);
535 if ((family == 0x0002) && (subtype == 0x0006)) {
536 workingPtr->handled = 1;
537 aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
538 } else if ((family == 0x000d) && (subtype == 0x0009)) {
539 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
541 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
545 case AIM_CONN_TYPE_CHAT: {
546 u_short family, subtype;
548 family = aimutil_get16(workingPtr->data);
549 subtype= aimutil_get16(workingPtr->data+2);
551 if ((family == 0x0000) && (subtype == 0x00001))
552 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
553 else if (family == 0x0001) {
554 if (subtype == 0x0001)
555 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
556 else if (subtype == 0x0003)
557 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
558 else if (subtype == 0x0007)
559 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
561 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
562 } else if (family == 0x000e) {
563 if (subtype == 0x0002)
564 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
565 else if (subtype == 0x0003)
566 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
567 else if (subtype == 0x0004)
568 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
569 else if (subtype == 0x0006)
570 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
572 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
574 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
575 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
579 case AIM_CONN_TYPE_RENDEZVOUS: {
580 /* make sure that we only get OFT frames on these connections */
581 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
582 printf("faim: internal error: non-OFT frames on OFT connection\n");
583 workingPtr->handled = 1; /* get rid of it */
587 /* XXX: implement this */
588 printf("faim: OFT frame!\n");
592 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
597 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
598 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
605 * This doesn't have to be called here. It could easily be done
606 * by a seperate thread or something. It's an administrative operation,
607 * and can take a while. Though the less you call it the less memory
610 aim_purge_rxqueue(sess);
615 int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
617 rxcallback_t userfunc = NULL;
620 int i = 10+8; /* skip SNAC and cookie */
624 type = aimutil_get16(command->data+i);
627 snlen = aimutil_get8(command->data+i);
630 memset(sn, 0, sizeof(sn));
631 strncpy(sn, command->data+i, snlen);
633 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
634 ret = userfunc(sess, command, type, sn);
639 int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
641 rxcallback_t userfunc = NULL;
642 int i = 10; /* skip SNAC */
644 unsigned long newrate;
646 if (command->commandlen != 0x2f) {
647 printf("faim: unknown rate change length 0x%04x\n", command->commandlen);
651 newrate = aimutil_get32(command->data+34);
653 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
654 ret = userfunc(sess, command, newrate);
659 int aim_parsemotd_middle(struct aim_session_t *sess,
660 struct command_rx_struct *command, ...)
662 rxcallback_t userfunc = NULL;
665 struct aim_tlvlist_t *tlvlist;
671 id = aimutil_get16(command->data+10);
676 tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12);
678 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
680 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
682 ret = userfunc(sess, command, id, msg);
684 aim_freetlvchain(&tlvlist);
690 int aim_handleredirect_middle(struct aim_session_t *sess,
691 struct command_rx_struct *command, ...)
693 struct aim_tlv_t *tmptlv = NULL;
694 int serviceid = 0x00;
695 char cookie[AIM_COOKIELEN];
697 rxcallback_t userfunc = NULL;
698 struct aim_tlvlist_t *tlvlist;
701 if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
703 printf("libfaim: major bug: unable to read tlvchain from redirect\n");
707 if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1)))
709 printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
710 aim_freetlvchain(&tlvlist);
713 serviceid = aimutil_get16(tmptlv->value);
715 if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1)))
717 printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
718 aim_freetlvchain(&tlvlist);
722 if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
724 printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
725 aim_freetlvchain(&tlvlist);
728 memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
730 if (serviceid == AIM_CONN_TYPE_CHAT)
736 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
738 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
739 free(sess->pendingjoin);
740 sess->pendingjoin = NULL;
744 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
746 ret = userfunc(sess, command, serviceid, ip, cookie);
750 * XXX: Is there a leak here? Where does IP get freed?
752 aim_freetlvchain(&tlvlist);
757 int aim_parse_unknown(struct aim_session_t *sess,
758 struct command_rx_struct *command, ...)
762 faimdprintf(1, "\nRecieved unknown packet:");
764 for (i = 0; i < command->commandlen; i++)
767 faimdprintf(1, "\n\t");
769 faimdprintf(1, "0x%2x ", command->data[i]);
772 faimdprintf(1, "\n\n");
778 int aim_negchan_middle(struct aim_session_t *sess,
779 struct command_rx_struct *command)
781 struct aim_tlvlist_t *tlvlist;
783 unsigned short code = 0;
784 struct aim_tlv_t *tmptlv;
785 rxcallback_t userfunc = NULL;
788 tlvlist = aim_readtlvchain(command->data, command->commandlen);
790 if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
791 code = aimutil_get16(tmptlv->value);
793 if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
794 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
796 userfunc = aim_callhandler(command->conn,
797 AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
799 ret = userfunc(sess, command, code, msg);
801 aim_freetlvchain(&tlvlist);
808 * aim_parse_generalerrs()
810 * Middle handler for 0x0001 snac of each family.
813 int aim_parse_generalerrs(struct aim_session_t *sess,
814 struct command_rx_struct *command, ...)
819 family = aimutil_get16(command->data+0);
820 subtype= aimutil_get16(command->data+2);
826 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);