src/protocols/oscar/ft.c

changeset 11369
8d396dd4633d
parent 11253
81cbd43ef85e
child 11399
2313bf7bed5b
--- a/src/protocols/oscar/ft.c	Tue Aug 30 05:21:30 2005 +0000
+++ b/src/protocols/oscar/ft.c	Tue Aug 30 05:21:58 2005 +0000
@@ -2,40 +2,40 @@
  * Oscar File transfer (OFT) and Oscar Direct Connect (ODC).
  * (ODC is also referred to as DirectIM and IM Image.)
  *
- * There are a few static helper functions at the top, then
+ * There are a few static helper functions at the top, then 
  * ODC stuff, then ft stuff.
  *
- * I feel like this is a good place to explain OFT, so I'm going to
- * do just that.  Each OFT packet has a header type.  I guess this
- * is pretty similar to the subtype of a SNAC packet.  The type
- * basically tells the other client the meaning of the OFT packet.
- * There are two distinct types of file transfer, which I usually
- * call "sendfile" and "getfile."  Sendfile is when you send a file
- * to another AIM user.  Getfile is when you share a group of files,
+ * I feel like this is a good place to explain OFT, so I'm going to 
+ * do just that.  Each OFT packet has a header type.  I guess this 
+ * is pretty similar to the subtype of a SNAC packet.  The type 
+ * basically tells the other client the meaning of the OFT packet.  
+ * There are two distinct types of file transfer, which I usually 
+ * call "sendfile" and "getfile."  Sendfile is when you send a file 
+ * to another AIM user.  Getfile is when you share a group of files, 
  * and other users request that you send them the files.
  *
  * A typical sendfile file transfer goes like this:
- *   1) Sender sends a channel 2 ICBM telling the other user that
- *      we want to send them a file.  At the same time, we open a
- *      listener socket (this should be done before sending the
- *      ICBM) on some port, and wait for them to connect to us.
- *      The ICBM we sent should contain our IP address and the port
+ *   1) Sender sends a channel 2 ICBM telling the other user that 
+ *      we want to send them a file.  At the same time, we open a 
+ *      listener socket (this should be done before sending the 
+ *      ICBM) on some port, and wait for them to connect to us.  
+ *      The ICBM we sent should contain our IP address and the port 
  *      number that we're listening on.
- *   2) The receiver connects to the sender on the given IP address
- *      and port.  After the connection is established, the receiver
+ *   2) The receiver connects to the sender on the given IP address 
+ *      and port.  After the connection is established, the receiver 
  *      sends an ICBM signifying that we are ready and waiting.
- *   3) The sender sends an OFT PROMPT message over the OFT
+ *   3) The sender sends an OFT PROMPT message over the OFT 
  *      connection.
- *   4) The receiver of the file sends back an exact copy of this
- *      OFT packet, except the cookie is filled in with the cookie
- *      from the ICBM.  I think this might be an attempt to verify
- *      that the user that is connected is actually the guy that
+ *   4) The receiver of the file sends back an exact copy of this 
+ *      OFT packet, except the cookie is filled in with the cookie 
+ *      from the ICBM.  I think this might be an attempt to verify 
+ *      that the user that is connected is actually the guy that 
  *      we sent the ICBM to.  Oh, I've been calling this the ACK.
- *   5) The sender starts sending raw data across the connection
+ *   5) The sender starts sending raw data across the connection 
  *      until the entire file has been sent.
- *   6) The receiver knows the file is finished because the sender
- *      sent the file size in an earlier OFT packet.  So then the
- *      receiver sends the DONE thingy (after filling in the
+ *   6) The receiver knows the file is finished because the sender 
+ *      sent the file size in an earlier OFT packet.  So then the 
+ *      receiver sends the DONE thingy (after filling in the 
  *      "received" checksum and size) and closes the connection.
  */
 
@@ -112,18 +112,18 @@
 /**
  * Calculate oft checksum of buffer
  *
- * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The
- * checksum is kind of a rolling checksum thing, so each time you get bytes
- * of a file you just call this puppy and it updates the checksum.  You can
- * calculate the checksum of an entire file by calling this in a while or a
+ * Prevcheck should be 0xFFFF0000 when starting a checksum of a file.  The 
+ * checksum is kind of a rolling checksum thing, so each time you get bytes 
+ * of a file you just call this puppy and it updates the checksum.  You can 
+ * calculate the checksum of an entire file by calling this in a while or a 
  * for loop, or something.
  *
- * Thanks to Graham Booker for providing this improved checksum routine,
- * which is simpler and should be more accurate than Josh Myer's original
+ * Thanks to Graham Booker for providing this improved checksum routine, 
+ * which is simpler and should be more accurate than Josh Myer's original 
  * code. -- wtm
  *
- * This algorithm works every time I have tried it.  The other fails
- * sometimes.  So, AOL who thought this up?  It has got to be the weirdest
+ * This algorithm works every time I have tried it.  The other fails 
+ * sometimes.  So, AOL who thought this up?  It has got to be the weirdest 
  * checksum I have ever seen.
  *
  * @param buffer Buffer of data to checksum.  Man I'd like to buff her...
@@ -144,7 +144,7 @@
 			val = buffer[i] << 8;
 		check -= val;
 		/*
-		 * The following appears to be necessary.... It happens
+		 * The following appears to be necessary.... It happens 
 		 * every once in a while and the checksum doesn't fail.
 		 */
 		if (check > oldcheck)
@@ -244,7 +244,7 @@
  *
  * @param sess The session.
  * @param conn The already-connected ODC connection.
- * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and
+ * @param typing If 0x0002, sends a "typing" message, 0x0001 sends "typed," and 
  *        0x0000 sends "stopped."
  * @return Return 0 if no errors, otherwise return the error number.
  */
@@ -317,7 +317,7 @@
 /**
  * Send client-to-client IM over an established direct connection.
  * Call this just like you would aim_send_im, to send a directim.
- *
+ * 
  * @param sess The session.
  * @param conn The already-connected ODC connection.
  * @param msg Null-terminated string to send.
@@ -384,7 +384,7 @@
 
 	/* end of hdr2 */
 
-#if 0 /* XXX - this is how you send buddy icon info... */
+#if 0 /* XXX - this is how you send buddy icon info... */	
 	aimbs_put16(hdrbs, 0x0008);
 	aimbs_put16(hdrbs, 0x000c);
 	aimbs_put16(hdrbs, 0x0000);
@@ -446,7 +446,7 @@
  *
  * @param sess The session.
  * @param sn The screen name of the buddy whose direct connection you want to find.
- * @return The conn for the direct connection with the given buddy, or NULL if no
+ * @return The conn for the direct connection with the given buddy, or NULL if no 
  *         connection was found.
  */
 faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn)
@@ -534,7 +534,7 @@
  *
  * This is a wrapper for aim_newconn.
  *
- * If addr is NULL, the socket is not created, but the connection is
+ * If addr is NULL, the socket is not created, but the connection is 
  * allocated and setup to connect.
  *
  * @param sess The Godly session.
@@ -631,7 +631,7 @@
 		while (payloadlength - recvd) {
 			if (payloadlength - recvd >= 1024)
 				i = aim_recv(conn->fd, &msg[recvd], 1024);
-			else
+			else 
 				i = aim_recv(conn->fd, &msg[recvd], payloadlength - recvd);
 			if (i <= 0) {
 				free(msg);
@@ -642,7 +642,7 @@
 			if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER)))
 				ret = userfunc(sess, &fr, snptr, (double)recvd / payloadlength);
 		}
-
+		
 		if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)))
 			ret = userfunc(sess, &fr, snptr, msg, payloadlength, encoding, isawaymsg);
 
@@ -654,7 +654,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 +667,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,8 +706,27 @@
 	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
+ * Remove the given oft_info struct from the oft_info linked list, and 
  * then free its memory.
  *
  * @param sess The session.
@@ -732,13 +761,13 @@
 /**
  * Creates a listener socket so the other dude can connect to us.
  *
- * You'll want to set up some kind of watcher on this socket.
- * When the state changes, call aim_handlerendconnection with
- * the connection returned by this.  aim_handlerendconnection
+ * You'll want to set up some kind of watcher on this socket.  
+ * When the state changes, call aim_handlerendconnection with 
+ * the connection returned by this.  aim_handlerendconnection 
  * will accept the pending connection and stop listening.
  *
  * @param sess The session.
- * @param oft_info File transfer information associated with this
+ * @param oft_info File transfer information associated with this 
  *        connection.
  * @return Return 0 if no errors, otherwise return the error number.
  */
@@ -802,7 +831,7 @@
 	fh->name[63] = '\0';
 
 	return fh;
-}
+} 
 
 /**
  * Fills a buffer with network-order fh data
@@ -812,7 +841,7 @@
  * @return Return non-zero on error.
  */
 static int aim_oft_buildheader(aim_bstream_t *bs, struct aim_fileheader_t *fh)
-{
+{ 
 	fu8_t *hdr;
 
 	if (!bs || !fh)
@@ -857,7 +886,7 @@
  *
  * @param sess The session.
  * @param type The subtype of the OFT packet we're sending.
- * @param oft_info The aim_oft_info struct with the connection and OFT
+ * @param oft_info The aim_oft_info struct with the connection and OFT 
  *        info we're sending.
  * @return Return 0 if no errors, otherwise return the error number.
  */
@@ -870,8 +899,8 @@
 
 #if 0
 	/*
-	 * If you are receiving a file, the cookie should be null, if you are sending a
-	 * file, the cookie should be the same as the one used in the ICBM negotiation
+	 * If you are receiving a file, the cookie should be null, if you are sending a 
+	 * file, the cookie should be the same as the one used in the ICBM negotiation 
 	 * SNACs.
 	 */
 	fh->lnameoffset = 0x1a;
@@ -901,13 +930,142 @@
 }
 
 /**
- * Handle incoming data on a rendezvous connection.  This is analogous to the
- * consumesnac function in rxhandlers.c, and I really think this should probably
+ * 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_tlvlist_t *tlvlist_sendfile;
+	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 */
+	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
+	
+	// TODO: Use built-in TLV 
+	//aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
+	//aim_tlvlist_write(&bs, &tlvlist_sendfile);
+	
+	aim_bstream_rewind(&bs);
+	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+		err = errno;
+	proxy_info->conn->lastactivity = time(NULL);
+	
+	//aim_tlvlist_free(tlvlist_sendfile);
+	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_tlvlist_t *tlvlist_sendfile;
+	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 */
+	aimbs_putcaps(&bs, AIM_CAPS_SENDFILE);	/* Value */
+	
+	// TODO: Use built-in TLV 
+	//aim_tlvlist_add_caps(&tlvlist_sendfile, 0x0001, AIM_CAPS_SENDFILE);
+	//aim_tlvlist_write(&bs, &tlvlist_sendfile);
+	
+	aim_bstream_rewind(&bs);
+	if (aim_bstream_send(&bs, proxy_info->conn, packet_len) != packet_len)
+		err = errno;
+	proxy_info->conn->lastactivity = time(NULL);
+	
+	//aim_tlvlist_free(tlvlist_sendfile);
+	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.
  *
  * @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
+ * @return Return 0 if the packet was handled correctly, otherwise return the 
  *         error number.
  */
 faim_internal int aim_rxdispatch_rendezvous(aim_session_t *sess, aim_frame_t *fr)
@@ -937,3 +1095,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 {
+					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
+					aim_conn_close(conn);
+					free(proxy_info);
+					proxy_info = NULL;
+				}
+			} else {
+				gaim_debug_warning("oscar","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 {
+					gaim_debug_warning("oscar","error reading rv proxy error packet\n");
+					aim_conn_close(conn);
+					free(proxy_info);
+					proxy_info = NULL;
+				}
+			} else {
+				gaim_debug_warning("oscar","invalid length for proxy error packet\n");
+				free(proxy_info);
+				proxy_info = NULL;
+			}
+		} else {
+			gaim_debug_warning("oscar","unknown type for aim rendezvous proxy packet\n");
+		}	
+	} else {
+		gaim_debug_warning("oscar","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;
+}

mercurial