source: protocols/oscar/conn.c @ b7d3cc34

0.99
Last change on this file since b7d3cc34 was b7d3cc34, checked in by Wilmer van der Gaast <wilmer@…>, at 2005-11-06T18:23:18Z

Initial repository (0.99 release tree)

  • Property mode set to 100644
File size: 15.8 KB
Line 
1
2/*
3 * conn.c
4 *
5 * Does all this gloriously nifty connection handling stuff...
6 *
7 */
8
9#include <aim.h> 
10#include "sock.h"
11
12static int aim_logoff(aim_session_t *sess);
13
14/*
15 * In OSCAR, every connection has a set of SNAC groups associated
16 * with it.  These are the groups that you can send over this connection
17 * without being guarenteed a "Not supported" SNAC error. 
18 *
19 * The grand theory of things says that these associations transcend
20 * what libfaim calls "connection types" (conn->type).  You can probably
21 * see the elegance here, but since I want to revel in it for a bit, you
22 * get to hear it all spelled out.
23 *
24 * So let us say that you have your core BOS connection running.  One
25 * of your modules has just given you a SNAC of the group 0x0004 to send
26 * you.  Maybe an IM destined for some twit in Greenland.  So you start
27 * at the top of your connection list, looking for a connection that
28 * claims to support group 0x0004.  You find one.  Why, that neat BOS
29 * connection of yours can do that.  So you send it on its way.
30 *
31 * Now, say, that fellow from Greenland has friends and they all want to
32 * meet up with you in a lame chat room.  This has landed you a SNAC
33 * in the family 0x000e and you have to admit you're a bit lost.  You've
34 * searched your connection list for someone who wants to make your life
35 * easy and deliver this SNAC for you, but there isn't one there.
36 *
37 * Here comes the good bit.  Without even letting anyone know, particularly
38 * the module that decided to send this SNAC, and definitly not that twit
39 * in Greenland, you send out a service request.  In this request, you have
40 * marked the need for a connection supporting group 0x000e.  A few seconds
41 * later, you receive a service redirect with an IP address and a cookie in
42 * it.  Great, you say.  Now I have something to do.  Off you go, making
43 * that connection.  One of the first things you get from this new server
44 * is a message saying that indeed it does support the group you were looking
45 * for.  So you continue and send rate confirmation and all that. 
46 *
47 * Then you remember you had that SNAC to send, and now you have a means to
48 * do it, and you do, and everyone is happy.  Except the Greenlander, who is
49 * still stuck in the bitter cold.
50 *
51 * Oh, and this is useful for building the Migration SNACs, too.  In the
52 * future, this may help convince me to implement rate limit mitigation
53 * for real.  We'll see.
54 *
55 * Just to make me look better, I'll say that I've known about this great
56 * scheme for quite some time now.  But I still haven't convinced myself
57 * to make libfaim work that way.  It would take a fair amount of effort,
58 * and probably some client API changes as well.  (Whenever I don't want
59 * to do something, I just say it would change the client API.  Then I
60 * instantly have a couple of supporters of not doing it.)
61 *
62 * Generally, addgroup is only called by the internal handling of the
63 * server ready SNAC.  So if you want to do something before that, you'll
64 * have to be more creative.  That is done rather early, though, so I don't
65 * think you have to worry about it.  Unless you're me.  I care deeply
66 * about such inane things.
67 *
68 */
69void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
70{
71        aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
72        struct snacgroup *sg;
73
74        if (!(sg = g_malloc(sizeof(struct snacgroup))))
75                return;
76
77        sg->group = group;
78
79        sg->next = ins->groups;
80        ins->groups = sg;
81
82        return;
83}
84
85aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
86{
87        aim_conn_t *cur;
88
89        for (cur = sess->connlist; cur; cur = cur->next) {
90                aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside;
91                struct snacgroup *sg;
92
93                for (sg = ins->groups; sg; sg = sg->next) {
94                        if (sg->group == group)
95                                return cur;
96                }
97        }
98
99        return NULL;
100}
101
102static void connkill_snacgroups(struct snacgroup **head)
103{
104        struct snacgroup *sg;
105
106        for (sg = *head; sg; ) {
107                struct snacgroup *tmp;
108
109                tmp = sg->next;
110                g_free(sg);
111                sg = tmp;
112        }
113
114        *head = NULL;
115
116        return;
117}
118
119static void connkill_rates(struct rateclass **head)
120{
121        struct rateclass *rc;
122
123        for (rc = *head; rc; ) {
124                struct rateclass *tmp;
125                struct snacpair *sp;
126
127                tmp = rc->next;
128
129                for (sp = rc->members; sp; ) {
130                        struct snacpair *tmpsp;
131
132                        tmpsp = sp->next;
133                        g_free(sp);
134                        sp = tmpsp;
135                }
136                g_free(rc);
137
138                rc = tmp;
139        }
140
141        *head = NULL;
142
143        return;
144}
145
146static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
147{
148
149        aim_rxqueue_cleanbyconn(sess, *deadconn);
150        aim_tx_cleanqueue(sess, *deadconn);
151
152        if ((*deadconn)->fd != -1) 
153                aim_conn_close(*deadconn);
154
155        /*
156         * XXX ->priv should never be touched by the library. I know
157         * it used to be, but I'm getting rid of all that.  Use
158         * ->internal instead.
159         */
160        if ((*deadconn)->priv)
161                g_free((*deadconn)->priv);
162
163        /*
164         * This will free ->internal if it necessary...
165         */
166        if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS)
167                aim_conn_kill_rend(sess, *deadconn);
168        else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
169                aim_conn_kill_chat(sess, *deadconn);
170
171        if ((*deadconn)->inside) {
172                aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;
173
174                connkill_snacgroups(&inside->groups);
175                connkill_rates(&inside->rates);
176
177                g_free(inside);
178        }
179
180        g_free(*deadconn);
181        *deadconn = NULL;
182
183        return;
184}
185
186/**
187 * aim_connrst - Clears out connection list, killing remaining connections.
188 * @sess: Session to be cleared
189 *
190 * Clears out the connection list and kills any connections left.
191 *
192 */
193static void aim_connrst(aim_session_t *sess)
194{
195
196        if (sess->connlist) {
197                aim_conn_t *cur = sess->connlist, *tmp;
198
199                while (cur) {
200                        tmp = cur->next;
201                        aim_conn_close(cur);
202                        connkill_real(sess, &cur);
203                        cur = tmp;
204                }
205        }
206
207        sess->connlist = NULL;
208
209        return;
210}
211
212/**
213 * aim_conn_init - Reset a connection to default values.
214 * @deadconn: Connection to be reset
215 *
216 * Initializes and/or resets a connection structure.
217 *
218 */
219static void aim_conn_init(aim_conn_t *deadconn)
220{
221
222        if (!deadconn)
223                return;
224
225        deadconn->fd = -1;
226        deadconn->subtype = -1;
227        deadconn->type = -1;
228        deadconn->seqnum = 0;
229        deadconn->lastactivity = 0;
230        deadconn->forcedlatency = 0;
231        deadconn->handlerlist = NULL;
232        deadconn->priv = NULL;
233        memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));
234
235        return;
236}
237
238/**
239 * aim_conn_getnext - Gets a new connection structure.
240 * @sess: Session
241 *
242 * Allocate a new empty connection structure.
243 *
244 */
245static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
246{
247        aim_conn_t *newconn;
248
249        if (!(newconn = g_new0(aim_conn_t,1))) 
250                return NULL;
251
252        if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) {
253                g_free(newconn);
254                return NULL;
255        }
256
257        aim_conn_init(newconn);
258
259        newconn->next = sess->connlist;
260        sess->connlist = newconn;
261
262        return newconn;
263}
264
265/**
266 * aim_conn_kill - Close and free a connection.
267 * @sess: Session for the connection
268 * @deadconn: Connection to be freed
269 *
270 * Close, clear, and free a connection structure. Should never be
271 * called from within libfaim.
272 *
273 */
274void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
275{
276        aim_conn_t *cur, **prev;
277
278        if (!deadconn || !*deadconn)   
279                return;
280
281        for (prev = &sess->connlist; (cur = *prev); ) {
282                if (cur == *deadconn) {
283                        *prev = cur->next;
284                        break;
285                }
286                prev = &cur->next;
287        }
288
289        if (!cur)
290                return; /* oops */
291
292        connkill_real(sess, &cur);
293
294        return;
295}
296
297/**
298 * aim_conn_close - Close a connection
299 * @deadconn: Connection to close
300 *
301 * Close (but not free) a connection.
302 *
303 * This leaves everything untouched except for clearing the
304 * handler list and setting the fd to -1 (used to recognize
305 * dead connections).  It will also remove cookies if necessary.
306 *
307 */
308void aim_conn_close(aim_conn_t *deadconn)
309{
310
311        if (deadconn->fd >= 3)
312                closesocket(deadconn->fd);
313        deadconn->fd = -1;
314        if (deadconn->handlerlist)
315                aim_clearhandlers(deadconn);
316        if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS)
317                aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn);
318
319        return;
320}
321
322/**
323 * aim_getconn_type - Find a connection of a specific type
324 * @sess: Session to search
325 * @type: Type of connection to look for
326 *
327 * Searches for a connection of the specified type in the
328 * specified session.  Returns the first connection of that
329 * type found.
330 *
331 * XXX except for RENDEZVOUS, all uses of this should be removed and
332 * use aim_conn_findbygroup() instead.
333 */
334aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
335{
336        aim_conn_t *cur;
337
338        for (cur = sess->connlist; cur; cur = cur->next) {
339                if ((cur->type == type) && 
340                                !(cur->status & AIM_CONN_STATUS_INPROGRESS))
341                        break;
342        }
343
344        return cur;
345}
346
347aim_conn_t *aim_getconn_type_all(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                        break;
354        }
355
356        return cur;
357}
358
359/**
360 * aim_cloneconn - clone an aim_conn_t
361 * @sess: session containing parent
362 * @src: connection to clone
363 *
364 * A new connection is allocated, and the values are filled in
365 * appropriately. Note that this function sets the new connnection's
366 * ->priv pointer to be equal to that of its parent: only the pointer
367 * is copied, not the data it points to.
368 *
369 * This function returns a pointer to the new aim_conn_t, or %NULL on
370 * error
371 */
372aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
373{
374        aim_conn_t *conn;
375
376        if (!(conn = aim_conn_getnext(sess)))
377                return NULL;
378
379        conn->fd = src->fd;
380        conn->type = src->type;
381        conn->subtype = src->subtype;
382        conn->seqnum = src->seqnum;
383        conn->priv = src->priv;
384        conn->internal = src->internal;
385        conn->lastactivity = src->lastactivity;
386        conn->forcedlatency = src->forcedlatency;
387        conn->sessv = src->sessv;
388        aim_clonehandlers(sess, conn, src);
389
390        if (src->inside) {
391                /*
392                 * XXX should clone this section as well, but since currently
393                 * this function only gets called for some of that rendezvous
394                 * crap, and not on SNAC connections, its probably okay for
395                 * now.
396                 *
397                 */
398        }
399
400        return conn;
401}
402
403/**
404 * aim_newconn - Open a new connection
405 * @sess: Session to create connection in
406 * @type: Type of connection to create
407 * @dest: Host to connect to (in "host:port" syntax)
408 *
409 * Opens a new connection to the specified dest host of specified
410 * type, using the proxy settings if available.  If @host is %NULL,
411 * the connection is allocated and returned, but no connection
412 * is made.
413 *
414 * FIXME: Return errors in a more sane way.
415 *
416 */
417aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
418{
419        aim_conn_t *connstruct;
420        guint16 port = AIM_LOGIN_PORT;
421        char *host;
422        int i;
423
424        if (!(connstruct = aim_conn_getnext(sess)))
425                return NULL;
426
427        connstruct->sessv = (void *)sess;
428        connstruct->type = type;
429
430        if (!dest) { /* just allocate a struct */
431                connstruct->fd = -1;
432                connstruct->status = 0;
433                return connstruct;
434        }
435
436        /*
437         * As of 23 Jul 1999, AOL now sends the port number, preceded by a
438         * colon, in the BOS redirect.  This fatally breaks all previous
439         * libfaims.  Bad, bad AOL.
440         *
441         * We put this here to catch every case.
442         *
443         */
444
445        for(i = 0; i < (int)strlen(dest); i++) {
446                if (dest[i] == ':') {
447                        port = atoi(&(dest[i+1]));
448                        break;
449                }
450        }
451
452        host = (char *)g_malloc(i+1);
453        strncpy(host, dest, i);
454        host[i] = '\0';
455
456        connstruct->fd = proxy_connect(host, port, NULL, NULL);
457
458        g_free(host);
459
460        return connstruct;
461}
462
463/**
464 * aim_conn_setlatency - Set a forced latency value for connection
465 * @conn: Conn to set latency for
466 * @newval: Number of seconds to force between transmits
467 *
468 * Causes @newval seconds to be spent between transmits on a connection.
469 *
470 * This is my lame attempt at overcoming not understanding the rate
471 * limiting.
472 *
473 * XXX: This should really be replaced with something that scales and
474 * backs off like the real rate limiting does.
475 *
476 */
477int aim_conn_setlatency(aim_conn_t *conn, int newval)
478{
479
480        if (!conn)
481                return -1;
482
483        conn->forcedlatency = newval;
484        conn->lastactivity = 0; /* reset this just to make sure */
485
486        return 0;
487}
488
489/**
490 * aim_session_init - Initializes a session structure
491 * @sess: Session to initialize
492 * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
493 * @debuglevel: Level of debugging output (zero is least)
494 *
495 * Sets up the initial values for a session.
496 *
497 */
498void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
499{
500
501        if (!sess)
502                return;
503
504        memset(sess, 0, sizeof(aim_session_t));
505        aim_connrst(sess);
506        sess->queue_outgoing = NULL;
507        sess->queue_incoming = NULL;
508        aim_initsnachash(sess);
509        sess->msgcookies = NULL;
510        sess->snacid_next = 0x00000001;
511
512        sess->flags = 0;
513
514        sess->modlistv = NULL;
515
516        sess->ssi.received_data = 0;
517        sess->ssi.waiting_for_ack = 0;
518        sess->ssi.holding_queue = NULL;
519        sess->ssi.revision = 0;
520        sess->ssi.items = NULL;
521        sess->ssi.timestamp = (time_t)0;
522
523        sess->locate.userinfo = NULL;
524        sess->locate.torequest = NULL;
525        sess->locate.requested = NULL;
526        sess->locate.waiting_for_response = FALSE;
527
528        sess->icq_info = NULL;
529        sess->authinfo = NULL;
530        sess->emailinfo = NULL;
531        sess->oft_info = NULL;
532
533
534        /*
535         * Default to SNAC login unless XORLOGIN is explicitly set.
536         */
537        if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
538                sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
539        sess->flags |= flags;
540
541        /*
542         * This must always be set.  Default to the queue-based
543         * version for back-compatibility. 
544         */
545        aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);
546
547
548        /*
549         * Register all the modules for this session...
550         */
551        aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
552        aim__registermodule(sess, general_modfirst);
553        aim__registermodule(sess, locate_modfirst);
554        aim__registermodule(sess, buddylist_modfirst);
555        aim__registermodule(sess, msg_modfirst);
556        aim__registermodule(sess, admin_modfirst);
557        aim__registermodule(sess, bos_modfirst);
558        aim__registermodule(sess, search_modfirst);
559        aim__registermodule(sess, stats_modfirst);
560        aim__registermodule(sess, chatnav_modfirst);
561        aim__registermodule(sess, chat_modfirst);
562        /* missing 0x0f - 0x12 */
563        aim__registermodule(sess, ssi_modfirst);
564        /* missing 0x14 */
565        aim__registermodule(sess, icq_modfirst);
566        /* missing 0x16 */
567        aim__registermodule(sess, auth_modfirst);
568
569        return;
570}
571
572/**
573 * aim_session_kill - Deallocate a session
574 * @sess: Session to kill
575 *
576 */
577void aim_session_kill(aim_session_t *sess)
578{
579        aim_cleansnacs(sess, -1);
580
581        aim_logoff(sess);
582
583        aim__shutdownmodules(sess);
584
585        return;
586}
587
588/*
589 * XXX this is nearly as ugly as proxyconnect().
590 */
591int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
592{
593        fd_set fds, wfds;
594        struct timeval tv;
595        int res, error = ETIMEDOUT;
596        aim_rxcallback_t userfunc;
597
598        if (!conn || (conn->fd == -1))
599                return -1;
600
601        if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
602                return -1;
603
604        FD_ZERO(&fds);
605        FD_SET(conn->fd, &fds);
606        FD_ZERO(&wfds);
607        FD_SET(conn->fd, &wfds);
608        tv.tv_sec = 0;
609        tv.tv_usec = 0;
610
611        if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
612                error = errno;
613                aim_conn_close(conn);
614                errno = error;
615                return -1;
616        } else if (res == 0) {
617                return 0; /* hasn't really completed yet... */
618        } 
619
620        if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
621                unsigned int len = sizeof(error);
622
623                if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
624                        error = errno;
625        }
626
627        if (error) {
628                aim_conn_close(conn);
629                errno = error;
630                return -1;
631        }
632
633#ifndef _WIN32
634        fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */
635#endif
636
637        conn->status &= ~AIM_CONN_STATUS_INPROGRESS;
638
639        if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
640                userfunc(sess, NULL, conn);
641
642        /* Flush out the queues if there was something waiting for this conn  */
643        aim_tx_flushqueue(sess);
644
645        return 0;
646}
647
648aim_session_t *aim_conn_getsess(aim_conn_t *conn)
649{
650
651        if (!conn)
652                return NULL;
653
654        return (aim_session_t *)conn->sessv;
655}
656
657/*
658 * aim_logoff()
659 *
660 * Closes -ALL- open connections.
661 *
662 */
663static int aim_logoff(aim_session_t *sess)
664{
665
666        aim_connrst(sess);  /* in case we want to connect again */
667
668        return 0;
669
670}
671
672/*
673 * aim_flap_nop()
674 *
675 * No-op.  WinAIM 4.x sends these _every minute_ to keep
676 * the connection alive. 
677 */
678int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
679{
680        aim_frame_t *fr;
681
682        if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
683                return -ENOMEM;
684
685        aim_tx_enqueue(sess, fr);
686
687        return 0;
688}
689
690
Note: See TracBrowser for help on using the repository browser.