aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/oscar/conn.c
blob: a178761e78c215d1a2c30363a4f697596711c9e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#!/usr/bin/env perl

use strict;
use warnings;

use inc::Module::Install;
use Module::Install::Catalyst;

name 'FixMyStreet-App';
all_from 'perllib/FixMyStreet/App.pm';

requires 'Catalyst::Action::RenderView';
requires 'Catalyst::Authentication::Store::DBIx::Class';
requires 'Catalyst::Model::Adaptor';
requires 'Catalyst::Plugin::Authentication';
requires 'Catalyst::Plugin::ConfigLoader';
requires 'Catalyst::Plugin::Session::Store::DBIC';
requires 'Catalyst::Plugin::Static::Simple';
requires 'Catalyst::Runtime' => '5.80031';
requires 'Catalyst::View::TT';
requires 'Config::General';
requires 'DBD::Pg' => '2.9.2';
requires 'DBIx::Class::Schema::Loader';
requires 'Email::Send';
requires 'Email::Simple';
requires 'Email::Valid';
requires 'HTML::Entities';
requires 'IO::String';
requires 'Moose';
requires 'namespace::autoclean';
requires 'Net::Domain::TLD';
requires 'Path::Class';
requires 'Readonly';
requires 'Sort::Key';
requires 'List::MoreUtils';
requires 'IPC::Run3';
requires 'Data::UUID';
requires 'YAML';
requires 'Geography::NationalGrid';
requires 'Digest::SHA1';
requires 'XML::RSS';
requires 'Regexp::Common';
requires 'Text::Template';
requires 'Image::Size';
requires 'Image::Magick';
requires 'Statistics::Distributions';

test_requires 'Test::More' => '0.88';
test_requires 'Test::WWW::Mechanize::Catalyst';
test_requires 'Sub::Override';

catalyst;

install_script glob('script/*.pl');
auto_install;
WriteAll;
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
/*
 * conn.c
 *
 * Does all this gloriously nifty connection handling stuff...
 *
 */

#include <aim.h> 
#include "sock.h"

static int aim_logoff(aim_session_t *sess);

/*
 * In OSCAR, every connection has a set of SNAC groups associated
 * with it.  These are the groups that you can send over this connection
 * without being guarenteed a "Not supported" SNAC error.  
 *
 * The grand theory of things says that these associations transcend 
 * what libfaim calls "connection types" (conn->type).  You can probably
 * see the elegance here, but since I want to revel in it for a bit, you 
 * get to hear it all spelled out.
 *
 * So let us say that you have your core BOS connection running.  One
 * of your modules has just given you a SNAC of the group 0x0004 to send
 * you.  Maybe an IM destined for some twit in Greenland.  So you start
 * at the top of your connection list, looking for a connection that 
 * claims to support group 0x0004.  You find one.  Why, that neat BOS
 * connection of yours can do that.  So you send it on its way.
 *
 * Now, say, that fellow from Greenland has friends and they all want to
 * meet up with you in a lame chat room.  This has landed you a SNAC
 * in the family 0x000e and you have to admit you're a bit lost.  You've
 * searched your connection list for someone who wants to make your life
 * easy and deliver this SNAC for you, but there isn't one there.
 *
 * Here comes the good bit.  Without even letting anyone know, particularly
 * the module that decided to send this SNAC, and definitly not that twit
 * in Greenland, you send out a service request.  In this request, you have
 * marked the need for a connection supporting group 0x000e.  A few seconds
 * later, you receive a service redirect with an IP address and a cookie in
 * it.  Great, you say.  Now I have something to do.  Off you go, making
 * that connection.  One of the first things you get from this new server
 * is a message saying that indeed it does support the group you were looking
 * for.  So you continue and send rate confirmation and all that.  
 * 
 * Then you remember you had that SNAC to send, and now you have a means to
 * do it, and you do, and everyone is happy.  Except the Greenlander, who is
 * still stuck in the bitter cold.
 *
 * Oh, and this is useful for building the Migration SNACs, too.  In the
 * future, this may help convince me to implement rate limit mitigation
 * for real.  We'll see.
 *
 * Just to make me look better, I'll say that I've known about this great
 * scheme for quite some time now.  But I still haven't convinced myself
 * to make libfaim work that way.  It would take a fair amount of effort,
 * and probably some client API changes as well.  (Whenever I don't want
 * to do something, I just say it would change the client API.  Then I 
 * instantly have a couple of supporters of not doing it.)
 *
 * Generally, addgroup is only called by the internal handling of the
 * server ready SNAC.  So if you want to do something before that, you'll
 * have to be more creative.  That is done rather early, though, so I don't
 * think you have to worry about it.  Unless you're me.  I care deeply
 * about such inane things.
 *
 */
void aim_conn_addgroup(aim_conn_t *conn, guint16 group)
{
	aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside;
	struct snacgroup *sg;

	if (!(sg = g_malloc(sizeof(struct snacgroup))))
		return;

	sg->group = group;

	sg->next = ins->groups;
	ins->groups = sg;

	return;
}

aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group)
{
	aim_conn_t *cur;

	for (cur = sess->connlist; cur; cur = cur->next) {
		aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside;
		struct snacgroup *sg;

		for (sg = ins->groups; sg; sg = sg->next) {
			if (sg->group == group)
				return cur;
		}
	}

	return NULL;
}

static void connkill_snacgroups(struct snacgroup **head)
{
	struct snacgroup *sg;

	for (sg = *head; sg; ) {
		struct snacgroup *tmp;

		tmp = sg->next;
		g_free(sg);
		sg = tmp;
	}

	*head = NULL;

	return;
}

static void connkill_rates(struct rateclass **head)
{
	struct rateclass *rc;

	for (rc = *head; rc; ) {
		struct rateclass *tmp;
		struct snacpair *sp;

		tmp = rc->next;

		for (sp = rc->members; sp; ) {
			struct snacpair *tmpsp;

			tmpsp = sp->next;
			g_free(sp);
			sp = tmpsp;
		}
		g_free(rc);

		rc = tmp;
	}

	*head = NULL;

	return;
}

static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn)
{

	aim_rxqueue_cleanbyconn(sess, *deadconn);
	aim_tx_cleanqueue(sess, *deadconn);

	if ((*deadconn)->fd != -1) 
		aim_conn_close(*deadconn);

	/*
	 * XXX ->priv should never be touched by the library. I know
	 * it used to be, but I'm getting rid of all that.  Use
	 * ->internal instead.
	 */
	if ((*deadconn)->priv)
		g_free((*deadconn)->priv);

	/*
	 * This will free ->internal if it necessary...
	 */
	if ((*deadconn)->type == AIM_CONN_TYPE_CHAT)
		aim_conn_kill_chat(sess, *deadconn);

	if ((*deadconn)->inside) {
		aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside;

		connkill_snacgroups(&inside->groups);
		connkill_rates(&inside->rates);

		g_free(inside);
	}

	g_free(*deadconn);
	*deadconn = NULL;

	return;
}

/**
 * aim_connrst - Clears out connection list, killing remaining connections.
 * @sess: Session to be cleared
 *
 * Clears out the connection list and kills any connections left.
 *
 */
static void aim_connrst(aim_session_t *sess)
{

	if (sess->connlist) {
		aim_conn_t *cur = sess->connlist, *tmp;

		while (cur) {
			tmp = cur->next;
			aim_conn_close(cur);
			connkill_real(sess, &cur);
			cur = tmp;
		}
	}

	sess->connlist = NULL;

	return;
}

/**
 * aim_conn_init - Reset a connection to default values.
 * @deadconn: Connection to be reset
 *
 * Initializes and/or resets a connection structure.
 *
 */
static void aim_conn_init(aim_conn_t *deadconn)
{

	if (!deadconn)
		return;

	deadconn->fd = -1;
	deadconn->subtype = -1;
	deadconn->type = -1;
	deadconn->seqnum = 0;
	deadconn->lastactivity = 0;
	deadconn->forcedlatency = 0;
	deadconn->handlerlist = NULL;
	deadconn->priv = NULL;
	memset(deadconn->inside, 0, sizeof(aim_conn_inside_t));

	return;
}

/**
 * aim_conn_getnext - Gets a new connection structure.
 * @sess: Session
 *
 * Allocate a new empty connection structure.
 *
 */
static aim_conn_t *aim_conn_getnext(aim_session_t *sess)
{
	aim_conn_t *newconn;

	if (!(newconn = g_new0(aim_conn_t,1))) 	
		return NULL;

	if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) {
		g_free(newconn);
		return NULL;
	}

	aim_conn_init(newconn);

	newconn->next = sess->connlist;
	sess->connlist = newconn;

	return newconn;
}

/**
 * aim_conn_kill - Close and free a connection.
 * @sess: Session for the connection
 * @deadconn: Connection to be freed
 *
 * Close, clear, and free a connection structure. Should never be
 * called from within libfaim.
 *
 */
void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn)
{
	aim_conn_t *cur, **prev;

	if (!deadconn || !*deadconn)	
		return;

	for (prev = &sess->connlist; (cur = *prev); ) {
		if (cur == *deadconn) {
			*prev = cur->next;
			break;
		}
		prev = &cur->next;
	}

	if (!cur)
		return; /* oops */

	connkill_real(sess, &cur);

	return;
}

/**
 * aim_conn_close - Close a connection
 * @deadconn: Connection to close
 *
 * Close (but not free) a connection.
 *
 * This leaves everything untouched except for clearing the 
 * handler list and setting the fd to -1 (used to recognize
 * dead connections).  It will also remove cookies if necessary.
 *
 */
void aim_conn_close(aim_conn_t *deadconn)
{

	if (deadconn->fd >= 3)
		closesocket(deadconn->fd);
	deadconn->fd = -1;
	if (deadconn->handlerlist)
		aim_clearhandlers(deadconn);

	return;
}

/**
 * aim_getconn_type - Find a connection of a specific type
 * @sess: Session to search
 * @type: Type of connection to look for
 *
 * Searches for a connection of the specified type in the 
 * specified session.  Returns the first connection of that
 * type found.
 *
 * XXX except for RENDEZVOUS, all uses of this should be removed and
 * use aim_conn_findbygroup() instead.
 */
aim_conn_t *aim_getconn_type(aim_session_t *sess, int type)
{
	aim_conn_t *cur;

	for (cur = sess->connlist; cur; cur = cur->next) {
		if ((cur->type == type) && 
				!(cur->status & AIM_CONN_STATUS_INPROGRESS))
			break;
	}

	return cur;
}

aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type)
{
	aim_conn_t *cur;

	for (cur = sess->connlist; cur; cur = cur->next) {
		if (cur->type == type)
			break;
	}

	return cur;
}

/**
 * aim_newconn - Open a new connection
 * @sess: Session to create connection in
 * @type: Type of connection to create
 * @dest: Host to connect to (in "host:port" syntax)
 *
 * Opens a new connection to the specified dest host of specified
 * type, using the proxy settings if available.  If @host is %NULL,
 * the connection is allocated and returned, but no connection 
 * is made.
 *
 * FIXME: Return errors in a more sane way.
 *
 */
aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest)
{
	aim_conn_t *connstruct;
	guint16 port = AIM_LOGIN_PORT;
	char *host;
	int i;

	if (!(connstruct = aim_conn_getnext(sess)))
		return NULL;

	connstruct->sessv = (void *)sess;
	connstruct->type = type;

	if (!dest) { /* just allocate a struct */
		connstruct->fd = -1;
		connstruct->status = 0;
		return connstruct;
	}

	/* 
	 * As of 23 Jul 1999, AOL now sends the port number, preceded by a 
	 * colon, in the BOS redirect.  This fatally breaks all previous 
	 * libfaims.  Bad, bad AOL.
	 *
	 * We put this here to catch every case. 
	 *
	 */

	for(i = 0; i < (int)strlen(dest); i++) {
		if (dest[i] == ':') {
			port = atoi(&(dest[i+1]));
			break;
		}
	}

	host = (char *)g_malloc(i+1);
	strncpy(host, dest, i);
	host[i] = '\0';

	connstruct->fd = proxy_connect(host, port, NULL, NULL);

	g_free(host);

	return connstruct;
}

/**
 * aim_conn_setlatency - Set a forced latency value for connection
 * @conn: Conn to set latency for
 * @newval: Number of seconds to force between transmits
 *
 * Causes @newval seconds to be spent between transmits on a connection.
 *
 * This is my lame attempt at overcoming not understanding the rate
 * limiting. 
 *
 * XXX: This should really be replaced with something that scales and
 * backs off like the real rate limiting does.
 *
 */
int aim_conn_setlatency(aim_conn_t *conn, int newval)
{

	if (!conn)
		return -1;

	conn->forcedlatency = newval;
	conn->lastactivity = 0; /* reset this just to make sure */

	return 0;
}

/**
 * aim_session_init - Initializes a session structure
 * @sess: Session to initialize
 * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together.
 * @debuglevel: Level of debugging output (zero is least)
 *
 * Sets up the initial values for a session.
 *
 */
void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel)
{

	if (!sess)
		return;

	memset(sess, 0, sizeof(aim_session_t));
	aim_connrst(sess);
	sess->queue_outgoing = NULL;
	sess->queue_incoming = NULL;
	aim_initsnachash(sess);
	sess->msgcookies = NULL;
	sess->snacid_next = 0x00000001;

	sess->flags = 0;

	sess->modlistv = NULL;

	sess->ssi.received_data = 0;
	sess->ssi.waiting_for_ack = 0;
	sess->ssi.holding_queue = NULL;
	sess->ssi.revision = 0;
	sess->ssi.items = NULL;
	sess->ssi.timestamp = (time_t)0;

	sess->locate.userinfo = NULL;
	sess->locate.torequest = NULL;
	sess->locate.requested = NULL;
	sess->locate.waiting_for_response = FALSE;

	sess->icq_info = NULL;
	sess->authinfo = NULL;
	sess->emailinfo = NULL;
	sess->oft_info = NULL;


	/*
	 * Default to SNAC login unless XORLOGIN is explicitly set.
	 */
	if (!(flags & AIM_SESS_FLAGS_XORLOGIN))
		sess->flags |= AIM_SESS_FLAGS_SNACLOGIN;
	sess->flags |= flags;

	/*
	 * This must always be set.  Default to the queue-based
	 * version for back-compatibility.  
	 */
	aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL);


	/*
	 * Register all the modules for this session...
	 */
	aim__registermodule(sess, misc_modfirst); /* load the catch-all first */
	aim__registermodule(sess, general_modfirst);
	aim__registermodule(sess, locate_modfirst);
	aim__registermodule(sess, buddylist_modfirst);
	aim__registermodule(sess, msg_modfirst);
	aim__registermodule(sess, admin_modfirst);
	aim__registermodule(sess, bos_modfirst);
	aim__registermodule(sess, search_modfirst);
	aim__registermodule(sess, stats_modfirst);
	aim__registermodule(sess, chatnav_modfirst);
	aim__registermodule(sess, chat_modfirst);
	/* missing 0x0f - 0x12 */
	aim__registermodule(sess, ssi_modfirst);
	/* missing 0x14 */
	aim__registermodule(sess, icq_modfirst);
	/* missing 0x16 */
	aim__registermodule(sess, auth_modfirst);

	return;
}

/**
 * aim_session_kill - Deallocate a session
 * @sess: Session to kill
 *
 */
void aim_session_kill(aim_session_t *sess)
{
	aim_cleansnacs(sess, -1);

	aim_logoff(sess);

	aim__shutdownmodules(sess);

	return;
}

/*
 * XXX this is nearly as ugly as proxyconnect().
 */
int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn)
{
	fd_set fds, wfds;
	struct timeval tv;
	int res, error = ETIMEDOUT;
	aim_rxcallback_t userfunc;

	if (!conn || (conn->fd == -1))
		return -1;

	if (!(conn->status & AIM_CONN_STATUS_INPROGRESS))
		return -1;

	FD_ZERO(&fds);
	FD_SET(conn->fd, &fds);
	FD_ZERO(&wfds);
	FD_SET(conn->fd, &wfds);
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) {
		error = errno;
		aim_conn_close(conn);
		errno = error;
		return -1;
	} else if (res == 0) {
		return 0; /* hasn't really completed yet... */
	} 

	if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) {
		unsigned int len = sizeof(error);

		if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
			error = errno;
	}

	if (error) {
		aim_conn_close(conn);
		errno = error;
		return -1;
	}

	sock_make_blocking(conn->fd);

	conn->status &= ~AIM_CONN_STATUS_INPROGRESS;

	if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE)))
		userfunc(sess, NULL, conn);

	/* Flush out the queues if there was something waiting for this conn  */
	aim_tx_flushqueue(sess);

	return 0;
}

aim_session_t *aim_conn_getsess(aim_conn_t *conn)
{

	if (!conn)
		return NULL;

	return (aim_session_t *)conn->sessv;
}

/*
 * aim_logoff()
 *
 * Closes -ALL- open connections.
 *
 */
static int aim_logoff(aim_session_t *sess)
{

	aim_connrst(sess);  /* in case we want to connect again */

	return 0;

}

/*
 * aim_flap_nop()
 *
 * No-op.  WinAIM 4.x sends these _every minute_ to keep
 * the connection alive.  
 */
int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn)
{
	aim_frame_t *fr;

	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0)))
		return -ENOMEM;

	aim_tx_enqueue(sess, fr);

	return 0;
}