+ aim_conn_kill(sess, &conn);
+
+ dvprintf("faimtest: getfile: disconnected from %s\n", sn);
+ return 1;
+}
+int faimtest_getfile_initiate(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ struct aim_conn_t *conn, *listenerconn;
+ struct aim_filetransfer_priv *priv;
+
+ va_start(ap, command);
+ conn = va_arg(ap, struct aim_conn_t *);
+ listenerconn = va_arg(ap, struct aim_conn_t *);
+ va_end(ap);
+
+ aim_conn_close(listenerconn);
+ aim_conn_kill(sess, &listenerconn);
+
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ, faimtest_getfile_filereq, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND, faimtest_getfile_filesend, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE, faimtest_getfile_complete, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEDISCONNECT, faimtest_getfile_disconnect, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING, faimtest_getfile_listing, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ, faimtest_getfile_listingreq, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE, faimtest_getfile_receive, 0);
+ aim_conn_addhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4, faimtest_getfile_state4, 0);
+
+ priv = (struct aim_filetransfer_priv *)conn->priv;
+
+ dvprintf("faimtest: getfile: %s (%s) connected to us on %d\n", priv->sn, priv->ip, conn->fd);
+ return 1;
+}
+
+int faimtest_getfile_listing(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ struct aim_conn_t *conn;
+ char *listing;
+ struct aim_filetransfer_priv *ft;
+ char *filename, *nameend, *sizec;
+ int filesize, namelen;
+
+ va_start(ap, command);
+ conn = va_arg(ap, struct aim_conn_t *);
+ ft = va_arg(ap, struct aim_filetransfer_priv *);
+ listing = va_arg(ap, char *);
+ va_end(ap);
+
+ dvprintf("listing on %d==================\n%s\n===========\n", conn->fd, listing);
+
+ nameend = strstr(listing+0x1a, "\r");
+
+ namelen = nameend - (listing + 0x1a);
+
+ filename = malloc(namelen + 1);
+ strncpy(filename, listing+0x1a, namelen);
+ filename[namelen] = 0x00;
+
+ sizec = malloc(8+1);
+ memcpy(sizec, listing + 0x11, 8);
+ sizec[8] = 0x00;
+
+ filesize = strtol(sizec, (char **)NULL, 10);
+
+ dvprintf("faimtest: requesting %d %s(%d long)\n", namelen, filename, filesize);
+
+ aim_oft_getfile_request(sess, conn, filename, filesize);
+
+ free(filename);
+ free(sizec);
+
+ return 0;
+}
+
+int faimtest_getfile_listingreq(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ struct aim_conn_t *oftconn;
+ struct aim_fileheader_t *fh;
+ int pos, bufpos = 0, bufsize = 2048, i;
+ char *buf;
+
+ va_start(ap, command);
+ oftconn = va_arg(ap, struct aim_conn_t *);
+ fh = va_arg(ap, struct aim_fileheader_t *);
+ va_end(ap);
+
+ dvprintf("faimtest: sending listing of size %ld\n", fh->size);
+
+ if(!(buf = malloc(2048)))
+ return -1;
+
+ 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(oftconn->fd, buf, bufsize, 0)) != bufsize ) {
+ dperror("faim: getfile_send: write1");
+ dprintf("faim: getfile_send: whoopsy, didn't write it all...\n");
+ free(buf);
+ return -1;
+ }
+ }
+ if( (buf[bufpos] = fgetc(listingfile)) == EOF) {
+ if(pos != fh->size) {
+ dvprintf("faim: getfile_send: hrm... apparent early EOF at pos 0x%x of 0x%lx\n", pos, fh->size);
+ free(buf);
+ return -1;
+ }
+ }
+ }
+
+ if( (i = send(oftconn->fd, buf, bufpos+1, 0)) != (bufpos+1)) {
+ dperror("faim: getfile_send: write2");
+ dprintf("faim: getfile_send cleanup: whoopsy, didn't write it all...\n");
+ free(buf);
+ return -1;
+ }
+
+ dprintf("faimtest: sent listing\n");
+ free(buf);
+ return 0;
+}
+
+int faimtest_getfile_receive(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ struct aim_conn_t *conn;
+ struct aim_filetransfer_priv *ft;
+ unsigned char data;
+ int pos;
+
+ va_start(ap, command);
+ conn = va_arg(ap, struct aim_conn_t *);
+ ft = va_arg(ap, struct aim_filetransfer_priv *);
+ va_end(ap);
+
+ dvprintf("faimtest: receiving %ld bytes of file data for %s:\n\t", ft->fh.size, ft->fh.name);
+
+ for(pos = 0; pos < ft->fh.size; pos++) {
+ read(conn->fd, &data, 1);
+ printf("%c(%02x) ", data, data);
+ }
+
+ printf("\n");
+
+ aim_oft_getfile_end(sess, conn);
+
+ return 0;
+}
+
+int faimtest_getfile_state4(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ va_list ap;
+ struct aim_conn_t *conn;
+
+ va_start(ap, command);
+ conn = va_arg(ap, struct aim_conn_t *);
+ va_end(ap);
+
+ aim_conn_close(conn);
+ aim_conn_kill(sess, &conn);
+ return 0;
+}
+
+
+int faimtest_parse_ratechange(struct aim_session_t *sess, struct command_rx_struct *command, ...)
+{
+ static char *codes[5] = {"invalid",
+ "change",
+ "warning",
+ "limit",
+ "limit cleared"};
+ va_list ap;
+ int code;
+ unsigned long rateclass, windowsize, clear, alert, limit, disconnect;
+ unsigned long currentavg, maxavg;
+
+ va_start(ap, command);
+
+ /* See code explanations below */
+ code = va_arg(ap, int);
+
+ /*
+ * See comments above aim_parse_ratechange_middle() in aim_rxhandlers.c.
+ */
+ rateclass = va_arg(ap, unsigned long);
+
+ /*
+ * Not sure what this is exactly. I think its the temporal
+ * relation factor (ie, how to make the rest of the numbers
+ * make sense in the real world).
+ */
+ windowsize = va_arg(ap, unsigned long);
+
+ /* Explained below */
+ clear = va_arg(ap, unsigned long);
+ alert = va_arg(ap, unsigned long);
+ limit = va_arg(ap, unsigned long);
+ disconnect = va_arg(ap, unsigned long);
+ currentavg = va_arg(ap, unsigned long);
+ maxavg = va_arg(ap, unsigned long);
+
+ va_end(ap);
+
+
+ dvprintf("faimtest: rate %s (rate class 0x%04lx): curavg = %ld, maxavg = %ld, alert at %ld, clear warning at %ld, limit at %ld, disconnect at %ld (window size = %ld)\n",
+ (code < 5)?codes[code]:"invalid",
+ rateclass,
+ currentavg, maxavg,
+ alert, clear,
+ limit, disconnect,
+ windowsize);
+
+ if (code == AIM_RATE_CODE_CHANGE) {
+ /*
+ * Not real sure when these get sent.
+ */
+ if (currentavg >= clear)
+ aim_conn_setlatency(command->conn, 0);
+
+ } else if (code == AIM_RATE_CODE_WARNING) {
+ /*
+ * We start getting WARNINGs the first time we go below the 'alert'
+ * limit (currentavg < alert) and they stop when either we pause
+ * long enough for currentavg to go above 'clear', or until we
+ * flood it bad enough to go below 'limit' (and start getting
+ * LIMITs instead) or even further and go below 'disconnect' and
+ * get disconnected completely (and won't be able to login right
+ * away either).
+ */
+ aim_conn_setlatency(command->conn, windowsize/4); /* XXX this is bogus! */
+
+ } else if (code == AIM_RATE_CODE_LIMIT) {
+ /*
+ * When we hit LIMIT, messages will start getting dropped.
+ */
+ aim_conn_setlatency(command->conn, windowsize/2); /* XXX this is bogus! */
+
+ } else if (code == AIM_RATE_CODE_CLEARLIMIT) {
+ /*
+ * The limit is cleared when curavg goes above 'clear'.
+ */
+ aim_conn_setlatency(command->conn, 0);
+ }