+
+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 */
+ if(check0 == 0) /* XXX: check this corner case */
+ 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 */
+ if(check1 == 0) /* XXX: check this corner case */
+ 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;
+ }
+
+ check0 &= 0xff; /* grab just the lowest byte */
+ check1 &= 0xff; /* this should be clean, but just in case */
+
+ *checksum = ((check0 * 0x1000000) + (check1 * 0x10000));
+
+ return *checksum;
+}
+
+
+/*
+ * aim_oft_buildheader:
+ * fills a buffer with network-order fh data.
+ * returns length written; -1 on error.
+ * dest: buffer to fill -- pre-alloced
+ * listingfh: fh to get data from
+ *
+ * DOES NOT DO BOUNDS CHECKING!
+ */
+
+
+faim_internal int aim_oft_buildheader(char *dest,struct aim_fileheader_t *listingfh)
+{
+ int i, curbyte;
+
+ if(!dest || !listingfh)
+ return -1;
+
+ curbyte = 0;
+ for(i = 0; i < 8; i++)
+ curbyte += aimutil_put8(dest+curbyte, listingfh->bcookie[i]);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->encrypt);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->compress);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->totfiles);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->filesleft);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->totparts);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->partsleft);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->totsize);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->size);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->modtime);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->checksum);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->rfrcsum);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->rfsize);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->cretime);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->rfcsum);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->nrecvd);
+ curbyte += aimutil_put32(dest+curbyte, listingfh->recvcsum);
+
+ memcpy(dest+curbyte, listingfh->idstring, 32);
+ curbyte += 32;
+
+ curbyte += aimutil_put8(dest+curbyte, listingfh->flags);
+ curbyte += aimutil_put8(dest+curbyte, listingfh->lnameoffset);
+ curbyte += aimutil_put8(dest+curbyte, listingfh->lsizeoffset);
+
+ memcpy(dest+curbyte, listingfh->dummy, 69);
+ curbyte += 69;
+
+ memcpy(dest+curbyte, listingfh->macfileinfo, 16);
+ curbyte += 16;
+
+ curbyte += aimutil_put16(dest+curbyte, listingfh->nencode);
+ curbyte += aimutil_put16(dest+curbyte, listingfh->nlanguage);
+
+ memcpy(dest+curbyte, listingfh->name, 64); /* XXX: Filenames longer than 64B */
+ curbyte += 64;
+
+ return curbyte;
+}
+
+/*
+ * int aim_getfile_send(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh)
+ * conn is the OFT connection to shove the data down,
+ * tosend is the FILE * for the file to send
+ * fh is the filled-in fh value
+ * returns -1 on error, 1 on success.
+ */
+
+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 = write(conn->fd, buf, bufsize)) != 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 = write(conn->fd, buf, bufpos+1)) != (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;
+}
+
+/*
+ * int aim_getfile_send_chunk(struct aim_conn_t *conn, FILE *tosend, struct aim_fileheader_t *fh, int pos, int bufsize)
+ * conn is the OFT connection to shove the data down,
+ * tosend is the FILE * for the file to send
+ * fh is the filled-in fh value
+ * pos is the position to start at (at beginning should be 0, after 5
+ * bytes are sent should be 5); -1 for "don't seek"
+ * bufsize is 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.
+ */
+
+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( write(conn->fd, buf, bufpos+1) != (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);
+}
+
+/*
+ * aim_tx_destroy:
+ * free's tx_command_t's. if command is locked, doesn't free.
+ * returns -1 on error (locked struct); 0 on success.
+ * command is the command to free
+ */
+
+faim_internal int aim_tx_destroy(struct command_tx_struct *command)
+{
+ if(command->lock)
+ return -1;
+ if(command->data)
+ free(command->data);
+ free(command);
+
+ return 0;
+}
+
+#if 0
+/*
+ * aim_getfile_intitiate:
+ * For those times when we want to open up the directim channel ourselves.
+ * sess is your session,
+ * conn is the BOS conn,
+ * priv is a dummy priv value (we'll let it get filled in later) (if
+ * you pass a NULL, we alloc one)
+ * 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,
+ struct aim_getfile_priv *priv,
+ char *destsn)
+{
+ struct command_tx_struct *newpacket;
+ struct aim_conn_t *newconn;
+
+ struct aim_msgcookie_t *cookie;
+
+ int curbyte, i, listenfd;
+ short port = 4443;
+
+ struct hostent *hptr;
+ struct utsname myname;
+
+ char cap[16];
+ char d[4]; /* XXX: IPv6. *cough* */
+
+ /*
+ * 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_IMIMAGE);
+
+ /*
+ * create the OSCAR packet
+ */
+
+ if (!(newpacket = aim_tx_new(AIM_FRAMETYPE_OSCAR, 0x0002, conn, 10+8+2+1+strlen(destsn)+4+4+0x32)))
+ return NULL;
+
+ newpacket->lock = 1; /* lock struct */
+
+ curbyte = 0;
+ curbyte += aim_putsnac(newpacket->data+curbyte,
+ 0x0004, 0x0006, 0x0000, sess->snac_nextid);
+
+ /*
+ * 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() % 20));
+ 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_OFTIM;
+
+ if(!priv)
+ priv = (struct aim_directim_priv *)calloc(1, sizeof(struct aim_directim_priv));
+
+ memcpy(priv->cookie, cookie, 8);
+ memcpy(priv->sn, destsn, sizeof(priv->sn));
+
+ cookie->data = 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, 0x0032);
+
+ /*
+ * 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: umm.. dunno. Zigamorph[1]?
+ * [1]: see esr's TNHD.
+ */
+
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f);
+ curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000);
+
+ printf("curbyte: 0x%x\n",curbyte);
+
+ 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");
+ aim_conn_kill(sess, &newconn);
+ return NULL;
+ }
+
+ newconn->fd = listenfd;
+ newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM;
+ newconn->priv = priv;
+ printf("faim: listening (fd = %d, unconnected)\n", newconn->fd);
+
+ /*
+ * XXX We need some way of closing the listener socket after
+ * n seconds of no connection. -- mid
+ */
+
+#ifdef USE_SNAC_FOR_IMS
+ {
+ struct aim_snac_t snac;
+
+ snac.id = sess->snac_nextid;
+ snac.family = 0x0004;
+ snac.type = 0x0006;
+ snac.flags = 0x0000;
+
+ snac.data = malloc(strlen(destsn)+1);
+ memcpy(snac.data, destsn, strlen(destsn)+1);
+
+ aim_newsnac(sess, &snac);
+
+ aim_cleansnacs(sess, 60); /* clean out all SNACs over 60sec old */
+ }
+#endif
+
+ return newconn;
+}
+
+#endif