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);
577 else if (subtype == 0x000a)
578 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
580 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
581 } else if (family == 0x000e) {
582 if (subtype == 0x0002)
583 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
584 else if (subtype == 0x0003)
585 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
586 else if (subtype == 0x0004)
587 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
588 else if (subtype == 0x0006)
589 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
591 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
593 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
594 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
598 case AIM_CONN_TYPE_RENDEZVOUS: {
599 /* make sure that we only get OFT frames on these connections */
600 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
601 printf("faim: internal error: non-OFT frames on OFT connection\n");
602 workingPtr->handled = 1; /* get rid of it */
606 /* XXX: implement this */
607 printf("faim: OFT frame!\n");
611 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
616 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
617 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
624 * This doesn't have to be called here. It could easily be done
625 * by a seperate thread or something. It's an administrative operation,
626 * and can take a while. Though the less you call it the less memory
629 aim_purge_rxqueue(sess);
634 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
636 rxcallback_t userfunc = NULL;
639 int i = 10+8; /* skip SNAC and cookie */
643 type = aimutil_get16(command->data+i);
646 snlen = aimutil_get8(command->data+i);
649 memset(sn, 0, sizeof(sn));
650 strncpy(sn, (char *)command->data+i, snlen);
652 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
653 ret = userfunc(sess, command, type, sn);
658 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
660 rxcallback_t userfunc = NULL;
664 unsigned long parmid, windowsize, clear, alert, limit, disconnect;
665 unsigned long currentavg, maxavg;
669 code = aimutil_get16(command->data+i);
672 parmid = aimutil_get16(command->data+i);
675 windowsize = aimutil_get32(command->data+i);
677 clear = aimutil_get32(command->data+i);
679 alert = aimutil_get32(command->data+i);
681 limit = aimutil_get32(command->data+i);
683 disconnect = aimutil_get32(command->data+i);
685 currentavg = aimutil_get32(command->data+i);
687 maxavg = aimutil_get32(command->data+i);
690 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
691 ret = userfunc(sess, command, code, parmid, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
696 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
698 rxcallback_t userfunc = NULL;
702 if(command->commandlen < 12) /* a warning level dec sends this */
705 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
708 if(!(sn = (char *)calloc(1, pos+1)))
711 memcpy(sn, command->data+13, pos);
713 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
714 ret = userfunc(sess, command, sn);
721 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
722 struct command_rx_struct *command, ...)
724 rxcallback_t userfunc = NULL;
727 struct aim_tlvlist_t *tlvlist;
734 * 1 Mandatory upgrade
737 * 4 Nothing's wrong ("top o the world" -- normal)
740 id = aimutil_get16(command->data+10);
745 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
748 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
749 aim_freetlvchain(&tlvlist);
753 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
755 ret = userfunc(sess, command, id, msg);
757 aim_freetlvchain(&tlvlist);
763 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
764 struct command_rx_struct *command, ...)
766 rxcallback_t userfunc = NULL;
768 unsigned short *families = NULL;
771 famcount = (command->commandlen-10)/2;
772 if (!(families = malloc(command->commandlen-10)))
775 for (i = 0; i < famcount; i++)
776 families[i] = aimutil_get16(command->data+((i*2)+10));
778 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
779 ret = userfunc(sess, command, famcount, families);
786 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
787 struct command_rx_struct *command, ...)
789 rxcallback_t userfunc = NULL;
793 vercount = (command->commandlen-10)/4;
795 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
796 ret = userfunc(sess, command, vercount, command->data+10);
801 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
802 struct command_rx_struct *command, ...)
805 unsigned char *cookie = NULL;
807 rxcallback_t userfunc = NULL;
808 struct aim_tlvlist_t *tlvlist;
811 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
813 if (aim_gettlv(tlvlist, 0x000d, 1))
814 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
815 if (aim_gettlv(tlvlist, 0x0005, 1))
816 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
817 if (aim_gettlv(tlvlist, 0x0006, 1))
818 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
820 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
826 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
827 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
828 free(sess->pendingjoin);
829 sess->pendingjoin = NULL;
830 sess->pendingjoinexchange = 0;
831 } else if (!serviceid || !ip || !cookie) { /* yeep! */
834 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
835 ret = userfunc(sess, command, serviceid, ip, cookie);
843 aim_freetlvchain(&tlvlist);
848 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
849 struct command_rx_struct *command, ...)
853 if (!sess || !command)
856 faimdprintf(1, "\nRecieved unknown packet:");
858 for (i = 0; i < command->commandlen; i++)
861 faimdprintf(1, "\n\t");
863 faimdprintf(1, "0x%2x ", command->data[i]);
866 faimdprintf(1, "\n\n");
872 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
873 struct command_rx_struct *command)
875 struct aim_tlvlist_t *tlvlist;
877 unsigned short code = 0;
878 rxcallback_t userfunc = NULL;
881 tlvlist = aim_readtlvchain(command->data, command->commandlen);
883 if (aim_gettlv(tlvlist, 0x0009, 1))
884 code = aim_gettlv16(tlvlist, 0x0009, 1);
886 if (aim_gettlv(tlvlist, 0x000b, 1))
887 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
889 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
890 ret = userfunc(sess, command, code, msg);
892 aim_freetlvchain(&tlvlist);
901 * aim_parse_generalerrs()
903 * Middle handler for 0x0001 snac of each family.
906 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
907 struct command_rx_struct *command, ...)
912 family = aimutil_get16(command->data+0);
913 subtype= aimutil_get16(command->data+2);
919 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);