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