--- 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; +}