File:  [Coherent Logic Development] / ChivanetAimPidgin / oscarprpl / src / c / peer.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: /*
   22:  * Functions dealing with peer connections.  This includes the code
   23:  * used to establish a peer connection for both Oscar File transfer
   24:  * (OFT) and Oscar Direct Connect (ODC).  (ODC is also referred to
   25:  * as DirectIM and IM Image.)
   26:  */
   27: 
   28: #ifdef HAVE_CONFIG_H
   29: #include  <config.h>
   30: #endif
   31: 
   32: /* From the oscar PRPL */
   33: #include "oscar.h"
   34: #include "peer.h"
   35: 
   36: /* From Purple */
   37: #include "conversation.h"
   38: #include "ft.h"
   39: #include "network.h"
   40: #include "notify.h"
   41: #include "request.h"
   42: #include "util.h"
   43: 
   44: #ifndef _WIN32
   45: #include <stdio.h>
   46: #include <netdb.h>
   47: #include <sys/socket.h>
   48: #include <netinet/in.h>
   49: #include <arpa/inet.h> /* for inet_ntoa */
   50: #include <limits.h> /* for UINT_MAX */
   51: #endif
   52: 
   53: #ifdef _WIN32
   54: #include "win32dep.h"
   55: #endif
   56: 
   57: /*
   58:  * I really want to switch all our networking code to using IPv6 only,
   59:  * but that really isn't a good idea at all.  Evan S. of Adium says
   60:  * OS X sets all connections as "AF_INET6/PF_INET6," even if there is
   61:  * nothing inherently IPv6 about them.  And I feel like Linux kernel
   62:  * 2.6.5 is doing the same thing.  So we REALLY should accept
   63:  * connections if they're showing up as IPv6.  Old OSes (Solaris?)
   64:  * that might not have full IPv6 support yet will fail if we try
   65:  * to use PF_INET6 but it isn't defined.  --Mark Doliner
   66:  */
   67: #ifndef PF_INET6
   68: #define PF_INET6 PF_INET
   69: #endif
   70: 
   71: PeerConnection *
   72: peer_connection_find_by_type(OscarData *od, const char *bn, guint64 type)
   73: {
   74: 	GSList *cur;
   75: 	PeerConnection *conn;
   76: 
   77: 	for (cur = od->peer_connections; cur != NULL; cur = cur->next)
   78: 	{
   79: 		conn = cur->data;
   80: 		if ((conn->type == type) && !oscar_util_name_compare(conn->bn, bn))
   81: 			return conn;
   82: 	}
   83: 
   84: 	return NULL;
   85: }
   86: 
   87: /**
   88:  * @param cookie This must be exactly 8 characters.
   89:  */
   90: PeerConnection *
   91: peer_connection_find_by_cookie(OscarData *od, const char *bn, const guchar *cookie)
   92: {
   93: 	GSList *cur;
   94: 	PeerConnection *conn;
   95: 
   96: 	for (cur = od->peer_connections; cur != NULL; cur = cur->next)
   97: 	{
   98: 		conn = cur->data;
   99: 		if (!memcmp(conn->cookie, cookie, 8) && !oscar_util_name_compare(conn->bn, bn))
  100: 			return conn;
  101: 	}
  102: 
  103: 	return NULL;
  104: }
  105: 
  106: PeerConnection *
  107: peer_connection_new(OscarData *od, guint64 type, const char *bn)
  108: {
  109: 	PeerConnection *conn;
  110: 	PurpleAccount *account;
  111: 
  112: 	account = purple_connection_get_account(od->gc);
  113: 
  114: 	conn = g_new0(PeerConnection, 1);
  115: 	conn->od = od;
  116: 	conn->type = type;
  117: 	conn->bn = g_strdup(bn);
  118: 	conn->buffer_outgoing = purple_circ_buffer_new(0);
  119: 	conn->listenerfd = -1;
  120: 	conn->fd = -1;
  121: 	conn->lastactivity = time(NULL);
  122: 	conn->use_proxy |= purple_account_get_bool(account, "always_use_rv_proxy", FALSE);
  123: 
  124: 	if (type == OSCAR_CAPABILITY_DIRECTIM)
  125: 		memcpy(conn->magic, "ODC2", 4);
  126: 	else if (type == OSCAR_CAPABILITY_SENDFILE)
  127: 		memcpy(conn->magic, "OFT2", 4);
  128: 
  129: 	od->peer_connections = g_slist_prepend(od->peer_connections, conn);
  130: 
  131: 	return conn;
  132: }
  133: 
  134: static void
  135: peer_connection_close(PeerConnection *conn)
  136: {
  137: 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  138: 		peer_odc_close(conn);
  139: 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
  140: 		peer_oft_close(conn);
  141: 
  142: 	if (conn->verified_connect_data != NULL)
  143: 	{
  144: 		purple_proxy_connect_cancel(conn->verified_connect_data);
  145: 		conn->verified_connect_data = NULL;
  146: 	}
  147: 
  148: 	if (conn->client_connect_data != NULL)
  149: 	{
  150: 		purple_proxy_connect_cancel(conn->client_connect_data);
  151: 		conn->client_connect_data = NULL;
  152: 	}
  153: 
  154: 	if (conn->listen_data != NULL)
  155: 	{
  156: 		purple_network_listen_cancel(conn->listen_data);
  157: 		conn->listen_data = NULL;
  158: 	}
  159: 
  160: 	if (conn->connect_timeout_timer != 0)
  161: 	{
  162: 		purple_timeout_remove(conn->connect_timeout_timer);
  163: 		conn->connect_timeout_timer = 0;
  164: 	}
  165: 
  166: 	if (conn->watcher_incoming != 0)
  167: 	{
  168: 		purple_input_remove(conn->watcher_incoming);
  169: 		conn->watcher_incoming = 0;
  170: 	}
  171: 	if (conn->watcher_outgoing != 0)
  172: 	{
  173: 		purple_input_remove(conn->watcher_outgoing);
  174: 		conn->watcher_outgoing = 0;
  175: 	}
  176: 	if (conn->listenerfd >= 0)
  177: 	{
  178: 		close(conn->listenerfd);
  179: 		conn->listenerfd = -1;
  180: 	}
  181: 	if (conn->fd >= 0)
  182: 	{
  183: 		close(conn->fd);
  184: 		conn->fd = -1;
  185: 	}
  186: 
  187: 	g_free(conn->buffer_incoming.data);
  188: 	conn->buffer_incoming.data = NULL;
  189: 	conn->buffer_incoming.len = 0;
  190: 	conn->buffer_incoming.offset = 0;
  191: 
  192: 	purple_circ_buffer_destroy(conn->buffer_outgoing);
  193: 	conn->buffer_outgoing = purple_circ_buffer_new(0);
  194: 
  195: 	conn->flags &= ~PEER_CONNECTION_FLAG_IS_INCOMING;
  196: }
  197: 
  198: static gboolean
  199: peer_connection_destroy_cb(gpointer data)
  200: {
  201: 	PeerConnection *conn;
  202: 
  203: 	conn = data;
  204: 
  205: 	purple_request_close_with_handle(conn);
  206: 
  207: 	peer_connection_close(conn);
  208: 
  209: 	if (conn->checksum_data != NULL)
  210: 		peer_oft_checksum_destroy(conn->checksum_data);
  211: 
  212: 	if (conn->xfer != NULL)
  213: 	{
  214: 		PurpleXferStatusType status;
  215: 		conn->xfer->data = NULL;
  216: 		status = purple_xfer_get_status(conn->xfer);
  217: 		if ((status != PURPLE_XFER_STATUS_DONE) &&
  218: 			(status != PURPLE_XFER_STATUS_CANCEL_LOCAL) &&
  219: 			(status != PURPLE_XFER_STATUS_CANCEL_REMOTE))
  220: 		{
  221: 			if ((conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_CLOSED) ||
  222: 				(conn->disconnect_reason == OSCAR_DISCONNECT_REMOTE_REFUSED))
  223: 				purple_xfer_cancel_remote(conn->xfer);
  224: 			else
  225: 				purple_xfer_cancel_local(conn->xfer);
  226: 		}
  227: 		purple_xfer_unref(conn->xfer);
  228: 		conn->xfer = NULL;
  229: 	}
  230: 
  231: 	g_free(conn->bn);
  232: 	g_free(conn->error_message);
  233: 	g_free(conn->proxyip);
  234: 	g_free(conn->clientip);
  235: 	g_free(conn->verifiedip);
  236: 	g_free(conn->xferdata.name);
  237: 	purple_circ_buffer_destroy(conn->buffer_outgoing);
  238: 
  239: 	conn->od->peer_connections = g_slist_remove(conn->od->peer_connections, conn);
  240: 
  241: 	g_free(conn);
  242: 
  243: 	return FALSE;
  244: }
  245: 
  246: void
  247: peer_connection_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
  248: {
  249: 	if (conn->destroy_timeout != 0)
  250: 		purple_timeout_remove(conn->destroy_timeout);
  251: 	conn->disconnect_reason = reason;
  252: 	g_free(conn->error_message);
  253: 	conn->error_message = g_strdup(error_message);
  254: 	peer_connection_destroy_cb(conn);
  255: }
  256: 
  257: void
  258: peer_connection_schedule_destroy(PeerConnection *conn, OscarDisconnectReason reason, const gchar *error_message)
  259: {
  260: 	if (conn->destroy_timeout != 0)
  261: 		/* Already taken care of */
  262: 		return;
  263: 
  264: 	purple_debug_info("oscar", "Scheduling destruction of peer connection\n");
  265: 	conn->disconnect_reason = reason;
  266: 	g_free(conn->error_message);
  267: 	conn->error_message = g_strdup(error_message);
  268: 	conn->destroy_timeout = purple_timeout_add(0, peer_connection_destroy_cb, conn);
  269: }
  270: 
  271: /*******************************************************************/
  272: /* Begin code for receiving data on a peer connection                */
  273: /*******************************************************************/
  274: 
  275: /**
  276:  * This should be used to read ODC and OFT framing info.  It should
  277:  * NOT be used to read the payload sent across the connection (IMs,
  278:  * file data, etc), and it should NOT be used to read proxy negotiation
  279:  * headers.
  280:  *
  281:  * Unlike flap_connection_recv_cb(), this only reads one frame at a
  282:  * time.  This is done so that the watcher can be changed during the
  283:  * handling of the frame.  If the watcher is changed then this
  284:  * function will not read in any more data.  This happens when
  285:  * reading the payload of a direct IM frame, or when we're
  286:  * receiving a file from the remote user.  Once the data has been
  287:  * read, the watcher will be switched back to this function to
  288:  * continue reading the next frame.
  289:  */
  290: void
  291: peer_connection_recv_cb(gpointer data, gint source, PurpleInputCondition cond)
  292: {
  293: 	PeerConnection *conn;
  294: 	gssize read;
  295: 
  296: 	conn = data;
  297: 
  298: 	/* Start reading a new ODC/OFT frame */
  299: 	if (conn->buffer_incoming.data == NULL)
  300: 	{
  301: 		/* Read the first 6 bytes (magic string and frame length) */
  302: 		read = recv(conn->fd, conn->header + conn->header_received,
  303: 				6 - conn->header_received, 0);
  304: 
  305: 		/* Check if the remote user closed the connection */
  306: 		if (read == 0)
  307: 		{
  308: 			peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
  309: 			return;
  310: 		}
  311: 
  312: 		/* If there was an error then close the connection */
  313: 		if (read < 0)
  314: 		{
  315: 			if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
  316: 				/* No worries */
  317: 				return;
  318: 
  319: 			peer_connection_destroy(conn,
  320: 					OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
  321: 			return;
  322: 		}
  323: 
  324: 		conn->lastactivity = time(NULL);
  325: 
  326: 		/* If we don't even have the first 6 bytes then do nothing */
  327: 		conn->header_received += read;
  328: 		if (conn->header_received < 6)
  329: 			return;
  330: 
  331: 		/* All ODC/OFT frames must start with a magic string */
  332: 		if (memcmp(conn->magic, conn->header, 4))
  333: 		{
  334: 			purple_debug_warning("oscar", "Expecting magic string to "
  335: 				"be %c%c%c%c but received magic string %c%c%c%c.  "
  336: 				"Closing connection.\n",
  337: 				conn->magic[0], conn->magic[1], conn->magic[2],
  338: 				conn->magic[3], conn->header[0], conn->header[1],
  339: 				conn->header[2], conn->header[3]);
  340: 			peer_connection_destroy(conn, OSCAR_DISCONNECT_INVALID_DATA, NULL);
  341: 			return;
  342: 		}
  343: 
  344: 		/* Initialize a new temporary ByteStream for incoming data */
  345: 		conn->buffer_incoming.len = aimutil_get16(&conn->header[4]) - 6;
  346: 		conn->buffer_incoming.data = g_new(guint8, conn->buffer_incoming.len);
  347: 		conn->buffer_incoming.offset = 0;
  348: 	}
  349: 
  350: 	/* Read data into the temporary buffer until it is complete */
  351: 	read = recv(conn->fd,
  352: 				&conn->buffer_incoming.data[conn->buffer_incoming.offset],
  353: 				conn->buffer_incoming.len - conn->buffer_incoming.offset,
  354: 				0);
  355: 
  356: 	/* Check if the remote user closed the connection */
  357: 	if (read == 0)
  358: 	{
  359: 		peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
  360: 		return;
  361: 	}
  362: 
  363: 	if (read < 0)
  364: 	{
  365: 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
  366: 			/* No worries */
  367: 			return;
  368: 
  369: 		peer_connection_destroy(conn,
  370: 				OSCAR_DISCONNECT_LOST_CONNECTION, g_strerror(errno));
  371: 		return;
  372: 	}
  373: 
  374: 	conn->lastactivity = time(NULL);
  375: 	conn->buffer_incoming.offset += read;
  376: 	if (conn->buffer_incoming.offset < conn->buffer_incoming.len)
  377: 		/* Waiting for more data to arrive */
  378: 		return;
  379: 
  380: 	/* We have a complete ODC/OFT frame!  Handle it and continue reading */
  381: 	byte_stream_rewind(&conn->buffer_incoming);
  382: 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  383: 	{
  384: 		peer_odc_recv_frame(conn, &conn->buffer_incoming);
  385: 	}
  386: 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
  387: 	{
  388: 		peer_oft_recv_frame(conn, &conn->buffer_incoming);
  389: 	}
  390: 
  391: 	g_free(conn->buffer_incoming.data);
  392: 	conn->buffer_incoming.data = NULL;
  393: 
  394: 	conn->header_received = 0;
  395: }
  396: 
  397: /*******************************************************************/
  398: /* End code for receiving data on a peer connection                */
  399: /*******************************************************************/
  400: 
  401: /*******************************************************************/
  402: /* Begin code for sending data on a peer connection                */
  403: /*******************************************************************/
  404: 
  405: static void
  406: send_cb(gpointer data, gint source, PurpleInputCondition cond)
  407: {
  408: 	PeerConnection *conn;
  409: 	gsize writelen;
  410: 	gssize wrotelen;
  411: 
  412: 	conn = data;
  413: 	writelen = purple_circ_buffer_get_max_read(conn->buffer_outgoing);
  414: 
  415: 	if (writelen == 0)
  416: 	{
  417: 		purple_input_remove(conn->watcher_outgoing);
  418: 		conn->watcher_outgoing = 0;
  419: 		/*
  420: 		 * The buffer is currently empty, so reset the current input
  421: 		 * and output positions to the start of the buffer.  We do
  422: 		 * this so that the next chunk of data that we put into the
  423: 		 * buffer can be read back out of the buffer in one fell swoop.
  424: 		 * Otherwise it gets fragmented and we have to read from the
  425: 		 * second half of the buffer than go back and read the rest of
  426: 		 * the chunk from the first half.
  427: 		 *
  428: 		 * We're using TCP, which is a stream based protocol, so this
  429: 		 * isn't supposed to matter.  However, experience has shown
  430: 		 * that at least the proxy file transfer code in AIM 6.1.41.2
  431: 		 * requires that the entire OFT frame arrive all at once.  If
  432: 		 * the frame is fragmented then AIM freaks out and aborts the
  433: 		 * file transfer.  Somebody should teach those guys how to
  434: 		 * write good TCP code.
  435: 		 */
  436: 		conn->buffer_outgoing->inptr = conn->buffer_outgoing->buffer;
  437: 		conn->buffer_outgoing->outptr = conn->buffer_outgoing->buffer;
  438: 		return;
  439: 	}
  440: 
  441: 	wrotelen = send(conn->fd, conn->buffer_outgoing->outptr, writelen, 0);
  442: 	if (wrotelen <= 0)
  443: 	{
  444: 		if (wrotelen < 0 && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
  445: 			/* No worries */
  446: 			return;
  447: 
  448: 		if (conn->ready)
  449: 		{
  450: 			purple_input_remove(conn->watcher_outgoing);
  451: 			conn->watcher_outgoing = 0;
  452: 			close(conn->fd);
  453: 			conn->fd = -1;
  454: 			peer_connection_schedule_destroy(conn,
  455: 					OSCAR_DISCONNECT_LOST_CONNECTION, NULL);
  456: 		}
  457: 		else
  458: 		{
  459: 			/*
  460: 			 * This could happen when unable to send a negotiation
  461: 			 * frame to a peer proxy server.
  462: 			 */
  463: 			peer_connection_trynext(conn);
  464: 		}
  465: 		return;
  466: 	}
  467: 
  468: 	purple_circ_buffer_mark_read(conn->buffer_outgoing, wrotelen);
  469: 	conn->lastactivity = time(NULL);
  470: }
  471: 
  472: /**
  473:  * This should be called by OFT/ODC code to send a standard OFT or ODC
  474:  * frame across the peer connection along with some payload data.  Or
  475:  * maybe a file.  Anything, really.
  476:  */
  477: void
  478: peer_connection_send(PeerConnection *conn, ByteStream *bs)
  479: {
  480: 	/* Add everything to our outgoing buffer */
  481: 	purple_circ_buffer_append(conn->buffer_outgoing, bs->data, bs->len);
  482: 
  483: 	/* If we haven't already started writing stuff, then start the cycle */
  484: 	if ((conn->watcher_outgoing == 0) && (conn->fd >= 0))
  485: 	{
  486: 		conn->watcher_outgoing = purple_input_add(conn->fd,
  487: 				PURPLE_INPUT_WRITE, send_cb, conn);
  488: 		send_cb(conn, conn->fd, 0);
  489: 	}
  490: }
  491: 
  492: /*******************************************************************/
  493: /* End code for sending data on a peer connection                  */
  494: /*******************************************************************/
  495: 
  496: /*******************************************************************/
  497: /* Begin code for establishing a peer connection                   */
  498: /*******************************************************************/
  499: 
  500: void
  501: peer_connection_finalize_connection(PeerConnection *conn)
  502: {
  503: 	conn->watcher_incoming = purple_input_add(conn->fd,
  504: 			PURPLE_INPUT_READ, peer_connection_recv_cb, conn);
  505: 
  506: 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  507: 	{
  508: 		/*
  509: 		 * If we are connecting to them then send our cookie so they
  510: 		 * can verify who we are.  Note: This doesn't seem to be
  511: 		 * necessary, but it also doesn't seem to hurt.
  512: 		 */
  513: 		if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
  514: 			peer_odc_send_cookie(conn);
  515: 	}
  516: 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
  517: 	{
  518: 		if (purple_xfer_get_type(conn->xfer) == PURPLE_XFER_SEND)
  519: 		{
  520: 			peer_oft_send_prompt(conn);
  521: 		}
  522: 	}
  523: 
  524: 	/*
  525: 	 * Tell the remote user that we're connected (which may also imply
  526: 	 * that we've accepted their request).
  527: 	 */
  528: 	if (!(conn->flags & PEER_CONNECTION_FLAG_IS_INCOMING))
  529: 		aim_im_sendch2_connected(conn);
  530: }
  531: 
  532: /**
  533:  * We tried to make an outgoing connection to a remote user.  It
  534:  * either connected or failed to connect.
  535:  */
  536: static void
  537: peer_connection_common_established_cb(gpointer data, gint source, const gchar *error_message, gboolean verified)
  538: {
  539: 	PeerConnection *conn;
  540: 
  541: 	conn = data;
  542: 
  543: 	if (verified)
  544: 		conn->verified_connect_data = NULL;
  545: 	else
  546: 		conn->client_connect_data = NULL;
  547: 
  548: 	if (source < 0)
  549: 	{
  550: 		if ((conn->verified_connect_data == NULL) &&
  551: 			(conn->client_connect_data == NULL))
  552: 		{
  553: 			/* Our parallel connection attemps have both failed. */
  554: 			peer_connection_trynext(conn);
  555: 		}
  556: 		return;
  557: 	}
  558: 
  559: 	purple_timeout_remove(conn->connect_timeout_timer);
  560: 	conn->connect_timeout_timer = 0;
  561: 
  562: 	if (conn->client_connect_data != NULL)
  563: 	{
  564: 		purple_proxy_connect_cancel(conn->client_connect_data);
  565: 		conn->client_connect_data = NULL;
  566: 	}
  567: 
  568: 	if (conn->verified_connect_data != NULL)
  569: 	{
  570: 		purple_proxy_connect_cancel(conn->verified_connect_data);
  571: 		conn->verified_connect_data = NULL;
  572: 	}
  573: 
  574: 	conn->fd = source;
  575: 
  576: 	peer_connection_finalize_connection(conn);
  577: }
  578: 
  579: static void
  580: peer_connection_verified_established_cb(gpointer data, gint source, const gchar *error_message)
  581: {
  582: 	peer_connection_common_established_cb(data, source, error_message, TRUE);
  583: }
  584: 
  585: static void
  586: peer_connection_client_established_cb(gpointer data, gint source, const gchar *error_message)
  587: {
  588: 	peer_connection_common_established_cb(data, source, error_message, FALSE);
  589: }
  590: 
  591: /**
  592:  * This is the watcher callback for any listening socket that is
  593:  * waiting for a peer to connect.  When a peer connects we set the
  594:  * input watcher to start reading data from the peer.
  595:  *
  596:  * To make sure that the connection is with the intended person and
  597:  * not with a malicious middle man, we don't send anything until we've
  598:  * received a peer frame from the remote user and have verified that
  599:  * the cookie in the peer frame matches the cookie that was exchanged
  600:  * in the channel 2 ICBM.
  601:  */
  602: void
  603: peer_connection_listen_cb(gpointer data, gint source, PurpleInputCondition cond)
  604: {
  605: 	PeerConnection *conn;
  606: 	struct sockaddr addr;
  607: 	socklen_t addrlen = sizeof(addr);
  608: 
  609: 	conn = data;
  610: 
  611: 	purple_debug_info("oscar", "Accepting connection on listener socket.\n");
  612: 
  613: 	conn->fd = accept(conn->listenerfd, &addr, &addrlen);
  614: 	if (conn->fd < 0)
  615: 	{
  616: 		if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
  617: 			/* No connection yet--no worries */
  618: 			/* TODO: Hmm, but they SHOULD be connected if we're here, right? */
  619: 			return;
  620: 
  621: 		peer_connection_trynext(conn);
  622: 		return;
  623: 	}
  624: 
  625: 	if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6))
  626: 	{
  627: 		/* Invalid connection type?!  Continue waiting. */
  628: 		close(conn->fd);
  629: 		return;
  630: 	}
  631: 
  632: 	//_purple_network_set_common_socket_flags(conn->fd); // TODO: Not public? Check
  633: 
  634: 	purple_input_remove(conn->watcher_incoming);
  635: 
  636: 	peer_connection_finalize_connection(conn);
  637: }
  638: 
  639: /**
  640:  * We've just opened a listener socket, so we send the remote
  641:  * user an ICBM and ask them to connect to us.
  642:  */
  643: static void
  644: peer_connection_establish_listener_cb(int listenerfd, gpointer data)
  645: {
  646: 	PeerConnection *conn;
  647: 	OscarData *od;
  648: 	PurpleConnection *gc;
  649: 	PurpleAccount *account;
  650: 	PurpleConversation *conv;
  651: 	char *tmp;
  652: 	FlapConnection *bos_conn;
  653: 	const char *listener_ip;
  654: 	const guchar *ip_atoi;
  655: 	unsigned short listener_port;
  656: 
  657: 	conn = data;
  658: 	conn->listen_data = NULL;
  659: 
  660: 	if (listenerfd < 0)
  661: 	{
  662: 		/* Could not open listener socket */
  663: 		peer_connection_trynext(conn);
  664: 		return;
  665: 	}
  666: 
  667: 	od = conn->od;
  668: 	gc = od->gc;
  669: 	account = purple_connection_get_account(gc);
  670: 	conn->listenerfd = listenerfd;
  671: 
  672: 	/* Watch for new connections on our listener socket */
  673: 	conn->watcher_incoming = purple_input_add(conn->listenerfd,
  674: 			PURPLE_INPUT_READ, peer_connection_listen_cb, conn);
  675: 
  676: 	/* Send the "please connect to me!" ICBM */
  677: 	bos_conn = flap_connection_findbygroup(od, SNAC_FAMILY_ICBM);
  678: 	if (bos_conn == NULL)
  679: 	{
  680: 		/* Not good */
  681: 		peer_connection_trynext(conn);
  682: 		return;
  683: 	}
  684: 
  685: 	if (bos_conn->gsc)
  686: 		listener_ip = purple_network_get_my_ip(bos_conn->gsc->fd);
  687: 	else
  688: 		listener_ip = purple_network_get_my_ip(bos_conn->fd);
  689: 
  690: 	ip_atoi = purple_network_ip_atoi(listener_ip);
  691: 	if (ip_atoi == NULL) {
  692: 		/* Could not convert IP to 4 byte array--weird, but this does
  693: 		   happen for some users (#4829, Adium #15839).  Maybe they're
  694: 		   connecting with IPv6...?  Maybe through a proxy? */
  695: 		purple_debug_error("oscar", "Can't ask peer to connect to us "
  696: 				"because purple_network_ip_atoi(%s) returned NULL. "
  697: 				"fd=%d. is_ssl=%d\n",
  698: 				listener_ip ? listener_ip : "(null)",
  699: 				bos_conn->gsc ? bos_conn->gsc->fd : bos_conn->fd,
  700: 				bos_conn->gsc ? 1 : 0);
  701: 		peer_connection_trynext(conn);
  702: 		return;
  703: 	}
  704: 
  705: 	listener_port = purple_network_get_port_from_fd(conn->listenerfd);
  706: 
  707: 	if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  708: 	{
  709: 		aim_im_sendch2_odc_requestdirect(od,
  710: 				conn->cookie, conn->bn, ip_atoi,
  711: 				listener_port, ++conn->lastrequestnumber);
  712: 
  713: 		/* Print a message to a local conversation window */
  714: 		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
  715: 		tmp = g_strdup_printf(_("Asking %s to connect to us at %s:%hu for "
  716: 				"Direct IM."), conn->bn, listener_ip, listener_port);
  717: 		purple_conversation_write(conv, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL));
  718: 		g_free(tmp);
  719: 	}
  720: 	else if (conn->type == OSCAR_CAPABILITY_SENDFILE)
  721: 	{
  722: 		aim_im_sendch2_sendfile_requestdirect(od,
  723: 				conn->cookie, conn->bn,
  724: 				ip_atoi,
  725: 				listener_port, ++conn->lastrequestnumber,
  726: 				(const gchar *)conn->xferdata.name,
  727: 				conn->xferdata.size, conn->xferdata.totfiles);
  728: 	}
  729: }
  730: 
  731: /**
  732:  * This is a callback function used when we're connecting to a peer
  733:  * using either the client IP or the verified IP and the connection
  734:  * took longer than 5 seconds to complete.  We do this because
  735:  * waiting for the OS to time out the connection attempt is not
  736:  * practical--the default timeout on many OSes can be 3 minutes or
  737:  * more, and users are impatient.
  738:  *
  739:  * Worst case scenario: the user is connected to the Internet using
  740:  * a modem with severe lag.  The peer connections fail and Purple falls
  741:  * back to using a proxied connection.  The lower bandwidth
  742:  * limitations imposed by the proxied connection won't matter because
  743:  * the user is using a modem.
  744:  *
  745:  * I suppose this line of thinking is discriminatory against people
  746:  * with very high lag but decent throughput who are transferring
  747:  * large files.  But we don't care about those people.
  748:  *
  749:  * I (Sean) changed the timeout from 15 to 5 seconds, as 60 seconds is
  750:  * too long for a user to wait to send a file. I'm also parallelizing
  751:  * requests when possible. The longest we should have to wait now is 10
  752:  * seconds. We shouldn't make it shorter than this.
  753:  */
  754: static gboolean
  755: peer_connection_tooktoolong(gpointer data)
  756: {
  757: 	PeerConnection *conn;
  758: 
  759: 	conn = data;
  760: 
  761: 	purple_debug_info("oscar", "Peer connection timed out after 5 seconds.  "
  762: 			"Trying next method...\n");
  763: 
  764: 	peer_connection_trynext(conn);
  765: 
  766: 	/* Cancel this timer.  It'll be added again, if needed. */
  767: 	return FALSE;
  768: }
  769: 
  770: /**
  771:  * Try to establish the given PeerConnection using a defined
  772:  * sequence of steps.
  773:  */
  774: void
  775: peer_connection_trynext(PeerConnection *conn)
  776: {
  777: 	PurpleAccount *account;
  778: 
  779: 	account = purple_connection_get_account(conn->od->gc);
  780: 
  781: 	/*
  782: 	 * Close any remnants of a previous failed connection attempt.
  783: 	 */
  784: 	peer_connection_close(conn);
  785: 
  786: 	/*
  787: 	 * 1. Attempt to connect to the remote user using their verifiedip and clientip.
  788: 	 *    We try these at the same time and use whichever succeeds first, so we don't
  789: 	 *    have to wait for a timeout.
  790: 	 */
  791: 	if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_DIRECT) &&
  792: 		(conn->verifiedip != NULL) && (conn->port != 0) && (!conn->use_proxy))
  793: 	{
  794: 		conn->flags |= PEER_CONNECTION_FLAG_TRIED_DIRECT;
  795: 
  796: 		if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  797: 		{
  798: 			gchar *tmp;
  799: 			PurpleConversation *conv;
  800: 			tmp = g_strdup_printf(_("Attempting to connect to %s:%hu."),
  801: 					conn->verifiedip, conn->port);
  802: 			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
  803: 			purple_conversation_write(conv, NULL, tmp,
  804: 					PURPLE_MESSAGE_SYSTEM, time(NULL));
  805: 			g_free(tmp);
  806: 		}
  807: 
  808: 		conn->verified_connect_data = purple_proxy_connect(NULL, account,
  809: 				conn->verifiedip, conn->port,
  810: 				peer_connection_verified_established_cb, conn);
  811: 
  812: 		if ((conn->verifiedip == NULL) ||
  813: 			!purple_strequal(conn->verifiedip, conn->clientip))
  814: 		{
  815: 			conn->client_connect_data = purple_proxy_connect(NULL, account,
  816: 					conn->clientip, conn->port,
  817: 					peer_connection_client_established_cb, conn);
  818: 		}
  819: 
  820: 		if ((conn->verified_connect_data != NULL) ||
  821: 			(conn->client_connect_data != NULL))
  822: 		{
  823: 			/* Connecting... */
  824: 			conn->connect_timeout_timer = purple_timeout_add_seconds(5,
  825: 					peer_connection_tooktoolong, conn);
  826: 			return;
  827: 		}
  828: 	}
  829: 
  830: 	/*
  831: 	 * 2. Attempt to have the remote user connect to us (using both
  832: 	 *    our verifiedip and our clientip).
  833: 	 */
  834: 	if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_INCOMING) &&
  835: 		(!conn->use_proxy))
  836: 	{
  837: 		conn->flags |= PEER_CONNECTION_FLAG_TRIED_INCOMING;
  838: 
  839: 		/*
  840: 		 * Remote user is connecting to us, so we'll need to verify
  841: 		 * that the user who connected is our friend.
  842: 		 */
  843: 		conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
  844: 
  845: 		conn->listen_data = purple_network_listen_range(5190, 5290, SOCK_STREAM,
  846: 				peer_connection_establish_listener_cb, conn);
  847: 		if (conn->listen_data != NULL)
  848: 		{
  849: 			/* Opening listener socket... */
  850: 			return;
  851: 		}
  852: 	}
  853: 
  854: 	/*
  855: 	 * 3. Attempt to have both users connect to an intermediate proxy
  856: 	 *    server.
  857: 	 */
  858: 	if (!(conn->flags & PEER_CONNECTION_FLAG_TRIED_PROXY))
  859: 	{
  860: 		conn->flags |= PEER_CONNECTION_FLAG_TRIED_PROXY;
  861: 
  862: 		/*
  863: 		 * If we initiate the proxy connection, then the remote user
  864: 		 * could be anyone, so we need to verify that the user who
  865: 		 * connected is our friend.
  866: 		 */
  867: 		if (!conn->use_proxy)
  868: 			conn->flags |= PEER_CONNECTION_FLAG_IS_INCOMING;
  869: 
  870: 		if (conn->type == OSCAR_CAPABILITY_DIRECTIM)
  871: 		{
  872: 			gchar *tmp;
  873: 			PurpleConversation *conv;
  874: 			tmp = g_strdup(_("Attempting to connect via proxy server."));
  875: 			conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, conn->bn);
  876: 			purple_conversation_write(conv, NULL, tmp,
  877: 					PURPLE_MESSAGE_SYSTEM, time(NULL));
  878: 			g_free(tmp);
  879: 		}
  880: 
  881: 		conn->verified_connect_data = purple_proxy_connect(NULL, account,
  882: 				(conn->proxyip != NULL)
  883: 					? conn->proxyip
  884: 					: (conn->od->icq ? ICQ_PEER_PROXY_SERVER : AIM_PEER_PROXY_SERVER),
  885: 				PEER_PROXY_PORT,
  886: 				peer_proxy_connection_established_cb, conn);
  887: 		if (conn->verified_connect_data != NULL)
  888: 		{
  889: 			/* Connecting... */
  890: 			return;
  891: 		}
  892: 	}
  893: 
  894: 	/* Give up! */
  895: 	peer_connection_destroy(conn, OSCAR_DISCONNECT_COULD_NOT_CONNECT, NULL);
  896: }
  897: 
  898: /**
  899:  * Initiate a peer connection with someone.
  900:  */
  901: void
  902: peer_connection_propose(OscarData *od, guint64 type, const char *bn)
  903: {
  904: 	PeerConnection *conn;
  905: 
  906: 	if (type == OSCAR_CAPABILITY_DIRECTIM)
  907: 	{
  908: 		conn = peer_connection_find_by_type(od, bn, type);
  909: 		if (conn != NULL)
  910: 		{
  911: 			if (conn->ready)
  912: 			{
  913: 				PurpleAccount *account;
  914: 				PurpleConversation *conv;
  915: 
  916: 				purple_debug_info("oscar", "Already have a direct IM "
  917: 						"session with %s.\n", bn);
  918: 				account = purple_connection_get_account(od->gc);
  919: 				conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
  920: 						bn, account);
  921: 				if (conv != NULL)
  922: 					purple_conversation_present(conv);
  923: 				return;
  924: 			}
  925: 
  926: 			/* Cancel the old connection and try again */
  927: 			peer_connection_destroy(conn, OSCAR_DISCONNECT_RETRYING, NULL);
  928: 		}
  929: 	}
  930: 
  931: 	conn = peer_connection_new(od, type, bn);
  932: 	conn->flags |= PEER_CONNECTION_FLAG_INITIATED_BY_ME;
  933: 	conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
  934: 	aim_icbm_makecookie(conn->cookie);
  935: 
  936: 	peer_connection_trynext(conn);
  937: }
  938: 
  939: /**
  940:  * Someone else wants to establish a peer connection with us,
  941:  * and we said yes.
  942:  */
  943: static void
  944: peer_connection_got_proposition_yes_cb(gpointer data, gint id)
  945: {
  946: 	PeerConnection *conn;
  947: 
  948: 	conn = data;
  949: 
  950: 	conn->flags |= PEER_CONNECTION_FLAG_APPROVED;
  951: 	peer_connection_trynext(conn);
  952: }
  953: 
  954: /**
  955:  * Someone else wants to establish a peer connection with us,
  956:  * and we said no.
  957:  *
  958:  * "Well, one time my friend asked me if I wanted to play the
  959:  *  piccolo.  But I said no."
  960:  */
  961: static void
  962: peer_connection_got_proposition_no_cb(gpointer data, gint id)
  963: {
  964: 	PeerConnection *conn;
  965: 
  966: 	conn = data;
  967: 
  968: 	aim_im_denytransfer(conn->od, conn->bn, conn->cookie,
  969: 			AIM_TRANSFER_DENY_DECLINE);
  970: 	peer_connection_destroy(conn, OSCAR_DISCONNECT_LOCAL_CLOSED, NULL);
  971: }
  972: 
  973: /**
  974:  * Someone else wants to establish a peer connection with us.
  975:  */
  976: void
  977: peer_connection_got_proposition(OscarData *od, const gchar *bn, const gchar *message, IcbmArgsCh2 *args)
  978: {
  979: 	PurpleConnection *gc;
  980: 	PurpleAccount *account;
  981: 	PeerConnection *conn;
  982: 	gchar *buf;
  983: 
  984: 	gc = od->gc;
  985: 	account = purple_connection_get_account(gc);
  986: 
  987: 	/*
  988: 	 * If we have a connection with this same cookie then they are
  989: 	 * probably just telling us they weren't able to connect to us
  990: 	 * and we should try connecting to them, instead.  Or they want
  991: 	 * to go through a proxy.
  992: 	 */
  993: 	conn = peer_connection_find_by_cookie(od, bn, args->cookie);
  994: 	if ((conn != NULL) && (conn->type == args->type))
  995: 	{
  996: 		purple_debug_info("oscar", "Remote user wants to try a "
  997: 				"different connection method\n");
  998: 		g_free(conn->proxyip);
  999: 		g_free(conn->clientip);
 1000: 		g_free(conn->verifiedip);
 1001: 		if (args->use_proxy)
 1002: 			conn->proxyip = g_strdup(args->proxyip);
 1003: 		else
 1004: 			conn->proxyip = NULL;
 1005: 		conn->verifiedip = g_strdup(args->verifiedip);
 1006: 		conn->clientip = g_strdup(args->clientip);
 1007: 		conn->port = args->port;
 1008: 		conn->use_proxy |= args->use_proxy;
 1009: 		conn->lastrequestnumber++;
 1010: 		peer_connection_trynext(conn);
 1011: 		return;
 1012: 	}
 1013: 
 1014: 	/* If this is a direct IM, then close any existing session */
 1015: 	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
 1016: 	{
 1017: 		conn = peer_connection_find_by_type(od, bn, args->type);
 1018: 		if (conn != NULL)
 1019: 		{
 1020: 			/* Close the old direct IM and start a new one */
 1021: 			purple_debug_info("oscar", "Received new direct IM request "
 1022: 				"from %s.  Destroying old connection.\n", bn);
 1023: 			peer_connection_destroy(conn, OSCAR_DISCONNECT_REMOTE_CLOSED, NULL);
 1024: 		}
 1025: 	}
 1026: 
 1027: 	/* Check for proper arguments */
 1028: 	if (args->type == OSCAR_CAPABILITY_SENDFILE)
 1029: 	{
 1030: 		if ((args->info.sendfile.filename == NULL) ||
 1031: 			(args->info.sendfile.totsize == 0) ||
 1032: 			(args->info.sendfile.totfiles == 0))
 1033: 		{
 1034: 			purple_debug_warning("oscar",
 1035: 					"%s tried to send you a file with incomplete "
 1036: 					"information.\n", bn);
 1037: 			return;
 1038: 		}
 1039: 	}
 1040: 
 1041: 	conn = peer_connection_new(od, args->type, bn);
 1042: 	memcpy(conn->cookie, args->cookie, 8);
 1043: 	if (args->use_proxy)
 1044: 		conn->proxyip = g_strdup(args->proxyip);
 1045: 	conn->clientip = g_strdup(args->clientip);
 1046: 	conn->verifiedip = g_strdup(args->verifiedip);
 1047: 	conn->port = args->port;
 1048: 	conn->use_proxy |= args->use_proxy;
 1049: 	conn->lastrequestnumber++;
 1050: 
 1051: 	if (args->type == OSCAR_CAPABILITY_DIRECTIM)
 1052: 	{
 1053: 		buf = g_strdup_printf(_("%s has just asked to directly connect to %s"),
 1054: 				bn, purple_account_get_username(account));
 1055: 
 1056: 		purple_request_action(conn, NULL, buf,
 1057: 						_("This requires a direct connection between "
 1058: 						  "the two computers and is necessary for IM "
 1059: 						  "Images.  Because your IP address will be "
 1060: 						  "revealed, this may be considered a privacy "
 1061: 						  "risk."),
 1062: 						PURPLE_DEFAULT_ACTION_NONE,
 1063: 						account, bn, NULL,
 1064: 						conn, 2,
 1065: 						_("C_onnect"), G_CALLBACK(peer_connection_got_proposition_yes_cb),
 1066: 						_("Cancel"), G_CALLBACK(peer_connection_got_proposition_no_cb));
 1067: 	}
 1068: 	else if (args->type == OSCAR_CAPABILITY_SENDFILE)
 1069: 	{
 1070: 		gchar *filename;
 1071: 
 1072: 		conn->xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, bn);
 1073: 		if (conn->xfer)
 1074: 		{
 1075: 			conn->xfer->data = conn;
 1076: 			purple_xfer_ref(conn->xfer);
 1077: 			purple_xfer_set_size(conn->xfer, args->info.sendfile.totsize);
 1078: 
 1079: 			/* Set the file name */
 1080: 			if (g_utf8_validate(args->info.sendfile.filename, -1, NULL))
 1081: 				filename = g_strdup(args->info.sendfile.filename);
 1082: 			else
 1083: 				filename = purple_utf8_salvage(args->info.sendfile.filename);
 1084: 
 1085: 			if (args->info.sendfile.subtype == AIM_OFT_SUBTYPE_SEND_DIR)
 1086: 			{
 1087: 				/*
 1088: 				 * If they are sending us a directory then the last character
 1089: 				 * of the file name will be an asterisk.  We don't want to
 1090: 				 * save stuff to a directory named "*" so we remove the
 1091: 				 * asterisk from the file name.
 1092: 				 */
 1093: 				char *tmp = strrchr(filename, '\\');
 1094: 				if ((tmp != NULL) && (tmp[1] == '*'))
 1095: 					tmp[0] = '\0';
 1096: 			}
 1097: 			purple_xfer_set_filename(conn->xfer, filename);
 1098: 			g_free(filename);
 1099: 
 1100: 			/*
 1101: 			 * Set the message, unless this is the dummy message from an
 1102: 			 * ICQ client or an empty message from an AIM client.
 1103: 			 * TODO: Maybe we should strip HTML and then see if strlen>0?
 1104: 			 */
 1105: 			if ((message != NULL) &&
 1106: 				(g_ascii_strncasecmp(message, "<ICQ_COOL_FT>", 13) != 0) &&
 1107: 				(g_ascii_strcasecmp(message, "<HTML>") != 0))
 1108: 			{
 1109: 				purple_xfer_set_message(conn->xfer, message);
 1110: 			}
 1111: 
 1112: 			/* Setup our I/O op functions */
 1113: 			purple_xfer_set_init_fnc(conn->xfer, peer_oft_recvcb_init);
 1114: 			purple_xfer_set_end_fnc(conn->xfer, peer_oft_recvcb_end);
 1115: 			purple_xfer_set_request_denied_fnc(conn->xfer, peer_oft_cb_generic_cancel);
 1116: 			purple_xfer_set_cancel_recv_fnc(conn->xfer, peer_oft_cb_generic_cancel);
 1117: 			purple_xfer_set_ack_fnc(conn->xfer, peer_oft_recvcb_ack_recv);
 1118: 
 1119: 			/* Now perform the request */
 1120: 			purple_xfer_request(conn->xfer);
 1121: 		}
 1122: 	}
 1123: }
 1124: 
 1125: /*******************************************************************/
 1126: /* End code for establishing a peer connection                     */
 1127: /*******************************************************************/

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