File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / flap_connection.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Mon Jan 27 19:48:25 2025 UTC (6 months ago) by snw
Branches: MAIN, CoherentLogicDevelopment
CVS tags: test-tag, start, HEAD
Pidgin AIM Plugin for ChivaNet

    1: /*
    2:  * Purple's oscar protocol plugin
    3:  * This file is the legal property of its developers.
    4:  * Please see the AUTHORS file distributed alongside this file.
    5:  *
    6:  * This library is free software; you can redistribute it and/or
    7:  * modify it under the terms of the GNU Lesser General Public
    8:  * License as published by the Free Software Foundation; either
    9:  * version 2 of the License, or (at your option) any later version.
   10:  *
   11:  * This library is distributed in the hope that it will be useful,
   12:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14:  * Lesser General Public License for more details.
   15:  *
   16:  * You should have received a copy of the GNU Lesser General Public
   17:  * License along with this library; if not, write to the Free Software
   18:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
   19: */
   20: 
   21: #include "oscar.h"
   22: 
   23: #include "eventloop.h"
   24: #include "proxy.h"
   25: 
   26: #ifndef _WIN32
   27: #include <netdb.h>
   28: #include <sys/socket.h>
   29: #include <netinet/in.h>
   30: #endif
   31: 
   32: #ifdef _WIN32
   33: #include "win32dep.h"
   34: #endif
   35: 
   36: /**
   37:  * This sends a channel 1 SNAC containing the FLAP version.
   38:  * The FLAP version is sent by itself at the beginning of every
   39:  * connection to a FLAP server.  It is always the very first
   40:  * packet sent by both the server and the client after the SYN,
   41:  * SYN/ACK, ACK handshake.
   42:  */
   43: void
   44: flap_connection_send_version(OscarData *od, FlapConnection *conn)
   45: {
   46: 	FlapFrame *frame;
   47: 
   48: 	frame = flap_frame_new(od, 0x01, 4);
   49: 	byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
   50: 	flap_connection_send(conn, frame);
   51: }
   52: 
   53: /**
   54:  * This sends a channel 1 FLAP containing the FLAP version and
   55:  * the authentication cookie.  This is sent when connecting to
   56:  * any FLAP server after the initial connection to the auth
   57:  * server.  It is always the very first packet sent by both the
   58:  * server and the client after the SYN, SYN/ACK, ACK handshake.
   59:  */
   60: void
   61: flap_connection_send_version_with_cookie(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy)
   62: {
   63: 	FlapFrame *frame;
   64: 	GSList *tlvlist = NULL;
   65: 
   66: 	frame = flap_frame_new(od, 0x01, 4 + 2 + 2 + length);
   67: 	byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
   68: 	aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
   69: 	aim_tlvlist_write(&frame->data, &tlvlist);
   70: 	aim_tlvlist_free(tlvlist);
   71: 
   72: 	flap_connection_send(conn, frame);
   73: }
   74: 
   75: void
   76: flap_connection_send_version_with_cookie_and_clientinfo(OscarData *od, FlapConnection *conn, guint16 length, const guint8 *chipsahoy, ClientInfo *ci, gboolean allow_multiple_logins)
   77: {
   78: 	FlapFrame *frame;
   79: 	GSList *tlvlist = NULL;
   80: 
   81: 	frame = flap_frame_new(od, 0x01, 1152 + length);
   82: 
   83: 	byte_stream_put32(&frame->data, 0x00000001); /* FLAP Version */
   84: 	aim_tlvlist_add_raw(&tlvlist, 0x0006, length, chipsahoy);
   85: 
   86: 	if (ci->clientstring != NULL)
   87: 		aim_tlvlist_add_str(&tlvlist, 0x0003, ci->clientstring);
   88: 	else {
   89: 		gchar *clientstring = oscar_get_clientstring();
   90: 		aim_tlvlist_add_str(&tlvlist, 0x0003, clientstring);
   91: 		g_free(clientstring);
   92: 	}
   93: 	aim_tlvlist_add_16(&tlvlist, 0x0017, (guint16)ci->major);
   94: 	aim_tlvlist_add_16(&tlvlist, 0x0018, (guint16)ci->minor);
   95: 	aim_tlvlist_add_16(&tlvlist, 0x0019, (guint16)ci->point);
   96: 	aim_tlvlist_add_16(&tlvlist, 0x001a, (guint16)ci->build);
   97: 	aim_tlvlist_add_8(&tlvlist, 0x004a, (allow_multiple_logins ? 0x01 : 0x03));
   98: 
   99: 	aim_tlvlist_write(&frame->data, &tlvlist);
  100: 
  101: 	aim_tlvlist_free(tlvlist);
  102: 
  103: 	flap_connection_send(conn, frame);
  104: }
  105: 
  106: static struct rateclass *
  107: flap_connection_get_rateclass(FlapConnection *conn, guint16 family, guint16 subtype)
  108: {
  109: 	gconstpointer key;
  110: 	gpointer rateclass;
  111: 
  112: 	key = GUINT_TO_POINTER((family << 16) + subtype);
  113: 	rateclass = g_hash_table_lookup(conn->rateclass_members, key);
  114: 	if (rateclass != NULL)
  115: 		return rateclass;
  116: 
  117: 	return conn->default_rateclass;
  118: }
  119: 
  120: /*
  121:  * Attempt to calculate what our new current average would be if we
  122:  * were to send a SNAC in this rateclass at the given time.
  123:  */
  124: static guint32
  125: rateclass_get_new_current(FlapConnection *conn, struct rateclass *rateclass, struct timeval *now)
  126: {
  127: 	unsigned long timediff; /* In milliseconds */
  128: 	guint32 current;
  129: 
  130: 	/* This formula is documented at http://dev.aol.com/aim/oscar/#RATELIMIT */
  131: 	timediff = (now->tv_sec - rateclass->last.tv_sec) * 1000 + (now->tv_usec - rateclass->last.tv_usec) / 1000;
  132: 	current = ((rateclass->current * (rateclass->windowsize - 1)) + timediff) / rateclass->windowsize;
  133: 
  134: 	return MIN(current, rateclass->max);
  135: }
  136: 
  137: /*
  138:  * Attempt to send the contents of a given queue
  139:  *
  140:  * @return TRUE if the queue was completely emptied or was initially
  141:  *         empty; FALSE if rate limiting prevented it from being
  142:  *         emptied.
  143:  */
  144: static gboolean flap_connection_send_snac_queue(FlapConnection *conn, struct timeval now, GQueue *queue)
  145: {
  146: 	while (!g_queue_is_empty(queue))
  147: 	{
  148: 		QueuedSnac *queued_snac;
  149: 		struct rateclass *rateclass;
  150: 
  151: 		queued_snac = g_queue_peek_head(queue);
  152: 
  153: 		rateclass = flap_connection_get_rateclass(conn, queued_snac->family, queued_snac->subtype);
  154: 		if (rateclass != NULL)
  155: 		{
  156: 			guint32 new_current;
  157: 
  158: 			new_current = rateclass_get_new_current(conn, rateclass, &now);
  159: 
  160: 			if (rateclass->dropping_snacs || new_current <= rateclass->alert)
  161: 				/* Not ready to send this SNAC yet--keep waiting. */
  162: 				return FALSE;
  163: 
  164: 			rateclass->current = new_current;
  165: 			rateclass->last.tv_sec = now.tv_sec;
  166: 			rateclass->last.tv_usec = now.tv_usec;
  167: 		}
  168: 
  169: 		flap_connection_send(conn, queued_snac->frame);
  170: 		g_free(queued_snac);
  171: 		g_queue_pop_head(queue);
  172: 	}
  173: 
  174: 	/* We emptied the queue */
  175: 	return TRUE;
  176: }
  177: 
  178: static gboolean flap_connection_send_queued(gpointer data)
  179: {
  180: 	FlapConnection *conn;
  181: 	struct timeval now;
  182: 
  183: 	conn = data;
  184: 	gettimeofday(&now, NULL);
  185: 
  186: 	purple_debug_info("oscar", "Attempting to send %u queued SNACs and %u queued low-priority SNACs for %p\n",
  187: 					  (conn->queued_snacs ? conn->queued_snacs->length : 0),
  188: 					  (conn->queued_lowpriority_snacs ? conn->queued_lowpriority_snacs->length : 0),
  189: 					  conn);
  190: 	if (!conn->queued_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_snacs)) {
  191: 		if (!conn->queued_lowpriority_snacs || flap_connection_send_snac_queue(conn, now, conn->queued_lowpriority_snacs)) {
  192: 			/* Both queues emptied. */
  193: 			conn->queued_timeout = 0;
  194: 			return FALSE;
  195: 		}
  196: 	}
  197: 
  198: 	/* We couldn't send all our SNACs. Keep trying */
  199: 	return TRUE;
  200: }
  201: 
  202: /**
  203:  * This sends a channel 2 FLAP containing a SNAC.  The SNAC family and
  204:  * subtype are looked up in the rate info for this connection, and if
  205:  * sending this SNAC will induce rate limiting then we delay sending
  206:  * of the SNAC by putting it into an outgoing holding queue.
  207:  *
  208:  * @param data The optional bytestream that makes up the data portion
  209:  *        of this SNAC.  For empty SNACs this should be NULL.
  210:  * @param high_priority If TRUE, the SNAC will be queued normally if
  211:  *        needed. If FALSE, it will be queued separately, to be sent
  212:  *        only if all high priority SNACs have been sent.
  213:  */
  214: void
  215: flap_connection_send_snac_with_priority(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data, gboolean high_priority)
  216: {
  217: 	FlapFrame *frame;
  218: 	guint32 length;
  219: 	gboolean enqueue = FALSE;
  220: 	struct rateclass *rateclass;
  221: 
  222: 	length = data != NULL ? data->offset : 0;
  223: 
  224: 	frame = flap_frame_new(od, 0x02, 10 + length);
  225: 	aim_putsnac(&frame->data, family, subtype, snacid);
  226: 
  227: 	if (length > 0)
  228: 	{
  229: 		byte_stream_rewind(data);
  230: 		byte_stream_putbs(&frame->data, data, length);
  231: 	}
  232: 
  233: 	if (conn->queued_timeout != 0)
  234: 		enqueue = TRUE;
  235: 	else if ((rateclass = flap_connection_get_rateclass(conn, family, subtype)) != NULL)
  236: 	{
  237: 		struct timeval now;
  238: 		guint32 new_current;
  239: 
  240: 		gettimeofday(&now, NULL);
  241: 		new_current = rateclass_get_new_current(conn, rateclass, &now);
  242: 
  243: 		if (rateclass->dropping_snacs || new_current <= rateclass->alert)
  244: 		{
  245: 			purple_debug_info("oscar", "Current rate for conn %p would be %u, but we alert at %u; enqueueing\n", conn, new_current, rateclass->alert);
  246: 
  247: 			enqueue = TRUE;
  248: 		}
  249: 		else
  250: 		{
  251: 			rateclass->current = new_current;
  252: 			rateclass->last.tv_sec = now.tv_sec;
  253: 			rateclass->last.tv_usec = now.tv_usec;
  254: 		}
  255: 	}
  256: 
  257: 	if (enqueue)
  258: 	{
  259: 		/* We've been sending too fast, so delay this message */
  260: 		QueuedSnac *queued_snac;
  261: 
  262: 		queued_snac = g_new(QueuedSnac, 1);
  263: 		queued_snac->family = family;
  264: 		queued_snac->subtype = subtype;
  265: 		queued_snac->frame = frame;
  266: 
  267: 		if (high_priority) {
  268: 			if (!conn->queued_snacs)
  269: 				conn->queued_snacs = g_queue_new();
  270: 			g_queue_push_tail(conn->queued_snacs, queued_snac);
  271: 		} else {
  272: 			if (!conn->queued_lowpriority_snacs)
  273: 				conn->queued_lowpriority_snacs = g_queue_new();
  274: 			g_queue_push_tail(conn->queued_lowpriority_snacs, queued_snac);
  275: 		}
  276: 
  277: 		if (conn->queued_timeout == 0)
  278: 			conn->queued_timeout = purple_timeout_add(500, flap_connection_send_queued, conn);
  279: 
  280: 		return;
  281: 	}
  282: 
  283: 	flap_connection_send(conn, frame);
  284: }
  285: 
  286: void
  287: flap_connection_send_snac(OscarData *od, FlapConnection *conn, guint16 family, const guint16 subtype, aim_snacid_t snacid, ByteStream *data)
  288: {
  289: 	flap_connection_send_snac_with_priority(od, conn, family, subtype, snacid, data, TRUE);
  290: }
  291: 
  292: /**
  293:  * This sends an empty channel 4 FLAP.  This is sent to signify
  294:  * that we're logging off.  This shouldn't really be necessary--
  295:  * usually the AIM server will detect that the TCP connection has
  296:  * been destroyed--but it's good practice.
  297:  */
  298: static void
  299: flap_connection_send_close(OscarData *od, FlapConnection *conn)
  300: {
  301: 	FlapFrame *frame;
  302: 
  303: 	frame = flap_frame_new(od, 0x04, 0);
  304: 	flap_connection_send(conn, frame);
  305: }
  306: 
  307: /**
  308:  * This sends an empty channel 5 FLAP.  This is used as a keepalive
  309:  * packet in FLAP connections.  WinAIM 4.x and higher send these
  310:  * _every minute_ to keep the connection alive.
  311:  */
  312: void
  313: flap_connection_send_keepalive(OscarData *od, FlapConnection *conn)
  314: {
  315: 	FlapFrame *frame;
  316: 
  317: 	frame = flap_frame_new(od, 0x05, 0);
  318: 	flap_connection_send(conn, frame);
  319: 
  320: 	/* clean out SNACs over 60sec old */
  321: 	aim_cleansnacs(od, 60);
  322: }
  323: 
  324: /**
  325:  * Allocate a new empty connection structure.
  326:  *
  327:  * @param od The oscar session associated with this connection.
  328:  * @param type Type of connection to create
  329:  *
  330:  * @return Returns the new connection structure.
  331:  */
  332: FlapConnection *
  333: flap_connection_new(OscarData *od, int type)
  334: {
  335: 	FlapConnection *conn;
  336: 
  337: 	conn = g_new0(FlapConnection, 1);
  338: 	conn->od = od;
  339: 	conn->buffer_outgoing = purple_circ_buffer_new(0);
  340: 	conn->fd = -1;
  341: 	conn->subtype = -1;
  342: 	conn->type = type;
  343: 	conn->rateclass_members = g_hash_table_new(g_direct_hash, g_direct_equal);
  344: 
  345: 	od->oscar_connections = g_slist_prepend(od->oscar_connections, conn);
  346: 
  347: 	return conn;
  348: }
  349: 
  350: /**
  351:  * Close (but not free) a connection.
  352:  *
  353:  * This cancels any currently pending connection attempt,
  354:  * closes any open fd and frees the auth cookie.
  355:  *
  356:  * @param conn The connection to close.
  357:  */
  358: void
  359: flap_connection_close(OscarData *od, FlapConnection *conn)
  360: {
  361: 	if (conn->connect_data != NULL)
  362: 	{
  363: 		purple_proxy_connect_cancel(conn->connect_data);
  364: 		conn->connect_data = NULL;
  365: 	}
  366: 
  367: 	if (conn->gsc != NULL && conn->gsc->connect_data != NULL)
  368: 	{
  369: 		purple_ssl_close(conn->gsc);
  370: 		conn->gsc = NULL;
  371: 	}
  372: 
  373: 	if (conn->new_conn_data != NULL)
  374: 	{
  375: 		if (conn->type == SNAC_FAMILY_CHAT)
  376: 		{
  377: 			oscar_chat_destroy(conn->new_conn_data);
  378: 			conn->new_conn_data = NULL;
  379: 		}
  380: 	}
  381: 
  382: 	if ((conn->fd >= 0 || conn->gsc != NULL)
  383: 			&& conn->type == SNAC_FAMILY_LOCATE)
  384: 		flap_connection_send_close(od, conn);
  385: 
  386: 	if (conn->watcher_incoming != 0)
  387: 	{
  388: 		purple_input_remove(conn->watcher_incoming);
  389: 		conn->watcher_incoming = 0;
  390: 	}
  391: 
  392: 	if (conn->watcher_outgoing != 0)
  393: 	{
  394: 		purple_input_remove(conn->watcher_outgoing);
  395: 		conn->watcher_outgoing = 0;
  396: 	}
  397: 
  398: 	if (conn->fd >= 0)
  399: 	{
  400: 		close(conn->fd);
  401: 		conn->fd = -1;
  402: 	}
  403: 
  404: 	if (conn->gsc != NULL)
  405: 	{
  406: 		purple_ssl_close(conn->gsc);
  407: 		conn->gsc = NULL;
  408: 	}
  409: 
  410: 	g_free(conn->buffer_incoming.data.data);
  411: 	conn->buffer_incoming.data.data = NULL;
  412: 
  413: 	purple_circ_buffer_destroy(conn->buffer_outgoing);
  414: 	conn->buffer_outgoing = NULL;
  415: }
  416: 
  417: /**
  418:  * Free a FlapFrame
  419:  *
  420:  * @param frame The frame to free.
  421:  */
  422: static void
  423: flap_frame_destroy(FlapFrame *frame)
  424: {
  425: 	g_free(frame->data.data);
  426: 	g_free(frame);
  427: }
  428: 
  429: static gboolean
  430: flap_connection_destroy_cb(gpointer data)
  431: {
  432: 	FlapConnection *conn;
  433: 	OscarData *od;
  434: 	PurpleAccount *account;
  435: 	aim_rxcallback_t userfunc;
  436: 
  437: 	conn = data;
  438: 	/* Explicitly added for debugging #5927.  Don't re-order this, only
  439: 	 * consider removing it.
  440: 	 */
  441: 	purple_debug_info("oscar", "Destroying FLAP connection %p\n", conn);
  442: 
  443: 	od = conn->od;
  444: 	account = purple_connection_get_account(od->gc);
  445: 
  446: 	purple_debug_info("oscar", "Destroying oscar connection (%p) of "
  447: 			"type 0x%04hx.  Disconnect reason is %d\n", conn,
  448: 			conn->type, conn->disconnect_reason);
  449: 
  450: 	od->oscar_connections = g_slist_remove(od->oscar_connections, conn);
  451: 
  452: 	if ((userfunc = aim_callhandler(od, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR)))
  453: 		userfunc(od, conn, NULL, conn->disconnect_code, conn->error_message);
  454: 
  455: 	/*
  456: 	 * TODO: If we don't have a SNAC_FAMILY_LOCATE connection then
  457: 	 * we should try to request one instead of disconnecting.
  458: 	 */
  459: 	if (!account->disconnecting && ((od->oscar_connections == NULL)
  460: 			|| (!flap_connection_getbytype(od, SNAC_FAMILY_LOCATE))))
  461: 	{
  462: 		/* No more FLAP connections!  Sign off this PurpleConnection! */
  463: 		gchar *tmp;
  464: 		PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR;
  465: 
  466: 		if (conn->disconnect_code == 0x0001) {
  467: 			reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE;
  468: 			tmp = g_strdup(_("You have signed on from another location"));
  469: 			if (!purple_account_get_remember_password(account))
  470: 				purple_account_set_password(account, NULL);
  471: 		} else if (conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED)
  472: 			tmp = g_strdup(_("Server closed the connection"));
  473: 		else if (conn->disconnect_reason == OSCAR_DISCONNECT_LOST_CONNECTION)
  474: 			tmp = g_strdup_printf(_("Lost connection with server: %s"),
  475: 					conn->error_message);
  476: 		else if (conn->disconnect_reason == OSCAR_DISCONNECT_INVALID_DATA)
  477: 			tmp = g_strdup(_("Received invalid data on connection with server"));
  478: 		else if (conn->disconnect_reason == OSCAR_DISCONNECT_COULD_NOT_CONNECT)
  479: 			tmp = g_strdup_printf(_("Unable to connect: %s"),
  480: 					conn->error_message);
  481: 		else
  482: 			/*
  483: 			 * We shouldn't print a message for some disconnect_reasons.
  484: 			 * Like OSCAR_DISCONNECT_LOCAL_CLOSED.
  485: 			 */
  486: 			tmp = NULL;
  487: 
  488: 		if (tmp != NULL)
  489: 		{
  490: 			purple_connection_error_reason(od->gc, reason, tmp);
  491: 			g_free(tmp);
  492: 		}
  493: 	}
  494: 
  495: 	flap_connection_close(od, conn);
  496: 
  497: 	g_free(conn->error_message);
  498: 	g_free(conn->cookie);
  499: 
  500: 	/*
  501: 	 * Free conn->internal, if necessary
  502: 	 */
  503: 	if (conn->type == SNAC_FAMILY_CHAT)
  504: 		flap_connection_destroy_chat(od, conn);
  505: 
  506: 	g_slist_free(conn->groups);
  507: 	while (conn->rateclasses != NULL)
  508: 	{
  509: 		g_free(conn->rateclasses->data);
  510: 		conn->rateclasses = g_slist_delete_link(conn->rateclasses, conn->rateclasses);
  511: 	}
  512: 
  513: 	g_hash_table_destroy(conn->rateclass_members);
  514: 
  515: 	if (conn->queued_snacs) {
  516: 		while (!g_queue_is_empty(conn->queued_snacs))
  517: 		{
  518: 			QueuedSnac *queued_snac;
  519: 			queued_snac = g_queue_pop_head(conn->queued_snacs);
  520: 			flap_frame_destroy(queued_snac->frame);
  521: 			g_free(queued_snac);
  522: 		}
  523: 		g_queue_free(conn->queued_snacs);
  524: 	}
  525: 
  526: 	if (conn->queued_lowpriority_snacs) {
  527: 		while (!g_queue_is_empty(conn->queued_lowpriority_snacs))
  528: 		{
  529: 			QueuedSnac *queued_snac;
  530: 			queued_snac = g_queue_pop_head(conn->queued_lowpriority_snacs);
  531: 			flap_frame_destroy(queued_snac->frame);
  532: 			g_free(queued_snac);
  533: 		}
  534: 		g_queue_free(conn->queued_lowpriority_snacs);
  535: 	}
  536: 
  537: 	if (conn->queued_timeout > 0)
  538: 		purple_timeout_remove(conn->queued_timeout);
  539: 
  540: 	g_free(conn);
  541: 
  542: 	return FALSE;
  543: }
  544: 
  545: /**
  546:  * See the comments for the parameters of
  547:  * flap_connection_schedule_destroy().
  548:  */
  549: void
  550: flap_connection_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
  551: {
  552: 	if (conn->destroy_timeout != 0)
  553: 		purple_timeout_remove(conn->destroy_timeout);
  554: 	conn->disconnect_reason = reason;
  555: 	g_free(conn->error_message);
  556: 	conn->error_message = g_strdup(error_message);
  557: 	flap_connection_destroy_cb(conn);
  558: }
  559: 
  560: /**
  561:  * Schedule Purple to destroy the given FlapConnection as soon as we
  562:  * return control back to the program's main loop.  We must do this
  563:  * if we want to destroy the connection but we are still using it
  564:  * for some reason.
  565:  *
  566:  * @param reason The reason for the disconnection.
  567:  * @param error_message A brief error message that gives more detail
  568:  *        regarding the reason for the disconnecting.  This should
  569:  *        be NULL for everything except OSCAR_DISCONNECT_LOST_CONNECTION,
  570:  *        in which case it should contain the value of g_strerror(errno),
  571:  *        and OSCAR_DISCONNECT_COULD_NOT_CONNECT, in which case it
  572:  *        should contain the error_message passed back from the call
  573:  *        to purple_proxy_connect().
  574:  */
  575: void
  576: flap_connection_schedule_destroy(FlapConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
  577: {
  578: 	if (conn->destroy_timeout != 0)
  579: 		/* Already taken care of */
  580: 		return;
  581: 
  582: 	purple_debug_info("oscar", "Scheduling destruction of FLAP "
  583: 			"connection %p of type 0x%04hx\n", conn, conn->type);
  584: 	conn->disconnect_reason = reason;
  585: 	g_free(conn->error_message);
  586: 	conn->error_message = g_strdup(error_message);
  587: 	conn->destroy_timeout = purple_timeout_add(0, flap_connection_destroy_cb, conn);
  588: }
  589: 
  590: /**
  591:  * In OSCAR, every connection has a set of SNAC groups associated
  592:  * with it.  These are the groups that you can send over this connection
  593:  * without being guaranteed a "Not supported" SNAC error.
  594:  *
  595:  * The grand theory of things says that these associations transcend
  596:  * what libfaim calls "connection types" (conn->type).  You can probably
  597:  * see the elegance here, but since I want to revel in it for a bit, you
  598:  * get to hear it all spelled out.
  599:  *
  600:  * So let us say that you have your core BOS connection running.  One
  601:  * of your modules has just given you a SNAC of the group 0x0004 to send
  602:  * you.  Maybe an IM destined for some twit in Greenland.  So you start
  603:  * at the top of your connection list, looking for a connection that
  604:  * claims to support group 0x0004.  You find one.  Why, that neat BOS
  605:  * connection of yours can do that.  So you send it on its way.
  606:  *
  607:  * Now, say, that fellow from Greenland has friends and they all want to
  608:  * meet up with you in a lame chat room.  This has landed you a SNAC
  609:  * in the family 0x000e and you have to admit you're a bit lost.  You've
  610:  * searched your connection list for someone who wants to make your life
  611:  * easy and deliver this SNAC for you, but there isn't one there.
  612:  *
  613:  * Here comes the good bit.  Without even letting anyone know, particularly
  614:  * the module that decided to send this SNAC, and definitely not that twit
  615:  * in Greenland, you send out a service request.  In this request, you have
  616:  * marked the need for a connection supporting group 0x000e.  A few seconds
  617:  * later, you receive a service redirect with an IP address and a cookie in
  618:  * it.  Great, you say.  Now I have something to do.  Off you go, making
  619:  * that connection.  One of the first things you get from this new server
  620:  * is a message saying that indeed it does support the group you were looking
  621:  * for.  So you continue and send rate confirmation and all that.
  622:  *
  623:  * Then you remember you had that SNAC to send, and now you have a means to
  624:  * do it, and you do, and everyone is happy.  Except the Greenlander, who is
  625:  * still stuck in the bitter cold.
  626:  *
  627:  * Oh, and this is useful for building the Migration SNACs, too.  In the
  628:  * future, this may help convince me to implement rate limit mitigation
  629:  * for real.  We'll see.
  630:  *
  631:  * Just to make me look better, I'll say that I've known about this great
  632:  * scheme for quite some time now.  But I still haven't convinced myself
  633:  * to make libfaim work that way.  It would take a fair amount of effort,
  634:  * and probably some client API changes as well.  (Whenever I don't want
  635:  * to do something, I just say it would change the client API.  Then I
  636:  * instantly have a couple of supporters of not doing it.)
  637:  *
  638:  * Generally, addgroup is only called by the internal handling of the
  639:  * server ready SNAC.  So if you want to do something before that, you'll
  640:  * have to be more creative.  That is done rather early, though, so I don't
  641:  * think you have to worry about it.  Unless you're me.  I care deeply
  642:  * about such inane things.
  643:  *
  644:  */
  645: 
  646: /**
  647:  * Find a FlapConnection that supports the given oscar
  648:  * family.
  649:  */
  650: FlapConnection *
  651: flap_connection_findbygroup(OscarData *od, guint16 group)
  652: {
  653: 	GSList *cur;
  654: 
  655: 	for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
  656: 	{
  657: 		FlapConnection *conn;
  658: 		GSList *l;
  659: 
  660: 		conn = cur->data;
  661: 
  662: 		for (l = conn->groups; l != NULL; l = l->next)
  663: 		{
  664: 			if (GPOINTER_TO_UINT(l->data) == group)
  665: 				return conn;
  666: 		}
  667: 	}
  668: 
  669: 	return NULL;
  670: }
  671: 
  672: /**
  673:  * Locates a connection of the specified type in the
  674:  * specified session.
  675:  *
  676:  * TODO: Use flap_connection_findbygroup everywhere and get rid of this.
  677:  *
  678:  * @param od The session to search.
  679:  * @param type The type of connection to look for.
  680:  *
  681:  * @return Returns the first connection found of the given target type,
  682:  *         or NULL if none could be found.
  683:  */
  684: FlapConnection *
  685: flap_connection_getbytype(OscarData *od, int type)
  686: {
  687: 	GSList *cur;
  688: 
  689: 	for (cur = od->oscar_connections; cur != NULL; cur = cur->next)
  690: 	{
  691: 		FlapConnection *conn;
  692: 		conn = cur->data;
  693: 		if ((conn->type == type) && (conn->connected))
  694: 			return conn;
  695: 	}
  696: 
  697: 	return NULL;
  698: }
  699: 
  700: FlapConnection *
  701: flap_connection_getbytype_all(OscarData *od, int type)
  702: {
  703: 	GSList *cur;
  704: 
  705: 	for (cur = od->oscar_connections; cur; cur = cur->next)
  706: 	{
  707: 		FlapConnection *conn;
  708: 		conn = cur->data;
  709: 		if (conn->type == type)
  710: 			return conn;
  711: 	}
  712: 
  713: 	return NULL;
  714: }
  715: 
  716: /**
  717:  * Allocate a new FLAP frame.
  718:  *
  719:  * @param channel The FLAP channel.  This is almost always 2.
  720:  */
  721: FlapFrame *
  722: flap_frame_new(OscarData *od, guint16 channel, int datalen)
  723: {
  724: 	FlapFrame *frame;
  725: 
  726: 	frame = g_new0(FlapFrame, 1);
  727: 	frame->channel = channel;
  728: 
  729: 	if (datalen > 0)
  730: 		byte_stream_new(&frame->data, datalen);
  731: 
  732: 	return frame;
  733: }
  734: 
  735: static void
  736: parse_snac(OscarData *od, FlapConnection *conn, FlapFrame *frame)
  737: {
  738: 	aim_module_t *cur;
  739: 	aim_modsnac_t snac;
  740: 
  741: 	if (byte_stream_bytes_left(&frame->data) < 10)
  742: 		return;
  743: 
  744: 	snac.family = byte_stream_get16(&frame->data);
  745: 	snac.subtype = byte_stream_get16(&frame->data);
  746: 	snac.flags = byte_stream_get16(&frame->data);
  747: 	snac.id = byte_stream_get32(&frame->data);
  748: 
  749: 	/* SNAC flags are apparently uniform across all SNACs, so we handle them here */
  750: 	if (snac.flags & 0x0001) {
  751: 		/*
  752: 		 * This means the SNAC will be followed by another SNAC with
  753: 		 * related information.  We don't need to do anything about
  754: 		 * this here.
  755: 		 */
  756: 	}
  757: 	if (snac.flags & 0x8000) {
  758: 		/*
  759: 		 * This packet contains the version of the family that this SNAC is
  760: 		 * in.  You get this when your SSI module is version 2 or higher.
  761: 		 * For now we have no need for this, but you could always save
  762: 		 * it as a part of aim_modnsac_t, or something.  The format is...
  763: 		 * 2 byte length of total mini-header (which is 6 bytes), then TLV
  764: 		 * of  type 0x0001, length 0x0002, value is the 2 byte version
  765: 		 * number
  766: 		 */
  767: 		byte_stream_advance(&frame->data, byte_stream_get16(&frame->data));
  768: 	}
  769: 
  770: 	for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
  771: 
  772: 		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
  773: 				(cur->family != snac.family))
  774: 			continue;
  775: 
  776: 		if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
  777: 			return;
  778: 	}
  779: }
  780: 
  781: static void
  782: parse_fakesnac(OscarData *od, FlapConnection *conn, FlapFrame *frame, guint16 family, guint16 subtype)
  783: {
  784: 	aim_module_t *cur;
  785: 	aim_modsnac_t snac;
  786: 
  787: 	snac.family = family;
  788: 	snac.subtype = subtype;
  789: 	snac.flags = snac.id = 0;
  790: 
  791: 	for (cur = (aim_module_t *)od->modlistv; cur; cur = cur->next) {
  792: 
  793: 		if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) &&
  794: 				(cur->family != snac.family))
  795: 			continue;
  796: 
  797: 		if (cur->snachandler(od, conn, cur, frame, &snac, &frame->data))
  798: 			return;
  799: 	}
  800: }
  801: 
  802: static void
  803: parse_flap_ch4(OscarData *od, FlapConnection *conn, FlapFrame *frame)
  804: {
  805: 	GSList *tlvlist;
  806: 	char *msg = NULL;
  807: 
  808: 	if (byte_stream_bytes_left(&frame->data) == 0) {
  809: 		/* XXX should do something with this */
  810: 		return;
  811: 	}
  812: 
  813: 	/* An ICQ account is logging in */
  814: 	if (conn->type == SNAC_FAMILY_AUTH)
  815: 	{
  816: 		parse_fakesnac(od, conn, frame, 0x0017, 0x0003);
  817: 		return;
  818: 	}
  819: 
  820: 	tlvlist = aim_tlvlist_read(&frame->data);
  821: 
  822: 	if (aim_tlv_gettlv(tlvlist, 0x0009, 1))
  823: 		conn->disconnect_code = aim_tlv_get16(tlvlist, 0x0009, 1);
  824: 
  825: 	if (aim_tlv_gettlv(tlvlist, 0x000b, 1))
  826: 		msg = aim_tlv_getstr(tlvlist, 0x000b, 1);
  827: 
  828: 	/*
  829: 	 * The server ended this FLAP connnection, so let's be nice and
  830: 	 * close the physical TCP connection
  831: 	 */
  832: 	flap_connection_schedule_destroy(conn,
  833: 			OSCAR_DISCONNECT_REMOTE_CLOSED, msg);
  834: 
  835: 	aim_tlvlist_free(tlvlist);
  836: 
  837: 	g_free(msg);
  838: }
  839: 
  840: /**
  841:  * Takes a new incoming FLAP frame and sends it to the appropriate
  842:  * handler function to be parsed.
  843:  */
  844: static void
  845: parse_flap(OscarData *od, FlapConnection *conn, FlapFrame *frame)
  846: {
  847: 	if (frame->channel == 0x01) {
  848: 		guint32 flap_version = byte_stream_get32(&frame->data);
  849: 		if (flap_version != 0x00000001)
  850: 		{
  851: 				/* Error! */
  852: 				purple_debug_warning("oscar", "Expecting FLAP version "
  853: 					"0x00000001 but received FLAP version %08x.  Closing connection.\n",
  854: 					flap_version);
  855: 				flap_connection_schedule_destroy(conn,
  856: 						OSCAR_DISCONNECT_INVALID_DATA, NULL);
  857: 		}
  858: 		else
  859: 			conn->connected = TRUE;
  860: 
  861: 	} else if (frame->channel == 0x02) {
  862: 		parse_snac(od, conn, frame);
  863: 
  864: 	} else if (frame->channel == 0x04) {
  865: 		parse_flap_ch4(od, conn, frame);
  866: 
  867: 	} else if (frame->channel == 0x05) {
  868: 		/* TODO: Reset our keepalive watchdog? */
  869: 
  870: 	}
  871: }
  872: 
  873: /**
  874:  * Read in all available data on the socket for a given connection.
  875:  * All complete FLAPs handled immedate after they're received.
  876:  * Incomplete FLAP data is stored locally and appended to the next
  877:  * time this callback is triggered.
  878:  *
  879:  * This is called by flap_connection_recv_cb and
  880:  * flap_connection_recv_cb_ssl for unencrypted/encrypted connections.
  881:  */
  882: static void
  883: flap_connection_recv(FlapConnection *conn)
  884: {
  885: 	gpointer buf;
  886: 	gsize buflen;
  887: 	gssize read;
  888: 
  889: 	/* Read data until we run out of data and break out of the loop */
  890: 	while (TRUE)
  891: 	{
  892: 		/* Start reading a new FLAP */
  893: 		if (conn->buffer_incoming.data.data == NULL)
  894: 		{
  895: 			buf = conn->header + conn->header_received;
  896: 			buflen = 6 - conn->header_received;
  897: 
  898: 			/* Read the first 6 bytes (the FLAP header) */
  899: 			if (conn->gsc)
  900: 				read = purple_ssl_read(conn->gsc, buf, buflen);
  901: 			else
  902: 				read = recv(conn->fd, buf, buflen, 0);
  903: 
  904: 			/* Check if the FLAP server closed the connection */
  905: 			if (read == 0)
  906: 			{
  907: 				flap_connection_schedule_destroy(conn,
  908: 						OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
  909: 				break;
  910: 			}
  911: 
  912: 			/* If there was an error then close the connection */
  913: 			if (read < 0)
  914: 			{
  915: 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
  916: 					/* No worries */
  917: 					break;
  918: 
  919: 				/* Error! */
  920: 				flap_connection_schedule_destroy(conn,
  921: 						OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
  922: 				break;
  923: 			}
  924: 			conn->od->gc->last_received = time(NULL);
  925: 
  926: 			/* If we don't even have a complete FLAP header then do nothing */
  927: 			conn->header_received += read;
  928: 			if (conn->header_received < 6)
  929: 				break;
  930: 
  931: 			/* All FLAP frames must start with the byte 0x2a */
  932: 			if (aimutil_get8(&conn->header[0]) != 0x2a)
  933: 			{
  934: 				flap_connection_schedule_destroy(conn,
  935: 						OSCAR_DISCONNECT_INVALID_DATA, NULL);
  936: 				break;
  937: 			}
  938: 
  939: 			/* Initialize a new temporary FlapFrame for incoming data */
  940: 			conn->buffer_incoming.channel = aimutil_get8(&conn->header[1]);
  941: 			conn->buffer_incoming.seqnum = aimutil_get16(&conn->header[2]);
  942: 			conn->buffer_incoming.data.len = aimutil_get16(&conn->header[4]);
  943: 			conn->buffer_incoming.data.data = g_new(guint8, conn->buffer_incoming.data.len);
  944: 			conn->buffer_incoming.data.offset = 0;
  945: 		}
  946: 
  947: 		buflen = conn->buffer_incoming.data.len - conn->buffer_incoming.data.offset;
  948: 		if (buflen)
  949: 		{
  950: 			buf = &conn->buffer_incoming.data.data[conn->buffer_incoming.data.offset];
  951: 			/* Read data into the temporary FlapFrame until it is complete */
  952: 			if (conn->gsc)
  953: 				read = purple_ssl_read(conn->gsc, buf, buflen);
  954: 			else
  955: 				read = recv(conn->fd, buf, buflen, 0);
  956: 
  957: 			/* Check if the FLAP server closed the connection */
  958: 			if (read == 0)
  959: 			{
  960: 				flap_connection_schedule_destroy(conn,
  961: 						OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
  962: 				break;
  963: 			}
  964: 
  965: 			if (read < 0)
  966: 			{
  967: 				if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
  968: 					/* No worries */
  969: 					break;
  970: 
  971: 				/* Error! */
  972: 				flap_connection_schedule_destroy(conn,
  973: 						OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
  974: 				break;
  975: 			}
  976: 
  977: 			conn->buffer_incoming.data.offset += read;
  978: 			if (conn->buffer_incoming.data.offset < conn->buffer_incoming.data.len)
  979: 				/* Waiting for more data to arrive */
  980: 				break;
  981: 		}
  982: 
  983: 		/* We have a complete FLAP!  Handle it and continue reading */
  984: 		byte_stream_rewind(&conn->buffer_incoming.data);
  985: 		parse_flap(conn->od, conn, &conn->buffer_incoming);
  986: 		conn->lastactivity = time(NULL);
  987: 
  988: 		g_free(conn->buffer_incoming.data.data);
  989: 		conn->buffer_incoming.data.data = NULL;
  990: 
  991: 		conn->header_received = 0;
  992: 	}
  993: }
  994: 
  995: void
  996: flap_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
  997: {
  998: 	FlapConnection *conn = data;
  999: 
 1000: 	flap_connection_recv(conn);
 1001: }
 1002: 
 1003: void
 1004: flap_connection_recv_cb_ssl(gpointer data, PurpleSslConnection *gsc, PurpleInputCondition cond)
 1005: {
 1006: 	FlapConnection *conn = data;
 1007: 
 1008: 	flap_connection_recv(conn);
 1009: }
 1010: 
 1011: /**
 1012:  * @param source When this function is called as a callback source is
 1013:  *        set to the fd that triggered the callback.  But this function
 1014:  *        is also called directly from flap_connection_send_byte_stream(),
 1015:  *        in which case source will be -1.  So don't use source--use
 1016:  *        conn->gsc or conn->fd instead.
 1017:  */
 1018: static void
 1019: send_cb(gpointer data, gint source, PurpleInputCondition cond)
 1020: {
 1021: 	FlapConnection *conn;
 1022: 	int writelen, ret;
 1023: 
 1024: 	conn = data;
 1025: 	writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
 1026: 
 1027: 	if (writelen == 0)
 1028: 	{
 1029: 		purple_input_remove(conn->watcher_outgoing);
 1030: 		conn->watcher_outgoing = 0;
 1031: 		return;
 1032: 	}
 1033: 
 1034: 	if (conn->gsc)
 1035: 		ret = purple_ssl_write(conn->gsc, conn->buffer_outgoing->outptr,
 1036: 				writelen);
 1037: 	else
 1038: 		ret = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
 1039: 	if (ret <= 0)
 1040: 	{
 1041: 		if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK))
 1042: 			/* No worries */
 1043: 			return;
 1044: 
 1045: 		/* Error! */
 1046: 		purple_input_remove(conn->watcher_outgoing);
 1047: 		conn->watcher_outgoing = 0;
 1048: 		if (conn->gsc) {
 1049: 			purple_ssl_close(conn->gsc);
 1050: 			conn->gsc = NULL;
 1051: 		} else {
 1052: 			close(conn->fd);
 1053: 			conn->fd = -1;
 1054: 		}
 1055: 		flap_connection_schedule_destroy(conn,
 1056: 				OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
 1057: 		return;
 1058: 	}
 1059: 
 1060: 	purple_circ_buffer_mark_read(conn->buffer_outgoing, ret);
 1061: }
 1062: 
 1063: static void
 1064: flap_connection_send_byte_stream(ByteStream *bs, FlapConnection *conn, size_t count)
 1065: {
 1066: 	if (conn == NULL)
 1067: 		return;
 1068: 
 1069: 	/* Make sure we don't send past the end of the bs */
 1070: 	if (count > byte_stream_bytes_left(bs))
 1071: 		count = byte_stream_bytes_left(bs); /* truncate to remaining space */
 1072: 
 1073: 	if (count == 0)
 1074: 		return;
 1075: 
 1076: 	/* Add everything to our outgoing buffer */
 1077: 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, count);
 1078: 
 1079: 	/* If we haven't already started writing stuff, then start the cycle */
 1080: 	if (conn->watcher_outgoing == 0)
 1081: 	{
 1082: 		if (conn->gsc) {
 1083: 			conn->watcher_outgoing = purple_input_add(conn->gsc->fd,
 1084: 					PURPLE_INPUT_WRITE, send_cb, conn);
 1085: 			send_cb(conn, -1, 0);
 1086: 		} else if (conn->fd >= 0) {
 1087: 			conn->watcher_outgoing = purple_input_add(conn->fd,
 1088: 					PURPLE_INPUT_WRITE, send_cb, conn);
 1089: 			send_cb(conn, -1, 0);
 1090: 		}
 1091: 	}
 1092: }
 1093: 
 1094: static void
 1095: sendframe_flap(FlapConnection *conn, FlapFrame *frame)
 1096: {
 1097: 	ByteStream bs;
 1098: 	int payloadlen, bslen;
 1099: 
 1100: 	payloadlen = byte_stream_curpos(&frame->data);
 1101: 
 1102: 	byte_stream_new(&bs, 6 + payloadlen);
 1103: 
 1104: 	/* FLAP header */
 1105: 	byte_stream_put8(&bs, 0x2a);
 1106: 	byte_stream_put8(&bs, frame->channel);
 1107: 	byte_stream_put16(&bs, frame->seqnum);
 1108: 	byte_stream_put16(&bs, payloadlen);
 1109: 
 1110: 	/* Payload */
 1111: 	byte_stream_rewind(&frame->data);
 1112: 	byte_stream_putbs(&bs, &frame->data, payloadlen);
 1113: 
 1114: 	bslen = byte_stream_curpos(&bs);
 1115: 	byte_stream_rewind(&bs);
 1116: 	flap_connection_send_byte_stream(&bs, conn, bslen);
 1117: 
 1118: 	byte_stream_destroy(&bs);
 1119: }
 1120: 
 1121: void
 1122: flap_connection_send(FlapConnection *conn, FlapFrame *frame)
 1123: {
 1124: 	frame->seqnum = ++(conn->seqnum_out);
 1125: 	sendframe_flap(conn, frame);
 1126: 	flap_frame_destroy(frame);
 1127: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>