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);
514 * This doesn't have to be called here. It could easily be done
515 * by a seperate thread or something. It's an administrative operation,
516 * and can take a while. Though the less you call it the less memory
519 aim_purge_rxqueue(sess);
524 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
526 rxcallback_t userfunc = NULL;
529 int i = 10+8; /* skip SNAC and cookie */
533 type = aimutil_get16(command->data+i);
536 snlen = aimutil_get8(command->data+i);
539 memset(sn, 0, sizeof(sn));
540 strncpy(sn, (char *)command->data+i, snlen);
542 if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
543 ret = userfunc(sess, command, type, sn);
549 * The Rate Limiting System, An Abridged Guide to Nonsense.
551 * OSCAR defines several 'rate classes'. Each class has seperate
552 * rate limiting properties (limit level, alert level, disconnect
553 * level, etc), and a set of SNAC family/type pairs associated with
554 * it. The rate classes, their limiting properties, and the definitions
555 * of which SNACs are belong to which class, are defined in the
556 * Rate Response packet at login to each host.
558 * Logically, all rate offenses within one class count against further
559 * offenses for other SNACs in the same class (ie, sending messages
560 * too fast will limit the number of user info requests you can send,
561 * since those two SNACs are in the same rate class).
563 * Since the rate classes are defined dynamically at login, the values
564 * below may change. But they seem to be fairly constant.
566 * Currently, BOS defines five rate classes, with the commonly used
567 * members as follows...
570 * - Everything thats not in any of the other classes
573 * - Buddy list add/remove
574 * - Permit list add/remove
575 * - Deny list add/remove
578 * - User information requests
582 * - A few unknowns: 2/9, 2/b, and f/2
586 * - Outgoing chat ICBMs
588 * The only other thing of note is that class 5 (chat) has slightly looser
589 * limiting properties than class 3 (normal messages). But thats just a
590 * small bit of trivia for you.
592 * The last thing that needs to be learned about the rate limiting
593 * system is how the actual numbers relate to the passing of time. This
594 * seems to be a big mystery.
597 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
599 rxcallback_t userfunc = NULL;
603 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
604 unsigned long currentavg, maxavg;
608 code = aimutil_get16(command->data+i);
611 rateclass = aimutil_get16(command->data+i);
614 windowsize = aimutil_get32(command->data+i);
616 clear = aimutil_get32(command->data+i);
618 alert = aimutil_get32(command->data+i);
620 limit = aimutil_get32(command->data+i);
622 disconnect = aimutil_get32(command->data+i);
624 currentavg = aimutil_get32(command->data+i);
626 maxavg = aimutil_get32(command->data+i);
629 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
630 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
635 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
637 rxcallback_t userfunc = NULL;
640 unsigned short newevil;
641 struct aim_userinfo_s userinfo;
644 newevil = aimutil_get16(command->data+10);
647 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
648 if (command->commandlen-i)
649 i += aim_extractuserinfo(sess, command->data+i, &userinfo);
651 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
652 ret = userfunc(sess, command, newevil, &userinfo);
657 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
658 struct command_rx_struct *command, ...)
660 rxcallback_t userfunc = NULL;
663 struct aim_tlvlist_t *tlvlist;
670 * 1 Mandatory upgrade
673 * 4 Nothing's wrong ("top o the world" -- normal)
676 id = aimutil_get16(command->data+10);
681 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
684 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
685 aim_freetlvchain(&tlvlist);
689 userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
691 ret = userfunc(sess, command, id, msg);
693 aim_freetlvchain(&tlvlist);
699 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
700 struct command_rx_struct *command, ...)
702 rxcallback_t userfunc = NULL;
704 unsigned short *families = NULL;
707 famcount = (command->commandlen-10)/2;
708 if (!(families = malloc(command->commandlen-10)))
711 for (i = 0; i < famcount; i++)
712 families[i] = aimutil_get16(command->data+((i*2)+10));
714 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
715 ret = userfunc(sess, command, famcount, families);
722 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
723 struct command_rx_struct *command)
725 rxcallback_t userfunc = NULL;
729 status = aimutil_get16(command->data+10);
731 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
732 ret = userfunc(sess, command, status);
737 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
738 struct command_rx_struct *command)
740 unsigned short subtype; /* called for both reply and change-reply */
743 subtype = aimutil_get16(command->data+2);
747 * unsigned short perms;
748 * unsigned short tlvcount;
749 * aim_tlv_t tlvs[tlvcount];
752 for (i = 10; i < command->commandlen; ) {
755 perms = aimutil_get16(command->data+i);
758 tlvcount = aimutil_get16(command->data+i);
762 rxcallback_t userfunc;
763 struct aim_tlv_t *tlv;
766 if ((aimutil_get16(command->data+i) == 0x0011) ||
767 (aimutil_get16(command->data+i) == 0x0004))
771 tlv = aim_grabtlvstr(command->data+i);
773 tlv = aim_grabtlv(command->data+i);
775 /* XXX fix so its only called once for the entire packet */
776 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
777 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
780 i += 2+2+tlv->length;
782 if (tlv && tlv->value)
794 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
795 struct command_rx_struct *command, ...)
797 rxcallback_t userfunc = NULL;
801 vercount = (command->commandlen-10)/4;
803 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
804 ret = userfunc(sess, command, vercount, command->data+10);
809 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
810 struct command_rx_struct *command, ...)
813 unsigned char *cookie = NULL;
815 rxcallback_t userfunc = NULL;
816 struct aim_tlvlist_t *tlvlist;
819 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
821 if (aim_gettlv(tlvlist, 0x000d, 1))
822 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
823 if (aim_gettlv(tlvlist, 0x0005, 1))
824 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
825 if (aim_gettlv(tlvlist, 0x0006, 1))
826 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
828 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
834 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
835 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
836 free(sess->pendingjoin);
837 sess->pendingjoin = NULL;
838 sess->pendingjoinexchange = 0;
839 } else if (!serviceid || !ip || !cookie) { /* yeep! */
842 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
843 ret = userfunc(sess, command, serviceid, ip, cookie);
851 aim_freetlvchain(&tlvlist);
856 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
857 struct command_rx_struct *command, ...)
861 if (!sess || !command)
864 faimdprintf(sess, 1, "\nRecieved unknown packet:");
866 for (i = 0; i < command->commandlen; i++)
869 faimdprintf(sess, 1, "\n\t");
871 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
874 faimdprintf(sess, 1, "\n\n");
880 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
881 struct command_rx_struct *command)
883 struct aim_tlvlist_t *tlvlist;
885 unsigned short code = 0;
886 rxcallback_t userfunc = NULL;
889 /* Used only by the older login protocol */
890 /* XXX remove this special case? */
891 if (command->conn->type == AIM_CONN_TYPE_AUTH)
892 return aim_authparse(sess, command);
894 tlvlist = aim_readtlvchain(command->data, command->commandlen);
896 if (aim_gettlv(tlvlist, 0x0009, 1))
897 code = aim_gettlv16(tlvlist, 0x0009, 1);
899 if (aim_gettlv(tlvlist, 0x000b, 1))
900 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
902 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
903 ret = userfunc(sess, command, code, msg);
905 aim_freetlvchain(&tlvlist);
914 * aim_parse_generalerrs()
916 * Middle handler for 0x0001 snac of each family.
919 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
920 struct command_rx_struct *command, ...)
922 unsigned short family;
923 unsigned short subtype;
926 rxcallback_t userfunc = NULL;
928 family = aimutil_get16(command->data+0);
929 subtype= aimutil_get16(command->data+2);
931 if (command->commandlen > 10)
932 error = aimutil_get16(command->data+10);
934 if ((userfunc = aim_callhandler(sess, command->conn, family, subtype)))
935 ret = userfunc(sess, command, error);