]> andersk Git - libfaim.git/blobdiff - src/rxhandlers.c
- Mon Sep 3 18:48:26 PDT 2001
[libfaim.git] / src / rxhandlers.c
index 16ce8205df5bf873bb421e45f09f27494f7e0d67..33b51f0d8e547200858a082acd715b14141d62b3 100644 (file)
 #define FAIM_INTERNAL
 #include <aim.h>
 
-/*
- * Bleck functions get called when there's no non-bleck functions
- * around to cleanup the mess...
- */
-faim_internal int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
+struct aim_rxcblist_s {
+       fu16_t family;
+       fu16_t type;
+       aim_rxcallback_t handler;
+       u_short flags;
+       struct aim_rxcblist_s *next;
+};
+
+static aim_module_t *findmodule(aim_session_t *sess, const char *name)
 {
-  u_short family;
-  u_short subtype;
-
-  u_short maxf;
-  u_short maxs;
-
-  /* XXX: this is ugly. and big just for debugging. */
-  char *literals[14][25] = {
-    {"Invalid", 
-     NULL
-    },
-    {"General", 
-     "Invalid",
-     "Error",
-     "Client Ready",
-     "Server Ready",
-     "Service Request",
-     "Redirect",
-     "Rate Information Request",
-     "Rate Information",
-     "Rate Information Ack",
-     NULL,
-     "Rate Information Change",
-     "Server Pause",
-     NULL,
-     "Server Resume",
-     "Request Personal User Information",
-     "Personal User Information",
-     "Evil Notification",
-     NULL,
-     "Migration notice",
-     "Message of the Day",
-     "Set Privacy Flags",
-     "Well Known URL",
-     "NOP"
-    },
-    {"Location", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Information", 
-      "Set user information", 
-      "Request User Information", 
-      "User Information", 
-      "Watcher Sub Request",
-      "Watcher Notification"
-    },
-    {"Buddy List Management", 
-      "Invalid", 
-      "Error", 
-      "Request Rights",
-      "Rights Information",
-      "Add Buddy", 
-      "Remove Buddy", 
-      "Watcher List Query", 
-      "Watcher List Response", 
-      "Watcher SubRequest", 
-      "Watcher Notification", 
-      "Reject Notification", 
-      "Oncoming Buddy", 
-      "Offgoing Buddy"
-    },
-    {"Messeging", 
-      "Invalid",
-      "Error", 
-      "Add ICBM Parameter",
-      "Remove ICBM Parameter", 
-      "Request Parameter Information",
-      "Parameter Information",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request",
-      "Evil Reply", 
-      "Missed Calls",
-      "Message Error", 
-      "Host Ack"
-    },
-    {"Advertisements", 
-      "Invalid", 
-      "Error", 
-      "Request Ad",
-      "Ad Data (GIFs)"
-    },
-    {"Invitation / Client-to-Client", 
-     "Invalid",
-     "Error",
-     "Invite a Friend",
-     "Invitation Ack"
-    },
-    {"Administrative", 
-      "Invalid",
-      "Error",
-      "Information Request",
-      "Information Reply",
-      "Information Change Request",
-      "Information Chat Reply",
-      "Account Confirm Request",
-      "Account Confirm Reply",
-      "Account Delete Request",
-      "Account Delete Reply"
-    },
-    {"Popups", 
-      "Invalid",
-      "Error",
-      "Display Popup"
-    },
-    {"BOS", 
-      "Invalid",
-      "Error",
-      "Request Rights",
-      "Rights Response",
-      "Set group permission mask",
-      "Add permission list entries",
-      "Delete permission list entries",
-      "Add deny list entries",
-      "Delete deny list entries",
-      "Server Error"
-    },
-    {"User Lookup", 
-      "Invalid",
-      "Error",
-      "Search Request",
-      "Search Response"
-    },
-    {"Stats", 
-      "Invalid",
-      "Error",
-      "Set minimum report interval",
-      "Report Events"
-    },
-    {"Translate", 
-      "Invalid",
-      "Error",
-      "Translate Request",
-      "Translate Reply",
-    },
-    {"Chat Navigation", 
-      "Invalid",
-      "Error",
-      "Request rights",
-      "Request Exchange Information",
-      "Request Room Information",
-      "Request Occupant List",
-      "Search for Room",
-      "Outgoing Message", 
-      "Incoming Message",
-      "Evil Request", 
-      "Evil Reply", 
-      "Chat Error",
-    }
-  };
-
-  maxf = sizeof(literals) / sizeof(literals[0]);
-  maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
-
-  family = aimutil_get16(workingPtr->data+0);
-  subtype= aimutil_get16(workingPtr->data+2);
-
-  if((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
-    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
-  else
-    faimdprintf(sess, 0, "bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
-
-  return 1;
-}
+       aim_module_t *cur;
 
-faim_export int aim_conn_addhandler(struct aim_session_t *sess,
-                       struct aim_conn_t *conn,
-                       u_short family,
-                       u_short type,
-                       rxcallback_t newhandler,
-                       u_short flags)
-{
-  struct aim_rxcblist_t *newcb;
-
-  if (!conn)
-    return -1;
-
-  faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
-
-  if (!(newcb = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t))))
-    return -1;
-  newcb->family = family;
-  newcb->type = type;
-  newcb->flags = flags;
-  if (!newhandler)
-    newcb->handler = &bleck;
-  else
-    newcb->handler = newhandler;
-  newcb->next = NULL;
-  
-  if (!conn->handlerlist)
-    conn->handlerlist = newcb;
-  else {
-    struct aim_rxcblist_t *cur;
-
-    cur = conn->handlerlist;
-
-    while (cur->next)
-      cur = cur->next;
-    cur->next = newcb;
-  }
-
-  return 0;
+       for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+               if (strcmp(name, cur->name) == 0)
+                       return cur;
+       }
+
+       return NULL;
 }
 
-faim_export int aim_clearhandlers(struct aim_conn_t *conn)
+faim_internal int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *))
 {
- struct aim_rxcblist_t *cur;
+       aim_module_t *mod;
+
+       if (!sess || !modfirst)
+               return -1;
 
- if (!conn)
-   return -1;
+       if (!(mod = malloc(sizeof(aim_module_t))))
+               return -1;
+       memset(mod, 0, sizeof(aim_module_t));
 
- for (cur = conn->handlerlist; cur; ) {
-   struct aim_rxcblist_t *tmp;
+       if (modfirst(sess, mod) == -1) {
+               free(mod);
+               return -1;
+       }
 
-   tmp = cur->next;
-   free(cur);
-   cur = tmp;
- }
- conn->handlerlist = NULL;
+       if (findmodule(sess, mod->name)) {
+               if (mod->shutdown)
+                       mod->shutdown(sess, mod);
+               free(mod);
+               return -1;
+       }
 
- return 0;
+       mod->next = (aim_module_t *)sess->modlistv;
+       (aim_module_t *)sess->modlistv = mod;
+
+       faimdprintf(sess, 1, "registered module %s (family 0x%04x)\n", mod->name, mod->family);
+
+       return 0;
 }
 
-faim_internal rxcallback_t aim_callhandler(struct aim_session_t *sess,
-                                          struct aim_conn_t *conn,
-                                          unsigned short family,
-                                          unsigned short type)
+faim_internal void aim__shutdownmodules(aim_session_t *sess)
 {
-  struct aim_rxcblist_t *cur;
+       aim_module_t *cur;
 
-  if (!conn)
-    return NULL;
+       for (cur = (aim_module_t *)sess->modlistv; cur; ) {
+               aim_module_t *tmp;
 
-  faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
-  
-  for (cur = conn->handlerlist; cur; cur = cur->next) {
-    if ((cur->family == family) && (cur->type == type))
-      return cur->handler;
-  }
+               tmp = cur->next;
 
-  if (type == AIM_CB_SPECIAL_DEFAULT) {
-    faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
-    return NULL; /* prevent infinite recursion */
-  }
+               if (cur->shutdown)
+                       cur->shutdown(sess, cur);
 
-  faimdprintf(sess, 1, "aim_callhandler: no handler for  0x%04x/0x%04x\n", family, type);
+               free(cur);
 
-  return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+               cur = tmp;
+       }
+
+       sess->modlistv = NULL;
+
+       return;
 }
 
-faim_internal int aim_callhandler_noparam(struct aim_session_t *sess,
-                                         struct aim_conn_t *conn,
-                                         u_short family,
-                                         u_short type,
-                                         struct command_rx_struct *ptr)
+static int consumesnac(aim_session_t *sess, aim_frame_t *rx)
 {
-  rxcallback_t userfunc = NULL;
-  userfunc = aim_callhandler(sess, conn, family, type);
-  if (userfunc)
-    return userfunc(sess, ptr);
-  return 1; /* XXX */
+       aim_module_t *cur;
+       aim_modsnac_t snac;
+
+       if (aim_bstream_empty(&rx->data) < 10)
+               return 0;
+
+       snac.family = aimbs_get16(&rx->data);
+       snac.subtype = aimbs_get16(&rx->data);
+       snac.flags = aimbs_get16(&rx->data);
+       snac.id = aimbs_get32(&rx->data);
+
+       for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+               if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+                               (cur->family != snac.family))
+                       continue;
+
+               if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+                       return 1;
+
+       }
+
+       return 0;
 }
 
-/*
-  aim_rxdispatch()
-
-  Basically, heres what this should do:
-    1) Determine correct packet handler for this packet
-    2) Mark the packet handled (so it can be dequeued in purge_queue())
-    3) Send the packet to the packet handler
-    4) Go to next packet in the queue and start over
-    5) When done, run purge_queue() to purge handled commands
-
-  Note that any unhandlable packets should probably be left in the
-  queue.  This is the best way to prevent data loss.  This means
-  that a single packet may get looked at by this function multiple
-  times.  This is more good than bad!  This behavior may change.
-
-  Aren't queue's fun? 
-
-  TODO: Get rid of all the ugly if's.
-  TODO: Clean up.
-  TODO: More support for mid-level handlers.
-  TODO: Allow for NULL handlers.
-  
- */
-faim_export int aim_rxdispatch(struct aim_session_t *sess)
+static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, fu16_t family, fu16_t subtype)
 {
-  int i = 0;
-  struct command_rx_struct *workingPtr = NULL;
-  
-  if (sess->queue_incoming == NULL) {
-    faimdprintf(sess, 1, "parse_generic: incoming packet queue empty.\n");
-    return 0;
-  } else {
-    workingPtr = sess->queue_incoming;
-    for (i = 0; workingPtr != NULL; workingPtr = workingPtr->next, i++) {
-      /*
-       * XXX: This is still fairly ugly.
-       */
-      if (workingPtr->handled)
-       continue;
-
-      /*
-       * This is a debugging/sanity check only and probably could/should be removed
-       * for stable code.
-       */
-      if (((workingPtr->hdrtype == AIM_FRAMETYPE_OFT) && 
-          (workingPtr->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
-         ((workingPtr->hdrtype == AIM_FRAMETYPE_OSCAR) && 
-          (workingPtr->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
-       faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", workingPtr->hdrtype, workingPtr->conn->type);
-       workingPtr->handled = 1;
-       continue;
-      }
-
-      switch(workingPtr->conn->type) {
-      case -1:
-       /*
-        * This can happen if we have a queued command
-        * that was recieved after a connection has 
-        * been terminated.  In which case, the handler
-        * list has been cleared, and there's nothing we
-        * can do for it.  We can only cancel it.
-        */
-       workingPtr->handled = 1;
-       break;
-      case AIM_CONN_TYPE_AUTH: {
-       unsigned long head;
-       
-       head = aimutil_get32(workingPtr->data);
-       if ((head == 0x00000001) && (workingPtr->commandlen == 4)) {
-         faimdprintf(sess, 1, "got connection ack on auth line\n");
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-       } else if (workingPtr->hdr.oscar.type == 0x04) {
-         /* Used only by the older login protocol */
-         workingPtr->handled = aim_authparse(sess, workingPtr);
-        } else {
-         unsigned short family,subtype;
-         
-         family = aimutil_get16(workingPtr->data);
-         subtype = aimutil_get16(workingPtr->data+2);
-         
-         switch (family) {
-           /* New login protocol */
-         case 0x0017:
-           if (subtype == 0x0001)
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0001, workingPtr);
-           else if (subtype == 0x0003)
-             workingPtr->handled = aim_authparse(sess, workingPtr);
-           else if (subtype == 0x0007)
-             workingPtr->handled = aim_authkeyparse(sess, workingPtr);
-           else
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
-           break;
-
-         case 0x0001:
-           if (subtype == 0x0003)
-             workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-           else if (subtype == 0x0007)
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
-           else if (subtype == 0x0018)
-             workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
-           else
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0xffff, workingPtr);
-           break;
-
-         case 0x0007:
-           if (subtype == 0x0003)
-             workingPtr->handled = aim_parse_infochange(sess, workingPtr);
-           else if (subtype == 0x0005)
-             workingPtr->handled = aim_parse_infochange(sess, workingPtr);
-           else if (subtype == 0x0007)
-             workingPtr->handled = aim_parse_accountconfirm(sess, workingPtr);
-           break;
-
-         case AIM_CB_FAM_SPECIAL:
-           if (subtype == AIM_CB_SPECIAL_DEBUGCONN_CONNECT) {
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-             break;
-           } else
-             workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
-           break;
-
-         default:
-           break;
-         }
-       }
-       break;
-      }
-      case AIM_CONN_TYPE_BOS: {
-       u_short family;
-       u_short subtype;
-
-       if (workingPtr->hdr.oscar.type == 0x04) {
-         workingPtr->handled = aim_negchan_middle(sess, workingPtr);
-         break;
-       }
+       aim_module_t *cur;
+       aim_modsnac_t snac;
+
+       snac.family = family;
+       snac.subtype = subtype;
+       snac.flags = snac.id = 0;
+
+       for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) {
+
+               if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && 
+                               (cur->family != snac.family))
+                       continue;
+
+               if (cur->snachandler(sess, cur, rx, &snac, &rx->data))
+                       return 1;
 
-       family = aimutil_get16(workingPtr->data);
-       subtype = aimutil_get16(workingPtr->data+2);
-       
-       switch (family) {
-       case 0x0000: /* not really a family, but it works */
-         if (subtype == 0x0001)
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-         else
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
-         break;
-       case 0x0001: /* Family: General */
-         switch (subtype) {
-         case 0x0001:
-           workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
-           break;
-         case 0x0003:
-           workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-           break;
-         case 0x0005:
-           workingPtr->handled = aim_handleredirect_middle(sess, workingPtr);
-           break;
-         case 0x0007:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
-           break;
-         case 0x000a:
-           workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
-           break;
-         case 0x000f:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
-           break;
-         case 0x0010:
-           workingPtr->handled = aim_parse_evilnotify_middle(sess, workingPtr);
-           break;
-         case 0x0013:
-           workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
-           break;
-         case 0x0018:
-           workingPtr->handled = aim_parse_hostversions(sess, workingPtr);
-           break;
-         default:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
-           break;
-         }
-         break;
-       case 0x0002: /* Family: Location */
-         switch (subtype) {
-         case 0x0001:
-           workingPtr->handled = aim_parse_locateerr(sess, workingPtr);
-           break;
-         case 0x0003:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0003, workingPtr);
-           break;
-         case 0x0006:
-           workingPtr->handled = aim_parse_userinfo_middle(sess, workingPtr);
-           break;
-         default:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOC, AIM_CB_LOC_DEFAULT, workingPtr);
-           break;
-         }
-         break;
-       case 0x0003: /* Family: Buddy List */
-         switch (subtype) {
-         case 0x0001:
-           workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
-           break;
-         case 0x0003:
-           workingPtr->handled = aim_parse_buddyrights(sess, workingPtr);
-           break;
-         case 0x000b: /* oncoming buddy */
-           workingPtr->handled = aim_parse_oncoming_middle(sess, workingPtr);
-           break;
-         case 0x000c: /* offgoing buddy */
-           workingPtr->handled = aim_parse_offgoing_middle(sess, workingPtr);
-           break;
-         default:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BUD, AIM_CB_BUD_DEFAULT, workingPtr);
-         }
-         break;
-       case 0x0004: /* Family: Messaging */
-         switch (subtype) {
-         case 0x0001:
-           workingPtr->handled = aim_parse_msgerror_middle(sess, workingPtr);
-           break;
-         case 0x0005:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x0005, workingPtr);
-           break;
-         case 0x0006:
-           workingPtr->handled = aim_parse_outgoing_im_middle(sess, workingPtr);
-           break;
-         case 0x0007:
-           workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
-           break;
-         case 0x000a:
-           workingPtr->handled = aim_parse_missedcall(sess, workingPtr);
-           break;
-         case 0x000c:
-           workingPtr->handled = aim_parse_msgack_middle(sess, workingPtr);
-           break;
-         default:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_MSG, AIM_CB_MSG_DEFAULT, workingPtr);
-         }
-         break;
-       case 0x0009:
-         if (subtype == 0x0001)
-           workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
-         else if (subtype == 0x0003)
-           workingPtr->handled = aim_parse_bosrights(sess, workingPtr);
-         else
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_BOS, AIM_CB_BOS_DEFAULT, workingPtr);
-         break;
-       case 0x000a:  /* Family: User lookup */
-         switch (subtype) {
-         case 0x0001:
-           workingPtr->handled = aim_parse_searcherror(sess, workingPtr);
-           break;
-         case 0x0003:
-           workingPtr->handled = aim_parse_searchreply(sess, workingPtr);
-           break;
-         default:
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_LOK, AIM_CB_LOK_DEFAULT, workingPtr);
-         }
-         break;
-       case 0x000b: {
-         if (subtype == 0x0001)
-           workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
-         else if (subtype == 0x0002)
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000b, 0x0002, workingPtr);
-         else
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_STS, AIM_CB_STS_DEFAULT, workingPtr);
-         break;
-       }
-       case 0x0013: {
-         faimdprintf(sess, 0, "lalala: 0x%04x/0x%04x\n", family, subtype);
-         break;
-       }
-       case AIM_CB_FAM_SPECIAL: 
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-         break;
-       default:
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
-         break;
-       } /* switch(family) */
-       break;
-      } /* AIM_CONN_TYPE_BOS */
-      case AIM_CONN_TYPE_ADS: {
-       unsigned short family;
-       unsigned short subtype;
-
-       family = aimutil_get16(workingPtr->data);
-       subtype= aimutil_get16(workingPtr->data+2);
-
-       if ((family == 0x0000) && (subtype == 0x00001)) {
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-       } else if ((family == 0x0001) && (subtype == 0x0003)) {
-         workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-       } else {
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-       }
-       break;
-      }
-      case AIM_CONN_TYPE_CHATNAV: {
-       u_short family;
-       u_short subtype;
-       family = aimutil_get16(workingPtr->data);
-       subtype= aimutil_get16(workingPtr->data+2);
-
-       if ((family == 0x0000) && (subtype == 0x00001)) {
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-       } else if ((family == 0x0001) && (subtype == 0x0003)) {
-         workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-       } else if ((family == 0x000d) && (subtype == 0x0009)) {
-         workingPtr->handled = aim_chatnav_parse_info(sess, workingPtr);
-       } else {
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-       }
-       break;
-      }
-      case AIM_CONN_TYPE_CHAT: {
-       u_short family, subtype;
-       
-       family = aimutil_get16(workingPtr->data);
-       subtype= aimutil_get16(workingPtr->data+2);
-       
-       if ((family == 0x0000) && (subtype == 0x00001)) {
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, workingPtr);
-       } else if (family == 0x0001) {
-         if (subtype == 0x0001)
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0001, workingPtr);
-         else if (subtype == 0x0003)
-           workingPtr->handled = aim_parse_hostonline(sess, workingPtr);
-         else if (subtype == 0x0007)
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
-         else if (subtype == 0x000a)
-           workingPtr->handled = aim_parse_ratechange_middle(sess, workingPtr);
-         else
-           workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, family, subtype, workingPtr);
-       } else if (family == 0x000e) {
-         if (subtype == 0x0002)
-           workingPtr->handled = aim_chat_parse_infoupdate(sess, workingPtr);
-         else if (subtype == 0x0003)
-           workingPtr->handled = aim_chat_parse_joined(sess, workingPtr);      
-         else if (subtype == 0x0004)
-           workingPtr->handled = aim_chat_parse_leave(sess, workingPtr);       
-         else if (subtype == 0x0006)
-           workingPtr->handled = aim_chat_parse_incoming(sess, workingPtr);
-         else  
-           faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype); 
-       } else {
-         faimdprintf(sess, 0, "Chat: unknown snac %04x/%04x\n", family, subtype);
-         workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_DEFAULT, workingPtr);
-       }
-       break;
-      }
-      case AIM_CONN_TYPE_RENDEZVOUS: {
-       /* make sure that we only get OFT frames on these connections */
-       if (workingPtr->hdrtype != AIM_FRAMETYPE_OFT) {
-         faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
-         workingPtr->handled = 1; /* get rid of it */
-         break;
        }
-       
-       /* XXX: implement this */
-       faimdprintf(sess, 0, "faim: OFT frame!\n");
-       
-       break;
-      }
-      case AIM_CONN_TYPE_RENDEZVOUS_OUT: {
-       /* not possible */
-       break;
-      }
-      default:
-       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);
-       workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
-       break;
-      }        
-    }
-  }
-
-  /* 
-   * This doesn't have to be called here.  It could easily be done
-   * by a seperate thread or something. It's an administrative operation,
-   * and can take a while. Though the less you call it the less memory
-   * you'll have :)
-   */
-  aim_purge_rxqueue(sess);
-  
-  return 0;
+
+       return 0;
 }
 
-faim_internal int aim_parse_msgack_middle(struct aim_session_t *sess, struct command_rx_struct *command)
+static int negchan_middle(aim_session_t *sess, aim_frame_t *fr)
 {
-  rxcallback_t userfunc = NULL;
-  char sn[MAXSNLEN];
-  unsigned short type;
-  int i = 10+8; /* skip SNAC and cookie */
-  int ret = 1;
-  unsigned char snlen;
+       aim_tlvlist_t *tlvlist;
+       char *msg = NULL;
+       fu16_t code = 0;
+       aim_rxcallback_t userfunc;
+       int ret = 1;
+
+       if (aim_bstream_empty(&fr->data) == 0) {
+               /* XXX should do something with this */
+               return 1;
+       }
+
+       /* Used only by the older login protocol */
+       /* XXX remove this special case? */
+       if (fr->conn->type == AIM_CONN_TYPE_AUTH)
+               return consumenonsnac(sess, fr, 0x0017, 0x0003);
 
-  type = aimutil_get16(command->data+i);
-  i += 2;
+       tlvlist = aim_readtlvchain(&fr->data);
 
-  snlen = aimutil_get8(command->data+i);
-  i++;
+       if (aim_gettlv(tlvlist, 0x0009, 1))
+               code = aim_gettlv16(tlvlist, 0x0009, 1);
 
-  memset(sn, 0, sizeof(sn));
-  strncpy(sn, (char *)command->data+i, snlen);
+       if (aim_gettlv(tlvlist, 0x000b, 1))
+               msg = aim_gettlv_str(tlvlist, 0x000b, 1);
 
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0004, 0x000c)))
-    ret =  userfunc(sess, command, type, sn);
+       if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
+               ret = userfunc(sess, fr, code, msg);
 
-  return ret;
+       aim_freetlvchain(&tlvlist);
+
+       free(msg);
+
+       return ret;
 }
 
 /*
- * The Rate Limiting System, An Abridged Guide to Nonsense.
- *
- * OSCAR defines several 'rate classes'.  Each class has seperate
- * rate limiting properties (limit level, alert level, disconnect
- * level, etc), and a set of SNAC family/type pairs associated with
- * it.  The rate classes, their limiting properties, and the definitions
- * of which SNACs are belong to which class, are defined in the
- * Rate Response packet at login to each host.  
- *
- * Logically, all rate offenses within one class count against further
- * offenses for other SNACs in the same class (ie, sending messages
- * too fast will limit the number of user info requests you can send,
- * since those two SNACs are in the same rate class).
- *
- * Since the rate classes are defined dynamically at login, the values
- * below may change. But they seem to be fairly constant.
- *
- * Currently, BOS defines five rate classes, with the commonly used
- * members as follows...
- *
- *  Rate class 0x0001:
- *     - Everything thats not in any of the other classes
- *
- *  Rate class 0x0002:
- *     - Buddy list add/remove
- *     - Permit list add/remove
- *     - Deny list add/remove
- *
- *  Rate class 0x0003:
- *     - User information requests
- *     - Outgoing ICBMs
- *
- *  Rate class 0x0004:
- *     - A few unknowns: 2/9, 2/b, and f/2
- *
- *  Rate class 0x0005:
- *     - Chat room create
- *     - Outgoing chat ICBMs
- *
- * The only other thing of note is that class 5 (chat) has slightly looser
- * limiting properties than class 3 (normal messages).  But thats just a 
- * small bit of trivia for you.
- *
- * The last thing that needs to be learned about the rate limiting
- * system is how the actual numbers relate to the passing of time.  This
- * seems to be a big mystery.
- * 
+ * Bleck functions get called when there's no non-bleck functions
+ * around to cleanup the mess...
  */
-faim_internal int aim_parse_ratechange_middle(struct aim_session_t *sess, struct command_rx_struct *command)
+faim_internal int bleck(aim_session_t *sess, aim_frame_t *frame, ...)
 {
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int i;
-  int code;
-  unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
-  unsigned long currentavg, maxavg;
-
-  i = 10;
-
-  code = aimutil_get16(command->data+i);
-  i += 2;
-
-  rateclass = aimutil_get16(command->data+i);
-  i += 2;
-
-  windowsize = aimutil_get32(command->data+i);
-  i += 4;
-  clear = aimutil_get32(command->data+i);
-  i += 4;
-  alert = aimutil_get32(command->data+i);
-  i += 4;
-  limit = aimutil_get32(command->data+i);
-  i += 4;
-  disconnect = aimutil_get32(command->data+i);
-  i += 4;
-  currentavg = aimutil_get32(command->data+i);
-  i += 4;
-  maxavg = aimutil_get32(command->data+i);
-  i += 4;
-
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x000a)))
-    ret =  userfunc(sess, command, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg);
-
-  return ret;
-}
+       fu16_t family, subtype;
+       fu16_t maxf, maxs;
+
+       static const char *channels[6] = {
+               "Invalid (0)",
+               "FLAP Version",
+               "SNAC",
+               "Invalid (3)",
+               "Negotiation",
+               "FLAP NOP"
+       };
+       static const int maxchannels = 5;
+       
+       /* XXX: this is ugly. and big just for debugging. */
+       static const char *literals[14][25] = {
+               {"Invalid", 
+                NULL
+               },
+               {"General", 
+                "Invalid",
+                "Error",
+                "Client Ready",
+                "Server Ready",
+                "Service Request",
+                "Redirect",
+                "Rate Information Request",
+                "Rate Information",
+                "Rate Information Ack",
+                NULL,
+                "Rate Information Change",
+                "Server Pause",
+                NULL,
+                "Server Resume",
+                "Request Personal User Information",
+                "Personal User Information",
+                "Evil Notification",
+                NULL,
+                "Migration notice",
+                "Message of the Day",
+                "Set Privacy Flags",
+                "Well Known URL",
+                "NOP"
+               },
+               {"Location", 
+                "Invalid",
+                "Error",
+                "Request Rights",
+                "Rights Information", 
+                "Set user information", 
+                "Request User Information", 
+                "User Information", 
+                "Watcher Sub Request",
+                "Watcher Notification"
+               },
+               {"Buddy List Management", 
+                "Invalid", 
+                "Error", 
+                "Request Rights",
+                "Rights Information",
+                "Add Buddy", 
+                "Remove Buddy", 
+                "Watcher List Query", 
+                "Watcher List Response", 
+                "Watcher SubRequest", 
+                "Watcher Notification", 
+                "Reject Notification", 
+                "Oncoming Buddy", 
+                "Offgoing Buddy"
+               },
+               {"Messeging", 
+                "Invalid",
+                "Error", 
+                "Add ICBM Parameter",
+                "Remove ICBM Parameter", 
+                "Request Parameter Information",
+                "Parameter Information",
+                "Outgoing Message", 
+                "Incoming Message",
+                "Evil Request",
+                "Evil Reply", 
+                "Missed Calls",
+                "Message Error", 
+                "Host Ack"
+               },
+               {"Advertisements", 
+                "Invalid", 
+                "Error", 
+                "Request Ad",
+                "Ad Data (GIFs)"
+               },
+               {"Invitation / Client-to-Client", 
+                "Invalid",
+                "Error",
+                "Invite a Friend",
+                "Invitation Ack"
+               },
+               {"Administrative", 
+                "Invalid",
+                "Error",
+                "Information Request",
+                "Information Reply",
+                "Information Change Request",
+                "Information Chat Reply",
+                "Account Confirm Request",
+                "Account Confirm Reply",
+                "Account Delete Request",
+                "Account Delete Reply"
+               },
+               {"Popups", 
+                "Invalid",
+                "Error",
+                "Display Popup"
+               },
+               {"BOS", 
+                "Invalid",
+                "Error",
+                "Request Rights",
+                "Rights Response",
+                "Set group permission mask",
+                "Add permission list entries",
+                "Delete permission list entries",
+                "Add deny list entries",
+                "Delete deny list entries",
+                "Server Error"
+               },
+               {"User Lookup", 
+                "Invalid",
+                "Error",
+                "Search Request",
+                "Search Response"
+               },
+               {"Stats", 
+                "Invalid",
+                "Error",
+                "Set minimum report interval",
+                "Report Events"
+               },
+               {"Translate", 
+                "Invalid",
+                "Error",
+                "Translate Request",
+                "Translate Reply",
+               },
+               {"Chat Navigation", 
+                "Invalid",
+                "Error",
+                "Request rights",
+                "Request Exchange Information",
+                "Request Room Information",
+                "Request Occupant List",
+                "Search for Room",
+                "Outgoing Message", 
+                "Incoming Message",
+                "Evil Request", 
+                "Evil Reply", 
+                "Chat Error",
+               }
+       };
+
+       maxf = sizeof(literals) / sizeof(literals[0]);
+       maxs = sizeof(literals[0]) / sizeof(literals[0][0]);
+
+       if (frame->hdr.flap.type == 0x02) {
+
+               family = aimbs_get16(&frame->data);
+               subtype = aimbs_get16(&frame->data);
+               
+               if ((family < maxf) && (subtype+1 < maxs) && (literals[family][subtype] != NULL))
+                       faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (%s)\n", channels[frame->hdr.flap.type], family, subtype, literals[family][subtype+1]);
+               else
+                       faimdprintf(sess, 0, "bleck: channel %s: null handler for %04x/%04x (no literal)\n", channels[frame->hdr.flap.type], family, subtype);
+       } else {
 
-faim_internal int aim_parse_evilnotify_middle(struct aim_session_t *sess, struct command_rx_struct *command)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int i;
-  unsigned short newevil;
-  struct aim_userinfo_s userinfo;
-
-  i = 10;
-  newevil = aimutil_get16(command->data+10);
-  i += 2;
-
-  memset(&userinfo, 0, sizeof(struct aim_userinfo_s));
-  if (command->commandlen-i)
-    i += aim_extractuserinfo(sess, command->data+i, &userinfo);
-
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0010)))
-    ret = userfunc(sess, command, newevil, &userinfo);
-  
-  return ret;
-}
+               if (frame->hdr.flap.type <= maxchannels)
+                       faimdprintf(sess, 0, "bleck: channel %s (0x%02x)\n", channels[frame->hdr.flap.type], frame->hdr.flap.type);
+               else
+                       faimdprintf(sess, 0, "bleck: unknown channel 0x%02x\n", frame->hdr.flap.type);
 
-faim_internal int aim_parsemotd_middle(struct aim_session_t *sess,
-                                      struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  char *msg;
-  int ret=1;
-  struct aim_tlvlist_t *tlvlist;
-  u_short id;
-
-  /*
-   * Code.
-   *
-   * Valid values:
-   *   1 Mandatory upgrade
-   *   2 Advisory upgrade
-   *   3 System bulletin
-   *   4 Nothing's wrong ("top o the world" -- normal)
-   *
-   */
-  id = aimutil_get16(command->data+10);
-
-  /* 
-   * TLVs follow 
-   */
-  if (!(tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12)))
-    return ret;
-
-  if (!(msg = aim_gettlv_str(tlvlist, 0x000b, 1))) {
-    aim_freetlvchain(&tlvlist);
-    return ret;
-  }
-  
-  userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0013);
-  if (userfunc)
-    ret =  userfunc(sess, command, id, msg);
-
-  aim_freetlvchain(&tlvlist);
-  free(msg);
-
-  return ret;  
+       }
+               
+       return 1;
 }
 
-faim_internal int aim_parse_hostonline(struct aim_session_t *sess,
-                                      struct command_rx_struct *command, ...)
+faim_export int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type, aim_rxcallback_t newhandler, fu16_t flags)
 {
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  unsigned short *families = NULL;
-  int famcount = 0, i;
+       struct aim_rxcblist_s *newcb;
 
-  famcount = (command->commandlen-10)/2;
-  if (!(families = malloc(command->commandlen-10)))
-    return ret;
+       if (!conn)
+               return -1;
 
-  for (i = 0; i < famcount; i++)
-    families[i] = aimutil_get16(command->data+((i*2)+10));
+       faimdprintf(sess, 1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
 
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0003)))
-    ret = userfunc(sess, command, famcount, families);
+       if (!(newcb = (struct aim_rxcblist_s *)calloc(1, sizeof(struct aim_rxcblist_s))))
+               return -1;
 
-  free(families);
+       newcb->family = family;
+       newcb->type = type;
+       newcb->flags = flags;
+       newcb->handler = newhandler ? newhandler : bleck;
+       newcb->next = NULL;
 
-  return ret;  
-}
+       if (!conn->handlerlist)
+               conn->handlerlist = (void *)newcb;
+       else {
+               struct aim_rxcblist_s *cur;
 
-faim_internal int aim_parse_accountconfirm(struct aim_session_t *sess,
-                                          struct command_rx_struct *command)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int status = -1;
-
-  status = aimutil_get16(command->data+10);
-
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, 0x0007)))
-    ret = userfunc(sess, command, status);
+               for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next)
+                       ;
+               cur->next = newcb;
+       }
 
-  return ret;  
+       return 0;
 }
 
-faim_internal int aim_parse_infochange(struct aim_session_t *sess,
-                                      struct command_rx_struct *command)
+faim_export int aim_clearhandlers(aim_conn_t *conn)
 {
-  unsigned short subtype; /* called for both reply and change-reply */
-  int i;
-
-  subtype = aimutil_get16(command->data+2);
-
-  /*
-   * struct {
-   *    unsigned short perms;
-   *    unsigned short tlvcount;
-   *    aim_tlv_t tlvs[tlvcount];
-   *  } admin_info[n];
-   */
-  for (i = 10; i < command->commandlen; ) {
-    int perms, tlvcount;
-
-    perms = aimutil_get16(command->data+i);
-    i += 2;
-
-    tlvcount = aimutil_get16(command->data+i);
-    i += 2;
-
-    while (tlvcount) {
-      rxcallback_t userfunc;
-      struct aim_tlv_t *tlv;
-      int str = 0;
-
-      if ((aimutil_get16(command->data+i) == 0x0011) ||
-         (aimutil_get16(command->data+i) == 0x0004))
-       str = 1;
-
-      if (str)
-       tlv = aim_grabtlvstr(command->data+i);
-      else
-       tlv = aim_grabtlv(command->data+i);
-
-      /* XXX fix so its only called once for the entire packet */
-      if ((userfunc = aim_callhandler(sess, command->conn, 0x0007, subtype)))
-       userfunc(sess, command, perms, tlv->type, tlv->length, tlv->value, str);
-
-      if (tlv)
-       i += 2+2+tlv->length;
-
-      if (tlv && tlv->value)
-       free(tlv->value);
-      if (tlv)
-       free(tlv);
-
-      tlvcount--;
-    }
-  }
-
-  return 1;
-}
+       struct aim_rxcblist_s *cur;
 
-faim_internal int aim_parse_hostversions(struct aim_session_t *sess,
-                                        struct command_rx_struct *command, ...)
-{
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
-  int vercount;
+       if (!conn)
+               return -1;
 
-  vercount = (command->commandlen-10)/4;
-  
-  if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0018)))
-    ret = userfunc(sess, command, vercount, command->data+10);
+       for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) {
+               struct aim_rxcblist_s *tmp;
 
-  return ret;  
-}
+               tmp = cur->next;
+               free(cur);
+               cur = tmp;
+       }
+       conn->handlerlist = NULL;
 
-faim_internal int aim_handleredirect_middle(struct aim_session_t *sess,
-                                           struct command_rx_struct *command, ...)
-{
-  int serviceid = 0;
-  unsigned char *cookie = NULL;
-  char *ip = NULL;
-  rxcallback_t userfunc = NULL;
-  struct aim_tlvlist_t *tlvlist;
-  int ret = 1;
-  
-  tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10);
-
-  if (aim_gettlv(tlvlist, 0x000d, 1))
-    serviceid = aim_gettlv16(tlvlist, 0x000d, 1);
-  if (aim_gettlv(tlvlist, 0x0005, 1))
-    ip = aim_gettlv_str(tlvlist, 0x0005, 1);
-  if (aim_gettlv(tlvlist, 0x0006, 1))
-    cookie = aim_gettlv_str(tlvlist, 0x0006, 1);
-
-  if ((serviceid == AIM_CONN_TYPE_CHAT) && sess->pendingjoin) {
-
-    /*
-     * Chat hack.
-     *
-     */
-    if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
-      ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin, (int)sess->pendingjoinexchange);
-      free(sess->pendingjoin);
-      sess->pendingjoin = NULL;
-      sess->pendingjoinexchange = 0;
-  } else if (!serviceid || !ip || !cookie) { /* yeep! */
-    ret = 1;
-  } else {
-    if ((userfunc = aim_callhandler(sess, command->conn, 0x0001, 0x0005)))
-      ret =  userfunc(sess, command, serviceid, ip, cookie);
-  }
-
-  if (ip)
-    free(ip);
-  if (cookie)
-    free(cookie);
-
-  aim_freetlvchain(&tlvlist);
-
-  return ret;
+       return 0;
 }
 
-faim_internal int aim_parse_unknown(struct aim_session_t *sess,
-                                         struct command_rx_struct *command, ...)
+faim_internal aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, fu16_t family, fu16_t type)
 {
-  u_int i = 0;
+       struct aim_rxcblist_s *cur;
 
-  if (!sess || !command)
-    return 1;
+       if (!conn)
+               return NULL;
 
-  faimdprintf(sess, 1, "\nRecieved unknown packet:");
+       faimdprintf(sess, 1, "aim_callhandler: calling for %04x/%04x\n", family, type);
 
-  for (i = 0; i < command->commandlen; i++)
-    {
-      if ((i % 8) == 0)
-       faimdprintf(sess, 1, "\n\t");
+       for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) {
+               if ((cur->family == family) && (cur->type == type))
+                       return cur->handler;
+       }
 
-      faimdprintf(sess, 1, "0x%2x ", command->data[i]);
-    }
-  
-  faimdprintf(sess, 1, "\n\n");
+       if (type == AIM_CB_SPECIAL_DEFAULT) {
+               faimdprintf(sess, 1, "aim_callhandler: no default handler for family 0x%04x\n", family);
+               return NULL; /* prevent infinite recursion */
+       }
 
-  return 1;
-}
+       faimdprintf(sess, 1, "aim_callhandler: no handler for  0x%04x/0x%04x\n", family, type);
 
+       return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT);
+}
 
-faim_internal int aim_negchan_middle(struct aim_session_t *sess,
-                                    struct command_rx_struct *command)
+faim_internal void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src)
 {
-  struct aim_tlvlist_t *tlvlist;
-  char *msg = NULL;
-  unsigned short code = 0;
-  rxcallback_t userfunc = NULL;
-  int ret = 1;
+       struct aim_rxcblist_s *cur;
 
-  tlvlist = aim_readtlvchain(command->data, command->commandlen);
-
-  if (aim_gettlv(tlvlist, 0x0009, 1))
-    code = aim_gettlv16(tlvlist, 0x0009, 1);
-
-  if (aim_gettlv(tlvlist, 0x000b, 1))
-    msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+       for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) {
+               aim_conn_addhandler(sess, dest, cur->family, cur->type, 
+                                               cur->handler, cur->flags);
+       }
 
-  if ((userfunc = aim_callhandler(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) 
-    ret =  userfunc(sess, command, code, msg);
+       return;
+}
 
-  aim_freetlvchain(&tlvlist);
+faim_internal int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,fu16_t family, fu16_t type, aim_frame_t *ptr)
+{
+       aim_rxcallback_t userfunc;
 
-  if (msg)
-    free(msg);
+       if ((userfunc = aim_callhandler(sess, conn, family, type)))
+               return userfunc(sess, ptr);
 
-  return ret;
+       return 1; /* XXX */
 }
 
 /*
- * aim_parse_generalerrs()
+ * aim_rxdispatch()
+ *
+ * Basically, heres what this should do:
+ *   1) Determine correct packet handler for this packet
+ *   2) Mark the packet handled (so it can be dequeued in purge_queue())
+ *   3) Send the packet to the packet handler
+ *   4) Go to next packet in the queue and start over
+ *   5) When done, run purge_queue() to purge handled commands
  *
- * Middle handler for 0x0001 snac of each family.
+ * TODO: Clean up.
+ * TODO: More support for mid-level handlers.
+ * TODO: Allow for NULL handlers.
  *
  */
-faim_internal int aim_parse_generalerrs(struct aim_session_t *sess,
-                                       struct command_rx_struct *command, ...)
+faim_export void aim_rxdispatch(aim_session_t *sess)
 {
-  unsigned short family;
-  unsigned short subtype;
-  int ret = 1;
-  int error = 0;
-  rxcallback_t userfunc = NULL;
-  
-  family = aimutil_get16(command->data+0);
-  subtype= aimutil_get16(command->data+2);
-  
-  if (command->commandlen > 10)
-    error = aimutil_get16(command->data+10);
-
-  if ((userfunc = aim_callhandler(sess, command->conn, family, subtype))) 
-    ret = userfunc(sess, command, error);
-
-  return ret;
+       int i;
+       aim_frame_t *cur;
+
+       for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) {
+
+               /*
+                * XXX: This is still fairly ugly.
+                */
+
+               if (cur->handled)
+                       continue;
+
+               /*
+                * This is a debugging/sanity check only and probably 
+                * could/should be removed for stable code.
+                */
+               if (((cur->hdrtype == AIM_FRAMETYPE_OFT) && 
+                  (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || 
+                 ((cur->hdrtype == AIM_FRAMETYPE_FLAP) && 
+                  (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) {
+                       faimdprintf(sess, 0, "rxhandlers: incompatible frame type %d on connection type 0x%04x\n", cur->hdrtype, cur->conn->type);
+                       cur->handled = 1;
+                       continue;
+               }
+
+               if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) {
+                       if (cur->hdrtype != AIM_FRAMETYPE_OFT) {
+                               faimdprintf(sess, 0, "internal error: non-OFT frames on OFT connection\n");
+                               cur->handled = 1; /* get rid of it */
+                       } else {
+                               /* XXX: implement this */
+                               faimdprintf(sess, 0, "faim: OFT frame!\n");
+                               cur->handled = 1; /* get rid of it */
+                       }
+                       continue;
+               }
+
+               if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) {
+                       /* not possible */
+                       faimdprintf(sess, 0, "rxdispatch called on RENDEZVOUS_OUT connection!\n");
+                       cur->handled = 1;
+                       continue;
+               }
+
+               if (cur->hdr.flap.type == 0x01) {
+                       
+                       cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */
+                       
+                       continue;
+                       
+               } else if (cur->hdr.flap.type == 0x02) {
+
+                       if ((cur->handled = consumesnac(sess, cur)))
+                               continue;
+
+               } else if (cur->hdr.flap.type == 0x04) {
+
+                       cur->handled = negchan_middle(sess, cur);
+                       continue;
+
+               } else if (cur->hdr.flap.type == 0x05)
+                       ;
+               
+               if (!cur->handled) {
+                       consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */
+                       cur->handled = 1;
+               }
+       }
+
+       /* 
+        * This doesn't have to be called here.  It could easily be done
+        * by a seperate thread or something. It's an administrative operation,
+        * and can take a while. Though the less you call it the less memory
+        * you'll have :)
+        */
+       aim_purge_rxqueue(sess);
+
+       return;
+}
+
+faim_internal int aim_parse_unknown(aim_session_t *sess, aim_frame_t *frame, ...)
+{
+       int i;
+
+       faimdprintf(sess, 1, "\nRecieved unknown packet:");
+
+       for (i = 0; aim_bstream_empty(&frame->data); i++) {
+               if ((i % 8) == 0)
+                       faimdprintf(sess, 1, "\n\t");
+
+               faimdprintf(sess, 1, "0x%2x ", aimbs_get8(&frame->data));
+       }
+
+       faimdprintf(sess, 1, "\n\n");
+
+       return 1;
 }
 
 
This page took 0.204229 seconds and 4 git commands to generate.