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 printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
180 printf("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(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_conn_t *conn,
248 struct aim_rxcblist_t *cur;
253 faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
255 cur = conn->handlerlist;
258 if ( (cur->family == family) && (cur->type == type) )
266 return aim_callhandler(conn, family, 0xffff);
269 faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
270 struct aim_conn_t *conn,
273 struct command_rx_struct *ptr)
275 rxcallback_t userfunc = NULL;
276 userfunc = aim_callhandler(conn, family, type);
278 return userfunc(sess, ptr);
285 Basically, heres what this should do:
286 1) Determine correct packet handler for this packet
287 2) Mark the packet handled (so it can be dequeued in purge_queue())
288 3) Send the packet to the packet handler
289 4) Go to next packet in the queue and start over
290 5) When done, run purge_queue() to purge handled commands
292 Note that any unhandlable packets should probably be left in the
293 queue. This is the best way to prevent data loss. This means
294 that a single packet may get looked at by this function multiple
295 times. This is more good than bad! This behavior may change.
299 TODO: Get rid of all the ugly if's.
301 TODO: More support for mid-level handlers.
302 TODO: Allow for NULL handlers.
305 faim_export int aim_rxdispatch(struct aim_session_t *sess)
308 struct command_rx_struct *workingPtr = NULL;
310 if (sess->queue_incoming == NULL) {
311 faimdprintf(1, "parse_generic: incoming packet queue empty.\n");
314 workingPtr = sess->queue_incoming;
315 for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
317 * XXX: This is still fairly ugly.
319 if (workingPtr->handled)
323 * This is a debugging/sanity check only and probably could/should be removed
326 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
327 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
328 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
329 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
330 printf("faim: rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
331 workingPtr->handled = 1;
335 switch(workingPtr->conn->type) {
338 * This can happen if we have a queued command
339 * that was recieved after a connection has
340 * been terminated. In which case, the handler
341 * list has been cleared, and there's nothing we
342 * can do for it. We can only cancel it.
344 workingPtr->handled = 1;
346 case AIM_CONN_TYPE_AUTH: {
349 head = aimutil_get32(workingPtr->data);
350 if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
351 faimdprintf(1, "got connection ack on auth line\n");
352 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
353 } else if (workingPtr->hdr.oscar.type == 0x04) {
354 /* Used only by the older login protocol */
355 workingPtr->handled = aim_authparse(sess, workingPtr);
357 unsigned short family,subtype;
359 family = aimutil_get16(workingPtr->data);
360 subtype = aimutil_get16(workingPtr->data+2);
363 /* New login protocol */
365 if (subtype == 0x0001)
366 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
367 else if (subtype == 0x0003)
368 workingPtr->handled = aim_authparse(sess, workingPtr);
369 else if (subtype == 0x0007)
370 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
372 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
375 if (subtype == 0x0003)
376 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
378 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
381 if (subtype == 0x0005)
382 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
384 case AIM_CB_FAM_SPECIAL:
385 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
386 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
389 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
397 case AIM_CONN_TYPE_BOS: {
401 if (workingPtr->hdr.oscar.type == 0x04) {
402 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
406 family = aimutil_get16(workingPtr->data);
407 subtype = aimutil_get16(workingPtr->data+2);
410 case 0x0000: /* not really a family, but it works */
411 if (subtype == 0x0001)
412 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
414 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
416 case 0x0001: /* Family: General */
419 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
422 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
425 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
428 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
431 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
434 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
437 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
440 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
443 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
446 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
450 case 0x0002: /* Family: Location */
453 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
456 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
459 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
462 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
466 case 0x0003: /* Family: Buddy List */
469 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
472 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
474 case 0x000b: /* oncoming buddy */
475 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
477 case 0x000c: /* offgoing buddy */
478 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
481 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
484 case 0x0004: /* Family: Messaging */
487 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
490 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
493 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
496 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
499 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
502 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
505 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
509 if (subtype == 0x0001)
510 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
511 else if (subtype == 0x0003)
512 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
514 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
516 case 0x000a: /* Family: User lookup */
519 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
522 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
525 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
529 if (subtype == 0x0001)
530 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
531 else if (subtype == 0x0002)
532 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
534 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
538 printf("lalala: 0x%04x/0x%04x\n", family, subtype);
541 case AIM_CB_FAM_SPECIAL:
542 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
545 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
547 } /* switch(family) */
549 } /* AIM_CONN_TYPE_BOS */
550 case AIM_CONN_TYPE_CHATNAV: {
553 family = aimutil_get16(workingPtr->data);
554 subtype= aimutil_get16(workingPtr->data+2);
556 if ((family == 0x0000) && (subtype == 0x00001)) {
557 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
558 } else if ((family == 0x0001) && (subtype == 0x0003)) {
559 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
560 } else if ((family == 0x000d) && (subtype == 0x0009)) {
561 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
563 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
567 case AIM_CONN_TYPE_CHAT: {
568 u_short family, subtype;
570 family = aimutil_get16(workingPtr->data);
571 subtype= aimutil_get16(workingPtr->data+2);
573 if ((family == 0x0000) && (subtype == 0x00001)) {
574 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
575 } else if (family == 0x0001) {
576 if (subtype == 0x0001)
577 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
578 else if (subtype == 0x0003)
579 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
580 else if (subtype == 0x0007)
581 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
582 else if (subtype == 0x000a)
583 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
585 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
586 } else if (family == 0x000e) {
587 if (subtype == 0x0002)
588 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
589 else if (subtype == 0x0003)
590 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
591 else if (subtype == 0x0004)
592 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
593 else if (subtype == 0x0006)
594 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
596 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
598 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
599 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
603 case AIM_CONN_TYPE_RENDEZVOUS: {
604 /* make sure that we only get OFT frames on these connections */
605 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
606 printf("faim: internal error: non-OFT frames on OFT connection\n");
607 workingPtr->handled = 1; /* get rid of it */
611 /* XXX: implement this */
612 printf("faim: OFT frame!\n");
616 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
621 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
622 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
629 * This doesn't have to be called here. It could easily be done
630 * by a seperate thread or something. It's an administrative operation,
631 * and can take a while. Though the less you call it the less memory
634 aim_purge_rxqueue(sess);
639 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
641 rxcallback_t userfunc = NULL;
644 int i = 10+8; /* skip SNAC and cookie */
648 type = aimutil_get16(command->data+i);
651 snlen = aimutil_get8(command->data+i);
654 memset(sn, 0, sizeof(sn));
655 strncpy(sn, (char *)command->data+i, snlen);
657 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
658 ret = userfunc(sess, command, type, sn);
664 * The Rate Limiting System, An Abridged Guide to Nonsense.
666 * OSCAR defines several 'rate classes'. Each class has seperate
667 * rate limiting properties (limit level, alert level, disconnect
668 * level, etc), and a set of SNAC family/type pairs associated with
669 * it. The rate classes, their limiting properties, and the definitions
670 * of which SNACs are belong to which class, are defined in the
671 * Rate Response packet at login to each host.
673 * Logically, all rate offenses within one class count against further
674 * offenses for other SNACs in the same class (ie, sending messages
675 * too fast will limit the number of user info requests you can send,
676 * since those two SNACs are in the same rate class).
678 * Since the rate classes are defined dynamically at login, the values
679 * below may change. But they seem to be fairly constant.
681 * Currently, BOS defines five rate classes, with the commonly used
682 * members as follows...
685 * - Everything thats not in any of the other classes
688 * - Buddy list add/remove
689 * - Permit list add/remove
690 * - Deny list add/remove
693 * - User information requests
697 * - A few unknowns: 2/9, 2/b, and f/2
701 * - Outgoing chat ICBMs
703 * The only other thing of note is that class 5 (chat) has slightly looser
704 * limiting properties than class 3 (normal messages). But thats just a
705 * small bit of trivia for you.
707 * The last thing that needs to be learned about the rate limiting
708 * system is how the actual numbers relate to the passing of time. This
709 * seems to be a big mystery.
712 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
714 rxcallback_t userfunc = NULL;
718 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
719 unsigned long currentavg, maxavg;
723 code = aimutil_get16(command->data+i);
726 rateclass = aimutil_get16(command->data+i);
729 windowsize = aimutil_get32(command->data+i);
731 clear = aimutil_get32(command->data+i);
733 alert = aimutil_get32(command->data+i);
735 limit = aimutil_get32(command->data+i);
737 disconnect = aimutil_get32(command->data+i);
739 currentavg = aimutil_get32(command->data+i);
741 maxavg = aimutil_get32(command->data+i);
744 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
745 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
750 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
752 rxcallback_t userfunc = NULL;
755 unsigned short newevil;
756 struct aim_userinfo_s userinfo;
759 newevil = aimutil_get16(command->data+10);
762 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
763 if (command->commandlen-i)
764 i += aim_extractuserinfo(command->data+i, &userinfo);
766 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
767 ret = userfunc(sess, command, newevil, &userinfo);
772 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
773 struct command_rx_struct *command, ...)
775 rxcallback_t userfunc = NULL;
778 struct aim_tlvlist_t *tlvlist;
785 * 1 Mandatory upgrade
788 * 4 Nothing's wrong ("top o the world" -- normal)
791 id = aimutil_get16(command->data+10);
796 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
799 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
800 aim_freetlvchain(&tlvlist);
804 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
806 ret = userfunc(sess, command, id, msg);
808 aim_freetlvchain(&tlvlist);
814 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
815 struct command_rx_struct *command, ...)
817 rxcallback_t userfunc = NULL;
819 unsigned short *families = NULL;
822 famcount = (command->commandlen-10)/2;
823 if (!(families = malloc(command->commandlen-10)))
826 for (i = 0; i < famcount; i++)
827 families[i] = aimutil_get16(command->data+((i*2)+10));
829 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
830 ret = userfunc(sess, command, famcount, families);
837 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
838 struct command_rx_struct *command, ...)
840 rxcallback_t userfunc = NULL;
844 vercount = (command->commandlen-10)/4;
846 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
847 ret = userfunc(sess, command, vercount, command->data+10);
852 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
853 struct command_rx_struct *command, ...)
856 unsigned char *cookie = NULL;
858 rxcallback_t userfunc = NULL;
859 struct aim_tlvlist_t *tlvlist;
862 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
864 if (aim_gettlv(tlvlist, 0x000d, 1))
865 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
866 if (aim_gettlv(tlvlist, 0x0005, 1))
867 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
868 if (aim_gettlv(tlvlist, 0x0006, 1))
869 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
871 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
877 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
878 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
879 free(sess->pendingjoin);
880 sess->pendingjoin = NULL;
881 sess->pendingjoinexchange = 0;
882 } else if (!serviceid || !ip || !cookie) { /* yeep! */
885 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
886 ret = userfunc(sess, command, serviceid, ip, cookie);
894 aim_freetlvchain(&tlvlist);
899 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
900 struct command_rx_struct *command, ...)
904 if (!sess || !command)
907 faimdprintf(1, "\nRecieved unknown packet:");
909 for (i = 0; i < command->commandlen; i++)
912 faimdprintf(1, "\n\t");
914 faimdprintf(1, "0x%2x ", command->data[i]);
917 faimdprintf(1, "\n\n");
923 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
924 struct command_rx_struct *command)
926 struct aim_tlvlist_t *tlvlist;
928 unsigned short code = 0;
929 rxcallback_t userfunc = NULL;
932 tlvlist = aim_readtlvchain(command->data, command->commandlen);
934 if (aim_gettlv(tlvlist, 0x0009, 1))
935 code = aim_gettlv16(tlvlist, 0x0009, 1);
937 if (aim_gettlv(tlvlist, 0x000b, 1))
938 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
940 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
941 ret = userfunc(sess, command, code, msg);
943 aim_freetlvchain(&tlvlist);
952 * aim_parse_generalerrs()
954 * Middle handler for 0x0001 snac of each family.
957 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
958 struct command_rx_struct *command, ...)
960 unsigned short family;
961 unsigned short subtype;
964 rxcallback_t userfunc = NULL;
966 family = aimutil_get16(command->data+0);
967 subtype= aimutil_get16(command->data+2);
969 if (command->commandlen > 10)
970 error = aimutil_get16(command->data+10);
972 if ((userfunc = aim_callhandler(command->conn, family, subtype)))
973 ret = userfunc(sess, command, error);