diff libfaim/aim_rxhandlers.c @ 237:6ced2f1c8b24

[gaim-migrate @ 247] How cool is this, libfaim is making a comeback. I completely redid everything, as was necessary because of the updates to libfaim since gaim 0.9.7. You can sign on and send/recv IMs, but there's a bad lag between display updates that I haven't figured out how to fix yet. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sat, 20 May 2000 00:30:53 +0000
parents 68b230f8da5f
children 1eeece1c7b7b
line wrap: on
line diff
--- a/libfaim/aim_rxhandlers.c	Thu May 18 18:20:18 2000 +0000
+++ b/libfaim/aim_rxhandlers.c	Sat May 20 00:30:53 2000 +0000
@@ -1,80 +1,274 @@
+/*
+ * aim_rxhandlers.c
+ *
+ * This file contains most all of the incoming packet handlers, along
+ * with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
+ * actually done in aim_rxqueue.c.
+ *
+ */
+
+#include <aim.h>
 
 /*
-  aim_rxhandlers.c
-
-  This file contains most all of the incoming packet handlers, along
-  with aim_rxdispatch(), the Rx dispatcher.  Queue/list management is
-  actually done in aim_rxqueue.c.
-  
+ * Bleck functions get called when there's no non-bleck functions
+ * around to cleanup the mess...
  */
+int bleck(struct aim_session_t *sess,struct command_rx_struct *workingPtr, ...)
+{
+  u_short family;
+  u_short subtype;
 
+  u_short maxf;
+  u_short maxs;
 
-#include "aim.h" /* for most everything */
+  /* 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",
+    }
+  };
 
-int bleck(struct command_rx_struct *param, ...)
-{
+  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))
+    printf("bleck: null handler for %04x/%04x (%s)\n", family, subtype, literals[family][subtype+1]);
+  else
+    printf("bleck: null handler for %04x/%04x (no literal)\n",family,subtype);
+
   return 1;
 }
 
-/*
- * The callbacks.  Used to pass data up from the wire into the client.
- *
- * TODO: MASSIVE OVERHAUL.  This method of doing it (array of function 
- *       pointers) is ugly.  Overhaul may mean including chained callbacks
- *       for having client features such as run-time loadable modules.
- *
- */
-rxcallback_t aim_callbacks[] = {
-  bleck, /* incoming IM */
-  bleck, /* oncoming buddy */
-  bleck, /* offgoing buddy */
-  bleck, /* messaging error */
-  bleck, /* server missed call */
-  bleck, /* login phase 4 packet C command 1*/
-  bleck, /* login phase 4 packet C command 2 */
-  bleck, /* login phase 2, first resp */
-  bleck, /* login phase 2, second resp -- **REQUIRED** */
-  bleck, /* login phase 3 packet B */
-  bleck, /* login phase 3D packet A */
-  bleck, /* login phase 3D packet B */
-  bleck, /* login phase 3D packet C */
-  bleck, /* login phase 3D packet D */
-  bleck, /* login phase 3D packet E */
-  bleck, /* redirect -- **REQUIRED** */
-  bleck, /* server rate change */
-  bleck, /* user location error */
-  aim_parse_unknown, /* completely unknown command */
-  bleck, /* User Info Response */
-  bleck, /* User Search by Address response */
-  bleck, /* User Search by Name response */
-  bleck, /* user search fail */
-  bleck, /* auth error */
-  bleck, /* auth success */
-  bleck, /* auth server ready */
-  bleck, /* auth other */
-  bleck, /* info change reply */
-  bleck, /* ChatNAV: server ready */
-  0x00
-};
+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 *new,*cur;
+
+  if (!conn)
+    return -1;
+
+  faimdprintf(1, "aim_conn_addhandler: adding for %04x/%04x\n", family, type);
+
+  new = (struct aim_rxcblist_t *)calloc(1, sizeof(struct aim_rxcblist_t));
+  new->family = family;
+  new->type = type;
+  new->flags = flags;
+  if (!newhandler)
+    new->handler = &bleck;
+  else
+    new->handler = newhandler;
+  new->next = NULL;
+  
+  cur = conn->handlerlist;
+  if (!cur)
+    conn->handlerlist = new;
+  else 
+    {
+      while (cur->next)
+	cur = cur->next;
+      cur->next = new;
+    }
+
+  return 0;
+}
+
+int aim_clearhandlers(struct aim_conn_t *conn)
+{
+ struct aim_rxcblist_t *cur,*tmp;
+ if (!conn)
+   return -1;
 
+ cur = conn->handlerlist;
+ while(cur)
+   {
+     tmp = cur->next;
+     free(cur);
+     cur = tmp;
+   }
+ return 0;
+}
 
-int aim_register_callbacks(rxcallback_t *newcallbacks)
+rxcallback_t aim_callhandler(struct aim_conn_t *conn,
+		    u_short family,
+		    u_short type)
 {
-  int i = 0;
+  struct aim_rxcblist_t *cur;
+
+  if (!conn)
+    return NULL;
+
+  faimdprintf(1, "aim_callhandler: calling for %04x/%04x\n", family, type);
   
-  for (i = 0; aim_callbacks[i] != 0x00; i++)
+  cur = conn->handlerlist;
+  while(cur)
     {
-      if ( (newcallbacks[i] != NULL) &&
-	   (newcallbacks[i] != 0x00) )
-	{
-#if debug > 3
-	  printf("aim_register_callbacks: changed handler %d\n", i);
-#endif
-	  aim_callbacks[i] = newcallbacks[i];
-	}
+      if ( (cur->family == family) && (cur->type == type) )
+	return cur->handler;
+      cur = cur->next;
     }
-  
-  return 0;
+
+  if (type==0xffff)
+    return NULL;
+  return aim_callhandler(conn, family, 0xffff);
+}
+
+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)
+{
+  rxcallback_t userfunc = NULL;
+  userfunc = aim_callhandler(conn, family, type);
+  if (userfunc)
+    return userfunc(sess, ptr);
+  return 1; /* XXX */
 }
 
 /*
@@ -100,349 +294,380 @@
   TODO: Allow for NULL handlers.
   
  */
-int aim_rxdispatch(void)
+int aim_rxdispatch(struct aim_session_t *sess)
 {
   int i = 0;
   struct command_rx_struct *workingPtr = NULL;
   
-  if (aim_queue_incoming == NULL)
-    /* this shouldn't really happen, unless the main loop's select is broke  */
-    printf("parse_generic: incoming packet queue empty.\n");
-  else
-    {
-      workingPtr = aim_queue_incoming;
-      for (i = 0; workingPtr != NULL; i++)
-	{
-	  switch(workingPtr->conn->type)
-	    {
-	    case AIM_CONN_TYPE_AUTH:
-	      if ( (workingPtr->data[0] == 0x00) && 
-		   (workingPtr->data[1] == 0x00) &&
-		   (workingPtr->data[2] == 0x00) &&
-		   (workingPtr->data[3] == 0x01) )
-		{
-#if debug > 0
-		  fprintf(stderr, "got connection ack on auth line\n");
+  if (sess->queue_incoming == NULL) {
+    faimdprintf(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;
+
+      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: {
+	u_long head;
+	
+	head = aimutil_get32(workingPtr->data);
+	if (head == 0x00000001) {
+	  faimdprintf(1, "got connection ack on auth line\n");
+	  workingPtr->handled = 1;
+	} else {
+	  u_short family,subtype;
+	  
+	  family = aimutil_get16(workingPtr->data);
+	  subtype = aimutil_get16(workingPtr->data+2);
+	  
+	  switch (family) {
+	    /* New login protocol */
+#ifdef SNACLOGIN
+	  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_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0x0007, workingPtr);
+	    else
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0017, 0xffff, workingPtr);
+	    break;
+#else	
+	    /* XXX: this isnt foolproof */
+	  case 0x0001:
+	    if (subtype == 0x0003)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_SERVERREADY, workingPtr);
+	    else
+	      workingPtr->handled = aim_authparse(sess, workingPtr);
+	    break;
+	  case 0x0007:
+	    if (subtype == 0x0005)
+	      workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_ADM, AIM_CB_ADM_INFOCHANGE_REPLY, workingPtr);
+	    break;
+	  default:
+	    /* Old login protocol */
+	    /* any user callbacks will be called from here */
+	    workingPtr->handled = aim_authparse(sess, workingPtr);
 #endif
-		  workingPtr->handled = 1;
-		}
-	      else
-		{
-		  /* any user callbacks will be called from here */
-		  workingPtr->handled = aim_authparse(workingPtr);
-		}
-	      break;
-	    case AIM_CONN_TYPE_BOS:
-	      {
-		u_short family;
-		u_short subtype;
-		family = (workingPtr->data[0] << 8) + workingPtr->data[1];
-		subtype = (workingPtr->data[2] << 8) + workingPtr->data[3];
-		switch (family)
-		  {
-		  case 0x0000: /* not really a family, but it works */
-		    if (subtype == 0x0001)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P2_1])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  case 0x0001: /* Family: General */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = aim_parse_generalerrs(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P2_2])(workingPtr);
-			break;
-		      case 0x0005:
-			workingPtr->handled = aim_handleredirect_middle(workingPtr);
-			break;
-		      case 0x0007:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3_B])(workingPtr);
-			break;
-		      case 0x000a:
-			workingPtr->handled = (aim_callbacks[AIM_CB_RATECHANGE])(workingPtr);
-			break;
-		      case 0x000f:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_A])(workingPtr);
-			break;
-		      case 0x0013:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P4_C2])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0002: /* Family: Location */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_MISSED_IM])(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_D])(workingPtr);
-			break;
-		      case 0x0006:
-			workingPtr->handled = aim_parse_userinfo_middle(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0003: /* Family: Buddy List */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = aim_parse_generalerrs(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_C])(workingPtr);
-			break;
-		      case 0x000b:
-			workingPtr->handled = (aim_callbacks[AIM_CB_ONCOMING_BUDDY])(workingPtr);
-			break;
-		      case 0x000c:
-			workingPtr->handled = (aim_callbacks[AIM_CB_OFFGOING_BUDDY])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0004: /* Family: Messeging */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_USERERROR])(workingPtr);
-			break;
-		      case 0x0005:
-			workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_E])(workingPtr);
-			break;
-		      case 0x0007:
-			workingPtr->handled = aim_parse_incoming_im_middle(workingPtr);
-			break;
-		      case 0x000a:
-			workingPtr->handled = (aim_callbacks[AIM_CB_MISSED_CALL])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x0009:
-		    if (subtype == 0x0001)
-		      workingPtr->handled = aim_parse_generalerrs(workingPtr);
-		    else if (subtype == 0x0003)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P3D_B])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  case 0x000a:  /* Family: User lookup */
-		    switch (subtype)
-		      {
-		      case 0x0001:
-			workingPtr->handled = (aim_callbacks[AIM_CB_SEARCH_FAIL])(workingPtr);
-			break;
-		      case 0x0003:
-			workingPtr->handled = (aim_callbacks[AIM_CB_SEARCH_ADDRESS])(workingPtr);
-			break;
-		      default:
-			workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		      }
-		    break;
-		  case 0x000b:
-		    if (subtype == 0x0001)
-		      workingPtr->handled = aim_parse_generalerrs(workingPtr);
-		    else if (subtype == 0x0002)
-		      workingPtr->handled = (aim_callbacks[AIM_CB_LOGIN_P4_C1])(workingPtr);
-		    else
-		      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  default:
-		    workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-		    break;
-		  }
-	      }
-	      break;
-	    case AIM_CONN_TYPE_CHATNAV:
-	      if ( (workingPtr->data[0] == 0x00) &&
-		   (workingPtr->data[1] == 0x02) &&
-		   (workingPtr->data[2] == 0x00) &&
-		   (workingPtr->data[3] == 0x06) )
-		{
-		  workingPtr->handled = 1;
-		  aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
-		}
-	      else
-		workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    case AIM_CONN_TYPE_CHAT:
-	      fprintf(stderr, "\nAHH! Dont know what to do with CHAT stuff yet!\n");
-	      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    default:
-	      fprintf(stderr, "\nAHHHHH! UNKNOWN CONNECTION TYPE!\n\n");
-	      workingPtr->handled = (aim_callbacks[AIM_CB_UNKNOWN])(workingPtr);
-	      break;
-	    }
-	      /* move to next command */
-	  workingPtr = workingPtr->next;
+	  }
 	}
+	break;
+      }
+      case AIM_CONN_TYPE_BOS: {
+	u_short family;
+	u_short subtype;
+	
+	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, 0x0000, 0x0001, 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_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, 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_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000a, workingPtr);
+	    break;
+	  case 0x000f:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x000f, workingPtr);
+	    break;
+	  case 0x0013:
+	    workingPtr->handled = aim_parsemotd_middle(sess, workingPtr);
+	    break;
+	  default:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_GEN, AIM_CB_GEN_DEFAULT, workingPtr);
+	    break;
+	  }
+	case 0x0002: /* Family: Location */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0002, 0x0001, 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;
+	  }
+	case 0x0003: /* Family: Buddy List */
+	  switch (subtype) {
+	  case 0x0001:
+	    workingPtr->handled = aim_parse_generalerrs(sess, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0003, 0x0003, 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: Messeging */
+	  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 0x0007:
+	    workingPtr->handled = aim_parse_incoming_im_middle(sess, workingPtr);
+	    break;
+	  case 0x000a:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0004, 0x000a, 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_callhandler_noparam(sess, workingPtr->conn, 0x0009, 0x0003, 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_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0001, workingPtr);
+	    break;
+	  case 0x0003:
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x000a, 0x0003, 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;
+	default:
+	  workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	  break;
+	}
+	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 == 0x0002) && (subtype == 0x0006)) {
+	  workingPtr->handled = 1;
+	  aim_conn_setstatus(workingPtr->conn, AIM_CONN_STATUS_READY);
+	} 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, 0x0000, 0x0001, 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_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0003, workingPtr);
+	  else if (subtype == 0x0007)
+	    workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, 0x0001, 0x0007, workingPtr);
+	  else
+	    printf("Chat: unknown snac %04x/%04x\n", family, subtype);
+	} 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	
+	    printf("Chat: unknown snac %04x/%04x\n", family, subtype); 
+	} else {
+	  printf("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;
+      }
+      default:
+	printf("\ninternal error: unknown connection type (very bad.) (type = %d, fd = %d, channel = %02x, commandlen = %02x)\n\n", workingPtr->conn->type, workingPtr->conn->fd, workingPtr->type, workingPtr->commandlen);
+	workingPtr->handled = aim_callhandler_noparam(sess, workingPtr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, workingPtr);
+	break;
+      }	
     }
+  }
 
-  aim_queue_incoming = aim_purge_rxqueue(aim_queue_incoming);
+  /* 
+   * 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;
 }
 
-/*
- * TODO: check and cure memory leakage in this function.
- */
-int aim_authparse(struct command_rx_struct *command)
+int aim_parsemotd_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command, ...)
 {
-  int iserror = 0;
-  struct aim_tlv_t *tlv = NULL;
-  char *errorurl = NULL;
-  short errorcode;
-  int z = 0;
+  rxcallback_t userfunc = NULL;
+  char *msg;
+  int ret=1;
+  struct aim_tlvlist_t *tlvlist;
+  u_short id;
+
+  /*
+   * Dunno.
+   */
+  id = aimutil_get16(command->data+10);
+
+  /* 
+   * TLVs follow 
+   */
+  tlvlist = aim_readtlvchain(command->data+12, command->commandlen-12);
+  
+  msg = aim_gettlv_str(tlvlist, 0x000b, 1);
+  
+  userfunc = aim_callhandler(command->conn, 0x0001, 0x0013);
+  if (userfunc)
+    ret =  userfunc(sess, command, id, msg);
+
+  aim_freetlvchain(&tlvlist);
+
+  return ret;
+  
+}
 
-  if ( (command->data[0] == 0x00) &&
-       (command->data[1] == 0x01) &&
-       (command->data[2] == 0x00) &&
-       (command->data[3] == 0x03) )
+int aim_handleredirect_middle(struct aim_session_t *sess,
+			      struct command_rx_struct *command, ...)
+{
+  struct aim_tlv_t *tmptlv = NULL;
+  int serviceid = 0x00;
+  char cookie[AIM_COOKIELEN];
+  char *ip = NULL;
+  rxcallback_t userfunc = NULL;
+  struct aim_tlvlist_t *tlvlist;
+  int ret = 1;
+  
+  if (!(tlvlist = aim_readtlvchain(command->data+10, command->commandlen-10)))
     {
-      /* "server ready"  -- can be ignored */
-      return (aim_callbacks[AIM_CB_AUTH_SVRREADY])(command);
+      printf("libfaim: major bug: unable to read tlvchain from redirect\n");
+      return ret;
+    }
+  
+  if (!(tmptlv = aim_gettlv(tlvlist, 0x000d, 1))) 
+    {
+      printf("libfaim: major bug: no service ID in tlvchain from redirect\n");
+      aim_freetlvchain(&tlvlist);
+      return ret;
     }
-  else if ( (command->data[0] == 0x00) &&
-	    (command->data[1] == 0x07) &&
-	    (command->data[2] == 0x00) &&
-	    (command->data[3] == 0x05) )
+  serviceid = aimutil_get16(tmptlv->value);
+
+  if (!(ip = aim_gettlv_str(tlvlist, 0x0005, 1))) 
+    {
+      printf("libfaim: major bug: no IP in tlvchain from redirect (service 0x%02x)\n", serviceid);
+      aim_freetlvchain(&tlvlist);
+      return ret;
+    }
+  
+  if (!(tmptlv = aim_gettlv(tlvlist, 0x0006, 1)))
     {
-      /* "information change reply" */
-      return (aim_callbacks[AIM_CB_AUTH_INFOCHNG_REPLY])(command);
+      printf("libfaim: major bug: no cookie in tlvchain from redirect (service 0x%02x)\n", serviceid);
+      aim_freetlvchain(&tlvlist);
+      return ret;
+    }
+  memcpy(cookie, tmptlv->value, AIM_COOKIELEN);
+
+  if (serviceid == AIM_CONN_TYPE_CHAT)
+    {
+      /*
+       * Chat hack.
+       *
+       */
+      userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
+      if (userfunc)
+	ret =  userfunc(sess, command, serviceid, ip, cookie, sess->pendingjoin);
+      free(sess->pendingjoin);
+      sess->pendingjoin = NULL;
     }
   else
     {
-      /* anything else -- usually used for login; just parse as pure TLVs */
-
+      userfunc = aim_callhandler(command->conn, 0x0001, 0x0005);
+      if (userfunc)
+	ret =  userfunc(sess, command, serviceid, ip, cookie);
+    }
 
-      /* all this block does is figure out if it's an
-	 error or a success, nothing more */
-      while (z < command->commandlen)
-	{
-	  tlv = aim_grabtlvstr(&(command->data[z]));
-	  switch(tlv->type) 
-	    {
-	    case 0x0001: /* screen name */
-	      aim_logininfo.screen_name = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0004: /* error URL */
-	      errorurl = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0005: /* BOS IP */
-	      aim_logininfo.BOSIP = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0006: /* auth cookie */
-	      aim_logininfo.cookie = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv=NULL;
-	      break;
-	    case 0x0011: /* email addy */
-	      aim_logininfo.email = tlv->value;
-	      z += 2 + 2 + tlv->length;
-	      free(tlv);
-	      tlv = NULL;
-	      break;
-	    case 0x0013: /* registration status */
-	      aim_logininfo.regstatus = *(tlv->value);
-	      z += 2 + 2 + tlv->length;
-	      aim_freetlv(&tlv);
-	      break;
-	    case 0x0008: /* error code */
-	      errorcode = *(tlv->value);
-	      z += 2 + 2 + tlv->length;
-	      aim_freetlv(&tlv);
-	      iserror = 1;
-	      break;
-	    default:
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	  /* dunno */
-	    }
-	}
+  /*
+   * XXX: Is there a leak here?  Where does IP get freed?
+   */
+  aim_freetlvchain(&tlvlist);
 
-      if (iserror && 
-	  errorurl && 
-	  errorcode)
-	return (aim_callbacks[AIM_CB_AUTH_ERROR])(command, &aim_logininfo, errorurl, errorcode);
-      else if (aim_logininfo.screen_name && 
-	       aim_logininfo.cookie && aim_logininfo.BOSIP)
-	return (aim_callbacks[AIM_CB_AUTH_SUCCESS])(command, &aim_logininfo);
-      else
-	return (aim_callbacks[AIM_CB_AUTH_OTHER])(command);
-    }
+  return ret;
 }
 
-/*
- * TODO: check for and cure any memory leaks here.
- */
-int aim_handleredirect_middle(struct command_rx_struct *command, ...)
+int aim_parse_unknown(struct aim_session_t *sess,
+		      struct command_rx_struct *command, ...)
 {
-  struct aim_tlv_t *tlv = NULL;
-  int z = 10;
-  int serviceid;
-  char *cookie;
-  char *ip;
+  u_int i = 0;
 
-  while (z < command->commandlen)
-    {
-      tlv = aim_grabtlvstr(&(command->data[z]));
-      switch(tlv->type)
-	{
-	case 0x000d:  /* service id */
-	  aim_freetlv(&tlv);
-	  /* regrab as an int */
-	  tlv = aim_grabtlv(&(command->data[z]));
-	  serviceid = (tlv->value[0] << 8) + tlv->value[1]; /* hehe */
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	  break;
-	case 0x0005:  /* service server IP */
-	  ip = tlv->value;
-	  z += 2 + 2 + tlv->length;
-	  free(tlv);
-	  tlv = NULL;
-	  break;
-	case 0x0006: /* auth cookie */
-	  cookie = tlv->value;
-	  z += 2 + 2 + tlv->length;
-	  free(tlv);
-	  tlv = NULL;
-	  break;
-	default:
-	  /* dunno */
-	  z += 2 + 2 + tlv->length;
-	  aim_freetlv(&tlv);
-	}
-    }
-  return (aim_callbacks[AIM_CB_LOGIN_P3D_F])(command, serviceid, ip, cookie);
-}
-
-int aim_parse_unknown(struct command_rx_struct *command, ...)
-{
-  int i = 0;
-
-  printf("\nRecieved unknown packet:");
+  faimdprintf(1, "\nRecieved unknown packet:");
 
   for (i = 0; i < command->commandlen; i++)
     {
@@ -464,18 +689,20 @@
  * Middle handler for 0x0001 snac of each family.
  *
  */
-int aim_parse_generalerrs(struct command_rx_struct *command, ...)
+int aim_parse_generalerrs(struct aim_session_t *sess,
+			  struct command_rx_struct *command, ...)
 {
   u_short family;
   u_short subtype;
-  family = (command->data[0] << 8) + command->data[1];
-  subtype = (command->data[2] << 8) + command->data[3];
+  
+  family = aimutil_get16(command->data+0);
+  subtype= aimutil_get16(command->data+2);
   
   switch(family)
     {
     default:
       /* Unknown family */
-      return (aim_callbacks[AIM_CB_UNKNOWN])(command);
+      return aim_callhandler_noparam(sess, command->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_UNKNOWN, command);
     }
 
   return 1;