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: Messaging */
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);
537 printf("lalala: 0x%04x/0x%04x\n", family, subtype);
540 case AIM_CB_FAM_SPECIAL:
541 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
544 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
546 } /* switch(family) */
548 } /* AIM_CONN_TYPE_BOS */
549 case AIM_CONN_TYPE_CHATNAV: {
552 family = aimutil_get16(workingPtr->data);
553 subtype= aimutil_get16(workingPtr->data+2);
555 if ((family == 0x0000) && (subtype == 0x00001)) {
556 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
557 } else if ((family == 0x0001) && (subtype == 0x0003)) {
558 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
559 } else if ((family == 0x000d) && (subtype == 0x0009)) {
560 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
562 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
566 case AIM_CONN_TYPE_CHAT: {
567 u_short family, subtype;
569 family = aimutil_get16(workingPtr->data);
570 subtype= aimutil_get16(workingPtr->data+2);
572 if ((family == 0x0000) && (subtype == 0x00001)) {
573 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
574 } else if (family == 0x0001) {
575 if (subtype == 0x0001)
576 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
577 else if (subtype == 0x0003)
578 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
579 else if (subtype == 0x0007)
580 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
581 else if (subtype == 0x000a)
582 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
584 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
585 } else if (family == 0x000e) {
586 if (subtype == 0x0002)
587 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
588 else if (subtype == 0x0003)
589 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
590 else if (subtype == 0x0004)
591 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
592 else if (subtype == 0x0006)
593 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
595 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
597 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
598 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
602 case AIM_CONN_TYPE_RENDEZVOUS: {
603 /* make sure that we only get OFT frames on these connections */
604 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
605 printf("faim: internal error: non-OFT frames on OFT connection\n");
606 workingPtr->handled = 1; /* get rid of it */
610 /* XXX: implement this */
611 printf("faim: OFT frame!\n");
615 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
620 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
621 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
628 * This doesn't have to be called here. It could easily be done
629 * by a seperate thread or something. It's an administrative operation,
630 * and can take a while. Though the less you call it the less memory
633 aim_purge_rxqueue(sess);
638 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
640 rxcallback_t userfunc = NULL;
643 int i = 10+8; /* skip SNAC and cookie */
647 type = aimutil_get16(command->data+i);
650 snlen = aimutil_get8(command->data+i);
653 memset(sn, 0, sizeof(sn));
654 strncpy(sn, (char *)command->data+i, snlen);
656 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
657 ret = userfunc(sess, command, type, sn);
662 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
664 rxcallback_t userfunc = NULL;
668 unsigned long parmid, windowsize, clear, alert, limit, disconnect;
669 unsigned long currentavg, maxavg;
673 code = aimutil_get16(command->data+i);
676 parmid = aimutil_get16(command->data+i);
679 windowsize = aimutil_get32(command->data+i);
681 clear = aimutil_get32(command->data+i);
683 alert = aimutil_get32(command->data+i);
685 limit = aimutil_get32(command->data+i);
687 disconnect = aimutil_get32(command->data+i);
689 currentavg = aimutil_get32(command->data+i);
691 maxavg = aimutil_get32(command->data+i);
694 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
695 ret = userfunc(sess, command, code, parmid, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
700 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
702 rxcallback_t userfunc = NULL;
706 if(command->commandlen < 12) /* a warning level dec sends this */
709 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
712 if(!(sn = (char *)calloc(1, pos+1)))
715 memcpy(sn, command->data+13, pos);
717 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
718 ret = userfunc(sess, command, sn);
725 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
726 struct command_rx_struct *command, ...)
728 rxcallback_t userfunc = NULL;
731 struct aim_tlvlist_t *tlvlist;
738 * 1 Mandatory upgrade
741 * 4 Nothing's wrong ("top o the world" -- normal)
744 id = aimutil_get16(command->data+10);
749 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
752 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
753 aim_freetlvchain(&tlvlist);
757 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
759 ret = userfunc(sess, command, id, msg);
761 aim_freetlvchain(&tlvlist);
767 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
768 struct command_rx_struct *command, ...)
770 rxcallback_t userfunc = NULL;
772 unsigned short *families = NULL;
775 famcount = (command->commandlen-10)/2;
776 if (!(families = malloc(command->commandlen-10)))
779 for (i = 0; i < famcount; i++)
780 families[i] = aimutil_get16(command->data+((i*2)+10));
782 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
783 ret = userfunc(sess, command, famcount, families);
790 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
791 struct command_rx_struct *command, ...)
793 rxcallback_t userfunc = NULL;
797 vercount = (command->commandlen-10)/4;
799 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
800 ret = userfunc(sess, command, vercount, command->data+10);
805 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
806 struct command_rx_struct *command, ...)
809 unsigned char *cookie = NULL;
811 rxcallback_t userfunc = NULL;
812 struct aim_tlvlist_t *tlvlist;
815 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
817 if (aim_gettlv(tlvlist, 0x000d, 1))
818 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
819 if (aim_gettlv(tlvlist, 0x0005, 1))
820 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
821 if (aim_gettlv(tlvlist, 0x0006, 1))
822 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
824 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
830 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
831 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
832 free(sess->pendingjoin);
833 sess->pendingjoin = NULL;
834 sess->pendingjoinexchange = 0;
835 } else if (!serviceid || !ip || !cookie) { /* yeep! */
838 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
839 ret = userfunc(sess, command, serviceid, ip, cookie);
847 aim_freetlvchain(&tlvlist);
852 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
853 struct command_rx_struct *command, ...)
857 if (!sess || !command)
860 faimdprintf(1, "\nRecieved unknown packet:");
862 for (i = 0; i < command->commandlen; i++)
865 faimdprintf(1, "\n\t");
867 faimdprintf(1, "0x%2x ", command->data[i]);
870 faimdprintf(1, "\n\n");
876 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
877 struct command_rx_struct *command)
879 struct aim_tlvlist_t *tlvlist;
881 unsigned short code = 0;
882 rxcallback_t userfunc = NULL;
885 tlvlist = aim_readtlvchain(command->data, command->commandlen);
887 if (aim_gettlv(tlvlist, 0x0009, 1))
888 code = aim_gettlv16(tlvlist, 0x0009, 1);
890 if (aim_gettlv(tlvlist, 0x000b, 1))
891 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
893 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
894 ret = userfunc(sess, command, code, msg);
896 aim_freetlvchain(&tlvlist);
905 * aim_parse_generalerrs()
907 * Middle handler for 0x0001 snac of each family.
910 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
911 struct command_rx_struct *command, ...)
916 family = aimutil_get16(command->data+0);
917 subtype= aimutil_get16(command->data+2);
923 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);