/*
* libyahoo2: yahoo2.h
*
* Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef YAHOO2_H
#define YAHOO2_H
#ifdef __cplusplus
extern "C" {
#endif
/* *** BitlBee: *** */
#include "bitlbee.h"
#undef free
#define free(x) g_free(x)
#undef malloc
#define malloc(x) g_malloc(x)
#undef calloc
#define calloc(x, y) g_calloc(x, y)
#undef realloc
#define realloc(x, y) g_realloc(x, y)
#undef strdup
#define strdup(x) g_strdup(x)
#undef strndup
#define strndup(x, y) g_strndup(x, y)
#undef snprintf
// #define snprintf( x... ) g_snprintf( x )
#undef strcasecmp
#define strcasecmp(x, y) g_strcasecmp(x, y)
#undef strncasecmp
#define strncasecmp(x, y, z) g_strncasecmp(x, y, z)
#include "yahoo2_types.h"
/* returns the socket descriptor object for a given pager connection. shouldn't be needed */
void *yahoo_get_fd(int id);
/* says how much logging to do */
/* see yahoo2_types.h for the different values */
int yahoo_set_log_level(enum yahoo_log_level level);
enum yahoo_log_level yahoo_get_log_level(void);
/* these functions should be self explanatory */
/* who always means the buddy you're acting on */
/* id is the successful value returned by yahoo_init */
/* init returns a connection id used to identify the connection hereon */
/* or 0 on failure */
/* you must call init before calling any other function */
/*
* The optional parameters to init are key/value pairs that specify
* server settings to use. This list must be NULL terminated - even
* if the list is empty. If a parameter isn't set, a default value
* will be used. Parameter keys are strings, parameter values are
* either strings or ints, depending on the key. Values passed in
* are copied, so you can use const/auto/static/pointers/whatever
* you want. Parameters are:
* NAME TYPE DEFAULT
* pager_host char * scs.msg.yahoo.com
* pager_port int 5050
* filetransfer_host char * filetransfer.msg.yahoo.com
* filetransfer_port int 80
* webcam_host char * webcam.yahoo.com
* webcam_port int 5100
* webcam_description char * ""
* local_host char * ""
* conn_type int Y_WCM_DSL
*
* You should set at least local_host if you intend to use webcams
*/
int yahoo_init_with_attributes(const char *username,
const char *password, ...);
/* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults
* for all attributes */
int yahoo_init(const char *username, const char *password);
/* release all resources held by this session */
/* you need to call yahoo_close for a session only if
* yahoo_logoff is never called for it (ie, it was never logged in) */
void yahoo_close(int id);
/* login logs in to the server */
/* initial is of type enum yahoo_status. see yahoo2_types.h */
void yahoo_login(int id, int initial);
void yahoo_logoff(int id);
/* reloads status of all buddies */
void yahoo_refresh(int id);
/* activates/deactivates an identity */
void yahoo_set_identity_status(int id, const char *identity,
int active);
/* regets the entire buddy list from the server */
void yahoo_get_list(int id);
/* download buddy contact information from your yahoo addressbook */
voidpre { 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 *//*
* 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_cloneconn - clone an aim_conn_t
* @sess: session containing parent
* @src: connection to clone
*
* A new connection is allocated, and the values are filled in
* appropriately. Note that this function sets the new connnection's
* ->priv pointer to be equal to that of its parent: only the pointer
* is copied, not the data it points to.
*
* This function returns a pointer to the new aim_conn_t, or %NULL on
* error
*/
aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src)
{
aim_conn_t *conn;
if (!(conn = aim_conn_getnext(sess)))
return NULL;
conn->fd = src->fd;
conn->type = src->type;
conn->subtype = src->subtype;
conn->seqnum = src->seqnum;
conn->priv = src->priv;
conn->internal = src->internal;
conn->lastactivity = src->lastactivity;
conn->forcedlatency = src->forcedlatency;
conn->sessv = src->sessv;
aim_clonehandlers(sess, conn, src);
if (src->inside) {
/*
* XXX should clone this section as well, but since currently
* this function only gets called for some of that rendezvous
* crap, and not on SNAC connections, its probably okay for
* now.
*
*/
}
return conn;
}
/**
* 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;
}