#include "ftptool.h" #pragma ident "@(#)transfer.c 1.6 94/07/14" #ifdef USE_PROTOTYPES int get_file(char *name, char *localname, int size) #else int get_file(name, localname, size) char *name; char *localname; int size; #endif { char *ftperr; int rval, answer; footer_message("Receiving %s...", name); if (abort_transfer) { return (EINTR); } if (!batch_mode && confirmoverwrites && (access(localname, F_OK) == 0)) { #ifdef XVIEW3 Xv_notice notice; notice = xv_create(base_window.panel, NOTICE, NOTICE_MESSAGE_STRINGS, localname, "already exists. Do you want to overwrite it?", NULL, NOTICE_BUTTON_YES, "Yes, overwrite it.", NOTICE_BUTTON, "No, stop the transfer.", 2, NOTICE_STATUS, &answer, XV_SHOW, TRUE, NULL); xv_destroy_safe(notice); #else answer = notice_prompt(base_window.panel, NULL, NOTICE_MESSAGE_STRINGS, localname, "already exists. Do you want to overwrite it?", NULL, NOTICE_BUTTON_YES, "Yes, overwrite it.", NOTICE_BUTTON, "No, stop the transfer.", 2, NULL); #endif if (answer != NOTICE_YES) { footer_message(""); return (EINTR); } } xv_set(base_window.abort, PANEL_INACTIVE, FALSE, NULL); settype((int)xv_get(host_window.advanced.transfer_mode, PANEL_VALUE)); rval = recvrequest("RETR", localname, name, "w", size); switch (rval) { case 1: /* non_fatal error */ ftperr = index(response_line, ':'); if (ftperr != NULL) { ftperr++; if (!strncmp(ftperr, "Permission", 10)) rval = EPERM; else if (!strncmp(ftperr, "not a plain file", 16)) rval = EISDIR; else rval = 0; } else rval = 0; break; case 2: rval = EIO; break; default: break; } return (rval); } #ifdef USE_PROTOTYPES int put_file(char *name, char *remote_name, int size) #else int put_file(name, remote_name, size) char *name; char *remote_name; int size; #endif { char *ftperr; int rval; local_footer_message("Sending %s...", name); if (abort_transfer) { return (EINTR); } xv_set(base_window.abort, PANEL_INACTIVE, FALSE, NULL); settype((int)xv_get(host_window.advanced.transfer_mode, PANEL_VALUE)); /* if unique, then use STOU */ if (unique_remote_names) rval = sendrequest("STOU", name, remote_name, size); else rval = sendrequest("STOR", name, remote_name, size); switch (rval) { case 1: /* non_fatal error */ ftperr = index(response_line, ':'); if (ftperr != NULL) { ftperr++; if (!strncmp(ftperr, "Permission", 10)) rval = EPERM; else rval = 0; } else rval = 0; break; case 2: rval = EIO; break; default: break; } return (rval); } #ifdef USE_PROTOTYPES int get_dir(char *parent_remote_dir, char *parent_local_dir, char *name, char *localname) #else int get_dir(parent_remote_dir, parent_local_dir, name, localname) char *parent_remote_dir; char *parent_local_dir; char *name; char *localname; #endif { int rval = 0; extern int errno; struct dirlist *head = NULL; struct dirlist *tmp; char *ftperr; char *rdir = NULL; char *ldir = NULL; char *lname = NULL; /* transfer a remote directory and its contents to the local machine */ /* First, make a directory */ if ((rval = make_dirs(localname, 1)) != 0) { if (rval != EEXIST) { /* XXX - this message is clobbered by 'done reading' */ /* message */ return (errno); } } /* cd to that dir */ command("CWD %s", name); if (code != 250) { sprintf(scratch, "Remote cd to %s failed.", name); ftperr = ftp_error(' ', scratch); footer_message(ftperr); return (1); } /* local cd */ if (chdir(localname) == -1) { /* go back up */ footer_message("Local cd to %s failed.", localname); (void) command("CWD %s", parent_remote_dir); return (errno); } head = read_remote_dir(); if (head == NULL) goto out; /* do files first */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (S_ISREG(tmp->mode)) { if ((rval = get_file(tmp->name, tmp->name, tmp->size)) != 0) goto out; } } rdir = make_path(parent_remote_dir, name); if (rdir == NULL) goto out; ldir = make_path(parent_local_dir, localname); if (ldir == NULL) goto out; /* recursively do directories */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (!S_ISDIR(tmp->mode)) continue; if ((rval = get_dir(rdir, ldir, tmp->name, tmp->name)) != 0) goto out; } /* try symlinks */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (!S_ISLNK(tmp->mode)) continue; lname = linkname(tmp->name); if (lname == NULL) goto out; rval = get_file(lname, lname, -1); if (rval == EISDIR) { /* try as a directory */ if ((rval = get_dir(rdir, ldir, lname, lname)) > 1) { /* if (rval == EEXIST) { } */ goto out; } } else if (rval != 0) { goto out; } free(lname); lname = NULL; } out: if (head) free_dirlist(head); if (rdir) free(rdir); if (ldir) free(ldir); /* go back up */ (void) command("CWD %s", parent_remote_dir); chdir(parent_local_dir); return (rval); } #ifdef USE_PROTOTYPES int put_dir(char *parent_remote_dir, char *parent_local_dir, char *name, char *localname) #else int put_dir(parent_remote_dir, parent_local_dir, name, localname) char *parent_remote_dir; char *parent_local_dir; char *name; char *localname; #endif { extern int errno; int rval = 0; struct dirlist *head = NULL; struct dirlist *tmp; char *ftperr; char *rdir = NULL; char *ldir = NULL; char *lname = NULL; /* transfer a local directory and its contents to the remote machine */ /* First, make a directory */ if (make_remote_dirs(name, 1)) { /* XXX - error? */ /* goto out; */ } /* cd to that dir */ if (command("CWD %s", name) == ERROR && code == 250) { sprintf(scratch, "Remote cd to %s failed.", name); ftperr = ftp_error(' ', scratch); local_footer_message(ftperr); return (1); } /* local cd */ if (chdir(localname) == -1) { local_footer_message("Local cd to %s failed.", localname); /* go back up */ (void) command("CWD %s", parent_remote_dir); return (errno); } head = read_local_dir("."); if (head == NULL) { rval = ENOMEM; goto out; } /* do files first */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (!S_ISREG(tmp->mode)) continue; if ((rval = put_file(tmp->name, tmp->name, tmp->size)) != 0) goto out; } rdir = make_path(parent_remote_dir, name); if (rdir == NULL) goto out; ldir = make_path(parent_local_dir, localname); if (ldir == NULL) goto out; /* recursively do directories */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (!S_ISDIR(tmp->mode)) continue; if ((rval = put_dir(rdir, ldir, tmp->name, tmp->name)) != 0) goto out; } /* try symlinks */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { if (!S_ISLNK(tmp->mode)) continue; lname = linkname(tmp->name); if (lname == NULL) goto out; rval = put_file(lname, lname, -1); if (rval == EPERM) { /* try as a directory */ if ((rval = put_dir(rdir, ldir, lname, lname)) > 1) { /* if (rval == EEXIST) { } */ goto out; } } else if (rval != 0) { goto out; } free(lname); lname = NULL; } out: if (head) free_dirlist(head); if (rdir) free(rdir); if (ldir) free(ldir); /* go back up */ (void) command("CWD %s", parent_remote_dir); chdir(parent_local_dir); return (rval); } #ifdef USE_PROTOTYPES char *make_path(char *parent, char *curdir) #else char *make_path(parent, curdir) char *parent; char *curdir; #endif { char *tmp; if (*curdir == '/') { tmp = (char *)strdup(curdir); } else { tmp = (char *)malloc((unsigned int)(strlen(parent) + 1 + strlen(curdir) + 1)); if (tmp == NULL) return (NULL); strcpy(tmp, parent); if (strcmp(parent, "/")) strcat(tmp, "/"); strcat(tmp, curdir); } return (tmp); } #define FILE_PERCENT 5.0 #define TOTAL_PERCENT 5.0 static double last_percent; static double last_total_percent; static int total_so_far; static int total_transfer_size; static struct timeval start; #ifdef USE_PROTOTYPES void init_status(int total) #else void init_status(total) int total; #endif { int gettimeofday(); total_so_far = 0; total_transfer_size = total; last_total_percent = 0; xv_set(status_window.total_gauge, PANEL_INACTIVE, FALSE, PANEL_VALUE, 0, NULL); (void) gettimeofday(&start, (struct timezone *)NULL); status_footer_message("Total transfer size: %d bytes.", total_transfer_size); } #ifdef USE_PROTOTYPES void end_status(void) #else void end_status() #endif { struct timeval stop; double total_time; double rate; char *unit; int gettimeofday(); (void) gettimeofday(&stop, (struct timezone *)NULL); xv_set(status_window.total_gauge, PANEL_INACTIVE, FALSE, PANEL_VALUE, 0, NULL); total_time = (stop.tv_sec + stop.tv_usec/1000000.0) - (start.tv_sec + start.tv_usec/1000000.0); rate = (double)total_so_far/total_time; if (rate >= 1048576.0) { unit = "Mbyte"; rate /= 1048576.0; } else if (rate >= 1024.0) { unit = "Kbyte"; rate /= 1024.0; } else unit = "byte"; sprintf(scratch, "%.2f seconds, %.2f %ss/second", total_time, rate, unit); status_footer_message(scratch); (void) strcat(scratch, "\n"); log_message(scratch); } #ifdef USE_PROTOTYPES void update_status_label(char *direction, char *name, int size) #else void update_status_label(direction, name, size) char *direction; char *name; int size; #endif { static char sizestr[40]; static char string[MAXPATHLEN + 30]; Rect *butrect; sprintf(string, "%s %s", direction, name); xv_set(status_window.message, PANEL_LABEL_STRING, string, NULL); if (size >= 0) { sprintf(sizestr, "%d bytes", size); xv_set(status_window.size, PANEL_LABEL_STRING, sizestr, NULL); last_percent = 0; xv_set(status_window.total_gauge, PANEL_INACTIVE, FALSE, NULL); } else { xv_set(status_window.size, PANEL_LABEL_STRING, "Symbolic links not counted.", NULL); xv_set(status_window.total_gauge, PANEL_INACTIVE, TRUE, NULL); } butrect = (Rect *)xv_get(status_window.dismiss, XV_RECT); xv_set(status_window.dismiss, XV_X, xv_get(status_window.panel, XV_WIDTH) / 2 - butrect->r_width / 2, NULL); } #ifdef USE_PROTOTYPES void update_status_gauge(long bytes) #else void update_status_gauge(bytes) long bytes; #endif { double percent; if (bytes <= 0) /* shouldn't happen */ return; total_so_far += bytes; if (total_so_far > total_transfer_size) total_so_far = total_transfer_size; if (total_transfer_size > 0.0) percent = (double)total_so_far / (double)total_transfer_size * 100.0; else percent = 0.0; if (percent < 0.0) percent = 0.0; if (percent > 100.0) percent = 100.0; if (percent >= (last_total_percent + TOTAL_PERCENT)) { while ((last_total_percent + TOTAL_PERCENT) < percent) last_total_percent += TOTAL_PERCENT; xv_set(status_window.total_gauge, PANEL_VALUE, (int)last_total_percent, NULL); } } #ifdef USE_PROTOTYPES int sum_local_dir(char *parent, char *dir) #else int sum_local_dir(parent, dir) char *parent, *dir; #endif { int total = 0; char *ldir = NULL; struct dirlist *head = NULL; struct dirlist *tmp; ldir = make_path(parent, dir); if (ldir == NULL) return (0); if (chdir(ldir) == -1) { status_footer_message("Could not change to directory: %s", sys_errlist[errno]); goto out; } head = read_local_dir("."); if (head == NULL) { goto out; } /* do files first */ for (tmp = head->next; tmp != NULL; tmp = tmp->next) { switch (tmp->mode & S_IFMT) { case S_IFREG: total += tmp->size; break; case S_IFDIR: total += sum_local_dir(ldir, tmp->name); break; default: break; } } if (chdir(parent) == -1) { status_footer_message("Could not change to parent: %s", sys_errlist[errno]); } out: if (ldir) free(ldir); if (head) free_dirlist(head); return (total); } /* * Called to sum all the sizes from the currently selected local items. */ #ifdef USE_PROTOTYPES int sum_local_size(void) #else int sum_local_size() #endif { int nitems; int row; int total = 0; struct dirlist *tmp; status_footer_message("Determining send total..."); /* loop over each selected element, and do a get, then unselect */ nitems = xv_get(local_window.list, PANEL_LIST_NROWS); for (row = 0; row < nitems; row++) if (xv_get(local_window.list, PANEL_LIST_SELECTED, row)) { tmp = (struct dirlist *)xv_get(local_window.list, PANEL_LIST_CLIENT_DATA, row); switch (tmp->mode & S_IFMT) { case S_IFDIR: total += sum_local_dir(local_dircache.first->name, tmp->name); break; case S_IFREG: total += tmp->size; break; case S_IFLNK: status_footer_message( "Ignoring symlink %s...", tmp->name); log_message( "Can only sum sizes of files and directories.\n"); break; default: status_footer_message( "Ignoring non-file/directory %s.", tmp->name); log_message( "Can only transfer files and directories.\n"); break; } } return (total); } #ifdef USE_PROTOTYPES int sum_remote_dir(char *parent, char *dir) #else int sum_remote_dir(parent, dir) char *parent, *dir; #endif { int total = 0; char *rdir = NULL; struct dirlist *head = NULL; struct dirlist *tmp; if (!strcmp(dir, "..")) return (0); rdir = make_path(parent, dir); if (rdir == NULL) return (0); code = -1; command("CWD %s", rdir); if (code == 550) { goto out; } head = read_remote_dir(); if (head == NULL) { goto out; } for (tmp = head->next; tmp != NULL; tmp = tmp->next) { switch (tmp->mode & S_IFMT) { case S_IFREG: if (tmp->size != -1) total += tmp->size; else status_footer_message( "Size of %s not available", tmp->name); break; case S_IFDIR: total += sum_remote_dir(rdir, tmp->name); break; default: break; } } (void) command("CWD %s", parent); out: if (rdir) free(rdir); if (head) free_dirlist(head); return (total); } /* * Called to sum all the sizes from the currently selected local items. */ #ifdef USE_PROTOTYPES int sum_remote_size(void) #else int sum_remote_size() #endif { int nitems; int row; int total = 0; struct dirlist *tmp; int mode; status_footer_message("Determining receive total..."); /* loop over each selected element, and do a get, then unselect */ nitems = xv_get(base_window.list, PANEL_LIST_NROWS); for (row = 0; row < nitems; row++) if (xv_get(base_window.list, PANEL_LIST_SELECTED, row)) { tmp = (struct dirlist *)xv_get(base_window.list, PANEL_LIST_CLIENT_DATA, row); mode = tmp->mode & S_IFMT; if (non_unix) mode = S_IFREG; switch (mode) { case S_IFDIR: total += sum_remote_dir(remote_dircache.first->name, tmp->name); break; case S_IFREG: if (tmp->size != -1) total += tmp->size; else status_footer_message( "Size of %s not available", tmp->name); break; case S_IFLNK: status_footer_message( "Ignoring symlink %s...", tmp->name); log_message( "Can only sum sizes of files and directories.\n"); break; default: status_footer_message( "Ignoring non-file/directory %s.", tmp->name); log_message( "Can only transfer files and directories.\n"); break; } } return (total); } #ifdef USE_PROTOTYPES int sum_remote_batch_size(void) #else int sum_remote_batch_size() #endif { int nitems; int row; int total = 0; struct batchlist *tmp; int mode; status_footer_message("Determining receive total..."); /* loop over each selected element, and do a get, then unselect */ nitems = xv_get(schedule_window.receive_list, PANEL_LIST_NROWS); for (row = 0; row < nitems; row++) { tmp = (struct batchlist *)xv_get(schedule_window.receive_list, PANEL_LIST_CLIENT_DATA, row); mode = tmp->mode & S_IFMT; if (non_unix) mode = S_IFREG; switch (mode) { case S_IFDIR: total += sum_remote_dir(remote_dircache.first->name, tmp->name); break; case S_IFREG: if (tmp->size != -1) total += tmp->size; else status_footer_message( "Size of %s not available", tmp->name); break; case S_IFLNK: status_footer_message( "Ignoring symlink %s...", tmp->name); log_message( "Can only sum sizes of files and directories.\n"); break; default: status_footer_message( "Ignoring non-file/directory %s.", tmp->name); log_message( "Can only transfer files and directories.\n"); break; } } return (total); } #ifdef USE_PROTOTYPES int sum_local_batch_size(void) #else int sum_local_batch_size() #endif { int nitems; int row; int total = 0; struct batchlist *tmp; status_footer_message("Determining send total..."); /* loop over each selected element, and do a get, then unselect */ nitems = xv_get(schedule_window.send_list, PANEL_LIST_NROWS); for (row = 0; row < nitems; row++) { tmp = (struct batchlist *)xv_get(schedule_window.send_list, PANEL_LIST_CLIENT_DATA, row); switch (tmp->mode & S_IFMT) { case S_IFDIR: total += sum_local_dir(local_dircache.first->name, tmp->name); break; case S_IFREG: total += tmp->size; break; case S_IFLNK: status_footer_message( "Ignoring symlink %s...", tmp->name); log_message( "Can only sum sizes of files and directories.\n"); break; default: status_footer_message( "Ignoring non-file/directory %s.", tmp->name); log_message( "Can only transfer files and directories.\n"); break; } } return (total); }