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 for (cur = conn->handlerlist; cur; ) {
232 struct aim_rxcblist_t *tmp;
238 conn->handlerlist = NULL;
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 = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
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 == 0x0003)
375 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
377 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
380 if (subtype == 0x0005)
381 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
383 case AIM_CB_FAM_SPECIAL:
384 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
385 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
388 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
396 case AIM_CONN_TYPE_BOS: {
400 if (workingPtr->hdr.oscar.type == 0x04) {
401 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
405 family = aimutil_get16(workingPtr->data);
406 subtype = aimutil_get16(workingPtr->data+2);
409 case 0x0000: /* not really a family, but it works */
410 if (subtype == 0x0001)
411 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
413 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
415 case 0x0001: /* Family: General */
418 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
421 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
424 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
427 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
430 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
433 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
436 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
439 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
442 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
445 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
449 case 0x0002: /* Family: Location */
452 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
455 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
458 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
461 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
465 case 0x0003: /* Family: Buddy List */
468 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
471 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
473 case 0x000b: /* oncoming buddy */
474 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
476 case 0x000c: /* offgoing buddy */
477 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
480 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
483 case 0x0004: /* Family: Messaging */
486 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
489 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
492 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
495 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
498 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
501 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
504 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
508 if (subtype == 0x0001)
509 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
510 else if (subtype == 0x0003)
511 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
513 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
515 case 0x000a: /* Family: User lookup */
518 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
521 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, workingPtr);
524 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
528 if (subtype == 0x0001)
529 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
530 else if (subtype == 0x0002)
531 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
533 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
537 printf("lalala: 0x%04x/0x%04x\n", family, subtype);
540 case AIM_CB_FAM_SPECIAL:
541 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
544 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
546 } /* switch(family) */
548 } /* AIM_CONN_TYPE_BOS */
549 case AIM_CONN_TYPE_CHATNAV: {
552 family = aimutil_get16(workingPtr->data);
553 subtype= aimutil_get16(workingPtr->data+2);
555 if ((family == 0x0000) && (subtype == 0x00001)) {
556 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
557 } else if ((family == 0x0001) && (subtype == 0x0003)) {
558 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
559 } else if ((family == 0x000d) && (subtype == 0x0009)) {
560 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
562 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
566 case AIM_CONN_TYPE_CHAT: {
567 u_short family, subtype;
569 family = aimutil_get16(workingPtr->data);
570 subtype= aimutil_get16(workingPtr->data+2);
572 if ((family == 0x0000) && (subtype == 0x00001)) {
573 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
574 } else if (family == 0x0001) {
575 if (subtype == 0x0001)
576 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
577 else if (subtype == 0x0003)
578 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
579 else if (subtype == 0x0007)
580 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
581 else if (subtype == 0x000a)
582 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
584 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
585 } else if (family == 0x000e) {
586 if (subtype == 0x0002)
587 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
588 else if (subtype == 0x0003)
589 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
590 else if (subtype == 0x0004)
591 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
592 else if (subtype == 0x0006)
593 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
595 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
597 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
598 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
602 case AIM_CONN_TYPE_RENDEZVOUS: {
603 /* make sure that we only get OFT frames on these connections */
604 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
605 printf("faim: internal error: non-OFT frames on OFT connection\n");
606 workingPtr->handled = 1; /* get rid of it */
610 /* XXX: implement this */
611 printf("faim: OFT frame!\n");
615 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
620 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
621 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
628 * This doesn't have to be called here. It could easily be done
629 * by a seperate thread or something. It's an administrative operation,
630 * and can take a while. Though the less you call it the less memory
633 aim_purge_rxqueue(sess);
638 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
640 rxcallback_t userfunc = NULL;
643 int i = 10+8; /* skip SNAC and cookie */
647 type = aimutil_get16(command->data+i);
650 snlen = aimutil_get8(command->data+i);
653 memset(sn, 0, sizeof(sn));
654 strncpy(sn, (char *)command->data+i, snlen);
656 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
657 ret = userfunc(sess, command, type, sn);
663 * The Rate Limiting System, An Abridged Guide to Nonsense.
665 * OSCAR defines several 'rate classes'. Each class has seperate
666 * rate limiting properties (limit level, alert level, disconnect
667 * level, etc), and a set of SNAC family/type pairs associated with
668 * it. The rate classes, their limiting properties, and the definitions
669 * of which SNACs are belong to which class, are defined in the
670 * Rate Response packet at login to each host.
672 * Logically, all rate offenses within one class count against further
673 * offenses for other SNACs in the same class (ie, sending messages
674 * too fast will limit the number of user info requests you can send,
675 * since those two SNACs are in the same rate class).
677 * Since the rate classes are defined dynamically at login, the values
678 * below may change. But they seem to be fairly constant.
680 * Currently, BOS defines five rate classes, with the commonly used
681 * members as follows...
684 * - Everything thats not in any of the other classes
687 * - Buddy list add/remove
688 * - Permit list add/remove
689 * - Deny list add/remove
692 * - User information requests
696 * - A few unknowns: 2/9, 2/b, and f/2
700 * - Outgoing chat ICBMs
702 * The only other thing of note is that class 5 (chat) has slightly looser
703 * limiting properties than class 3 (normal messages). But thats just a
704 * small bit of trivia for you.
706 * The last thing that needs to be learned about the rate limiting
707 * system is how the actual numbers relate to the passing of time. This
708 * seems to be a big mystery.
711 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
713 rxcallback_t userfunc = NULL;
717 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
718 unsigned long currentavg, maxavg;
722 code = aimutil_get16(command->data+i);
725 rateclass = aimutil_get16(command->data+i);
728 windowsize = aimutil_get32(command->data+i);
730 clear = aimutil_get32(command->data+i);
732 alert = aimutil_get32(command->data+i);
734 limit = aimutil_get32(command->data+i);
736 disconnect = aimutil_get32(command->data+i);
738 currentavg = aimutil_get32(command->data+i);
740 maxavg = aimutil_get32(command->data+i);
743 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
744 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
749 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
751 rxcallback_t userfunc = NULL;
754 unsigned short newevil;
755 struct aim_userinfo_s userinfo;
758 newevil = aimutil_get16(command->data+10);
761 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
762 if (command->commandlen-i)
763 i += aim_extractuserinfo(command->data+i, &userinfo);
765 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
766 ret = userfunc(sess, command, newevil, &userinfo);
771 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
772 struct command_rx_struct *command, ...)
774 rxcallback_t userfunc = NULL;
777 struct aim_tlvlist_t *tlvlist;
784 * 1 Mandatory upgrade
787 * 4 Nothing's wrong ("top o the world" -- normal)
790 id = aimutil_get16(command->data+10);
795 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
798 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
799 aim_freetlvchain(&tlvlist);
803 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
805 ret = userfunc(sess, command, id, msg);
807 aim_freetlvchain(&tlvlist);
813 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
814 struct command_rx_struct *command, ...)
816 rxcallback_t userfunc = NULL;
818 unsigned short *families = NULL;
821 famcount = (command->commandlen-10)/2;
822 if (!(families = malloc(command->commandlen-10)))
825 for (i = 0; i < famcount; i++)
826 families[i] = aimutil_get16(command->data+((i*2)+10));
828 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
829 ret = userfunc(sess, command, famcount, families);
836 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
837 struct command_rx_struct *command, ...)
839 rxcallback_t userfunc = NULL;
843 vercount = (command->commandlen-10)/4;
845 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
846 ret = userfunc(sess, command, vercount, command->data+10);
851 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
852 struct command_rx_struct *command, ...)
855 unsigned char *cookie = NULL;
857 rxcallback_t userfunc = NULL;
858 struct aim_tlvlist_t *tlvlist;
861 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
863 if (aim_gettlv(tlvlist, 0x000d, 1))
864 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
865 if (aim_gettlv(tlvlist, 0x0005, 1))
866 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
867 if (aim_gettlv(tlvlist, 0x0006, 1))
868 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
870 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
876 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
877 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
878 free(sess->pendingjoin);
879 sess->pendingjoin = NULL;
880 sess->pendingjoinexchange = 0;
881 } else if (!serviceid || !ip || !cookie) { /* yeep! */
884 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
885 ret = userfunc(sess, command, serviceid, ip, cookie);
893 aim_freetlvchain(&tlvlist);
898 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
899 struct command_rx_struct *command, ...)
903 if (!sess || !command)
906 faimdprintf(1, "\nRecieved unknown packet:");
908 for (i = 0; i < command->commandlen; i++)
911 faimdprintf(1, "\n\t");
913 faimdprintf(1, "0x%2x ", command->data[i]);
916 faimdprintf(1, "\n\n");
922 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
923 struct command_rx_struct *command)
925 struct aim_tlvlist_t *tlvlist;
927 unsigned short code = 0;
928 rxcallback_t userfunc = NULL;
931 tlvlist = aim_readtlvchain(command->data, command->commandlen);
933 if (aim_gettlv(tlvlist, 0x0009, 1))
934 code = aim_gettlv16(tlvlist, 0x0009, 1);
936 if (aim_gettlv(tlvlist, 0x000b, 1))
937 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
939 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
940 ret = userfunc(sess, command, code, msg);
942 aim_freetlvchain(&tlvlist);
951 * aim_parse_generalerrs()
953 * Middle handler for 0x0001 snac of each family.
956 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
957 struct command_rx_struct *command, ...)
959 unsigned short family;
960 unsigned short subtype;
963 rxcallback_t userfunc = NULL;
965 family = aimutil_get16(command->data+0);
966 subtype= aimutil_get16(command->data+2);
968 if (command->commandlen > 10)
969 error = aimutil_get16(command->data+10);
971 if ((userfunc = aim_callhandler(command->conn, family, subtype)))
972 ret = userfunc(sess, command, error);