source: libfaim/conn.c @ f36222f

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