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