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);
376 if (subtype == 0x0003)
377 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
378 else if (subtype == 0x0007)
379 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
380 else if (subtype == 0x0018)
381 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
383 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
387 if (subtype == 0x0003)
388 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
389 else if (subtype == 0x0005)
390 workingPtr->handled = aim_parse_infochange(sess, workingPtr);
391 else if (subtype == 0x0007)
392 workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
395 case AIM_CB_FAM_SPECIAL:
396 if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
397 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
400 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
409 case AIM_CONN_TYPE_BOS: {
413 if (workingPtr->hdr.oscar.type == 0x04) {
414 workingPtr->handled = aim_negchan_middle(sess, workingPtr);
418 family = aimutil_get16(workingPtr->data);
419 subtype = aimutil_get16(workingPtr->data+2);
422 case 0x0000: /* not really a family, but it works */
423 if (subtype == 0x0001)
424 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
426 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
428 case 0x0001: /* Family: General */
431 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
434 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
437 workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
440 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
443 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
446 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
449 workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
452 workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
455 workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
458 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
462 case 0x0002: /* Family: Location */
465 workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
468 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
471 workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
474 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
478 case 0x0003: /* Family: Buddy List */
481 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
484 workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
486 case 0x000b: /* oncoming buddy */
487 workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
489 case 0x000c: /* offgoing buddy */
490 workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
493 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
496 case 0x0004: /* Family: Messaging */
499 workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
502 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
505 workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
508 workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
511 workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
514 workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
517 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
521 if (subtype == 0x0001)
522 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
523 else if (subtype == 0x0003)
524 workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
526 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
528 case 0x000a: /* Family: User lookup */
531 workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
534 workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
537 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
541 if (subtype == 0x0001)
542 workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
543 else if (subtype == 0x0002)
544 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
546 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
550 printf("lalala: 0x%04x/0x%04x\n", family, subtype);
553 case AIM_CB_FAM_SPECIAL:
554 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
557 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
559 } /* switch(family) */
561 } /* AIM_CONN_TYPE_BOS */
562 case AIM_CONN_TYPE_ADS: {
563 unsigned short family;
564 unsigned short subtype;
566 family = aimutil_get16(workingPtr->data);
567 subtype= aimutil_get16(workingPtr->data+2);
569 if ((family == 0x0000) && (subtype == 0x00001)) {
570 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
571 } else if ((family == 0x0001) && (subtype == 0x0003)) {
572 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
574 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
578 case AIM_CONN_TYPE_CHATNAV: {
581 family = aimutil_get16(workingPtr->data);
582 subtype= aimutil_get16(workingPtr->data+2);
584 if ((family == 0x0000) && (subtype == 0x00001)) {
585 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
586 } else if ((family == 0x0001) && (subtype == 0x0003)) {
587 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
588 } else if ((family == 0x000d) && (subtype == 0x0009)) {
589 workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
591 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
595 case AIM_CONN_TYPE_CHAT: {
596 u_short family, subtype;
598 family = aimutil_get16(workingPtr->data);
599 subtype= aimutil_get16(workingPtr->data+2);
601 if ((family == 0x0000) && (subtype == 0x00001)) {
602 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
603 } else if (family == 0x0001) {
604 if (subtype == 0x0001)
605 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
606 else if (subtype == 0x0003)
607 workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
608 else if (subtype == 0x0007)
609 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
610 else if (subtype == 0x000a)
611 workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
613 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
614 } else if (family == 0x000e) {
615 if (subtype == 0x0002)
616 workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
617 else if (subtype == 0x0003)
618 workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);
619 else if (subtype == 0x0004)
620 workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);
621 else if (subtype == 0x0006)
622 workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
624 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
626 printf("Chat: unknown snac %04x/%04x\n", family, subtype);
627 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
631 case AIM_CONN_TYPE_RENDEZVOUS: {
632 /* make sure that we only get OFT frames on these connections */
633 if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
634 printf("faim: internal error: non-OFT frames on OFT connection\n");
635 workingPtr->handled = 1; /* get rid of it */
639 /* XXX: implement this */
640 printf("faim: OFT frame!\n");
644 case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
649 printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->commandlen);
650 workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
657 * This doesn't have to be called here. It could easily be done
658 * by a seperate thread or something. It's an administrative operation,
659 * and can take a while. Though the less you call it the less memory
662 aim_purge_rxqueue(sess);
667 faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
669 rxcallback_t userfunc = NULL;
672 int i = 10+8; /* skip SNAC and cookie */
676 type = aimutil_get16(command->data+i);
679 snlen = aimutil_get8(command->data+i);
682 memset(sn, 0, sizeof(sn));
683 strncpy(sn, (char *)command->data+i, snlen);
685 if ((userfunc = aim_callhandler(command->conn, 0x0004, 0x000c)))
686 ret = userfunc(sess, command, type, sn);
692 * The Rate Limiting System, An Abridged Guide to Nonsense.
694 * OSCAR defines several 'rate classes'. Each class has seperate
695 * rate limiting properties (limit level, alert level, disconnect
696 * level, etc), and a set of SNAC family/type pairs associated with
697 * it. The rate classes, their limiting properties, and the definitions
698 * of which SNACs are belong to which class, are defined in the
699 * Rate Response packet at login to each host.
701 * Logically, all rate offenses within one class count against further
702 * offenses for other SNACs in the same class (ie, sending messages
703 * too fast will limit the number of user info requests you can send,
704 * since those two SNACs are in the same rate class).
706 * Since the rate classes are defined dynamically at login, the values
707 * below may change. But they seem to be fairly constant.
709 * Currently, BOS defines five rate classes, with the commonly used
710 * members as follows...
713 * - Everything thats not in any of the other classes
716 * - Buddy list add/remove
717 * - Permit list add/remove
718 * - Deny list add/remove
721 * - User information requests
725 * - A few unknowns: 2/9, 2/b, and f/2
729 * - Outgoing chat ICBMs
731 * The only other thing of note is that class 5 (chat) has slightly looser
732 * limiting properties than class 3 (normal messages). But thats just a
733 * small bit of trivia for you.
735 * The last thing that needs to be learned about the rate limiting
736 * system is how the actual numbers relate to the passing of time. This
737 * seems to be a big mystery.
740 faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
742 rxcallback_t userfunc = NULL;
746 unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
747 unsigned long currentavg, maxavg;
751 code = aimutil_get16(command->data+i);
754 rateclass = aimutil_get16(command->data+i);
757 windowsize = aimutil_get32(command->data+i);
759 clear = aimutil_get32(command->data+i);
761 alert = aimutil_get32(command->data+i);
763 limit = aimutil_get32(command->data+i);
765 disconnect = aimutil_get32(command->data+i);
767 currentavg = aimutil_get32(command->data+i);
769 maxavg = aimutil_get32(command->data+i);
772 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x000a)))
773 ret = userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
778 faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
780 rxcallback_t userfunc = NULL;
783 unsigned short newevil;
784 struct aim_userinfo_s userinfo;
787 newevil = aimutil_get16(command->data+10);
790 memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
791 if (command->commandlen-i)
792 i += aim_extractuserinfo(command->data+i, &userinfo);
794 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0010)))
795 ret = userfunc(sess, command, newevil, &userinfo);
800 faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
801 struct command_rx_struct *command, ...)
803 rxcallback_t userfunc = NULL;
806 struct aim_tlvlist_t *tlvlist;
813 * 1 Mandatory upgrade
816 * 4 Nothing's wrong ("top o the world" -- normal)
819 id = aimutil_get16(command->data+10);
824 if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
827 if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
828 aim_freetlvchain(&tlvlist);
832 userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
834 ret = userfunc(sess, command, id, msg);
836 aim_freetlvchain(&tlvlist);
842 faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
843 struct command_rx_struct *command, ...)
845 rxcallback_t userfunc = NULL;
847 unsigned short *families = NULL;
850 famcount = (command->commandlen-10)/2;
851 if (!(families = malloc(command->commandlen-10)))
854 for (i = 0; i < famcount; i++)
855 families[i] = aimutil_get16(command->data+((i*2)+10));
857 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0003)))
858 ret = userfunc(sess, command, famcount, families);
865 faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
866 struct command_rx_struct *command)
868 rxcallback_t userfunc = NULL;
872 status = aimutil_get16(command->data+10);
874 if ((userfunc = aim_callhandler(command->conn, 0x0007, 0x0007)))
875 ret = userfunc(sess, command, status);
880 faim_internal int aim_parse_infochange(struct aim_session_t *sess,
881 struct command_rx_struct *command)
883 unsigned short subtype; /* called for both reply and change-reply */
886 subtype = aimutil_get16(command->data+2);
890 * unsigned short perms;
891 * unsigned short tlvcount;
892 * aim_tlv_t tlvs[tlvcount];
895 for (i = 10; i < command->commandlen; ) {
898 perms = aimutil_get16(command->data+i);
901 tlvcount = aimutil_get16(command->data+i);
905 rxcallback_t userfunc;
906 struct aim_tlv_t *tlv;
909 if ((aimutil_get16(command->data+i) == 0x0011) ||
910 (aimutil_get16(command->data+i) == 0x0004))
914 tlv = aim_grabtlvstr(command->data+i);
916 tlv = aim_grabtlv(command->data+i);
918 /* XXX fix so its only called once for the entire packet */
919 if ((userfunc = aim_callhandler(command->conn, 0x0007, subtype)))
920 userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
923 i += 2+2+tlv->length;
925 if (tlv && tlv->value)
937 faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
938 struct command_rx_struct *command, ...)
940 rxcallback_t userfunc = NULL;
944 vercount = (command->commandlen-10)/4;
946 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0018)))
947 ret = userfunc(sess, command, vercount, command->data+10);
952 faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
953 struct command_rx_struct *command, ...)
956 unsigned char *cookie = NULL;
958 rxcallback_t userfunc = NULL;
959 struct aim_tlvlist_t *tlvlist;
962 tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
964 if (aim_gettlv(tlvlist, 0x000d, 1))
965 serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
966 if (aim_gettlv(tlvlist, 0x0005, 1))
967 ip = aim_gettlv_str(tlvlist, 0x0005, 1);
968 if (aim_gettlv(tlvlist, 0x0006, 1))
969 cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
971 if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
977 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
978 ret = userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
979 free(sess->pendingjoin);
980 sess->pendingjoin = NULL;
981 sess->pendingjoinexchange = 0;
982 } else if (!serviceid || !ip || !cookie) { /* yeep! */
985 if ((userfunc = aim_callhandler(command->conn, 0x0001, 0x0005)))
986 ret = userfunc(sess, command, serviceid, ip, cookie);
994 aim_freetlvchain(&tlvlist);
999 faim_internal int aim_parse_unknown(struct aim_session_t *sess,
1000 struct command_rx_struct *command, ...)
1004 if (!sess || !command)
1007 faimdprintf(1, "\nRecieved unknown packet:");
1009 for (i = 0; i < command->commandlen; i++)
1012 faimdprintf(1, "\n\t");
1014 faimdprintf(1, "0x%2x ", command->data[i]);
1017 faimdprintf(1, "\n\n");
1023 faim_internal int aim_negchan_middle(struct aim_session_t *sess,
1024 struct command_rx_struct *command)
1026 struct aim_tlvlist_t *tlvlist;
1028 unsigned short code = 0;
1029 rxcallback_t userfunc = NULL;
1032 tlvlist = aim_readtlvchain(command->data, command->commandlen);
1034 if (aim_gettlv(tlvlist, 0x0009, 1))
1035 code = aim_gettlv16(tlvlist, 0x0009, 1);
1037 if (aim_gettlv(tlvlist, 0x000b, 1))
1038 msg = aim_gettlv_str(tlvlist, 0x000b, 1);
1040 if ((userfunc = aim_callhandler(command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
1041 ret = userfunc(sess, command, code, msg);
1043 aim_freetlvchain(&tlvlist);
1052 * aim_parse_generalerrs()
1054 * Middle handler for 0x0001 snac of each family.
1057 faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
1058 struct command_rx_struct *command, ...)
1060 unsigned short family;
1061 unsigned short subtype;
1064 rxcallback_t userfunc = NULL;
1066 family = aimutil_get16(command->data+0);
1067 subtype= aimutil_get16(command->data+2);
1069 if (command->commandlen > 10)
1070 error = aimutil_get16(command->data+10);
1072 if ((userfunc = aim_callhandler(command->conn, family, subtype)))
1073 ret = userfunc(sess, command, error);