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.
14 * Bleck functions get called when there's no non-bleck functions
15 * around to cleanup the mess...
17 faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
25 /* XXX: this is ugly. and big just for debugging. */
26 char *literals[14][25] = {
37 "Rate Information Request",
39 "Rate Information Ack",
41 "Rate Information Change",
45 "Request Personal User Information",
46 "Personal User Information",
60 "Set user information",
61 "Request User Information",
63 "Watcher Sub Request",
64 "Watcher Notification"
66 {"Buddy List Management",
74 "Watcher List Response",
76 "Watcher Notification",
77 "Reject Notification",
85 "Remove ICBM Parameter",
86 "Request Parameter Information",
87 "Parameter Information",
102 {"Invitation / Client-to-Client",
111 "Information Request",
113 "Information Change Request",
114 "Information Chat Reply",
115 "Account Confirm Request",
116 "Account Confirm Reply",
117 "Account Delete Request",
118 "Account Delete Reply"
130 "Set group permission mask",
131 "Add permission list entries",
132 "Delete permission list entries",
133 "Add deny list entries",
134 "Delete deny list entries",
146 "Set minimum report interval",
159 "Request Exchange Information",
160 "Request Room Information",
161 "Request Occupant List",
171 maxf = sizeof(literals) / sizeof(literals[0]);
172 maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
174 family = aimutil_get16(workingPtr->data+0);
175 subtype= aimutil_get16(workingPtr->data+2);
177 if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
178 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
180 faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
185 faim_export int aim_conn_addhandler(struct aim_session_t *sess,
186 struct aim_conn_t *conn,
189 rxcallback_t newhandler,
192 struct aim_rxcblist_t *newcb;
197 faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
199 if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
201 newcb->family = family;
203 newcb->flags = flags;
205 newcb->handler = &bleck;
207 newcb->handler = newhandler;
210 if (!conn->handlerlist)
211 conn->handlerlist = newcb;
213 struct aim_rxcblist_t *cur;
215 cur = conn->handlerlist;
225 faim_export int aim_clearhandlers(struct aim_conn_t *conn)
227 struct aim_rxcblist_t *cur;
232 for (cur = conn->handlerlist; cur; ) {
233 struct aim_rxcblist_t *tmp;
239 conn->handlerlist = NULL;
244 faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess,
245 struct aim_conn_t *conn,
246 unsigned short family,
249 struct aim_rxcblist_t *cur;
254 faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
256 for (cur = conn->handlerlist; cur; cur = cur->next) {
257 if ((cur->family == family) && (cur->type == type))
261 if (type == AIM_CB_SPECIAL_DEFAULT) {
262 faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
263 return NULL; /* prevent infinite recursion */
266 faimdprintf(sess, 1, "aim_callhandler: no handler for 0x%04x/0x%04x\n", family, type);
268 return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
271 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
272 struct aim_conn_t *conn,
275 struct command_rx_struct *ptr)
277 rxcallback_t userfunc = NULL;
278 userfunc = aim_callhandler(sess, conn, family, type);
280 return userfunc(sess, ptr);
287 Basically, heres what this should do:
288 1) Determine correct packet handler for this packet
289 2) Mark the packet handled (so it can be dequeued in purge_queue())
290 3) Send the packet to the packet handler
291 4) Go to next packet in the queue and start over
292 5) When done, run purge_queue() to purge handled commands
294 Note that any unhandlable packets should probably be left in the
295 queue. This is the best way to prevent data loss. This means
296 that a single packet may get looked at by this function multiple
297 times. This is more good than bad! This behavior may change.
301 TODO: Get rid of all the ugly if's.
303 TODO: More support for mid-level handlers.
304 TODO: Allow for NULL handlers.
307 faim_export int aim_rxdispatch(struct aim_session_t *sess)
310 struct command_rx_struct *workingPtr = NULL;
312 if (sess->queue_incoming == NULL) {
313 faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
316 workingPtr = sess->queue_incoming;
317 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
318 unsigned short family,subtype;
321 * XXX: This is still fairly ugly.
323 if (workingPtr->handled)
327 * This is a debugging/sanity check only and probably could/should be removed
330 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
331 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
332 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
333 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
334 faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
335 workingPtr->handled = 1;
339 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
340 /* make sure that we only get OFT frames on these connections */
341 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
342 faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
343 workingPtr->handled = 1; /* get rid of it */
345 /* XXX: implement this */
346 faimdprintf(sess, 0, "faim: OFT frame!\n");
347 workingPtr->handled = 1; /* get rid of it */
352 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
354 faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
355 workingPtr->handled = 1;
359 if ((workingPtr->commandlen == 4) &&
360 (aimutil_get32(workingPtr->data) == 0x00000001)) {
361 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
365 if (workingPtr->hdr.oscar.type == 0x04) {
366 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
370 family = aimutil_get16(workingPtr->data);
371 subtype = aimutil_get16(workingPtr->data+2);
373 if (family == 0x0001) {
375 if (subtype == 0x0001)
376 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
377 else if (subtype == 0x0003)
378 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
379 else if (subtype == 0x0005)
380 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
381 else if (subtype == 0x0007)
382 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
383 else if (subtype == 0x000a)
384 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
385 else if (subtype == 0x000f)
386 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
387 else if (subtype == 0x0010)
388 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
389 else if (subtype == 0x0013)
390 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
391 else if (subtype == 0x0018)
392 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
394 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
396 } else if (family == 0x0002) {
398 if (subtype == 0x0001)
399 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
400 else if (subtype == 0x0003)
401 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
402 else if (subtype == 0x0006)
403 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
405 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
407 } else if (family == 0x0003) {
409 if (subtype == 0x0001)
410 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
411 else if (subtype == 0x0003)
412 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
413 else if (subtype == 0x000b)
414 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
415 else if (subtype == 0x000c)
416 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
418 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
420 } else if (family == 0x0004) {
422 if (subtype == 0x0001)
423 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
424 else if (subtype == 0x0005)
425 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
426 else if (subtype == 0x0006)
427 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
428 else if (subtype == 0x0007)
429 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
430 else if (subtype == 0x000a)
431 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
432 else if (subtype == 0x000c)
433 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
435 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
437 } else if (family == 0x0007) {
439 if (subtype == 0x0003)
440 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
441 else if (subtype == 0x0005)
442 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
443 else if (subtype == 0x0007)
444 workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
447 } else if (family == 0x0009) {
449 if (subtype == 0x0001)
450 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
451 else if (subtype == 0x0003)
452 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
454 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
456 } else if (family == 0x000a) {
458 if (subtype == 0x0001)
459 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
460 else if (subtype == 0x0003)
461 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
463 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
465 } else if (family == 0x000b) {
467 if (subtype == 0x0001)
468 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
469 else if (subtype == 0x0002)
470 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
472 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
474 } else if (family == 0x000d) {
476 if (subtype == 0x0009)
477 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
479 } else if (family == 0x000e) {
481 if (subtype == 0x0002)
482 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
483 else if (subtype == 0x0003)
484 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
485 else if (subtype == 0x0004)
486 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
487 else if (subtype == 0x0006)
488 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
490 } else if (family == 0x0013) {
492 faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
494 } else if (family == 0x0017) { /* New login protocol */
496 if (subtype == 0x0001)
497 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
498 else if (subtype == 0x0003)
499 workingPtr->handled = aim_authparse(sess, workingPtr);
500 else if (subtype == 0x0007)
501 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
503 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
505 } else if (family == AIM_CB_FAM_SPECIAL) {
507 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
511 /* Try it raw and see if we can get it to happen... */
512 if (!workingPtr->handled) /* XXX this is probably bad. */
513 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
519 * This doesn't have to be called here. It could easily be done
520 * by a seperate thread or something. It's an administrative operation,
521 * and can take a while. Though the less you call it the less memory
524 aim_purge_rxqueue(sess);
529 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
531 rxcallback_t userfunc = NULL;
534 int i = 10+8; /* skip SNAC and cookie */
538 type = aimutil_get16(command->data+i);
541 snlen = aimutil_get8(command->data+i);
544 memset(sn, 0, sizeof(sn));
545 strncpy(sn, (char *)command->data+i, snlen);
547 if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
548 ret = userfunc(sess, command, type, sn);
554 * The Rate Limiting System, An Abridged Guide to Nonsense.
556 * OSCAR defines several 'rate classes'. Each class has seperate
557 * rate limiting properties (limit level, alert level, disconnect
558 * level, etc), and a set of SNAC family/type pairs associated with
559 * it. The rate classes, their limiting properties, and the definitions
560 * of which SNACs are belong to which class, are defined in the
561 * Rate Response packet at login to each host.
563 * Logically, all rate offenses within one class count against further
564 * offenses for other SNACs in the same class (ie, sending messages
565 * too fast will limit the number of user info requests you can send,
566 * since those two SNACs are in the same rate class).
568 * Since the rate classes are defined dynamically at login, the values
569 * below may change. But they seem to be fairly constant.
571 * Currently, BOS defines five rate classes, with the commonly used
572 * members as follows...
575 * - Everything thats not in any of the other classes
578 * - Buddy list add/remove
579 * - Permit list add/remove
580 * - Deny list add/remove
583 * - User information requests
587 * - A few unknowns: 2/9, 2/b, and f/2
591 * - Outgoing chat ICBMs
593 * The only other thing of note is that class 5 (chat) has slightly looser
594 * limiting properties than class 3 (normal messages). But thats just a
595 * small bit of trivia for you.
597 * The last thing that needs to be learned about the rate limiting
598 * system is how the actual numbers relate to the passing of time. This
599 * seems to be a big mystery.
602 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
604 rxcallback_t userfunc = NULL;
608 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
609 unsigned long currentavg, maxavg;
613 code = aimutil_get16(command->data+i);
616 rateclass = aimutil_get16(command->data+i);
619 windowsize = aimutil_get32(command->data+i);
621 clear = aimutil_get32(command->data+i);
623 alert = aimutil_get32(command->data+i);
625 limit = aimutil_get32(command->data+i);
627 disconnect = aimutil_get32(command->data+i);
629 currentavg = aimutil_get32(command->data+i);
631 maxavg = aimutil_get32(command->data+i);
634 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
635 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
640 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
642 rxcallback_t userfunc = NULL;
645 unsigned short newevil;
646 struct aim_userinfo_s userinfo;
649 newevil = aimutil_get16(command->data+10);
652 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
653 if (command->commandlen-i)
654 i += aim_extractuserinfo(sess, command->data+i, &userinfo);
656 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
657 ret = userfunc(sess, command, newevil, &userinfo);
662 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
663 struct command_rx_struct *command, ...)
665 rxcallback_t userfunc = NULL;
668 struct aim_tlvlist_t *tlvlist;
675 * 1 Mandatory upgrade
678 * 4 Nothing's wrong ("top o the world" -- normal)
681 id = aimutil_get16(command->data+10);
686 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
689 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
690 aim_freetlvchain(&tlvlist);
694 userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
696 ret = userfunc(sess, command, id, msg);
698 aim_freetlvchain(&tlvlist);
704 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
705 struct command_rx_struct *command, ...)
707 rxcallback_t userfunc = NULL;
709 unsigned short *families = NULL;
712 famcount = (command->commandlen-10)/2;
713 if (!(families = malloc(command->commandlen-10)))
716 for (i = 0; i < famcount; i++)
717 families[i] = aimutil_get16(command->data+((i*2)+10));
719 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
720 ret = userfunc(sess, command, famcount, families);
727 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
728 struct command_rx_struct *command)
730 rxcallback_t userfunc = NULL;
734 status = aimutil_get16(command->data+10);
736 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
737 ret = userfunc(sess, command, status);
742 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
743 struct command_rx_struct *command)
745 unsigned short subtype; /* called for both reply and change-reply */
748 subtype = aimutil_get16(command->data+2);
752 * unsigned short perms;
753 * unsigned short tlvcount;
754 * aim_tlv_t tlvs[tlvcount];
757 for (i = 10; i < command->commandlen; ) {
760 perms = aimutil_get16(command->data+i);
763 tlvcount = aimutil_get16(command->data+i);
767 rxcallback_t userfunc;
768 struct aim_tlv_t *tlv;
771 if ((aimutil_get16(command->data+i) == 0x0011) ||
772 (aimutil_get16(command->data+i) == 0x0004))
776 tlv = aim_grabtlvstr(command->data+i);
778 tlv = aim_grabtlv(command->data+i);
780 /* XXX fix so its only called once for the entire packet */
781 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
782 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
785 i += 2+2+tlv->length;
787 if (tlv && tlv->value)
799 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
800 struct command_rx_struct *command, ...)
802 rxcallback_t userfunc = NULL;
806 vercount = (command->commandlen-10)/4;
808 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
809 ret = userfunc(sess, command, vercount, command->data+10);
814 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
815 struct command_rx_struct *command, ...)
818 unsigned char *cookie = NULL;
820 rxcallback_t userfunc = NULL;
821 struct aim_tlvlist_t *tlvlist;
824 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
826 if (aim_gettlv(tlvlist, 0x000d, 1))
827 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
828 if (aim_gettlv(tlvlist, 0x0005, 1))
829 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
830 if (aim_gettlv(tlvlist, 0x0006, 1))
831 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
833 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
839 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
840 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
841 free(sess->pendingjoin);
842 sess->pendingjoin = NULL;
843 sess->pendingjoinexchange = 0;
844 } else if (!serviceid || !ip || !cookie) { /* yeep! */
847 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
848 ret = userfunc(sess, command, serviceid, ip, cookie);
856 aim_freetlvchain(&tlvlist);
861 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
862 struct command_rx_struct *command, ...)
866 if (!sess || !command)
869 faimdprintf(sess, 1, "\nRecieved unknown packet:");
871 for (i = 0; i < command->commandlen; i++)
874 faimdprintf(sess, 1, "\n\t");
876 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
879 faimdprintf(sess, 1, "\n\n");
885 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
886 struct command_rx_struct *command)
888 struct aim_tlvlist_t *tlvlist;
890 unsigned short code = 0;
891 rxcallback_t userfunc = NULL;
894 /* Used only by the older login protocol */
895 /* XXX remove this special case? */
896 if (command->conn->type == AIM_CONN_TYPE_AUTH)
897 return aim_authparse(sess, command);
899 tlvlist = aim_readtlvchain(command->data, command->commandlen);
901 if (aim_gettlv(tlvlist, 0x0009, 1))
902 code = aim_gettlv16(tlvlist, 0x0009, 1);
904 if (aim_gettlv(tlvlist, 0x000b, 1))
905 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
907 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
908 ret = userfunc(sess, command, code, msg);
910 aim_freetlvchain(&tlvlist);
919 * aim_parse_generalerrs()
921 * Middle handler for 0x0001 snac of each family.
924 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
925 struct command_rx_struct *command, ...)
927 unsigned short family;
928 unsigned short subtype;
931 rxcallback_t userfunc = NULL;
933 family = aimutil_get16(command->data+0);
934 subtype= aimutil_get16(command->data+2);
936 if (command->commandlen > 10)
937 error = aimutil_get16(command->data+10);
939 if ((userfunc = aim_callhandler(sess, command->conn, family, subtype)))
940 ret = userfunc(sess, command, error);