* Implemented high-speed pipelined file download in IPC code.
$Log$
+ Revision 601.11 2002/09/29 04:55:13 error
+ * File and attachment downloads now use the new IPC code.
+ * Implemented high-speed pipelined file download in IPC code.
+
Revision 601.10 2002/09/29 04:41:43 error
* file_ops.c: cmd_read() now returns a short read at end-of-file instead of
4096, this prevents trailing garbage on the downloaded file; also it now
Fri Jul 10 1998 Art Cancro <ajc@uncensored.citadel.org>
* Initial CVS import
+
#endif
char express_msgs = 0;
-static volatile int download_in_progress = 0; /* download file open */
-static volatile int upload_in_progress = 0; /* upload file open */
-/* static volatile int serv_sock; */ /* Socket on which we talk to server */
-
/*
* Does nothing. The server should always return 200.
/* OPEN */
-int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf, char *cret)
+int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
+ void (*progress_gauge_callback)(long, long), char *cret)
{
register int ret;
size_t bytes;
if (!filename) return -2;
if (!buf) return -2;
if (*buf) return -2;
- if (download_in_progress) return -2;
+ if (ipc->downloading) return -2;
aaa = (char *)malloc(strlen(filename) + 6);
if (!aaa) return -1;
sprintf(aaa, "OPEN %s", filename);
ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
free(aaa);
- /* FIXME: Possible race condition */
if (ret / 100 == 2) {
- download_in_progress = 1;
+ ipc->downloading = 1;
bytes = extract_long(cret, 0);
last_mod = extract_int(cret, 1);
extract(mimetype, cret, 2);
- ret = CtdlIPCReadDownload(ipc, buf, bytes, cret);
+ ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
+/* ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret); */
ret = CtdlIPCEndDownload(ipc, cret);
if (ret / 100 == 2)
sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
/* OPNA */
int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void **buf,
- char *cret)
+ void (*progress_gauge_callback)(long, long), char *cret)
{
register int ret;
size_t bytes;
if (*buf) return -2;
if (!part) return -2;
if (!msgnum) return -2;
- if (download_in_progress) return -2;
+ if (ipc->downloading) return -2;
aaa = (char *)malloc(strlen(part) + 17);
if (!aaa) return -1;
sprintf(aaa, "OPNA %ld|%s", msgnum, part);
ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
free(aaa);
- /* FIXME: Possible race condition */
if (ret / 100 == 2) {
- download_in_progress = 1;
+ ipc->downloading = 1;
bytes = extract_long(cret, 0);
last_mod = extract_int(cret, 1);
extract(mimetype, cret, 2);
- ret = CtdlIPCReadDownload(ipc, buf, bytes, cret);
+ ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
ret = CtdlIPCEndDownload(ipc, cret);
if (ret / 100 == 2)
sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
/* OIMG */
-int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf, char *cret)
+int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
+ void (*progress_gauge_callback)(long, long), char *cret)
{
register int ret;
size_t bytes;
if (!buf) return -1;
if (*buf) return -1;
if (!filename) return -1;
- if (download_in_progress) return -1;
+ if (ipc->downloading) return -1;
aaa = (char *)malloc(strlen(filename) + 6);
if (!aaa) return -1;
sprintf(aaa, "OIMG %s", filename);
ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
free(aaa);
- /* FIXME: Possible race condition */
if (ret / 100 == 2) {
- download_in_progress = 1;
+ ipc->downloading = 1;
bytes = extract_long(cret, 0);
last_mod = extract_int(cret, 1);
extract(mimetype, cret, 2);
- ret = CtdlIPCReadDownload(ipc, buf, bytes, cret);
+ ret = CtdlIPCReadDownload(ipc, buf, bytes, progress_gauge_callback, cret);
ret = CtdlIPCEndDownload(ipc, cret);
if (ret / 100 == 2)
sprintf(cret, "%d|%ld|%s|%s", bytes, last_mod,
if (!cret) return -1;
if (!filename) return -1;
if (!comment) return -1;
- if (upload_in_progress) return -1;
+ if (ipc->uploading) return -1;
aaa = (char *)malloc(strlen(filename) + strlen(comment) + 7);
if (!aaa) return -1;
sprintf(aaa, "UOPN %s|%s", filename, comment);
ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
free(aaa);
- /* FIXME: Possible race condition */
if (ret / 100 == 2)
- upload_in_progress = 1;
+ ipc->uploading = 1;
ret = CtdlIPCWriteUpload(ipc, buf, bytes, cret);
ret = CtdlIPCEndUpload(ipc, cret);
return ret;
if (!cret) return -1;
if (!filename) return -1;
- if (upload_in_progress) return -1;
+ if (ipc->uploading) return -1;
aaa = (char *)malloc(strlen(filename) + 17);
if (!aaa) return -1;
sprintf(aaa, "UIMG %d|%s", for_real, filename);
ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
free(aaa);
- /* FIXME: Possible race condition */
if (ret / 100 == 2)
- upload_in_progress = 1;
+ ipc->uploading = 1;
return ret;
}
*buf = (void *)realloc(*buf, (size_t)(offset + len));
if (*buf) {
/* I know what I'm doing */
- serv_read(ipc, (char *)&buf[offset], len);
+ serv_read(ipc, (*buf + offset), len);
} else {
/* We have to read regardless */
serv_read(ipc, aaa, len);
register int ret;
if (!cret) return -2;
- if (!download_in_progress) return -2;
+ if (!ipc->downloading) return -2;
ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
if (ret / 100 == 2)
- download_in_progress = 0;
+ ipc->downloading = 0;
return ret;
}
/* READ */
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, char *cret)
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+ void (*progress_gauge_callback)(long, long), char *cret)
{
register size_t len;
if (!cret) return -1;
if (!buf) return -1;
if (*buf) return -1;
- if (!download_in_progress) return -1;
+ if (!ipc->downloading) return -1;
len = 0;
+ if (progress_gauge_callback)
+ progress_gauge_callback(len, bytes);
while (len < bytes) {
- len = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
- if (len == -1) {
+ register size_t block;
+
+ block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
+ if (block == -1) {
free(*buf);
return 0;
}
+ len += block;
+ if (progress_gauge_callback)
+ progress_gauge_callback(len, bytes);
}
return len;
}
+/* READ - pipelined */
+int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+ void (*progress_gauge_callback)(long, long), char *cret)
+{
+ register size_t len;
+ register int calls; /* How many calls in the pipeline */
+ register int i; /* iterator */
+ char aaa[4096];
+
+ if (!cret) return -1;
+ if (!buf) return -1;
+ if (*buf) return -1;
+ if (!ipc->downloading) return -1;
+
+ *buf = (void *)realloc(*buf, bytes);
+ if (!*buf) return -1;
+
+ len = 0;
+ CtdlIPC_lock(ipc);
+ if (progress_gauge_callback)
+ progress_gauge_callback(len, bytes);
+
+ /* How many calls will be in the pipeline? */
+ calls = bytes / 4096;
+ if (bytes % 4096) calls++;
+
+ /* Send all requests at once */
+ for (i = 0; i < calls; i++) {
+ sprintf(aaa, "READ %d|4096", i * 4096);
+ CtdlIPC_putline(ipc, aaa);
+ }
+
+ /* Receive all responses at once */
+ for (i = 0; i < calls; i++) {
+ CtdlIPC_getline(ipc, aaa);
+ if (aaa[0] != '6')
+ strcpy(cret, &aaa[4]);
+ else {
+ len = extract_long(&aaa[4], 0);
+ /* I know what I'm doing */
+ serv_read(ipc, ((*buf) + (i * 4096)), len);
+ }
+ if (progress_gauge_callback)
+ progress_gauge_callback(i * 4096 + len, bytes);
+ }
+ CtdlIPC_unlock(ipc);
+ return len;
+}
+
+
/* UCLS */
int CtdlIPCEndUpload(CtdlIPC *ipc, char *cret)
{
register int ret;
if (!cret) return -1;
- if (!upload_in_progress) return -1;
+ if (!ipc->uploading) return -1;
ret = CtdlIPCGenericCommand(ipc, "UCLS", NULL, 0, NULL, NULL, cret);
if (ret / 100 == 2)
- upload_in_progress = 0;
+ ipc->uploading = 0;
return ret;
}
int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret);
int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename, const char *destnode, char *cret);
int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret);
-int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf, char *cret);
+int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
+ void (*progress_gauge_callback)(long, long), char *cret);
int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part, void **buf,
- char *cret);
-int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf, char *cret);
+ void (*progress_gauge_callback)(long, long), char *cret);
+int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
+ void (*progress_gauge_callback)(long, long), char *cret);
int CtdlIPCFileUpload(CtdlIPC *ipc, const char *filename, const char *comment, void *buf,
size_t bytes, char *cret);
int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *filename, size_t bytes,
int CtdlIPCEndUpload(CtdlIPC *ipc, char *cret);
int CtdlIPCWriteUpload(CtdlIPC *ipc, void *buf, size_t bytes, char *cret);
int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret);
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, char *cret);
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+ void (*progress_gauge_callback)(long, long), char *cret);
+int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+ void (*progress_gauge_callback)(long, long), char *cret);
int CtdlIPCGenericCommand(CtdlIPC *ipc, const char *command, const char *to_send,
size_t bytes_to_send, char **to_receive,
size_t *bytes_to_receive, char *proto_response);
int sock;
/* 1 if server is local, 0 otherwise or if not connected */
int isLocal;
+ /* 1 if a download is open on the server, 0 otherwise */
+ int downloading;
+ /* 1 if an upload is open on the server, 0 otherwise */
+ int uploading;
} CtdlIPC;
/* C constructor */
#endif
ipc->sock = -1; /* Not connected */
ipc->isLocal = 0; /* Not local, of course! */
+ ipc->downloading = 0;
+ ipc->uploading = 0;
strcpy(cithost, DEFAULT_HOST); /* default host */
strcpy(citport, DEFAULT_PORT); /* default port */
void getline(char *string, int lim);
int file_checksum(char *filename);
void do_edit(char *desc, char *read_cmd, char *check_cmd, char *write_cmd);
+void progress(long int curr, long int cmax);
long *msg_arr = NULL;
int msg_arr_size = 0;
char pagin;
char cmd[SIZ];
char targ[ROOMNAMELEN];
- char filename[SIZ];
+ char filename[PATH_MAX];
+ char save_to[PATH_MAX];
+ void *attachment = NULL; /* Downloaded attachment */
FILE *dest = NULL; /* Alternate destination other than screen */
int r; /* IPC response code */
case 'f':
newprompt("Which section? ", filename,
((sizeof filename) - 1));
- snprintf(cmd, sizeof cmd,
- "OPNA %ld|%s", msg_arr[a], filename);
- CtdlIPC_putline(ipc, cmd);
- CtdlIPC_getline(ipc, cmd);
- if (cmd[0] == '2') {
- extract(filename, &cmd[4], 2);
- download_to_local_disk(ipc, filename,
- extract_int(&cmd[4],
- 0));
+ r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
+ filename, &attachment, progress, cmd);
+ extract(filename, cmd, 2);
+ destination_directory(save_to, filename);
+ r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
+ filename, &attachment, progress, cmd);
+ if (r / 100 != 2) {
+ scr_printf("%s\n", cmd);
} else {
- scr_printf("%s\n", &cmd[4]);
+ save_buffer(attachment,
+ extract_long(cmd, 0),
+ save_to);
}
+ if (attachment) free(attachment);
goto RMSGREAD;
case 'd':
scr_printf("*** Delete this message? ");
}
-/* Here's the code for simply transferring the file to the client,
- * for folks who have their own clientware. It's a lot simpler than
- * the [XYZ]modem code below...
- * (This function assumes that a download file is already open on the server)
+/*
+ * saves filelen bytes from file at pathname
*/
-void download_to_local_disk(CtdlIPC *ipc,
- char *supplied_filename, long total_bytes)
+int save_buffer(void *file, size_t filelen, const char *pathname)
{
- char buf[SIZ];
- char dbuf[4096];
- long transmitted_bytes = 0L;
- long aa, bb;
- FILE *savefp;
- int broken = 0;
- int packet;
- char filename[SIZ];
+ size_t block = 0;
+ size_t bytes_written = 0;
+ FILE *fp;
- strcpy(filename, supplied_filename);
- if (strlen(filename) == 0) {
- newprompt("Filename: ", filename, 250);
+ fp = fopen(pathname, "w");
+ if (!fp) {
+ err_printf("Cannot open '%s': %s\n", pathname, strerror(errno));
+ return 0;
}
+ do {
+ block = fwrite(file + bytes_written, 1,
+ filelen - bytes_written, fp);
+ bytes_written += block;
+ } while (errno == EINTR && bytes_written < filelen);
+ fclose(fp);
- scr_printf("Enter the name of the directory to save '%s'\n"
- "to, or press return for the current directory.\n", filename);
- newprompt("Directory: ", dbuf, sizeof dbuf);
- if (strlen(dbuf) == 0)
- strcpy(dbuf, ".");
- strcat(dbuf, "/");
- strcat(dbuf, filename);
-
- savefp = fopen(dbuf, "w");
- if (savefp == NULL) {
- scr_printf("Cannot open '%s': %s\n", dbuf, strerror(errno));
- /* close the download file at the server */
- CtdlIPC_putline(ipc, "CLOS");
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '2') {
- scr_printf("%s\n", &buf[4]);
- }
- return;
- }
- progress(0, total_bytes);
- while ((transmitted_bytes < total_bytes) && (broken == 0)) {
- bb = total_bytes - transmitted_bytes;
- aa = ((bb < 4096) ? bb : 4096);
- snprintf(buf, sizeof buf, "READ %ld|%ld", transmitted_bytes, aa);
- CtdlIPC_putline(ipc, buf);
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '6') {
- scr_printf("%s\n", &buf[4]);
- return;
- }
- packet = extract_int(&buf[4], 0);
- serv_read(ipc, dbuf, packet);
- if (fwrite(dbuf, packet, 1, savefp) < 1)
- broken = 1;
- transmitted_bytes = transmitted_bytes + (long) packet;
- progress(transmitted_bytes, total_bytes);
- }
- fclose(savefp);
- /* close the download file at the server */
- CtdlIPC_putline(ipc, "CLOS");
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '2') {
- scr_printf("%s\n", &buf[4]);
+ if (bytes_written < filelen) {
+ err_printf("Trouble saving '%s': %s\n", pathname,
+ strerror(errno));
+ return 0;
}
- return;
+ return 1;
+}
+
+
+/*
+ * Save supplied_filename in dest directory; gets the name only
+ */
+void destination_directory(char *dest, const char *supplied_filename)
+{
+ scr_printf("Enter the name of the directory to save '%s'\n"
+ "to, or press return for the current directory.\n",
+ supplied_filename);
+ newprompt("Directory: ", dest, PATH_MAX);
+ if (strlen(dest) == 0) {
+ dest[0] = '.';
+ dest[1] = 0;
+ }
+ strcat(dest, "/");
+ strcat(dest, supplied_filename);
}
void download(CtdlIPC *ipc, int proto)
{
char buf[SIZ];
- char filename[SIZ];
- char tempname[SIZ];
+ char filename[PATH_MAX];
+ char tempname[PATH_MAX];
char transmit_cmd[SIZ];
- long total_bytes = 0L;
- char dbuf[4096];
- long transmitted_bytes = 0L;
- long aa, bb;
- int packet;
FILE *tpipe = NULL;
int broken = 0;
+ int r;
+ void *file = NULL; /* The downloaded file */
+ long filelen = 0L; /* The downloaded file length */
if ((room_flags & QR_DOWNLOAD) == 0) {
scr_printf("*** You cannot download from this room.\n");
return;
}
- newprompt("Enter filename: ", filename, 255);
+ newprompt("Enter filename: ", filename, PATH_MAX);
- snprintf(buf, sizeof buf, "OPEN %s", filename);
- CtdlIPC_putline(ipc, buf);
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '2') {
- scr_printf("%s\n", &buf[4]);
+ /* Save to local disk, for folks with their own copy of the client */
+ if (proto == 5) {
+ destination_directory(tempname, filename);
+ r = CtdlIPCFileDownload(ipc, filename, &file, progress, buf);
+ if (r / 100 != 2) {
+ scr_printf("%s\n", buf);
+ return;
+ }
+ save_buffer(file, extract_long(buf, 0), tempname);
+ free(file);
return;
}
- total_bytes = extract_long(&buf[4], 0);
- /* Save to local disk, for folks with their own copy of the client */
- if (proto == 5) {
- download_to_local_disk(ipc, filename, total_bytes);
+ r = CtdlIPCFileDownload(ipc, filename, &file, progress, buf);
+ if (r / 100 != 2) {
+ scr_printf("%s\n", buf);
return;
}
+ filelen = extract_long(buf, 0);
/* Meta-download for public clients */
- scr_printf("Fetching file from Citadel server...\n");
+ /* scr_printf("Fetching file from Citadel server...\n"); */
mkdir(tempdir, 0700);
snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
tpipe = fopen(tempname, "wb");
- while ((transmitted_bytes < total_bytes) && (broken == 0)) {
- progress(transmitted_bytes, total_bytes);
- bb = total_bytes - transmitted_bytes;
- aa = ((bb < 4096) ? bb : 4096);
- snprintf(buf, sizeof buf, "READ %ld|%ld", transmitted_bytes, aa);
- CtdlIPC_putline(ipc, buf);
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '6') {
- scr_printf("%s\n", &buf[4]);
- }
- packet = extract_int(&buf[4], 0);
- serv_read(ipc, dbuf, packet);
- if (fwrite(dbuf, packet, 1, tpipe) < 1) {
- broken = 1;
- }
- transmitted_bytes = transmitted_bytes + (long) packet;
+ if (fwrite(file, filelen, 1, tpipe) < filelen) {
+ broken = 1;
}
fclose(tpipe);
- progress(transmitted_bytes, total_bytes);
-
- /* close the download file at the server */
- CtdlIPC_putline(ipc, "CLOS");
- CtdlIPC_getline(ipc, buf);
- if (buf[0] != '2') {
- scr_printf("%s\n", &buf[4]);
- }
+ if (file) free(file);
if (proto == 0) {
+ /* FIXME: display internally instead */
snprintf(transmit_cmd, sizeof transmit_cmd,
"SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
tempname);
else if (proto == 4)
snprintf(transmit_cmd, sizeof transmit_cmd, "exec sz %s", tempname);
else
+ /* FIXME: display internally instead */
snprintf(transmit_cmd, sizeof transmit_cmd, "exec cat %s", tempname);
screen_reset();
/* clean up the temporary directory */
nukedir(tempdir);
- scr_putc(7);
+ scr_putc(7); /* Beep beep! */
}
void edit_floor(CtdlIPC *ipc);
void kill_floor(CtdlIPC *ipc);
void enter_bio(CtdlIPC *ipc);
-void download_to_local_disk(CtdlIPC *ipc, char *, long);
void hit_any_key(void);
+int save_buffer(void *file, size_t filelen, const char *pathname);
+void destination_directory(char *dest, const char *supplied_filename);
/*
void progress(long int curr, long int cmax)
{
- static long dots_printed;
+ static char dots[] =
+ "**************************************************";
+ char dots_printed[51];
+ char fmt[42];
long a;
- if (curr==0) {
- scr_printf(".......................................");
- scr_printf(".......................................\r");
- scr_flush();
- dots_printed = 0;
- }
- else if (curr==cmax) {
- scr_printf("\r%79s\n","");
- }
- else {
- a=(curr * 100) / cmax;
- a=a*78; a=a/100;
- while (dots_printed < a) {
- scr_printf("*");
- ++dots_printed;
- scr_flush();
- }
+ if (curr >= cmax) {
+ sln_printf("\r%79s\r","");
+ } else {
+ /* a will be range 0-50 rather than 0-100 */
+ a=(curr * 50) / cmax;
+ sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
+ strncpy(dots_printed, dots, a);
+ dots_printed[a] = 0;
+ sln_printf(fmt, dots_printed, "",
+ curr * 100 / cmax, curr, cmax);
+ sln_flush();
}
}