--- 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 @@
/* 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 @@
+ /* reqnum: 0x0001 usually; 0x0002 = reply request for stage 2 proxy transfer */ + fu8_t use_proxy; /* Did the user request that we use a rv proxy? */ 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); @@ -914,18 +959,38 @@
+struct aim_rv_proxy_info { + 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 */ + fu16_t err_code; /* Valid only for cmd_type of AIM_RV_PROXY_ERROR */
+ char *proxyip; /* Not necessarily an IP; could be the hostname of the proxy */ + 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 */ 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); --- 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 @@
+ * 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) { + /* XXX - Should be like "21CBF95" and null terminated */ + for (i = 0; i < 7; i++) + gen_cookie[i] = 0x30 + ((fu8_t)rand() % 10); + memcpy(cookie, gen_cookie, 8); * Subtype 0x0006 - Send an "I want to send you this file" message
@@ -757,55 +773,112 @@
aim_tlvlist_t *tl=NULL, *subtl=NULL;
if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !oft_info)
- /* 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 */
- aim_tlvlist_add_16(&subtl, 0x000a, 0x0001);
- aim_tlvlist_add_noval(&subtl, 0x000f);
+ fu8_t ip_comp[4]; /* The bitwise complement of the ip */ + /* 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); + 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) {
nexttoken = strtok(oft_info->clientip, ".");
while (nexttoken && i<4) {
nexttoken = strtok(NULL, ".");
+ /* 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);
- buflen = 2+2+4+strlen(oft_info->fh.name)+1;
- 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));
- aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data);
+ /* 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 */ + nexttoken = strtok(oft_info->proxyip, "."); + while (nexttoken && i<4) { + ip[i] = atoi(nexttoken); + nexttoken = strtok(NULL, "."); + 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))) { + buflen = 2+2+4+strlen(oft_info->fh.name)+1; + 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)); + aim_tlvlist_add_raw(&subtl, 0x2711, bs.len, bs.data); { /* 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 if (aim_tlv_gettlv(list2, 0x000a, 1))
+ args.info.sendfile.reqnum = aim_tlv_get16(list2, 0x000a, 1); @@ -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))
- * 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; + args.info.sendfile.use_proxy = FALSE; 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 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 @@
+ * 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) { struct aim_oft_info *oft_info;
- gaim_debug_info("oscar","AAA - in oscar_clientip_timeout\n");
+ gaim_debug_info("oscar","AAA - in oscar_xfer_ip_timeout\n"); oft_info = (struct aim_oft_info*) xfer->data;
/* Check to see if the clientip has produced any results */
- msg = g_strdup_printf(_("Transfer of file %s timed out."),gaim_xfer_get_filename(xfer));
- gaim_xfer_conversation_write(xfer, msg, TRUE);
- gaim_xfer_cancel_local(xfer);
+ /* This connection has worn out its welcome. Goodbye. */ + 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); + 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); + /* 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->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); + /* 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); + if(oft_info->xfer_reffed) { + oft_info->xfer_reffed = FALSE; + gaim_xfer_cancel_local(xfer); + gaim_debug_warning("oscar","unknown xfer method encountered in timout\n"); - gaim_debug_info("oscar","connection successful; no action taken\n");
+ if(oft_info->xfer_reffed) { + oft_info->xfer_reffed = FALSE; + gaim_debug_info("oscar","connection successful; timeout off\n"); + gaim_debug_info("oscar","transfer already done; nothing to do\n"); - * This is a gaim timeout callback for when the verifiedip looks to be useless
- * It tries the file transfer again using the clientip
-static gboolean oscar_verifiedip_timeout(gpointer data) {
- struct aim_oft_info *oft_info;
- gaim_debug_info("oscar","AAA - in oscar_verifiedip_timeout\n");
- xfer = (GaimXfer*) 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);
- gaim_debug_info("oscar","connection successful; no action taken\n");
- * 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; + GaimInputFunction nextstop_cb; g_return_if_fail(xfer != NULL);
g_return_if_fail(xfer->data != NULL);
+ proxy_info = oft_info->proxy_info; gc = oft_info->sess->aux_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 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; - /* 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); - gaim_timeout_add(FT_IP_TIMEOUT * 1000, oscar_clientip_timeout, xfer);
+ gaim_timeout_add(FT_CLIENTIP_TIMEOUT, oscar_xfer_ip_timeout, xfer);
+ 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) { + proxy_info->conn = oft_info->conn; + 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; + nextstop_cb = oscar_sendfile_connected; 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);
+ 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); 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 +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); + if (!(proxy_info = oft_info->proxy_info)) { + gaim_debug_warning("oscar","NULL proxy_info; aborting\n"); + gaim_xfer_cancel_local(xfer); + /* Remove the rv proxy watcher and put the connection type back the way we found it */ + gaim_input_remove(xfer->watcher); + 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, + /* 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); + 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 +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); + if (!(proxy_info = oft_info->proxy_info)) { + gaim_debug_warning("oscar","NULL proxy_info; aborting\n"); + gaim_xfer_cancel_local(xfer); + /* 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); + 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 +static void oscar_xfer_proxylogin_cb(gpointer data, gint source, GaimInputCondition condition) { + struct aim_oft_info *oft_info; + gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin_cb\n"); + gaim_debug_warning("oscar","NULL xfer; aborting\n"); + gaim_xfer_cancel_local(xfer); + if (!(oft_info = xfer->data)) { + gaim_debug_warning("oscar","NULL oft_info; aborting\n"); + gaim_xfer_cancel_local(xfer); + 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; + oscar_xfer_proxylogin_ack(xfer); + free(oft_info->proxy_info); + oft_info->proxy_info = NULL; + 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); + free(oft_info->proxy_info); + oft_info->proxy_info = NULL; + gaim_xfer_cancel_remote(xfer); + /* We should never get here */ + gaim_debug_info("oscar","proxylogin switch defaulted unexpectedly\n"); + 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) + struct aim_oft_info *oft_info; + struct aim_rv_proxy_info *proxy_info; + gaim_debug_info("oscar","AAA - in oscar_xfer_proxylogin\n"); + gaim_debug_warning("oscar","NULL xfer; aborting\n"); + gaim_xfer_cancel_local(xfer); + if (!(oft_info = xfer->data)) { + gaim_debug_warning("oscar","NULL oft_info; aborting\n"); + gaim_xfer_cancel_local(xfer); + if (!(proxy_info = oft_info->proxy_info)) { + gaim_debug_warning("oscar","NULL proxy_info; aborting\n"); + gaim_xfer_cancel_local(xfer); + if(oft_info->success) { + gaim_debug_info("oscar","connection already successful, ignoring 2nd conn\n"); + 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", + 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", + gaim_xfer_cancel_local(xfer); + 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", + 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", + gaim_xfer_cancel_local(xfer); + gaim_debug_warning("oscar","no proxy type specified; aborting transfer\n"); + gaim_xfer_cancel_local(xfer); + gaim_debug_warning("oscar","no send_or_recv value specified; aborting\n"); + gaim_xfer_cancel_local(xfer); + 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"); + 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); + 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;
- 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 @@
"port is %hu, ip is %s\n",
xfer->local_port, oft_info->clientip);
- 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);
- gaim_xfer_error(GAIM_XFER_SEND, xfer->who,
- _("Unable to establish listener socket."));
- gaim_xfer_cancel_local(xfer);
+ xfer->watcher = gaim_input_add(oft_info->conn->fd, GAIM_INPUT_READ, oscar_callback, + 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->sess && oft_info->conn) + aim_conn_kill(oft_info->sess, &oft_info->conn); + aim_oft_destroyinfo(oft_info); 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 static gboolean oscar_can_receive_file(GaimConnection *gc, const char *who) {
gboolean can_receive = FALSE;
OscarData *od = gc->proto_data;
@@ -2190,13 +2580,21 @@
+ * Called by the Gaim core when the user indicates that a file is to be sent to static void oscar_send_file(GaimConnection *gc, const char *who, const char *file) {
struct aim_oft_info *oft_info;
+ use_rv_proxy = gaim_prefs_get_bool("/plugins/prpl/oscar/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);
+ /* 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); + 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); - /* Setup our I/O op functions */
- gaim_xfer_set_init_fnc(xfer, oscar_xfer_init_send);
+ /* Setup our I/O op functions */ + gaim_xfer_set_init_fnc(xfer, oscar_xfer_init_recv); + 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 @@
+ * 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 @@
- * 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 @@
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"); conn = va_arg(ap, aim_conn_t *);
listenerconn = va_arg(ap, aim_conn_t *);
- if (!(xfer = oscar_find_xfer_by_conn(od->file_transfers, listenerconn)))
- 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"); + if (!(oft_info = xfer->data)) { + gaim_debug_warning("oscar","NULL data\n");
- /* Stop watching listener conn; watch transfer conn instead */
- gaim_input_remove(xfer->watcher);
- aim_conn_kill(sess, &listenerconn);
- 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); + 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 */ * 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 static void oscar_sendfile_connected(gpointer data, gint source, GaimInputCondition condition) {
@@ -3213,34 +3658,46 @@
if (!(oft_info = xfer->data))
+ if(oft_info->success) { + gaim_debug_info("oscar","connection already successful; ignoring 2nd conn\n"); - /* 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");
- gaim_debug_info("oscar","received source of 0 for fd; aborting transfer\n");
- gaim_xfer_cancel_remote(xfer);
- 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); 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 */ 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);
+ /* 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 @@
struct aim_fileheader_t *fh;
"AAA - in oscar_sendfile_prompt\n");
conn = va_arg(ap, aim_conn_t *);
cookie = va_arg(ap, fu8_t *);
@@ -3334,6 +3792,7 @@
struct aim_fileheader_t *fh;
+ struct aim_oft_info *oft_info; gaim_debug_info("oscar", "AAA - in oscar_sendfile_done\n");
@@ -3342,8 +3801,18 @@
fh = va_arg(ap, struct aim_fileheader_t *);
- 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"); + if(!(oft_info = xfer->data)) { + gaim_debug_warning("oscar","NULL oft_info\n"); + if(oft_info->xfer_reffed) { + oft_info->xfer_reffed = FALSE; @@ -3516,21 +3985,25 @@
} else if (args->reqclass & AIM_CAPS_SENDFILE) {
- 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 */
struct aim_oft_info *oft_info;
- if (!args->cookie || !args->port || !args->verifiedip ||
+ struct aim_rv_proxy_info *proxy_info = NULL; + 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);
- gaim_debug_warning("oscar",
- "IP for a proxy server was given. Gaim "
- "does not support this yet.\n");
@@ -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); + /* 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,
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); 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);
- oft_info->proxyip = g_strdup(args->proxyip);
- 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); + oft_info->proxyip = g_strdup(args->proxyip); + oft_info->verifiedip = g_strdup(args->verifiedip); + oft_info->proxy_info = proxy_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 */
+ /* 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 */ + struct aim_oft_info *oft_info; + if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie))) { + /* 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", + 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); + 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); + 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; + = aim_rv_proxy_createinfo(od->sess, args->cookie, 0); + oscar_xfer_init_recv(xfer); + gaim_debug_info("oscar","received request to redirect transfer; clientip/verifiedip: %s / %s\n", + args->clientip, args->verifiedip); + oft_info->method = AIM_XFER_REDIR; + oft_info->verifiedip = g_strdup(args->verifiedip); + 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); + 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. */ + struct aim_oft_info *oft_info; + if ((xfer = oscar_find_xfer_by_cookie(od->file_transfers, args->cookie))) { + /* 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); + 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); + gaim_debug_warning("oscar", + "stage 3 rv proxy file receive: no proxy ip specified\n"); + 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 */
@@ -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"); gaim_debug_error("oscar",
"unknown rendezvous status!\n");
@@ -7533,6 +8155,30 @@
+static GaimPluginPrefFrame * oscar_pref_frame(GaimPlugin *plugin) + GaimPluginPrefFrame *frame; + 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); +static GaimPluginUiInfo prefs_info = static GaimPluginProtocolInfo prpl_info =
OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE,
@@ -7628,7 +8274,7 @@
&prpl_info, /**< extra_info */
- NULL, /**< prefs_info */
+ &prefs_info, /**< prefs_info */ @@ -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);
+ 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);