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;
311 static int critical = 0;
314 return 0; /* don't call recursively! */
318 if (sess->queue_incoming == NULL) {
319 faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
321 workingPtr = sess->queue_incoming;
322 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
323 unsigned short family,subtype;
326 * XXX: This is still fairly ugly.
328 if (workingPtr->handled)
332 * This is a debugging/sanity check only and probably could/should be removed
335 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
336 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
337 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
338 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
339 faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
340 workingPtr->handled = 1;
344 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
345 /* make sure that we only get OFT frames on these connections */
346 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
347 faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
348 workingPtr->handled = 1; /* get rid of it */
350 /* XXX: implement this */
351 faimdprintf(sess, 0, "faim: OFT frame!\n");
352 workingPtr->handled = 1; /* get rid of it */
357 if (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
359 faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
360 workingPtr->handled = 1;
364 if ((workingPtr->commandlen == 4) &&
365 (aimutil_get32(workingPtr->data) == 0x00000001)) {
366 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
370 if (workingPtr->hdr.oscar.type == 0x04) {
371 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
375 family = aimutil_get16(workingPtr->data);
376 subtype = aimutil_get16(workingPtr->data+2);
378 if (family == 0x0001) {
380 if (subtype == 0x0001)
381 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
382 else if (subtype == 0x0003)
383 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
384 else if (subtype == 0x0005)
385 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
386 else if (subtype == 0x0007)
387 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
388 else if (subtype == 0x000a)
389 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
390 else if (subtype == 0x000f)
391 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
392 else if (subtype == 0x0010)
393 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
394 else if (subtype == 0x0013)
395 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
396 else if (subtype == 0x0018)
397 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
399 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
401 } else if (family == 0x0002) {
403 if (subtype == 0x0001)
404 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
405 else if (subtype == 0x0003)
406 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
407 else if (subtype == 0x0006)
408 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
410 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
412 } else if (family == 0x0003) {
414 if (subtype == 0x0001)
415 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
416 else if (subtype == 0x0003)
417 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
418 else if (subtype == 0x000b)
419 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
420 else if (subtype == 0x000c)
421 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
423 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
425 } else if (family == 0x0004) {
427 if (subtype == 0x0001)
428 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
429 else if (subtype == 0x0005)
430 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
431 else if (subtype == 0x0006)
432 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
433 else if (subtype == 0x0007)
434 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
435 else if (subtype == 0x000a)
436 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
437 else if (subtype == 0x000c)
438 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
440 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
442 } else if (family == 0x0007) {
444 if (subtype == 0x0003)
445 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
446 else if (subtype == 0x0005)
447 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
448 else if (subtype == 0x0007)
449 workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
452 } else if (family == 0x0009) {
454 if (subtype == 0x0001)
455 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
456 else if (subtype == 0x0003)
457 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
459 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
461 } else if (family == 0x000a) {
463 if (subtype == 0x0001)
464 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
465 else if (subtype == 0x0003)
466 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
468 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
470 } else if (family == 0x000b) {
472 if (subtype == 0x0001)
473 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
474 else if (subtype == 0x0002)
475 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
477 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
479 } else if (family == 0x000d) {
481 if (subtype == 0x0009)
482 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
484 } else if (family == 0x000e) {
486 if (subtype == 0x0002)
487 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
488 else if (subtype == 0x0003)
489 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
490 else if (subtype == 0x0004)
491 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
492 else if (subtype == 0x0006)
493 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
495 } else if (family == 0x0013) {
497 faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
499 } else if (family == 0x0017) { /* New login protocol */
501 if (subtype == 0x0001)
502 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
503 else if (subtype == 0x0003)
504 workingPtr->handled = aim_authparse(sess, workingPtr);
505 else if (subtype == 0x0007)
506 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
508 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
510 } else if (family == AIM_CB_FAM_SPECIAL) {
512 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);
531 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
533 rxcallback_t userfunc = NULL;
536 int i = 10+8; /* skip SNAC and cookie */
540 type = aimutil_get16(command->data+i);
543 snlen = aimutil_get8(command->data+i);
546 memset(sn, 0, sizeof(sn));
547 strncpy(sn, (char *)command->data+i, snlen);
549 if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
550 ret = userfunc(sess, command, type, sn);
556 * The Rate Limiting System, An Abridged Guide to Nonsense.
558 * OSCAR defines several 'rate classes'. Each class has seperate
559 * rate limiting properties (limit level, alert level, disconnect
560 * level, etc), and a set of SNAC family/type pairs associated with
561 * it. The rate classes, their limiting properties, and the definitions
562 * of which SNACs are belong to which class, are defined in the
563 * Rate Response packet at login to each host.
565 * Logically, all rate offenses within one class count against further
566 * offenses for other SNACs in the same class (ie, sending messages
567 * too fast will limit the number of user info requests you can send,
568 * since those two SNACs are in the same rate class).
570 * Since the rate classes are defined dynamically at login, the values
571 * below may change. But they seem to be fairly constant.
573 * Currently, BOS defines five rate classes, with the commonly used
574 * members as follows...
577 * - Everything thats not in any of the other classes
580 * - Buddy list add/remove
581 * - Permit list add/remove
582 * - Deny list add/remove
585 * - User information requests
589 * - A few unknowns: 2/9, 2/b, and f/2
593 * - Outgoing chat ICBMs
595 * The only other thing of note is that class 5 (chat) has slightly looser
596 * limiting properties than class 3 (normal messages). But thats just a
597 * small bit of trivia for you.
599 * The last thing that needs to be learned about the rate limiting
600 * system is how the actual numbers relate to the passing of time. This
601 * seems to be a big mystery.
604 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
606 rxcallback_t userfunc = NULL;
610 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
611 unsigned long currentavg, maxavg;
615 code = aimutil_get16(command->data+i);
618 rateclass = aimutil_get16(command->data+i);
621 windowsize = aimutil_get32(command->data+i);
623 clear = aimutil_get32(command->data+i);
625 alert = aimutil_get32(command->data+i);
627 limit = aimutil_get32(command->data+i);
629 disconnect = aimutil_get32(command->data+i);
631 currentavg = aimutil_get32(command->data+i);
633 maxavg = aimutil_get32(command->data+i);
636 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
637 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
642 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
644 rxcallback_t userfunc = NULL;
647 unsigned short newevil;
648 struct aim_userinfo_s userinfo;
651 newevil = aimutil_get16(command->data+10);
654 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
655 if (command->commandlen-i)
656 i += aim_extractuserinfo(sess, command->data+i, &userinfo);
658 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
659 ret = userfunc(sess, command, newevil, &userinfo);
664 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
665 struct command_rx_struct *command, ...)
667 rxcallback_t userfunc = NULL;
670 struct aim_tlvlist_t *tlvlist;
677 * 1 Mandatory upgrade
680 * 4 Nothing's wrong ("top o the world" -- normal)
683 id = aimutil_get16(command->data+10);
688 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
691 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
692 aim_freetlvchain(&tlvlist);
696 userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
698 ret = userfunc(sess, command, id, msg);
700 aim_freetlvchain(&tlvlist);
706 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
707 struct command_rx_struct *command, ...)
709 rxcallback_t userfunc = NULL;
711 unsigned short *families = NULL;
714 famcount = (command->commandlen-10)/2;
715 if (!(families = malloc(command->commandlen-10)))
718 for (i = 0; i < famcount; i++)
719 families[i] = aimutil_get16(command->data+((i*2)+10));
721 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
722 ret = userfunc(sess, command, famcount, families);
729 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
730 struct command_rx_struct *command)
732 rxcallback_t userfunc = NULL;
736 status = aimutil_get16(command->data+10);
738 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
739 ret = userfunc(sess, command, status);
744 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
745 struct command_rx_struct *command)
747 unsigned short subtype; /* called for both reply and change-reply */
750 subtype = aimutil_get16(command->data+2);
754 * unsigned short perms;
755 * unsigned short tlvcount;
756 * aim_tlv_t tlvs[tlvcount];
759 for (i = 10; i < command->commandlen; ) {
762 perms = aimutil_get16(command->data+i);
765 tlvcount = aimutil_get16(command->data+i);
769 rxcallback_t userfunc;
770 struct aim_tlv_t *tlv;
773 if ((aimutil_get16(command->data+i) == 0x0011) ||
774 (aimutil_get16(command->data+i) == 0x0004))
778 tlv = aim_grabtlvstr(command->data+i);
780 tlv = aim_grabtlv(command->data+i);
782 /* XXX fix so its only called once for the entire packet */
783 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
784 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
787 i += 2+2+tlv->length;
789 if (tlv && tlv->value)
801 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
802 struct command_rx_struct *command, ...)
804 rxcallback_t userfunc = NULL;
808 vercount = (command->commandlen-10)/4;
810 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
811 ret = userfunc(sess, command, vercount, command->data+10);
816 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
817 struct command_rx_struct *command, ...)
820 unsigned char *cookie = NULL;
822 rxcallback_t userfunc = NULL;
823 struct aim_tlvlist_t *tlvlist;
826 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
828 if (aim_gettlv(tlvlist, 0x000d, 1))
829 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
830 if (aim_gettlv(tlvlist, 0x0005, 1))
831 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
832 if (aim_gettlv(tlvlist, 0x0006, 1))
833 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
835 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
841 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
842 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
843 free(sess->pendingjoin);
844 sess->pendingjoin = NULL;
845 sess->pendingjoinexchange = 0;
846 } else if (!serviceid || !ip || !cookie) { /* yeep! */
849 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
850 ret = userfunc(sess, command, serviceid, ip, cookie);
858 aim_freetlvchain(&tlvlist);
863 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
864 struct command_rx_struct *command, ...)
868 if (!sess || !command)
871 faimdprintf(sess, 1, "\nRecieved unknown packet:");
873 for (i = 0; i < command->commandlen; i++)
876 faimdprintf(sess, 1, "\n\t");
878 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
881 faimdprintf(sess, 1, "\n\n");
887 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
888 struct command_rx_struct *command)
890 struct aim_tlvlist_t *tlvlist;
892 unsigned short code = 0;
893 rxcallback_t userfunc = NULL;
896 /* Used only by the older login protocol */
897 /* XXX remove this special case? */
898 if (command->conn->type == AIM_CONN_TYPE_AUTH)
899 return aim_authparse(sess, command);
901 tlvlist = aim_readtlvchain(command->data, command->commandlen);
903 if (aim_gettlv(tlvlist, 0x0009, 1))
904 code = aim_gettlv16(tlvlist, 0x0009, 1);
906 if (aim_gettlv(tlvlist, 0x000b, 1))
907 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
909 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
910 ret = userfunc(sess, command, code, msg);
912 aim_freetlvchain(&tlvlist);
921 * aim_parse_generalerrs()
923 * Middle handler for 0x0001 snac of each family.
926 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
927 struct command_rx_struct *command, ...)
929 unsigned short family;
930 unsigned short subtype;
933 rxcallback_t userfunc = NULL;
935 family = aimutil_get16(command->data+0);
936 subtype= aimutil_get16(command->data+2);
938 if (command->commandlen > 10)
939 error = aimutil_get16(command->data+10);
941 if ((userfunc = aim_callhandler(sess, command->conn, family, subtype)))
942 ret = userfunc(sess, command, error);