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