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 faim_internal 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 faim_export int aim_conn_addhandler(struct aim_session_t *sess,
185 struct aim_conn_t *conn,
188 rxcallback_t newhandler,
191 struct aim_rxcblist_t *newcb;
196 faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
198 if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
200 newcb->family = family;
202 newcb->flags = flags;
204 newcb->handler = &bleck;
206 newcb->handler = newhandler;
209 if (!conn->handlerlist)
210 conn->handlerlist = newcb;
212 struct aim_rxcblist_t *cur;
214 cur = conn->handlerlist;
224 faim_export int aim_clearhandlers(struct aim_conn_t *conn)
226 struct aim_rxcblist_t *cur;
231 for (cur = conn->handlerlist; cur; ) {
232 struct aim_rxcblist_t *tmp;
238 conn->handlerlist = NULL;
243 faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn,
247 struct aim_rxcblist_t *cur;
252 faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
254 cur = conn->handlerlist;
257 if ( (cur->family == family) && (cur->type == type) )
265 return aim_callhandler(conn, family, 0xffff);
268 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
269 struct aim_conn_t *conn,
272 struct command_rx_struct *ptr)
274 rxcallback_t userfunc = NULL;
275 userfunc = aim_callhandler(conn, family, type);
277 return userfunc(sess, ptr);
284 Basically, heres what this should do:
285 1) Determine correct packet handler for this packet
286 2) Mark the packet handled (so it can be dequeued in purge_queue())
287 3) Send the packet to the packet handler
288 4) Go to next packet in the queue and start over
289 5) When done, run purge_queue() to purge handled commands
291 Note that any unhandlable packets should probably be left in the
292 queue. This is the best way to prevent data loss. This means
293 that a single packet may get looked at by this function multiple
294 times. This is more good than bad! This behavior may change.
298 TODO: Get rid of all the ugly if's.
300 TODO: More support for mid-level handlers.
301 TODO: Allow for NULL handlers.
304 faim_export int aim_rxdispatch(struct aim_session_t *sess)
307 struct command_rx_struct *workingPtr = NULL;
309 if (sess->queue_incoming == NULL) {
310 faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
313 workingPtr = sess->queue_incoming;
314 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
316 * XXX: This is still fairly ugly.
318 if (workingPtr->handled)
322 * This is a debugging/sanity check only and probably could/should be removed
325 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
326 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
327 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
328 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
329 printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
330 workingPtr->handled = 1;
334 switch(workingPtr->conn->type) {
337 * This can happen if we have a queued command
338 * that was recieved after a connection has
339 * been terminated. In which case, the handler
340 * list has been cleared, and there's nothing we
341 * can do for it. We can only cancel it.
343 workingPtr->handled = 1;
345 case AIM_CONN_TYPE_AUTH: {
348 head = aimutil_get32(workingPtr->data);
349 if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
350 faimdprintf(1, "got connection ack on auth line\n");
351 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
352 } else if (workingPtr->hdr.oscar.type == 0x04) {
353 /* Used only by the older login protocol */
354 workingPtr->handled = aim_authparse(sess, workingPtr);
356 unsigned short family,subtype;
358 family = aimutil_get16(workingPtr->data);
359 subtype = aimutil_get16(workingPtr->data+2);
362 /* New login protocol */
364 if (subtype == 0x0001)
365 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
366 else if (subtype == 0x0003)
367 workingPtr->handled = aim_authparse(sess, workingPtr);
368 else if (subtype == 0x0007)
369 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
371 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
374 if (subtype == 0x0003)
375 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
377 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
380 if (subtype == 0x0005)
381 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
383 case AIM_CB_FAM_SPECIAL:
384 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
385 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
388 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
396 case AIM_CONN_TYPE_BOS: {
400 if (workingPtr->hdr.oscar.type == 0x04) {
401 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
405 family = aimutil_get16(workingPtr->data);
406 subtype = aimutil_get16(workingPtr->data+2);
409 case 0x0000: /* not really a family, but it works */
410 if (subtype == 0x0001)
411 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
413 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
415 case 0x0001: /* Family: General */
418 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
421 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
424 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
427 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
430 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
433 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
436 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
439 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
442 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
445 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
449 case 0x0002: /* Family: Location */
452 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
455 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
458 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
461 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
465 case 0x0003: /* Family: Buddy List */
468 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
471 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
473 case 0x000b: /* oncoming buddy */
474 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
476 case 0x000c: /* offgoing buddy */
477 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
480 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
483 case 0x0004: /* Family: Messeging */
486 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
489 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
492 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
495 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
498 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
501 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
504 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
508 if (subtype == 0x0001)
509 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
510 else if (subtype == 0x0003)
511 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
513 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
515 case 0x000a: /* Family: User lookup */
518 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
521 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
524 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
528 if (subtype == 0x0001)
529 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
530 else if (subtype == 0x0002)
531 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
533 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
536 case AIM_CB_FAM_SPECIAL:
537 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
540 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
542 } /* switch(family) */
544 } /* AIM_CONN_TYPE_BOS */
545 case AIM_CONN_TYPE_CHATNAV: {
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, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
553 } else if ((family == 0x0001) && (subtype == 0x0003)) {
554 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
555 } else if ((family == 0x000d) && (subtype == 0x0009)) {
556 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
558 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
562 case AIM_CONN_TYPE_CHAT: {
563 u_short family, subtype;
565 family = aimutil_get16(workingPtr->data);
566 subtype= aimutil_get16(workingPtr->data+2);
568 if ((family == 0x0000) && (subtype == 0x00001)) {
569 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
570 } else if (family == 0x0001) {
571 if (subtype == 0x0001)
572 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
573 else if (subtype == 0x0003)
574 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
575 else if (subtype == 0x0007)
576 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
578 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
579 } else if (family == 0x000e) {
580 if (subtype == 0x0002)
581 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
582 else if (subtype == 0x0003)
583 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
584 else if (subtype == 0x0004)
585 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
586 else if (subtype == 0x0006)
587 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
589 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
591 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
592 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
596 case AIM_CONN_TYPE_RENDEZVOUS: {
597 /* make sure that we only get OFT frames on these connections */
598 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
599 printf("faim: internal error: non-OFT frames on OFT connection\n");
600 workingPtr->handled = 1; /* get rid of it */
604 /* XXX: implement this */
605 printf("faim: OFT frame!\n");
609 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
614 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
615 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
622 * This doesn't have to be called here. It could easily be done
623 * by a seperate thread or something. It's an administrative operation,
624 * and can take a while. Though the less you call it the less memory
627 aim_purge_rxqueue(sess);
632 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
634 rxcallback_t userfunc = NULL;
637 int i = 10+8; /* skip SNAC and cookie */
641 type = aimutil_get16(command->data+i);
644 snlen = aimutil_get8(command->data+i);
647 memset(sn, 0, sizeof(sn));
648 strncpy(sn, (char *)command->data+i, snlen);
650 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
651 ret = userfunc(sess, command, type, sn);
656 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
658 rxcallback_t userfunc = NULL;
660 unsigned long newrate;
662 if (command->commandlen != 0x2f) {
663 printf("faim: unknown rate change length 0x%04x\n", command->commandlen);
667 newrate = aimutil_get32(command->data+34);
669 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
670 ret = userfunc(sess, command, newrate);
675 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
677 rxcallback_t userfunc = NULL;
681 if(command->commandlen < 12) /* a warning level dec sends this */
684 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
687 if(!(sn = (char *)calloc(1, pos+1)))
690 memcpy(sn, command->data+13, pos);
692 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
693 ret = userfunc(sess, command, sn);
700 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
701 struct command_rx_struct *command, ...)
703 rxcallback_t userfunc = NULL;
706 struct aim_tlvlist_t *tlvlist;
713 * 1 Mandatory upgrade
716 * 4 Nothing's wrong ("top o the world" -- normal)
719 id = aimutil_get16(command->data+10);
724 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
727 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
728 aim_freetlvchain(&tlvlist);
732 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
734 ret = userfunc(sess, command, id, msg);
736 aim_freetlvchain(&tlvlist);
742 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
743 struct command_rx_struct *command, ...)
745 rxcallback_t userfunc = NULL;
747 unsigned short *families = NULL;
750 famcount = (command->commandlen-10)/2;
751 if (!(families = malloc(command->commandlen-10)))
754 for (i = 0; i < famcount; i++)
755 families[i] = aimutil_get16(command->data+((i*2)+10));
757 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
758 ret = userfunc(sess, command, famcount, families);
765 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
766 struct command_rx_struct *command, ...)
768 rxcallback_t userfunc = NULL;
772 vercount = (command->commandlen-10)/4;
774 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
775 ret = userfunc(sess, command, vercount, command->data+10);
780 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
781 struct command_rx_struct *command, ...)
784 unsigned char *cookie = NULL;
786 rxcallback_t userfunc = NULL;
787 struct aim_tlvlist_t *tlvlist;
790 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
792 if (aim_gettlv(tlvlist, 0x000d, 1))
793 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
794 if (aim_gettlv(tlvlist, 0x0005, 1))
795 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
796 if (aim_gettlv(tlvlist, 0x0006, 1))
797 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
799 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
805 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
806 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
807 free(sess->pendingjoin);
808 sess->pendingjoin = NULL;
809 sess->pendingjoinexchange = 0;
810 } else if (!serviceid || !ip || !cookie) { /* yeep! */
813 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
814 ret = userfunc(sess, command, serviceid, ip, cookie);
822 aim_freetlvchain(&tlvlist);
827 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
828 struct command_rx_struct *command, ...)
832 if (!sess || !command)
835 faimdprintf(1, "\nRecieved unknown packet:");
837 for (i = 0; i < command->commandlen; i++)
840 faimdprintf(1, "\n\t");
842 faimdprintf(1, "0x%2x ", command->data[i]);
845 faimdprintf(1, "\n\n");
851 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
852 struct command_rx_struct *command)
854 struct aim_tlvlist_t *tlvlist;
856 unsigned short code = 0;
857 rxcallback_t userfunc = NULL;
860 tlvlist = aim_readtlvchain(command->data, command->commandlen);
862 if (aim_gettlv(tlvlist, 0x0009, 1))
863 code = aim_gettlv16(tlvlist, 0x0009, 1);
865 if (aim_gettlv(tlvlist, 0x000b, 1))
866 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
868 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
869 ret = userfunc(sess, command, code, msg);
871 aim_freetlvchain(&tlvlist);
880 * aim_parse_generalerrs()
882 * Middle handler for 0x0001 snac of each family.
885 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
886 struct command_rx_struct *command, ...)
891 family = aimutil_get16(command->data+0);
892 subtype= aimutil_get16(command->data+2);
898 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);