+}
+
+/*
+ * aim_oft_checksum - calculate oft checksum of buffer
+ * @buffer: buffer of data to checksum
+ * @bufsize: size of buffer
+ * @checksum: pointer to integer to place result in (pointer!)
+
+ *
+ * note that checksum is a pointer. checksum should be filled with
+ * 0xFFFF0000 for each new file; you can have this checksum chunks of
+ * files in series if you just call it repeatedly in a for(; ; ) loop
+ * and don't reset the checksum between each call. and you thought we
+ * didn't care about you and your pathetic client's meomry footprint
+ * ;^)
+ *
+ */
+faim_export int aim_oft_checksum(char *buffer, int bufsize, int *checksum){ short check0, check1;
+ int i;
+ check0 = ((*checksum & 0xFF000000) >> 16);
+ check1 = ((*checksum & 0x00ff0000) >> 16);
+ for(i = 0; i < bufsize; i++) {
+ if(i % 2) { /* use check1 -- second byte */
+ if ( (short)buffer[i] > check1 ) { /* wrapping */
+ check1 += 0x100; /* this is a cheap way to wrap */
+
+ /* if we're wrapping, decrement the other one */
+ /* XXX: check this corner case */
+ if(check0 == 0)
+ check0 = 0x00ff;
+ else
+ check0--;
+ }
+ check1 -= buffer[i];
+ } else { /* use check0 -- first byte */
+ if ( (short)buffer[i] > check0 ) { /* wrapping */
+ check0 += 0x100; /* this is a cheap way to wrap */
+
+ /* if we're wrapping, decrement the other one */
+ /* XXX: check this corner case */
+ if(check1 == 0)
+ check1 = 0x00ff;
+ else
+ check1--;
+ }
+ check0 -= buffer[i];
+ }
+ }
+
+ if(check0 > 0xff || check1 > 0xff) {
+ /* they shouldn't be able to do this. error! */
+ faimdprintf(2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1);
+ return -1;
+ }
+
+ /* grab just the lowest byte; this should be clean, but just in
+ case */
+ check0 &= 0xff;
+ check1 &= 0xff;
+
+ *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
+ return *checksum;
+}
+
+/*
+ * aim_oft_buildheader - fills a buffer with network-order fh data
+ * @dest: buffer to fill -- pre-alloced
+ * @fh: fh to get data from
+ *
+ * returns length written; -1 on error.
+ * DOES NOT DO BOUNDS CHECKING!
+ *
+ */
+faim_internal int aim_oft_buildheader(unsigned char *dest,struct aim_fileheader_t *fh)
+{
+ int i, curbyte;
+ if(!dest || !fh)
+ return -1;
+ curbyte = 0;
+ for(i = 0; i < 8; i++)
+ curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]);
+ curbyte += aimutil_put16(dest+curbyte, fh->encrypt);
+ curbyte += aimutil_put16(dest+curbyte, fh->compress);
+ curbyte += aimutil_put16(dest+curbyte, fh->totfiles);
+ curbyte += aimutil_put16(dest+curbyte, fh->filesleft);
+ curbyte += aimutil_put16(dest+curbyte, fh->totparts);
+ curbyte += aimutil_put16(dest+curbyte, fh->partsleft);
+ curbyte += aimutil_put32(dest+curbyte, fh->totsize);
+ curbyte += aimutil_put32(dest+curbyte, fh->size);
+ curbyte += aimutil_put32(dest+curbyte, fh->modtime);
+ curbyte += aimutil_put32(dest+curbyte, fh->checksum);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfsize);
+ curbyte += aimutil_put32(dest+curbyte, fh->cretime);
+ curbyte += aimutil_put32(dest+curbyte, fh->rfcsum);
+ curbyte += aimutil_put32(dest+curbyte, fh->nrecvd);
+ curbyte += aimutil_put32(dest+curbyte, fh->recvcsum);
+ memcpy(dest+curbyte, fh->idstring, 32);
+ curbyte += 32;
+ curbyte += aimutil_put8(dest+curbyte, fh->flags);
+ curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset);
+ curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset);
+ memcpy(dest+curbyte, fh->dummy, 69);
+ curbyte += 69;
+ memcpy(dest+curbyte, fh->macfileinfo, 16);
+ curbyte += 16;
+ curbyte += aimutil_put16(dest+curbyte, fh->nencode);
+ curbyte += aimutil_put16(dest+curbyte, fh->nlanguage);
+ memset(dest+curbyte, 0x00, 64);
+ memcpy(dest+curbyte, fh->name, 64);
+
+ /* XXX: Filenames longer than 64B */
+ curbyte += 64;
+ return curbyte;
+}
+#if 0
+/*
+* aim_getfile_send - send a FULL file down (ie: blocking; that is: DON'T USE IT, use send_chunk below.)
+* @conn: the OFT connection to shove the data down,
+* @tosend: is the FILE
+* for the file to send
+* @fh: the filled-in fh value
+
+*
+* returns -1 on error, 1 on success. This sends the whold file and
+* blocks, so it will totally mess you up if you use it. use the
+* send_chunk below and work out a schduling setup or some such.
+
+*
+*/
+faim_export int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
+{
+ int pos, bufpos, i;
+ const int bufsize = 4096;
+ char *buf;
+
+ /* sanity checks
+ */
+
+ if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->subtype != AIM_CONN_SUBTYPE_OFT_GETFILE)
+ { faimdprintf(1, "getfile_send: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
+ return -1;
+ }
+ if(!tosend)
+ { faimdprintf(1, "getfile_send: file pointer isn't valid\n");
+ return -1;
+ }
+ if(!fh)
+ { faimdprintf(1, "getfile_send: fh isn't valid\n");
+ return -1;
+ }
+ /* real code
+ */
+
+ if(!(buf = (char *)calloc(1, bufsize)))
+ { perror("faim: getfile_send: calloc:");
+ faimdprintf(2, "getfile_send calloc error\n");
+ return -1;
+ } pos = 0;
+ if( fseek(tosend, 0, SEEK_SET) == -1)
+ { perror("faim: getfile_send: fseek:");
+ faimdprintf(2, "getfile_send fseek error\n");
+ } faimdprintf(2, "faim: getfile_send: using %i byte blocks\n", bufsize);
+ for(pos = 0;
+ pos < fh->size;
+ pos++) { bufpos = pos % bufsize;
+ if(bufpos == 0 && pos > 0)
+ {
+ /* filled our buffer. spit it across the wire
+ */
+
+ if ( (i = send(conn->fd, buf, bufsize, 0)) != bufsize )
+ { perror("faim: getfile_send: write1: ");
+ faimdprintf(1, "faim: getfile_send: whoopsy, didn't write it all...\n");
+ free(buf);
+ return -1;
+ } }
+ if( (buf[bufpos] = fgetc(tosend)) == EOF)
+ {
+ if(pos != fh->size)
+ { printf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
+ faimdprintf(1, "faim: getfile_send: hrm... apparent early EOF at pos 0x%lx of 0x%lx\n", pos, fh->size);
+ free(buf);
+ return -1;
+ } } }
+ if( (i = send(conn->fd, buf, bufpos+1, 0)) != (bufpos+1))
+ { perror("faim: getfile_send: write2: ");
+ faimdprintf(1, "faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
+ free(buf);
+ return -1;
+ } free(buf);
+ fclose(tosend);
+ return 1;
+}
+
+/*
+* aim_getfile_send_chunk - send a chunk of a file down a conn
+* @conn: the OFT connection to shove the data down,
+* @tosend: the buffer to send
+* @fh: the filled-in fh value
+* @pos: the position to start at (see below)
+* @bufsize: the size of the chunk to send
+
+*
+* returns -1 on error, new pos on success.
+* Notes on usage:
+* You don't really have to keep track of pos if you don't want
+* to. just always call with -1 for pos, and it'll use the one
+* contained within the FILE
+*.
+
+*
+* if (pos + chunksize > fh->size), we only send as much data as we
+* can get (ie: up to fh->size.
+
+*
+* the value of pos: at beginning should be 0, after 5 bytes are sent
+* should be 5); -1 for "don't seek"
+
+*
+* GOING TO BE CHANGED TO USE unsigned char
+* BUFFERS!!!
+*/
+faim_export int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
+{
+ int bufpos;
+ char *buf;
+
+ /* sanity checks
+ */
+
+ if(conn->type != AIM_CONN_TYPE_RENDEZVOUS || conn->type != AIM_CONN_SUBTYPE_OFT_GETFILE)
+ { faimdprintf(1, "faim: getfile_send_chunk: conn->type(0x%04x) != RENDEZVOUS or conn->subtype(0x%04x) != GETFILE\n", conn->type, conn->subtype);
+ return -1;
+ }
+ if(!tosend)
+ { faimdprintf(1, "faim: getfile_send_chunk: file pointer isn't valid\n");
+ return -1;
+ }
+ if(!fh)
+ { faimdprintf(1, "faim: getfile_send_chunk: fh isn't valid\n");
+ return -1;
+ }
+ /* real code
+ */
+
+ if(!(buf = (char *)calloc(1, bufsize)))
+ { perror("faim: getfile_send_chunk: calloc:");
+ faimdprintf(2, "faim: getfile_send_chunk calloc error\n");
+ return -1;
+ }
+ if(pos != -1)
+ {
+ if( fseek(tosend, pos, SEEK_SET) == -1)
+ { perror("faim: getfile_send_chunk: fseek:");
+ faimdprintf(2, "faim: getfile_send_chunk fseek error\n");
+ } } faimdprintf(2, "faim: getfile_send_chunk: using %i byte blocks\n", bufsize);
+ for(bufpos = 0;
+ pos < fh->size;
+ bufpos++, pos++) {
+ if( (buf[bufpos] = fgetc(tosend)) == EOF)
+ {
+ if(pos != fh->size)
+ { faimdprintf(1, "faim: getfile_send_chunk: hrm... apparent early EOF at pos 0x%x of 0x%x\n", pos, fh->size);
+ free(buf);
+ return -1;
+ } } }
+ if( send(conn->fd, buf, bufpos+1, 0) != (bufpos+1))
+ { faimdprintf(1, "faim: getfile_send_chunk cleanup: whoopsy, didn't write it all...\n");
+ free(buf);
+ return -1;
+ } free(buf);
+ return (pos + bufpos);
+}
+#endif
+/*
+* aim_tx_destroy - free's tx_command_t's
+* @command: the command to free
+
+*
+* if command is locked, doesn't free.
+* returns -1 on error (locked struct); 0 on success.
+
+*
+*/
+faim_internal int aim_tx_destroy(struct command_tx_struct *command){
+ if(command->lock)
+ return -1;
+ if(command->data)
+ free(command->data);
+ if (command->hdrtype == AIM_FRAMETYPE_OFT && command->hdr.oft.hdr2)
+ free(command->hdr.oft.hdr2);
+ free(command);
+ return 0;
+}
+
+/*
+ * aim_getfile_intitiate - For those times when we want to open up the getfile dialog ourselves.
+ * @sess: your session,
+ * @conn: the BOS conn,
+ * @destsn is the SN to connect to.
+ */
+faim_export struct aim_conn_t *aim_getfile_initiate(struct aim_session_t *sess, struct aim_conn_t *conn, char *destsn)
+{
+ struct command_tx_struct *newpacket;
+ struct aim_conn_t *newconn;
+ struct aim_filetransfer_priv *priv;
+ struct aim_msgcookie_t *cookie;
+ int curbyte, i, listenfd;
+ short port = 4443;
+ struct hostent *hptr;
+ struct utsname myname;
+ char cap[16];
+ char d[4];
+
+ /* Open our socket */
+
+ if( (listenfd = aim_listenestablish(port)) == -1)
+ return NULL;
+
+ /* get our local IP */
+
+ if(uname(&myname) < 0)
+ return NULL;
+ if( (hptr = gethostbyname(myname.nodename)) == NULL)
+ return NULL;
+ memcpy(&d, hptr->h_addr_list[0], 4);
+
+ /* XXX: this probably isn't quite kosher, but it works */
+ aim_putcap(cap, 16, AIM_CAPS_GETFILE);
+
+ /* create the OSCAR packet */
+
+ if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x42)))
+ return NULL;
+ newpacket->lock = 1;
+
+ /* lock struct */
+ curbyte = 0;
+ curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+ /* XXX: check the cookie before commiting to using it */
+
+ /* Generate a random message cookie
+ * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */
+ for (i=0; i<7; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10));
+
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ /* grab all the data for cookie caching. */
+
+ cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t));
+ memcpy(cookie->cookie, newpacket->data+curbyte-8, 8);
+ cookie->type = AIM_COOKIETYPE_OFTGET;
+
+ priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv));
+ memcpy(priv->cookie, cookie, 8);
+ memcpy(priv->sn, destsn, sizeof(priv->sn));
+ memcpy(priv->fh.name, "listing.txt", strlen("listing.txt"));
+ priv->state = 1;
+ cookie->data = priv;
+ conn->priv = priv;
+ aim_cachecookie(sess, cookie);
+
+ /* cache da cookie */
+
+ /* Channel ID */
+ curbyte += aimutil_put16(newpacket->data+curbyte,0x0002);
+
+ /* Destination SN (prepended with byte length) */
+ curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn));
+ curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn));
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+ /* enTLV start */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042);
+
+ /* Flag data / ICBM Parameters? */
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ /* Cookie */
+ curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8);
+
+ /* Capability String */
+ curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10);
+
+ /* 000a/0002 : 0001 */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001);
+
+ /* 0003/0004: IP address */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004);
+ for(i = 0; i < 4; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, d[i]);
+
+ /* already in network byte order */
+
+ /* 0005/0002: Port */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002);
+ curbyte += aimutil_put16(newpacket->data+curbyte, port);
+
+ /* 000f/0000: ?? */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+ /* 2711/000c: ?? */
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c);
+ curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001);
+
+ for(i = 0; i < 0x000c - 4; i++)
+ curbyte += aimutil_put8(newpacket->data+curbyte, 0x00);
+
+ newpacket->commandlen = curbyte;
+ newpacket->lock = 0;
+ aim_tx_enqueue(sess, newpacket);
+
+ /* allocate and set up our connection */
+
+ i = fcntl(listenfd, F_GETFL, 0);
+ fcntl(listenfd, F_SETFL, i | O_NONBLOCK);
+ newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL);
+
+ if (!newconn){
+ perror("aim_newconn");
+ return NULL;
+ }
+
+ newconn->fd = listenfd;
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE;
+ newconn->priv = priv;
+ faimdprintf(2,"faim: listening (fd = %d, unconnected)\n", newconn->fd);
+
+ return newconn;
+}
+
+/*
+ * aim_oft_getfile_request - request a particular file over an established getfile connection
+ * @sess: your session
+ * @conn: the established OFT getfile connection
+ * @name: filename to request
+ * @size: size of the file
+ *
+ */
+faim_export int aim_oft_getfile_request(struct aim_session_t *sess, struct aim_conn_t *conn, const unsigned char *name, const int size)
+{
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+ if(!sess || !conn || !conn->priv || !name)
+ return 0;
+
+ if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x120c, conn, 0))) {
+ faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+ ft->fh.filesleft = 1;
+ ft->fh.totfiles = 1;
+ ft->fh.totparts = 1;
+ ft->fh.partsleft = 1;
+ ft->fh.totsize = size;
+ ft->fh.size = size;
+ ft->fh.checksum = 0;
+ memcpy(ft->fh.name, name, strlen(name));
+ memset(ft->fh.name+strlen(name), 0, 1);
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ if(!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+}
+
+/*
+ * aim_oft_getfile_ack - acknowledge a getfile download as complete
+ * @sess: your session
+ * @conn: the getfile conn to send the ack over
+ *
+ * call this function after you have read all the data in a particular
+ * filetransfer
+ *
+ */
+faim_export int aim_oft_getfile_ack(struct aim_session_t *sess, struct aim_conn_t *conn) {
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+
+ if(!sess || !conn || !conn->priv)
+ return -1;
+
+ if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0202, conn, 0))) {
+ faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100-8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+
+ if(!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+ return 0;
+}
+
+/*
+ * aim_oft_getfile_end - end a getfile.
+ * @sess: your session
+ * @conn: the getfile connection
+ *
+ * call this before you close the getfile connection if you're on the
+ * receiving/requesting end.
+ */
+faim_export int aim_oft_getfile_end(struct aim_session_t *sess, struct aim_conn_t *conn)
+{
+ struct command_tx_struct *newoft;
+ struct aim_filetransfer_priv *ft;
+
+ if(!sess || !conn || !conn->priv)
+ return -1;
+
+ if(!(newoft = aim_tx_new(AIM_FRAMETYPE_OFT, 0x0204, conn, 0))) {
+ faimdprintf(2, "faim: aim_accepttransfer: tx_new OFT failed\n");
+ return -1;
+ }
+
+ newoft->lock = 1;
+
+ memcpy(newoft->hdr.oft.magic, "OFT2", 4);
+ newoft->hdr.oft.hdr2len = 0x100 - 8;
+
+ if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ ft = (struct aim_filetransfer_priv *)conn->priv;
+ ft->state = 4; /* no longer wanting data */
+ ft->fh.nrecvd = ft->fh.size;
+ ft->fh.recvcsum = ft->fh.checksum;
+ ft->fh.flags = 0x21;
+
+ if(!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) {
+ newoft->lock = 0;
+ aim_tx_destroy(newoft);
+ return -1;
+ }
+
+ newoft->lock = 0;
+ aim_tx_enqueue(sess, newoft);
+
+ return 0;