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