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 = 1;
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, 0x0000, 0x0001, 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 == 0x0002) && (subtype == 0x0006)) {
546 workingPtr->handled = 1;
547 aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
548 } else if ((family == 0x000d) && (subtype == 0x0009)) {
549 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
551 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
555 case AIM_CONN_TYPE_CHAT: {
556 u_short family, subtype;
558 family = aimutil_get16(workingPtr->data);
559 subtype= aimutil_get16(workingPtr->data+2);
561 if ((family == 0x0000) && (subtype == 0x00001))
562 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0000, 0x0001, workingPtr);
563 else if (family == 0x0001) {
564 if (subtype == 0x0001)
565 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
566 else if (subtype == 0x0003)
567 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
568 else if (subtype == 0x0007)
569 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
571 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
572 } else if (family == 0x000e) {
573 if (subtype == 0x0002)
574 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
575 else if (subtype == 0x0003)
576 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
577 else if (subtype == 0x0004)
578 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
579 else if (subtype == 0x0006)
580 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
582 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
584 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
585 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
589 case AIM_CONN_TYPE_RENDEZVOUS: {
590 /* make sure that we only get OFT frames on these connections */
591 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
592 printf("faim: internal error: non-OFT frames on OFT connection\n");
593 workingPtr->handled = 1; /* get rid of it */
597 /* XXX: implement this */
598 printf("faim: OFT frame!\n");
602 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
607 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
608 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
615 * This doesn't have to be called here. It could easily be done
616 * by a seperate thread or something. It's an administrative operation,
617 * and can take a while. Though the less you call it the less memory
620 aim_purge_rxqueue(sess);
625 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
627 rxcallback_t userfunc = NULL;
630 int i = 10+8; /* skip SNAC and cookie */
634 type = aimutil_get16(command->data+i);
637 snlen = aimutil_get8(command->data+i);
640 memset(sn, 0, sizeof(sn));
641 strncpy(sn, (char *)command->data+i, snlen);
643 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
644 ret = userfunc(sess, command, type, sn);
649 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
651 rxcallback_t userfunc = NULL;
653 unsigned long newrate;
655 if (command->commandlen != 0x2f) {
656 printf("faim: unknown rate change length 0x%04x\n", command->commandlen);
660 newrate = aimutil_get32(command->data+34);
662 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
663 ret = userfunc(sess, command, newrate);
668 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
670 rxcallback_t userfunc = NULL;
674 if(command->commandlen < 12) /* a warning level dec sends this */
677 if ((pos = aimutil_get8(command->data+ 12)) > MAXSNLEN)
680 if(!(sn = (char *)calloc(1, pos+1)))
683 memcpy(sn, command->data+13, pos);
685 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
686 ret = userfunc(sess, command, sn);
693 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
694 struct command_rx_struct *command, ...)
696 rxcallback_t userfunc = NULL;
699 struct aim_tlvlist_t *tlvlist;
706 * 1 Mandatory upgrade
709 * 4 Nothing's wrong ("top o the world" -- normal)
712 id = aimutil_get16(command->data+10);
717 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
720 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
721 aim_freetlvchain(&tlvlist);
725 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
727 ret = userfunc(sess, command, id, msg);
729 aim_freetlvchain(&tlvlist);
735 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
736 struct command_rx_struct *command, ...)
738 struct aim_tlv_t *tmptlv = NULL;
739 int serviceid = 0x00;
740 unsigned char cookie[AIM_COOKIELEN];
742 rxcallback_t userfunc = NULL;
743 struct aim_tlvlist_t *tlvlist;
746 if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
748 printf("libfaim: major bug: unable to read tlvchain from redirect\n");
752 if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1)))
754 printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
755 aim_freetlvchain(&tlvlist);
758 serviceid = aimutil_get16(tmptlv->value);
760 if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1)))
762 printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
764 aim_freetlvchain(&tlvlist);
768 if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
770 printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
772 aim_freetlvchain(&tlvlist);
775 memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
777 if (serviceid == AIM_CONN_TYPE_CHAT)
783 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
785 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
786 free(sess->pendingjoin);
787 sess->pendingjoin = NULL;
791 userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
793 ret = userfunc(sess, command, serviceid, ip, cookie);
797 aim_freetlvchain(&tlvlist);
802 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
803 struct command_rx_struct *command, ...)
807 faimdprintf(1, "\nRecieved unknown packet:");
809 for (i = 0; i < command->commandlen; i++)
812 faimdprintf(1, "\n\t");
814 faimdprintf(1, "0x%2x ", command->data[i]);
817 faimdprintf(1, "\n\n");
823 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
824 struct command_rx_struct *command)
826 struct aim_tlvlist_t *tlvlist;
828 unsigned short code = 0;
829 struct aim_tlv_t *tmptlv;
830 rxcallback_t userfunc = NULL;
833 tlvlist = aim_readtlvchain(command->data, command->commandlen);
835 if ((tmptlv = aim_gettlv(tlvlist, 0x0009, 1)))
836 code = aimutil_get16(tmptlv->value);
838 if ((tmptlv = aim_gettlv(tlvlist, 0x000b, 1)))
839 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
841 userfunc = aim_callhandler(command->conn,
842 AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR);
844 ret = userfunc(sess, command, code, msg);
846 aim_freetlvchain(&tlvlist);
853 * aim_parse_generalerrs()
855 * Middle handler for 0x0001 snac of each family.
858 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
859 struct command_rx_struct *command, ...)
864 family = aimutil_get16(command->data+0);
865 subtype= aimutil_get16(command->data+2);
871 return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);