/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % X X TTTTT PPPP % % X X T P P % % X T PPPP % % X X T P % % X X T P % % % % % % File transfer program. % % % % % % % % Software Design % % John Cristy % % October 1992 % % % % % % Copyright 1995 E. I. Dupont de Nemours and Company % % % % Permission to use, copy, modify, distribute, and sell this software and % % its documentation for any purpose is hereby granted without fee, % % provided that the above copyright notice appear in all copies and that % % both that copyright notice and this permission notice appear in % % supporting documentation, and that the name of E. I. Dupont de Nemours % % and Company not be used in advertising or publicity pertaining to % % distribution of the software without specific, written prior % % permission. E. I. Dupont de Nemours and Company makes no representations % % about the suitability of this software for any purpose. It is provided % % "as is" without express or implied warranty. % % % % E. I. Dupont de Nemours and Company disclaims all warranties with regard % % to this software, including all implied warranties of merchantability % % and fitness, in no event shall E. I. Dupont de Nemours and Company be % % liable for any special, indirect or consequential damages or any % % damages whatsoever resulting from loss of use, data or profits, whether % % in an action of contract, negligence or other tortious action, arising % % out of or in connection with the use or performance of this software. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Xtp is a utility for retrieving, listing, or printing files from a % remote network site. Xtp performs most of the same functions as the % FTP program, but does not require any interactive commands. You simply % specify the file transfer task on the command line and xtp performs the % transfer automatically. % % This program was adapted from a similiar program written by Steve Singles, % University of Delaware. % % Command syntax: % % Usage: xtp [-options ...] < uniform resource locator > % % Where options include: % -account password supplemental password % -binary retrieve files as binary % -exclude expression exclude files that match the expression % -directory list file names that match the expression % -file name store the file with this name % -get get files that match the expression % -port number port number of FTP server % -print print files that match the expression % -proxy hostname access remote host via this proxy host % -prune do not recursively search for files % -put put files that match the expression % -retrieve retrieve files that match the expression % -timeout seconds specifies maximum seconds of XTP session % % */ /* Include declarations. */ #include "xtp.h" #include "regular.h" #include #include #include #ifdef SVR4 #include #include #endif #include #include /* Variable declarations. */ static char *client_name, slave_tty[16]; static int child, master, status; static RegularExpression *directory_expression, *exclude_expression, *print_expression, *retrieve_expression; /* External declarations. */ extern char *GetHostInfo(char *); /* Forward declarations. */ static char *Wait(void); static int MakeDirectory(char *); static void DirectoryRequest(char *, char *), Error(char *,char *), ExecuteFtp(char *,char *), GetPseudoTerminal(void), PrintRequest(char *,unsigned int), ProcessRequest(char *,unsigned int,unsigned int), RetrieveRequest(char *,unsigned int), Usage(void); /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % D i r e c t o r y R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function DirectoryRequest lists a file name and its attributes. % % The format of the DirectoryRequest routine is: % % DirectoryRequest(fileinfo,filename) % % A description of each parameter follows: % % o fileinfo: Specifies a pointer to a character array that contains % information about the file. % % o filename: Specifies a pointer to a character array that contains % the name of the file. % */ static void DirectoryRequest(char *fileinfo,char *filename) { register char *p; status=0; for (p=filename; *p != '\0'; p++) if (!isprint(*p)) *p=' '; if (*fileinfo == '\0') (void) fprintf(stdout,"%s\n",filename); else (void) fprintf(stdout,"%s %s\n",fileinfo,filename); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % E r r o r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function Error displays an error message and then terminates the program. % % The format of the Error routine is: % % Error(message,qualifier) % % A description of each parameter follows: % % o message: Specifies the message to display before terminating the % program. % % o qualifier: Specifies any qualifier to the message. % % */ static void Error(char *message,char *qualifier) { (void) fprintf(stderr,"%s: %s",client_name,message); if (qualifier != (char *) NULL) (void) fprintf(stderr," (%s)",qualifier); (void) fprintf(stderr,".\n"); if (child > 0) if (master != -1) { (void) fcntl(master,F_SETFL,fcntl(master,F_GETFL) | O_NONBLOCK); write(master,"quit\n",5); while (Wait()); } exit(1); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % E x p a n d F i l e n a m e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ExpandFilename expands '~' in a filename. % % The format of the ExpandFilename function is: % % ExpandFilename(filename) % % A description of each parameter follows: % % o filename: Specifies a pointer to an character array that contains the % filename. % % */ void ExpandFilename(char *filename) { #if !defined(vms) && !defined(macintosh) && !defined(WIN32) char expanded_filename[MaxTextLength]; register char *p; if (filename == (char *) NULL) return; if (*filename != '~') return; if (*(filename+1) == '/') { /* Substitute ~ with $HOME. */ p=(char *) getenv("HOME"); if (p == (char *) NULL) p="."; (void) strcpy(expanded_filename,p); (void) strcat(expanded_filename,filename+1); } else { char username[MaxTextLength]; struct passwd *entry; /* Substitute ~ with home directory from password file. */ (void) strcpy(username,filename+1); p=strchr(username,'/'); if (p != (char *) NULL) *p='\0'; entry=getpwnam(username); if (entry == (struct passwd *) NULL) return; (void) strcpy(expanded_filename,entry->pw_dir); if (p != (char *) NULL) { (void) strcat(expanded_filename,"/"); (void) strcat(expanded_filename,p+1); } } (void) strcpy(filename,expanded_filename); #endif } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % E x e c u t e F t p % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ExecuteFtp executes the FTP program as a child process. % % The format of the ExecuteFtp routine is: % % ExecuteFtp(hostname,port) % % A description of each parameter follows: % % o hostname: Specifies a pointer to a character array that contains the % name of the host to establish a connection to a FTP server. % % o port: Specifies a port number. If the port number is NULL, xtp % attempts to contact a FTP server at the default port. % % % */ static void ExecuteFtp(char *hostname,char *port) { int slave; struct sigaction action; struct termios attributes; /* Get slave tty line. */ action.sa_handler=SIG_IGN; (void) sigemptyset(&action.sa_mask); action.sa_flags=0; (void) sigaction(SIGTSTP,&action,(struct sigaction *) NULL); slave=open(slave_tty,O_RDWR | O_NOCTTY); if (slave < 0) Error("Unable to open slave pseudo-terminal",slave_tty); /* Condition slave tty line. */ #ifdef SVR4 (void) ioctl(slave,I_PUSH,"ptem"); (void) ioctl(slave,I_PUSH,"ldterm"); (void) ioctl(slave,I_PUSH,"ttcompat"); #endif (void) tcgetattr(slave,&attributes); attributes.c_iflag&=(~(BRKINT | IGNPAR | PARMRK | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)); attributes.c_iflag|=IGNBRK | IXOFF; attributes.c_oflag&=(~OPOST); attributes.c_lflag&= (~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | ISIG | NOFLSH | TOSTOP)); attributes.c_cflag&=(~(CSIZE | CSTOPB | HUPCL | PARENB)); attributes.c_cflag|=CLOCAL | CREAD | CS8; (void) tcflush(slave,TCIFLUSH); (void) tcsetattr(slave,TCSANOW,&attributes); /* Execute FTP program as a child process. */ (void) close(master); (void) dup2(slave,STDIN_FILENO); (void) dup2(slave,STDOUT_FILENO); (void) dup2(slave,STDERR_FILENO); (void) close(slave); (void) execlp("ftp","ftp","-n","-i","-g","-v",hostname,port,(char *) 0); perror("ftp"); (void) kill(0,SIGTERM); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t L o g i n I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function GetLoginInfo searches the .netrc file for a machine token that % matches the remote machine specified with parameter hostname. Once a % match is made, the subsequent .netrc tokens are processed, stopping when % the EOF is reached or another machine token is encountered. % % The format of the GetLoginInfo routine is: % % GetLoginInfo(hostname,user,ident,account) % % */ static void GetLoginInfo(const char *hostname,char *user,char *ident, char *account) { char filename[MaxTextLength], keyword[MaxTextLength], value[MaxTextLength]; FILE *file; int match; register int c, i; /* Open netrc file. */ (void) strcpy(filename,"~/.netrc"); ExpandFilename(filename); file=fopen(filename,"r"); if (file == (FILE *) NULL) return; /* Search netrc file for a machine name match. */ match=False; c=fgetc(file); if (c == EOF) return; while (isgraph(c) && (c != EOF)) { register char *p; if (!isalnum(c)) c=fgetc(file); else { /* Determine a keyword and its value. */ p=keyword; do { if ((p-keyword) < (MaxTextLength-1)) *p++=(char) c; c=fgetc(file); } while (isalnum(c)); *p='\0'; while (isspace(c) || (c == '=')) c=fgetc(file); p=value; if (c != '"') while (!isspace(c) && (c != EOF)) { if ((p-value) < (MaxTextLength-1)) *p++=(char) c; c=fgetc(file); } else { c=fgetc(file); while ((c != '"') && (c != EOF)) { if ((p-value) < (MaxTextLength-1)) *p++=(char) c; c=fgetc(file); } } *p='\0'; /* Assign a value to the specified keyword. */ if (strcmp(keyword,"machine") == 0) { if (match) break; match=strcmp(hostname,value) == 0; *user='\0'; *ident='\0'; *account='\0'; } if (match) { if (strcmp(keyword,"login") == 0) (void) strcpy(user,value); if (strcmp(keyword,"password") == 0) (void) strcpy(ident,value); if (strcmp(keyword,"account") == 0) (void) strcpy(account,value); } } while (isspace(c)) c=fgetc(file); } (void) fclose(file); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t P s e u d o T e r m i n a l % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function GetPseudoTerminal returns a master/slave pair of pseudo-terminals. % % The format of the GetPseudoTerminal routine is: % % GetPseudoTerminal() % % */ static void GetPseudoTerminal(void) { register char *cp; struct termios attributes; #ifdef SVR4 *slave_tty='\0'; master=open("/dev/ptmx",O_RDWR); if (master > 0) if ((grantpt(master) != -1) && (unlockpt(master) != -1)) { cp=(char *) ptsname(master); if (cp != (char *) NULL) (void) strcpy(slave_tty,cp); } if (*slave_tty == '\0') { close(master); master=(-1); } #else char master_tty[16]; register char *bank; struct stat info; master=(-1); for (bank="pqrs"; *bank; bank++) { (void) sprintf(master_tty,"/dev/pty%c0",*bank); (void) sprintf(slave_tty,"/dev/tty%c0",*bank); if (stat(master_tty,&info) < 0) break; for (cp="0123456789abcdef"; *cp; cp++) { (void) sprintf((char *) master_tty,"/dev/pty%c%c",*bank,*cp); master=open(master_tty,O_RDWR); if (master < 0) continue; /* Verify slave side is usable. */ (void) sprintf(slave_tty,"/dev/tty%c%c",*bank,*cp); if (access(slave_tty,R_OK | W_OK) == 0) break; (void) close(master); } if (access(slave_tty,R_OK | W_OK) == 0) break; } #endif if (master < 0) Error("All network ports in use",(char *) NULL); /* Condition master tty line. */ (void) tcgetattr(master,&attributes); attributes.c_lflag&=(~(ICANON | ECHO)); (void) tcflush(master,TCIFLUSH); (void) tcsetattr(master,TCSANOW,&attributes); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M a k e D i r e c t o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function MakeDirectory checks each component of a directory path and if it % does not exist, creates it. % % The format of the MakeDirectory routine is: % % MakeDirectory(directory) % % A description of each parameter follows: % % o directory: Specifies a pointer to a character array that contains % the name of the directory to create. % % */ static int MakeDirectory(char *directory) { register char *p; struct stat info; /* Determine first component of the directory. */ p=strrchr(directory,'/'); if ((p == (char *) NULL) || (p == directory)) return(False); *p='\0'; if (lstat(directory,&info) < 0) { /* Path component does not exist; create it. */ if (MakeDirectory(directory) == 0) if (mkdir(directory,(mode_t) 0777) >= 0) { *p='/'; return(False); } } else if (S_ISDIR(info.st_mode)) { /* Path component already exists. */ *p='/'; return(False); } /* Path component is a file not a directory. */ *p='/'; return(True); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % P r i n t R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function PrintRequest prints a file on the remote FTP server. % % The format of the PrintRequest routine is: % % PrintRequest(filename,verbose) % % A description of each parameter follows: % % o filename: Specifies a pointer to a character array that contains % the name of the file to print. % % o verbose: An unsigned integer. A value other than zero dhows all % responses from the remote server. % % */ static void PrintRequest(char *filename,unsigned int verbose) { char command[MaxTextLength], *response; /* get remote-file [ - | < |zcat > ]. */ (void) sprintf(command,"get %s",filename); if (strcmp(filename+strlen(filename)-2,".Z") == 0) (void) strcat(command," |zcat\n"); else if (strcmp(filename+strlen(filename)-3,".gz") == 0) (void) strcat(command," |gunzip -c\n"); else (void) strcat(command," -\n"); (void) write(master,command,strlen(command)); (void) fprintf(stdout,"%s:\n",filename); while (response=Wait()) if (status == 0) (void) fprintf(stdout,"%s\n",response); else if ((status == 5) || verbose) (void) fprintf(stderr,"%s\n",response); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % P r o c e s s R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function ProcessRequest first gets a recursive listing of the remote % directory. Next each filename in the list is either accepted or rejected % based on a user specified regular expresssion. If any files match the % regular expression, its filename is listed, printed, or retrieved as % specified by the command line arguments. % % The format of the ProcessRequest routine is: % % ProcessRequest(system_type,prune,verbose) % % A description of each parameter follows: % % o system_type: Specifies what type of system the remote host is: % UNIX, VMS, or other. % % o prune: Specifies whether to recusively search for files. % % o verbose: An unsigned integer. A value other than zero dhows all % responses from the remote server. % % */ static void ProcessRequest(char *system_type,unsigned int prune, unsigned int verbose) { #define AccessExpression "[Pp]ermission denied|not found|cannot access" #define DateExpression " (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " #define ListExpression "ls-l([Rt])+([Rt])*([^ ])*" char command[MaxTextLength], directory[MaxTextLength], **filelist, filename[MaxTextLength << 1], *info, *response; register char *p; register int i; RegularExpression *access_expression, *date_expression; unsigned int match, maximum_files, number_files; if (*system_type == '\0') { /* Determine system type. */ (void) strcpy(system_type,"UNIX"); (void) strcpy(command,"quote syst\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if (status == 2) (void) strcpy(system_type,response+4); } /* Ask remote server for a file listing. */ (void) strcpy(command,"dir\n"); if (!prune) if (strncmp("VMS",system_type,3) == 0) (void) strcpy(command,"ls [...]\n"); else if ((strncmp("UNIX",system_type,4) == 0) || (strncmp("Windows_NT",system_type,10) == 0)) { RegularExpression *list_expression; /* Get a recursive file listing. */ (void) write(master,command,strlen(command)); (void) strcpy(command,"ls -ltR\n"); list_expression=CompileRegularExpression(ListExpression); while (response=Wait()) if ((status == 0) && (*response != '\0')) if (ExecuteRegularExpression(list_expression,response)) { /* Remote site has a directory listing file. */ (void) strncpy(filename,list_expression->subpattern[0], list_expression->pattern_length); (void) fprintf(stderr,"Using remote file listing %s...\n", filename); (void) sprintf(command,"get %s",filename); if (strcmp(filename+strlen(filename)-2,".Z") == 0) (void) strcat(command," |zcat\n"); else if (strcmp(filename+strlen(filename)-3,".gz") == 0) (void) strcat(command," |gunzip -c\n"); else (void) strcat(command," -\n"); while (Wait()); break; } free((char *) list_expression); } (void) write(master,command,strlen(command)); while (response=Wait()) if ((status == 0) || (status == 5)) break; if (status == 5) { /* Directory command has limited functionality. */ while (Wait()); (void) strcpy(command,"dir\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if ((status == 0) || (status == 5)) break; } status=(-1); if (response == (char *) NULL) return; /* Search the recursive directory listing and act on expression matches. */ access_expression=CompileRegularExpression(AccessExpression); date_expression=CompileRegularExpression(DateExpression); *directory='\0'; if (print_expression || retrieve_expression) { maximum_files=2048; filelist=(char **) malloc(maximum_files*sizeof(char *)); if (filelist == (char **) NULL) Error("Unable to allocate memory",(char *) NULL); number_files=0; } do { if ((status > 0) || (*response == '\0')) continue; /* Construct file name and info. */ while (*response == ' ') response++; info=response; p=response+strlen(response)-1; if (*p == '\r') p--; if ((strncmp("UNIX",system_type,4) != 0) && (strncmp("Windows_NT",system_type,10) != 0)) { if (strncmp("VMS",system_type,3) == 0) if (*p == ']') { /* File is a directory. */ do { p--; } while (*p == ' '); *(++p)='\0'; (void) strcpy(directory,response); (void) strcat(directory,"]"); continue; } while ((*info != ' ') && *info) info++; *info='\0'; p=response; } else { if ((*response != '-') && (*response != 'F')) { if (*p == ':') { /* File is a directory. */ do { p--; } while (*p == ' '); *(++p)='\0'; (void) strcpy(directory,response); (void) strcat(directory,"/"); } continue; } if (ExecuteRegularExpression(access_expression,response)) continue; if (ExecuteRegularExpression(date_expression,response)) p=date_expression->subpattern[0]+13; *p++='\0'; while (*p == ' ') p++; } (void) strcpy(filename,directory); (void) strcat(filename,p); if (exclude_expression) if (ExecuteRegularExpression(exclude_expression,filename)) continue; if (directory_expression) if (ExecuteRegularExpression(directory_expression,filename)) DirectoryRequest(info,filename); match=False; if (print_expression) match|=ExecuteRegularExpression(print_expression,filename); if (retrieve_expression) match|=ExecuteRegularExpression(retrieve_expression,filename); if (!match) continue; if (number_files >= maximum_files) { maximum_files<<=1; filelist=(char **) realloc(filelist,maximum_files*sizeof(char *)); if (filelist == (char **) NULL) Error("Unable to allocate memory",(char *) NULL); } filelist[number_files]=(char *) malloc((strlen(filename)+1)*sizeof(char)); if (filelist[number_files] == (char *) NULL) Error("Unable to allocate memory",(char *) NULL); (void) strcpy(filelist[number_files],filename); number_files++; } while (response=Wait()); free((char *) date_expression); free((char *) access_expression); if (!print_expression && !retrieve_expression) return; if (number_files == 0) { Warning("no files matched your expression",(char *) NULL); return; } /* Print, or retrieve a file. */ for (i=0; i < number_files; i++) { if (print_expression) if (ExecuteRegularExpression(print_expression,filelist[i])) PrintRequest(filelist[i],verbose); if (retrieve_expression) if (ExecuteRegularExpression(retrieve_expression,filelist[i])) RetrieveRequest(filelist[i],verbose); free((char *) filelist[i]); } free((char *) filelist); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e t r i e v e R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function RetrieveRequest retrieves a file from the remote FTP server. % % The format of the RetrieveRequest routine is: % % RetrieveRequest(filename,verbose) % % A description of each parameter follows: % % o filename: Specifies a pointer to a character array that contains % the name of the file to retrieve. % % o verbose: An unsigned integer. A value other than zero dhows all % responses from the remote server. % % */ static void RetrieveRequest(char *filename,unsigned int verbose) { char command[MaxTextLength], *response; /* get remote-file */ (void) MakeDirectory(filename); (void) sprintf(command,"get %s\n",filename); (void) write(master,command,strlen(command)); while (response=Wait()) if (verbose) (void) fprintf(stderr,"%s\n",response); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S i g n a l A l a r m % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SignalAlarm is called if the timer expires. % % The format of the SignalAlarm routine is: % % SignalAlarm(status) % % */ static void SignalAlarm(int status) { char message[MaxTextLength]; int process_status; while (waitpid((pid_t) NULL,&process_status,WNOHANG) > 0); (void) sprintf(message,"timeout expired, status %x",process_status); Error(message,(char *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S i g n a l C h i l d % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function SignalChild is called if the status of the child process changes. % % The format of the SignalChild routine is: % % SignalChild(status) % % */ static void SignalChild(int status) { char message[MaxTextLength]; int process_status; while (waitpid((pid_t) NULL,&process_status,WNOHANG) > 0); (void) sprintf(message,"child died, status %x",process_status); Error(message,(char *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % U s a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Procedure Usage displays the program usage; % % The format of the Usage routine is: % % Usage() % % */ static void Usage(void) { char **p; static char *options[]= { " -account password supplemental password", " -binary retrieve files as binary", " -exclude expression exclude files that match the expression", " -directory list file names that match the expression", " -file name store the file with this name", " -get get files that match the expression", " -port number port number of FTP server", " -print print files that match the expression", " -prune do not recursively search for files", " -proxy hostname access remote host via this proxy host", " -put put files that match the expression", " -retrieve retrieve files that match the expression", " -timeout seconds specifies maximum seconds of XTP session", " -verbose show all responses from the remote server", "", " has the format:", "", " protocol://host/[directory/[filename]]", "", "where protocol is `ftp' and host is `[user[:password]]@hostname'.", "User defaults to `anonymous' and password defaults to `host.domain'.", "Note that `directory/[filename]' is interpreted relative to the home", "directory for `user', thus an absolute pathname must be specified", "with the leading '/':", "", " ftp://host//tmp/anyfile", "", "As an extension, the filename part of the locator is expanded", "by the shell for options -get or -put, otherwise it is processed", "as a regular expression. For convenience, the protocol component", "of the uniform resource locator (ftp://) may be omitted.", NULL }; (void) fprintf(stderr, "Usage: %s [-options ...] \n",client_name); (void) fprintf(stderr,"\nWhere options include:\n"); for (p=options; *p; p++) (void) fprintf(stderr,"%s\n",*p); exit(1); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % W a i t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % Function Wait reads a line of output from the remote FTP server. % % The format of the Wait() routine is: % % response=Wait() % % A description of each parameter follows: % % o response: Function Wait returns this pointer to the output obtained % from the remote FTP server. % % */ static char *Wait(void) { register char *p; static char buffer[1024], *q; static char line[1024]; static int count=0; status=0; p=line; do { if (count <= 0) { /* The buffer is empty; read output from the remote FTP server. */ count=read(master,buffer,sizeof(buffer)); q=buffer; if (count <= 0) { if (p == line) return((char *) NULL); break; } } count--; *p=(*q++); if (*p == '\n') break; p++; if ((p-line) >= 5) if (!strncmp(p-5,"ftp> ",5)) if (count == 0) return((char *) NULL); } while (p < (line+sizeof(line))); *p='\0'; if (isdigit(*line)) status=atoi(line)/100; return(line); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % m a i n % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ int main(int argc,char **argv) { #define AccountExpression "[Aa]ccount" #define ConnectExpression "[Nn]ot connected" #define IdentExpression "[Pp]assword" #define TypeExpression "system type is " char account[MaxTextLength], command[MaxTextLength], directory[MaxTextLength], filename[MaxTextLength], *get_expression, *host_info, hostname[MaxTextLength], ident[MaxTextLength], *localname, *port, protocol[MaxTextLength], *proxy, *put_expression, *remotename, system_type[MaxTextLength], *url, user[MaxTextLength]; int binary, field, process_status; register char *p, *q, *response; RegularExpression *account_expression, *connect_expression, *ident_expression, *type_expression; struct sigaction action; unsigned int count, prune, timeout, verbose; client_name=argv[0]; if (argc < 2) Usage(); /* Initialize program variables. */ *account='\0'; binary=True; child=(-1); *directory='\0'; directory_expression=(RegularExpression *) NULL; exclude_expression=(RegularExpression *) NULL; *filename='\0'; *ident='\0'; get_expression=(char *) NULL; *hostname='\0'; localname=(char *) NULL; master=(-1); port=(char *) NULL; print_expression=(RegularExpression *) NULL; *protocol='\0'; proxy=(char *) getenv("xtp_proxy"); prune=False; put_expression=(char *) NULL; remotename=(char *) NULL; retrieve_expression=(RegularExpression *) NULL; *system_type='\0'; timeout=0; *user='\0'; verbose=False; /* Parse uniform resource locator. */ field=0; url=argv[argc-1]; p=url; for (q=url; *q != '\0'; q++) { if (*q != '/') continue; switch (field) { case 0: { if ((q > url) && (*(q-1) == ':')) (void) strncat(protocol,p,q-p); else { (void) strncat(hostname,p,q-p); field+=2; } break; } case 1: break; case 2: { (void) strncat(hostname,p,q-p); break; } default: { (void) strncat(directory,p,q-p+1); break; } } field++; p=q+1; } if (*directory != '\0') directory[strlen(directory)-1]='\0'; (void) strncat(filename,p,q-p); if (*hostname == '\0') (void) gethostname(hostname,64); GetLoginInfo(hostname,user,ident,account); p=strrchr(hostname,'@'); if (p != (char *) NULL) { /* Parse login-name:password@site. */ GetLoginInfo(p+1,user,ident,account); strncpy(user,hostname,p-hostname); user[p-hostname]='\0'; strcpy(hostname,p+1); p=strchr(user,':'); if (p != (char *) NULL) { strcpy(ident,p+1); user[p-user]='\0'; } } /* Parse command line arguments. */ for (*argv++; *argv && ((**argv == '-') || (**argv == '+')); argv++) switch (argv[0][1]) { case 'a': { (void) strcpy(account,*++argv); break; } case 'b': { binary=(**argv == '-'); break; } case 'd': { directory_expression=CompileRegularExpression(filename); if (!directory_expression) exit(1); break; } case 'e': { exclude_expression=CompileRegularExpression(*++argv); if (!exclude_expression) exit(1); break; } case 'f': { localname=(*++argv); remotename=(*++argv); break; } case '?': case 'h': { Usage(); break; } case 'g': { get_expression=filename; break; } case 'p': { if (strncmp("port",*argv+1,2) == 0) { port=(*++argv); break; } if (strncmp("proxy",*argv+1,3) == 0) { proxy=(char *) NULL; if (**argv == '-') { argv++; if (*argv == (char *) NULL) Error("Missing host on -proxy",(char *) NULL); proxy=(*argv); } break; } if (strncmp("prune",*argv+1,3) == 0) { prune=(**argv == '-'); break; } if (strncmp("print",*argv+1,3) == 0) { print_expression=CompileRegularExpression(filename); if (!print_expression) exit(1); break; } if (strncmp("put",*argv+1,2) == 0) { put_expression=filename; break; } Error("Unrecognized option",*argv); break; } case 'r': { retrieve_expression=CompileRegularExpression(filename); if (!retrieve_expression) exit(1); break; } case 't': { timeout=atoi(*++argv); break; } case 'v': { verbose=True; break; } default: { Error("Unrecognized option",(char *) NULL); break; } } if ((directory_expression == (RegularExpression *) NULL) && (print_expression == (RegularExpression *) NULL) && (retrieve_expression == (RegularExpression *) NULL) && (put_expression == (char *) NULL)) get_expression=filename; if (*protocol != '\0') if (strcmp(protocol,"ftp:") != 0) if ((get_expression == (char *) NULL) || (strlen(get_expression) == 0)) Error("Unsupported protocol",protocol); else { /* Use GET to handle non-ftp protocol but only for *get*. */ (void) sprintf(command,"GET %s > %s",url,filename); status=system(command); return(status < 0); } if ((*ident == '\0') && (*user == '\0')) { struct passwd *user_info; /* Identify user as user@host.domain. */ (void) strcpy(user,"anonymous"); (void) strcpy(ident,"anonymous"); user_info=getpwuid(geteuid()); if (user_info != (struct passwd *) NULL) (void) strcpy(ident,user_info->pw_name); if (proxy != (char *) NULL) { (void) strcat(ident,"@"); (void) strcat(ident,proxy); } else { p=ident+strlen(ident); *p++='@'; (void) gethostname(p,64); p=ident+strlen(ident); *p++='.'; (void) getdomainname(p,64); } } if (proxy != (char *) NULL) { /* Access the remote host via a proxy ftpd client. */ (void) strcat(user,"@"); (void) strcat(user,hostname); (void) strcpy(hostname,proxy); } host_info=GetHostInfo(hostname); if (host_info == (char *) NULL) Error("Unknown host",hostname); if (verbose) if (*directory == '\0') (void) fprintf(stderr,"%s\n",host_info); else (void) fprintf(stderr,"%s %s\n",host_info,directory); GetPseudoTerminal(); /* Set signal handlers. */ action.sa_handler=SignalAlarm; (void) sigemptyset(&action.sa_mask); action.sa_flags=0; (void) sigaction(SIGALRM,&action,(struct sigaction *) NULL); if (timeout != 0) (void) alarm(Max(timeout >> 4,120)); /* enable login timer. */ action.sa_handler=SignalChild; (void) sigaction(SIGCHLD,&action,(struct sigaction *) NULL); /* Connect and logon to host. */ child=fork(); if (child < 0) Error("Unable to fork",(char *) NULL); if (child == 0) ExecuteFtp(hostname,port); type_expression=CompileRegularExpression(TypeExpression); while (response=Wait()) { if (verbose) (void) fprintf(stderr,"%s\n",response); if (ExecuteRegularExpression(type_expression,response)) (void) strcpy(system_type,type_expression->subpattern_end[0]); } free((char *) type_expression); if (*user == '\0') (void) strcpy(user,"anonymous"); (void) sprintf(command,"user %s\n",user); if (*ident != '\0') (void) sprintf(command,"user %s %s\n",user,ident); (void) write(master,command,strlen(command)); /* Logon dialog may require a password or account information. */ account_expression=CompileRegularExpression(AccountExpression); connect_expression=CompileRegularExpression(ConnectExpression); ident_expression=CompileRegularExpression(IdentExpression); while (response=Wait()) { if (verbose) (void) fprintf(stderr,"%s\n",response); if (status == 3) if (*ident == '\0') if (ExecuteRegularExpression(ident_expression,response)) { if (!verbose) (void) fprintf(stderr,"%s\n",response); count=read(master,command,sizeof(command)); command[count]='\0'; (void) fprintf(stderr,"%s",command); } if (status == 3) if (*account == '\0') if (ExecuteRegularExpression(account_expression,response)) { if (!verbose) (void) fprintf(stderr,"%s\n",response); count=read(master,command,sizeof(command)); command[count]='\0'; (void) fprintf(stderr,"%s",command); } if (status == 5) Error(response,user); if (ExecuteRegularExpression(connect_expression,response)) Error("Unable to connect to remote host",hostname); } free((char *) ident_expression); free((char *) connect_expression); free((char *) account_expression); if (timeout != 0) (void) alarm(timeout); /* enable session timer. */ if (*directory != '\0') { /* Change remote working directory. */ (void) sprintf(command,"cd %s\n",directory); (void) write(master,command,strlen(command)); while (response=Wait()) { if (verbose) (void) fprintf(stderr,"%s\n",response); if (status == 5) Error("No such directory",directory); } (void) strcpy(command,"pwd\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if (verbose) (void) fprintf(stderr,"%s\n",response); } if (binary) { /* Set file transfer type. */ (void) strcpy(command,"binary\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if (verbose) (void) fprintf(stderr,"%s\n",response); (void) strcpy(command,"type\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if (verbose) (void) fprintf(stderr,"%s\n",response); } if (get_expression != (char *) NULL) { /* Process get request. */ (void) sprintf(command,"mget %s\n",get_expression); if (!IsGlob(get_expression) && (localname != (char *) NULL)) (void) sprintf(command,"get %s %s\n",get_expression,localname); (void) write(master,command,strlen(command)); while (response=Wait()) if ((status == 5) || verbose) (void) fprintf(stderr,"%s\n",response); } else if (put_expression != (char *) NULL) { /* Process put request. */ (void) strcpy(command,"glob on\n"); (void) write(master,command,strlen(command)); while (response=Wait()) if (verbose) (void) fprintf(stderr,"%s\n",response); (void) sprintf(command,"mput %s\n",put_expression); if (!IsGlob(put_expression) && (remotename != (char *) NULL)) (void) sprintf(command,"put %s %s\n",put_expression,remotename); (void) write(master,command,strlen(command)); while (response=Wait()) if ((status == 5) || verbose) (void) fprintf(stderr,"%s\n",response); } else ProcessRequest(system_type,prune,verbose); (void) strcpy(command,"quit\n"); (void) write(master,command,strlen(command)); /* Wait for child to finish. */ action.sa_handler=SIG_DFL; (void) sigemptyset(&action.sa_mask); action.sa_flags=0; (void) sigaction(SIGCHLD,&action,(struct sigaction *) NULL); (void) fcntl(master,F_SETFL,fcntl(master,F_GETFL) | O_NONBLOCK); while (Wait()); (void) waitpid(child,&process_status,WNOHANG); (void) close(master); if (directory_expression != (RegularExpression *) NULL) free((char *) directory_expression); if (exclude_expression != (RegularExpression *) NULL) free((char *) exclude_expression); if (print_expression != (RegularExpression *) NULL) free((char *) print_expression); if (retrieve_expression != (RegularExpression *) NULL) free((char *) retrieve_expression); return(status < 0); }