gaim/gaim

d49cfaa0c441
Parents cd8a432e34f2
Children 1773ae407d23
Proxied OSCAR File Transfers and such. This code still needs to be tested
more thoroughly, but I plan to put this code through heavy testing before
it's time for the next release. After the code has been tested by
several other people, I'll go ahead and patch HEAD also. If anyone wants
to pull down oldstatus CVS and test AIM/ICQ file transfers, please do.
--- a/src/protocols/oscar/aim.h Sun Aug 14 02:30:46 2005 -0400
+++ b/src/protocols/oscar/aim.h Mon Aug 15 02:36:36 2005 -0400
@@ -286,9 +286,53 @@
#define AIM_CONN_TYPE_EMAIL 0x0018
/* they start getting arbitrary for rendezvous stuff =) */
+#define AIM_CONN_TYPE_RENDEZVOUS_PROXY 0xfffd /* these speak a strange language */
#define AIM_CONN_TYPE_RENDEZVOUS 0xfffe /* these do not speak FLAP! */
#define AIM_CONN_TYPE_LISTENER 0xffff /* socket waiting for accept() */
+/* Command types for doing a rendezvous proxy login */
+#define AIM_RV_PROXY_PACKETVER_DFLT 0x044a
+#define AIM_RV_PROXY_ERROR 0x0001
+#define AIM_RV_PROXY_INIT_SEND 0x0002 /* First command sent when creating a connection */
+#define AIM_RV_PROXY_INIT_RECV 0x0004 /* First command sent when receiving existing connection */
+#define AIM_RV_PROXY_ACK 0x0003
+#define AIM_RV_PROXY_READY 0x0005
+
+/* Number of bytes expected in each of the above packets, including the 2 bytes specifying length */
+#define AIM_RV_PROXY_ERROR_LEN 14
+#define AIM_RV_PROXY_INIT_SEND_LEN 55
+#define AIM_RV_PROXY_INIT_RECV_LEN 57
+#define AIM_RV_PROXY_ACK_LEN 18
+#define AIM_RV_PROXY_READY_LEN 12
+#define AIM_RV_PROXY_HDR_LEN 12 /* Bytes in just the header alone */
+
+/* Default values for unknown/unused values in rendezvous proxy negotiation packets */
+#define AIM_RV_PROXY_SERVER_FLAGS 0x0220 /* Default flags sent by proxy server */
+#define AIM_RV_PROXY_CLIENT_FLAGS 0x0000 /* Default flags sent by client */
+#define AIM_RV_PROXY_UNKNOWNA_DFLT 0x00000000 /* Default value for an unknown block */
+#define AIM_RV_PROXY_SERVER_URL "ars.oscar.aol.com"
+#define AIM_RV_PROXY_CONNECT_PORT 5190 /* The port we should always connect to */
+
+/* What is the purpose of this transfer? (Who will end up with a new file?)
+ * These values are used in oft_info->send_or_recv */
+#define AIM_XFER_SEND 0x0001
+#define AIM_XFER_RECV 0x0002
+
+/* Via what method is the data getting routed?
+ * These values are used in oft_info->method */
+#define AIM_XFER_DIRECT 0x0001 /* Direct connection; receiver connects to sender */
+#define AIM_XFER_REDIR 0x0002 /* Redirected connection; sender connects to receiver */
+#define AIM_XFER_PROXY 0x0003 /* Proxied connection */
+
+/* Who requested the proxy?
+ * The difference between a stage 2 and stage 3 proxied transfer is that the receiver does the
+ * initial login for a stage 2, but the sender must do it for a stage 3.
+ * These values are used in oft_info->stage */
+#define AIM_XFER_PROXY_NONE 0x0001
+#define AIM_XFER_PROXY_STG1 0x0002 /* Sender requested a proxy be used (stage1) */
+#define AIM_XFER_PROXY_STG2 0x0003 /* Receiver requested a proxy be used (stage2) */
+#define AIM_XFER_PROXY_STG3 0x0004 /* Receiver requested a proxy be used (stage3) */
+
/*
* Subtypes, we need these for OFT stuff.
*/
@@ -296,7 +340,7 @@
#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002
#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003
#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004
-#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005
+#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005
/*
* Status values returned from aim_conn_new(). ORed together.
@@ -467,23 +511,21 @@
} aim_session_t;
/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */
-#define AIM_ICQ_STATE_NORMAL 0x00000000
-#define AIM_ICQ_STATE_AWAY 0x00000001
-#define AIM_ICQ_STATE_DND 0x00000002
-#define AIM_ICQ_STATE_OUT 0x00000004
-#define AIM_ICQ_STATE_BUSY 0x00000010
-#define AIM_ICQ_STATE_CHAT 0x00000020
-#define AIM_ICQ_STATE_INVISIBLE 0x00000100
-#define AIM_ICQ_STATE_WEBAWARE 0x00010000
-#define AIM_ICQ_STATE_HIDEIP 0x00020000
-#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
+#define AIM_ICQ_STATE_NORMAL 0x00000000
+#define AIM_ICQ_STATE_AWAY 0x00000001
+#define AIM_ICQ_STATE_DND 0x00000002
+#define AIM_ICQ_STATE_OUT 0x00000004
+#define AIM_ICQ_STATE_BUSY 0x00000010
+#define AIM_ICQ_STATE_CHAT 0x00000020
+#define AIM_ICQ_STATE_INVISIBLE 0x00000100
+#define AIM_ICQ_STATE_WEBAWARE 0x00010000
+#define AIM_ICQ_STATE_HIDEIP 0x00020000
+#define AIM_ICQ_STATE_BIRTHDAY 0x00080000
#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000
-#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
+#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000
#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000
#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000
-
-
/*
* Get command from connections
*
@@ -840,6 +882,9 @@
fu16_t totfiles;
fu32_t totsize;
char *filename;
+ /* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */
+ fu16_t reqnum;
+ fu8_t use_proxy; /* Did the user request that we use a rv proxy? */
} sendfile;
} info;
void *destructor; /* used internally only */
@@ -875,7 +920,7 @@
/* 0x0008 */ faim_export int aim_im_warn(aim_session_t *sess, aim_conn_t *conn, const char *destsn, fu32_t flags);
/* 0x000b */ faim_export int aim_im_denytransfer(aim_session_t *sess, const char *sender, const char *cookie, fu16_t code);
/* 0x0014 */ faim_export int aim_im_sendmtn(aim_session_t *sess, fu16_t type1, const char *sn, fu16_t type2);
-
+faim_export void aim_im_makecookie(char* cookie);
/* ft.c */
@@ -914,18 +959,38 @@
/* 256 */
};
+struct aim_rv_proxy_info {
+ fu16_t packet_ver;
+ fu16_t cmd_type;
+ fu16_t flags;
+ char* ip; /* IP address sent along with this packet */
+ fu16_t port; /* This is NOT the port we should use to connect. Always connect to 5190 */
+ char cookie[8];
+ fu32_t unknownA;
+ fu16_t err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */
+ aim_conn_t *conn;
+ aim_session_t *sess;
+};
+
struct aim_oft_info {
char cookie[8];
char *sn;
- char *proxyip;
+ char *proxyip; /* Not necessarily an IP; could be the hostname of the proxy */
char *clientip;
char *verifiedip;
fu16_t port;
+
+ int send_or_recv; /* Send or receive */
+ int method; /* What method is being used to transfer this file? DIRECT, REDIR, or PROXY */
+ int stage; /* At what stage was a proxy requested? NONE, STG1, STG2*/
+ int xfer_reffed; /* There are many timers, but we should only ref the xfer once */
+
aim_conn_t *conn;
aim_session_t *sess;
int success; /* Was the connection successful? Used for timing out the transfer. */
struct aim_fileheader_t fh;
struct aim_oft_info *next;
+ struct aim_rv_proxy_info *proxy_info;
};
faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck);
@@ -940,12 +1005,21 @@
const fu8_t *localip, fu16_t port, const fu8_t *mycookie);
faim_export aim_conn_t *aim_odc_connect(aim_session_t *sess, const char *sn, const char *addr, const fu8_t *cookie);
-faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename);
+faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn,
+ const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv,
+ int method, int stage);
faim_export int aim_oft_destroyinfo(struct aim_oft_info *oft_info);
+faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const fu8_t *cookie, fu16_t port);
faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info);
+faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info);
+faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info);
+faim_export int aim_sendfile_listen(aim_session_t *sess, struct aim_oft_info *oft_info, int listenfd);
+faim_export int aim_oft_sendheader(aim_session_t *sess, fu16_t type, struct aim_oft_info *oft_info);
+int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count);
+faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn);
/* 0x0002 - locate.c */
/*
--- a/src/protocols/oscar/ft.c Sun Aug 14 02:30:46 2005 -0400
+++ b/src/protocols/oscar/ft.c Mon Aug 15 02:36:36 2005 -0400
@@ -61,6 +61,8 @@
#include "win32dep.h"
#endif
+#include <debug.h>
+
/*
* I really want to switch all our networking code to using IPv6 only,
* but that really isn't a good idea at all. Evan S. of Adium says
@@ -654,7 +656,7 @@
return ret;
}
-faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename)
+faim_export struct aim_oft_info *aim_oft_createinfo(aim_session_t *sess, const fu8_t *cookie, const char *sn, const char *ip, fu16_t port, fu32_t size, fu32_t modtime, char *filename, int send_or_recv, int method, int stage)
{
struct aim_oft_info *new;
@@ -667,11 +669,21 @@
new->sess = sess;
if (cookie)
memcpy(new->cookie, cookie, 8);
+ else
+ aim_im_makecookie(new->cookie);
if (ip)
new->clientip = strdup(ip);
+ else
+ new->clientip = NULL;
if (sn)
new->sn = strdup(sn);
+ else
+ new->sn = NULL;
+ new->method = method;
+ new->send_or_recv = send_or_recv;
+ new->stage = stage;
new->port = port;
+ new->xfer_reffed = FALSE;
new->success = FALSE;
new->fh.totfiles = 1;
new->fh.filesleft = 1;
@@ -696,6 +708,25 @@
return new;
}
+faim_export struct aim_rv_proxy_info *aim_rv_proxy_createinfo(aim_session_t *sess, const fu8_t *cookie,
+ fu16_t port)
+{
+ struct aim_rv_proxy_info *proxy_info;
+
+ if (!(proxy_info = (struct aim_rv_proxy_info*)calloc(1, sizeof(struct aim_rv_proxy_info))))
+ return NULL;
+
+ proxy_info->sess = sess;
+ proxy_info->port = port;
+ proxy_info->packet_ver = AIM_RV_PROXY_PACKETVER_DFLT;
+ proxy_info->unknownA = AIM_RV_PROXY_UNKNOWNA_DFLT;
+
+ if (cookie)
+ memcpy(proxy_info->cookie, cookie, 8);
+
+ return proxy_info;
+}
+
/**
* Remove the given oft_info struct from the oft_info linked list, and
* then free its memory.
@@ -901,6 +932,123 @@
}
/**
+ * Create a rendezvous "init recv" packet and send it on its merry way.
+ * This is the first packet sent to the proxy server by the second client
+ * involved in this rendezvous proxy session.
+ *
+ * @param sess The session.
+ * @param proxy_info Changable pieces of data for this packet
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_rv_proxy_init_recv(struct aim_rv_proxy_info *proxy_info)
+{
+ aim_bstream_t bs;
+ fu8_t *bs_raw;
+ fu16_t packet_len;
+ fu8_t sn_len;
+ int err;
+
+ err = 0;
+
+ if (!proxy_info)
+ return -EINVAL;
+
+ sn_len = strlen(proxy_info->sess->sn);
+ packet_len = 2 + 2 /* packet_len, packet_ver */
+ + 2 + 4 /* cmd_type, unknownA */
+ + 2 /* flags */
+ + 1 + sn_len /* Length/value pair for screenname */
+ + 8 /* ICBM Cookie */
+ + 2 /* port */
+ + 2 + 2 + 16; /* TLV for Filesend capability block */
+
+ if (!(bs_raw = malloc(packet_len)))
+ return -ENOMEM;
+
+ aim_bstream_init(&bs, bs_raw, packet_len);
+ aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
+ aimbs_put16(&bs, proxy_info->packet_ver);
+ aimbs_put16(&bs, AIM_RV_PROXY_INIT_RECV);
+ aimbs_put32(&bs, proxy_info->unknownA);
+ aimbs_put16(&bs, proxy_info->flags);
+ aimbs_put8(&bs, sn_len);
+ aimbs_putraw(&bs, proxy_info->sess->sn, sn_len);
+ aimbs_put16(&bs, proxy_info->port);
+ aimbs_putraw(&bs, proxy_info->cookie, 8);
+
+ aimbs_put16(&bs, 0x0001); /* Type */
+ aimbs_put16(&bs, 16); /* Length */
+ aim_putcap(&bs, AIM_CAPS_SENDFILE); /* Value */
+
+ aim_bstream_rewind(&bs);
+ if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+ err = errno;
+ proxy_info->conn->lastactivity = time(NULL);
+
+ free(bs_raw);
+
+ return err;
+}
+
+
+/**
+ * Create a rendezvous "init send" packet and send it on its merry way.
+ * This is the first packet sent to the proxy server by the client
+ * first indicating that this will be a proxied connection
+ *
+ * @param sess The session.
+ * @param proxy_info Changable pieces of data for this packet
+ * @return Return 0 if no errors, otherwise return the error number.
+ */
+faim_export int aim_rv_proxy_init_send(struct aim_rv_proxy_info *proxy_info)
+{
+ aim_bstream_t bs;
+ fu8_t *bs_raw;
+ fu16_t packet_len;
+ fu8_t sn_len;
+ int err;
+
+ err = 0;
+
+ if (!proxy_info)
+ return -EINVAL;
+
+ sn_len = strlen(proxy_info->sess->sn);
+ packet_len = 2 + 2 /* packet_len, packet_ver */
+ + 2 + 4 /* cmd_type, unknownA */
+ + 2 /* flags */
+ + 1 + sn_len /* Length/value pair for screenname */
+ + 8 /* ICBM Cookie */
+ + 2 + 2 + 16; /* TLV for Filesend capability block */
+
+ if (!(bs_raw = malloc(packet_len)))
+ return -ENOMEM;
+
+ aim_bstream_init(&bs, bs_raw, packet_len);
+ aimbs_put16(&bs, packet_len - 2); /* Length includes only packets after length marker */
+ aimbs_put16(&bs, proxy_info->packet_ver);
+ aimbs_put16(&bs, AIM_RV_PROXY_INIT_SEND);
+ aimbs_put32(&bs, proxy_info->unknownA);
+ aimbs_put16(&bs, proxy_info->flags);
+ aimbs_put8(&bs, sn_len);
+ aimbs_putraw(&bs, proxy_info->sess->sn, sn_len);
+ aimbs_putraw(&bs, proxy_info->cookie, 8);
+
+ aimbs_put16(&bs, 0x0001); /* Type */
+ aimbs_put16(&bs, 16); /* Length */
+ aim_putcap(&bs, AIM_CAPS_SENDFILE); /* Value */
+
+ aim_bstream_rewind(&bs);
+ if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+ err = errno;
+ proxy_info->conn->lastactivity = time(NULL);
+
+ free(bs_raw);
+
+ return err;
+}
+
+/**
* Handle incoming data on a rendezvous connection. This is analogous to the
* consumesnac function in rxhandlers.c, and I really think this should probably
* be in rxhandlers.c as well, but I haven't finished cleaning everything up yet.
@@ -937,3 +1085,101 @@
return ret;
}
+
+/**
+ * Handle incoming data on a rendezvous proxy connection. This is similar to
+ * aim_rxdispatch_rendezvous above and should probably be kept with that function.
+ *
+ * @param sess The session.
+ * @param fr The frame allocated for the incoming data.
+ * @return Return 0 if the packet was handled correctly, otherwise return the
+ * error number.
+ */
+faim_internal struct aim_rv_proxy_info *aim_rv_proxy_read(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_bstream_t bs_hdr;
+ fu8_t hdr_buf[AIM_RV_PROXY_HDR_LEN];
+ aim_bstream_t bs_body; /* The body (everything but the header) of the packet */
+ fu8_t *body_buf = NULL;
+ fu8_t body_len;
+
+ char str_ip[30] = {""};
+ fu8_t ip_temp[4];
+
+ fu16_t len;
+ struct aim_rv_proxy_info *proxy_info;
+
+ if(!(proxy_info = malloc(sizeof(struct aim_rv_proxy_info))))
+ return NULL;
+
+ aim_bstream_init(&bs_hdr, hdr_buf, AIM_RV_PROXY_HDR_LEN);
+ if (aim_bstream_recv(&bs_hdr, conn->fd, AIM_RV_PROXY_HDR_LEN) == AIM_RV_PROXY_HDR_LEN) {
+ aim_bstream_rewind(&bs_hdr);
+ len = aimbs_get16(&bs_hdr);
+ proxy_info->packet_ver = aimbs_get16(&bs_hdr);
+ proxy_info->cmd_type = aimbs_get16(&bs_hdr);
+ proxy_info->unknownA = aimbs_get32(&bs_hdr);
+ proxy_info->flags = aimbs_get16(&bs_hdr);
+ if(proxy_info->cmd_type == AIM_RV_PROXY_READY) {
+ /* Do a little victory dance
+ * A ready packet contains no additional information */
+ } else if(proxy_info->cmd_type == AIM_RV_PROXY_ERROR) {
+ if(len == AIM_RV_PROXY_ERROR_LEN - 2) {
+ body_len = AIM_RV_PROXY_ERROR_LEN - AIM_RV_PROXY_HDR_LEN;
+ body_buf = malloc(body_len);
+ aim_bstream_init(&bs_body, body_buf, body_len);
+ if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
+ aim_bstream_rewind(&bs_body);
+ proxy_info->err_code = aimbs_get16(&bs_body);
+ } else {
+ faimdprintf(sess, 0, "faim: error reading rv proxy error packet\n");
+ aim_conn_close(conn);
+ free(proxy_info);
+ proxy_info = NULL;
+ }
+ } else {
+ faimdprintf(sess, 0, "faim: invalid length for proxy error packet\n");
+ free(proxy_info);
+ proxy_info = NULL;
+ }
+ } else if(proxy_info->cmd_type == AIM_RV_PROXY_ACK) {
+ if(len == AIM_RV_PROXY_ACK_LEN - 2) {
+ body_len = AIM_RV_PROXY_ACK_LEN - AIM_RV_PROXY_HDR_LEN;
+ body_buf = malloc(body_len);
+ aim_bstream_init(&bs_body, body_buf, body_len);
+ if (aim_bstream_recv(&bs_body, conn->fd, body_len) == body_len) {
+ aim_bstream_rewind(&bs_body);
+ proxy_info->port = aimbs_get16(&bs_body);
+ int i;
+ for(i=0; i<4; i++)
+ ip_temp[i] = aimbs_get8(&bs_body);
+ snprintf(str_ip, sizeof(str_ip), "%hhu.%hhu.%hhu.%hhu",
+ ip_temp[0], ip_temp[1],
+ ip_temp[2], ip_temp[3]);
+ proxy_info->ip = strdup(str_ip);
+ } else {
+ faimdprintf(sess, 0, "faim: error reading rv proxy error packet\n");
+ aim_conn_close(conn);
+ free(proxy_info);
+ proxy_info = NULL;
+ }
+ } else {
+ faimdprintf(sess, 0, "faim: invalid length for proxy error packet\n");
+ free(proxy_info);
+ proxy_info = NULL;
+ }
+ } else {
+ faimdprintf(sess, 0, "faim: unknown type for aim rendezvous proxy packet\n");
+ }
+ } else {
+ faimdprintf(sess, 0, "faim: error reading header of rv proxy packet\n");
+ aim_conn_close(conn);
+ free(proxy_info);
+ proxy_info = NULL;
+ }
+ if(body_buf) {
+ free(body_buf);
+ body_buf = NULL;
+ }
+ return proxy_info;
+}
--- a/src/protocols/oscar/im.c Sun Aug 14 02:30:46 2005 -0400
+++ b/src/protocols/oscar/im.c Mon Aug 15 02:36:36 2005 -0400
@@ -747,6 +747,22 @@
return 0;
}
+/*
+ * Generates a random ICBM cookie in a character array of length 8
+ * and copies it into the variable passed as cookie.
+ * Extracted from aim_im_sendch2_sendfile_ask
+ */
+faim_export void aim_im_makecookie(char* cookie) {
+ int i;
+ char gen_cookie[8];
+
+ /* XXX - Should be like "21CBF95" and null terminated */
+ for (i = 0; i < 7; i++)
+ gen_cookie[i] = 0x30 + ((fu8_t)rand() % 10);
+ gen_cookie[7] = '\0';
+ memcpy(cookie, gen_cookie, 8);
+}
+
/**
* Subtype 0x0006 - Send an "I want to send you this file" message
*
@@ -757,55 +773,112 @@
aim_frame_t *fr;
aim_snacid_t snacid;
aim_tlvlist_t *tl=NULL, *subtl=NULL;
- int i;
if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
return -EINVAL;
- /* XXX - Should be like "21CBF95" and null terminated */
- for (i = 0; i < 7; i++)
- oft_info->cookie[i] = 0x30 + ((fu8_t)rand() % 10);
- oft_info->cookie[7] = '\0';
+ /* The cookie must already have been generated by this point */
{ /* Create the subTLV chain */
fu8_t *buf;
int buflen;
aim_bstream_t bs;
-
- aim_tlvlist_add_16(&subtl, 0x000a, 0x0001);
- aim_tlvlist_add_noval(&subtl, 0x000f);
+ fu8_t ip[4];
+ fu8_t ip_comp[4]; /* The bitwise complement of the ip */
+ char *nexttoken;
+ int i;
+
+ /* In a stage 2 proxied transfer & a transfer redirect, we send a second "reply request"
+ * Being the second request for this transfer, its request number is 2
+ * You can fill in the blank for a stage 3's request number... */
+ if( (oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG2)
+ || (oft_info->send_or_recv == AIM_XFER_RECV
+ && oft_info->stage == AIM_XFER_PROXY_STG3)
+ || oft_info->method == AIM_XFER_REDIR)
+ aim_tlvlist_add_16(&subtl, 0x000a, 0x0002);
+ else if(oft_info->send_or_recv == AIM_XFER_SEND && oft_info->stage == AIM_XFER_PROXY_STG3)
+ aim_tlvlist_add_16(&subtl, 0x000a, 0x0003);
+ else
+ aim_tlvlist_add_16(&subtl, 0x000a, 0x0001);
+
+ /* This is usually necessary, but ruins a redirect and a stg3 proxy request */
+ if(!(oft_info->send_or_recv == AIM_XFER_RECV
+ && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+ aim_tlvlist_add_noval(&subtl, 0x000f);
+ }
+
+ /* If the following is ever enabled, ensure that it is not sent with a receive redirect
+ * or stage 3 proxy redirect for a file receive (same conditions for sending 0x000f above) */
/* aim_tlvlist_add_raw(&subtl, 0x000e, 2, "en");
aim_tlvlist_add_raw(&subtl, 0x000d, 8, "us-ascii");
aim_tlvlist_add_raw(&subtl, 0x000c, 24, "Please accept this file."); */
/* XXX - Change oft_info->clientip to an array of 4 bytes */
if (oft_info->clientip) {
- fu8_t ip[4];
- char *nexttoken;
- int i = 0;
+ i = 0;
nexttoken = strtok(oft_info->clientip, ".");
while (nexttoken && i<4) {
ip[i] = atoi(nexttoken);
+ ip_comp[i] = ~ip[i];
nexttoken = strtok(NULL, ".");
i++;
}
+
+ /* If there is no proxyip, we must fill it in with the clientip */
+ if(!oft_info->proxyip) {
+ aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
+ aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp); /* check? value */
+ }
+
aim_tlvlist_add_raw(&subtl, 0x0003, 4, ip);
}
- aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port);
-
- /* TLV t(2711) */
- buflen = 2+2+4+strlen(oft_info->fh.name)+1;
- buf = malloc(buflen);
- aim_bstream_init(&bs, buf, buflen);
- aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
- aimbs_put16(&bs, oft_info->fh.totfiles);
- aimbs_put32(&bs, oft_info->fh.totsize);
-
- /* Filename - NULL terminated, for some odd reason */
- aimbs_putraw(&bs, oft_info->fh.name, strlen(oft_info->fh.name));
- aimbs_put8(&bs, 0x00);
-
- aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data);
- free(buf);
+
+ /* Don't send the proxyip & accompanying info during a receive redirect or stg3 proxy request */
+ if(!(oft_info->send_or_recv == AIM_XFER_RECV
+ && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+ if (oft_info->proxyip) { /* Generate the proxyip */
+ i = 0;
+ nexttoken = strtok(oft_info->proxyip, ".");
+ while (nexttoken && i<4) {
+ ip[i] = atoi(nexttoken);
+ ip_comp[i] = ~ip[i];
+ nexttoken = strtok(NULL, ".");
+ i++;
+ }
+ aim_tlvlist_add_raw(&subtl, 0x0002, 4, ip);
+ /* This zero-length TLV specifies a proxy will be used */
+ aim_tlvlist_add_noval(&subtl, 0x0010);
+
+ /* Proxied transfers fail without this next (check?) value */
+ aim_tlvlist_add_raw(&subtl, 0x0016, 4, ip_comp);
+ }
+ }
+
+ /* Don't send the port & its check during a stage 3 proxy request */
+ if(!(oft_info->send_or_recv == AIM_XFER_RECV && oft_info->stage == AIM_XFER_PROXY_STG3)) {
+ aim_tlvlist_add_16(&subtl, 0x0005, oft_info->port);
+
+ /* Check value? Bitwise complement of the port */
+ aim_tlvlist_add_16(&subtl, 0x0017, ~(oft_info->port));
+ }
+
+ /* winAIM gets mad at us if we send too much info during a send redirect or stg3 proxy request */
+ if(!(oft_info->send_or_recv == AIM_XFER_RECV
+ && (oft_info->method == AIM_XFER_REDIR || oft_info->stage == AIM_XFER_PROXY_STG3))) {
+ /* TLV t(2711) */
+ buflen = 2+2+4+strlen(oft_info->fh.name)+1;
+ buf = malloc(buflen);
+ aim_bstream_init(&bs, buf, buflen);
+ aimbs_put16(&bs, (oft_info->fh.totfiles > 1) ? 0x0002 : 0x0001);
+ aimbs_put16(&bs, oft_info->fh.totfiles);
+ aimbs_put32(&bs, oft_info->fh.totsize);
+
+ /* Filename - NULL terminated, for some odd reason */
+ aimbs_putraw(&bs, oft_info->fh.name, strlen(oft_info->fh.name));
+ aimbs_put8(&bs, 0x00);
+
+ aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data);
+ free(buf);
+ }
}
{ /* Create the main TLV chain */
@@ -1847,8 +1920,15 @@
* 0x0002 - "I will accept this file from you"
* 0x0002 - Also used in ICQ Lite Beta 4.0 URLs
*/
+ /*
+ * This is what I call the request number of the file transfer
+ * 0x0001 - Initial file transfer request for no proxy or stage 1 proxy
+ * 0x0002 - "Reply request"
+ * 0x0003 - A third request has been sent; applies only to stage 3 proxied transfers
+ * -- Jonathan
+ */
if (aim_tlv_gettlv(list2, 0x000a, 1))
- ;
+ args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1);
/*
* Error code.
@@ -1882,16 +1962,18 @@
* Maybe means we should connect directly to transfer the file?
* Also used in ICQ Lite Beta 4.0 URLs. Also empty.
*/
+ /* I don't think this indicates a direct transfer; this flag is
+ * also present in a stage 1 proxied file send request -- Jonathan */
if (aim_tlv_gettlv(list2, 0x000f, 1))
;
/*
- * Unknown -- no value
- *
- * Maybe means we should proxy the file transfer through an AIM server?
+ * Flag meaning we should proxy the file transfer through an AIM server
*/
if (aim_tlv_gettlv(list2, 0x0010, 1))
- ;
+ args.info.sendfile.use_proxy = TRUE;
+ else
+ args.info.sendfile.use_proxy = FALSE;
if (strlen(proxyip))
args.proxyip = (char *)proxyip;
--- a/src/protocols/oscar/oscar.c Sun Aug 14 02:30:46 2005 -0400
+++ b/src/protocols/oscar/oscar.c Mon Aug 15 02:36:36 2005 -0400
@@ -59,8 +59,15 @@
#define OSCAR_CONNECT_STEPS 6
#define OSCAR_DEFAULT_CUSTOM_ENCODING "ISO-8859-1"
-/* Seconds each file transfer ip address will be given to make a connection */
-#define FT_IP_TIMEOUT 15
+/* Milliseconds each file transfer ip address will be given to make a connection.
+ * Conservative values are commented out. Impatient values are currently in use.
+ * With these quick timeouts, a connection will happen more quickly,
+ * but a proxy is more likely to be used. If we are defaulting to a proxied
+ * transfer too often, these values should be increased */
+#define FT_CLIENTIP_TIMEOUT 1000 /* 5000 */
+#define FT_VERIFIEDIP_TIMEOUT 5000 /* 15000 */
+#define FT_REDIR_TIMEOUT 10000 /* 20000 */ /* Time to wait for redirected transfer */
+#define FT_PROXYIP_TIMEOUT 15000 /* 15000 */
static int caps_aim = AIM_CAPS_CHAT | AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT;
static int caps_icq = AIM_CAPS_BUDDYICON | AIM_CAPS_DIRECTIM | AIM_CAPS_SENDFILE | AIM_CAPS_ICQUTF8 | AIM_CAPS_INTEROPERATE | AIM_CAPS_ICHAT;
@@ -296,6 +303,7 @@
static void oscar_callback(gpointer data, gint source, GaimInputCondition condition);
static void oscar_direct_im_initiate(GaimConnection *gc, const char *who, const char *cookie);
static void oscar_xfer_init_recv(GaimXfer *xfer);
+static void oscar_xfer_init_send(GaimXfer *xfer);
/* remove these at some point? */
/* Because I don't like forward declarations? I think that was why... */
@@ -1882,8 +1890,17 @@
* -They begin to send us lots of raw data.
* -When they finish sending data we send an AIM_CB_OFT_DONE and then close
* the connection.
+ *
+ * Update August 2005:
+ * The series of events for transfers has been seriously complicated by the addition
+ * of transfer redirects and proxied connections. I could throw a whole lot of words
+ * at trying to explain things here, but it probably wouldn't do much good. To get
+ * a better idea of what happens, take a look at the diagrams and documentation
+ * from my Summer of Code project. -- Jonathan Clark
*/
static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition);
+static void oscar_xfer_proxylogin(gpointer data, gint source, GaimInputCondition condition);
+static void oscar_send_file_request(GaimXfer *xfer);
/*
* Miscellaneous xfer functions
@@ -1924,6 +1941,10 @@
return NULL;
}
+/*
+ * We're done shoving raw data through the connection. Send an OFT header
+ * with the bytes received filled in to indicate this party's over.
+ */
static void oscar_xfer_end(GaimXfer *xfer)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -1943,111 +1964,190 @@
od->file_transfers = g_slist_remove(od->file_transfers, xfer);
}
-/* xfer functions used when receiving files */
+/*
+ * xfer functions used when receiving files
+ */
/*
- * This is a gaim timeout callback for when the clientip looks to be useless (after verifiedip has been tried)
- * It gives up on the file transfer completely if it doesn't approve of the file transfer status
+ * This is a gaim timeout callback called X milliseconds after a connection is attempted
+ * By this point, we've lost faith in the connection method we just tried and want to
+ * try something new. Hopefully, that new connection method will be more successful,
+ * otherwise, we'll end up here again and again until the connection is successful
+ * or we've tried every method... if that happens we just throw our hands up
+ * and inform the user of his bad karma.
*/
-static gboolean oscar_clientip_timeout(gpointer data) {
+static gboolean oscar_xfer_ip_timeout(gpointer data) {
GaimXfer *xfer;
struct aim_oft_info *oft_info;
char *msg = NULL;
- gaim_debug_info("oscar","AAA - in oscar_clientip_timeout\n");
+ gaim_debug_info("oscar","AAA - in oscar_xfer_ip_timeout\n");
+
+
xfer = (GaimXfer*) data;
if(xfer->data) {
oft_info = (struct aim_oft_info*) xfer->data;
-
+
/* Check to see if the clientip has produced any results */
if(!oft_info->success) {
- msg = g_strdup_printf(_("Transfer of file %s timed out."),gaim_xfer_get_filename(xfer));
- gaim_xfer_conversation_write(xfer, msg, TRUE);
- g_free(msg);
- gaim_xfer_unref(xfer);
- gaim_xfer_cancel_local(xfer);
+ /* This connection has worn out its welcome. Goodbye. */
+ if(oft_info->conn)
+ close(oft_info->conn->fd);
+ aim_conn_kill(oft_info->sess, &oft_info->conn);
+
+ if(oft_info->method == AIM_XFER_DIRECT || oft_info->method == AIM_XFER_REDIR) {
+ /* If (we're currently using the verified ip)
+ * In case clientip & verifiedip are the same,
+ * we must prevent an infinite loop */
+ if(xfer->remote_ip && oft_info->verifiedip
+ && g_ascii_strcasecmp(xfer->remote_ip, oft_info->verifiedip) == 0
+ && g_ascii_strcasecmp(oft_info->clientip, oft_info->verifiedip) != 0 )
+ {
+ /* The verifiedip timed out */
+ if(oft_info->method == AIM_XFER_DIRECT) {
+ /* clientip & verifiedip failed, request a redirect
+ * that is, we want the sender to connect to us */
+
+ /* Let the user not to lose hope quite yet*/
+ msg = g_strdup_printf(_("Attempting connection redirect..."));
+ gaim_xfer_conversation_write(xfer, msg, FALSE);
+ g_free(msg);
+
+ gaim_timeout_add(FT_REDIR_TIMEOUT,
+ oscar_xfer_ip_timeout, xfer);
+ oft_info->method = AIM_XFER_REDIR;
+ g_free(oft_info->proxyip);
+ oft_info->proxyip = NULL;
+ oft_info->clientip = g_strdup( gaim_network_get_my_ip(
+ oft_info->conn ? oft_info->conn->fd : -1));
+ oscar_xfer_init_send(xfer);
+ } else {
+ /* clientip, verifiedip, and redirect all failed. */
+ gaim_debug_info("oscar",
+ "redirect timed out. requesting stg3 proxy\n");
+
+ /* Kill our listener */
+ gaim_input_remove(xfer->watcher);
+ aim_conn_kill(oft_info->sess, &oft_info->conn);
+
+ /* Instead of failing here, request a stage 3 proxy */
+ g_free(oft_info->clientip);
+ g_free(oft_info->verifiedip);
+ oft_info->clientip = NULL;
+ oft_info->verifiedip = NULL;
+ oft_info->port = 0;
+ oft_info->conn->type = AIM_CONN_TYPE_RENDEZVOUS;
+ oft_info->method = AIM_XFER_PROXY;
+ oft_info->stage = AIM_XFER_PROXY_STG3;
+
+ aim_im_sendch2_sendfile_ask(oft_info->sess, oft_info);
+ }
+ } else {
+ /* clientip timed out, now try verifiedip */
+ g_free(xfer->remote_ip);
+ xfer->remote_ip = g_strdup(oft_info->verifiedip);
+ gaim_debug_info("oscar","attempting connection using verifiedip\n");
+ oscar_xfer_init_recv(xfer);
+ }
+ } else if(oft_info->method == AIM_XFER_PROXY) {
+ /* proxyip timed out */
+ msg = g_strdup_printf(_("Transfer of file %s timed out."),
+ gaim_xfer_get_filename(xfer));
+ gaim_xfer_conversation_write(xfer, msg, TRUE);
+ g_free(msg);
+ if(oft_info->xfer_reffed) {
+ oft_info->xfer_reffed = FALSE;
+ gaim_xfer_unref(xfer);
+ }
+ gaim_xfer_cancel_local(xfer);
+ } else {
+ gaim_debug_warning("oscar","unknown xfer method encountered in timout\n");
+ }
} else {
- gaim_debug_info("oscar","connection successful; no action taken\n");
+ if(oft_info->xfer_reffed) {
+ oft_info->xfer_reffed = FALSE;
+ gaim_xfer_unref(xfer);
+ }
+ gaim_debug_info("oscar","connection successful; timeout off\n");
}
+ } else {
+ gaim_debug_info("oscar","transfer already done; nothing to do\n");
}
return FALSE;
}
/*
- * This is a gaim timeout callback for when the verifiedip looks to be useless
- * It tries the file transfer again using the clientip
- *
- * BBB
- */
-static gboolean oscar_verifiedip_timeout(gpointer data) {
- GaimXfer *xfer;
- struct aim_oft_info *oft_info;
-
- gaim_debug_info("oscar","AAA - in oscar_verifiedip_timeout\n");
- xfer = (GaimXfer*) data;
- if(xfer->data) {
- oft_info = (struct aim_oft_info*) xfer->data;
-
- /* Check to see if the verifiedip has produced any results */
- if(!oft_info->success) {
- /* gaim_xfer_conversation_write(xfer,
- "Attempting file transfer via secondary IP address...", FALSE); */
-
- /* The verifiedip connection has worn out its welcome. Goodbye. */
- aim_conn_kill(oft_info->sess, &oft_info->conn);
-
- /* Try the file transfer again with the clientip */
- g_free(xfer->remote_ip);
- xfer->remote_ip = g_strdup(oft_info->clientip);
- gaim_debug_info("oscar","attempting connection using clientip\n");
- oscar_xfer_init_recv(xfer);
- } else {
- gaim_debug_info("oscar","connection successful; no action taken\n");
- }
- }
- return FALSE;
-}
-
-/*
- * xfer functions used when receiving files
+ * Connect to another client or a file transfer proxy server.
+ * Though this function has traditionally only been used during file receives,
+ * it is now called to make any sort of file transfer connection via gaim_proxy_connect.
*/
static void oscar_xfer_init_recv(GaimXfer *xfer)
{
struct aim_oft_info *oft_info;
+ struct aim_rv_proxy_info *proxy_info;
GaimConnection *gc;
OscarData *od;
+ GaimInputFunction nextstop_cb;
+ int rc;
g_return_if_fail(xfer != NULL);
g_return_if_fail(xfer->data != NULL);
oft_info = xfer->data;
+ proxy_info = oft_info->proxy_info;
gc = oft_info->sess->aux_data;
od = gc->proto_data;
- gaim_debug_info("oscar", "AAA - in oscar_xfer_recv_init\n");
+ gaim_debug_info("oscar", "AAA - in oscar_xfer_init_recv\n");
/* Start a timer for this ip address
- * If the verifiedip fails, try the clientip
- * If clientip fails, declare the whole file transfer dead
- * This xfer reference will be released in oscar_clientip_timeout */
- if(xfer->data) {
+ * If the clientip fails, try the verifiedip
+ * If that fails, wait for the transfer to redirect
+ * This xfer reference will be released in oscar_xfer_ip_timeout */
+ if(!oft_info->xfer_reffed) {
+ oft_info->xfer_reffed = TRUE;
gaim_xfer_ref(xfer);
- /* If clientip & verifiedip are the same, we must prevent an infinite loop */
- if(g_ascii_strcasecmp(xfer->remote_ip, oft_info->verifiedip) == 0
+ }
+
+ if(oft_info->method != AIM_XFER_PROXY) {
+ /* If (we're currently using the verified ip)
+ * In case clientip & verifiedip are the same, we must prevent an infinite loop */
+ if(xfer->remote_ip && oft_info->verifiedip
+ && g_ascii_strcasecmp(xfer->remote_ip, oft_info->verifiedip) == 0
&& g_ascii_strcasecmp(oft_info->clientip, oft_info->verifiedip) != 0 ) {
- gaim_timeout_add(FT_IP_TIMEOUT * 1000, oscar_verifiedip_timeout, xfer);
+ gaim_timeout_add(FT_VERIFIEDIP_TIMEOUT, oscar_xfer_ip_timeout, xfer);
} else {
- gaim_timeout_add(FT_IP_TIMEOUT * 1000, oscar_clientip_timeout, xfer);
+ gaim_timeout_add(FT_CLIENTIP_TIMEOUT, oscar_xfer_ip_timeout, xfer);
}
- }
-
+ } else {
+ gaim_timeout_add(FT_PROXYIP_TIMEOUT, oscar_xfer_ip_timeout, xfer);
+ }
oft_info->conn = aim_newconn(od->sess, AIM_CONN_TYPE_RENDEZVOUS, NULL);
+
+ /* If we're routing this transfer through a AOL proxy server, do the special login
+ * before telling the other client we're ready for action.
+ * Note, firststop_cb is the first function called after gaim has made a connection
+ * Also, the connection type is changed until the proxy login is complete */
+ if(oft_info->method == AIM_XFER_PROXY) {
+ if(proxy_info)
+ proxy_info->conn = oft_info->conn;
+ else {
+ gaim_debug_warning("oscar","NULL proxy_info\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+ nextstop_cb = oscar_xfer_proxylogin;
+ oft_info->conn->type = AIM_CONN_TYPE_RENDEZVOUS_PROXY;
+ } else {
+ nextstop_cb = oscar_sendfile_connected;
+ }
+
if (oft_info->conn) {
oft_info->conn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE;
- aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT, oscar_sendfile_prompt, 0);
- oft_info->conn->fd = xfer->fd = gaim_proxy_connect(gaim_connection_get_account(gc),
- xfer->remote_ip, xfer->remote_port, oscar_sendfile_connected, xfer);
- if (xfer->fd == -1) {
+ aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_PROMPT,
+ oscar_sendfile_prompt, 0);
+ rc = gaim_proxy_connect(gaim_connection_get_account(gc),
+ xfer->remote_ip, xfer->remote_port, nextstop_cb, xfer);
+ if (rc == -1) {
gaim_xfer_error(GAIM_XFER_RECEIVE, xfer->who,
_("Unable to establish file descriptor."));
gaim_xfer_cancel_local(xfer);
@@ -2058,9 +2158,11 @@
gaim_xfer_cancel_local(xfer);
/* Try a different port? Ask them to connect to us? /join #gaim and whine? */
}
-
-}
-
+}
+
+/*
+ * "Never mind. This transfer wasn't such a great idea after all."
+ */
static void oscar_xfer_cancel_recv(GaimXfer *xfer)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -2078,6 +2180,9 @@
od->file_transfers = g_slist_remove(od->file_transfers, xfer);
}
+/*
+ * Called after every data packet we receive
+ */
static void oscar_xfer_ack_recv(GaimXfer *xfer, const char *buffer, size_t size)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -2087,9 +2192,287 @@
}
/*
+ * xfer functions for proxied transfers
+ */
+
+/*
+ * Called by oscar_send_proxylogin_cb when we receive a ready packet
+ * BBB
+ */
+void oscar_xfer_proxylogin_ready(GaimXfer *xfer, gint fd) {
+ struct aim_oft_info *oft_info;
+ struct aim_rv_proxy_info *proxy_info;
+
+ gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin_ready\n");
+ if (!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL oft_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if (!(proxy_info = oft_info->proxy_info)) {
+ gaim_debug_warning("oscar","NULL proxy_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+
+ /* Remove the rv proxy watcher and put the connection type back the way we found it */
+ gaim_input_remove(xfer->watcher);
+ xfer->watcher = 0;
+ oft_info->conn->type = AIM_CONN_TYPE_RENDEZVOUS;
+
+ if(oft_info->send_or_recv == AIM_XFER_SEND) {
+
+ if(oft_info->stage == AIM_XFER_PROXY_STG2) {
+ aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info);
+
+ /* For stage 2, both file headers are filled in */
+ memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8);
+ }
+
+ /* The following is taken from oscar_sendfile_estblsh */
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+ oscar_sendfile_ack, 0);
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+ oscar_sendfile_done, 0);
+ xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback,
+ oft_info->conn);
+
+ /* Inform the other user that we are connected and ready to transfer */
+ aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info);
+ } else if(oft_info->send_or_recv == AIM_XFER_RECV) {
+ oscar_sendfile_connected(xfer, fd, GAIM_INPUT_READ);
+ } else {
+ gaim_debug_warning("oscar","no value for send_or_recv; aborting transfer\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+}
+
+/*
+ * Called by oscar_sendfile_proxylogin_cb when we receive an ack packet in reply to an init_send
+ * BBB
+ */
+void oscar_xfer_proxylogin_ack(GaimXfer *xfer) {
+ struct aim_oft_info *oft_info;
+ struct aim_rv_proxy_info *proxy_info;
+
+ gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin_ack\n");
+ if (!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL oft_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if (!(proxy_info = oft_info->proxy_info)) {
+ gaim_debug_warning("oscar","NULL proxy_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+
+ /* Use the proxy "port" we just ACK-quired (hah) so that the proxy will love us */
+ oft_info->port = proxy_info->port;
+ oft_info->proxyip = g_strdup(proxy_info->ip);
+ gaim_debug_info("oscar","received client ip and port: %s:%d\n",
+ oft_info->proxyip, oft_info->port);
+
+ if(oft_info->send_or_recv == AIM_XFER_SEND) {
+ oscar_send_file_request(xfer);
+ } else if(oft_info->send_or_recv == AIM_XFER_RECV) {
+ strncpy(oft_info->fh.name, xfer->filename, 64);
+ oft_info->fh.name[63] = '\0';
+ oft_info->fh.totsize = gaim_xfer_get_size(xfer);
+ oft_info->fh.size = gaim_xfer_get_size(xfer);
+ oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename);
+
+ aim_im_sendch2_sendfile_ask(oft_info->sess, oft_info);
+ } else {
+ gaim_debug_warning("oscar","no value for send_or_recv; aborting transfer\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+}
+
+/*
+ * This is called whenever we receive data while negotiating a rendezvous proxy connection
+ * BBB
+ */
+static void oscar_xfer_proxylogin_cb(gpointer data, gint source, GaimInputCondition condition) {
+ GaimXfer *xfer;
+ struct aim_oft_info *oft_info;
+
+ gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin_cb\n");
+ if (!(xfer = data)) {
+ gaim_debug_warning("oscar","NULL xfer; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if (!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL oft_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+
+ if( (oft_info->proxy_info = aim_rv_proxy_read(oft_info->sess, oft_info->conn)) ) {
+
+ switch(oft_info->proxy_info->cmd_type) {
+ case AIM_RV_PROXY_READY:
+ oscar_xfer_proxylogin_ready(xfer, source);
+ free(oft_info->proxy_info);
+ oft_info->proxy_info = NULL;
+ break;
+ case AIM_RV_PROXY_ACK:
+ oscar_xfer_proxylogin_ack(xfer);
+ free(oft_info->proxy_info);
+ oft_info->proxy_info = NULL;
+ break;
+ case AIM_RV_PROXY_ERROR:
+ gaim_debug_info("oscar","error logging into rendezvous proxy; err code is %x\n",
+ oft_info->proxy_info->err_code);
+ gaim_input_remove(xfer->watcher);
+ xfer->watcher = 0;
+ free(oft_info->proxy_info);
+ oft_info->proxy_info = NULL;
+ gaim_xfer_cancel_remote(xfer);
+ break;
+ /* We should never get here */
+ default:
+ gaim_debug_info("oscar","proxylogin switch defaulted unexpectedly\n");
+ }
+ } else {
+ gaim_debug_info("oscar","could not read rv proxy packet\n");
+ }
+}
+
+/*
+ * Called to send necessary login data to a rendezvous proxy server once we're connected
+ * Takes xfer is data and fd as source
+ */
+static void oscar_xfer_proxylogin(gpointer data, gint source, GaimInputCondition condition)
+{
+ GaimXfer *xfer;
+ struct aim_oft_info *oft_info;
+ struct aim_rv_proxy_info *proxy_info;
+ int err;
+
+ gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin\n");
+ if (!(xfer = data)) {
+ gaim_debug_warning("oscar","NULL xfer; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if (!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL oft_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if (!(proxy_info = oft_info->proxy_info)) {
+ gaim_debug_warning("oscar","NULL proxy_info; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ return;
+ }
+ if(oft_info->success) {
+ gaim_debug_info("oscar","connection already successful, ignoring 2nd conn\n");
+ return;
+ }
+
+ xfer->fd = source;
+ oft_info->conn->fd = source;
+
+ proxy_info->conn = oft_info->conn;
+ proxy_info->flags = AIM_RV_PROXY_CLIENT_FLAGS;
+ memcpy(proxy_info->cookie, oft_info->cookie, 8);
+
+ if(oft_info->send_or_recv == AIM_XFER_SEND) {
+ if(oft_info->stage == AIM_XFER_PROXY_STG1 || oft_info->stage == AIM_XFER_PROXY_STG3) {
+ gaim_debug_info("oscar","sending INIT SEND for stage 1/3 rv proxied send\n");
+ if( (err = aim_rv_proxy_init_send(proxy_info)) ) {
+ gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
+ _("Unable to log into file transfer proxy."));
+ gaim_debug_info("oscar", "error while sending INIT SEND rv proxy packet: %s\n",
+ strerror(err));
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else if(oft_info->stage == AIM_XFER_PROXY_STG2) {
+ gaim_debug_info("oscar","sending INIT RECV for stage 2 rv proxied send\n");
+ if( (err = aim_rv_proxy_init_recv(proxy_info)) ) {
+ gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
+ _("Unable to log into file transfer proxy."));
+ gaim_debug_info("oscar", "error while sending INIT RECV rv proxy packet: %s\n",
+ strerror(err));
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else {
+ gaim_debug_warning("oscar","no proxy type specified; aborting transfer\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else if(oft_info->send_or_recv == AIM_XFER_RECV) {
+ if(oft_info->stage == AIM_XFER_PROXY_STG2) {
+ gaim_debug_info("oscar","sending INIT SEND for stage 2 rv proxied receive\n");
+ if( (err = aim_rv_proxy_init_send(proxy_info)) ) {
+ gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
+ _("Unable to log into file transfer proxy."));
+ gaim_debug_info("oscar", "error while sending INIT SEND rv proxy packet: %s\n",
+ strerror(err));
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else if(oft_info->stage == AIM_XFER_PROXY_STG1
+ || oft_info->stage == AIM_XFER_PROXY_STG3) {
+ gaim_debug_info("oscar","sending INIT RECV for stage 1/3 rv proxied receive\n");
+ if( (err = aim_rv_proxy_init_recv(proxy_info)) ) {
+ gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
+ _("Unable to log into file transfer proxy."));
+ gaim_debug_info("oscar", "error while sending INIT RECV rv proxy packet: %s\n",
+ strerror(err));
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else {
+ gaim_debug_warning("oscar","no proxy type specified; aborting transfer\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+ } else {
+ gaim_debug_warning("oscar","no send_or_recv value specified; aborting\n");
+ gaim_xfer_cancel_local(xfer);
+ }
+ free(proxy_info);
+ oft_info->proxy_info = NULL;
+
+ xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_xfer_proxylogin_cb, xfer);
+}
+
+/*
* xfer functions used when sending files
*/
+/*
+ * Send a request to another client notifying them we want to sent a file
+ */
+static void oscar_send_file_request(GaimXfer *xfer)
+{
+ struct aim_oft_info *oft_info = xfer->data;
+ GaimConnection *gc = oft_info->sess->aux_data;
+ OscarData *od = gc->proto_data;
+
+ gaim_debug_info("oscar", "AAA - in oscar_send_file_request\n");
+
+ if (oft_info->conn) {
+ xfer->filename = g_path_get_basename(xfer->local_filename);
+ strncpy(oft_info->fh.name, xfer->filename, 64);
+ oft_info->fh.name[63] = '\0';
+ oft_info->fh.totsize = gaim_xfer_get_size(xfer);
+ oft_info->fh.size = gaim_xfer_get_size(xfer);
+ oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename);
+
+ aim_im_sendch2_sendfile_ask(od->sess, oft_info);
+ aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT,
+ AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0);
+ } else {
+ gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
+ _("Unable to establish listener socket or no AOL proxy connection present."));
+ gaim_xfer_cancel_local(xfer);
+ }
+}
+
+/*
+ * Opens a listener socket in preparation for sending a file
+ * This is not called if we are using a rendezvous proxy server
+ */
static void oscar_xfer_init_send(GaimXfer *xfer)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -2097,15 +2480,8 @@
OscarData *od = gc->proto_data;
int listenfd;
- gaim_debug_info("oscar", "AAA - in oscar_xfer_send_init\n");
-
- xfer->filename = g_path_get_basename(xfer->local_filename);
- strncpy(oft_info->fh.name, xfer->filename, 64);
- oft_info->fh.name[63] = '\0';
- oft_info->fh.totsize = gaim_xfer_get_size(xfer);
- oft_info->fh.size = gaim_xfer_get_size(xfer);
- oft_info->fh.checksum = aim_oft_checksum_file(xfer->local_filename);
-
+ gaim_debug_info("oscar", "AAA - in oscar_xfer_init_send\n");
+
/* Create a listening socket and an associated libfaim conn */
if ((listenfd = gaim_network_listen_range(5190, 5199)) < 0) {
gaim_xfer_cancel_local(xfer);
@@ -2120,17 +2496,19 @@
gaim_debug_misc("oscar",
"port is %hu, ip is %s\n",
xfer->local_port, oft_info->clientip);
- if (oft_info->conn) {
- xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
- aim_im_sendch2_sendfile_ask(od->sess, oft_info);
- aim_conn_addhandler(od->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED, oscar_sendfile_estblsh, 0);
- } else {
- gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
- _("Unable to establish listener socket."));
- gaim_xfer_cancel_local(xfer);
- }
-}
-
+
+ if(oft_info->conn)
+ xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback,
+ oft_info->conn);
+ else
+ gaim_debug_info("oscar","NULL oft_info->conn; not adding watcher\n");
+
+ oscar_send_file_request(xfer);
+}
+
+/*
+ * "On second thought, you don't deserve this file."
+ */
static void oscar_xfer_cancel_send(GaimXfer *xfer)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -2142,12 +2520,20 @@
if (gaim_xfer_get_status(xfer) != GAIM_XFER_STATUS_CANCEL_REMOTE)
aim_im_sendch2_sendfile_cancel(oft_info->sess, oft_info);
- aim_conn_kill(oft_info->sess, &oft_info->conn);
- aim_oft_destroyinfo(oft_info);
+ /* Added a few sanity checks to prevent segfaulting
+ * Better safe than sorry */
+ if(oft_info) {
+ if(oft_info->sess && oft_info->conn)
+ aim_conn_kill(oft_info->sess, &oft_info->conn);
+ aim_oft_destroyinfo(oft_info);
+ }
xfer->data = NULL;
od->file_transfers = g_slist_remove(od->file_transfers, xfer);
}
+/*
+ * Called when we send some data to the other client
+ */
static void oscar_xfer_ack_send(GaimXfer *xfer, const char *buffer, size_t size)
{
struct aim_oft_info *oft_info = xfer->data;
@@ -2176,6 +2562,10 @@
}
}
+/*
+ * Called by the Gaim core to determine whether or not we're allowed to send a file
+ * to this user.
+ */
static gboolean oscar_can_receive_file(GaimConnection *gc, const char *who) {
gboolean can_receive = FALSE;
OscarData *od = gc->proto_data;
@@ -2190,13 +2580,21 @@
return can_receive;
}
+/*
+ * Called by the Gaim core when the user indicates that a file is to be sent to
+ * a special someone.
+ */
static void oscar_send_file(GaimConnection *gc, const char *who, const char *file) {
-
OscarData *od;
GaimXfer *xfer;
struct aim_oft_info *oft_info;
const char *ip;
-
+ gboolean use_rv_proxy;
+
+ use_rv_proxy = gaim_prefs_get_bool("/plugins/prpl/oscar/use_rv_proxy");
+ if(use_rv_proxy)
+ gaim_debug_info("oscar","using stage 1 proxied transfer\n");
+
od = (OscarData *)gc->proto_data;
/* You want to send a file to someone else, you're so generous */
@@ -2205,12 +2603,28 @@
xfer = gaim_xfer_new(gc->account, GAIM_XFER_SEND, who);
/* Create the oscar-specific data */
- ip = gaim_network_get_my_ip(od->conn ? od->conn->fd : -1);
- oft_info = aim_oft_createinfo(od->sess, NULL, who, ip, 0, 0, 0, NULL);
+ if (use_rv_proxy) {
+ /* This hostname will be resolved by gaim_proxy_connect */
+ xfer->remote_ip = AIM_RV_PROXY_SERVER_URL;
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+ oft_info = aim_oft_createinfo(od->sess, NULL /*cookie*/, who, 0 /*ip*/, 0, 0, 0, NULL,
+ AIM_XFER_SEND, AIM_XFER_PROXY, AIM_XFER_PROXY_STG1);
+ oft_info->proxy_info = aim_rv_proxy_createinfo(oft_info->sess, NULL, 0);
+ /* We must create a cookie before the request is sent
+ * so that it can be sent to the proxy */
+ aim_im_makecookie(oft_info->cookie);
+ } else {
+ ip = gaim_network_get_my_ip(od->conn ? od->conn->fd : -1);
+ oft_info = aim_oft_createinfo(od->sess, NULL, who, ip, 0, 0, 0, NULL,
+ AIM_XFER_SEND, AIM_XFER_DIRECT, AIM_XFER_PROXY_NONE);
+ }
xfer->data = oft_info;
- /* Setup our I/O op functions */
- gaim_xfer_set_init_fnc(xfer, oscar_xfer_init_send);
+ /* Setup our I/O op functions */
+ if (use_rv_proxy)
+ gaim_xfer_set_init_fnc(xfer, oscar_xfer_init_recv);
+ else
+ gaim_xfer_set_init_fnc(xfer, oscar_xfer_init_send);
gaim_xfer_set_end_fnc(xfer, oscar_xfer_end);
gaim_xfer_set_cancel_send_fnc(xfer, oscar_xfer_cancel_send);
gaim_xfer_set_request_denied_fnc(xfer, oscar_xfer_cancel_send);
@@ -2226,6 +2640,11 @@
gaim_xfer_request(xfer);
}
+/*
+ * End (for the most part) of file transfer code
+ * There's a bit more in incomingim_chan2()
+ */
+
static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) {
GaimConnection *gc = sess->aux_data;
OscarData *od = gc->proto_data;
@@ -3157,9 +3576,10 @@
/* BBB */
/*
- * This is called after a remote AIM user has connected to us. We
- * want to do some voodoo with the socket file descriptors, add a
- * callback or two, and then send the AIM_CB_OFT_PROMPT.
+ * This is called after a remote AIM user has connected to us.
+ * If not using a rendezvous proxy, then we want to do some
+ * voodoo with the socket file descriptors. Then we always
+ * add a callback or two, and then send the AIM_CB_OFT_PROMPT.
*/
static int oscar_sendfile_estblsh(aim_session_t *sess, aim_frame_t *fr, ...) {
GaimConnection *gc = sess->aux_data;
@@ -3169,39 +3589,64 @@
va_list ap;
aim_conn_t *conn, *listenerconn;
- gaim_debug_info("oscar",
- "AAA - in oscar_sendfile_estblsh\n");
+ gaim_debug_info("oscar", "AAA - in oscar_sendfile_estblsh\n");
+
va_start(ap, fr);
conn = va_arg(ap, aim_conn_t *);
listenerconn = va_arg(ap, aim_conn_t *);
va_end(ap);
- if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn)))
- return 1;
-
- if (!(oft_info = xfer->data))
+ /* Finding by conn will work for proxied connections only
+ * Finding by listenerconn will work for direct connections only */
+ if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn))) {
+ if(!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn))) {
+ gaim_debug_warning("oscar","xfer not found via connection\n");
+ return 1;
+ }
+ }
+ if (!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL data\n");
return 1;
-
- /* Stop watching listener conn; watch transfer conn instead */
- gaim_input_remove(xfer->watcher);
- aim_conn_kill(sess, &listenerconn);
-
- oft_info->conn = conn;
- xfer->fd = oft_info->conn->fd;
-
- aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK, oscar_sendfile_ack, 0);
- aim_conn_addhandler(sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE, oscar_sendfile_done, 0);
+ }
+
+ /* Mark connection as success so further connections aren't attempted
+ * This is important here since some receive file code paths pass through here */
+ oft_info->success = TRUE;
+
+ if(oft_info->method != AIM_XFER_PROXY) {
+ /* Stop watching listener conn; watch transfer conn instead */
+ gaim_input_remove(xfer->watcher);
+
+ aim_conn_kill(sess, &listenerconn);
+
+ oft_info->conn = conn;
+ xfer->fd = oft_info->conn->fd;
+ }
+
xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
-
- /* Inform the other user that we are connected and ready to transfer */
- aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info);
-
+
+ if(oft_info->send_or_recv == AIM_XFER_SEND) {
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+ oscar_sendfile_ack, 0);
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+ oscar_sendfile_done, 0);
+
+ /* Inform the other user that we are connected and ready to transfer */
+ aim_oft_sendheader(sess, AIM_CB_OFT_PROMPT, oft_info);
+ }
+
+ /* For a file send, we'll hopefully end up in oscar_sendfile_ack next
+ * For a file receive, oscar_sendfile_prompt */
+
return 0;
}
/*
* This is the gaim callback passed to gaim_proxy_connect when connecting to another AIM
- * user in order to transfer a file.
+ * user in order to transfer a file
+ * Takes xfer as data and fd as source
+ *
+ * BBB
*/
static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) {
GaimXfer *xfer;
@@ -3213,34 +3658,46 @@
return;
if (!(oft_info = xfer->data))
return;
+ if(oft_info->success) {
+ gaim_debug_info("oscar","connection already successful; ignoring 2nd conn\n");
+ return;
+ }
if (source < 0) {
- /* This will also be called 3 minutes after the verifiedip times out.
- * However, we might have made a successful connection with the clientip
- * so we need to make sure the verifiedip's failures aren't taken out
- * on the poor little clientip, which might actually have been a success. */
- if(oft_info->success) {
- gaim_debug_info("oscar","fd of 0 for verifiedip, but clientip succeeded; ignoring\n");
- return;
- } else {
- gaim_debug_info("oscar","received source of 0 for fd; aborting transfer\n");
- gaim_xfer_cancel_remote(xfer);
- return;
- }
- }
-
- gaim_debug_info("oscar","marking connection as success\n");
+ gaim_debug_info("oscar","received fd of %d; aborting transfer\n", source);
+ gaim_xfer_cancel_remote(xfer);
+ return;
+ }
oft_info->success = TRUE; /* Mark this connection as successful before it times out */
+ /* We might have already set these in oscar_sendfile_proxylogin, but it won't
+ * hurt to do it again since it is rather necessary */
xfer->fd = source;
oft_info->conn->fd = source;
-
+
aim_conn_completeconnect(oft_info->sess, oft_info->conn);
+
xfer->watcher = gaim_input_add(xfer->fd, GAIM_INPUT_READ, oscar_callback, oft_info->conn);
- /* Inform the other user that we are connected and ready to transfer */
- aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info);
-
- return;
+ /* Inform the other user that we are connected and accept the transfer
+ * Except for a stage 2 receive, then we'll be the ones receiving this accept message */
+ if(oft_info->stage != AIM_XFER_PROXY_STG2)
+ aim_im_sendch2_sendfile_accept(oft_info->sess, oft_info);
+
+ /* Don't wait around if this is a redirected send */
+ if(oft_info->send_or_recv == AIM_XFER_SEND) {
+ /* We should only get here if this is a redirected file send */
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_ACK,
+ oscar_sendfile_ack, 0);
+ aim_conn_addhandler(oft_info->sess, oft_info->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DONE,
+ oscar_sendfile_done, 0);
+
+ /* Redirected connections won't love us unless we give them a cookie */
+ if(oft_info->method == AIM_XFER_REDIR)
+ memcpy(&oft_info->fh.bcookie, oft_info->cookie, 8);
+
+ /* Inform the other user that we are ready to transfer */
+ aim_oft_sendheader(oft_info->sess, AIM_CB_OFT_PROMPT, oft_info);
+ }
}
/*
@@ -3258,9 +3715,10 @@
aim_conn_t *conn;
fu8_t *cookie;
struct aim_fileheader_t *fh;
-
+
gaim_debug_info("oscar",
"AAA - in oscar_sendfile_prompt\n");
+
va_start(ap, fr);
conn = va_arg(ap, aim_conn_t *);
cookie = va_arg(ap, fu8_t *);
@@ -3334,6 +3792,7 @@
aim_conn_t *conn;
fu8_t *cookie;
struct aim_fileheader_t *fh;
+ struct aim_oft_info *oft_info;
gaim_debug_info("oscar", "AAA - in oscar_sendfile_done\n");
va_start(ap, fr);
@@ -3342,8 +3801,18 @@
fh = va_arg(ap, struct aim_fileheader_t *);
va_end(ap);
- if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn)))
+ if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, conn))) {
+ gaim_debug_warning("oscar","xfer not found\n");
return 1;
+ }
+ if(!(oft_info = xfer->data)) {
+ gaim_debug_warning("oscar","NULL oft_info\n");
+ return 1;
+ }
+ if(oft_info->xfer_reffed) {
+ oft_info->xfer_reffed = FALSE;
+ gaim_xfer_unref(xfer);
+ }
xfer->fd = conn->fd;
gaim_xfer_end(xfer);
@@ -3516,21 +3985,25 @@
g_free(name);
} else if (args->reqclass & AIM_CAPS_SENDFILE) {
/* BBB */
- if (args->status == AIM_RENDEZVOUS_PROPOSE) {
+ /* This is the first sendfile request where we need to notify the user that someone
+ * wants to send a file */
+ if (args->status == AIM_RENDEZVOUS_PROPOSE
+ && (args->info.sendfile.reqnum == 0x0001)) {
/* Someone wants to send a file (or files) to us */
GaimXfer *xfer;
struct aim_oft_info *oft_info;
-
- if (!args->cookie || !args->port || !args->verifiedip ||
+ struct aim_rv_proxy_info *proxy_info = NULL;
+ gboolean use_rv_proxy;
+ int proxy_stage;
+ int xfer_method;
+ const char *proxy_ip = NULL;
+
+ if (!args->cookie || !args->port ||
!args->info.sendfile.filename || !args->info.sendfile.totsize ||
!args->info.sendfile.totfiles || !args->reqclass) {
gaim_debug_warning("oscar",
"%s tried to send you a file with incomplete "
"information.\n", userinfo->sn);
- if (args->proxyip)
- gaim_debug_warning("oscar",
- "IP for a proxy server was given. Gaim "
- "does not support this yet.\n");
g_free(message);
return 1;
}
@@ -3550,9 +4023,37 @@
/* Build the file transfer handle */
xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, userinfo->sn);
- xfer->remote_ip = g_strdup(args->verifiedip);
- xfer->remote_port = args->port;
-
+
+ use_rv_proxy = gaim_prefs_get_bool("/plugins/prpl/oscar/use_rv_proxy");
+
+ if(args->info.sendfile.use_proxy) {
+ /* The sender requested (stage 1) that we use a rendezvous proxy */
+ xfer_method = AIM_XFER_PROXY;
+ proxy_stage = AIM_XFER_PROXY_STG1;
+ gaim_debug_info("oscar","using stage 1 proxy with ip: %s\n",
+ args->proxyip, args->port);
+ xfer->remote_ip = g_strdup(args->proxyip);
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+ proxy_info = aim_rv_proxy_createinfo(od->sess, args->cookie, args->port);
+ } else if(use_rv_proxy) {
+ /* If the local user indicated that a rendezvous proxy is necessary
+ * start a stage 2 proxied transfer */
+ gaim_debug_info("oscar","using stage 2 proxied transfer\n");
+ xfer_method = AIM_XFER_PROXY;
+ proxy_stage = AIM_XFER_PROXY_STG2;
+ /* This hostname will be resolved by gaim_proxy_connect */
+ xfer->remote_ip = AIM_RV_PROXY_SERVER_URL;
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+ proxy_info = aim_rv_proxy_createinfo(od->sess, args->cookie, 0);
+ } else {
+ /* We are receiving a file directly with no rendezvous proxy */
+ xfer_method = AIM_XFER_DIRECT;
+ proxy_stage = AIM_XFER_PROXY_NONE;
+ xfer->remote_ip = g_strdup(args->clientip);
+ xfer->remote_port = args->port;
+ }
+
+ /* Use UTF8 so that the world will be a happier place */
if (g_utf8_validate(args->info.sendfile.filename, -1,
NULL)) {
gaim_xfer_set_filename(xfer,
@@ -3566,20 +4067,30 @@
gaim_xfer_set_size(xfer, args->info.sendfile.totsize);
- /* Ignore <ICQ_COOL_FT> XML that is sent along with ICQ sendfile requests */
- if(g_ascii_strncasecmp(message,"<ICQ_COOL_FT>",13)) {
- gaim_debug_info("oscar","Ignoring ICQ file transfer message: %s\n", message);
+ /* Ignore messages that start with <ICQ_COOL_FT> (XML that is sent along
+ * with ICQ sendfile requests) & <HTML> message that is sent with AOL file
+ * transfers (Note: this latter message is ignored only if whole message
+ * is <HTML>, but not if it starts with <HTML> */
+ if(message && ( g_ascii_strncasecmp(message,"<ICQ_COOL_FT>",13) == 0
+ || g_ascii_strcasecmp(message,"<HTML>") == 0) ) {
+ gaim_debug_info("oscar","Ignoring file transfer message: %s\n", message);
g_free(message);
message = NULL;
}
gaim_xfer_set_message(xfer, message);
/* Create the oscar-specific data */
- oft_info = aim_oft_createinfo(od->sess, args->cookie, userinfo->sn, args->clientip, xfer->remote_port, 0, 0, NULL);
- if (args->proxyip)
- oft_info->proxyip = g_strdup(args->proxyip);
- if (args->verifiedip)
- oft_info->verifiedip = g_strdup(args->verifiedip);
+ oft_info = aim_oft_createinfo(od->sess, args->cookie, userinfo->sn, args->clientip,
+ xfer->remote_port, 0, 0, NULL, AIM_XFER_RECV, xfer_method, proxy_stage);
+ if(proxy_stage == AIM_XFER_PROXY_STG2 && proxy_ip) {
+ oft_info->proxyip = g_strdup(proxy_ip);
+ } else {
+ if (args->proxyip)
+ oft_info->proxyip = g_strdup(args->proxyip);
+ if (args->verifiedip)
+ oft_info->verifiedip = g_strdup(args->verifiedip);
+ }
+ oft_info->proxy_info = proxy_info;
xfer->data = oft_info;
/* Setup our I/O op functions */
@@ -3591,9 +4102,118 @@
/* Keep track of this transfer for later */
od->file_transfers = g_slist_append(od->file_transfers, xfer);
-
+
/* Now perform the request */
gaim_xfer_request(xfer);
+
+ /* A secondary request has been sent to negotiate the connection method */
+ } else if (args->status == AIM_RENDEZVOUS_PROPOSE && args->info.sendfile.reqnum == 0x0002) {
+ /* We have asked to send a file to someone else, but they sent us a reply request
+ * asking us to use an alternative method of connecting */
+ GaimXfer *xfer;
+ struct aim_oft_info *oft_info;
+
+ if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie))) {
+ oft_info = xfer->data;
+
+ /* Stop the listener connection */
+ gaim_input_remove(xfer->watcher);
+ aim_conn_kill(sess, &oft_info->conn); /* This is currently the listener */
+
+ if(args->info.sendfile.use_proxy) {
+ gaim_debug_info("oscar",
+ "received request for stage 2 rv proxy with ip: %s\n",
+ args->proxyip);
+ oft_info->method = AIM_XFER_PROXY;
+ oft_info->stage = AIM_XFER_PROXY_STG2;
+
+ oft_info->proxy_info = aim_rv_proxy_createinfo(oft_info->sess,
+ args->cookie, args->port);
+ if(args->proxyip) {
+ if(xfer->remote_ip)
+ g_free(xfer->remote_ip);
+ xfer->remote_ip = g_strdup(args->proxyip);
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+ oscar_xfer_init_recv(xfer);
+ } else {
+ gaim_debug_warning("oscar",
+ "stage 2 rv proxy file send: no proxy ip specified\n");
+ }
+ } else if(args->clientip
+ && g_ascii_strcasecmp(args->clientip,"0.0.0.0") == 0)
+ {
+ gaim_debug_warning("oscar",
+ "other client wants us to send stage 3 proxy info\n");
+ oft_info->method = AIM_XFER_PROXY;
+ oft_info->stage = AIM_XFER_PROXY_STG3;
+
+ /* Clean useless data from oft_info */
+ oft_info->clientip = NULL;
+ oft_info->verifiedip = NULL;
+
+ /* This hostname will be resolved in gaim_proxy_connect */
+ xfer->remote_ip = AIM_RV_PROXY_SERVER_URL;
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+
+ oft_info->proxy_info
+ = aim_rv_proxy_createinfo(od->sess, args->cookie, 0);
+ oscar_xfer_init_recv(xfer);
+ } else {
+ gaim_debug_info("oscar","received request to redirect transfer; clientip/verifiedip: %s / %s\n",
+ args->clientip, args->verifiedip);
+ oft_info->method = AIM_XFER_REDIR;
+ if (args->verifiedip)
+ oft_info->verifiedip = g_strdup(args->verifiedip);
+ if (args->clientip) {
+ oft_info->clientip = g_strdup(args->clientip);
+ xfer->remote_ip = g_strdup(args->clientip);
+ }
+ xfer->remote_port = args->port;
+
+ /* This file send will briefly follow file receive codepaths */
+ oscar_xfer_init_recv(xfer);
+ }
+ } else {
+ gaim_debug_warning("oscar","received file tranfer reply request: xfer not found\n");
+ }
+
+ /* A THIRD request has been sent trying to figure out what connection method will be used
+ * to transfer this file */
+ } else if (args->status == AIM_RENDEZVOUS_PROPOSE && args->info.sendfile.reqnum == 0x0003) {
+ /* We are receiving a file from someone. We sent a request to use a stage 3
+ * proxy. They did the initial proxy login and have sent us the info in a
+ * third file transfer request. */
+ GaimXfer *xfer;
+ struct aim_oft_info *oft_info;
+
+ if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie))) {
+ oft_info = xfer->data;
+
+ /* We are receiving a file */
+ gaim_debug_info("oscar",
+ "other client sent us stage 3 proxy info\n");
+
+ /* The following pieces of information should already have
+ * been set in oscar_xfer_ip_timeout, but we'll list them
+ * again just for clarity. */
+ oft_info->method = AIM_XFER_PROXY;
+ oft_info->stage = AIM_XFER_PROXY_STG3;
+
+ oft_info->proxy_info = aim_rv_proxy_createinfo(oft_info->sess,
+ args->cookie, args->port);
+ if(args->proxyip) {
+ if(xfer->remote_ip)
+ g_free(xfer->remote_ip);
+ xfer->remote_ip = g_strdup(args->proxyip);
+ xfer->remote_port = AIM_RV_PROXY_CONNECT_PORT;
+ oscar_xfer_init_recv(xfer);
+ } else {
+ gaim_debug_warning("oscar",
+ "stage 3 rv proxy file receive: no proxy ip specified\n");
+ }
+ } else {
+ gaim_debug_warning("oscar","received file tranfer reply request: xfer not found\n");
+ }
} else if (args->status == AIM_RENDEZVOUS_CANCEL) {
/* The other user wants to cancel a file transfer */
GaimXfer *xfer;
@@ -3608,6 +4228,8 @@
* get this, then maybe a third party connected
* to us, and we shouldn't send them anything.
*/
+ gaim_debug_info("oscar",
+ "AAA - received chan 2 AIM_RENDEZVOUS_ACCEPT\n");
} else {
gaim_debug_error("oscar",
"unknown rendezvous status!\n");
@@ -7533,6 +8155,30 @@
return buf;
}
+
+static GaimPluginPrefFrame * oscar_pref_frame(GaimPlugin *plugin)
+{
+ GaimPluginPrefFrame *frame;
+ GaimPluginPref *ppref;
+
+ frame = gaim_plugin_pref_frame_new();
+
+ ppref = gaim_plugin_pref_new_with_label(_("File Transfers"));
+ gaim_plugin_pref_frame_add(frame, ppref);
+
+ ppref = gaim_plugin_pref_new_with_name_and_label(
+ "/plugins/prpl/oscar/use_rv_proxy",
+ _("Use AIM/ICQ proxy server (Slower/More Secure/Usually Works)"));
+ gaim_plugin_pref_frame_add(frame, ppref);
+
+ return frame;
+}
+
+static GaimPluginUiInfo prefs_info =
+{
+ oscar_pref_frame,
+};
+
static GaimPluginProtocolInfo prpl_info =
{
OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE,
@@ -7628,7 +8274,7 @@
NULL, /**< ui_info */
&prpl_info, /**< extra_info */
- NULL, /**< prefs_info */
+ &prefs_info, /**< prefs_info */
oscar_actions
};
@@ -7645,6 +8291,10 @@
option = gaim_account_option_string_new(_("Encoding"), "encoding", OSCAR_DEFAULT_CUSTOM_ENCODING);
prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
+
+ /* Preferences */
+ gaim_prefs_add_none("/plugins/prpl/oscar");
+ gaim_prefs_add_bool("/plugins/prpl/oscar/use_rv_proxy", FALSE);
}
GAIM_INIT_PLUGIN(oscar, init_plugin, info);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/rxqueue.c Mon Aug 15 02:36:36 2005 -0400
@@ -0,0 +1,278 @@
+/*
+ * This file contains the management routines for the receive
+ * (incoming packet) queue. The actual packet handlers are in
+ * rxhandlers.c.
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+
+/*
+ *
+ */
+faim_internal int aim_recv(int fd, void *buf, size_t count)
+{
+ int left, cur;
+
+ for (cur = 0, left = count; left; ) {
+ int ret;
+
+ ret = recv(fd, ((unsigned char *)buf)+cur, left, 0);
+
+ /* Of course EOF is an error, only morons disagree with that. */
+ if (ret <= 0)
+ return -1;
+
+ cur += ret;
+ left -= ret;
+ }
+
+ return cur;
+}
+
+/*
+ * Read into a byte stream. Will not read more than count, but may read
+ * less if there is not enough room in the stream buffer.
+ */
+faim_internal int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count)
+{
+ int red = 0;
+
+ if (!bs || (fd < 0) || (count < 0))
+ return -1;
+
+ if (count > (bs->len - bs->offset))
+ count = bs->len - bs->offset; /* truncate to remaining space */
+
+ if (count) {
+
+ red = aim_recv(fd, bs->data + bs->offset, count);
+
+ if (red <= 0)
+ return -1;
+ }
+
+ bs->offset += red;
+
+ return red;
+}
+
+/**
+ * Free an aim_frame_t
+ *
+ * @param frame The frame to free.
+ * @return -1 on error; 0 on success.
+ */
+faim_internal void aim_frame_destroy(aim_frame_t *frame)
+{
+
+ free(frame->data.data); /* XXX aim_bstream_free */
+ free(frame);
+
+ return;
+}
+
+/*
+ * Read a FLAP header from conn into fr, and return the number of
+ * bytes in the payload.
+ *
+ * @return -1 on error, otherwise return the length of the payload.
+ */
+static int aim_get_command_flap(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
+{
+ fu8_t hdr_raw[6];
+ aim_bstream_t hdr;
+
+ fr->hdrtype = AIM_FRAMETYPE_FLAP;
+
+ /*
+ * Read FLAP header. Six bytes total.
+ *
+ * Byte # | Description
+ * -------|-------------
+ * 0x00 | Always 0x2a
+ * 0x01 | Channel number, usually "2." "1" is used during login,
+ * | 4 is used during logoff.
+ * 0x02 | Sequence number, 2 bytes.
+ * 0x04 | Number of data bytes that follow, 2 bytes.
+ */
+ aim_bstream_init(&hdr, hdr_raw, sizeof(hdr_raw));
+ if (aim_bstream_recv(&hdr, conn->fd, 6) < 6) {
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ aim_bstream_rewind(&hdr);
+
+ /*
+ * This shouldn't happen unless the socket breaks, the server breaks,
+ * or we break. We must handle it just in case.
+ */
+ if (aimbs_get8(&hdr) != 0x2a) {
+ faimdprintf(sess, 0, "Invalid FLAP frame received on FLAP connection!");
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ fr->hdr.flap.channel = aimbs_get8(&hdr);
+ fr->hdr.flap.seqnum = aimbs_get16(&hdr);
+
+ return aimbs_get16(&hdr);
+}
+
+/*
+ * Read a rendezvous header from conn into fr, and return the number of
+ * bytes in the payload.
+ *
+ * @return -1 on error, otherwise return the length of the payload.
+ */
+static int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr)
+{
+ fu8_t hdr_raw[8];
+ aim_bstream_t hdr;
+
+ fr->hdrtype = AIM_FRAMETYPE_OFT;
+
+ /*
+ * Read rendezvous header
+ */
+ aim_bstream_init(&hdr, hdr_raw, sizeof(hdr_raw));
+ if (aim_bstream_recv(&hdr, conn->fd, 8) < 8) {
+ aim_conn_close(conn);
+ return -1;
+ }
+
+ aim_bstream_rewind(&hdr);
+
+ aimbs_getrawbuf(&hdr, fr->hdr.rend.magic, 4);
+ fr->hdr.rend.hdrlen = aimbs_get16(&hdr);
+ fr->hdr.rend.type = aimbs_get16(&hdr);
+
+ return fr->hdr.rend.hdrlen - 8;
+}
+
+/*
+ * Grab a single command sequence off the socket, and enqueue it in the incoming event queue
+ * in a separate struct.
+ *
+ * @return 0 on success, otherwise return the error number.
+ */
+faim_export int aim_get_command(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *fr;
+ int payloadlen;
+
+ if (!sess || !conn)
+ return -EINVAL;
+
+ if (conn->fd == -1)
+ return -1; /* it's an aim_conn_close()'d connection */
+
+ /* Zero is a valid file descriptor when stdin is closed
+ if (conn->fd < 3) // can happen when people abuse the interface
+ return -1;
+ */
+
+ if (conn->status & AIM_CONN_STATUS_INPROGRESS)
+ return aim_conn_completeconnect(sess, conn);
+
+ if (!(fr = (aim_frame_t *)calloc(sizeof(aim_frame_t), 1)))
+ return -ENOMEM;
+
+ /*
+ * Rendezvous (client to client) connections do not speak FLAP, so this
+ * function will break on them.
+ */
+ if (conn->type == AIM_CONN_TYPE_RENDEZVOUS)
+ payloadlen = aim_get_command_rendezvous(sess, conn, fr);
+ else if (conn->type == AIM_CONN_TYPE_LISTENER) {
+ faimdprintf(sess, 0, "AIM_CONN_TYPE_LISTENER on fd %d\n", conn->fd);
+ free(fr);
+ return -1;
+ } else
+ payloadlen = aim_get_command_flap(sess, conn, fr);
+
+ if (payloadlen < 0) {
+ free(fr);
+ return -1;
+ }
+
+ if (payloadlen > 0) {
+ fu8_t *payload = NULL;
+
+ if (!(payload = (fu8_t *) malloc(payloadlen))) {
+ aim_frame_destroy(fr);
+ return -1;
+ }
+
+ aim_bstream_init(&fr->data, payload, payloadlen);
+
+ /* read the payload */
+ if (aim_bstream_recv(&fr->data, conn->fd, payloadlen) < payloadlen) {
+ aim_frame_destroy(fr); /* free's payload */
+ aim_conn_close(conn);
+ return -1;
+ }
+ } else
+ aim_bstream_init(&fr->data, NULL, 0);
+
+ aim_bstream_rewind(&fr->data);
+
+ fr->conn = conn;
+
+ /* Enqueue this puppy */
+ fr->next = NULL; /* this will always be at the bottom */
+ if (sess->queue_incoming == NULL)
+ sess->queue_incoming = fr;
+ else {
+ aim_frame_t *cur;
+ for (cur = sess->queue_incoming; cur->next; cur = cur->next);
+ cur->next = fr;
+ }
+
+ fr->conn->lastactivity = time(NULL);
+
+ return 0;
+}
+
+/*
+ * Purge receive queue of all handled commands (->handled==1).
+ *
+ */
+faim_export void aim_purge_rxqueue(aim_session_t *sess)
+{
+ aim_frame_t *cur, **prev;
+
+ for (prev = &sess->queue_incoming; (cur = *prev); ) {
+ if (cur->handled) {
+ *prev = cur->next;
+ aim_frame_destroy(cur);
+ } else
+ prev = &cur->next;
+ }
+
+ return;
+}
+
+/*
+ * Since aim_get_command will aim_conn_kill dead connections, we need
+ * to clean up the rxqueue of unprocessed connections on that socket.
+ *
+ * XXX: this is something that was handled better in the old connection
+ * handling method, but eh.
+ */
+faim_internal void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *currx;
+
+ for (currx = sess->queue_incoming; currx; currx = currx->next) {
+ if ((!currx->handled) && (currx->conn == conn))
+ currx->handled = 1;
+ }
+
+ return;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/protocols/oscar/txqueue.c Mon Aug 15 02:36:36 2005 -0400
@@ -0,0 +1,417 @@
+/*
+ * txqueue.c
+ *
+ * Herein lies all the management routines for the transmit (Tx) queue.
+ *
+ */
+
+#define FAIM_INTERNAL
+#include <aim.h>
+
+#ifndef _WIN32
+#include <sys/socket.h>
+#else
+#include "win32dep.h"
+#endif
+
+/*
+ * Allocate a new tx frame.
+ *
+ * This is more for looks than anything else.
+ *
+ * Right now, that is. If/when we implement a pool of transmit
+ * frames, this will become the request-an-unused-frame part.
+ *
+ * framing = AIM_FRAMETYPE_OFT/FLAP
+ * chan = channel for FLAP, hdrtype for OFT
+ *
+ */
+faim_internal aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, fu8_t framing, fu16_t chan, int datalen)
+{
+ aim_frame_t *fr;
+
+ if (!sess || !conn) {
+ faimdprintf(sess, 0, "aim_tx_new: No session or no connection specified!\n");
+ return NULL;
+ }
+
+ /* For sanity... */
+ if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || (conn->type == AIM_CONN_TYPE_LISTENER)) {
+ if (framing != AIM_FRAMETYPE_OFT) {
+ faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for rendezvous connection\n");
+ return NULL;
+ }
+ } else {
+ if (framing != AIM_FRAMETYPE_FLAP) {
+ faimdprintf(sess, 0, "aim_tx_new: attempted to allocate inappropriate frame type for FLAP connection\n");
+ return NULL;
+ }
+ }
+
+ if (!(fr = (aim_frame_t *)calloc(1, sizeof(aim_frame_t))))
+ return NULL;
+
+ fr->conn = conn;
+ fr->hdrtype = framing;
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+ fr->hdr.flap.channel = chan;
+ else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
+ fr->hdr.rend.type = chan;
+ else
+ faimdprintf(sess, 0, "tx_new: unknown framing\n");
+
+ if (datalen > 0) {
+ fu8_t *data;
+
+ if (!(data = (unsigned char *)malloc(datalen))) {
+ aim_frame_destroy(fr);
+ return NULL;
+ }
+
+ aim_bstream_init(&fr->data, data, datalen);
+ }
+
+ return fr;
+}
+
+/*
+ * This increments the tx command count, and returns the seqnum
+ * that should be stamped on the next FLAP packet sent. This is
+ * normally called during the final step of packet preparation
+ * before enqueuement (in aim_tx_enqueue()).
+ */
+static flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn)
+{
+ flap_seqnum_t ret;
+
+ ret = ++conn->seqnum;
+
+ return ret;
+}
+
+/*
+ * The overall purpose here is to enqueue the passed in command struct
+ * into the outgoing (tx) queue. Basically...
+ * 1) Make a scope-irrelevant copy of the struct
+ * 3) Mark as not-sent-yet
+ * 4) Enqueue the struct into the list
+ * 6) Return
+ *
+ * Note that this is only used when doing queue-based transmitting;
+ * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased.
+ *
+ */
+static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr)
+{
+
+ if (!fr->conn) {
+ faimdprintf(sess, 1, "aim_tx_enqueue: WARNING: enqueueing packet with no connecetion\n");
+ fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS);
+ }
+
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP) {
+ /* assign seqnum -- XXX should really not assign until hardxmit */
+ fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+ }
+
+ fr->handled = 0; /* not sent yet */
+
+ /* see overhead note in aim_rxqueue counterpart */
+ if (!sess->queue_outgoing)
+ sess->queue_outgoing = fr;
+ else {
+ aim_frame_t *cur;
+ for (cur = sess->queue_outgoing; cur->next; cur = cur->next);
+ cur->next = fr;
+ }
+
+ return 0;
+}
+
+/*
+ * Parallel to aim_tx_enqueue__queuebased, however, this bypasses
+ * the whole queue mess when you want immediate writes to happen.
+ *
+ * Basically the same as its __queuebased couterpart, however
+ * instead of doing a list append, it just calls aim_tx_sendframe()
+ * right here.
+ *
+ */
+static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr)
+{
+ int ret;
+
+ if (!fr->conn) {
+ faimdprintf(sess, 1, "aim_tx_enqueue: ERROR: packet has no connection\n");
+ aim_frame_destroy(fr);
+ return 0;
+ }
+
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+ fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn);
+
+ fr->handled = 0; /* not sent yet */
+
+ ret = aim_tx_sendframe(sess, fr);
+
+ aim_frame_destroy(fr);
+
+ return ret;
+}
+
+faim_export int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *))
+{
+
+ if (what == AIM_TX_QUEUED)
+ sess->tx_enqueue = &aim_tx_enqueue__queuebased;
+ else if (what == AIM_TX_IMMEDIATE)
+ sess->tx_enqueue = &aim_tx_enqueue__immediate;
+ else if (what == AIM_TX_USER) {
+ if (!func)
+ return -EINVAL;
+ sess->tx_enqueue = func;
+ } else
+ return -EINVAL; /* unknown action */
+
+ return 0;
+}
+
+faim_internal int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr)
+{
+
+ /*
+ * If we want to send on a connection that is in progress, we have to force
+ * them to use the queue based version. Otherwise, use whatever they
+ * want.
+ */
+ if (fr && fr->conn &&
+ (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) {
+ return aim_tx_enqueue__queuebased(sess, fr);
+ }
+
+ return (*sess->tx_enqueue)(sess, fr);
+}
+
+static int aim_send(int fd, const void *buf, size_t count)
+{
+ int left, cur;
+
+ for (cur = 0, left = count; left; ) {
+ int ret;
+ ret = send(fd, ((unsigned char *)buf)+cur, left, 0);
+
+ if (ret == -1)
+ return -1;
+ else if (ret == 0)
+ return cur;
+
+ cur += ret;
+ left -= ret;
+ }
+
+ return cur;
+}
+
+int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count)
+{
+ int wrote = 0;
+
+ if (!bs || !conn || (count < 0))
+ return -EINVAL;
+
+ /* Make sure we don't send past the end of the bs */
+ if (count > aim_bstream_empty(bs))
+ count = aim_bstream_empty(bs); /* truncate to remaining space */
+
+ if (count) {
+ /*
+ * I need to rewrite this. "Updating the UI" doesn't make sense. The program is
+ * blocked and the UI can't redraw. We're blocking all of Gaim. We need to set
+ * up an actual txqueue and a GAIM_INPUT_WRITE callback and only write when we
+ * can. Why is this file called txqueue anyway? Lets rename it to txblock.
+ */
+ if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) &&
+ (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {
+ const char *sn = aim_odc_getsn(conn);
+ aim_rxcallback_t userfunc;
+
+ while (count - wrote > 1024) {
+ int ret;
+
+ ret = aim_send(conn->fd, bs->data + bs->offset + wrote, 1024);
+ if (ret > 0)
+ wrote += ret;
+ if (ret < 0)
+ return -1;
+ if ((userfunc=aim_callhandler(conn->sessv, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
+ userfunc(conn->sessv, NULL, sn, count-wrote>1024 ? ((double)wrote / count) : 1);
+ }
+ }
+
+ if (count - wrote) {
+ wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote);
+ }
+ }
+
+ bs->offset += wrote;
+
+ return wrote;
+}
+
+static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr)
+{
+ aim_bstream_t bs;
+ fu8_t *bs_raw;
+ int payloadlen, err = 0, bslen;
+
+ payloadlen = aim_bstream_curpos(&fr->data);
+
+ if (!(bs_raw = malloc(6 + payloadlen)))
+ return -ENOMEM;
+
+ aim_bstream_init(&bs, bs_raw, 6 + payloadlen);
+
+ /* FLAP header */
+ aimbs_put8(&bs, 0x2a);
+ aimbs_put8(&bs, fr->hdr.flap.channel);
+ aimbs_put16(&bs, fr->hdr.flap.seqnum);
+ aimbs_put16(&bs, payloadlen);
+
+ /* payload */
+ aim_bstream_rewind(&fr->data);
+ aimbs_putbs(&bs, &fr->data, payloadlen);
+
+ bslen = aim_bstream_curpos(&bs);
+ aim_bstream_rewind(&bs);
+ if (aim_bstream_send(&bs, fr->conn, bslen) != bslen)
+ err = -errno;
+
+ free(bs_raw); /* XXX aim_bstream_free */
+
+ fr->handled = 1;
+ fr->conn->lastactivity = time(NULL);
+
+ return err;
+}
+
+static int sendframe_rendezvous(aim_session_t *sess, aim_frame_t *fr)
+{
+ aim_bstream_t bs;
+ fu8_t *bs_raw;
+ int payloadlen, err = 0, bslen;
+
+ payloadlen = aim_bstream_curpos(&fr->data);
+
+ if (!(bs_raw = malloc(8 + payloadlen)))
+ return -ENOMEM;
+
+ aim_bstream_init(&bs, bs_raw, 8 + payloadlen);
+
+ /* Rendezvous header */
+ aimbs_putraw(&bs, fr->hdr.rend.magic, 4);
+ aimbs_put16(&bs, fr->hdr.rend.hdrlen);
+ aimbs_put16(&bs, fr->hdr.rend.type);
+
+ /* payload */
+ aim_bstream_rewind(&fr->data);
+ aimbs_putbs(&bs, &fr->data, payloadlen);
+
+ bslen = aim_bstream_curpos(&bs);
+ aim_bstream_rewind(&bs);
+ if (aim_bstream_send(&bs, fr->conn, bslen) != bslen)
+ err = -errno;
+
+ free(bs_raw); /* XXX aim_bstream_free */
+
+ fr->handled = 1;
+ fr->conn->lastactivity = time(NULL);
+
+ return err;
+}
+
+faim_internal int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr)
+{
+ if (fr->hdrtype == AIM_FRAMETYPE_FLAP)
+ return sendframe_flap(sess, fr);
+ else if (fr->hdrtype == AIM_FRAMETYPE_OFT)
+ return sendframe_rendezvous(sess, fr);
+
+ return -1;
+}
+
+faim_export int aim_tx_flushqueue(aim_session_t *sess)
+{
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+
+ if (cur->handled)
+ continue; /* already been sent */
+
+ if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS))
+ continue;
+
+ /*
+ * And now for the meager attempt to force transmit
+ * latency and avoid missed messages.
+ */
+ if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) {
+ /*
+ * XXX should be a break! we don't want to block the
+ * upper layers
+ *
+ * XXX or better, just do this right.
+ *
+ */
+ sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL));
+ }
+
+ /* XXX this should call the custom "queuing" function!! */
+ aim_tx_sendframe(sess, cur);
+ }
+
+ /* purge sent commands from queue */
+ aim_tx_purgequeue(sess);
+
+ return 0;
+}
+
+/*
+ * This is responsible for removing sent commands from the transmit
+ * queue. This is not a required operation, but it of course helps
+ * reduce memory footprint at run time!
+ */
+faim_export void aim_tx_purgequeue(aim_session_t *sess)
+{
+ aim_frame_t *cur, **prev;
+
+ for (prev = &sess->queue_outgoing; (cur = *prev); ) {
+ if (cur->handled) {
+ *prev = cur->next;
+ aim_frame_destroy(cur);
+ } else
+ prev = &cur->next;
+ }
+
+ return;
+}
+
+/**
+ * Get rid of packets waiting for tx on a dying conn. For now this
+ * simply marks all packets as sent and lets them disappear without
+ * warning.
+ *
+ * @param sess A session.
+ * @param conn Connection that's dying.
+ */
+faim_internal void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn)
+{
+ aim_frame_t *cur;
+
+ for (cur = sess->queue_outgoing; cur; cur = cur->next) {
+ if (cur->conn == conn)
+ cur->handled = 1;
+ }
+
+ return;
+}