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++) {
319 * XXX: This is still fairly ugly.
321 if (workingPtr->handled)
325 * This is a debugging/sanity check only and probably could/should be removed
328 if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) &&
329 (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) ||
330 ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) &&
331 (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
332 faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
333 workingPtr->handled = 1;
337 switch(workingPtr->conn->type) {
340 * This can happen if we have a queued command
341 * that was recieved after a connection has
342 * been terminated. In which case, the handler
343 * list has been cleared, and there's nothing we
344 * can do for it. We can only cancel it.
346 workingPtr->handled = 1;
348 case AIM_CONN_TYPE_AUTH: {
351 head = aimutil_get32(workingPtr->data);
352 if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
353 faimdprintf(sess, 1, "got connection ack on auth line\n");
354 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
355 } else if (workingPtr->hdr.oscar.type == 0x04) {
356 /* Used only by the older login protocol */
357 workingPtr->handled = aim_authparse(sess, workingPtr);
359 unsigned short family,subtype;
361 family = aimutil_get16(workingPtr->data);
362 subtype = aimutil_get16(workingPtr->data+2);
365 /* New login protocol */
367 if (subtype == 0x0001)
368 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
369 else if (subtype == 0x0003)
370 workingPtr->handled = aim_authparse(sess, workingPtr);
371 else if (subtype == 0x0007)
372 workingPtr->handled = aim_authkeyparse(sess, workingPtr);
374 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
378 if (subtype == 0x0003)
379 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
380 else if (subtype == 0x0007)
381 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
382 else if (subtype == 0x0018)
383 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
385 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
389 if (subtype == 0x0003)
390 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
391 else if (subtype == 0x0005)
392 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
393 else if (subtype == 0x0007)
394 workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
397 case AIM_CB_FAM_SPECIAL:
398 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
399 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
402 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
411 case AIM_CONN_TYPE_BOS: {
415 if (workingPtr->hdr.oscar.type == 0x04) {
416 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
420 family = aimutil_get16(workingPtr->data);
421 subtype = aimutil_get16(workingPtr->data+2);
424 case 0x0000: /* not really a family, but it works */
425 if (subtype == 0x0001)
426 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
428 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
430 case 0x0001: /* Family: General */
433 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
436 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
439 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
442 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
445 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
448 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
451 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
454 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
457 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
460 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
464 case 0x0002: /* Family: Location */
467 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
470 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
473 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
476 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
480 case 0x0003: /* Family: Buddy List */
483 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
486 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
488 case 0x000b: /* oncoming buddy */
489 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
491 case 0x000c: /* offgoing buddy */
492 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
495 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
498 case 0x0004: /* Family: Messaging */
501 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
504 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
507 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
510 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
513 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
516 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
519 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
523 if (subtype == 0x0001)
524 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
525 else if (subtype == 0x0003)
526 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
528 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
530 case 0x000a: /* Family: User lookup */
533 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
536 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
539 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
543 if (subtype == 0x0001)
544 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
545 else if (subtype == 0x0002)
546 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
548 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
552 faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
555 case AIM_CB_FAM_SPECIAL:
556 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
559 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
561 } /* switch(family) */
563 } /* AIM_CONN_TYPE_BOS */
564 case AIM_CONN_TYPE_ADS: {
565 unsigned short family;
566 unsigned short subtype;
568 family = aimutil_get16(workingPtr->data);
569 subtype= aimutil_get16(workingPtr->data+2);
571 if ((family == 0x0000) && (subtype == 0x00001)) {
572 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
573 } else if ((family == 0x0001) && (subtype == 0x0003)) {
574 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
576 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
580 case AIM_CONN_TYPE_CHATNAV: {
583 family = aimutil_get16(workingPtr->data);
584 subtype= aimutil_get16(workingPtr->data+2);
586 if ((family == 0x0000) && (subtype == 0x00001)) {
587 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
588 } else if ((family == 0x0001) && (subtype == 0x0003)) {
589 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
590 } else if ((family == 0x000d) && (subtype == 0x0009)) {
591 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
593 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
597 case AIM_CONN_TYPE_CHAT: {
598 u_short family, subtype;
600 family = aimutil_get16(workingPtr->data);
601 subtype= aimutil_get16(workingPtr->data+2);
603 if ((family == 0x0000) && (subtype == 0x00001)) {
604 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
605 } else if (family == 0x0001) {
606 if (subtype == 0x0001)
607 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
608 else if (subtype == 0x0003)
609 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
610 else if (subtype == 0x0007)
611 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
612 else if (subtype == 0x000a)
613 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
615 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
616 } else if (family == 0x000e) {
617 if (subtype == 0x0002)
618 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
619 else if (subtype == 0x0003)
620 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
621 else if (subtype == 0x0004)
622 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
623 else if (subtype == 0x0006)
624 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
626 faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype);
628 faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype);
629 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
633 case AIM_CONN_TYPE_RENDEZVOUS: {
634 /* make sure that we only get OFT frames on these connections */
635 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
636 faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
637 workingPtr->handled = 1; /* get rid of it */
641 /* XXX: implement this */
642 faimdprintf(sess, 0, "faim: OFT frame!\n");
646 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
651 faimdprintf(sess, 0, "internal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
652 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
659 * This doesn't have to be called here. It could easily be done
660 * by a seperate thread or something. It's an administrative operation,
661 * and can take a while. Though the less you call it the less memory
664 aim_purge_rxqueue(sess);
669 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
671 rxcallback_t userfunc = NULL;
674 int i = 10+8; /* skip SNAC and cookie */
678 type = aimutil_get16(command->data+i);
681 snlen = aimutil_get8(command->data+i);
684 memset(sn, 0, sizeof(sn));
685 strncpy(sn, (char *)command->data+i, snlen);
687 if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
688 ret = userfunc(sess, command, type, sn);
694 * The Rate Limiting System, An Abridged Guide to Nonsense.
696 * OSCAR defines several 'rate classes'. Each class has seperate
697 * rate limiting properties (limit level, alert level, disconnect
698 * level, etc), and a set of SNAC family/type pairs associated with
699 * it. The rate classes, their limiting properties, and the definitions
700 * of which SNACs are belong to which class, are defined in the
701 * Rate Response packet at login to each host.
703 * Logically, all rate offenses within one class count against further
704 * offenses for other SNACs in the same class (ie, sending messages
705 * too fast will limit the number of user info requests you can send,
706 * since those two SNACs are in the same rate class).
708 * Since the rate classes are defined dynamically at login, the values
709 * below may change. But they seem to be fairly constant.
711 * Currently, BOS defines five rate classes, with the commonly used
712 * members as follows...
715 * - Everything thats not in any of the other classes
718 * - Buddy list add/remove
719 * - Permit list add/remove
720 * - Deny list add/remove
723 * - User information requests
727 * - A few unknowns: 2/9, 2/b, and f/2
731 * - Outgoing chat ICBMs
733 * The only other thing of note is that class 5 (chat) has slightly looser
734 * limiting properties than class 3 (normal messages). But thats just a
735 * small bit of trivia for you.
737 * The last thing that needs to be learned about the rate limiting
738 * system is how the actual numbers relate to the passing of time. This
739 * seems to be a big mystery.
742 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
744 rxcallback_t userfunc = NULL;
748 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
749 unsigned long currentavg, maxavg;
753 code = aimutil_get16(command->data+i);
756 rateclass = aimutil_get16(command->data+i);
759 windowsize = aimutil_get32(command->data+i);
761 clear = aimutil_get32(command->data+i);
763 alert = aimutil_get32(command->data+i);
765 limit = aimutil_get32(command->data+i);
767 disconnect = aimutil_get32(command->data+i);
769 currentavg = aimutil_get32(command->data+i);
771 maxavg = aimutil_get32(command->data+i);
774 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
775 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
780 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
782 rxcallback_t userfunc = NULL;
785 unsigned short newevil;
786 struct aim_userinfo_s userinfo;
789 newevil = aimutil_get16(command->data+10);
792 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
793 if (command->commandlen-i)
794 i += aim_extractuserinfo(sess, command->data+i, &userinfo);
796 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
797 ret = userfunc(sess, command, newevil, &userinfo);
802 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
803 struct command_rx_struct *command, ...)
805 rxcallback_t userfunc = NULL;
808 struct aim_tlvlist_t *tlvlist;
815 * 1 Mandatory upgrade
818 * 4 Nothing's wrong ("top o the world" -- normal)
821 id = aimutil_get16(command->data+10);
826 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
829 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
830 aim_freetlvchain(&tlvlist);
834 userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
836 ret = userfunc(sess, command, id, msg);
838 aim_freetlvchain(&tlvlist);
844 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
845 struct command_rx_struct *command, ...)
847 rxcallback_t userfunc = NULL;
849 unsigned short *families = NULL;
852 famcount = (command->commandlen-10)/2;
853 if (!(families = malloc(command->commandlen-10)))
856 for (i = 0; i < famcount; i++)
857 families[i] = aimutil_get16(command->data+((i*2)+10));
859 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
860 ret = userfunc(sess, command, famcount, families);
867 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
868 struct command_rx_struct *command)
870 rxcallback_t userfunc = NULL;
874 status = aimutil_get16(command->data+10);
876 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
877 ret = userfunc(sess, command, status);
882 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
883 struct command_rx_struct *command)
885 unsigned short subtype; /* called for both reply and change-reply */
888 subtype = aimutil_get16(command->data+2);
892 * unsigned short perms;
893 * unsigned short tlvcount;
894 * aim_tlv_t tlvs[tlvcount];
897 for (i = 10; i < command->commandlen; ) {
900 perms = aimutil_get16(command->data+i);
903 tlvcount = aimutil_get16(command->data+i);
907 rxcallback_t userfunc;
908 struct aim_tlv_t *tlv;
911 if ((aimutil_get16(command->data+i) == 0x0011) ||
912 (aimutil_get16(command->data+i) == 0x0004))
916 tlv = aim_grabtlvstr(command->data+i);
918 tlv = aim_grabtlv(command->data+i);
920 /* XXX fix so its only called once for the entire packet */
921 if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
922 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
925 i += 2+2+tlv->length;
927 if (tlv && tlv->value)
939 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
940 struct command_rx_struct *command, ...)
942 rxcallback_t userfunc = NULL;
946 vercount = (command->commandlen-10)/4;
948 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
949 ret = userfunc(sess, command, vercount, command->data+10);
954 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
955 struct command_rx_struct *command, ...)
958 unsigned char *cookie = NULL;
960 rxcallback_t userfunc = NULL;
961 struct aim_tlvlist_t *tlvlist;
964 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
966 if (aim_gettlv(tlvlist, 0x000d, 1))
967 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
968 if (aim_gettlv(tlvlist, 0x0005, 1))
969 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
970 if (aim_gettlv(tlvlist, 0x0006, 1))
971 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
973 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
979 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
980 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
981 free(sess->pendingjoin);
982 sess->pendingjoin = NULL;
983 sess->pendingjoinexchange = 0;
984 } else if (!serviceid || !ip || !cookie) { /* yeep! */
987 if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
988 ret = userfunc(sess, command, serviceid, ip, cookie);
996 aim_freetlvchain(&tlvlist);
1001 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
1002 struct command_rx_struct *command, ...)
1006 if (!sess || !command)
1009 faimdprintf(sess, 1, "\nRecieved unknown packet:");
1011 for (i = 0; i < command->commandlen; i++)
1014 faimdprintf(sess, 1, "\n\t");
1016 faimdprintf(sess, 1, "0x%2x ", command->data[i]);
1019 faimdprintf(sess, 1, "\n\n");
1025 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
1026 struct command_rx_struct *command)
1028 struct aim_tlvlist_t *tlvlist;
1030 unsigned short code = 0;
1031 rxcallback_t userfunc = NULL;
1034 tlvlist = aim_readtlvchain(command->data, command->commandlen);
1036 if (aim_gettlv(tlvlist, 0x0009, 1))
1037 code = aim_gettlv16(tlvlist, 0x0009, 1);
1039 if (aim_gettlv(tlvlist, 0x000b, 1))
1040 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
1042 if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
1043 ret = userfunc(sess, command, code, msg);
1045 aim_freetlvchain(&tlvlist);
1054 * aim_parse_generalerrs()
1056 * Middle handler for 0x0001 snac of each family.
1059 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
1060 struct command_rx_struct *command, ...)
1062 unsigned short family;
1063 unsigned short subtype;
1066 rxcallback_t userfunc = NULL;
1068 family = aimutil_get16(command->data+0);
1069 subtype= aimutil_get16(command->data+2);
1071 if (command->commandlen > 10)
1072 error = aimutil_get16(command->data+10);
1074 if ((userfunc = aim_callhandler(sess, command->conn, family, subtype)))
1075 ret = userfunc(sess, command, error);