]> andersk Git - libfaim.git/blob - src/conn.c
- Fri Jun 15 17:51:27 EDT 2001
[libfaim.git] / src / conn.c
1
2 /*
3  *  aim_conn.c
4  *
5  * Does all this gloriously nifty connection handling stuff...
6  *
7  */
8
9 #define FAIM_INTERNAL
10 #include <aim.h> 
11
12 #ifndef _WIN32
13 #include <netdb.h>
14 #include <sys/socket.h>
15 #include <netinet/in.h>
16 #endif
17
18 /**
19  * aim_connrst - Clears out connection list, killing remaining connections.
20  * @sess: Session to be cleared
21  *
22  * Clears out the connection list and kills any connections left.
23  *
24  */
25 static void aim_connrst(struct aim_session_t *sess)
26 {
27   faim_mutex_init(&sess->connlistlock);
28   if (sess->connlist) {
29     struct aim_conn_t *cur = sess->connlist, *tmp;
30
31     while(cur) {
32       tmp = cur->next;
33       aim_conn_close(cur);
34       free(cur);
35       cur = tmp;
36     }
37   }
38   sess->connlist = NULL;
39   return;
40 }
41
42 /**
43  * aim_conn_init - Reset a connection to default values.
44  * @deadconn: Connection to be reset
45  *
46  * Initializes and/or resets a connection structure.
47  *
48  */
49 static void aim_conn_init(struct aim_conn_t *deadconn)
50 {
51   if (!deadconn)
52     return;
53
54   deadconn->fd = -1;
55   deadconn->subtype = -1;
56   deadconn->type = -1;
57   deadconn->seqnum = 0;
58   deadconn->lastactivity = 0;
59   deadconn->forcedlatency = 0;
60   deadconn->handlerlist = NULL;
61   deadconn->priv = NULL;
62   faim_mutex_init(&deadconn->active);
63   faim_mutex_init(&deadconn->seqnum_lock);
64   
65   return;
66 }
67
68 /**
69  * aim_conn_getnext - Gets a new connection structure.
70  * @sess: Session
71  *
72  * Allocate a new empty connection structure.
73  *
74  */
75 static struct aim_conn_t *aim_conn_getnext(struct aim_session_t *sess)
76 {
77   struct aim_conn_t *newconn, *cur;
78
79   if (!(newconn = malloc(sizeof(struct aim_conn_t))))   
80     return NULL;
81
82   memset(newconn, 0, sizeof(struct aim_conn_t));
83   aim_conn_init(newconn);
84   newconn->next = NULL;
85
86   faim_mutex_lock(&sess->connlistlock);
87   if (sess->connlist == NULL)
88     sess->connlist = newconn;
89   else {
90     for (cur = sess->connlist; cur->next; cur = cur->next)
91       ;
92     cur->next = newconn;
93   }
94   faim_mutex_unlock(&sess->connlistlock);
95
96   return newconn;
97 }
98
99 /**
100  * aim_conn_kill - Close and free a connection.
101  * @sess: Session for the connection
102  * @deadconn: Connection to be freed
103  *
104  * Close, clear, and free a connection structure. Should never be
105  * called from within libfaim.
106  *
107  */
108 faim_export void aim_conn_kill(struct aim_session_t *sess, struct aim_conn_t **deadconn)
109 {
110   struct aim_conn_t *cur;
111
112   if (!deadconn || !*deadconn)  
113     return;
114
115   aim_tx_cleanqueue(sess, *deadconn);
116
117   faim_mutex_lock(&sess->connlistlock);
118   if (sess->connlist == NULL)
119     ;
120   else if (sess->connlist->next == NULL) {
121     if (sess->connlist == *deadconn)
122       sess->connlist = NULL;
123   } else {
124     cur = sess->connlist;
125     while (cur->next) {
126       if (cur->next == *deadconn) {
127         cur->next = cur->next->next;
128         break;
129       }
130       cur = cur->next;
131     }
132   }
133   faim_mutex_unlock(&sess->connlistlock);
134
135   /* XXX: do we need this for txqueue too? */
136   aim_rxqueue_cleanbyconn(sess, *deadconn);
137
138   if ((*deadconn)->fd != -1) 
139     aim_conn_close(*deadconn);
140   if ((*deadconn)->priv)
141     free((*deadconn)->priv);
142   free(*deadconn);
143   deadconn = NULL;
144
145   return;
146 }
147
148 /**
149  * aim_conn_close - Close a connection
150  * @deadconn: Connection to close
151  *
152  * Close (but not free) a connection.
153  *
154  * This leaves everything untouched except for clearing the 
155  * handler list and setting the fd to -1 (used to recognize
156  * dead connections).
157  *
158  */
159 faim_export void aim_conn_close(struct aim_conn_t *deadconn)
160 {
161
162   faim_mutex_destroy(&deadconn->active);
163   faim_mutex_destroy(&deadconn->seqnum_lock);
164   if (deadconn->fd >= 3)
165     close(deadconn->fd);
166   deadconn->fd = -1;
167   if (deadconn->handlerlist)
168     aim_clearhandlers(deadconn);
169
170   return;
171 }
172
173 /**
174  * aim_getconn_type - Find a connection of a specific type
175  * @sess: Session to search
176  * @type: Type of connection to look for
177  *
178  * Searches for a connection of the specified type in the 
179  * specified session.  Returns the first connection of that
180  * type found.
181  *
182  */
183 faim_export struct aim_conn_t *aim_getconn_type(struct aim_session_t *sess,
184                                                 int type)
185 {
186   struct aim_conn_t *cur;
187
188   faim_mutex_lock(&sess->connlistlock);
189   for (cur = sess->connlist; cur; cur = cur->next) {
190     if ((cur->type == type) && !(cur->status & AIM_CONN_STATUS_INPROGRESS))
191       break;
192   }
193   faim_mutex_unlock(&sess->connlistlock);
194
195   return cur;
196 }
197
198 faim_export struct aim_conn_t *aim_getconn_type_all(struct aim_session_t *sess,
199                                                     int type)
200 {
201   struct aim_conn_t *cur;
202
203   faim_mutex_lock(&sess->connlistlock);
204   for (cur = sess->connlist; cur; cur = cur->next) {
205     if (cur->type == type)
206       break;
207   }
208   faim_mutex_unlock(&sess->connlistlock);
209
210   return cur;
211 }
212
213 /* If you pass -1 for the fd, you'll get what you ask for.  Gibberish. */
214 faim_export struct aim_conn_t *aim_getconn_fd(struct aim_session_t *sess,
215                                               int fd)
216 {
217   struct aim_conn_t *cur;
218
219   faim_mutex_lock(&sess->connlistlock);
220   for (cur = sess->connlist; cur; cur = cur->next) {
221     if (cur->fd == fd)
222       break;
223   }
224   faim_mutex_unlock(&sess->connlistlock);
225
226   return cur;
227 }
228
229 /**
230  * aim_proxyconnect - An extrememly quick and dirty SOCKS5 interface. 
231  * @sess: Session to connect
232  * @host: Host to connect to
233  * @port: Port to connect to
234  * @statusret: Return value of the connection
235  *
236  * Attempts to connect to the specified host via the configured
237  * proxy settings, if present.  If no proxy is configured for
238  * this session, the connection is done directly.
239  *
240  */
241 static int aim_proxyconnect(struct aim_session_t *sess, 
242                             char *host, unsigned short port,
243                             int *statusret)
244 {
245   int fd = -1;
246
247   if (strlen(sess->socksproxy.server)) { /* connecting via proxy */
248     int i;
249     unsigned char buf[512];
250     struct sockaddr_in sa;
251     struct hostent *hp;
252     char *proxy;
253     unsigned short proxyport = 1080;
254
255     for(i=0;i<(int)strlen(sess->socksproxy.server);i++) {
256       if (sess->socksproxy.server[i] == ':') {
257         proxyport = atoi(&(sess->socksproxy.server[i+1]));
258         break;
259       }
260     }
261     proxy = (char *)malloc(i+1);
262     strncpy(proxy, sess->socksproxy.server, i);
263     proxy[i] = '\0';
264
265     if (!(hp = gethostbyname(proxy))) {
266       faimdprintf(sess, 0, "proxyconnect: unable to resolve proxy name\n");
267       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
268       return -1;
269     }
270     free(proxy);
271
272     memset(&sa.sin_zero, 0, 8);
273     sa.sin_port = htons(proxyport);
274     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
275     sa.sin_family = hp->h_addrtype;
276   
277     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
278     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
279       faimdprintf(sess, 0, "proxyconnect: unable to connect to proxy\n");
280       close(fd);
281       return -1;
282     }
283
284     i = 0;
285     buf[0] = 0x05; /* SOCKS version 5 */
286     if (strlen(sess->socksproxy.username)) {
287       buf[1] = 0x02; /* two methods */
288       buf[2] = 0x00; /* no authentication */
289       buf[3] = 0x02; /* username/password authentication */
290       i = 4;
291     } else {
292       buf[1] = 0x01;
293       buf[2] = 0x00;
294       i = 3;
295     }
296
297     if (write(fd, buf, i) < i) {
298       *statusret = errno;
299       close(fd);
300       return -1;
301     }
302
303     if (read(fd, buf, 2) < 2) {
304       *statusret = errno;
305       close(fd);
306       return -1;
307     }
308
309     if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
310       *statusret = EINVAL;
311       close(fd);
312       return -1;
313     }
314
315     /* check if we're doing username authentication */
316     if (buf[1] == 0x02) {
317       i  = aimutil_put8(buf, 0x01); /* version 1 */
318       i += aimutil_put8(buf+i, strlen(sess->socksproxy.username));
319       i += aimutil_putstr(buf+i, sess->socksproxy.username, strlen(sess->socksproxy.username));
320       i += aimutil_put8(buf+i, strlen(sess->socksproxy.password));
321       i += aimutil_putstr(buf+i, sess->socksproxy.password, strlen(sess->socksproxy.password));
322       if (write(fd, buf, i) < i) {
323         *statusret = errno;
324         close(fd);
325         return -1;
326       }
327       if (read(fd, buf, 2) < 2) {
328         *statusret = errno;
329         close(fd);
330         return -1;
331       }
332       if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
333         *statusret = EINVAL;
334         close(fd);
335         return -1;
336       }
337     }
338
339     i  = aimutil_put8(buf, 0x05);
340     i += aimutil_put8(buf+i, 0x01); /* CONNECT */
341     i += aimutil_put8(buf+i, 0x00); /* reserved */
342     i += aimutil_put8(buf+i, 0x03); /* address type: host name */
343     i += aimutil_put8(buf+i, strlen(host));
344     i += aimutil_putstr(buf+i, host, strlen(host));
345     i += aimutil_put16(buf+i, port);
346
347     if (write(fd, buf, i) < i) {
348       *statusret = errno;
349       close(fd);
350       return -1;
351     }
352     if (read(fd, buf, 10) < 10) {
353       *statusret = errno;
354       close(fd);
355       return -1;
356     }
357     if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
358       *statusret = EINVAL;
359       close(fd);
360       return -1;
361     }
362
363   } else { /* connecting directly */
364     struct sockaddr_in sa;
365     struct hostent *hp;
366
367     if (!(hp = gethostbyname(host))) {
368       *statusret = (h_errno | AIM_CONN_STATUS_RESOLVERR);
369       return -1;
370     }
371
372     memset(&sa, 0, sizeof(struct sockaddr_in));
373     sa.sin_port = htons(port);
374     memcpy(&sa.sin_addr, hp->h_addr, hp->h_length);
375     sa.sin_family = hp->h_addrtype;
376   
377     fd = socket(hp->h_addrtype, SOCK_STREAM, 0);
378
379     if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT)
380       fcntl(fd, F_SETFL, O_NONBLOCK); /* XXX save flags */
381
382     if (connect(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0) {
383       if (sess->flags & AIM_SESS_FLAGS_NONBLOCKCONNECT) {
384         if ((errno == EINPROGRESS) || (errno == EINTR)) {
385           if (statusret)
386             *statusret |= AIM_CONN_STATUS_INPROGRESS;
387           return fd;
388         }
389       }
390       close(fd);
391       fd = -1;
392     }
393   }
394   return fd;
395 }
396
397 /**
398  * aim_cloneconn - clone an aim_conn_t
399  * @sess: session containing parent
400  * @src: connection to clone
401  *
402  * A new connection is allocated, and the values are filled in
403  * appropriately. Note that this function sets the new connnection's
404  * ->priv pointer to be equal to that of its parent: only the pointer
405  * is copied, not the data it points to.
406  *
407  * This function returns a pointer to the new aim_conn_t, or %NULL on
408  * error
409  */
410 faim_internal struct aim_conn_t *aim_cloneconn(struct aim_session_t *sess,
411                                                struct aim_conn_t *src)
412 {
413   struct aim_conn_t *conn;
414     struct aim_rxcblist_t *cur;
415
416   if (!(conn = aim_conn_getnext(sess)))
417     return NULL;
418
419   faim_mutex_lock(&conn->active);
420
421   conn->fd = src->fd;
422   conn->type = src->type;
423   conn->subtype = src->subtype;
424   conn->seqnum = src->seqnum;
425   conn->priv = src->priv;
426   conn->lastactivity = src->lastactivity;
427   conn->forcedlatency = src->forcedlatency;
428   conn->sessv = src->sessv;
429
430   /* clone handler list */
431   for (cur = src->handlerlist; cur; cur = cur->next) {
432     aim_conn_addhandler(sess, conn, cur->family, cur->type, 
433                         cur->handler, cur->flags);
434   }
435
436   faim_mutex_unlock(&conn->active);
437
438   return conn;
439 }
440
441 /**
442  * aim_newconn - Open a new connection
443  * @sess: Session to create connection in
444  * @type: Type of connection to create
445  * @dest: Host to connect to (in "host:port" syntax)
446  *
447  * Opens a new connection to the specified dest host of specified
448  * type, using the proxy settings if available.  If @host is %NULL,
449  * the connection is allocated and returned, but no connection 
450  * is made.
451  *
452  * FIXME: Return errors in a more sane way.
453  *
454  */
455 faim_export struct aim_conn_t *aim_newconn(struct aim_session_t *sess,
456                                            int type, char *dest)
457 {
458   struct aim_conn_t *connstruct;
459   int ret;
460   u_short port = FAIM_LOGIN_PORT;
461   char *host = NULL;
462   int i=0;
463
464   if ((connstruct=aim_conn_getnext(sess))==NULL)
465     return NULL;
466
467   faim_mutex_lock(&connstruct->active);
468
469   connstruct->sessv = (void *)sess;
470   connstruct->type = type;
471
472   if (!dest) { /* just allocate a struct */
473     connstruct->fd = -1;
474     connstruct->status = 0;
475     faim_mutex_unlock(&connstruct->active);
476     return connstruct;
477   }
478
479   /* 
480    * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
481    * colon, in the BOS redirect.  This fatally breaks all previous 
482    * libfaims.  Bad, bad AOL.
483    *
484    * We put this here to catch every case. 
485    *
486    */
487
488   for(i=0;i<(int)strlen(dest);i++) {
489     if (dest[i] == ':') {
490       port = atoi(&(dest[i+1]));
491       break;
492     }
493   }
494   host = (char *)malloc(i+1);
495   strncpy(host, dest, i);
496   host[i] = '\0';
497
498   if ((ret = aim_proxyconnect(sess, host, port, &connstruct->status)) < 0) {
499     connstruct->fd = -1;
500     connstruct->status = (errno | AIM_CONN_STATUS_CONNERR);
501     free(host);
502     faim_mutex_unlock(&connstruct->active);
503     return connstruct;
504   } else
505     connstruct->fd = ret;
506   
507   faim_mutex_unlock(&connstruct->active);
508
509   free(host);
510
511   return connstruct;
512 }
513
514 /**
515  * aim_conngetmaxfd - Return the highest valued file discriptor in session
516  * @sess: Session to search
517  *
518  * Returns the highest valued filed descriptor of all open 
519  * connections in @sess.
520  *
521  */
522 faim_export int aim_conngetmaxfd(struct aim_session_t *sess)
523 {
524   int j = 0;
525   struct aim_conn_t *cur;
526
527   faim_mutex_lock(&sess->connlistlock);
528   for (cur = sess->connlist; cur; cur = cur->next) {
529     if (cur->fd > j)
530       j = cur->fd;
531   }
532   faim_mutex_unlock(&sess->connlistlock);
533
534   return j;
535 }
536
537 /**
538  * aim_conn_in_sess - Predicate to test the precense of a connection in a sess
539  * @sess: Session to look in
540  * @conn: Connection to look for
541  *
542  * Searches @sess for the passed connection.  Returns 1 if its present,
543  * zero otherwise.
544  *
545  */
546 faim_export int aim_conn_in_sess(struct aim_session_t *sess, struct aim_conn_t *conn)
547 {
548   struct aim_conn_t *cur;
549
550   faim_mutex_lock(&sess->connlistlock);
551   for(cur = sess->connlist; cur; cur = cur->next)
552     if(cur == conn) {
553       faim_mutex_unlock(&sess->connlistlock);
554       return 1;
555     }
556   faim_mutex_unlock(&sess->connlistlock);
557   return 0;
558 }
559
560 /**
561  * aim_select - Wait for a socket with data or timeout
562  * @sess: Session to wait on
563  * @timeout: How long to wait
564  * @status: Return status
565  *
566  * Waits for a socket with data or for timeout, whichever comes first.
567  * See select(2).
568  * 
569  * Return codes in *status:
570  *   -1  error in select() (%NULL returned)
571  *    0  no events pending (%NULL returned)
572  *    1  outgoing data pending (%NULL returned)
573  *    2  incoming data pending (connection with pending data returned)
574  *
575  * XXX: we could probably stand to do a little courser locking here.
576  *
577  */ 
578 faim_export struct aim_conn_t *aim_select(struct aim_session_t *sess,
579                                           struct timeval *timeout, 
580                                           int *status)
581 {
582   struct aim_conn_t *cur;
583   fd_set fds, wfds;
584   int maxfd = 0;
585   int i, haveconnecting = 0;
586
587   faim_mutex_lock(&sess->connlistlock);
588   if (sess->connlist == NULL) {
589     faim_mutex_unlock(&sess->connlistlock);
590     *status = -1;
591     return NULL;
592   }
593   faim_mutex_unlock(&sess->connlistlock);
594
595   FD_ZERO(&fds);
596   FD_ZERO(&wfds);
597   maxfd = 0;
598
599   faim_mutex_lock(&sess->connlistlock);
600   for (cur = sess->connlist; cur; cur = cur->next) {
601     if (cur->fd == -1) {
602       /* don't let invalid/dead connections sit around */
603       *status = 2;
604       faim_mutex_unlock(&sess->connlistlock);
605       return cur;
606     } else if (cur->status & AIM_CONN_STATUS_INPROGRESS) {
607       FD_SET(cur->fd, &wfds);
608       
609       haveconnecting++;
610     }
611     FD_SET(cur->fd, &fds);
612     if (cur->fd > maxfd)
613       maxfd = cur->fd;
614   }
615   faim_mutex_unlock(&sess->connlistlock);
616
617   /* 
618    * If we have data waiting to be sent, return
619    *
620    * We have to not do this if theres at least one
621    * connection thats still connecting, since that connection
622    * may have queued data and this return would prevent
623    * the connection from ever completing!  This is a major
624    * inadequacy of the libfaim way of doing things.  It means
625    * that nothing can transmit as long as there's connecting
626    * sockets. Evil.
627    *
628    * But its still better than having blocking connects.
629    *
630    */
631   if (!haveconnecting && (sess->queue_outgoing != NULL)) {
632     *status = 1;
633     return NULL;
634   } 
635
636   if ((i = select(maxfd+1, &fds, &wfds, NULL, timeout))>=1) {
637     faim_mutex_lock(&sess->connlistlock);
638     for (cur = sess->connlist; cur; cur = cur->next) {
639       if ((FD_ISSET(cur->fd, &fds)) || 
640           ((cur->status & AIM_CONN_STATUS_INPROGRESS) && 
641            FD_ISSET(cur->fd, &wfds))) {
642         *status = 2;
643         faim_mutex_unlock(&sess->connlistlock);
644         return cur; /* XXX race condition here -- shouldnt unlock connlist */
645       }
646     }
647     *status = 0; /* shouldn't happen */
648   } else if ((i == -1) && (errno == EINTR)) /* treat interrupts as a timeout */
649     *status = 0;
650   else
651     *status = i; /* can be 0 or -1 */
652
653   faim_mutex_unlock(&sess->connlistlock);
654
655   return NULL;  /* no waiting or error, return */
656 }
657
658 /**
659  * aim_conn_isready - Test if a connection is marked ready
660  * @conn: Connection to test
661  *
662  * Returns true if the connection is ready, false otherwise.
663  * Returns -1 if the connection is invalid.
664  *
665  * XXX: This is deprecated.
666  *
667  */
668 faim_export int aim_conn_isready(struct aim_conn_t *conn)
669 {
670   if (conn)
671     return (conn->status & 0x0001);
672   return -1;
673 }
674
675 /**
676  * aim_conn_setstatus - Set the status of a connection
677  * @conn: Connection
678  * @status: New status
679  *
680  * @newstatus is %XOR'd with the previous value of the connection
681  * status and returned.  Returns -1 if the connection is invalid.
682  *
683  * This isn't real useful.
684  *
685  */
686 faim_export int aim_conn_setstatus(struct aim_conn_t *conn, int status)
687 {
688   int val;
689
690   if (!conn)
691     return -1;
692   
693   faim_mutex_lock(&conn->active);
694   val = conn->status ^= status;
695   faim_mutex_unlock(&conn->active);
696
697   return val;
698 }
699
700 /**
701  * aim_conn_setlatency - Set a forced latency value for connection
702  * @conn: Conn to set latency for
703  * @newval: Number of seconds to force between transmits
704  *
705  * Causes @newval seconds to be spent between transmits on a connection.
706  *
707  * This is my lame attempt at overcoming not understanding the rate
708  * limiting. 
709  *
710  * XXX: This should really be replaced with something that scales and
711  * backs off like the real rate limiting does.
712  *
713  */
714 faim_export int aim_conn_setlatency(struct aim_conn_t *conn, int newval)
715 {
716   if (!conn)
717     return -1;
718
719   faim_mutex_lock(&conn->active);
720   conn->forcedlatency = newval;
721   conn->lastactivity = 0; /* reset this just to make sure */
722   faim_mutex_unlock(&conn->active);
723
724   return 0;
725 }
726
727 /**
728  * aim_setupproxy - Configure a proxy for this session
729  * @sess: Session to set proxy for
730  * @server: SOCKS server
731  * @username: SOCKS username
732  * @password: SOCKS password
733  *
734  * Call this with your SOCKS5 proxy server parameters before
735  * the first call to aim_newconn().  If called with all %NULL
736  * args, it will clear out a previously set proxy.  
737  *
738  * Set username and password to %NULL if not applicable.
739  *
740  */
741 faim_export void aim_setupproxy(struct aim_session_t *sess, char *server, char *username, char *password)
742 {
743   /* clear out the proxy info */
744   if (!server || !strlen(server)) {
745     memset(sess->socksproxy.server, 0, sizeof(sess->socksproxy.server));
746     memset(sess->socksproxy.username, 0, sizeof(sess->socksproxy.username));
747     memset(sess->socksproxy.password, 0, sizeof(sess->socksproxy.password));
748     return;
749   }
750
751   strncpy(sess->socksproxy.server, server, sizeof(sess->socksproxy.server));
752   if (username && strlen(username)) 
753     strncpy(sess->socksproxy.username, username, sizeof(sess->socksproxy.username));
754   if (password && strlen(password))
755     strncpy(sess->socksproxy.password, password, sizeof(sess->socksproxy.password));
756   return;
757 }
758
759 static void defaultdebugcb(struct aim_session_t *sess, int level, const char *format, va_list va)
760 {
761   vfprintf(stderr, format, va);
762 }
763
764 /**
765  * aim_session_init - Initializes a session structure
766  * @sess: Session to initialize
767  * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
768  * @debuglevel: Level of debugging output (zero is least)
769  *
770  * Sets up the initial values for a session.
771  *
772  */
773 faim_export void aim_session_init(struct aim_session_t *sess, unsigned long flags, int debuglevel)
774 {
775   if (!sess)
776     return;
777
778   memset(sess, 0, sizeof(struct aim_session_t));
779   aim_connrst(sess);
780   sess->queue_outgoing = NULL;
781   sess->queue_incoming = NULL;
782   sess->pendingjoin = NULL;
783   sess->pendingjoinexchange = 0;
784   aim_initsnachash(sess);
785   sess->msgcookies = NULL;
786   sess->snac_nextid = 0x00000001;
787
788   sess->flags = 0;
789   sess->debug = debuglevel;
790   sess->debugcb = defaultdebugcb;
791
792   sess->modlistv = NULL;
793
794   /*
795    * Default to SNAC login unless XORLOGIN is explicitly set.
796    */
797   if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
798     sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
799   sess->flags |= flags;
800
801   /*
802    * This must always be set.  Default to the queue-based
803    * version for back-compatibility.  
804    */
805   aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
806
807
808   /*
809    * Register all the modules for this session...
810    */
811   aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
812   aim__registermodule(sess, buddylist_modfirst);
813   aim__registermodule(sess, admin_modfirst);
814   aim__registermodule(sess, bos_modfirst);
815   aim__registermodule(sess, search_modfirst);
816   aim__registermodule(sess, stats_modfirst);
817   aim__registermodule(sess, auth_modfirst);
818   aim__registermodule(sess, msg_modfirst);
819   aim__registermodule(sess, chatnav_modfirst);
820   aim__registermodule(sess, chat_modfirst);
821   aim__registermodule(sess, locate_modfirst);
822   aim__registermodule(sess, general_modfirst);
823
824   return;
825 }
826
827 /**
828  * aim_session_kill - Deallocate a session
829  * @sess: Session to kill
830  *
831  *
832  */
833 faim_export void aim_session_kill(struct aim_session_t *sess)
834 {
835
836   aim_logoff(sess);
837
838   aim__shutdownmodules(sess);
839
840   return;
841 }
842
843 /**
844  * aim_setdebuggingcb - Set the function to call when outputting debugging info
845  * @sess: Session to change
846  * @cb: Function to call
847  *
848  * The function specified is called whenever faimdprintf() is used within
849  * libfaim, and the session's debugging level is greater tha nor equal to
850  * the value faimdprintf was called with.
851  *
852  */
853 faim_export int aim_setdebuggingcb(struct aim_session_t *sess, faim_debugging_callback_t cb)
854 {
855
856   if (!sess)
857     return -1;
858
859   sess->debugcb = cb;
860
861   return 0;
862 }
863
864 /**
865  * aim_conn_isconnecting - Determine if a connection is connecting
866  * @conn: Connection to examine
867  *
868  * Returns nonzero if the connection is in the process of
869  * connecting (or if it just completed and aim_conn_completeconnect()
870  * has yet to be called on it).
871  *
872  */
873 faim_export int aim_conn_isconnecting(struct aim_conn_t *conn)
874 {
875   if (!conn)
876     return 0;
877   return (conn->status & AIM_CONN_STATUS_INPROGRESS)?1:0;
878 }
879
880 faim_export int aim_conn_completeconnect(struct aim_session_t *sess, struct aim_conn_t *conn)
881 {
882   fd_set fds, wfds;
883   struct timeval tv;
884   int res, error = ETIMEDOUT;
885   aim_rxcallback_t userfunc;
886
887   if (!conn || (conn->fd == -1))
888     return -1;
889
890   if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
891     return -1;
892
893   FD_ZERO(&fds);
894   FD_SET(conn->fd, &fds);
895   FD_ZERO(&wfds);
896   FD_SET(conn->fd, &wfds);
897   tv.tv_sec = 0;
898   tv.tv_usec = 0;
899
900   if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
901     error = errno;
902     aim_conn_close(conn);
903     errno = error;
904     return -1;
905   } else if (res == 0) {
906     faimdprintf(sess, 0, "aim_conn_completeconnect: false alarm on %d\n", conn->fd);
907     return 0; /* hasn't really completed yet... */
908   } 
909
910   if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
911     int len = sizeof(error);
912
913     if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
914       error = errno;
915   }
916
917   if (error) {
918     aim_conn_close(conn);
919     errno = error;
920     return -1;
921   }
922
923   fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
924
925   conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
926
927   if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
928     userfunc(sess, NULL, conn);
929
930   /* Flush out the queues if there was something waiting for this conn  */
931   aim_tx_flushqueue(sess);
932
933   return 0;
934 }
935
936 faim_export struct aim_session_t *aim_conn_getsess(struct aim_conn_t *conn)
937 {
938
939   if (!conn)
940     return NULL;
941
942   return (struct aim_session_t *)conn->sessv;
943 }
944
945 /*
946  * aim_logoff()
947  *
948  * Closes -ALL- open connections.
949  *
950  */
951 faim_export int aim_logoff(struct aim_session_t *sess)
952 {
953
954   aim_connrst(sess);  /* in case we want to connect again */
955
956   return 0;
957
958 }
959
This page took 0.114518 seconds and 5 git commands to generate.