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) {
350 faimdprintf(1, "got connection ack on auth line\n");
351 workingPtr->handled = 1;
352 } else if (workingPtr->hdr.oscar.type == 0x0004) {
353 workingPtr->handled = aim_authparse(sess, workingPtr);
355 u_short family,subtype;
357 family = aimutil_get16(workingPtr->data);
358 subtype = aimutil_get16(workingPtr->data+2);
361 /* New login protocol */
363 if (subtype == 0x0001)
364 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
365 else if (subtype == 0x0003)
366 workingPtr->handled = aim_authparse(sess, workingPtr);
367 else if (subtype == 0x0007)
368 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
370 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
373 if (subtype == 0x0005)
374 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
376 case AIM_CB_FAM_SPECIAL:
377 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
378 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
380 } /* others fall through */
383 /* Old login protocol */
384 /* any user callbacks will be called from here */
385 workingPtr->handled = aim_authparse(sess, workingPtr);
392 case AIM_CONN_TYPE_BOS: {
396 if (workingPtr->hdr.oscar.type == 0x04) {
397 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
401 family = aimutil_get16(workingPtr->data);
402 subtype = aimutil_get16(workingPtr->data+2);
405 case 0x0000: /* not really a family, but it works */
406 if (subtype == 0x0001)
407 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
409 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
411 case 0x0001: /* Family: General */
414 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
417 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
420 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
423 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
426 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
429 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
432 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
435 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
438 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
442 case 0x0002: /* Family: Location */
445 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
448 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
451 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
454 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
458 case 0x0003: /* Family: Buddy List */
461 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
464 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
466 case 0x000b: /* oncoming buddy */
467 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
469 case 0x000c: /* offgoing buddy */
470 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
473 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
476 case 0x0004: /* Family: Messeging */
479 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
482 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
485 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
488 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
491 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
494 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
497 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
501 if (subtype == 0x0001)
502 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
503 else if (subtype == 0x0003)
504 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
506 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
508 case 0x000a: /* Family: User lookup */
511 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
514 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
517 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
521 if (subtype == 0x0001)
522 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
523 else if (subtype == 0x0002)
524 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
526 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
529 case AIM_CB_FAM_SPECIAL:
530 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
533 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
535 } /* switch(family) */
537 } /* AIM_CONN_TYPE_BOS */
538 case AIM_CONN_TYPE_CHATNAV: {
541 family = aimutil_get16(workingPtr->data);
542 subtype= aimutil_get16(workingPtr->data+2);
544 if ((family == 0x0002) && (subtype == 0x0006)) {
545 workingPtr->handled = 1;
546 aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
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, 0x0000, 0x0001, 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 faimdprintf(1, "\nRecieved unknown packet:");
808 for (i = 0; i < command->commandlen; i++)
811 faimdprintf(1, "\n\t");
813 faimdprintf(1, "0x%2x ", command->data[i]);
816 faimdprintf(1, "\n\n");
822 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
823 struct command_rx_struct *command)
825 struct aim_tlvlist_t *tlvlist;
827 unsigned short code = 0;
828 struct aim_tlv_t *tmptlv;
829 rxcallback_t userfunc = NULL;
832 tlvlist = aim_readtlvchain(command->data, command->commandlen);
834 if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
835 code = aimutil_get16(tmptlv->value);
837 if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
838 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
840 userfunc = aim_callhandler(command->conn,
841 AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
843 ret = userfunc(sess, command, code, msg);
845 aim_freetlvchain(&tlvlist);
852 * aim_parse_generalerrs()
854 * Middle handler for 0x0001 snac of each family.
857 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
858 struct command_rx_struct *command, ...)
863 family = aimutil_get16(command->data+0);
864 subtype= aimutil_get16(command->data+2);
870 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);