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 cur = conn->handlerlist;
233 struct aim_rxcblist_t *tmp;
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, ...)
737 struct aim_tlv_t *tmptlv = NULL;
738 int serviceid = 0x00;
739 unsigned char cookie[AIM_COOKIELEN];
741 rxcallback_t userfunc = NULL;
742 struct aim_tlvlist_t *tlvlist;
745 if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
747 printf("libfaim: major bug: unable to read tlvchain from redirect\n");
751 if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1)))
753 printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
754 aim_freetlvchain(&tlvlist);
757 serviceid = aimutil_get16(tmptlv->value);
759 if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1)))
761 printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
763 aim_freetlvchain(&tlvlist);
767 if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
769 printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
771 aim_freetlvchain(&tlvlist);
774 memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
776 if (serviceid == AIM_CONN_TYPE_CHAT)
782 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
784 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
785 free(sess->pendingjoin);
786 sess->pendingjoin = NULL;
790 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
792 ret = userfunc(sess, command, serviceid, ip, cookie);
796 aim_freetlvchain(&tlvlist);
801 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
802 struct command_rx_struct *command, ...)
806 if (!sess || !command)
809 faimdprintf(1, "\nRecieved unknown packet:");
811 for (i = 0; i < command->commandlen; i++)
814 faimdprintf(1, "\n\t");
816 faimdprintf(1, "0x%2x ", command->data[i]);
819 faimdprintf(1, "\n\n");
825 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
826 struct command_rx_struct *command)
828 struct aim_tlvlist_t *tlvlist;
830 unsigned short code = 0;
831 struct aim_tlv_t *tmptlv;
832 rxcallback_t userfunc = NULL;
835 tlvlist = aim_readtlvchain(command->data, command->commandlen);
837 if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
838 code = aimutil_get16(tmptlv->value);
840 if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
841 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
843 userfunc = aim_callhandler(command->conn,
844 AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
846 ret = userfunc(sess, command, code, msg);
848 aim_freetlvchain(&tlvlist);
855 * aim_parse_generalerrs()
857 * Middle handler for 0x0001 snac of each family.
860 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
861 struct command_rx_struct *command, ...)
866 family = aimutil_get16(command->data+0);
867 subtype= aimutil_get16(command->data+2);
873 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);