]> andersk Git - libfaim.git/blobdiff - aim_conn.c
- Sun Feb 11 01:07:36 UTC 2001
[libfaim.git] / aim_conn.c
index ddc0ba8f4f00848d752bdfa3bad1e0163cf03164..d8a8e210cfa22d3cb86d4c38c9556508019a51ea 100644 (file)
  *
  */
 
+#define FAIM_INTERNAL
 #include <faim/aim.h> 
 
-void aim_connrst(struct aim_session_t *sess)
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+/**
+ * aim_connrst - Clears out connection list, killing remaining connections.
+ * @sess: Session to be cleared
+ *
+ * Clears out the connection list and kills any connections left.
+ *
+ */
+faim_internal void aim_connrst(struct aim_session_t *sess)
 {
-  int i;
-  for (i = 0; i < AIM_CONN_MAX; i++)
-    {
-      sess->conns[i].fd = -1;
-      sess->conns[i].type = -1;
-      sess->conns[i].status = 0;
-      sess->conns[i].seqnum = 0;
-      sess->conns[i].lastactivity = 0;
-      sess->conns[i].forcedlatency = 0;
-      aim_clearhandlers(&(sess->conns[i]));
-      sess->conns[i].handlerlist = NULL;
-    }
+  faim_mutex_init(&sess->connlistlock);
+  if (sess->connlist) {
+    struct aim_conn_t *cur = sess->connlist, *tmp;
 
+    while(cur) {
+      tmp = cur->next;
+      aim_conn_close(cur);
+      free(cur);
+      cur = tmp;
+    }
+  }
+  sess->connlist = NULL;
+  return;
 }
 
-struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
+/**
+ * aim_conn_init - Reset a connection to default values.
+ * @deadconn: Connection to be reset
+ *
+ * Initializes and/or resets a connection structure.
+ *
+ */
+static void aim_conn_init(struct aim_conn_t *deadconn)
 {
-  int i;
-  for (i=0;i<AIM_CONN_MAX;i++)
-    if (sess->conns[i].fd == -1)
-      return &(sess->conns[i]);
-  return NULL;
-}
+  if (!deadconn)
+    return;
 
-void aim_conn_close(struct aim_conn_t *deadconn)
-{
-  if (deadconn->fd >= 3)
-    close(deadconn->fd);
   deadconn->fd = -1;
+  deadconn->subtype = -1;
   deadconn->type = -1;
   deadconn->seqnum = 0;
   deadconn->lastactivity = 0;
   deadconn->forcedlatency = 0;
-  aim_clearhandlers(deadconn);
   deadconn->handlerlist = NULL;
+  deadconn->priv = NULL;
+  faim_mutex_init(&deadconn->active);
+  faim_mutex_init(&deadconn->seqnum_lock);
+  
+  return;
 }
 
-struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
-                                   int type)
+/**
+ * aim_conn_getnext - Gets a new connection structure.
+ * @sess: Session
+ *
+ * Allocate a new empty connection structure.
+ *
+ */
+faim_internal struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
 {
-  int i;
-  for (i=0; i<AIM_CONN_MAX; i++)
-    if (sess->conns[i].type == type)
-      return &(sess->conns[i]);
-  return NULL;
+  struct aim_conn_t *newconn, *cur;
+
+  if (!(newconn = malloc(sizeof(struct aim_conn_t))))  
+    return NULL;
+
+  memset(newconn, 0, sizeof(struct aim_conn_t));
+  aim_conn_init(newconn);
+  newconn->next = NULL;
+
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL)
+    sess->connlist = newconn;
+  else {
+    for (cur = sess->connlist; cur->next; cur = cur->next)
+      ;
+    cur->next = newconn;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  return newconn;
 }
 
-/*
- * aim_newconn(type, dest)
+/**
+ * aim_conn_kill - Close and free a connection.
+ * @sess: Session for the connection
+ * @deadconn: Connection to be freed
+ *
+ * Close, clear, and free a connection structure.
+ *
+ */
+faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
+{
+  struct aim_conn_t *cur;
+
+  if (!deadconn || !*deadconn) 
+    return;
+
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL)
+    ;
+  else if (sess->connlist->next == NULL) {
+    if (sess->connlist == *deadconn)
+      sess->connlist = NULL;
+  } else {
+    cur = sess->connlist;
+    while (cur->next) {
+      if (cur->next == *deadconn) {
+       cur->next = cur->next->next;
+       break;
+      }
+      cur = cur->next;
+    }
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  /* XXX: do we need this for txqueue too? */
+  aim_rxqueue_cleanbyconn(sess, *deadconn);
+
+  if ((*deadconn)->fd != -1) 
+    aim_conn_close(*deadconn);
+  if ((*deadconn)->priv)
+    free((*deadconn)->priv);
+  free(*deadconn);
+  deadconn = NULL;
+
+  return;
+}
+
+/**
+ * aim_conn_close - Close a connection
+ * @deadconn: Connection to close
+ *
+ * Close (but not free) a connection.
+ *
+ * This leaves everything untouched except for clearing the 
+ * handler list and setting the fd to -1 (used to recognize
+ * dead connections).
+ *
+ */
+faim_export void aim_conn_close(struct aim_conn_t *deadconn)
+{
+
+  faim_mutex_destroy(&deadconn->active);
+  faim_mutex_destroy(&deadconn->seqnum_lock);
+  if (deadconn->fd >= 3)
+    close(deadconn->fd);
+  deadconn->fd = -1;
+  if (deadconn->handlerlist)
+    aim_clearhandlers(deadconn);
+
+  return;
+}
+
+/**
+ * aim_getconn_type - Find a connection of a specific type
+ * @sess: Session to search
+ * @type: Type of connection to look for
+ *
+ * Searches for a connection of the specified type in the 
+ * specified session.  Returns the first connection of that
+ * type found.
+ *
+ */
+faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
+                                               int type)
+{
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
+      break;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+  return cur;
+}
+
+/**
+ * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. 
+ * @sess: Session to connect
+ * @host: Host to connect to
+ * @port: Port to connect to
+ * @statusret: Return value of the connection
+ *
+ * Attempts to connect to the specified host via the configured
+ * proxy settings, if present.  If no proxy is configured for
+ * this session, the connection is done directly.
+ *
+ */
+static int aim_proxyconnect(struct aim_session_t *sess, 
+                           char *host, unsigned short port,
+                           int *statusret)
+{
+  int fd = -1;
+
+  if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
+    int i;
+    unsigned char buf[512];
+    struct sockaddr_in sa;
+    struct hostent *hp;
+    char *proxy;
+    unsigned short proxyport = 1080;
+
+    for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
+      if (sess->socksproxy.server[i] == ':') {
+       proxyport = atoi(&(sess->socksproxy.server[i+1]));
+       break;
+      }
+    }
+    proxy = (char *)malloc(i+1);
+    strncpy(proxy, sess->socksproxy.server, i);
+    proxy[i] = '\0';
+
+    if (!(hp = gethostbyname(proxy))) {
+      printf("proxyconnect: unable to resolve proxy name\n");
+      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+      return -1;
+    }
+    free(proxy);
+
+    memset(&sa.sin_zero, 0, 8);
+    sa.sin_port = htons(proxyport);
+    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+    sa.sin_family = hp->h_addrtype;
+  
+    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+      printf("proxyconnect: unable to connect to proxy\n");
+      close(fd);
+      return -1;
+    }
+
+    i = 0;
+    buf[0] = 0x05; /* SOCKS version 5 */
+    if (strlen(sess->socksproxy.username)) {
+      buf[1] = 0x02; /* two methods */
+      buf[2] = 0x00; /* no authentication */
+      buf[3] = 0x02; /* username/password authentication */
+      i = 4;
+    } else {
+      buf[1] = 0x01;
+      buf[2] = 0x00;
+      i = 3;
+    }
+
+    if (write(fd, buf, i) < i) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+
+    if (read(fd, buf, 2) < 2) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+
+    if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+      *statusret = EINVAL;
+      close(fd);
+      return -1;
+    }
+
+    /* check if we're doing username authentication */
+    if (buf[1] == 0x02) {
+      i  = aimutil_put8(buf, 0x01); /* version 1 */
+      i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
+      i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
+      i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
+      i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
+      if (write(fd, buf, i) < i) {
+       *statusret = errno;
+       close(fd);
+       return -1;
+      }
+      if (read(fd, buf, 2) < 2) {
+       *statusret = errno;
+       close(fd);
+       return -1;
+      }
+      if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+       *statusret = EINVAL;
+       close(fd);
+       return -1;
+      }
+    }
+
+    i  = aimutil_put8(buf, 0x05);
+    i += aimutil_put8(buf+i, 0x01); /* CONNECT */
+    i += aimutil_put8(buf+i, 0x00); /* reserved */
+    i += aimutil_put8(buf+i, 0x03); /* address type: host name */
+    i += aimutil_put8(buf+i, strlen(host));
+    i += aimutil_putstr(buf+i, host, strlen(host));
+    i += aimutil_put16(buf+i, port);
+
+    if (write(fd, buf, i) < i) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+    if (read(fd, buf, 10) < 10) {
+      *statusret = errno;
+      close(fd);
+      return -1;
+    }
+    if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+      *statusret = EINVAL;
+      close(fd);
+      return -1;
+    }
+
+  } else { /* connecting directly */
+    struct sockaddr_in sa;
+    struct hostent *hp;
+
+    if (!(hp = gethostbyname(host))) {
+      *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
+      return -1;
+    }
+
+    memset(&sa, 0, sizeof(struct sockaddr_in));
+    sa.sin_port = htons(port);
+    memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
+    sa.sin_family = hp->h_addrtype;
+  
+    fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+
+    if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
+      fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
+
+    if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
+      if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
+       if ((errno == EINPROGRESS) || (errno == EINTR)) {
+         if (statusret)
+           *statusret |= AIM_CONN_STATUS_INPROGRESS;
+         return fd;
+       }
+      }
+      close(fd);
+      fd = -1;
+    }
+  }
+  return fd;
+}
+
+faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess,
+                                              struct aim_conn_t *src)
+{
+  struct aim_conn_t *conn;
+    struct aim_rxcblist_t *cur;
+
+  if (!(conn = aim_conn_getnext(sess)))
+    return NULL;
+  
+
+  faim_mutex_lock(&conn->active);
+
+  conn->fd = src->fd;
+  conn->type = src->type;
+  conn->subtype = src->subtype;
+  conn->seqnum = src->seqnum;
+  conn->priv = src->priv;
+  conn->lastactivity = src->lastactivity;
+  conn->forcedlatency = src->forcedlatency;
+
+  /* clone handler list */
+  for (cur = src->handlerlist; cur; cur = cur->next) {
+    aim_conn_addhandler(sess, conn, cur->family, cur->type, 
+                       cur->handler, cur->flags);
+  }
+
+  faim_mutex_unlock(&conn->active);
+
+  return conn;
+}
+
+/**
+ * aim_newconn - Open a new connection
+ * @sess: Session to create connection in
+ * @type: Type of connection to create
+ * @dest: Host to connect to (in "host:port" syntax)
  *
- * Opens a new connection to the specified dest host of type type.
+ * Opens a new connection to the specified dest host of specified
+ * type, using the proxy settings if available.  If @host is %NULL,
+ * the connection is allocated and returned, but no connection 
+ * is made.
  *
- * TODO: fix for proxies
  * FIXME: Return errors in a more sane way.
  *
  */
-struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
-                              int type, char *dest)
+faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
+                                          int type, char *dest)
 {
   struct aim_conn_t *connstruct;
   int ret;
-  struct sockaddr_in sa;
-  struct hostent *hp;
   u_short port = FAIM_LOGIN_PORT;
   char *host = NULL;
   int i=0;
-  
-  if (!dest || ((connstruct=aim_conn_getnext(sess))==NULL))
+
+  if ((connstruct=aim_conn_getnext(sess))==NULL)
     return NULL;
 
+  faim_mutex_lock(&connstruct->active);
+  
   connstruct->type = type;
 
+  if (!dest) { /* just allocate a struct */
+    connstruct->fd = -1;
+    connstruct->status = 0;
+    faim_mutex_unlock(&connstruct->active);
+    return connstruct;
+  }
+
   /* 
    * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
    * colon, in the BOS redirect.  This fatally breaks all previous 
@@ -91,176 +436,382 @@ struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
    *
    */
 
-  for(i=0;i<strlen(dest);i++)
-    {
-      if (dest[i] == ':') {
-       port = atoi(&(dest[i+1]));
-       break;
-      }
+  for(i=0;i<(int)strlen(dest);i++) {
+    if (dest[i] == ':') {
+      port = atoi(&(dest[i+1]));
+      break;
     }
+  }
   host = (char *)malloc(i+1);
   strncpy(host, dest, i);
   host[i] = '\0';
 
-  hp = gethostbyname2(host, AF_INET);
-  free(host);
+  if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
+    connstruct->fd = -1;
+    connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
+    free(host);
+    faim_mutex_unlock(&connstruct->active);
+    return connstruct;
+  } else
+    connstruct->fd = ret;
+  
+  faim_mutex_unlock(&connstruct->active);
 
-  if (hp == NULL)
-    {
-      connstruct->status = (h_errno | AIM_CONN_STATUS_RESOLVERR);
-      return connstruct;
-    }
+  free(host);
 
-  memset(&sa.sin_zero, 0, 8);
-  sa.sin_port = htons(port);
-  memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
-  sa.sin_family = hp->h_addrtype;
-  
-  connstruct->fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
-  ret = connect(connstruct->fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in));
-  if( ret < 0)
-    {
-      connstruct->fd = -1;
-      connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
-      return connstruct;
-    }
-  
   return connstruct;
 }
 
-int aim_conngetmaxfd(struct aim_session_t *sess)
+/**
+ * aim_conngetmaxfd - Return the highest valued file discriptor in session
+ * @sess: Session to search
+ *
+ * Returns the highest valued filed descriptor of all open 
+ * connections in @sess.
+ *
+ */
+faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
 {
-  int i,j;
-  j=0;
-  for (i=0;i<AIM_CONN_MAX;i++)
-    if(sess->conns[i].fd > j)
-      j = sess->conns[i].fd;
+  int j = 0;
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->fd > j)
+      j = cur->fd;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
   return j;
 }
 
-int aim_countconn(struct aim_session_t *sess)
+/**
+ * aim_conn_in_sess - Predicate to test the precense of a connection in a sess
+ * @sess: Session to look in
+ * @conn: Connection to look for
+ *
+ * Searches @sess for the passed connection.  Returns 1 if its present,
+ * zero otherwise.
+ *
+ */
+faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
 {
-  int i,cnt;
-  cnt = 0;
-  for (i=0;i<AIM_CONN_MAX;i++)
-    if (sess->conns[i].fd > -1)
-      cnt++;
-  return cnt;
+  struct aim_conn_t *cur;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for(cur = sess->connlist; cur; cur = cur->next)
+    if(cur == conn) {
+      faim_mutex_unlock(&sess->connlistlock);
+      return 1;
+    }
+  faim_mutex_unlock(&sess->connlistlock);
+  return 0;
 }
 
-/*
- * aim_select(timeout)
+/**
+ * aim_select - Wait for a socket with data or timeout
+ * @sess: Session to wait on
+ * @timeout: How long to wait
+ * @status: Return status
  *
  * Waits for a socket with data or for timeout, whichever comes first.
- * See select(2).
+ * See select().
  * 
+ * Return codes in *status:
+ *   -1  error in select() (%NULL returned)
+ *    0  no events pending (%NULL returned)
+ *    1  outgoing data pending (%NULL returned)
+ *    2  incoming data pending (connection with pending data returned)
+ *
+ * XXX: we could probably stand to do a little courser locking here.
+ *
  */ 
-struct aim_conn_t *aim_select(struct aim_session_t *sess,
-                             struct timeval *timeout)
+faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
+                                         struct timeval *timeout, int *status)
 {
-  fd_set fds;
-  fd_set errfds;
-  int i;
+  struct aim_conn_t *cur;
+  fd_set fds, wfds;
+  int maxfd = 0;
+  int i, haveconnecting = 0;
 
-  if (aim_countconn(sess) <= 0)
-    return 0;
+  faim_mutex_lock(&sess->connlistlock);
+  if (sess->connlist == NULL) {
+    faim_mutex_unlock(&sess->connlistlock);
+    *status = -1;
+    return NULL;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
+
+  FD_ZERO(&fds);
+  FD_ZERO(&wfds);
+  maxfd = 0;
+
+  faim_mutex_lock(&sess->connlistlock);
+  for (cur = sess->connlist; cur; cur = cur->next) {
+    if (cur->fd == -1) {
+      /* don't let invalid/dead connections sit around */
+      *status = 2;
+      faim_mutex_unlock(&sess->connlistlock);
+      return cur;
+    } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
+      FD_SET(cur->fd, &wfds);
+      haveconnecting++;
+    }
+    FD_SET(cur->fd, &fds);
+    if (cur->fd > maxfd)
+      maxfd = cur->fd;
+  }
+  faim_mutex_unlock(&sess->connlistlock);
 
   /* 
-   * If we have data waiting to be sent, return immediatly
+   * If we have data waiting to be sent, return
+   *
+   * We have to not do this if theres at least one
+   * connection thats still connecting, since that connection
+   * may have queued data and this return would prevent
+   * the connection from ever completing!  This is a major
+   * inadequacy of the libfaim way of doing things.  It means
+   * that nothing can transmit as long as there's connecting
+   * sockets. Evil.
+   *
+   * But its still better than having blocking connects.
+   *
    */
-  if (sess->queue_outgoing)
-    return (struct aim_conn_t *)1;
+  if (!haveconnecting && (sess->queue_outgoing != NULL)) {
+    *status = 1;
+    return NULL;
+  } 
 
-  FD_ZERO(&fds);
-  FD_ZERO(&errfds);
-  
-  for(i=0;i<AIM_CONN_MAX;i++)
-    if (sess->conns[i].fd>-1)
-      {
-       FD_SET(sess->conns[i].fd, &fds);
-       FD_SET(sess->conns[i].fd, &errfds);
+  if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
+    faim_mutex_lock(&sess->connlistlock);
+    for (cur = sess->connlist; cur; cur = cur->next) {
+      if ((FD_ISSET(cur->fd, &fds)) || 
+         ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
+          FD_ISSET(cur->fd, &wfds))) {
+       *status = 2;
+       faim_mutex_unlock(&sess->connlistlock);
+       return cur; /* XXX race condition here -- shouldnt unlock connlist */
       }
-  
-  i = select(aim_conngetmaxfd(sess)+1, &fds, NULL, &errfds, timeout);
-  if (i>=1)
-    {
-      int j;
-      for (j=0;j<AIM_CONN_MAX;j++)
-       {
-         if (sess->conns[j].fd > -1)
-           {
-             if ((FD_ISSET(sess->conns[j].fd, &errfds)))
-               {
-                 /* got an exception; close whats left of it up */
-                 aim_conn_close(&(sess->conns[j]));
-                 return (struct aim_conn_t *)-1;
-               }
-             else if ((FD_ISSET(sess->conns[j].fd, &fds)))
-               return &(sess->conns[j]);  /* return the first waiting struct */
-           }
-       }
-      /* should never get here */
     }
+    *status = 0; /* shouldn't happen */
+  } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
+    *status = 0;
   else
-    return (struct aim_conn_t *)i;  /* no waiting or error, return -- FIXME: return type funnies */
-  return NULL; /* NO REACH */
+    *status = i; /* can be 0 or -1 */
+
+  faim_mutex_unlock(&sess->connlistlock);
+  return NULL;  /* no waiting or error, return */
 }
 
-int aim_conn_isready(struct aim_conn_t *conn)
+/**
+ * aim_conn_isready - Test if a connection is marked ready
+ * @conn: Connection to test
+ *
+ * Returns true if the connection is ready, false otherwise.
+ * Returns -1 if the connection is invalid.
+ *
+ * XXX: This is deprecated.
+ *
+ */
+faim_export int aim_conn_isready(struct aim_conn_t *conn)
 {
   if (conn)
     return (conn->status & 0x0001);
-  else
-    return -1;
+  return -1;
 }
 
-int aim_conn_setstatus(struct aim_conn_t *conn, int status)
+/**
+ * aim_conn_setstatus - Set the status of a connection
+ * @conn: Connection
+ * @status: New status
+ *
+ * @newstatus is %XOR'd with the previous value of the connection
+ * status and returned.  Returns -1 if the connection is invalid.
+ *
+ * This isn't real useful.
+ *
+ */
+faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
 {
-  if (conn)
-    return (conn->status ^= status);
-  else
+  int val;
+
+  if (!conn)
     return -1;
+  
+  faim_mutex_lock(&conn->active);
+  val = conn->status ^= status;
+  faim_mutex_unlock(&conn->active);
+  return val;
 }
 
-int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
+/**
+ * aim_conn_setlatency - Set a forced latency value for connection
+ * @conn: Conn to set latency for
+ * @newval: Number of seconds to force between transmits
+ *
+ * Causes @newval seconds to be spent between transmits on a connection.
+ *
+ * This is my lame attempt at overcoming not understanding the rate
+ * limiting. 
+ *
+ * XXX: This should really be replaced with something that scales and
+ * backs off like the real rate limiting does.
+ *
+ */
+faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
 {
   if (!conn)
     return -1;
-  
+
+  faim_mutex_lock(&conn->active);
   conn->forcedlatency = newval;
   conn->lastactivity = 0; /* reset this just to make sure */
+  faim_mutex_unlock(&conn->active);
 
   return 0;
 }
 
-void aim_session_init(struct aim_session_t *sess)
+/**
+ * aim_setupproxy - Configure a proxy for this session
+ * @sess: Session to set proxy for
+ * @server: SOCKS server
+ * @username: SOCKS username
+ * @password: SOCKS password
+ *
+ * Call this with your SOCKS5 proxy server parameters before
+ * the first call to aim_newconn().  If called with all %NULL
+ * args, it will clear out a previously set proxy.  
+ *
+ * Set username and password to %NULL if not applicable.
+ *
+ */
+faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
 {
-  int i;
+  /* clear out the proxy info */
+  if (!server || !strlen(server)) {
+    memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
+    memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
+    memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
+    return;
+  }
 
+  strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
+  if (username && strlen(username)) 
+    strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
+  if (password && strlen(password))
+    strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
+  return;
+}
+
+/**
+ * aim_session_init - Initializes a session structure
+ * @sess: Session to initialize
+ * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
+ *
+ * Sets up the initial values for a session.
+ *
+ */
+faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags)
+{
   if (!sess)
     return;
 
-  sess->logininfo.screen_name[0] = '\0';
-  sess->logininfo.BOSIP = NULL;
-  sess->logininfo.cookie[0] = '\0';
-  sess->logininfo.email = NULL;
-  sess->logininfo.regstatus = 0x00;
-  
-  for (i = 0; i < AIM_CONN_MAX; i++)
-    {
-      sess->conns[i].fd = -1;
-      sess->conns[i].type = -1;
-      sess->conns[i].status = 0;
-      sess->conns[i].seqnum = 0;
-      sess->conns[i].lastactivity = 0;
-      sess->conns[i].forcedlatency = 0;
-      sess->conns[i].handlerlist = NULL;
-    }
-  
+  memset(sess, 0, sizeof(struct aim_session_t));
+  aim_connrst(sess);
   sess->queue_outgoing = NULL;
   sess->queue_incoming = NULL;
-  sess->outstanding_snacs = NULL;
+  sess->pendingjoin = NULL;
+  sess->pendingjoinexchange = 0;
+  aim_initsnachash(sess);
+  sess->msgcookies = NULL;
   sess->snac_nextid = 0x00000001;
 
+  sess->flags = 0;
+
+  /*
+   * Default to SNAC login unless XORLOGIN is explicitly set.
+   */
+  if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
+    sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
+  sess->flags |= flags;
+
+  /*
+   * This must always be set.  Default to the queue-based
+   * version for back-compatibility.  
+   */
+  aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
+
   return;
 }
+
+/**
+ * aim_conn_isconnecting - Determine if a connection is connecting
+ * @conn: Connection to examine
+ *
+ * Returns nonzero if the connection is in the process of
+ * connecting (or if it just completed and aim_conn_completeconnect()
+ * has yet to be called on it).
+ *
+ */
+faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
+{
+  if (!conn)
+    return 0;
+  return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
+}
+
+faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+  fd_set fds, wfds;
+  struct timeval tv;
+  int res, error = ETIMEDOUT;
+  rxcallback_t userfunc;
+
+  if (!conn || (conn->fd == -1))
+    return -1;
+
+  if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
+    return -1;
+
+  FD_ZERO(&fds);
+  FD_SET(conn->fd, &fds);
+  FD_ZERO(&wfds);
+  FD_SET(conn->fd, &wfds);
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+
+  if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
+    error = errno;
+    aim_conn_close(conn);
+    errno = error;
+    return -1;
+  } else if (res == 0) {
+    printf("faim: aim_conn_completeconnect: false alarm on %d\n", conn->fd);
+    return 0; /* hasn't really completed yet... */
+  } 
+
+  if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
+    int len = sizeof(error);
+
+    if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+      error = errno;
+  }
+
+  if (error) {
+    aim_conn_close(conn);
+    errno = error;
+    return -1;
+  }
+
+  fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
+
+  conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
+
+  if ((userfunc = aim_callhandler(conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
+    userfunc(sess, NULL, conn);
+
+  /* Flush out the queues if there was something waiting for this conn  */
+  aim_tx_flushqueue(sess);
+
+  return 0;
+}
This page took 0.159823 seconds and 4 git commands to generate.