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 == 0x0005)
375 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
377 case AIM_CB_FAM_SPECIAL:
378 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
379 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
381 } /* others fall through */
384 /* Old login protocol */
385 /* any user callbacks will be called from here */
386 workingPtr->handled = aim_authparse(sess, workingPtr);
393 case AIM_CONN_TYPE_BOS: {
397 if (workingPtr->hdr.oscar.type == 0x04) {
398 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
402 family = aimutil_get16(workingPtr->data);
403 subtype = aimutil_get16(workingPtr->data+2);
406 case 0x0000: /* not really a family, but it works */
407 if (subtype == 0x0001)
408 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
410 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
412 case 0x0001: /* Family: General */
415 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
418 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
421 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
424 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
427 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
430 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
433 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
436 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
439 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
443 case 0x0002: /* Family: Location */
446 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
449 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
452 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
455 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
459 case 0x0003: /* Family: Buddy List */
462 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
465 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
467 case 0x000b: /* oncoming buddy */
468 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
470 case 0x000c: /* offgoing buddy */
471 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
474 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
477 case 0x0004: /* Family: Messeging */
480 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
483 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
486 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
489 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
492 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
495 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
498 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
502 if (subtype == 0x0001)
503 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
504 else if (subtype == 0x0003)
505 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
507 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
509 case 0x000a: /* Family: User lookup */
512 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
515 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
518 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
522 if (subtype == 0x0001)
523 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
524 else if (subtype == 0x0002)
525 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
527 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
530 case AIM_CB_FAM_SPECIAL:
531 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
534 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
536 } /* switch(family) */
538 } /* AIM_CONN_TYPE_BOS */
539 case AIM_CONN_TYPE_CHATNAV: {
542 family = aimutil_get16(workingPtr->data);
543 subtype= aimutil_get16(workingPtr->data+2);
545 if ((family == 0x0000) && (subtype == 0x00001)) {
546 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
547 } else if ((family == 0x000d) && (subtype == 0x0009)) {
548 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
550 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
554 case AIM_CONN_TYPE_CHAT: {
555 u_short family, subtype;
557 family = aimutil_get16(workingPtr->data);
558 subtype= aimutil_get16(workingPtr->data+2);
560 if ((family == 0x0000) && (subtype == 0x00001)) {
561 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
562 } else if (family == 0x0001) {
563 if (subtype == 0x0001)
564 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
565 else if (subtype == 0x0003)
566 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
567 else if (subtype == 0x0007)
568 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
570 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
571 } else if (family == 0x000e) {
572 if (subtype == 0x0002)
573 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
574 else if (subtype == 0x0003)
575 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
576 else if (subtype == 0x0004)
577 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
578 else if (subtype == 0x0006)
579 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
581 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
583 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
584 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
588 case AIM_CONN_TYPE_RENDEZVOUS: {
589 /* make sure that we only get OFT frames on these connections */
590 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
591 printf("faim: internal error: non-OFT frames on OFT connection\n");
592 workingPtr->handled = 1; /* get rid of it */
596 /* XXX: implement this */
597 printf("faim: OFT frame!\n");
601 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
606 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
607 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
614 * This doesn't have to be called here. It could easily be done
615 * by a seperate thread or something. It's an administrative operation,
616 * and can take a while. Though the less you call it the less memory
619 aim_purge_rxqueue(sess);
624 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
626 rxcallback_t userfunc = NULL;
629 int i = 10+8; /* skip SNAC and cookie */
633 type = aimutil_get16(command->data+i);
636 snlen = aimutil_get8(command->data+i);
639 memset(sn, 0, sizeof(sn));
640 strncpy(sn, (char *)command->data+i, snlen);
642 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
643 ret = userfunc(sess, command, type, sn);
648 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
650 rxcallback_t userfunc = NULL;
652 unsigned long newrate;
654 if (command->commandlen != 0x2f) {
655 printf("faim: unknown rate change length 0x%04x\n", command->commandlen);
659 newrate = aimutil_get32(command->data+34);
661 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
662 ret = userfunc(sess, command, newrate);
667 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
669 rxcallback_t userfunc = NULL;
673 if(command->commandlen < 12) /* a warning level dec sends this */
676 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
679 if(!(sn = (char *)calloc(1, pos+1)))
682 memcpy(sn, command->data+13, pos);
684 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
685 ret = userfunc(sess, command, sn);
692 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
693 struct command_rx_struct *command, ...)
695 rxcallback_t userfunc = NULL;
698 struct aim_tlvlist_t *tlvlist;
705 * 1 Mandatory upgrade
708 * 4 Nothing's wrong ("top o the world" -- normal)
711 id = aimutil_get16(command->data+10);
716 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
719 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
720 aim_freetlvchain(&tlvlist);
724 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
726 ret = userfunc(sess, command, id, msg);
728 aim_freetlvchain(&tlvlist);
734 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
735 struct command_rx_struct *command, ...)
738 unsigned char *cookie = NULL;
740 rxcallback_t userfunc = NULL;
741 struct aim_tlvlist_t *tlvlist;
744 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
746 if (aim_gettlv(tlvlist, 0x000d, 1))
747 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
748 if (aim_gettlv(tlvlist, 0x0005, 1))
749 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
750 if (aim_gettlv(tlvlist, 0x0006, 1))
751 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
753 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
759 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
760 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
761 free(sess->pendingjoin);
762 sess->pendingjoin = NULL;
763 sess->pendingjoinexchange = 0;
764 } else if (!serviceid || !ip || !cookie) { /* yeep! */
767 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
768 ret = userfunc(sess, command, serviceid, ip, cookie);
776 aim_freetlvchain(&tlvlist);
781 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
782 struct command_rx_struct *command, ...)
786 if (!sess || !command)
789 faimdprintf(1, "\nRecieved unknown packet:");
791 for (i = 0; i < command->commandlen; i++)
794 faimdprintf(1, "\n\t");
796 faimdprintf(1, "0x%2x ", command->data[i]);
799 faimdprintf(1, "\n\n");
805 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
806 struct command_rx_struct *command)
808 struct aim_tlvlist_t *tlvlist;
810 unsigned short code = 0;
811 rxcallback_t userfunc = NULL;
814 tlvlist = aim_readtlvchain(command->data, command->commandlen);
816 if (aim_gettlv(tlvlist, 0x0009, 1))
817 code = aim_gettlv16(tlvlist, 0x0009, 1);
819 if (aim_gettlv(tlvlist, 0x000b, 1))
820 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
822 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
823 ret = userfunc(sess, command, code, msg);
825 aim_freetlvchain(&tlvlist);
834 * aim_parse_generalerrs()
836 * Middle handler for 0x0001 snac of each family.
839 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
840 struct command_rx_struct *command, ...)
845 family = aimutil_get16(command->data+0);
846 subtype= aimutil_get16(command->data+2);
852 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);