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 *new,*cur;
196 faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
198 new = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
199 new->family = family;
203 new->handler = &bleck;
205 new->handler = newhandler;
208 cur = conn->handlerlist;
210 conn->handlerlist = new;
221 faim_export int aim_clearhandlers(struct aim_conn_t *conn)
223 struct aim_rxcblist_t *cur,*tmp;
227 cur = conn->handlerlist;
237 faim_internal rxcallback_t aim_callhandler(struct aim_conn_t *conn,
241 struct aim_rxcblist_t *cur;
246 faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
248 cur = conn->handlerlist;
251 if ( (cur->family == family) && (cur->type == type) )
258 return aim_callhandler(conn, family, 0xffff);
261 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
262 struct aim_conn_t *conn,
265 struct command_rx_struct *ptr)
267 rxcallback_t userfunc = NULL;
268 userfunc = aim_callhandler(conn, family, type);
270 return userfunc(sess, ptr);
277 Basically, heres what this should do:
278 1) Determine correct packet handler for this packet
279 2) Mark the packet handled (so it can be dequeued in purge_queue())
280 3) Send the packet to the packet handler
281 4) Go to next packet in the queue and start over
282 5) When done, run purge_queue() to purge handled commands
284 Note that any unhandlable packets should probably be left in the
285 queue. This is the best way to prevent data loss. This means
286 that a single packet may get looked at by this function multiple
287 times. This is more good than bad! This behavior may change.
291 TODO: Get rid of all the ugly if's.
293 TODO: More support for mid-level handlers.
294 TODO: Allow for NULL handlers.
297 faim_export int aim_rxdispatch(struct aim_session_t *sess)
300 struct command_rx_struct *workingPtr = NULL;
302 if (sess->queue_incoming == NULL) {
303 faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
306 workingPtr = sess->queue_incoming;
307 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
309 * XXX: This is still fairly ugly.
311 if (workingPtr->handled)
315 * This is a debugging/sanity check only and probably could/should be removed
318 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
319 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
320 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
321 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
322 printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
323 workingPtr->handled = 1;
327 switch(workingPtr->conn->type) {
330 * This can happen if we have a queued command
331 * that was recieved after a connection has
332 * been terminated. In which case, the handler
333 * list has been cleared, and there's nothing we
334 * can do for it. We can only cancel it.
336 workingPtr->handled = 1;
338 case AIM_CONN_TYPE_AUTH: {
341 head = aimutil_get32(workingPtr->data);
342 if (head == 0x00000001) {
343 faimdprintf(1, "got connection ack on auth line\n");
344 workingPtr->handled = 1;
345 } else if (workingPtr->hdr.oscar.type == 0x0004) {
346 workingPtr->handled = aim_authparse(sess, workingPtr);
348 u_short family,subtype;
350 family = aimutil_get16(workingPtr->data);
351 subtype = aimutil_get16(workingPtr->data+2);
354 /* New login protocol */
356 if (subtype == 0x0001)
357 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
358 else if (subtype == 0x0003)
359 workingPtr->handled = aim_authparse(sess, workingPtr);
360 else if (subtype == 0x0007)
361 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
363 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
366 if (subtype == 0x0005)
367 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
369 case AIM_CB_FAM_SPECIAL:
370 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
371 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
373 } /* others fall through */
376 /* Old login protocol */
377 /* any user callbacks will be called from here */
378 workingPtr->handled = aim_authparse(sess, workingPtr);
385 case AIM_CONN_TYPE_BOS: {
389 if (workingPtr->hdr.oscar.type == 0x04) {
390 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
394 family = aimutil_get16(workingPtr->data);
395 subtype = aimutil_get16(workingPtr->data+2);
398 case 0x0000: /* not really a family, but it works */
399 if (subtype == 0x0001)
400 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
402 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
404 case 0x0001: /* Family: General */
407 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
410 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
413 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
416 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
419 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
422 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
425 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
428 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
431 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
435 case 0x0002: /* Family: Location */
438 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
441 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
444 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
447 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
451 case 0x0003: /* Family: Buddy List */
454 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
457 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
459 case 0x000b: /* oncoming buddy */
460 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
462 case 0x000c: /* offgoing buddy */
463 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
466 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
469 case 0x0004: /* Family: Messeging */
472 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
475 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
478 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
481 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
484 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
487 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
490 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
494 if (subtype == 0x0001)
495 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
496 else if (subtype == 0x0003)
497 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
499 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
501 case 0x000a: /* Family: User lookup */
504 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
507 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
510 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
514 if (subtype == 0x0001)
515 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
516 else if (subtype == 0x0002)
517 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
519 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
522 case AIM_CB_FAM_SPECIAL:
523 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
526 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
528 } /* switch(family) */
530 } /* AIM_CONN_TYPE_BOS */
531 case AIM_CONN_TYPE_CHATNAV: {
534 family = aimutil_get16(workingPtr->data);
535 subtype= aimutil_get16(workingPtr->data+2);
537 if ((family == 0x0002) && (subtype == 0x0006)) {
538 workingPtr->handled = 1;
539 aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
540 } else if ((family == 0x000d) && (subtype == 0x0009)) {
541 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
543 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
547 case AIM_CONN_TYPE_CHAT: {
548 u_short family, subtype;
550 family = aimutil_get16(workingPtr->data);
551 subtype= aimutil_get16(workingPtr->data+2);
553 if ((family == 0x0000) && (subtype == 0x00001))
554 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
555 else if (family == 0x0001) {
556 if (subtype == 0x0001)
557 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
558 else if (subtype == 0x0003)
559 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
560 else if (subtype == 0x0007)
561 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
563 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
564 } else if (family == 0x000e) {
565 if (subtype == 0x0002)
566 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
567 else if (subtype == 0x0003)
568 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
569 else if (subtype == 0x0004)
570 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
571 else if (subtype == 0x0006)
572 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
574 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
576 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
577 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
581 case AIM_CONN_TYPE_RENDEZVOUS: {
582 /* make sure that we only get OFT frames on these connections */
583 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
584 printf("faim: internal error: non-OFT frames on OFT connection\n");
585 workingPtr->handled = 1; /* get rid of it */
589 /* XXX: implement this */
590 printf("faim: OFT frame!\n");
594 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
599 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
600 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
607 * This doesn't have to be called here. It could easily be done
608 * by a seperate thread or something. It's an administrative operation,
609 * and can take a while. Though the less you call it the less memory
612 aim_purge_rxqueue(sess);
617 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
619 rxcallback_t userfunc = NULL;
622 int i = 10+8; /* skip SNAC and cookie */
626 type = aimutil_get16(command->data+i);
629 snlen = aimutil_get8(command->data+i);
632 memset(sn, 0, sizeof(sn));
633 strncpy(sn, (char *)command->data+i, snlen);
635 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
636 ret = userfunc(sess, command, type, sn);
641 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
643 rxcallback_t userfunc = NULL;
645 unsigned long newrate;
647 if (command->commandlen != 0x2f) {
648 printf("faim: unknown rate change length 0x%04x\n", command->commandlen);
652 newrate = aimutil_get32(command->data+34);
654 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
655 ret = userfunc(sess, command, newrate);
660 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
662 rxcallback_t userfunc = NULL;
666 if(command->commandlen < 12) /* a warning level dec sends this */
669 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
672 if(!(sn = (char *)calloc(1, pos+1)))
675 memcpy(sn, command->data+13, pos);
677 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
678 ret = userfunc(sess, command, sn);
685 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
686 struct command_rx_struct *command, ...)
688 rxcallback_t userfunc = NULL;
691 struct aim_tlvlist_t *tlvlist;
698 * 1 Mandatory upgrade
701 * 4 Nothing's wrong ("top o the world" -- normal)
704 id = aimutil_get16(command->data+10);
709 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
712 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
713 aim_freetlvchain(&tlvlist);
717 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
719 ret = userfunc(sess, command, id, msg);
721 aim_freetlvchain(&tlvlist);
727 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
728 struct command_rx_struct *command, ...)
730 struct aim_tlv_t *tmptlv = NULL;
731 int serviceid = 0x00;
732 unsigned char cookie[AIM_COOKIELEN];
734 rxcallback_t userfunc = NULL;
735 struct aim_tlvlist_t *tlvlist;
738 if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
740 printf("libfaim: major bug: unable to read tlvchain from redirect\n");
744 if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1)))
746 printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
747 aim_freetlvchain(&tlvlist);
750 serviceid = aimutil_get16(tmptlv->value);
752 if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1)))
754 printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
756 aim_freetlvchain(&tlvlist);
760 if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
762 printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
764 aim_freetlvchain(&tlvlist);
767 memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
769 if (serviceid == AIM_CONN_TYPE_CHAT)
775 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
777 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
778 free(sess->pendingjoin);
779 sess->pendingjoin = NULL;
783 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
785 ret = userfunc(sess, command, serviceid, ip, cookie);
789 aim_freetlvchain(&tlvlist);
794 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
795 struct command_rx_struct *command, ...)
799 faimdprintf(1, "\nRecieved unknown packet:");
801 for (i = 0; i < command->commandlen; i++)
804 faimdprintf(1, "\n\t");
806 faimdprintf(1, "0x%2x ", command->data[i]);
809 faimdprintf(1, "\n\n");
815 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
816 struct command_rx_struct *command)
818 struct aim_tlvlist_t *tlvlist;
820 unsigned short code = 0;
821 struct aim_tlv_t *tmptlv;
822 rxcallback_t userfunc = NULL;
825 tlvlist = aim_readtlvchain(command->data, command->commandlen);
827 if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
828 code = aimutil_get16(tmptlv->value);
830 if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
831 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
833 userfunc = aim_callhandler(command->conn,
834 AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
836 ret = userfunc(sess, command, code, msg);
838 aim_freetlvchain(&tlvlist);
845 * aim_parse_generalerrs()
847 * Middle handler for 0x0001 snac of each family.
850 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
851 struct command_rx_struct *command, ...)
856 family = aimutil_get16(command->data+0);
857 subtype= aimutil_get16(command->data+2);
863 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);