⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ft.c

📁 oscar message protocol stack
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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  * 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,  * 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  *      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  *      sends an ICBM signifying that we are ready and waiting. *   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  *      we sent the ICBM to.  Oh, I've been calling this the ACK. *   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  *      "received" checksum and size) and closes the connection. */#define FAIM_INTERNAL#ifdef HAVE_CONFIG_H#include  <config.h>#endif#include <aim.h>#ifndef _WIN32#include <stdio.h>#include <netdb.h>#include <sys/socket.h>#include <netinet/in.h>#include <sys/utsname.h> /* for aim_odc_initiate */#include <arpa/inet.h> /* for inet_ntoa */#include <limits.h> /* for UINT_MAX */#define G_DIR_SEPARATOR '/'#endif#ifdef _WIN32#include "win32dep.h"#endif/* * I really want to switch all our networking code to using IPv6 only, * but that really isn't a good idea at all.  Evan S. of Adium says * OS X sets all connections as "AF_INET6/PF_INET6," even if there is * nothing inherently IPv6 about them.  And I feel like Linux kernel * 2.6.5 is doing the same thing.  So we REALLY should accept * connections if they're showing up as IPv6.  Old OSes (Solaris?) * that might not have full IPv6 support yet will fail if we try * to use PF_INET6 but it isn't defined.  --Mark Doliner */#ifndef PF_INET6#define PF_INET6 PF_INET#endifstruct aim_odc_intdata {	fu8_t cookie[8];	char sn[MAXSNLEN+1];	char ip[22];};/** * Convert the directory separator from / (0x2f) to ^A (0x01) * * @param name The filename to convert. */static void aim_oft_dirconvert_tostupid(char *name){	while (name[0]) {		if (name[0] == 0x01)			name[0] = G_DIR_SEPARATOR;		name++;	}}/** * Convert the directory separator from ^A (0x01) to / (0x2f) * * @param name The filename to convert. */static void aim_oft_dirconvert_fromstupid(char *name){	while (name[0]) {		if (name[0] == G_DIR_SEPARATOR)			name[0] = 0x01;		name++;	}}/** * 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  * 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  * 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  * checksum I have ever seen. * * @param buffer Buffer of data to checksum.  Man I'd like to buff her... * @param bufsize Size of buffer. * @param prevcheck Previous checksum. */faim_export fu32_t aim_oft_checksum_chunk(const fu8_t *buffer, int bufferlen, fu32_t prevcheck){	fu32_t check = (prevcheck >> 16) & 0xffff, oldcheck;	int i;	unsigned short val;	for (i=0; i<bufferlen; i++) {		oldcheck = check;		if (i&1)			val = buffer[i];		else			val = buffer[i] << 8;		check -= val;		/*		 * The following appears to be necessary.... It happens 		 * every once in a while and the checksum doesn't fail.		 */		if (check > oldcheck)			check--;	}	check = ((check & 0x0000ffff) + (check >> 16));	check = ((check & 0x0000ffff) + (check >> 16));	return check << 16;}faim_export fu32_t aim_oft_checksum_file(char *filename) {	FILE *fd;	fu32_t checksum = 0xffff0000;	if ((fd = fopen(filename, "rb"))) {		int bytes;		fu8_t buffer[1024];		while ((bytes = fread(buffer, 1, 1024, fd)))			checksum = aim_oft_checksum_chunk(buffer, bytes, checksum);		fclose(fd);	}	return checksum;}/** * After establishing a listening socket, this is called to accept a connection.  It * clones the conn used by the listener, and passes both of these to a signal handler. * The signal handler should close the listener conn and keep track of the new conn, * since this is what is used for file transfers and what not. * * @param sess The session. * @param cur The conn the incoming connection is on. * @return Return 0 if no errors, otherwise return the error number. */faim_export int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur){	int acceptfd = 0;	struct sockaddr addr;	socklen_t addrlen = sizeof(addr);	int ret = 0;	aim_conn_t *newconn;	char ip[20];	unsigned short port;	if ((acceptfd = accept(cur->fd, &addr, &addrlen)) == -1)		return 0; /* not an error */	if ((addr.sa_family != PF_INET) && (addr.sa_family != PF_INET6)) {		close(acceptfd);		aim_conn_close(cur);		return -1;	}	strncpy(ip, inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), sizeof(ip));	port = ntohs(((struct sockaddr_in *)&addr)->sin_port);	if (!(newconn = aim_cloneconn(sess, cur))) {		close(acceptfd);		aim_conn_close(cur);		return -ENOMEM;	}	newconn->type = AIM_CONN_TYPE_RENDEZVOUS;	newconn->fd = acceptfd;	if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) {		aim_rxcallback_t userfunc;		struct aim_odc_intdata *priv;		priv = (struct aim_odc_intdata *)(newconn->internal = cur->internal);		cur->internal = NULL;		snprintf(priv->ip, sizeof(priv->ip), "%s:%hu", ip, port);		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIM_ESTABLISHED)))			ret = userfunc(sess, NULL, newconn, cur);	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) {	} else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) {		aim_rxcallback_t userfunc;		if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_ESTABLISHED)))			ret = userfunc(sess, NULL, newconn, cur);	} else {		faimdprintf(sess, 1,"Got a connection on a listener that's not rendezvous.  Closing connection.\n");		aim_conn_close(newconn);		ret = -1;	}	return ret;}/** * Send client-to-client typing notification over an established direct connection. * * @param sess The session. * @param conn The already-connected ODC connection. * @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. */faim_export int aim_odc_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing){	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;	aim_frame_t *fr;	aim_bstream_t *hdrbs;	fu8_t *hdr;	int hdrlen = 0x44;	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS))		return -EINVAL;	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0001, 0)))		return -ENOMEM;	memcpy(fr->hdr.rend.magic, "ODC2", 4);	fr->hdr.rend.hdrlen = hdrlen + 8;	if (!(hdr = calloc(1, hdrlen))) {		aim_frame_destroy(fr);		return -ENOMEM;	}	hdrbs = &(fr->data);	aim_bstream_init(hdrbs, hdr, hdrlen);	aimbs_put16(hdrbs, 0x0006);	aimbs_put16(hdrbs, 0x0000);	aimbs_putraw(hdrbs, intdata->cookie, 8);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put32(hdrbs, 0x00000000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	if (typing == 0x0002)		aimbs_put16(hdrbs, 0x0002 | 0x0008);	else if (typing == 0x0001)		aimbs_put16(hdrbs, 0x0002 | 0x0004);	else		aimbs_put16(hdrbs, 0x0002);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));	aim_bstream_setpos(hdrbs, 52); /* bleeehh */	aimbs_put8(hdrbs, 0x00);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put8(hdrbs, 0x00);	/* end of hdr */	aim_tx_enqueue(sess, fr);	return 0;}/** * 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. * @param len The length of the message to send, including binary data. * @param encoding See the AIM_CHARSET_* defines in aim.h * @param isawaymsg 0 if this is not an auto-response, 1 if it is. * @return Return 0 if no errors, otherwise return the error number. */faim_export int aim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len, int encoding, int isawaymsg){	aim_frame_t *fr;	aim_bstream_t *hdrbs;	struct aim_odc_intdata *intdata = (struct aim_odc_intdata *)conn->internal;	int hdrlen = 0x44;	fu8_t *hdr;	if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS) || !msg)		return -EINVAL;	if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0)))		return -ENOMEM;	memcpy(fr->hdr.rend.magic, "ODC2", 4);	fr->hdr.rend.hdrlen = hdrlen + 8;	if (!(hdr = calloc(1, hdrlen + len))) {		aim_frame_destroy(fr);		return -ENOMEM;	}	hdrbs = &(fr->data);	aim_bstream_init(hdrbs, hdr, hdrlen + len);	aimbs_put16(hdrbs, 0x0006);	aimbs_put16(hdrbs, 0x0000);	aimbs_putraw(hdrbs, intdata->cookie, 8);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put32(hdrbs, len);	aimbs_put16(hdrbs, encoding);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	/* flags - used for typing notification and to mark if this is an away message */	aimbs_put16(hdrbs, 0x0000 | isawaymsg);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_putraw(hdrbs, sess->sn, strlen(sess->sn));	aim_bstream_setpos(hdrbs, 52); /* bleeehh */	aimbs_put8(hdrbs, 0x00);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x0000);	aimbs_put8(hdrbs, 0x00);	/* end of hdr2 */#if 0 /* XXX - this is how you send buddy icon info... */		aimbs_put16(hdrbs, 0x0008);	aimbs_put16(hdrbs, 0x000c);	aimbs_put16(hdrbs, 0x0000);	aimbs_put16(hdrbs, 0x1466);	aimbs_put16(hdrbs, 0x0001);	aimbs_put16(hdrbs, 0x2e0f);	aimbs_put16(hdrbs, 0x393e);	aimbs_put16(hdrbs, 0xcac8);#endif	aimbs_putraw(hdrbs, msg, len);	aim_tx_enqueue(sess, fr);	return 0;}/** * Get the screen name of the peer of a direct connection. * * @param conn The ODC connection. * @return The screen name of the dude, or NULL if there was an anomaly. */faim_export const char *aim_odc_getsn(aim_conn_t *conn){	struct aim_odc_intdata *intdata;	if (!conn || !conn->internal)		return NULL;	if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) ||			(conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM))		return NULL;	intdata = (struct aim_odc_intdata *)conn->internal;	return intdata->sn;}/** * Get the cookie of a direct connection. * * @param conn The ODC connection. * @return The cookie, an 8 byte unterminated string, or NULL if there was an anomaly. */faim_export const char *aim_odc_getcookie(aim_conn_t *conn){	struct aim_odc_intdata *intdata;	if (!conn || !conn->internal)		return NULL;	intdata = (struct aim_odc_intdata *)conn->internal;	return intdata->cookie;}/** * Find the conn of a direct connection with the given buddy. * * @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  *         connection was found. */faim_export aim_conn_t *aim_odc_getconn(aim_session_t *sess, const char *sn){	aim_conn_t *cur;	struct aim_odc_intdata *intdata;	if (!sess || !sn || !strlen(sn))		return NULL;	for (cur = sess->connlist; cur; cur = cur->next) {		if ((cur->type == AIM_CONN_TYPE_RENDEZVOUS) && (cur->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) {			intdata = cur->internal;			if (!aim_sncmp(intdata->sn, sn))				return cur;		}	}	return NULL;}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -