| /* C K U F I O -- Kermit file system support for UNIX, Aegis, and Plan 9 */ |
| |
| #define CK_NONBLOCK /* See zoutdump() */ |
| |
| #ifdef aegis |
| char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004"; |
| #else |
| #ifdef Plan9 |
| char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004"; |
| #else |
| char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004"; |
| #endif /* Plan9 */ |
| #endif /* aegis */ |
| /* |
| Author: Frank da Cruz <fdc@columbia.edu>, |
| Columbia University Academic Information Systems, New York City, |
| and others noted in the comments below. Note: CUCCA = Previous name of |
| Columbia University Academic Information Systems. |
| |
| Copyright (C) 1985, 2004, |
| Trustees of Columbia University in the City of New York. |
| All rights reserved. See the C-Kermit COPYING.TXT file or the |
| copyright text in the ckcmai.c module for disclaimer and permissions. |
| */ |
| |
| /* |
| NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be |
| compatible with C preprocessors that support only #ifdef, #else, #endif, |
| #define, and #undef. Please do not use #if, logical operators, or other |
| preprocessor features in any of the portable C-Kermit modules. You can, |
| of course, use these constructions in platform-specific modules where you |
| know they are supported. |
| */ |
| /* Include Files */ |
| |
| #ifdef MINIX2 |
| #define _MINIX |
| #endif /* MINIX2 */ |
| |
| #include "ckcsym.h" |
| #include "ckcdeb.h" |
| #include "ckcasc.h" |
| |
| #ifndef NOCSETS |
| #include "ckcxla.h" |
| #endif /* NOCSETS */ |
| |
| #ifdef COMMENT |
| /* This causes trouble in C-Kermit 8.0. I don't remember the original */ |
| /* reason for this being here but it must have been needed at the time... */ |
| #ifdef OSF13 |
| #ifdef CK_ANSIC |
| #ifdef _NO_PROTO |
| #undef _NO_PROTO |
| #endif /* _NO_PROTO */ |
| #endif /* CK_ANSIC */ |
| #endif /* OSF13 */ |
| #endif /* COMMENT */ |
| |
| #include <errno.h> |
| #include <signal.h> |
| |
| #ifdef MINIX2 |
| #undef MINIX |
| #undef CKSYSLOG |
| #include <limits.h> |
| #include <time.h> |
| #define NOFILEH |
| #endif /* MINIX2 */ |
| |
| #ifdef MINIX |
| #include <limits.h> |
| #include <sys/types.h> |
| #include <time.h> |
| #else |
| #ifdef POSIX |
| #include <limits.h> |
| #else |
| #ifdef SVR3 |
| #include <limits.h> |
| #endif /* SVR3 */ |
| #endif /* POSIX */ |
| #endif /* MINIX */ |
| /* |
| Directory Separator macros, to allow this module to work with both UNIX and |
| OS/2: Because of ambiguity with the command line editor escape \ character, |
| the directory separator is currently left as / for OS/2 too, because the |
| OS/2 kernel also accepts / as directory separator. But this is subject to |
| change in future versions to conform to the normal OS/2 style. |
| */ |
| #ifndef DIRSEP |
| #define DIRSEP '/' |
| #endif /* DIRSEP */ |
| #ifndef ISDIRSEP |
| #define ISDIRSEP(c) ((c)=='/') |
| #endif /* ISDIRSEP */ |
| |
| #ifdef SDIRENT |
| #define DIRENT |
| #endif /* SDIRENT */ |
| |
| #ifdef XNDIR |
| #include <sys/ndir.h> |
| #else /* !XNDIR */ |
| #ifdef NDIR |
| #include <ndir.h> |
| #else /* !NDIR, !XNDIR */ |
| #ifdef RTU |
| #include "/usr/lib/ndir.h" |
| #else /* !RTU, !NDIR, !XNDIR */ |
| #ifdef DIRENT |
| #ifdef SDIRENT |
| #include <sys/dirent.h> |
| #else |
| #include <dirent.h> |
| #endif /* SDIRENT */ |
| #else |
| #include <sys/dir.h> |
| #endif /* DIRENT */ |
| #endif /* RTU */ |
| #endif /* NDIR */ |
| #endif /* XNDIR */ |
| |
| #ifdef UNIX /* Pointer arg to wait() allowed */ |
| #define CK_CHILD /* Assume this is safe in all UNIX */ |
| #endif /* UNIX */ |
| |
| extern int binary, recursive, stathack; |
| #ifdef CK_CTRLZ |
| extern int eofmethod; |
| #endif /* CK_CTRLZ */ |
| |
| #include <pwd.h> /* Password file for shell name */ |
| #ifdef CK_SRP |
| #include <t_pwd.h> /* SRP Password file */ |
| #endif /* CK_SRP */ |
| |
| #ifdef HPUX10_TRUSTED |
| #include <hpsecurity.h> |
| #include <prot.h> |
| #endif /* HPUX10_TRUSTED */ |
| |
| #ifdef COMMENT |
| /* Moved to ckcdeb.h */ |
| #ifdef POSIX |
| #define UTIMEH |
| #else |
| #ifdef HPUX9 |
| #define UTIMEH |
| #endif /* HPUX9 */ |
| #endif /* POSIX */ |
| #endif /* COMMENT */ |
| |
| #ifdef SYSUTIMEH /* <sys/utime.h> if requested, */ |
| #include <sys/utime.h> /* for extra fields required by */ |
| #else /* 88Open spec. */ |
| #ifdef UTIMEH /* or <utime.h> if requested */ |
| #include <utime.h> /* (SVR4, POSIX) */ |
| #ifndef BSD44 |
| #ifndef V7 |
| /* Not sure why this is here. What it implies is that the code bracketed |
| by SYSUTIMEH is valid on all platforms on which we support time |
| functionality. But we know that is not true because the BSD44 and V7 |
| platforms do not support sys/utime.h and the data structures which |
| are defined in them. Now this worked before because prior to today's |
| changes the UTIMEH definition for BSD44 and V7 did not take place |
| until after SYSUTIMEH was defined. It also would not have been a |
| problem if the ordering of all the time blocks was consistent. All but |
| one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case |
| is where this problem was triggered. |
| */ |
| #define SYSUTIMEH /* Use this for both cases. */ |
| #endif /* V7 */ |
| #endif /* BSD44 */ |
| #endif /* UTIMEH */ |
| #endif /* SYSUTIMEH */ |
| |
| #ifndef NOTIMESTAMP |
| #ifdef POSIX |
| #ifndef AS400 |
| #define TIMESTAMP |
| #endif /* AS400 */ |
| #endif /* POSIX */ |
| |
| #ifdef BSD44 /* BSD 4.4 */ |
| #ifndef TIMESTAMP |
| #define TIMESTAMP /* Can do file dates */ |
| #endif /* TIMESTAMP */ |
| #include <sys/time.h> |
| #include <sys/timeb.h> |
| |
| #else /* Not BSD44 */ |
| |
| #ifdef BSD4 /* BSD 4.3 and below */ |
| #define TIMESTAMP /* Can do file dates */ |
| #include <time.h> /* Need this */ |
| #include <sys/timeb.h> /* Need this if really BSD */ |
| |
| #else /* Not BSD 4.3 and below */ |
| |
| #ifdef SVORPOSIX /* System V or POSIX */ |
| #ifndef TIMESTAMP |
| #define TIMESTAMP |
| #endif /* TIMESTAMP */ |
| #include <time.h> |
| |
| /* void tzset(); (the "void" type upsets some compilers) */ |
| #ifndef IRIX60 |
| #ifndef ultrix |
| #ifndef CONVEX9 |
| /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */ |
| #ifndef Plan9 |
| extern long timezone; |
| #endif /* Plan9 */ |
| #endif /* CONVEX9 */ |
| #endif /* ultrix */ |
| #endif /* IRIX60 */ |
| #endif /* SVORPOSIX */ |
| #endif /* BSD4 */ |
| #endif /* BSD44 */ |
| |
| #ifdef COHERENT |
| #include <time.h> |
| #endif /* COHERENT */ |
| |
| /* Is `y' a leap year? */ |
| #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) |
| |
| /* Number of leap years from 1970 to `y' (not including `y' itself). */ |
| #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400) |
| |
| #endif /* NOTIMESTAMP */ |
| |
| #ifdef CIE |
| #include <stat.h> /* File status */ |
| #else |
| #include <sys/stat.h> |
| #endif /* CIE */ |
| |
| /* Macro to alleviate isdir() calls internal to this module */ |
| |
| static struct stat STATBUF; |
| #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0)) |
| |
| extern char uidbuf[]; |
| extern int xferlog; |
| extern char * xferfile; |
| int iklogopen = 0; |
| static time_t timenow; |
| |
| #define IKSDMSGLEN CKMAXPATH+512 |
| |
| static char iksdmsg[IKSDMSGLEN]; |
| |
| extern int local; |
| |
| extern int server, en_mkd, en_cwd, en_del; |
| |
| /* |
| Functions (n is one of the predefined file numbers from ckcker.h): |
| |
| zopeni(n,name) -- Opens an existing file for input. |
| zopeno(n,name,attr,fcb) -- Opens a new file for output. |
| zclose(n) -- Closes a file. |
| zchin(n,&c) -- Gets the next character from an input file. |
| zsinl(n,&s,x) -- Read a line from file n, max len x, into address s. |
| zsout(n,s) -- Write a null-terminated string to output file, buffered. |
| zsoutl(n,s) -- Like zsout, but appends a line terminator. |
| zsoutx(n,s,x) -- Write x characters to output file, unbuffered. |
| zchout(n,c) -- Add a character to an output file, unbuffered. |
| zchki(name) -- Check if named file exists and is readable, return size. |
| zchko(name) -- Check if named file can be created. |
| zchkspa(name,n) -- Check if n bytes available to create new file, name. |
| znewn(name,s) -- Make a new unique file name based on the given name. |
| zdelet(name) -- Delete the named file. |
| zxpand(string) -- Expands the given wildcard string into a list of files. |
| znext(string) -- Returns the next file from the list in "string". |
| zxrewind() -- Rewind zxpand list. |
| zxcmd(n,cmd) -- Execute the command in a lower fork on file number n. |
| zclosf() -- Close input file associated with zxcmd()'s lower fork. |
| zrtol(n1,n2) -- Convert remote filename into local form. |
| zltor(n1,n2) -- Convert local filename into remote form. |
| zchdir(dirnam) -- Change working directory. |
| zhome() -- Return pointer to home directory name string. |
| zkself() -- Kill self, log out own job. |
| zsattr(struct zattr *) -- Return attributes for file which is being sent. |
| zstime(f, struct zattr *, x) - Set file creation date from attribute packet. |
| zrename(old, new) -- Rename a file. |
| zcopy(source,destination) -- Copy a file. |
| zmkdir(path) -- Create the directory path if possible |
| zfnqfp(fname,len,fullpath) - Determine full path for file name. |
| zgetfs(name) -- return file size regardless of accessibility |
| zchkpid(pid) -- tell if PID is valid and active |
| */ |
| |
| /* Kermit-specific includes */ |
| /* |
| Definitions here supersede those from system include files. |
| ckcdeb.h is included above. |
| */ |
| #include "ckcker.h" /* Kermit definitions */ |
| #include "ckucmd.h" /* For keyword tables */ |
| #include "ckuver.h" /* Version herald */ |
| |
| char *ckzsys = HERALD; |
| |
| /* |
| File access checking ... There are two calls to access() in this module. |
| If this program is installed setuid or setgid on a Berkeley-based UNIX |
| system that does NOT incorporate the saved-original-effective-uid/gid |
| feature, then, when we have swapped the effective and original uid/gid, |
| access() fails because it uses what it thinks are the REAL ids, but we have |
| swapped them. This occurs on systems where ANYBSD is defined, NOSETREU |
| is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take |
| care of this situation like so: |
| |
| ifdef ANYBSD |
| ifndef NOSETREU |
| ifndef SAVEDUID |
| define SW_ACC_ID |
| endif |
| endif |
| endif |
| |
| But we can't test such a general scheme everywhere, so let's only do this |
| when we know we have to... |
| */ |
| #ifdef NEXT /* NeXTSTEP 1.0-3.0 */ |
| #define SW_ACC_ID |
| #endif /* NEXT */ |
| |
| /* Support for tilde-expansion in file and directory names */ |
| |
| #ifdef POSIX |
| #define NAMEENV "LOGNAME" |
| #else |
| #ifdef BSD4 |
| #define NAMEENV "USER" |
| #else |
| #ifdef ATTSV |
| #define NAMEENV "LOGNAME" |
| #endif /* ATTSV */ |
| #endif /* BSD4 */ |
| #endif /* POSIX */ |
| |
| /* Berkeley Unix Version 4.x */ |
| /* 4.1bsd support from Charles E Brooks, EDN-VAX */ |
| |
| #ifdef BSD4 |
| #ifdef MAXNAMLEN |
| #define BSD42 |
| #endif /* MAXNAMLEN */ |
| #endif /* BSD4 */ |
| |
| /* Definitions of some system commands */ |
| |
| char *DELCMD = "rm -f "; /* For file deletion */ |
| char *CPYCMD = "cp "; /* For file copy */ |
| char *RENCMD = "mv "; /* For file rename */ |
| char *PWDCMD = "pwd "; /* For saying where I am */ |
| |
| #ifdef COMMENT |
| #ifdef HPUX10 |
| char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */ |
| char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */ |
| #else |
| char *DIRCMD = "/bin/ls -l "; /* For directory listing */ |
| char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */ |
| #endif /* HPUX10 */ |
| #else |
| char *DIRCMD = "ls -l "; /* For directory listing */ |
| char *DIRCM2 = "ls -l "; /* For directory listing, no args */ |
| #endif /* COMMENT */ |
| |
| char *TYPCMD = "cat "; /* For typing a file */ |
| |
| #ifdef HPUX |
| char *MAILCMD = "mailx"; /* For sending mail */ |
| #else |
| #ifdef DGUX540 |
| char *MAILCMD = "mailx"; |
| #else |
| #ifdef UNIX |
| #ifdef CK_MAILCMD |
| char *MAILCMD = CK_MAILCMD; /* CFLAGS override */ |
| #else |
| char *MAILCMD = "Mail"; /* Default */ |
| #endif /* CK_MAILCMD */ |
| #else |
| char *MAILCMD = ""; |
| #endif /* UNIX */ |
| #endif /* HPUX */ |
| #endif /* DGUX40 */ |
| |
| #ifdef UNIX |
| #ifdef ANYBSD /* BSD uses lpr to spool */ |
| #ifdef DGUX540 /* And DG/UX */ |
| char * PRINTCMD = "lp"; |
| #else |
| char * PRINTCMD = "lpr"; |
| #endif /* DGUX540 */ |
| #else /* Sys V uses lp */ |
| #ifdef TRS16 /* except for Tandy-16/6000... */ |
| char * PRINTCMD = "lpr"; |
| #else |
| char * PRINTCMD = "lp"; |
| #endif /* TRS16 */ |
| #endif /* ANYBSD */ |
| #else /* Not UNIX */ |
| #define PRINTCMD "" |
| #endif /* UNIX */ |
| |
| #ifdef FT18 /* Fortune For:Pro 1.8 */ |
| #undef BSD4 |
| #endif /* FT18 */ |
| |
| #ifdef BSD4 |
| char *SPACMD = "pwd ; df ."; /* Space in current directory */ |
| #else |
| #ifdef FT18 |
| char *SPACMD = "pwd ; du ; df ."; |
| #else |
| char *SPACMD = "df "; |
| #endif /* FT18 */ |
| #endif /* BSD4 */ |
| |
| char *SPACM2 = "df "; /* For space in specified directory */ |
| |
| #ifdef FT18 |
| #define BSD4 |
| #endif /* FT18 */ |
| |
| #ifdef BSD4 |
| char *WHOCMD = "finger "; |
| #else |
| char *WHOCMD = "who "; |
| #endif /* BSD4 */ |
| |
| /* More system-dependent includes, which depend on symbols defined */ |
| /* in the Kermit-specific includes. Oh what a tangled web we weave... */ |
| |
| #ifdef COHERENT /* <sys/file.h> */ |
| #define NOFILEH |
| #endif /* COHERENT */ |
| |
| #ifdef MINIX |
| #define NOFILEH |
| #endif /* MINIX */ |
| |
| #ifdef aegis |
| #define NOFILEH |
| #endif /* aegis */ |
| |
| #ifdef unos |
| #define NOFILEH |
| #endif /* unos */ |
| |
| #ifndef NOFILEH |
| #include <sys/file.h> |
| #endif /* NOFILEH */ |
| |
| #ifndef is68k /* Whether to include <fcntl.h> */ |
| #ifndef BSD41 /* All but a couple UNIXes have it. */ |
| #ifndef FT18 |
| #ifndef COHERENT |
| #include <fcntl.h> |
| #endif /* COHERENT */ |
| #endif /* FT18 */ |
| #endif /* BSD41 */ |
| #endif /* is68k */ |
| |
| #ifdef COHERENT |
| #ifdef _I386 |
| #include <fcntl.h> |
| #else |
| #include <sys/fcntl.h> |
| #endif /* _I386 */ |
| #endif /* COHERENT */ |
| |
| extern int inserver; /* I am IKSD */ |
| int guest = 0; /* Anonymous user */ |
| |
| #ifdef IKSD |
| extern int isguest; |
| extern char * anonroot; |
| #endif /* IKSD */ |
| |
| #ifdef CK_LOGIN |
| #define GUESTPASS 256 |
| static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */ |
| static int logged_in = 0; /* Set when user is logged in */ |
| static int askpasswd = 0; /* Have OK user, must ask for passwd */ |
| #endif /* CK_LOGIN */ |
| |
| #ifdef CKROOT |
| static char ckroot[CKMAXPATH+1] = { NUL, NUL }; |
| static int ckrootset = 0; |
| int ckrooterr = 0; |
| #endif /* CKROOT */ |
| |
| _PROTOTYP( VOID ignorsigs, (void) ); |
| _PROTOTYP( VOID restorsigs, (void) ); |
| |
| /* |
| Change argument to "(const char *)" if this causes trouble. |
| Or... if it causes trouble, then maybe it was already declared |
| in a header file after all, so you can remove this prototype. |
| */ |
| #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */ |
| #ifndef _POSIX_SOURCE |
| #ifndef NEXT |
| #ifndef SVR4 |
| /* POSIX <pwd.h> already gave prototypes for these. */ |
| #ifdef IRIX40 |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| #ifdef IRIX51 |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| #ifdef M_UNIX |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| #ifdef HPUX9 |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| #ifdef HPUX10 |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| #ifdef DCGPWNAM |
| _PROTOTYP( struct passwd * getpwnam, (const char *) ); |
| #else |
| _PROTOTYP( struct passwd * getpwnam, (char *) ); |
| #endif /* DCGPWNAM */ |
| #endif /* HPUX10 */ |
| #endif /* HPUX9 */ |
| #endif /* M_UNIX */ |
| #endif /* IRIX51 */ |
| #endif /* IRIX40 */ |
| #ifndef SUNOS4 |
| #ifndef HPUX9 |
| #ifndef HPUX10 |
| #ifndef _SCO_DS |
| _PROTOTYP( struct passwd * getpwuid, (PWID_T) ); |
| #endif /* _SCO_DS */ |
| #endif /* HPUX10 */ |
| #endif /* HPUX9 */ |
| #endif /* SUNOS4 */ |
| _PROTOTYP( struct passwd * getpwent, (void) ); |
| #endif /* SVR4 */ |
| #endif /* NEXT */ |
| #endif /* _POSIX_SOURCE */ |
| #endif /* NDGPWNAM */ |
| |
| #ifdef CK_SHADOW /* Shadow Passwords... */ |
| #include <shadow.h> |
| #endif /* CK_SHADOW */ |
| #ifdef CK_PAM /* PAM... */ |
| #include <security/pam_appl.h> |
| #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */ |
| #define PAM_SERVICE_TYPE "kermit" |
| #endif /* PAM_SERVICE_TYPE */ |
| |
| #ifdef SOLARIS |
| #define PAM_CONST |
| #else /* SOLARIS */ |
| #define PAM_CONST CONST |
| #endif |
| |
| static char * pam_pw = NULL; |
| |
| int |
| #ifdef CK_ANSIC |
| pam_cb(int num_msg, |
| PAM_CONST struct pam_message **msg, |
| struct pam_response **resp, |
| void *appdata_ptr |
| ) |
| #else /* CK_ANSIC */ |
| pam_cb(num_msg, msg, resp, appdata_ptr) |
| int num_msg; |
| PAM_CONST struct pam_message **msg; |
| struct pam_response **resp; |
| void *appdata_ptr; |
| #endif /* CK_ANSIC */ |
| { |
| int i; |
| |
| debug(F111,"pam_cb","num_msg",num_msg); |
| |
| for (i = 0; i < num_msg; i++) { |
| char message[PAM_MAX_MSG_SIZE]; |
| |
| /* Issue prompt and get response */ |
| debug(F111,"pam_cb","Message",i); |
| debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style); |
| if (msg[i]->msg_style == PAM_ERROR_MSG) { |
| debug(F111,"pam_cb","PAM ERROR",0); |
| fprintf(stdout,"%s\n", msg[i]->msg); |
| return(0); |
| } else if (msg[i]->msg_style == PAM_TEXT_INFO) { |
| debug(F111,"pam_cb","PAM TEXT INFO",0); |
| fprintf(stdout,"%s\n", msg[i]->msg); |
| return(0); |
| } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) { |
| debug(F111,"pam_cb","Reading response, no echo",0); |
| /* Ugly hack. We check to see if a password has been pushed */ |
| /* into zvpasswd(). This would be true if the password was */ |
| /* received by REMOTE LOGIN. */ |
| if (pam_pw) { |
| ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE); |
| } else |
| readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); |
| } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) { |
| debug(F111,"pam_cb","Reading response, with echo",0); |
| readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE); |
| } else { |
| debug(F111,"pam_cb","unknown style",0); |
| return(0); |
| } |
| |
| /* Allocate space for this message's response structure */ |
| resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response)); |
| if (!resp[i]) { |
| int j; |
| debug(F110,"pam_cb","malloc failure",0); |
| for (j = 0; j < i; j++) { |
| free(resp[j]->resp); |
| free(resp[j]); |
| } |
| return(0); |
| } |
| |
| /* Allocate a buffer for the response */ |
| resp[i]->resp = (char *) malloc((int)strlen(message) + 1); |
| if (!resp[i]->resp) { |
| int j; |
| debug(F110,"pam_cb","malloc failure",0); |
| for (j = 0; j < i; j++) { |
| free(resp[j]->resp); |
| free(resp[j]); |
| } |
| free(resp[i]); |
| return(0); |
| } |
| /* Return the results back to PAM */ |
| strcpy(resp[i]->resp, message); /* safe (prechecked) */ |
| resp[i]->resp_retcode = 0; |
| } |
| debug(F110,"pam_cb","Exiting",0); |
| return(0); |
| } |
| #endif /* CK_PAM */ |
| |
| /* Define macros for getting file type */ |
| |
| #ifdef OXOS |
| /* |
| Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined |
| incorrectly, so we force their redefinition. |
| */ |
| #undef S_ISREG |
| #undef S_ISDIR |
| #endif /* OXOS */ |
| |
| #ifdef UTSV /* Same deal for Amdahl UTSV */ |
| #undef S_ISREG |
| #undef S_ISDIR |
| #endif /* UTSV */ |
| |
| #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */ |
| #undef S_ISREG |
| #undef S_ISDIR |
| #endif /* UNISYS52 */ |
| |
| #ifdef ICLSVR3 /* And for old ICL versions */ |
| #undef S_ISREG |
| #undef S_ISDIR |
| #endif /* ICLSVR3 */ |
| |
| #ifdef ISDIRBUG /* Also allow this from command line */ |
| #ifdef S_ISREG |
| #undef S_ISREG |
| #endif /* S_ISREG */ |
| #ifdef S_ISDIR |
| #undef S_ISDIR |
| #endif /* S_ISDIR */ |
| #endif /* ISDIRBUG */ |
| |
| #ifndef _IFMT |
| #ifdef S_IFMT |
| #define _IFMT S_IFMT |
| #else |
| #define _IFMT 0170000 |
| #endif /* S_IFMT */ |
| #endif /* _IFMT */ |
| |
| #ifndef S_ISREG |
| #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) |
| #endif /* S_ISREG */ |
| #ifndef S_ISDIR |
| #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) |
| #endif /* S_ISDIR */ |
| |
| /* The following mainly for NeXTSTEP... */ |
| |
| #ifndef S_IWUSR |
| #define S_IWUSR 0000200 |
| #endif /* S_IWUSR */ |
| |
| #ifndef S_IRGRP |
| #define S_IRGRP 0000040 |
| #endif /* S_IRGRP */ |
| |
| #ifndef S_IWGRP |
| #define S_IWGRP 0000020 |
| #endif /* S_IWGRP */ |
| |
| #ifndef S_IXGRP |
| #define S_IXGRP 0000010 |
| #endif /* S_IXGRP */ |
| |
| #ifndef S_IROTH |
| #define S_IROTH 0000004 |
| #endif /* S_IROTH */ |
| |
| #ifndef S_IWOTH |
| #define S_IWOTH 0000002 |
| #endif /* S_IWOTH */ |
| |
| #ifndef S_IXOTH |
| #define S_IXOTH 0000001 |
| #endif /* S_IXOTH */ |
| /* |
| Define maximum length for a file name if not already defined. |
| NOTE: This applies to a path segment (directory or file name), |
| not the entire path string, which can be CKMAXPATH bytes long. |
| */ |
| #ifdef QNX |
| #ifdef _MAX_FNAME |
| #define MAXNAMLEN _MAX_FNAME |
| #else |
| #define MAXNAMLEN 48 |
| #endif /* _MAX_FNAME */ |
| #else |
| #ifndef MAXNAMLEN |
| #ifdef sun |
| #define MAXNAMLEN 255 |
| #else |
| #ifdef FILENAME_MAX |
| #define MAXNAMLEN FILENAME_MAX |
| #else |
| #ifdef NAME_MAX |
| #define MAXNAMLEN NAME_MAX |
| #else |
| #ifdef _POSIX_NAME_MAX |
| #define MAXNAMLEN _POSIX_NAME_MAX |
| #else |
| #ifdef _D_NAME_MAX |
| #define MAXNAMLEN _D_NAME_MAX |
| #else |
| #ifdef DIRSIZ |
| #define MAXNAMLEN DIRSIZ |
| #else |
| #define MAXNAMLEN 14 |
| #endif /* DIRSIZ */ |
| #endif /* _D_NAME_MAX */ |
| #endif /* _POSIX_NAME_MAX */ |
| #endif /* NAME_MAX */ |
| #endif /* FILENAME_MAX */ |
| #endif /* sun */ |
| #endif /* MAXNAMLEN */ |
| #endif /* QNX */ |
| |
| #ifdef COMMENT |
| /* As of 2001-11-03 this is handled in ckcdeb.h */ |
| /* Longest pathname ... */ |
| /* |
| Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it |
| defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ... |
| There is not necessarily even a definition for it anywhere, or it might have |
| another name. If you get it wrong, bad things happen with getcwd() and/or |
| getwd(). If you allocate a buffer that is too short, getwd() might write |
| over memory and getcwd() will fail with ERANGE. The definitions of these |
| functions (e.g. in SVID or POSIX.1) do not tell you how to determine the |
| maximum path length in order to allocate a buffer that is the right size. |
| */ |
| #ifdef BSD44 |
| #include <sys/param.h> /* For MAXPATHLEN */ |
| #endif /* BSD44 */ |
| #ifdef COHERENT |
| #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */ |
| #endif /* COHERENT */ |
| #endif /* COMMENT */ |
| |
| #ifdef MAXPATHLEN |
| #ifdef MAXPATH |
| #undef MAXPATH |
| #endif /* MAXPATH */ |
| #define MAXPATH MAXPATHLEN |
| #else |
| #ifdef PATH_MAX |
| #define MAXPATH PATH_MAX |
| #else |
| #ifdef _POSIX_PATH_MAX |
| #define MAXPATH _POSIX_PATH_MAX |
| #else |
| #ifdef BSD42 |
| #define MAXPATH 1024 |
| #else |
| #ifdef SVR4 |
| #define MAXPATH 1024 |
| #else |
| #define MAXPATH 255 |
| #endif /* SVR4 */ |
| #endif /* BSD42 */ |
| #endif /* _POSIX_PATH_MAX */ |
| #endif /* PATH_MAX */ |
| #endif /* MAXPATHLEN */ |
| |
| /* Maximum number of filenames for wildcard expansion */ |
| |
| #ifndef MAXWLD |
| /* Already defined in ckcdeb.h so the following is superfluous. */ |
| /* Don't expect changing them to have any effect. */ |
| #ifdef CK_SMALL |
| #define MAXWLD 50 |
| #else |
| #ifdef BIGBUFOK |
| #define MAXWLD 102400 |
| #else |
| #define MAXWLD 8192 |
| #endif /* BIGBUFOK */ |
| #endif /* CK_SMALL */ |
| #endif /* MAXWLD */ |
| |
| static int maxnames = MAXWLD; |
| |
| /* Define the size of the string space for filename expansion. */ |
| |
| #ifndef DYNAMIC |
| #ifdef PROVX1 |
| #define SSPACE 500 |
| #else |
| #ifdef BSD29 |
| #define SSPACE 500 |
| #else |
| #ifdef pdp11 |
| #define SSPACE 500 |
| #else |
| #ifdef aegis |
| #define SSPACE 10000 /* Size of string-generating buffer */ |
| #else /* Default static buffer size */ |
| #ifdef BIGBUFOK |
| #define SSPACE 65000 /* Size of string-generating buffer */ |
| #else |
| #define SSPACE 2000 /* size of string-generating buffer */ |
| #endif /* BIGBUFOK */ |
| #endif /* aegis */ |
| #endif /* pdp11 */ |
| #endif /* BSD29 */ |
| #endif /* PROVX1 */ |
| static char sspace[SSPACE]; /* Buffer for generating filenames */ |
| #else /* is DYNAMIC */ |
| #ifdef BIGBUFOK |
| #define SSPACE 500000 |
| #else |
| #define SSPACE 10000 |
| #endif /* BIGBUFOK */ |
| char *sspace = (char *)0; |
| #endif /* DYNAMIC */ |
| static int ssplen = SSPACE; /* Length of string space buffer */ |
| |
| #ifdef DCLFDOPEN |
| /* fdopen() needs declaring because it's not declared in <stdio.h> */ |
| _PROTOTYP( FILE * fdopen, (int, char *) ); |
| #endif /* DCLFDOPEN */ |
| |
| #ifdef DCLPOPEN |
| /* popen() needs declaring because it's not declared in <stdio.h> */ |
| _PROTOTYP( FILE * popen, (char *, char *) ); |
| #endif /* DCLPOPEN */ |
| |
| extern int nopush; |
| |
| /* More internal function prototypes */ |
| /* |
| * The path structure is used to represent the name to match. |
| * Each slash-separated segment of the name is kept in one |
| * such structure, and they are linked together, to make |
| * traversing the name easier. |
| */ |
| struct path { |
| char npart[MAXNAMLEN+4]; /* name part of path segment */ |
| struct path *fwd; /* forward ptr */ |
| }; |
| #ifndef NOPUSH |
| _PROTOTYP( int shxpand, (char *, char *[], int ) ); |
| #endif /* NOPUSH */ |
| _PROTOTYP( static int fgen, (char *, char *[], int ) ); |
| _PROTOTYP( static VOID traverse, (struct path *, char *, char *) ); |
| _PROTOTYP( static VOID addresult, (char *, int) ); |
| #ifdef COMMENT |
| /* Replaced by ckmatch() */ |
| _PROTOTYP( static int match, (char *, char *) ); |
| #endif /* COMMENT */ |
| _PROTOTYP( char * whoami, (void) ); |
| _PROTOTYP( UID_T real_uid, (void) ); |
| _PROTOTYP( static struct path *splitpath, (char *p) ); |
| _PROTOTYP( char * zdtstr, (time_t) ); |
| _PROTOTYP( time_t zstrdt, (char *, int) ); |
| |
| /* Some systems define these symbols in include files, others don't... */ |
| |
| #ifndef R_OK |
| #define R_OK 4 /* For access */ |
| #endif /* R_OK */ |
| |
| #ifndef W_OK |
| #define W_OK 2 |
| #endif /* W_OK */ |
| |
| #ifndef X_OK |
| #define X_OK 1 |
| #endif /* X_OK */ |
| |
| #ifndef O_RDONLY |
| #define O_RDONLY 000 |
| #endif /* O_RDONLY */ |
| |
| /* syslog and wtmp items for Internet Kermit Service */ |
| |
| extern char * clienthost; /* From ckcmai.c. */ |
| |
| static char fullname[CKMAXPATH+1]; |
| static char tmp2[CKMAXPATH+1]; |
| |
| extern int ckxlogging; |
| |
| #ifdef CKXPRINTF /* Our printf macro conflicts with */ |
| #undef printf /* use of "printf" in syslog.h */ |
| #endif /* CKXPRINTF */ |
| #ifdef CKSYSLOG |
| #ifdef RTAIX |
| #include <sys/syslog.h> |
| #else /* RTAIX */ |
| #include <syslog.h> |
| #endif /* RTAIX */ |
| #endif /* CKSYSLOG */ |
| #ifdef CKXPRINTF |
| #define printf ckxprintf |
| #endif /* CKXPRINTF */ |
| |
| int ckxanon = 1; /* Anonymous login ok */ |
| int ckxperms = 0040; /* Anonymous file permissions */ |
| int ckxpriv = 1; /* Priv'd login ok */ |
| |
| #ifndef XFERFILE |
| #define XFERFILE "/var/log/iksd.log" |
| #endif /* XFERFILE */ |
| |
| /* wtmp logging for IKSD... */ |
| |
| #ifndef CKWTMP /* wtmp logging not selected */ |
| int ckxwtmp = 0; /* Know this at runtime */ |
| #else /* wtmp file details */ |
| int ckxwtmp = 1; |
| #ifdef UTMPBUG /* Unfortunately... */ |
| /* |
| Some versions of Linux have a <utmp.h> file that contains |
| "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers |
| any program that uses any of these words as variable names, function |
| names, macro names, etc. (Other versions of Linux have this declaration |
| within #if 0 ... #endif.) There is nothing we can do about this other |
| than to not include the stupid file. But we need stuff from it, so... |
| */ |
| #include <features.h> |
| #include <sys/types.h> |
| #define UT_LINESIZE 32 |
| #define UT_NAMESIZE 32 |
| #define UT_HOSTSIZE 256 |
| |
| struct timeval { |
| time_t tv_sec; |
| time_t tv_usec; |
| }; |
| |
| struct exit_status { |
| short int e_termination; /* Process termination status. */ |
| short int e_exit; /* Process exit status. */ |
| }; |
| |
| struct utmp { |
| short int ut_type; /* Type of login */ |
| pid_t ut_pid; /* Pid of login process */ |
| char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */ |
| char ut_id[4]; /* Inittab id */ |
| char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */ |
| |
| char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */ |
| struct exit_status ut_exit; /* Exit status */ |
| long ut_session; /* Session ID, used for windowing */ |
| struct timeval ut_tv; /* Time entry was made */ |
| int32_t ut_addr_v6[4]; /* Internet address of remote host */ |
| char pad[20]; /* Reserved */ |
| }; |
| |
| #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */ |
| #define ut_name ut_user /* ... */ |
| |
| extern void |
| logwtmp __P ((__const char *__ut_line, __const char *__ut_name, |
| __const char *__ut_host)); |
| |
| #else /* Not UTMPBUG */ |
| |
| #ifndef HAVEUTMPX /* Who has <utmpx.h> */ |
| #ifdef SOLARIS |
| #define HAVEUTMPX |
| #else |
| #ifdef IRIX60 |
| #define HAVEUTMPX |
| #else |
| #ifdef CK_SCOV5 |
| #define HAVEUTMPX |
| #else |
| #ifdef HPUX100 |
| #define HAVEUTMPX |
| #else |
| #ifdef UNIXWARE |
| #define HAVEUTMPX |
| #endif /* UNIXWARE */ |
| #endif /* HPUX100 */ |
| #endif /* CK_SCOV5 */ |
| #endif /* IRIX60 */ |
| #endif /* SOLARIS */ |
| #endif /* HAVEUTMPX */ |
| #ifdef HAVEUTMPX |
| #include <utmpx.h> |
| #else |
| #ifdef OSF50 |
| /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */ |
| #define __V40_OBJ_COMPAT 1 |
| #endif /* OSF50 */ |
| #include <utmp.h> |
| #ifdef OSF50 |
| #undef __V40_OBJ_COMPAT |
| #endif /* OSF50 */ |
| #endif /* HAVEUTMPX */ |
| #endif /* UTMPBUG */ |
| |
| #ifndef WTMPFILE |
| #ifdef QNX |
| #define WTMPFILE "/usr/adm/wtmp.1" |
| #else |
| #ifdef LINUX |
| #define WTMPFILE "/var/log/wtmp" |
| #else |
| #define WTMPFILE "/usr/adm/wtmp" |
| #endif /* QNX */ |
| #endif /* LINUX */ |
| #endif /* WTMPFILE */ |
| char * wtmpfile = NULL; |
| |
| static int wtmpfd = 0; |
| static char cksysline[32] = { NUL, NUL }; |
| |
| #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */ |
| #ifdef HAVEUTMPX /* utmpx always does */ |
| #define HAVEUTHOST |
| #else |
| #ifdef LINUX /* Linux does */ |
| #define HAVEUTHOST |
| #else |
| #ifdef SUNOS4 /* SunOS does */ |
| #define HAVEUTHOST |
| #else |
| #ifdef AIX41 /* AIX 4.1 and later do */ |
| #define HAVEUTHOST |
| #endif /* AIX41 */ |
| #endif /* SUNOS4 */ |
| #endif /* LINUX */ |
| #endif /* HAVEUTMPX */ |
| #endif /* HAVEUTHOST */ |
| |
| #ifdef UW200 |
| PID_T _vfork() { /* To satisfy a library foulup */ |
| return(fork()); /* in Unixware 2.0.x */ |
| } |
| #endif /* UW200 */ |
| |
| VOID |
| #ifdef CK_ANSIC |
| logwtmp(const char * line, const char * name, const char * host) |
| #else |
| logwtmp(line, name, host) char *line, *name, *host; |
| #endif /* CK_ANSIC */ |
| /* logwtmp */ { |
| #ifdef HAVEUTMPX |
| struct utmpx ut; /* Needed for ut_host[] */ |
| #else |
| struct utmp ut; |
| #endif /* HAVEUTMPX */ |
| struct stat buf; |
| /* time_t time(); */ |
| |
| if (!ckxwtmp) |
| return; |
| |
| if (!wtmpfile) |
| makestr(&wtmpfile,WTMPFILE); |
| |
| if (!line) line = ""; |
| if (!name) name = ""; |
| if (!host) host = ""; |
| |
| if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) { |
| ckxwtmp = 0; |
| debug(F110,"WTMP open failed",line,0); |
| return; |
| } |
| if (!fstat(wtmpfd, &buf)) { |
| ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line)); |
| ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name)); |
| #ifdef HAVEUTHOST |
| /* Not portable */ |
| ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host)); |
| #endif /* HAVEUTHOST */ |
| #ifdef HAVEUTMPX |
| time(&ut.ut_tv.tv_sec); |
| #else |
| #ifdef LINUX |
| /* In light of the following comment perhaps the previous line should */ |
| /* be "#ifndef COMMENT". */ |
| { |
| /* |
| * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time) |
| * are not the same and attempt to use an address of |
| * ut.ut_time as an argument to time() call may cause |
| * "unaligned access" trap. |
| */ |
| time_t zz; |
| time(&zz); |
| ut.ut_time = zz; |
| } |
| #else |
| time(&ut.ut_time); |
| #endif /* LINUX */ |
| #endif /* HAVEUTMPX */ |
| if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) != |
| sizeof(struct utmp)) { |
| #ifndef NOFTRUNCATE |
| #ifndef COHERENT |
| ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */ |
| #else |
| chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */ |
| #endif /* COHERENT */ |
| #endif /* NOFTRUNCATE */ |
| debug(F110,"WTMP write error",line,0); |
| } else { |
| debug(F110,"WTMP record OK",line,0); |
| return; |
| } |
| } |
| } |
| #endif /* CKWTMP */ |
| |
| #ifdef CKSYSLOG |
| /* |
| C K S Y S L O G -- C-Kermit system logging function, |
| |
| For use by other modules. |
| This module can, but doesn't have to, use it. |
| Call with: |
| n = SYSLG_xx values defined in ckcdeb.h |
| s1, s2, s3: strings. |
| */ |
| VOID |
| cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; { |
| int level; |
| |
| if (!ckxlogging) /* syslogging */ |
| return; |
| if (!s1) s1 = ""; /* Fix null args */ |
| if (!s2) s2 = ""; |
| if (!s3) s3 = ""; |
| switch (n) { /* Translate Kermit level */ |
| case SYSLG_DB: /* to syslog level */ |
| level = LOG_DEBUG; |
| break; |
| default: |
| level = m ? LOG_INFO : LOG_ERR; |
| } |
| debug(F110,"cksyslog s1",s1,0); |
| debug(F110,"cksyslog s2",s2,0); |
| debug(F110,"cksyslog s3",s3,0); |
| errno = 0; |
| syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */ |
| debug(F101,"cksyslog errno","",errno); |
| } |
| #endif /* CKSYSLOG */ |
| |
| |
| /* Declarations */ |
| |
| int maxnam = MAXNAMLEN; /* Available to the outside */ |
| int maxpath = MAXPATH; |
| int ck_znewn = -1; |
| |
| #ifdef UNIX |
| char startupdir[MAXPATH+1]; |
| #endif /* UNIX */ |
| |
| int pexitstat = -2; /* Process exit status */ |
| |
| FILE *fp[ZNFILS] = { /* File pointers */ |
| NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL |
| }; |
| |
| /* Flags for each file indicating whether it was opened with popen() */ |
| int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| |
| /* Buffers and pointers used in buffered file input and output. */ |
| #ifdef DYNAMIC |
| extern char *zinbuffer, *zoutbuffer; |
| #else |
| extern char zinbuffer[], zoutbuffer[]; |
| #endif /* DYNAMIC */ |
| extern char *zinptr, *zoutptr; |
| extern int zincnt, zoutcnt; |
| extern int wildxpand; |
| |
| static long iflen = -1L; /* Input file length */ |
| |
| static PID_T pid = 0; /* pid of child fork */ |
| static int fcount = 0; /* Number of files in wild group */ |
| static int nxpand = 0; /* Copy of fcount */ |
| static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */ |
| |
| #ifndef NOFRILLS |
| #define ZMBUFLEN 200 |
| static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */ |
| #endif /* NOFRILLS */ |
| |
| char **mtchs = NULL; /* Matches found for filename */ |
| char **mtchptr = NULL; /* Pointer to current match */ |
| |
| /* Z K S E L F -- Kill Self: log out own job, if possible. */ |
| |
| /* Note, should get current pid, but if your system doesn't have */ |
| /* getppid(), then just kill(0,9)... */ |
| |
| #ifndef SVR3 |
| #ifndef POSIX |
| #ifndef OSFPC |
| /* Already declared in unistd.h for SVR3 and POSIX */ |
| #ifdef CK_ANSIC |
| extern PID_T getppid(void); |
| #else |
| #ifndef PS2AIX10 |
| #ifndef COHERENT |
| extern PID_T getppid(); |
| #endif /* COHERENT */ |
| #endif /* PS2AIX10 */ |
| #endif /* CK_ANSIC */ |
| #endif /* OSFPC */ |
| #endif /* POSIX */ |
| #endif /* SVR3 */ |
| |
| int |
| zkself() { /* For "bye", but no guarantee! */ |
| #ifdef PROVX1 |
| return(kill(0,9)); |
| #else |
| #ifdef V7 |
| return(kill(0,9)); |
| #else |
| #ifdef TOWER1 |
| return(kill(0,9)); |
| #else |
| #ifdef FT18 |
| return(kill(0,9)); |
| #else |
| #ifdef aegis |
| return(kill(0,9)); |
| #else |
| #ifdef COHERENT |
| return(kill((PID_T)getpid(),1)); |
| #else |
| #ifdef PID_T |
| exit(kill((PID_T)getppid(),1)); |
| return(0); |
| #else |
| exit(kill(getppid(),1)); |
| return(0); |
| #endif |
| #endif |
| #endif |
| #endif |
| #endif |
| #endif |
| #endif |
| } |
| |
| static VOID |
| getfullname(name) char * name; { |
| char *p = (char *)fullname; |
| int len = 0; |
| fullname[0] = '\0'; |
| /* If necessary we could also chase down symlinks here... */ |
| #ifdef COMMENT |
| /* This works but is incompatible with wuftpd */ |
| if (isguest && anonroot) { |
| ckstrncpy(fullname,anonroot,CKMAXPATH); |
| len = strlen(fullname); |
| if (len > 0) |
| if (fullname[len-1] == '/') |
| len--; |
| } |
| p += len; |
| #endif /* COMMENT */ |
| zfnqfp(name, CKMAXPATH - len, p); |
| while (*p) { |
| if (*p < '!') *p = '_'; |
| p++; |
| } |
| } |
| |
| /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */ |
| |
| VOID /* Called in ckcmai.c */ |
| doiklog() { |
| if (iklogopen) /* Already open? */ |
| return; |
| if (xferlog) { /* Open iksd log if requested */ |
| if (!xferfile) /* If no pathname given */ |
| makestr(&xferfile,XFERFILE); /* use this default */ |
| if (*xferfile) { |
| xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660); |
| debug(F101,"doiklog open","",xferlog); |
| if (xferlog < 0) { |
| #ifdef CKSYSLOG |
| syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile); |
| #endif /* CKSYSLOG */ |
| debug(F101,"doiklog open errno","",errno); |
| xferlog = 0; |
| } else |
| iklogopen = 1; |
| } else |
| xferlog = 0; |
| #ifdef CKSYSLOG |
| if (xferlog && ckxlogging) |
| syslog(LOG_INFO, "xferlog: %s open ok", xferfile); |
| #endif /* CKSYSLOG */ |
| } |
| } |
| |
| /* Z O P E N I -- Open an existing file for input. */ |
| |
| /* Returns 1 on success, 0 on failure */ |
| |
| int |
| zopeni(n,name) int n; char *name; { |
| int x; |
| |
| debug(F111,"zopeni",name,n); |
| if ((x = chkfn(n)) != 0) { |
| debug(F111,"zopeni chkfn",ckitoa(n),x); |
| return(0); |
| } |
| zincnt = 0; /* Reset input buffer */ |
| if (n == ZSYSFN) { /* Input from a system function? */ |
| #ifdef COMMENT |
| /*** Note, this function should not be called with ZSYSFN ***/ |
| /*** Always call zxcmd() directly, and give it the real file number ***/ |
| /*** you want to use. ***/ |
| return(zxcmd(n,name)); /* Try to fork the command */ |
| #else |
| debug(F110,"zopeni called with ZSYSFN, failing!",name,0); |
| *nambuf = '\0'; /* No filename. */ |
| return(0); /* fail. */ |
| #endif /* COMMENT */ |
| } |
| if (n == ZSTDIO) { /* Standard input? */ |
| if (is_a_tty(0)) { |
| fprintf(stderr,"Terminal input not allowed"); |
| debug(F110,"zopeni: attempts input from unredirected stdin","",0); |
| return(0); |
| } |
| fp[ZIFILE] = stdin; |
| ispipe[ZIFILE] = 0; |
| return(1); |
| } |
| #ifdef CKROOT |
| debug(F111,"zopeni setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zopeni setroot violation",name,0); |
| return(0); |
| } |
| #endif /* CKROOT */ |
| fp[n] = fopen(name,"r"); /* Real file, open it. */ |
| debug(F111,"zopeni fopen", name, fp[n]); |
| #ifdef ZDEBUG |
| printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); |
| #endif /* ZDEBUG */ |
| ispipe[n] = 0; |
| |
| if (xferlog |
| #ifdef CKSYSLOG |
| || ((ckxsyslog >= SYSLG_FA) && ckxlogging) |
| #endif /* CKSYSLOG */ |
| ) { |
| getfullname(name); |
| debug(F110,"zopeni fullname",fullname,0); |
| } |
| if (fp[n] == NULL) { |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FA && ckxlogging) { |
| syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname); |
| perror(fullname); |
| } else |
| #endif /* CKSYSLOG */ |
| perror(name); |
| return(0); |
| } else { |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FA && ckxlogging) |
| syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname); |
| #endif /* CKSYSLOG */ |
| clearerr(fp[n]); |
| return(1); |
| } |
| } |
| |
| #ifdef QNX |
| #define DONDELAY |
| #else |
| #ifdef O_NDELAY |
| #define DONDELAY |
| #endif /* O_NDELAY */ |
| #endif /* QNX */ |
| |
| /* Z O P E N O -- Open a new file for output. */ |
| |
| /*ARGSUSED*/ /* zz not used */ |
| int |
| zopeno(n,name,zz,fcb) |
| /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; { |
| |
| char p[8]; |
| int append = 0; |
| |
| /* As of Version 5A, the attribute structure and the file information */ |
| /* structure are included in the arglist. */ |
| |
| #ifdef DEBUG |
| debug(F111,"zopeno",name,n); |
| if (fcb) { |
| debug(F101,"zopeno fcb disp","",fcb->dsp); |
| debug(F101,"zopeno fcb type","",fcb->typ); |
| debug(F101,"zopeno fcb char","",fcb->cs); |
| } else { |
| debug(F100,"zopeno fcb is NULL","",0); |
| } |
| #endif /* DEBUG */ |
| |
| if (chkfn(n) != 0) /* Already open? */ |
| return(0); /* Nothing to do. */ |
| |
| if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */ |
| fp[ZOFILE] = stdout; |
| ispipe[ZOFILE] = 0; |
| #ifdef COMMENT |
| /* This seems right but it breaks client server ops */ |
| fp[n] = stdout; |
| ispipe[n] = 0; |
| #endif /* COMMENT */ |
| #ifdef DEBUG |
| if (n != ZDFILE) |
| debug(F101,"zopeno fp[n]=stdout","",fp[n]); |
| #endif /* DEBUG */ |
| zoutcnt = 0; |
| zoutptr = zoutbuffer; |
| return(1); |
| } |
| |
| /* A real file. Open it in desired mode (create or append). */ |
| |
| #ifdef CKROOT |
| debug(F111,"zopeno setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zopeno setroot violation",name,0); |
| return(0); |
| } |
| #endif /* CKROOT */ |
| |
| ckstrncpy(p,"w",8); /* Assume write/create mode */ |
| if (fcb) { /* If called with an FCB... */ |
| if (fcb->dsp == XYFZ_A) { /* Does it say Append? */ |
| ckstrncpy(p,"a",8); /* Yes. */ |
| debug(F100,"zopeno append","",0); |
| append = 1; |
| } |
| } |
| |
| if (xferlog |
| #ifdef CKSYSLOG |
| || ((ckxsyslog >= SYSLG_FC) && ckxlogging) |
| #endif /* CKSYSLOG */ |
| ) { |
| getfullname(name); |
| debug(F110,"zopeno fullname",fullname,0); |
| } |
| debug(F110,"zopeno fopen arg",p,0); |
| fp[n] = fopen(name,p); /* Try to open the file */ |
| ispipe[ZIFILE] = 0; |
| |
| #ifdef ZDEBUG |
| printf("ZOPENO fp[%d]=%ld\n",n,fp[n]); |
| #endif /* ZDEBUG */ |
| |
| if (fp[n] == NULL) { /* Failed */ |
| debug(F101,"zopeno failed errno","",errno); |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FC && ckxlogging) |
| syslog(LOG_INFO, "file[%d] %s: %s failed (%m)", |
| n, |
| fullname, |
| append ? "append" : "create" |
| ); |
| #endif /* CKSYSLOG */ |
| #ifdef COMMENT /* Let upper levels print message. */ |
| perror("Can't open output file"); |
| #endif /* COMMENT */ |
| } else { /* Succeeded */ |
| extern int zofbuffer, zofblock, zobufsize; |
| debug(F101, "zopeno zobufsize", "", zobufsize); |
| if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */ |
| setbuf(fp[n],NULL); /* make it unbuffered. */ |
| #ifdef DONDELAY |
| } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */ |
| int flags; |
| if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1) |
| fcntl(fileno(fp[n]),F_SETFL, flags | |
| #ifdef QNX |
| O_NONBLOCK |
| #else |
| O_NDELAY |
| #endif /* QNX */ |
| ); |
| debug(F100,"zopeno ZOFILE nonblocking","",0); |
| #endif /* DONDELAY */ |
| } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */ |
| setbuf(fp[n],NULL); |
| debug(F100,"zopeno ZOFILE unbuffered","",0); |
| } |
| |
| #ifdef CK_LOGIN |
| /* Enforce anonymous file-creation permission */ |
| if (isguest) |
| if (n == ZWFILE || n == ZMFILE || |
| n == ZOFILE || n == ZDFILE || |
| n == ZTFILE || n == ZPFILE || |
| n == ZSFILE) |
| chmod(name,ckxperms); |
| #endif /* CK_LOGIN */ |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FC && ckxlogging) |
| syslog(LOG_INFO, "file[%d] %s: %s ok", |
| n, |
| fullname, |
| append ? "append" : "create" |
| ); |
| #endif /* CKSYSLOG */ |
| debug(F100, "zopeno ok", "", 0); |
| } |
| zoutcnt = 0; /* (PWP) reset output buffer */ |
| zoutptr = zoutbuffer; |
| return((fp[n] != NULL) ? 1 : 0); |
| } |
| |
| /* Z C L O S E -- Close the given file. */ |
| |
| /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */ |
| |
| int |
| zclose(n) int n; { |
| int x = 0, x2 = 0; |
| extern long ffc; |
| |
| debug(F101,"zclose file number","",n); |
| if (chkfn(n) < 1) return(0); /* Check range of n */ |
| if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */ |
| x2 = zoutdump(); |
| |
| if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */ |
| #ifndef NOPUSH |
| x = zclosf(n); /* do it specially */ |
| #else |
| x = EOF; |
| #endif /* NOPUSH */ |
| debug(F101,"zclose zclosf","",x); |
| debug(F101,"zclose zclosf fp[n]","",fp[n]); |
| } else { |
| if ((fp[n] != stdout) && (fp[n] != stdin)) |
| x = fclose(fp[n]); |
| fp[n] = NULL; |
| #ifdef COMMENT |
| if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */ |
| if (fp[ZOFILE] == stdout) |
| fp[ZOFILE] = NULL; |
| #endif /* COMMENT */ |
| } |
| iflen = -1L; /* Invalidate file length */ |
| if (x == EOF) { /* if we got a close error */ |
| debug(F101,"zclose fclose fails","",x); |
| return(-1); |
| } else if (x2 < 0) { /* or error flushing last buffer */ |
| debug(F101,"zclose error flushing last buffer","",x2); |
| return(-1); /* then return an error */ |
| } else { |
| /* Print log record compatible with wu-ftpd */ |
| if (xferlog && (n == ZIFILE || n == ZOFILE)) { |
| char * s, *p; |
| extern char ttname[]; |
| if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */ |
| debug(F101,"zclose iklogopen","",iklogopen); |
| if (iklogopen) { |
| int len; |
| char * fnam; |
| |
| timenow = time(NULL); |
| #ifdef CK_LOGIN |
| if (logged_in) |
| s = clienthost; |
| else |
| #endif /* CK_LOGIN */ |
| s = (char *)ttname; |
| if (!s) s = ""; |
| if (!*s) s = "*"; |
| #ifdef CK_LOGIN |
| if (logged_in) { |
| p = guestpass; |
| if (!*p) p = "*"; |
| } else |
| #endif /* CK_LOGIN */ |
| p = whoami(); |
| |
| len = 24 + 12 + (int)strlen(s) + 16 |
| + (int)strlen(fullname) + 1 + 1 + 1 + 1 |
| + (int)strlen(p) + 6 + 2 + 12; |
| fnam = fullname; |
| if (!*fnam) fnam = "(pipe)"; |
| |
| if (len > IKSDMSGLEN) |
| sprintf(iksdmsg, /* SAFE */ |
| "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow)); |
| else |
| sprintf(iksdmsg, /* SAFE */ |
| "%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n", |
| ctime(&timenow), /* date/time */ |
| gtimer(), /* elapsed secs */ |
| s, /* peer name */ |
| ffc, /* byte count */ |
| fnam, /* full pathname of file */ |
| (binary ? 'b' : 'a'), /* binary or ascii */ |
| "_", /* options = none */ |
| n == ZIFILE ? 'o' : 'i', /* in/out */ |
| #ifdef CK_LOGIN |
| (isguest ? 'a' : 'r'), /* User type */ |
| #else |
| 'r', |
| #endif /* CK_LOGIN */ |
| p, /* Username or guest passwd */ |
| #ifdef CK_LOGIN |
| logged_in ? "iks" : "kermit", /* Record ID */ |
| #else |
| "kermit", |
| #endif /* CK_LOGIN */ |
| 0, /* User ID on client system unknown */ |
| "*" /* Ditto */ |
| ); |
| debug(F110,"zclose iksdmsg",iksdmsg,0); |
| write(xferlog, iksdmsg, (int)strlen(iksdmsg)); |
| } |
| } |
| debug(F101,"zclose returns","",1); |
| return(1); |
| } |
| } |
| |
| /* Z C H I N -- Get a character from the input file. */ |
| |
| /* Returns -1 if EOF, 0 otherwise with character returned in argument */ |
| |
| int |
| zchin(n,c) int n; int *c; { |
| int a; |
| |
| #ifdef IKSD |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| a = coninc(0); |
| if (*c < 0) |
| return(-1); |
| } else |
| #endif /* IKSD */ |
| /* (PWP) Just in case this gets called when it shouldn't. */ |
| if (n == ZIFILE) { |
| a = zminchar(); /* Note: this catches Ctrl-Z */ |
| if (a < 0) /* (See zinfill()...) */ |
| return(-1); |
| } else { |
| a = getc(fp[n]); |
| if (a == EOF) return(-1); |
| #ifdef CK_CTRLZ |
| /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ |
| if (!binary && a == 0x1A && eofmethod == XYEOF_Z) |
| return(-1); |
| #endif /* CK_CTRLZ */ |
| } |
| *c = (CHAR) a & 0377; |
| return(0); |
| } |
| |
| /* Z S I N L -- Read a line from a file */ |
| |
| /* |
| Writes the line into the address provided by the caller. |
| n is the Kermit "channel number". |
| Writing terminates when newline is encountered, newline is not copied. |
| Writing also terminates upon EOF or if length x is exhausted. |
| Returns 0 on success, -1 on EOF or error. |
| */ |
| int |
| zsinl(n,s,x) int n, x; char *s; { |
| int a, z = 0; /* z is return code. */ |
| int count = 0; |
| int len = 0; |
| char *buf; |
| extern CHAR feol; /* Line terminator */ |
| |
| if (!s || chkfn(n) < 1) /* Make sure file is open, etc */ |
| return(-1); |
| buf = s; |
| s[0] = '\0'; /* Don't return junk */ |
| |
| a = -1; /* Current character, none yet. */ |
| while (x--) { /* Up to given length */ |
| int old = 0; |
| if (feol) /* Previous character */ |
| old = a; |
| if (zchin(n,&a) < 0) { /* Read a character from the file */ |
| debug(F101,"zsinl zchin fail","",count); |
| if (count == 0) |
| z = -1; /* EOF or other error */ |
| break; |
| } else |
| count++; |
| if (feol) { /* Single-character line terminator */ |
| if (a == feol) |
| break; |
| } else { /* CRLF line terminator */ |
| if (a == '\015') /* CR, get next character */ |
| continue; |
| if (old == '\015') { /* Previous character was CR */ |
| if (a == '\012') { /* This one is LF, so we have a line */ |
| break; |
| } else { /* Not LF, deposit CR */ |
| *s++ = '\015'; |
| x--; |
| len++; |
| } |
| } |
| } |
| *s = a; /* Deposit character */ |
| s++; |
| len++; |
| } |
| *s = '\0'; /* Terminate the string */ |
| debug(F011,"zsinl",buf,len); |
| return(z); |
| } |
| |
| /* Z X I N -- Read x bytes from a file */ |
| |
| /* |
| Reads x bytes (or less) from channel n and writes them |
| to the address provided by the caller. |
| Returns number of bytes read on success, 0 on EOF or error. |
| */ |
| int |
| zxin(n,s,x) int n, x; char *s; { |
| #ifdef IKSD |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| int a, i; |
| a = ttchk(); |
| if (a < 1) return(0); |
| for (i = 0; i < a && i < x; i++) |
| s[i] = coninc(0); |
| return(i); |
| } |
| #endif /* IKSD */ |
| |
| return(fread(s, sizeof (char), x, fp[n])); |
| } |
| |
| /* |
| Z I N F I L L -- Buffered file input. |
| |
| (re)fill the file input buffer with data. All file input |
| should go through this routine, usually by calling the zminchar() |
| macro defined in ckcker.h. Returns: |
| |
| Value 0..255 on success, the character that was read. |
| -1 on end of file. |
| -2 on any kind of error other than end of file. |
| -3 timeout when reading from pipe (Kermit packet mode only). |
| */ |
| int |
| zinfill() { |
| extern int kactive, srvping; |
| errno = 0; |
| |
| #ifdef ZDEBUG |
| printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]); |
| #endif /* ZDEBUG */ |
| |
| #ifdef IKSD |
| if (inserver && !local && fp[ZIFILE] == stdin) { |
| int a, i; |
| a = ttchk(); |
| if (a < 0) return(-2); |
| for (i = 0; i < a && i < INBUFSIZE; i++) { |
| zinbuffer[i] = coninc(0); |
| } |
| zincnt = i; |
| /* set pointer to beginning, (== &zinbuffer[0]) */ |
| zinptr = zinbuffer; |
| if (zincnt == 0) return(-1); |
| zincnt--; /* One less char in buffer */ |
| return((int)(*zinptr++) & 0377); /* because we return the first */ |
| } |
| #endif /* IKSD */ |
| |
| debug(F101,"zinfill kactive","",kactive); |
| |
| if (!(kactive && ispipe[ZIFILE])) { |
| if (feof(fp[ZIFILE])) { |
| debug(F100,"ZINFILL feof","",0); |
| #ifdef ZDEBUG |
| printf("ZINFILL EOF\n"); |
| #endif /* ZDEBUG */ |
| return(-1); |
| } |
| } |
| clearerr(fp[ZIFILE]); |
| |
| #ifdef SELECT |
| /* Here we can call select() to get a timeout... */ |
| if (kactive && ispipe[ZIFILE]) { |
| int secs, z = 0; |
| #ifndef NOXFER |
| if (srvping) { |
| secs = 1; |
| debug(F101,"zinfill calling ttwait","",secs); |
| z = ttwait(fileno(fp[ZIFILE]),secs); |
| debug(F101,"zinfill ttwait","",z); |
| } |
| #endif /* NOXFER */ |
| if (z == 0) |
| return(-3); |
| } |
| #endif /* SELECT */ |
| |
| #ifdef DEBUG |
| if (deblog) { |
| int i; |
| debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE); |
| #ifdef USE_MEMCPY |
| memset(zinbuffer, 0xFF, INBUFSIZE); |
| #else |
| for (i = 0; i < INBUFSIZE; i++) { |
| zinbuffer[i] = 0xFF; |
| #ifdef COMMENT /* Too much! */ |
| debug(F101,"ZINFILL zinbuffer[i]","",i); |
| #endif /* COMMENT */ |
| } |
| #endif /* USE_MEMCPY */ |
| ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE); |
| debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); |
| } |
| #endif /* DEBUG */ |
| |
| /* |
| Note: The following read MUST be nonblocking when reading from a pipe |
| and we want timeouts to work. See zxcmd(). |
| */ |
| zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]); |
| debug(F101,"ZINFILL fread","",zincnt); /* Just the size */ |
| #ifdef ZDEBUG |
| printf("FREAD=%d\n",zincnt); |
| #endif /* ZDEBUG */ |
| #ifdef CK_CTRLZ |
| /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */ |
| if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) { |
| register int i; |
| for (i = 0; i < zincnt; i++) { |
| if (zinbuffer[i] == SUB) { |
| zincnt = i; /* Stop at first Ctrl-Z */ |
| if (i == 0) |
| return(-1); |
| break; |
| } |
| } |
| } |
| #endif /* CK_CTRLZ */ |
| |
| if (zincnt == 0) { /* Got nothing? */ |
| if (ferror(fp[ZIFILE])) { |
| debug(F100,"ZINFILL ferror","",0); |
| debug(F101,"ZINFILL errno","",errno); |
| #ifdef ZDEBUG |
| printf("ZINFILL errno=%d\n",errno); |
| #endif /* ZDEBUG */ |
| #ifdef EWOULDBLOCK |
| return((errno == EWOULDBLOCK) ? -3 : -2); |
| #else |
| return(-2); |
| #endif /* EWOULDBLOCK */ |
| } |
| |
| /* In case feof() didn't work just above -- sometimes it doesn't... */ |
| |
| if (feof(fp[ZIFILE]) ) { |
| debug(F100,"ZINFILL count 0 EOF return -1","",0); |
| return (-1); |
| } else { |
| debug(F100,"ZINFILL count 0 not EOF return -2","",0); |
| return(-2); |
| } |
| } |
| zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */ |
| zincnt--; /* One less char in buffer */ |
| return((int)(*zinptr++) & 0377); /* because we return the first */ |
| } |
| |
| /* Z S O U T -- Write a string out to the given file, buffered. */ |
| |
| int |
| zsout(n,s) int n; char *s; { |
| int rc = 0; |
| rc = chkfn(n); |
| if (rc < 1) return(-1); /* Keep this, prevents memory faults */ |
| if (!s) return(0); /* Null pointer, do nothing, succeed */ |
| if (!*s) return(0); /* empty string, ditto */ |
| |
| #ifdef IKSD |
| /* |
| This happens with client-side Kermit server when a REMOTE command |
| was sent from the server to the client and the server is supposed to |
| display the text, but of course there is no place to display it |
| since it is in remote mode executing Kermit protocol. |
| */ |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| #ifdef COMMENT |
| return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0); |
| #else |
| return(0); |
| #endif /* COMMENT */ |
| } |
| #endif /* IKSD */ |
| |
| if (n == ZSFILE) |
| return(write(fileno(fp[n]),s,(int)strlen(s))); |
| rc = fputs(s,fp[n]) == EOF ? -1 : 0; |
| if (n == ZWFILE) |
| fflush(fp[n]); |
| return(rc); |
| } |
| |
| /* Z S O U T L -- Write string to file, with line terminator, buffered */ |
| |
| int |
| zsoutl(n,s) int n; char *s; { |
| if (zsout(n,s) < 0) |
| return(-1); |
| |
| #ifdef IKSD |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| #ifdef COMMENT |
| return(ttoc(LF)); |
| #else |
| return(0); /* See comments in zsout() */ |
| #endif /* COMMENT */ |
| } |
| #endif /* IKSD */ |
| |
| if (n == ZSFILE) /* Session log is unbuffered */ |
| return(write(fileno(fp[n]),"\n",1)); |
| else if (fputs("\n",fp[n]) == EOF) |
| return(-1); |
| if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */ |
| fflush(fp[n]); |
| return(0); |
| } |
| |
| /* Z S O U T X -- Write x characters to file, unbuffered. */ |
| |
| int |
| zsoutx(n,s,x) int n, x; char *s; { |
| #ifdef IKSD |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| #ifdef COMMENT |
| return(ttol(s,x)); /* See comments in zsout() */ |
| #else |
| return(x); |
| #endif /* COMMENT */ |
| } |
| #endif /* IKSD */ |
| |
| #ifdef COMMENT |
| if (chkfn(n) < 1) return(-1); |
| return(write(fp[n]->_file,s,x)); |
| #endif /* COMMENT */ |
| return(write(fileno(fp[n]),s,x) == x ? x : -1); |
| } |
| |
| /* Z C H O U T -- Add a character to the given file. */ |
| |
| /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */ |
| |
| int |
| #ifdef CK_ANSIC |
| zchout(register int n, char c) |
| #else |
| zchout(n,c) register int n; char c; |
| #endif /* CK_ANSIC */ |
| /* zchout() */ { |
| /* if (chkfn(n) < 1) return(-1); */ |
| |
| #ifdef IKSD |
| if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) { |
| #ifdef COMMENT |
| return(ttoc(c)); |
| #else |
| return(0); /* See comments in zsout() */ |
| #endif /* COMMENT */ |
| } |
| #endif /* IKSD */ |
| |
| if (n == ZSFILE) /* Use unbuffered for session log */ |
| return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1); |
| /* Buffered for everything else */ |
| if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */ |
| return(ferror(fp[n])?-1:0); /* Check to make sure */ |
| else /* Otherwise... */ |
| return(0); /* There was no error. */ |
| } |
| |
| /* (PWP) buffered character output routine to speed up file IO */ |
| |
| int |
| zoutdump() { |
| int x; |
| char * zp; |
| zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */ |
| #ifdef DEBUG |
| if (deblog) |
| debug(F101,"zoutdump zoutcnt","",zoutcnt); |
| #endif /* DEBUG */ |
| if (zoutcnt == 0) { /* Nothing to output */ |
| return(0); |
| } else if (zoutcnt < 0) { /* Unexpected negative argument */ |
| zoutcnt = 0; /* Reset output buffer count */ |
| return(-1); /* and fail. */ |
| } |
| |
| #ifdef IKSD |
| if (inserver && !local && fp[ZOFILE] == stdout) { |
| #ifdef COMMENT |
| x = ttol(zoutbuffer,zoutcnt); |
| #else |
| x = 1; /* See comments in zsout() */ |
| #endif /* COMMENT */ |
| zoutcnt = 0; |
| return(x > 0 ? 0 : -1); |
| } |
| #endif /* IKSD */ |
| |
| /* |
| Frank Prindle suggested that replacing this fwrite() by an fflush() |
| followed by a write() would improve the efficiency, especially when |
| writing to stdout. Subsequent tests showed a 5-fold improvement. |
| */ |
| #ifdef COMMENT |
| if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ... |
| #endif /* COMMENT */ |
| |
| #ifndef CK_NONBLOCK |
| fflush(fp[ZOFILE]); |
| #endif /* CK_NONBLOCK */ |
| zp = zoutbuffer; |
| while (zoutcnt > 0) { |
| if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) { |
| #ifdef DEBUG |
| if (deblog) /* Save a function call... */ |
| debug(F101,"zoutdump wrote","",x); |
| #endif /* DEBUG */ |
| zoutcnt -= x; /* Adjust output buffer count */ |
| zp += x; /* and pointer */ |
| } else { |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"zoutdump write error","",errno); |
| debug(F101,"zoutdump write returns","",x); |
| } |
| #endif /* DEBUG */ |
| zoutcnt = 0; /* Reset output buffer count */ |
| return(-1); /* write() failed */ |
| } |
| } |
| return(0); |
| } |
| |
| /* C H K F N -- Internal function to verify file number is ok */ |
| |
| /* |
| Returns: |
| -1: File number n is out of range |
| 0: n is in range, but file is not open |
| 1: n in range and file is open |
| */ |
| int |
| chkfn(n) int n; { |
| /* if (n != ZDFILE) debug(F101,"chkfn","",n); */ |
| if (n < 0 || n >= ZNFILS) { |
| if (n != ZDFILE) debug(F101,"chkfn out of range","",n); |
| return(-1); |
| } else { |
| /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */ |
| return((fp[n] == NULL) ? 0 : 1); |
| } |
| } |
| |
| /* Z G E T F S -- Return file size regardless of accessibility */ |
| /* |
| Used for directory listings, etc. |
| Returns: |
| The size of the file in bytes, 0 or greater, if the size can be learned. |
| -1 if the file size can not be obtained. |
| Also (and this is a hack just for UNIX): |
| If the argument is the name of a symbolic link, |
| the global variable issymlink is set to 1, |
| and the global buffer linkname[] gets the link value. |
| And it sets zgfs_dir to 1 if it's a directory, otherwise 0. |
| This lets us avoid numerous redundant calls to stat(). |
| */ |
| int zgfs_link = 0; |
| int zgfs_dir = 0; |
| time_t zgfs_mtime = 0; |
| unsigned int zgfs_mode = 0; |
| |
| #ifdef CKSYMLINK |
| char linkname[CKMAXPATH+1]; |
| #ifndef _IFLNK |
| #define _IFLNK 0120000 |
| #endif /* _IFLNK */ |
| #endif /* CKSYMLINK */ |
| |
| long |
| zgetfs(name) char *name; { |
| struct stat buf; |
| char fnam[CKMAXPATH+4]; |
| long size = -1L; |
| int x; |
| int needrlink = 0; |
| char * s; |
| |
| if (!name) name = ""; |
| if (!*name) return(-1); |
| |
| #ifdef UNIX |
| x = strlen(name); |
| if (x == 9 && !strcmp(name,"/dev/null")) |
| return(0); |
| #endif /* UNIX */ |
| |
| s = name; |
| #ifdef DTILDE |
| if (*s == '~') { |
| s = tilde_expand(s); |
| if (!s) s = ""; |
| if (!*s) s = name; |
| } |
| #endif /* DTILDE */ |
| x = ckstrncpy(fnam,s,CKMAXPATH); |
| s = fnam; |
| debug(F111,"zgetfs fnam",s,x); |
| if (x > 0 && s[x-1] == '/') |
| s[x-1] = '\0'; |
| |
| zgfs_dir = 0; /* Assume it's not a directory */ |
| zgfs_link = 0; /* Assume it's not a symlink */ |
| zgfs_mtime = 0; /* No time yet */ |
| zgfs_mode = 0; /* No permission bits yet */ |
| |
| #ifdef CKSYMLINK /* We're doing symlinks? */ |
| #ifdef USE_LSTAT /* OK to use lstat()? */ |
| x = lstat(s,&buf); |
| debug(F101,"STAT","",1); |
| if (x < 0) /* stat() failed */ |
| return(-1); |
| if ( /* Now see if it's a symlink */ |
| #ifdef S_ISLNK |
| S_ISLNK(buf.st_mode) |
| #else |
| #ifdef _IFLNK |
| ((_IFMT & buf.st_mode) == _IFLNK) |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| ) { |
| zgfs_link = 1; /* It's a symlink */ |
| linkname[0] = '\0'; /* Get the name */ |
| x = readlink(s,linkname,CKMAXPATH); |
| debug(F101,"zgetfs readlink",s,x); |
| if (x > -1 && x < CKMAXPATH) { /* It's a link */ |
| linkname[x] = '\0'; |
| size = buf.st_size; /* Remember size of link */ |
| x = stat(s,&buf); /* Now stat the linked-to file */ |
| debug(F101,"STAT","",2); |
| if (x < 0) /* so we can see if it's a directory */ |
| return(-1); |
| } else { |
| ckstrncpy(linkname,"(lookup failed)",CKMAXPATH); |
| } |
| } |
| #else /* !USE_LSTAT */ |
| x = stat(s,&buf); /* No lstat(), use stat() instead */ |
| debug(F101,"STAT","",3); |
| if (x < 0) |
| return(-1); |
| #endif /* USE_LSTAT */ |
| |
| /* Do we need to call readlink()? */ |
| |
| #ifdef NOLINKBITS |
| /* |
| lstat() does not work in SCO operating systems. From "man NS lstat": |
| |
| lstat obtains information about the file named by path. In the case of a |
| symbolic link, lstat returns information about the link, and not the file |
| named by the link. It is only used by the NFS automount daemon and should |
| not be utilized by users. |
| */ |
| needrlink = 1; |
| debug(F101,"zgetfs forced needrlink","",needrlink); |
| #else |
| #ifdef S_ISLNK |
| needrlink = S_ISLNK(buf.st_mode); |
| debug(F101,"zgetfs S_ISLNK needrlink","",needrlink); |
| #else |
| #ifdef _IFLNK |
| needrlink = (_IFMT & buf.st_mode) == _IFLNK; |
| debug(F101,"zgetfs _IFLNK needrlink","",needrlink); |
| #else |
| needrlink = 1; |
| debug(F101,"zgetfs default needrlink","",needrlink); |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| #endif /* NOLINKBITS */ |
| |
| if (needrlink) { |
| linkname[0] = '\0'; |
| errno = 0; |
| x = readlink(s,linkname,CKMAXPATH); |
| #ifdef DEBUG |
| debug(F111,"zgetfs readlink",s,x); |
| if (x < 0) |
| debug(F101,"zgetfs readlink errno","",errno); |
| else |
| debug(F110,"zgetfs readlink result",linkname,0); |
| #endif /* DEBUG */ |
| if (x > -1 && x < CKMAXPATH) { |
| zgfs_link = 1; |
| linkname[x] = '\0'; |
| } |
| } |
| #else /* !CKSYMLINK */ |
| x = stat(s,&buf); /* Just stat the file */ |
| debug(F111,"zgetfs stat",s,x); |
| if (x < 0) /* and get the size */ |
| return(-1); |
| #endif /* CKSYMLINK */ |
| |
| zgfs_mtime = buf.st_mtime; |
| zgfs_mode = buf.st_mode; |
| zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */ |
| debug(F111,"zgetfs size",s,size); |
| debug(F111,"zgetfs st_size",s,buf.st_size); |
| return((size < 0L) ? buf.st_size : size); /* Return the size */ |
| } |
| |
| |
| /* Z C H K I -- Check if input file exists and is readable */ |
| |
| /* |
| Returns: |
| >= 0 if the file can be read (returns the size). |
| -1 if file doesn't exist or can't be accessed, |
| -2 if file exists but is not readable (e.g. a directory file). |
| -3 if file exists but protected against read access. |
| |
| For Berkeley Unix, a file must be of type "regular" to be readable. |
| Directory files, special files, and symbolic links are not readable. |
| */ |
| long |
| zchki(name) char *name; { |
| struct stat buf; |
| char * s; |
| int x, itsadir = 0; |
| extern int zchkid, diractive, matchfifo; |
| |
| if (!name) |
| return(-1); |
| x = strlen(name); |
| if (x < 1) |
| return(-1); |
| s = name; |
| |
| #ifdef UNIX |
| if (x == 9 && !strcmp(s,"/dev/null")) |
| return(0); |
| if (x == 8 && !strcmp(s,"/dev/tty")) |
| return(0); |
| #endif /* UNIX */ |
| |
| #ifdef DTILDE |
| if (*s == '~') { |
| s = tilde_expand(s); |
| if (!s) s = ""; |
| if (!*s) s = name; |
| } |
| #endif /* DTILDE */ |
| |
| #ifdef CKROOT |
| debug(F111,"zchki setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zchki setroot violation",name,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| x = stat(s,&buf); |
| debug(F101,"STAT","",5); |
| if (x < 0) { |
| debug(F111,"zchki stat fails",s,errno); |
| return(-1); |
| } |
| if (S_ISDIR (buf.st_mode)) |
| itsadir = 1; |
| |
| if (!(itsadir && zchkid)) { /* Unless this... */ |
| if (!S_ISREG (buf.st_mode) /* Must be regular file */ |
| #ifdef S_ISFIFO |
| && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */ |
| #endif /* S_ISFIFO */ |
| ) { |
| debug(F111,"zchki not regular file (or fifo)",s,matchfifo); |
| return(-2); |
| } |
| } |
| debug(F111,"zchki stat ok:",s,x); |
| |
| if (diractive) { /* If listing don't check access */ |
| x = 1; |
| } else { |
| #ifdef SW_ACC_ID |
| debug(F100,"zchki swapping ids for access()","",0); |
| priv_on(); |
| #endif /* SW_ACC_ID */ |
| if ((x = access(s,R_OK)) < 0) |
| x = access(s,X_OK); /* For RUN-class commands */ |
| #ifdef SW_ACC_ID |
| priv_off(); |
| debug(F100,"zchki swapped ids restored","",0); |
| #endif /* SW_ACC_ID */ |
| } |
| if (x < 0) { /* Is the file accessible? */ |
| debug(F111,"zchki access failed:",s,x); /* No */ |
| return(-3); |
| } else { |
| iflen = buf.st_size; /* Yes, remember size */ |
| ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */ |
| debug(F111,"zchki access ok:",s,iflen); |
| return((iflen > -1L) ? iflen : 0L); |
| } |
| } |
| |
| /* Z C H K O -- Check if output file can be created */ |
| |
| /* |
| Returns -1 if write permission for the file would be denied, 0 otherwise. |
| |
| NOTE: The design is flawed. There is no distinction among: |
| . Can I overwrite an existing file? |
| . Can I create a file (or directory) in an existing directory? |
| . Can I create a file (or directory) and its parent(s)? |
| */ |
| int |
| zchko(name) char *name; { |
| int i, x, itsadir = 0; |
| char *s; |
| char * oname; |
| extern int zchkod; /* Used by IF WRITEABLE */ |
| |
| debug(F110,"zchko entry",name,0); |
| |
| if (!name) return(-1); /* Watch out for null pointer. */ |
| |
| oname = name; |
| |
| #ifdef CKROOT |
| debug(F111,"zchko setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zchko setroot violation",name,0); |
| errno = EACCES; |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| x = (int)strlen(name); /* Get length of filename */ |
| debug(F111,"zchko len",name,x); |
| debug(F111,"zchko zchkod",name,zchkod); |
| |
| #ifdef UNIX |
| /* |
| Writing to null device is OK. |
| */ |
| if (x == 9 && !strcmp(name,"/dev/null")) |
| return(0); |
| if (x == 8 && !strcmp(name,"/dev/tty")) |
| return(0); |
| #endif /* UNIX */ |
| |
| s = name; |
| #ifdef DTILDE |
| if (*s == '~') { |
| s = tilde_expand(s); |
| if (!s) s = ""; |
| if (!*s) s = name; |
| x = strlen(s); |
| } |
| #endif /* DTILDE */ |
| name = s; |
| s = NULL; |
| /* |
| zchkod is a global flag meaning we're checking not to see if the directory |
| file is writeable, but if it's OK to create files IN the directory. |
| */ |
| if (!zchkod && isdir(name)) /* Directories are not writeable */ |
| return(-1); |
| |
| s = malloc(x+3); /* Must copy because we can't */ |
| if (!s) { /* write into our argument. */ |
| fprintf(stderr,"zchko: Malloc error 46\n"); |
| return(-1); |
| } |
| ckstrncpy(s,name,x+3); |
| |
| for (i = x; i > 0; i--) { /* Strip filename from right. */ |
| if (ISDIRSEP(s[i-1])) { |
| itsadir = 1; |
| break; |
| } |
| } |
| debug(F101,"zchko i","",i); |
| debug(F101,"zchko itsadir","",itsadir); |
| |
| #ifdef COMMENT |
| /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */ |
| if (i == 0) /* If no path, use current directory */ |
| strcpy(s,"./"); |
| else /* Otherwise, use given one. */ |
| s[i] = '\0'; |
| #else |
| #ifdef COMMENT |
| /* |
| The following does not work for "foo/bar" where the foo directory does |
| not exist even though we could create it: access("foo/.") fails, but |
| access("foo") works OK. |
| */ |
| /* So now we use "path/." if path given, or "." if no path given. */ |
| s[i++] = '.'; /* Append "." to path. */ |
| s[i] = '\0'; |
| #else |
| /* So NOW we strip path segments from the right as long as they don't */ |
| /* exist -- we only call access() for path segments that *do* exist.. */ |
| /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */ |
| /* succeeds when I have write access to foo and bar but baz doesn't exit.) */ |
| |
| if (itsadir && i > 0) { |
| s[i-1] = '\0'; |
| while (s[0] && !isdir(s)) { |
| for (i = (int)strlen(s); i > 0; i--) { |
| if (ISDIRSEP(s[i-1])) { |
| s[i-1] = '\0'; |
| break; |
| } |
| } |
| if (i == 0) |
| s[0] = '\0'; |
| } |
| } else { |
| s[i++] = '.'; /* Append "." to path. */ |
| s[i] = '\0'; |
| } |
| #endif /* COMMENT */ |
| #endif /* COMMENT */ |
| |
| if (!s[0]) |
| ckstrncpy(s,".",x+3); |
| |
| #ifdef SW_ACC_ID |
| debug(F100,"zchko swapping ids for access()","",0); |
| priv_on(); |
| #endif /* SW_ACC_ID */ |
| |
| x = access(s,W_OK); /* Check access of path. */ |
| |
| #ifdef SW_ACC_ID |
| priv_off(); |
| debug(F100,"zchko swapped ids restored","",0); |
| #endif /* SW_ACC_ID */ |
| |
| if (x < 0) |
| debug(F111,"zchko access failed:",s,errno); |
| else |
| debug(F111,"zchko access ok:",s,x); |
| free(s); /* Free temporary storage */ |
| |
| return((x < 0) ? -1 : 0); /* and return. */ |
| } |
| |
| /* Z D E L E T -- Delete the named file. */ |
| |
| /* Returns: -1 on error, 0 on success */ |
| |
| int |
| zdelet(name) char *name; { |
| int x; |
| #ifdef CK_LOGIN |
| if (isguest) |
| return(-1); |
| #endif /* CK_LOGIN */ |
| |
| #ifdef CKROOT |
| debug(F111,"zdelet setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zdelet setroot violation",name,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| x = unlink(name); |
| debug(F111,"zdelet",name,x); |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FC && ckxlogging) { |
| fullname[0] = '\0'; |
| zfnqfp(name,CKMAXPATH,fullname); |
| debug(F110,"zdelet fullname",fullname,0); |
| if (x < 0) |
| syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname); |
| else |
| syslog(LOG_INFO, "file[] %s: delete ok", fullname); |
| } |
| #endif /* CKSYSLOG */ |
| return(x); |
| } |
| |
| /* Z R T O L -- Convert remote filename into local form */ |
| |
| VOID |
| zrtol(name,name2) char *name, *name2; { |
| nzrtol(name,name2,1,0,CKMAXPATH); |
| } |
| |
| VOID |
| nzrtol(name,name2,fncnv,fnrpath,max) |
| char *name, *name2; int fncnv, fnrpath, max; |
| { /* nzrtol */ |
| char *s, *p; |
| int flag = 0, n = 0; |
| char fullname[CKMAXPATH+1]; |
| int devnull = 0; |
| int acase = 0; |
| if (!name2) return; |
| if (!name) name = ""; |
| |
| debug(F111,"nzrtol name",name,fncnv); |
| |
| #ifdef DTILDE |
| s = name; |
| if (*s == '~') { |
| s = tilde_expand(s); |
| if (!s) s = ""; |
| if (*s) name = s; |
| } |
| #endif /* DTILDE */ |
| |
| /* Handle the path -- we don't have to convert its format, since */ |
| /* the standard path format and our (UNIX) format are the same. */ |
| |
| fullname[0] = NUL; |
| devnull = !strcmp(name,"/dev/null"); |
| |
| if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */ |
| zstrip(name,&p); |
| strncpy(fullname,p,CKMAXPATH); |
| } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */ |
| strncpy(fullname,name,CKMAXPATH); |
| } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */ |
| ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL); |
| } else { /* Ditto */ |
| ckstrncpy(fullname,name,CKMAXPATH); |
| } |
| fullname[CKMAXPATH] = NUL; |
| debug(F110,"nzrtol fullname",fullname,0); |
| |
| #ifndef NOTRUNCATE |
| /* |
| The maximum length for any segment of a filename is MAXNAMLEN, defined |
| above. On some platforms (at least QNX) if a segment exceeds this limit, |
| the open fails with ENAMETOOLONG, so we must prevent it by truncating each |
| overlong name segment to the maximum segment length before passing the |
| name to open(). This must be done even when file names are literal, so as |
| not to halt a file transfer unnecessarily. |
| */ |
| { |
| char buf[CKMAXPATH+1]; /* New temporary buffer on stack */ |
| char *p = fullname; /* Source and */ |
| char *s = buf; /* destination pointers */ |
| int i = 0, n = 0; |
| debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN); |
| while (*p && n < CKMAXPATH) { /* Copy name to new buffer */ |
| if (++i > MAXNAMLEN) { /* If this segment too long */ |
| while (*p && *p != '/') /* skip past the rest... */ |
| p++; |
| i = 0; /* and reset counter. */ |
| } else if (*p == '/') { /* End of this segment. */ |
| i = 0; /* Reset counter. */ |
| } |
| *s++ = *p++; /* Copy this character. */ |
| n++; |
| } |
| *s = NUL; |
| ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */ |
| debug(F111,"nzrtol sizing",fullname,n); |
| } |
| #endif /* NOTRUNCATE */ |
| |
| if (!fncnv || devnull) { /* Not converting */ |
| ckstrncpy(name2,fullname,max); /* We're done. */ |
| return; |
| } |
| name = fullname; /* Converting */ |
| |
| p = name2; |
| for (; *name != '\0' && n < maxnam; name++) { |
| if (*name > SP) flag = 1; /* Strip leading blanks and controls */ |
| if (flag == 0 && *name < '!') |
| continue; |
| if (fncnv > 0) { |
| if (*name == SP) { |
| *p++ = '_'; |
| n++; |
| continue; |
| } |
| if (isupper(*name)) /* Check for mixed case */ |
| acase |= 1; |
| else if (islower(*name)) |
| acase |= 2; |
| } |
| *p++ = *name; |
| n++; |
| } |
| *p-- = '\0'; /* Terminate */ |
| while (*p < '!' && p > name2) /* Strip trailing blanks & controls */ |
| *p-- = '\0'; |
| |
| if (*name2 == '\0') { /* Nothing left? */ |
| ckstrncpy(name2,"NONAME",max); /* do this... */ |
| } else if (acase == 1) { /* All uppercase? */ |
| p = name2; /* So convert all letters to lower */ |
| while (*p) { |
| if (isupper(*p)) |
| *p = tolower(*p); |
| p++; |
| } |
| } |
| debug(F110,"nzrtol new name",name2,0); |
| } |
| |
| |
| /* Z S T R I P -- Strip device & directory name from file specification */ |
| |
| /* Strip pathname from filename "name", return pointer to result in name2 */ |
| |
| static char work[CKMAXPATH+1]; |
| |
| VOID |
| zstrip(name,name2) char *name, **name2; { |
| char *cp, *pp; |
| int n = 0; |
| |
| debug(F110,"zstrip before",name,0); |
| if (!name) { *name2 = ""; return; } |
| pp = work; |
| #ifdef DTILDE |
| /* Strip leading tilde */ |
| if (*name == '~') name++; |
| debug(F110,"zstrip after tilde-stripping",name,0); |
| #endif /* DTILDE */ |
| for (cp = name; *cp; cp++) { |
| if (ISDIRSEP(*cp)) { |
| pp = work; |
| n = 0; |
| } else { |
| *pp++ = *cp; |
| if (n++ >= CKMAXPATH) |
| break; |
| } |
| } |
| *pp = '\0'; /* Terminate the string */ |
| *name2 = work; |
| debug(F110,"zstrip after",*name2,0); |
| } |
| |
| /* Z L T O R -- Local TO Remote */ |
| |
| VOID |
| zltor(name,name2) char *name, *name2; { |
| nzltor(name,name2,1,0,CKMAXPATH); |
| } |
| |
| /* N Z L T O R -- New Local TO Remote */ |
| |
| /* |
| fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal. |
| */ |
| VOID |
| nzltor(name,name2,fncnv,fnspath,max) |
| char *name, *name2; int fncnv, fnspath, max; |
| { /* nzltor */ |
| char *cp, *pp; |
| #ifdef COMMENT |
| int dc = 0; |
| #endif /* COMMENT */ |
| int n = 0; |
| char *dotp = NULL; |
| char *dirp = NULL; |
| char fullname[CKMAXPATH+1]; |
| char *p; |
| CHAR c; |
| |
| #ifndef NOCSETS |
| extern int fcharset, /* tcharset, */ language; |
| int langsv; |
| _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */ |
| #ifdef CK_ANSIC |
| extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR); |
| #else |
| extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(); |
| #endif /* CK_ANSIC */ |
| langsv = language; |
| language = L_USASCII; |
| #ifdef COMMENT |
| /* Proper translation of filenames must be done elsewhere */ |
| n = tcharset ? tcharset : TC_USASCII; |
| sxo = xls[n][fcharset]; |
| #else |
| sxo = xls[TC_USASCII][fcharset]; |
| #endif /* COMMENT */ |
| #endif /* NOCSETS */ |
| |
| debug(F110,"nzltor name",name,0); |
| |
| /* Handle pathname */ |
| |
| fullname[0] = NUL; |
| if (fnspath == PATH_OFF) { /* PATHNAMES OFF */ |
| zstrip(name,&p); |
| ckstrncpy(fullname,p,CKMAXPATH); |
| } else { /* PATHNAMES RELATIVE or ABSOLUTE */ |
| char * p = name; |
| while (1) { |
| if (!strncmp(p,"../",3)) |
| p += 3; |
| else if (!strncmp(p,"./",2)) |
| p += 2; |
| else |
| break; |
| } |
| if (fnspath == PATH_ABS) { /* ABSOLUTE */ |
| zfnqfp(p,CKMAXPATH,fullname); |
| } else { /* RELATIVE */ |
| ckstrncpy(fullname,p,CKMAXPATH); |
| } |
| } |
| debug(F110,"nzltor fullname",fullname,0); |
| |
| if (!fncnv) { /* Not converting */ |
| ckstrncpy(name2,fullname,max); /* We're done. */ |
| #ifndef NOCSETS |
| langsv = language; |
| #endif /* NOCSETS */ |
| return; |
| } |
| name = fullname; /* Converting */ |
| |
| #ifdef aegis |
| char *namechars; |
| int tilde = 0, bslash = 0; |
| |
| if ((namechars = getenv("NAMECHARS")) != NULL) { |
| if (ckstrchr(namechars, '~' ) != NULL) tilde = '~'; |
| if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; |
| } else { |
| tilde = '~'; |
| bslash = '\\'; |
| } |
| #endif /* aegis */ |
| |
| pp = work; /* Output buffer */ |
| for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */ |
| c = *cp; |
| #ifndef NOCSETS |
| if (sxo) c = (*sxo)(c); /* Convert to ASCII */ |
| #endif /* NOCSETS */ |
| if (fncnv > 0 && islower(c)) /* Uppercase letters */ |
| *pp++ = toupper(c); /* Change tilde to hyphen */ |
| else if (c == '~') |
| *pp++ = '-'; |
| else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */ |
| *pp++ = 'X'; |
| else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */ |
| *pp++ = 'X'; |
| else if (c == ' ') /* Change space to underscore */ |
| *pp++ = '_'; |
| else if (c < ' ') /* Change controls to 'X' */ |
| *pp++ = 'X'; |
| else if (fncnv > 0 && c == '.') { /* Change dot to underscore */ |
| dotp = pp; /* Remember where we last did this */ |
| *pp++ = '_'; |
| } else { |
| if (c == '/') |
| dirp = pp; |
| *pp++ = c; |
| } |
| } |
| *pp = NUL; /* Tie it off. */ |
| #ifdef COMMENT |
| if (dotp) *dotp = '.'; /* Restore last dot (if any) */ |
| #else |
| if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */ |
| #endif /* COMMENT */ |
| cp = name2; /* If nothing before dot, */ |
| if (*work == '.') *cp++ = 'X'; /* insert 'X' */ |
| ckstrncpy(cp,work,max); |
| #ifndef NOCSETS |
| language = langsv; |
| #endif /* NOCSETS */ |
| debug(F110,"nzltor name2",name2,0); |
| } |
| |
| |
| /* Z C H D I R -- Change directory */ |
| /* |
| Call with: |
| dirnam = pointer to name of directory to change to, |
| which may be "" or NULL to indicate user's home directory. |
| Returns: |
| 0 on failure |
| 1 on success |
| */ |
| int |
| zchdir(dirnam) char *dirnam; { |
| char *hd, *sp; |
| #ifdef IKSDB |
| _PROTOTYP (int slotdir,(char *,char *)); |
| #endif /* IKSDB */ |
| #ifndef NOSPL |
| extern struct mtab *mactab; /* Main macro table */ |
| extern int nmac; /* Number of macros */ |
| #endif /* NOSPL */ |
| |
| debug(F110,"zchdir",dirnam,0); |
| if (!dirnam) dirnam = ""; |
| if (!*dirnam) /* If argument is null or empty, */ |
| dirnam = zhome(); /* use user's home directory. */ |
| sp = dirnam; |
| debug(F110,"zchdir 2",dirnam,0); |
| |
| #ifdef DTILDE |
| hd = tilde_expand(dirnam); /* Attempt to expand tilde */ |
| if (!hd) hd = ""; |
| if (*hd == '\0') hd = dirnam; /* in directory name. */ |
| #else |
| hd = dirnam; |
| #endif /* DTILDE */ |
| debug(F110,"zchdir 3",hd,0); |
| |
| #ifdef CKROOT |
| debug(F111,"zchdir setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(hd)) { |
| debug(F110,"zchdir setroot violation",hd,0); |
| return(0); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef pdp11 |
| /* Just to save some space */ |
| return((chdir(hd) == 0) ? 1 : 0); |
| #else |
| if (chdir(hd) == 0) { /* Try to cd */ |
| #ifdef IKSDB |
| #ifdef CK_LOGIN |
| if (inserver && ikdbopen) |
| slotdir(isguest ? anonroot : "", zgtdir()); |
| #endif /* CK_LOGIN */ |
| #endif /* IKSDB */ |
| |
| #ifndef NOSPL |
| if (nmac) { /* Any macros defined? */ |
| int k; /* Yes */ |
| static int on_cd = 0; |
| if (!on_cd) { |
| on_cd = 1; |
| k = mlook(mactab,"on_cd",nmac); /* Look this up */ |
| if (k >= 0) { /* If found, */ |
| if (dodo(k,zgtdir(),0) > -1) /* set it up, */ |
| parser(1); /* and execute it */ |
| } |
| on_cd = 0; |
| } |
| } |
| #endif /* NOSPL */ |
| return(1); |
| } |
| return(0); |
| #endif /* pdp11 */ |
| } |
| |
| int |
| #ifdef CK_ANSIC |
| zchkpid(unsigned long xpid) |
| #else |
| zchkpid(xpid) unsigned long xpid; |
| #endif /* CK_ANSIC */ |
| { |
| return((kill((PID_T)xpid,0) < 0) ? 0 : 1); |
| } |
| |
| |
| /* Z H O M E -- Return pointer to user's home directory */ |
| |
| static char * zhomdir = NULL; |
| |
| char * |
| zhome() { |
| char * home; |
| |
| #ifdef CKROOT |
| if (ckrootset) |
| return((char *)ckroot); |
| #endif /* CKROOT */ |
| |
| #ifdef Plan9 |
| home = getenv("home"); |
| #else |
| home = getenv("HOME"); |
| #endif /* Plan9 */ |
| makestr(&zhomdir,home); |
| return(home ? zhomdir : "."); |
| } |
| |
| /* Z G T D I R -- Returns a pointer to the current directory */ |
| |
| /* |
| The "preferred" interface for getting the current directory in modern UNIX |
| is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as |
| SunOS), it is implemented by forking a shell, feeding it the pwd command, |
| and returning the result, which is not only inefficient but also can result |
| in stray messages to the terminal. In such cases -- as well as when |
| getcwd() is not available at all -- getwd() can be used instead by defining |
| USE_GETWD. However, note that getwd() provides no buffer-length argument |
| and therefore no safeguard against memory leaks. |
| */ |
| #ifndef USE_GETWD |
| #ifdef BSD42 |
| #define USE_GETWD |
| #else |
| #ifdef SUNOS4 |
| #define USE_GETWD |
| #endif /* SUNOS4 */ |
| #endif /* BSD42 */ |
| #endif /* USE_GETWD */ |
| |
| #ifdef pdp11 |
| #define CWDBL 80 /* Save every byte we can... */ |
| #else |
| #define CWDBL CKMAXPATH |
| #endif /* pdp11 */ |
| static char cwdbuf[CWDBL+2]; |
| /* |
| NOTE: The getcwd() prototypes are commented out on purpose. If you get |
| compile-time warnings, search through your system's header files to see |
| which one has the needed prototype, and #include it. Usually it is |
| <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and |
| make any needed adjustments there (and report them). |
| */ |
| char * |
| zgtdir() { |
| char * buf = cwdbuf; |
| char * s; |
| |
| #ifdef USE_GETWD |
| extern char *getwd(); |
| s = getwd(buf); |
| debug(F110,"zgtdir BSD4 getwd()",s,0); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| #ifdef BSD44 |
| #ifdef DCLGETCWD |
| _PROTOTYP( char * getcwd, (char *, SIZE_T) ); |
| #endif /* DCLGETCWD */ |
| debug(F101,"zgtdir BSD44 CWDBL","",CWDBL); |
| s = getcwd(buf,CWDBL); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| #ifdef MINIX2 |
| #ifdef DCLGETCWD |
| _PROTOTYP( char * getcwd, (char *, SIZE_T) ); |
| #endif /* DCLGETCWD */ |
| debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL); |
| s = getcwd(buf,CWDBL); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| #ifdef SVORPOSIX |
| #ifdef COMMENT |
| /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */ |
| /* Anyway it's already prototyped in some header file that we have included. */ |
| extern char *getcwd(); |
| #else |
| #ifdef DCLGETCWD |
| _PROTOTYP( char * getcwd, (char *, SIZE_T) ); |
| #endif /* DCLGETCWD */ |
| #endif /* COMMENT */ |
| debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL); |
| s = getcwd(buf,CWDBL); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| #ifdef COHERENT |
| #ifdef _I386 |
| #ifdef DCLGETCWD |
| extern char *getcwd(); |
| #endif /* DCLGETCWD */ |
| debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL); |
| s = getcwd(buf,CWDBL); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| extern char *getwd(); |
| debug(F101,"zgtdir COHERENT CWDBL","",CWDBL); |
| s = getwd(buf); |
| if (!s) s = "./"; |
| return(s); |
| #endif /* _I386 */ |
| #else |
| #ifdef SUNOS4 |
| debug(F101,"zgtdir SUNOS CWDBL","",CWDBL); |
| s = getcwd(buf,CWDBL); |
| if (!s) s = "./"; |
| return(s); |
| #else |
| return("./"); |
| #endif /* SUNOS4 */ |
| #endif /* COHERENT */ |
| #endif /* SYSVORPOSIX */ |
| #endif /* MINIX2 */ |
| #endif /* BSD44 */ |
| #endif /* USE_GETWD */ |
| } |
| |
| /* Z X C M D -- Run a system command so its output can be read like a file */ |
| |
| #ifndef NOPUSH |
| int |
| zxcmd(filnum,comand) int filnum; char *comand; { |
| int out; |
| int pipes[2]; |
| extern int kactive; /* From ckcpro.w and ckcmai.c */ |
| |
| if (nopush) { |
| debug(F100,"zxcmd fails: nopush","",0); |
| return(-1); |
| } |
| debug(F111,"zxcmd",comand,filnum); |
| if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */ |
| if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */ |
| return(0); |
| |
| out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; |
| debug(F101,"zxcmd out",comand,out); |
| |
| /* Output to a command */ |
| |
| if (out) { /* Need popen() to do this. */ |
| ckstrncpy(fullname,"(pipe)",CKMAXPATH); |
| #ifdef NOPOPEN |
| return(0); /* no popen(), fail. */ |
| #else |
| /* Use popen() to run the command. */ |
| |
| #ifdef _POSIX_SOURCE |
| /* Strictly speaking, popen() is not available in POSIX.1 */ |
| #define DCLPOPEN |
| #endif /* _POSIX_SOURCE */ |
| |
| debug(F110,"zxcmd out",comand,0); |
| |
| if (priv_chk()) { |
| debug(F100,"zxcmd priv_chk failed","",0); |
| return(0); |
| } |
| errno = 0; |
| fp[filnum] = popen(comand,"w"); |
| debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno); |
| if (fp[filnum] == NULL) |
| return(0); |
| #ifdef COMMENT |
| /* I wonder what this is all about... */ |
| close(pipes[0]); /* Don't need the input side */ |
| fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */ |
| fp[ZSYSFN] = fp[filnum]; /* Remember. */ |
| #endif /* COMMENT */ |
| ispipe[filnum] = 1; |
| zoutcnt = 0; /* (PWP) reset input buffer */ |
| zoutptr = zoutbuffer; |
| return(1); |
| #endif /* NOPOPEN */ |
| } |
| |
| /* Input from a command */ |
| |
| #ifdef SNI541 |
| /* SINIX-L 5.41 does not like fdopen() */ |
| return(0); |
| #else |
| if (pipe(pipes) != 0) { |
| debug(F100,"zxcmd pipe failure","",0); |
| return(0); /* can't make pipe, fail */ |
| } |
| |
| /* Create a fork in which to run the named process */ |
| |
| if (( |
| #ifdef aegis |
| pid = vfork() /* child */ |
| #else |
| pid = fork() /* child */ |
| #endif /* aegis */ |
| ) == 0) { |
| |
| /* We're in the fork. */ |
| |
| char *shpath, *shname, *shptr; /* Find user's preferred shell */ |
| #ifndef aegis |
| struct passwd *p; |
| char *defshell; |
| #ifdef HPUX10 /* Default shell */ |
| defshell = "/usr/bin/sh"; |
| #else |
| #ifdef Plan9 |
| defshell = "/bin/rc"; |
| #else |
| defshell = "/bin/sh"; |
| #endif /* Plan9 */ |
| #endif /* HPUX10 */ |
| #endif /* aegis */ |
| if (priv_can()) exit(1); /* Turn off any privileges! */ |
| debug(F101,"zxcmd pid","",pid); |
| close(pipes[0]); /* close input side of pipe */ |
| close(0); /* close stdin */ |
| if (open("/dev/null",0) < 0) return(0); /* replace input by null */ |
| #ifndef OXOS |
| #ifndef SVORPOSIX |
| dup2(pipes[1],1); /* BSD: replace stdout & stderr */ |
| dup2(pipes[1],2); /* by the pipe */ |
| #else |
| close(1); /* AT&T: close stdout */ |
| if (dup(pipes[1]) != 1) /* Send stdout to the pipe */ |
| return(0); |
| close(2); /* Send stderr to the pipe */ |
| if (dup(pipes[1]) != 2) |
| return(0); |
| #endif /* SVORPOSIX */ |
| #else /* OXOS */ |
| dup2(pipes[1],1); |
| dup2(pipes[1],2); |
| #endif /* OXOS */ |
| close(pipes[1]); /* Don't need this any more. */ |
| |
| #ifdef aegis |
| if ((shpath = getenv("SERVERSHELL")) == NULL) |
| shpath = "/bin/sh"; |
| #else |
| shpath = getenv("SHELL"); /* What shell? */ |
| if (shpath == NULL) { |
| p = getpwuid( real_uid() ); /* Get login data */ |
| debug(F111,"zxcmd shpath","getpwuid()",p); |
| if (p == (struct passwd *)NULL || !*(p->pw_shell)) |
| shpath = defshell; |
| else shpath = p->pw_shell; |
| } |
| #endif /* aegis */ |
| shptr = shname = shpath; |
| while (*shptr != '\0') |
| if (*shptr++ == '/') |
| shname = shptr; |
| debug(F110,shpath,shname,0); |
| restorsigs(); /* Restore ignored signals */ |
| execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */ |
| exit(0); /* just punt if it failed. */ |
| } else if (pid == (PID_T) -1) { |
| debug(F100,"zxcmd fork failure","",0); |
| return(0); |
| } |
| debug(F101,"zxcmd pid","",pid); |
| close(pipes[1]); /* Don't need the output side */ |
| ispipe[filnum] = 1; /* Remember it's a pipe */ |
| fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */ |
| |
| #ifdef DONDELAY |
| #ifdef SELECT |
| if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */ |
| int flags, x; |
| if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) { |
| debug(F101,"zxcmd fcntl 1 pipe flags","",flags); |
| x = fcntl(fileno(fp[filnum]),F_SETFL, flags | |
| #ifdef QNX |
| O_NONBLOCK |
| #else |
| O_NDELAY |
| #endif /* QNX */ |
| ); |
| debug(F101,"zxcmd fcntl 2 result","",x); |
| } |
| } |
| #endif /* SELECT */ |
| #endif /* DONDELAY */ |
| #endif /* SNI541 */ |
| fp[ZSYSFN] = fp[filnum]; /* Remember. */ |
| zincnt = 0; /* (PWP) reset input buffer */ |
| zinptr = zinbuffer; |
| fullname[0] = '\0'; |
| return(1); |
| } /* zxcmd */ |
| |
| /* Z C L O S F - wait for the child fork to terminate and close the pipe. */ |
| |
| /* Used internally by zclose - returns -1 on failure, 1 on success. */ |
| |
| int |
| zclosf(filnum) int filnum; { |
| int wstat, out; |
| int statusp; |
| |
| debug(F101,"zclosf filnum","",filnum); |
| out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ; |
| debug(F101,"zclosf out","",out); |
| |
| #ifndef NOPOPEN |
| if (ispipe[filnum] |
| /* In UNIX we use popen() only for output files */ |
| && out |
| ) { |
| int x; |
| x = pclose(fp[filnum]); |
| pexitstat = x >> 8; |
| debug(F101,"zclosf pclose","",x); |
| debug(F101,"zclosf pexitstat","",pexitstat); |
| fp[filnum] = fp[ZSYSFN] = NULL; |
| ispipe[filnum] = 0; |
| return((x != 0) ? -1 : 1); |
| } |
| #endif /* NOPOPEN */ |
| debug(F101,"zclosf fp[filnum]","", fp[filnum]); |
| debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); |
| |
| if (pid != (PID_T) 0) { |
| debug(F101,"zclosf killing pid","",pid); |
| #ifdef Plan9 |
| kill(pid, SIGKILL); |
| #else |
| kill(pid,9); |
| #endif /* Plan9 */ |
| |
| #ifndef CK_CHILD |
| /* |
| This is the original code (before 20 April 1997) and has proven totally |
| portable. But it does not give us the process's return code. |
| */ |
| while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ; |
| #else |
| /* Here we try to get the return code. Let's hope this is portable too. */ |
| while ((wstat = wait(&statusp)) != pid && wstat != -1) ; |
| pexitstat = (statusp & 0xff) ? statusp : statusp >> 8; |
| debug(F101,"zclosf wait statusp","",statusp); |
| debug(F101,"zclosf wait pexitstat","",pexitstat); |
| #endif /* CK_CHILD */ |
| pid = 0; |
| } |
| fclose(fp[filnum]); |
| fp[filnum] = fp[ZSYSFN] = NULL; |
| |
| ispipe[filnum] = 0; |
| debug(F101,"zclosf fp[filnum]","",fp[filnum]); |
| #ifdef CK_CHILD |
| return(pexitstat == 0 ? 1 : -1); |
| #else |
| return(1); |
| #endif /* CK_CHILD */ |
| } |
| |
| #else /* NOPUSH */ |
| |
| int |
| zxcmd(filnum,comand) int filnum; char *comand; { |
| return(0); |
| } |
| int |
| zclosf(filnum) int filnum; { |
| return(EOF); |
| } |
| #endif /* NOPUSH */ |
| |
| |
| /* Z X P A N D -- Expand a wildcard string into an array of strings */ |
| /* |
| As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this |
| function is only used internally. See nzxpand() below. |
| |
| Returns the number of files that match fnarg, with data structures set up |
| so that first file (if any) will be returned by the next znext() call. |
| |
| Depends on external variable wildxpand: 0 means we expand wildcards |
| internally, nonzero means we call the shell to do it. |
| */ |
| static int xdironly = 0; |
| static int xfilonly = 0; |
| static int xmatchdot = 0; |
| static int xrecursive = 0; |
| static int xnobackup = 0; |
| static int xnolinks = 0; |
| |
| static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */ |
| static int remlen; /* Remaining space in caller's array */ |
| static int numfnd = 0; /* Number of matches found */ |
| |
| #define MINSPACE 1024 |
| |
| static int |
| initspace(resarry,len) char * resarry[]; int len; { |
| #ifdef DYNAMIC |
| if (len < MINSPACE) len = MINSPACE; |
| if (!sspace) { /* Need to allocate string space? */ |
| while (len >= MINSPACE) { |
| if ((sspace = malloc(len+2))) { /* Got it. */ |
| debug(F101,"fgen string space","",len); |
| break; |
| } |
| len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */ |
| } |
| if (len <= MINSPACE) { /* Did we get it? */ |
| fprintf(stderr,"fgen can't malloc string space\n"); |
| return(-1); |
| } |
| ssplen = len; |
| } |
| #endif /* DYNAMIC */ |
| |
| freeptr = sspace; /* This is where matches are copied. */ |
| resptr = resarry; /* Static copies of these so */ |
| remlen = len; /* recursive calls can alter them. */ |
| debug(F101,"initspace ssplen","",ssplen); |
| return(0); |
| } |
| |
| /* |
| Z S E T F I L -- Query or change the size of file list buffers. |
| |
| fc = 1: Change current string space to n, return new size. |
| fc = 2: Return current string space size. |
| fc = 3: Change current maxnames to n, return new maxnames. |
| fc = 4: Return current maxnames. |
| Returns < 0 on error. |
| */ |
| int |
| zsetfil(n, fc) int n, fc; { |
| #ifdef DYNAMIC |
| switch (fc) { |
| case 1: /* Stringspace */ |
| if (sspace) { |
| free(sspace); |
| sspace = NULL; |
| } |
| if (initspace(mtchs,n) < 0) |
| return(-1); |
| case 2: /* Fall thru deliberately */ |
| return(ssplen); |
| case 3: /* Listsize */ |
| if (mtchs) { |
| free((char *)mtchs); |
| mtchs = NULL; |
| } |
| mtchs = (char **)malloc(n * sizeof(char *)); |
| if (!mtchs) |
| return(-1); |
| maxnames = n; |
| case 4: /* Fall thru deliberately */ |
| return(maxnames); |
| } |
| #endif /* DYNAMIC */ |
| return(-1); |
| } |
| |
| |
| |
| #ifndef NONZXPAND |
| #ifndef pdp11 |
| static |
| #endif /* pdp11 */ |
| #endif /* NONZXPAND */ |
| int |
| zxpand(fnarg) char *fnarg; { |
| extern int diractive; |
| char fnbuf[CKMAXPATH+8], * fn, * p; |
| |
| #ifdef DTILDE /* Built with tilde-expansion? */ |
| char *tnam; |
| #endif /* DTILDE */ |
| int x; |
| int haveonedir = 0; |
| |
| if (!fnarg) { /* If no argument provided */ |
| nxpand = fcount = 0; |
| return(0); /* Return zero files found */ |
| } |
| debug(F110,"zxpand entry",fnarg,0); |
| debug(F101,"zxpand xdironly","",xdironly); |
| debug(F101,"zxpand xfilonly","",xfilonly); |
| |
| if (!*fnarg) { /* If no argument provided */ |
| nxpand = fcount = 0; |
| return(0); /* Return zero files found */ |
| } |
| |
| #ifdef CKROOT |
| debug(F111,"zxpand setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(fnarg)) { |
| debug(F110,"zxpand setroot violation",fnarg,0); |
| nxpand = fcount = 0; |
| return(0); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef COMMENT |
| /* |
| This would have been perfect, except it makes us return fully qualified |
| pathnames for all files. |
| */ |
| zfnqfp(fnarg,CKMAXPATH,fnbuf); |
| debug(F110,"zxpand zfnqfp",fnbuf,0); |
| s = zgtdir(); |
| debug(F110,"zxpand zgtdir",s,0); |
| p = fnbuf; |
| while (*p && *s) /* Make it relative */ |
| if (*s++ != *p++) |
| break; |
| fn = (*s) ? fnbuf : p; |
| debug(F110,"zxpand fn 0",fn,0); |
| if (!*fn) { |
| fn = fnbuf; |
| fnbuf[0] = '*'; |
| fnbuf[1] = '\0'; |
| } |
| debug(F110,"zxpand fn 0.5",fn,0); |
| #else |
| #ifdef DTILDE /* Built with tilde-expansion? */ |
| if (*fnarg == '~') { /* Starts with tilde? */ |
| tnam = tilde_expand(fnarg); /* Try to expand it. */ |
| ckstrncpy(fnbuf,tnam,CKMAXPATH); |
| } else |
| #endif /* DTILDE */ |
| ckstrncpy(fnbuf,fnarg,CKMAXPATH); |
| fn = fnbuf; /* Point to what we'll work with */ |
| #endif /* COMMENT */ |
| debug(F110,"zxpand fn 1",fn,0); |
| |
| if (!*fn) /* But make sure something is there */ |
| return(0); |
| |
| p = fn + (int)strlen(fn) - 1; |
| if (*p == '/') { /* If last char = / it must be a dir */ |
| if (!xfilonly && !iswild(p)) haveonedir++; |
| ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */ |
| } else if (p > fn) { /* If ends in "/." */ |
| if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */ |
| *p = '*'; |
| } else if (p == fn) { /* If it's '.' alone */ |
| if (*p == '.') /* change '.' to '*' */ |
| *p = '*'; |
| } |
| debug(F110,"zxpand fn 2",fn,0); |
| x = isdir(fn); /* Is it a directory? */ |
| debug(F111,"zxpand isdir 1",fn,x); |
| if (x) { /* If so, make it into a wildcard */ |
| if (!xfilonly && !iswild(p)) |
| haveonedir++; |
| if ((x = strlen(fn)) > 0) { |
| if (!ISDIRSEP(fn[x-1])) |
| fn[x++] = DIRSEP; |
| fn[x++] = '*'; |
| fn[x] = '\0'; |
| } |
| } |
| debug(F111,"zxpand fn 3",fn,haveonedir); |
| /* |
| The following allows us to parse a single directory name without opening |
| the directory and looking at its contents. The diractive flag is a horrible |
| hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd |
| have to change the API. |
| */ |
| if (!diractive && haveonedir) { |
| #ifdef COMMENT |
| fcount = (mtchs == NULL && |
| (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) |
| ? 0 : 1; |
| #else |
| fcount = 0; |
| if (!mtchs) { |
| mtchs = (char **)malloc(maxnames * sizeof(*mtchs)); |
| if (mtchs) |
| fcount = 1; |
| if (!fcount) |
| return(nxpand = fcount); |
| } |
| #endif /* COMMENT */ |
| debug(F110,"zxpand haveonedir A1",fnarg,0); |
| initspace(mtchs,ssplen); |
| addresult(fnarg,1); |
| if (numfnd < 0) return(-1); |
| mtchptr = mtchs; /* Save pointer for next. */ |
| debug(F110,"zxpand haveonedir A2",*mtchptr,0); |
| return(nxpand = fcount); |
| } |
| |
| #ifndef NOPUSH |
| if (!nopush && wildxpand) /* Who is expanding wildcards? */ |
| fcount = (mtchs == NULL && /* Shell */ |
| (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) |
| ? 0 |
| : shxpand(fn,mtchs,maxnames); |
| else |
| #endif /* NOPUSH */ |
| fcount = (mtchs == NULL && /* Kermit */ |
| (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL) |
| ? 0 |
| : fgen(fn,mtchs,maxnames); /* Look up the file. */ |
| |
| if (fcount == 0 && haveonedir) { |
| fcount = 1; |
| debug(F110,"zxpand haveonedir B",fnarg,0); |
| addresult(fnarg,1); |
| if (numfnd < 0) return(-1); |
| } |
| mtchptr = mtchs; /* Save pointer for next. */ |
| nxpand = fcount; |
| |
| #ifdef DEBUG |
| if (deblog) { |
| if (fcount > 1) |
| debug(F111,"zxpand ok",mtchs[0],fcount); |
| else |
| debug(F101,"zxpand fcount","",fcount); |
| } |
| #endif /* DEBUG */ |
| return(fcount); |
| } |
| |
| #ifndef NONZXPAND |
| /* N Z X P A N D -- Expand a file list, with options. */ |
| /* |
| Call with: |
| s = pointer to filename or pattern. |
| flags = option bits: |
| |
| flags & ZX_FILONLY Match regular files |
| flags & ZX_DIRONLY Match directories |
| flags & ZX_RECURSE Descend through directory tree |
| flags & ZX_MATCHDOT Match "dot files" |
| flags & ZX_NOBACKUP Don't match "backup files" |
| flags & ZX_NOLINKS Don't follow symlinks. |
| |
| Returns the number of files that match s, with data structures set up |
| so that first file (if any) will be returned by the next znext() call. |
| */ |
| int |
| nzxpand(s,flags) char * s; int flags; { |
| char * p; |
| int x; |
| |
| debug(F111,"nzxpand",s,flags); |
| x = flags & (ZX_DIRONLY|ZX_FILONLY); |
| xdironly = (x == ZX_DIRONLY); |
| xfilonly = (x == ZX_FILONLY); |
| if (xdironly && xfilonly) { |
| xdironly = 0; |
| xfilonly = 0; |
| } |
| xmatchdot = (flags & ZX_MATCHDOT); |
| debug(F111,"nzxpand xmatchdot 1",s,xmatchdot); |
| /* If xmatchdot not set by caller but pattern implies it, set it anyway */ |
| if (!xmatchdot && ((p = ckstrchr(s,'.')))) { |
| if (p == s && p[1] != '/') { |
| xmatchdot = 1; |
| debug(F111,"nzxpand xmatchdot 2",s,xmatchdot); |
| } else if (p > s) { |
| xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/'); |
| debug(F111,"nzxpand xmatchdot 3",s,xmatchdot); |
| } |
| } |
| xrecursive = (flags & ZX_RECURSE); |
| xnobackup = (flags & ZX_NOBACKUP); |
| xnolinks = (flags & ZX_NOLINKS); |
| |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"nzxpand xdironly","",xdironly); |
| debug(F101,"nzxpand xfilonly","",xfilonly); |
| debug(F101,"nzxpand xmatchdot","",xmatchdot); |
| debug(F101,"nzxpand xrecursive","",xrecursive); |
| debug(F101,"nzxpand xnobackup","",xnobackup); |
| debug(F101,"nzxpand xnolinks","",xnolinks); |
| } |
| #endif /* DEBUG */ |
| |
| x = zxpand(s); |
| if (x > 1) |
| sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */ |
| xdironly = 0; |
| xfilonly = 0; |
| xmatchdot = 0; |
| xrecursive = 0; |
| xnobackup = 0; |
| xnolinks = 0; |
| return(x); |
| } |
| #endif /* NONZXPAND */ |
| |
| #ifndef NOZXREWIND |
| /* Z X R E W I N D -- Rewinds the zxpand() list */ |
| |
| int |
| zxrewind() { |
| /* if (!mtchs) return(-1); */ |
| fcount = nxpand; |
| mtchptr = mtchs; |
| return(nxpand); |
| } |
| #endif /* NOZXREWIND */ |
| |
| /* Z N E X T -- Get name of next file from list created by zxpand(). */ |
| /* |
| Returns >0 if there's another file, with its name copied into the arg string, |
| or 0 if no more files in list. |
| */ |
| int |
| znext(fn) char *fn; { |
| if (fcount-- > 0) { |
| ckstrncpy(fn,*mtchptr++,CKMAXPATH); |
| } else { |
| fn[0] = '\0'; |
| } |
| #ifndef COMMENT |
| debug(F111,"znext",fn,fcount+1); |
| return(fcount+1); |
| #else |
| debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */ |
| return(fcount); |
| #endif /* COMMENT */ |
| } |
| |
| /* Z C H K S P A -- Check if there is enough space to store the file */ |
| |
| /* |
| Call with file specification f, size n in bytes. |
| Returns -1 on error, 0 if not enough space, 1 if enough space. |
| */ |
| /*ARGSUSED*/ |
| int |
| #ifdef CK_ANSIC |
| zchkspa(char *f, long n) |
| #else |
| zchkspa(f,n) char *f; long n; |
| #endif /* CK_ANSIC */ |
| /* zchkspa() */ { |
| /* In UNIX there is no good (and portable) way. */ |
| return(1); /* Always say OK. */ |
| } |
| |
| #ifdef COMMENT /* (not used) */ |
| |
| /* I S B A C K U P -- Tells if given file has a backup suffix */ |
| /* |
| Returns: |
| -1: Invalid argument |
| 0: File does not have a backup suffix |
| >0: Backup suffix number |
| */ |
| int |
| isbackup(fn) char * fn; { /* Get backup suffix number */ |
| int i, j, k, x, state, flag; |
| |
| if (!fn) /* Watch out for null pointers. */ |
| return(-1); |
| if (!*fn) /* And empty names. */ |
| return(-1); |
| |
| flag = state = 0; |
| for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) { |
| switch (state) { |
| case 0: /* State 0 - final char */ |
| if (fn[i] == '~') /* Is tilde */ |
| state = 1; /* Switch to next state */ |
| else /* Otherwise */ |
| flag = 1; /* Quit - no backup suffix. */ |
| break; |
| case 1: /* State 1 - digits */ |
| if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */ |
| return(atoi(&fn[i+1])); |
| } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */ |
| continue; /* Keep going */ |
| } else { /* Something else */ |
| flag = 1; /* Not a backup suffix - quit. */ |
| } |
| break; |
| } |
| } |
| return(0); |
| } |
| #endif /* COMMENT */ |
| |
| |
| /* Z N E W N -- Make a new name for the given file */ |
| |
| /* |
| Given the name, fn, of a file that already exists, this function builds a |
| new name of the form "<oldname>.~<n>~", where <oldname> is argument name |
| (fn), and <n> is a version number, one higher than any existing version |
| number for that file, up to 99999. This format is consistent with that used |
| by GNU EMACS. If the constructed name is too long for the system's maximum, |
| enough characters are truncated from the end of <fn> to allow the version |
| number to fit. If no free version numbers exist between 1 and 99999, a |
| version number of "xxxx" is used. Returns a pointer to the new name in |
| argument s. |
| */ |
| #ifdef pdp11 |
| #define ZNEWNBL 63 /* Name buffer length */ |
| #define ZNEWNMD 3 /* Max digits for version number */ |
| #else |
| #define ZNEWNBL CKMAXPATH |
| #define ZNEWNMD 4 |
| #endif /* pdp11 */ |
| |
| #define MAXBUDIGITS 5 |
| |
| static char znewbuf[ZNEWNBL+12]; |
| |
| VOID |
| znewn(fn,s) char *fn, **s; { |
| char * buf; /* Pointer to buffer for new name */ |
| char * xp, * namepart = NULL; /* Pointer to filename part */ |
| struct zfnfp * fnfp; /* znfqfp() result struct pointer */ |
| int d = 0, t, fnlen, buflen; |
| int n, i, k, flag, state; |
| int max = MAXNAMLEN; /* Maximum name length */ |
| char * dname = NULL; |
| |
| buf = znewbuf; |
| *s = NULL; /* Initialize return value */ |
| if (!fn) fn = ""; /* Check filename argument */ |
| i = strlen(fn); |
| |
| /* If incoming file already has a backup suffix, remove it. */ |
| /* Then we'll tack a new on later, which will be the highest for this file. */ |
| |
| if (i <= max && i > 0 && fn[i-1] == '~') { |
| char * p; |
| i--; |
| debug(F111,"znewn suffix removal",fn,i); |
| if ((dname = (char *)malloc(i+1))) { |
| ckstrncpy(dname,fn,i+1); |
| p = dname; |
| for (flag = state = 0; (!flag && (i > 0)); i--) { |
| switch (state) { |
| case 0: /* State 0 - final char */ |
| if (p[i] == '~') /* Is tilde */ |
| state = 1; /* Switch to next state */ |
| else /* Otherwise */ |
| flag = 1; /* Quit - no backup suffix. */ |
| break; |
| case 1: /* State 1 - digits */ |
| if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */ |
| p[i-1] = NUL; /* Trim it */ |
| fn = dname; |
| debug(F111,"znewn suffix removal 2",fn,i); |
| flag = 1; /* done */ |
| } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */ |
| continue; /* Keep going */ |
| } else { /* Something else */ |
| flag = 1; /* Not a backup suffix - quit. */ |
| } |
| break; |
| } |
| } |
| } |
| } |
| if ((fnlen = strlen(fn)) < 1) { /* Get length */ |
| if (dname) free(dname); |
| return; |
| } |
| debug(F111,"znewn",fn,fnlen); |
| |
| debug(F101,"znewn max 1","",max); |
| if (max < 14) max = 14; /* Make max reasonable for any UNIX */ |
| if (max > ZNEWNBL) max = ZNEWNBL; |
| debug(F101,"znewn max 2","",max); |
| |
| if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */ |
| namepart = fnfp->fname; /* Isolate the filename */ |
| k = strlen(fn); /* Length of name part */ |
| debug(F111,"znewn namepart",namepart,k); |
| } else { |
| if (dname) free(dname); |
| return; |
| } |
| buflen = fnfp->len; /* Length of fully qualified name */ |
| debug(F111,"znewn len",buf,buflen); |
| |
| if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */ |
| /* Make pattern for backup names */ |
| ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen); |
| n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */ |
| debug(F111,"znewn A matches",buf,n); |
| while (n-- > 0) { /* Find any existing name.~n~ files */ |
| xp = *mtchptr++; /* Point at matching name */ |
| t = atoi(xp+buflen+2); /* Get number */ |
| if (t > d) d = t; /* Save d = highest version number */ |
| } |
| sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */ |
| debug(F110,"znewn A newname",buf,0); |
| } else { /* Backup name would be too long */ |
| int xlen; /* So we have to eat back into it */ |
| int delta; |
| char buf2[ZNEWNBL+12]; |
| |
| delta = max - k; |
| debug(F101,"znewn B delta","",delta); |
| |
| for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */ |
| ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */ |
| xlen = buflen - i - 3 + delta; /* how many digits are in the */ |
| ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */ |
| n = nzxpand(buf2,ZX_FILONLY); |
| debug(F111,"znewn B matches",buf2,n); |
| if (n > 0) |
| break; |
| } |
| while (n-- > 0) { /* Find any existing name.~n~ files */ |
| xp = *mtchptr++; /* Point at matching name */ |
| t = atoi(xp+xlen+2); /* Get number */ |
| if (t > d) d = t; /* Save d = highest version number */ |
| } |
| if (d > 0) /* If the odometer turned over... */ |
| if ((d % 10) == 9) /* back up one space. */ |
| xlen--; |
| sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */ |
| ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */ |
| debug(F110,"znewn B new name",buf,0); |
| } |
| *s = buf; /* Point to new name */ |
| ck_znewn = d+1; /* Also make it available globally */ |
| if (dname) free(dname); |
| return; |
| } |
| |
| /* Z R E N A M E -- Rename a file */ |
| /* |
| Call with old and new names. |
| If new name is the name of a directory, the 'old' file is moved to |
| that directory. |
| Returns 0 on success, -1 on failure. |
| */ |
| int |
| zrename(old,new) char *old, *new; { |
| char *p, *s; |
| int x; |
| |
| if (!old) old = ""; |
| if (!new) new = ""; |
| debug(F110,"zrename old",old,0); |
| debug(F110,"zrename new",new,0); |
| if (!*old) return(-1); |
| if (!*new) return(-1); |
| |
| #ifdef IKSD |
| #ifdef CK_LOGIN |
| if (inserver && isguest) |
| return(-1); |
| #endif /* CK_LOGIN */ |
| #endif /* IKSD */ |
| |
| #ifdef CKROOT |
| debug(F111,"zrename setroot",ckroot,ckrootset); |
| if (ckrootset) { |
| if (!zinroot(old)) { |
| debug(F110,"zrename old: setroot violation",old,0); |
| return(-1); |
| } |
| if (!zinroot(new)) { |
| debug(F110,"zrename new: setroot violation",new,0); |
| return(-1); |
| } |
| } |
| #endif /* CKROOT */ |
| |
| p = NULL; |
| s = new; |
| |
| if (isdir(new)) { |
| char *q = NULL; |
| x = strlen(new); |
| if (!(p = malloc(strlen(new) + strlen(old) + 2))) |
| return(-1); |
| strcpy(p,new); /* (safe) Directory part */ |
| if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */ |
| strcat(p,"/"); /* (safe) */ |
| zstrip(old,&q); /* Strip path part from old name */ |
| strcat(p,q); /* cat to new directory (safe) */ |
| s = p; |
| debug(F110,"zrename dir",s,0); |
| } |
| #ifdef DEBUG |
| else debug(F110,"zrename no dir",s,0); |
| #endif /* DEBUG */ |
| |
| #ifdef IKSD |
| if (inserver && (!ENABLED(en_del))) { |
| if (zchki(s) > -1) /* Destination file exists? */ |
| return(-1); |
| } |
| #endif /* IKSD */ |
| |
| x = -1; /* Return code. */ |
| #ifdef RENAME |
| /* Atomic, preferred, uses a single system call, rename(), if available. */ |
| x = rename(old,s); |
| debug(F111,"zrename rename()",old,x); |
| if (x) x = -1; |
| #endif /* RENAME */ |
| |
| /* If rename() failed or not available try link()/unlink() */ |
| |
| if (x < 0) { |
| if (zchko(old) > -1) { /* Requires write access to orignal */ |
| x = link(old,s); |
| debug(F111,"zrename link()",old,x); |
| if (x > -1) { /* Make a link with the new name. */ |
| x = unlink(old); |
| debug(F111,"zrename unlink()",old,x); |
| } |
| /* If link/unlink failed copy and delete */ |
| if (x < 0) { |
| x = zcopy(old,s); |
| debug(F111,"zrename zcopy()",old,x); |
| if (x > -1) { |
| x = zdelet(old); |
| debug(F111,"zrename zdelet()",old,x); |
| } |
| } |
| } |
| } |
| fullname[0] = '\0'; /* Clear this out for next time. */ |
| |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_FC && ckxlogging) { |
| zfnqfp(old,CKMAXPATH,fullname); |
| tmp2[0] = '\0'; |
| zfnqfp(s,CKMAXPATH,tmp2); |
| if (x > -1) |
| syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2); |
| else |
| syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2); |
| } |
| #endif /* CKSYSLOG */ |
| |
| if (p) free(p); |
| return(x); |
| } |
| |
| /* Z C O P Y -- Copy a single file. */ |
| /* |
| Call with source and destination names. |
| If destination is a directory, the source file is |
| copied to that directory with its original name. |
| Returns: |
| 0 on success. |
| <0 on failure: |
| -2 = source file is not a regular file. |
| -3 = source file not found. |
| -4 = permission denied. |
| -5 = source and destination are the same file. |
| -6 = i/o error. |
| -1 = other error. |
| */ |
| int |
| zcopy(source,destination) char *source, *destination; { |
| char *src, *dst; /* Local pointers to filenames */ |
| int x, y, rc; /* Workers */ |
| int in = -1, out = -1; /* i/o file descriptors */ |
| struct stat srcbuf; /* Source file info buffer */ |
| int perms; /* Output file permissions */ |
| char buf[1024]; /* File copying buffer */ |
| |
| if (!source) source = ""; |
| if (!destination) destination = ""; |
| |
| debug(F110,"zcopy src arg",source,0); |
| debug(F110,"zcopy dst arg",destination,0); |
| |
| if (!*source) return(-1); |
| if (!*destination) return(-1); |
| |
| #ifdef IKSD |
| #ifdef CK_LOGIN |
| if (inserver && isguest) |
| return(-4); |
| #endif /* CK_LOGIN */ |
| #endif /* IKSD */ |
| |
| #ifdef CKROOT |
| debug(F111,"zcopy setroot",ckroot,ckrootset); |
| if (ckrootset) { |
| if (!zinroot(source)) { |
| debug(F110,"zcopy source: setroot violation",source,0); |
| return(-1); |
| } |
| if (!zinroot(destination)) { |
| debug(F110,"zcopy destination: setroot violation",destination,0); |
| return(-1); |
| } |
| } |
| #endif /* CKROOT */ |
| |
| src = source; |
| dst = destination; |
| |
| if (stat(src,&srcbuf) == 0) { /* Get source file info */ |
| struct stat dstbuf; /* Destination file info buffer */ |
| debug(F101,"STAT","",6); |
| if (stat(dst,&dstbuf) == 0) { |
| debug(F101,"STAT","",7); |
| if (srcbuf.st_dev == dstbuf.st_dev) |
| if (srcbuf.st_ino == dstbuf.st_ino) { |
| debug(F100,"zcopy files identical: stat()","",0); |
| return(-5); |
| } |
| } |
| } else { /* stat() failed... */ |
| debug(F101,"STAT","",8); |
| debug(F111,"source file not found",src,errno); |
| return(-3); |
| } |
| fullname[0] = '\0'; /* Get full pathnames */ |
| if (zfnqfp(source,CKMAXPATH,fullname)) |
| src = fullname; |
| debug(F110,"zcopy src",src,0); |
| tmp2[0] = '\0'; |
| if (zfnqfp(destination,CKMAXPATH,tmp2)) |
| dst = tmp2; |
| debug(F110,"zcopy dst 1",dst,0); |
| if (!strcmp(src,dst)) { /* Src and dst are same file? */ |
| debug(F100,"zcopy files identical: strcmp()","",0); /* This... */ |
| return(-5); /* should not happen. */ |
| } |
| if (isdir(src)) { /* Source file is a directory? */ |
| debug(F110,"zcopy source is directory",src,0); |
| return(-2); /* Fail */ |
| } |
| if (isdir(dst)) { /* Destination is a directory? */ |
| char *q = NULL; /* Yes, add filename to it. */ |
| x = strlen(dst); |
| if (x < 1) return(-1); |
| if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */ |
| tmp2[x++] = '/'; |
| tmp2[x] = '\0'; |
| } |
| debug(F111,"zcopy dst 2",dst,x); |
| zstrip(src,&q); /* Strip path part from old name */ |
| ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */ |
| } |
| debug(F110,"zcopy dst 3",dst,0); |
| |
| #ifdef IKSD |
| if (inserver && (!ENABLED(en_del))) { |
| if (zchki(dst) > -1) /* Destination file exists? */ |
| return(-4); |
| } |
| #endif /* IKSD */ |
| |
| perms = umask(0); /* Get user's umask */ |
| umask(perms); /* Put it back! */ |
| perms ^= 0777; /* Flip the bits */ |
| perms &= 0666; /* Zero execute bits from umask */ |
| perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */ |
| rc = -1; /* Default return code */ |
| errno = 0; /* Reset errno */ |
| in = open(src, O_RDONLY, 0); /* Open source file */ |
| debug(F111,"zcopy open source",src,in); |
| if (in > -1) { /* If open... */ |
| /* Open destination file */ |
| #ifdef O_TRUNC |
| out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms); |
| #else |
| out = open(dst, O_WRONLY|O_CREAT, perms); |
| #endif /* O_TRUNC */ |
| debug(F111,"zcopy open dest",dst,out); |
| if (out > -1) { /* If open... */ |
| while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */ |
| y = write(out,buf,x); |
| if (y < 0) { /* On write failure */ |
| x = -1; |
| rc = -6; /* Indicate i/o error */ |
| break; |
| } |
| } |
| debug(F101,"zcopy final read","",x); |
| debug(F101,"zcopy errno","",errno); |
| rc = (x == 0) ? 0 : -6; /* In case of read failure */ |
| } |
| } |
| if (in > -1) close(in); /* Close files */ |
| if (out > -1) close(out); |
| if (rc == -1) { /* Set return code */ |
| switch (errno) { |
| case ENOENT: rc = -3; break; |
| case EACCES: rc = -4; break; |
| case EIO: rc = -6; |
| } |
| } |
| |
| #ifdef CKSYSLOG |
| if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) { |
| if (rc) |
| syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2); |
| else |
| syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2); |
| } |
| #endif /* CKSYSLOG */ |
| |
| return(rc); |
| } |
| |
| /* Z S A T T R */ |
| /* |
| Fills in a Kermit file attribute structure for the file which is to be sent. |
| Returns 0 on success with the structure filled in, or -1 on failure. |
| If any string member is null, then it should be ignored. |
| If any numeric member is -1, then it should be ignored. |
| */ |
| #ifdef CK_PERMS |
| |
| #ifdef CK_GPERMS |
| #undef CK_GPERMS |
| #endif /* CK_GPERMS */ |
| |
| #ifdef UNIX |
| #ifndef S_IRUSR |
| #define S_IRUSR 0400 |
| #endif /* S_IRUSR */ |
| #ifndef S_IWUSR |
| #define S_IXUSR 0200 |
| #endif /* S_IWUSR */ |
| #ifndef S_IXUSR |
| #define S_IXUSR 0100 |
| #endif /* S_IXUSR */ |
| #endif /* UNIX */ |
| |
| #ifdef S_IRUSR |
| #ifdef S_IWUSR |
| #ifdef S_IXUSR |
| #define CK_GPERMS |
| #endif /* S_IXUSR */ |
| #endif /* S_IWUSR */ |
| #endif /* S_IRUSR */ |
| |
| static char gperms[2]; |
| |
| #endif /* CK_GPERMS */ |
| |
| static char lperms[24]; |
| |
| #ifdef CK_PERMS |
| static char xlperms[24]; |
| |
| /* Z S E T P E R M -- Set permissions of a file */ |
| |
| int |
| zsetperm(f,code) char * f; int code; { |
| int x; |
| #ifdef CK_SCO32V4 |
| mode_t mask; |
| #else |
| int mask; |
| #endif /* CK_SCO32V4 */ |
| mask = code; |
| if (inserver && guest) { |
| debug(F110,"zsetperm guest",f,0); |
| return(0); |
| } |
| x = chmod(f,mask); |
| if (x < 0) { |
| debug(F111,"zsetperm error",f,errno); |
| return(0); |
| } |
| debug(F111,"zsetperm ok",f,mask); |
| return(1); |
| } |
| |
| /* Z G P E R M -- Get permissions of a file as an octal string */ |
| |
| char * |
| zgperm(f) char *f; { |
| extern int diractive; |
| int x; char *s = (char *)xlperms; |
| struct stat buf; |
| debug(F110,"zgperm",f,0); |
| if (!f) return("----------"); |
| if (!*f) return("----------"); |
| |
| #ifdef CKROOT |
| debug(F111,"zgperm setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(f)) { |
| debug(F110,"zgperm setroot violation",f,0); |
| return("----------"); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef USE_LSTAT |
| if (diractive) |
| x = lstat(f,&buf); |
| else |
| #endif /* USE_LSTAT */ |
| x = stat(f,&buf); |
| debug(F101,"STAT","",9); |
| if (x < 0) |
| return("----------"); |
| sprintf(s,"%o",buf.st_mode); |
| debug(F110,"zgperm",s,0); |
| return(s); |
| } |
| |
| /* Like zgperm() but returns permissions in "ls -l" string format */ |
| |
| static char xsperms[24]; |
| |
| char * |
| ziperm(f) char * f; { |
| extern int diractive; |
| int x; char *s = (char *)xsperms; |
| struct stat buf; |
| unsigned int perms = 0; |
| |
| debug(F110,"ziperm",f,0); |
| |
| if (!f) return(NULL); |
| if (!*f) return(NULL); |
| |
| if (diractive && zgfs_mode != 0) { |
| perms = zgfs_mode; /* zgetfs() already got them */ |
| } else { |
| #ifdef USE_LSTAT |
| if (diractive) |
| x = lstat(f,&buf); |
| else |
| #endif /* USE_LSTAT */ |
| x = stat(f,&buf); |
| debug(F101,"STAT","",10); |
| if (x < 0) |
| return("----------"); |
| perms = buf.st_mode; |
| } |
| switch (perms & S_IFMT) { |
| case S_IFDIR: |
| *s++ = 'd'; |
| break; |
| case S_IFCHR: /* Character special */ |
| *s++ = 'c'; |
| break; |
| case S_IFBLK: /* Block special */ |
| *s++ = 'b'; |
| break; |
| case S_IFREG: /* Regular */ |
| *s++ = '-'; |
| break; |
| #ifdef S_IFLNK |
| case S_IFLNK: /* Symbolic link */ |
| *s++ = 'l'; |
| break; |
| #endif /* S_IFLNK */ |
| #ifdef S_IFSOCK |
| case S_IFSOCK: /* Socket */ |
| *s++ = 's'; |
| break; |
| #endif /* S_IFSOCK */ |
| #ifdef S_IFIFO |
| #ifndef Plan9 |
| #ifndef COHERENT |
| case S_IFIFO: /* FIFO */ |
| *s++ = 'p'; |
| break; |
| #endif /* COHERENT */ |
| #endif /* Plan9 */ |
| #endif /* S_IFIFO */ |
| #ifdef S_IFWHT |
| case S_IFWHT: /* Whiteout */ |
| *s++ = 'w'; |
| break; |
| #endif /* S_IFWHT */ |
| default: /* Unknown */ |
| *s++ = '?'; |
| break; |
| } |
| if (perms & S_IRUSR) /* Owner's permissions */ |
| *s++ = 'r'; |
| else |
| *s++ = '-'; |
| if (perms & S_IWUSR) |
| *s++ = 'w'; |
| else |
| *s++ = '-'; |
| switch (perms & (S_IXUSR | S_ISUID)) { |
| case 0: |
| *s++ = '-'; |
| break; |
| case S_IXUSR: |
| *s++ = 'x'; |
| break; |
| case S_ISUID: |
| *s++ = 'S'; |
| break; |
| case S_IXUSR | S_ISUID: |
| *s++ = 's'; |
| break; |
| } |
| if (perms & S_IRGRP) /* Group permissions */ |
| *s++ = 'r'; |
| else |
| *s++ = '-'; |
| if (perms & S_IWGRP) |
| *s++ = 'w'; |
| else |
| *s++ = '-'; |
| switch (perms & (S_IXGRP | S_ISGID)) { |
| case 0: |
| *s++ = '-'; |
| break; |
| case S_IXGRP: |
| *s++ = 'x'; |
| break; |
| case S_ISGID: |
| *s++ = 'S'; |
| break; |
| case S_IXGRP | S_ISGID: |
| *s++ = 's'; |
| break; |
| } |
| if (perms & S_IROTH) /* World permissions */ |
| *s++ = 'r'; |
| else |
| *s++ = '-'; |
| if (perms & S_IWOTH) |
| *s++ = 'w'; |
| else |
| *s++ = '-'; |
| switch ( |
| #ifdef Plan9 |
| perms & (S_IXOTH) |
| #else |
| perms & (S_IXOTH | S_ISVTX) |
| #endif |
| ) { |
| case 0: |
| *s++ = '-'; |
| break; |
| case S_IXOTH: |
| *s++ = 'x'; |
| break; |
| #ifndef Plan9 |
| case S_ISVTX: |
| *s++ = 'T'; |
| break; |
| case S_IXOTH | S_ISVTX: |
| *s++ = 't'; |
| break; |
| #endif /* Plan9 */ |
| } |
| *s = '\0'; |
| debug(F110,"ziperm",xsperms,0); |
| return((char *)xsperms); |
| } |
| |
| #else |
| |
| char * |
| zgperm(f) char *f; { |
| return("----------"); |
| } |
| char * |
| ziperms(f) char *f; { |
| return("----------"); |
| } |
| #endif /* CK_PERMS */ |
| |
| int |
| zsattr(xx) struct zattr *xx; { |
| long k; int x; |
| struct stat buf; |
| |
| k = iflen % 1024L; /* File length in K */ |
| if (k != 0L) k = 1L; |
| xx->lengthk = (iflen / 1024L) + k; |
| xx->type.len = 0; /* File type can't be filled in here */ |
| xx->type.val = ""; |
| if (*nambuf) { |
| xx->date.val = zfcdat(nambuf); /* File creation date */ |
| xx->date.len = (int)strlen(xx->date.val); |
| } else { |
| xx->date.len = 0; |
| xx->date.val = ""; |
| } |
| xx->creator.len = 0; /* File creator */ |
| xx->creator.val = ""; |
| xx->account.len = 0; /* File account */ |
| xx->account.val = ""; |
| xx->area.len = 0; /* File area */ |
| xx->area.val = ""; |
| xx->password.len = 0; /* Area password */ |
| xx->password.val = ""; |
| xx->blksize = -1L; /* File blocksize */ |
| xx->xaccess.len = 0; /* File access */ |
| xx->xaccess.val = ""; |
| xx->encoding.len = 0; /* Transfer syntax */ |
| xx->encoding.val = 0; |
| xx->disp.len = 0; /* Disposition upon arrival */ |
| xx->disp.val = ""; |
| xx->lprotect.len = 0; /* Local protection */ |
| xx->lprotect.val = ""; |
| xx->gprotect.len = 0; /* Generic protection */ |
| xx->gprotect.val = ""; |
| x = -1; |
| if (*nambuf) x = stat(nambuf,&buf); |
| debug(F101,"STAT","",11); |
| if (x >= 0) { |
| debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777); |
| /* UNIX filemode as an octal string without filetype bits */ |
| sprintf(lperms,"%o",buf.st_mode & 0777); |
| xx->lprotect.len = (int)strlen(lperms); |
| xx->lprotect.val = (char *)lperms; |
| x = 0; |
| #ifdef CK_GPERMS |
| /* Generic permissions only if we have stat.h symbols defined */ |
| if (buf.st_mode & S_IRUSR) x |= 1; /* Read */ |
| if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */ |
| if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */ |
| gperms[0] = tochar(x); |
| gperms[1] = NUL; |
| xx->gprotect.len = 1; |
| xx->gprotect.val = (char *)gperms; |
| #endif /* CK_GPERMS */ |
| } |
| debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len); |
| debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len); |
| xx->systemid.val = "U1"; /* U1 = UNIX */ |
| xx->systemid.len = 2; /* System ID */ |
| xx->recfm.len = 0; /* Record format */ |
| xx->recfm.val = ""; |
| xx->sysparam.len = 0; /* System-dependent parameters */ |
| xx->sysparam.val = ""; |
| xx->length = iflen; /* Length */ |
| return(0); |
| } |
| |
| /* Z F C D A T -- Get file creation date */ |
| /* |
| Call with pointer to filename. |
| On success, returns pointer to modification date in yyyymmdd hh:mm:ss format. |
| On failure, returns pointer to null string. |
| */ |
| static char datbuf[40]; |
| |
| char * |
| #ifdef CK_ANSIC |
| zdtstr(time_t timearg) |
| #else |
| zdtstr(timearg) time_t timearg; |
| #endif /* CK_ANSIC */ |
| /* zdtstr */ { |
| #ifndef TIMESTAMP |
| return(""); |
| #else |
| struct tm * time_stamp; |
| struct tm * localtime(); |
| int yy, ss; |
| |
| debug(F101,"zdtstr timearg","",timearg); |
| if (timearg < 0) |
| return(""); |
| time_stamp = localtime(&(timearg)); |
| if (!time_stamp) { |
| debug(F100,"localtime returns null","",0); |
| return(""); |
| } |
| /* |
| We assume that tm_year is ALWAYS years since 1900. |
| Any platform where this is not the case will have problems |
| starting in 2000. |
| */ |
| yy = time_stamp->tm_year; /* Year - 1900 */ |
| debug(F101,"zdtstr tm_year","",time_stamp->tm_year); |
| if (yy > 1000) { |
| debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy); |
| } |
| yy += 1900; |
| debug(F101,"zdatstr year","",yy); |
| |
| if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11) |
| return(""); |
| if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31) |
| return(""); |
| if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23) |
| return(""); |
| if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59) |
| return(""); |
| ss = time_stamp->tm_sec; /* Seconds */ |
| if (ss < 0 || ss > 59) /* Some systems give a BIG number */ |
| ss = 0; |
| sprintf(datbuf, |
| #ifdef pdp11 |
| /* For some reason, 2.1x BSD sprintf gets the last field wrong. */ |
| "%04d%02d%02d %02d:%02d:00", |
| #else |
| "%04d%02d%02d %02d:%02d:%02d", |
| #endif /* pdp11 */ |
| yy, |
| time_stamp->tm_mon + 1, |
| time_stamp->tm_mday, |
| time_stamp->tm_hour, |
| time_stamp->tm_min |
| #ifndef pdp11 |
| , ss |
| #endif /* pdp11 */ |
| ); |
| yy = (int)strlen(datbuf); |
| debug(F111,"zdatstr",datbuf,yy); |
| if (yy > 17) datbuf[17] = '\0'; |
| return(datbuf); |
| #endif /* TIMESTAMP */ |
| } |
| |
| char * |
| zfcdat(name) char *name; { |
| #ifdef TIMESTAMP |
| struct stat buffer; |
| extern int diractive; |
| unsigned int mtime; |
| int x; |
| char * s; |
| |
| if (!name) |
| return(""); |
| s = name; |
| if (!*s) |
| return(""); |
| |
| #ifdef CKROOT |
| debug(F111,"zfcdat setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(name)) { |
| debug(F110,"zfcdat setroot violation",name,0); |
| return(""); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef DTILDE |
| if (*s == '~') { |
| s = tilde_expand(s); |
| if (!s) s = ""; |
| if (!*s) s = name; |
| } |
| #endif /* DTILDE */ |
| |
| datbuf[0] = '\0'; |
| x = 0; |
| debug(F111,"zfcdat",s,diractive); |
| |
| if (diractive && zgfs_mtime) { |
| mtime = zgfs_mtime; |
| } else { |
| #ifdef USE_LSTAT |
| if (diractive) { |
| x = lstat(s,&buffer); |
| debug(F101,"STAT","",12); |
| debug(F101,"zfcdat lstat","",x); |
| } else { |
| #endif /* USE_LSTAT */ |
| x = stat(s,&buffer); |
| debug(F101,"STAT","",13); |
| debug(F101,"zfcdat stat","",x); |
| #ifdef USE_LSTAT |
| } |
| #endif /* USE_LSTAT */ |
| if (x != 0) { |
| #ifdef USE_LSTAT |
| debug(F111,"zfcdat stat failed",s,errno); |
| #else |
| debug(F111,"zfcdat lstat failed",s,errno); |
| #endif /* USE_LSTAT */ |
| return(""); |
| } |
| debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime); |
| mtime = buffer.st_mtime; |
| } |
| return(zdtstr(mtime)); |
| #else |
| return(""); |
| #endif /* TIMESTAMP */ |
| } |
| |
| #ifndef NOTIMESTAMP |
| |
| /* Z S T R D T -- Converts local date string to internal representation */ |
| /* |
| In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC, |
| suitable for comparison with UNIX file dates. As far as I know, there is |
| no library or system call -- at least nothing reasonably portable -- to |
| convert local time to UTC. |
| */ |
| time_t |
| zstrdt(date,len) char * date; int len; { |
| #ifdef M_UNIX |
| /* |
| SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). |
| ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in |
| dependence on the XPG4 supplement presence. So always use |
| what the system header file supplies in ODT 3.0... |
| */ |
| #ifndef ODT30 |
| #ifndef _SCO_DS |
| extern void ftime(); /* extern void ftime(struct timeb *) */ |
| #endif /* _SCO_DS */ |
| #endif /* ODT30 */ |
| #else |
| #ifndef M_XENIX |
| extern int ftime(); |
| #endif /* M_XENIX */ |
| #endif /* M_UNIX */ |
| extern struct tm * localtime(); |
| |
| /* And this should have been declared always through a header file */ |
| #ifdef HPUX10 |
| time_t tmx; |
| long days; |
| #else |
| #ifdef BSD44 |
| time_t tmx; |
| long days; |
| #else |
| long tmx, days; |
| #endif /* BSD44 */ |
| #endif /* HPUX10 */ |
| int i, n, isleapyear; |
| /* J F M A M J J A S O N D */ |
| /* 31 28 31 30 31 30 31 31 30 31 30 31 */ |
| static |
| int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 }; |
| char s[5]; |
| struct tm *time_stamp; |
| |
| #ifdef BSD44 |
| struct timeval tp[2]; |
| long xtimezone = 0L; |
| #else |
| #ifdef V7 |
| struct utimbuf { |
| time_t timep[2]; /* New access and modificaton time */ |
| } tp; |
| char *tz; |
| long timezone; /* In case timezone not defined in .h file */ |
| #else |
| #ifdef SYSUTIMEH |
| struct utimbuf tp; |
| #else |
| struct utimbuf { |
| time_t atime; |
| time_t mtime; |
| } tp; |
| #endif /* SYSUTIMEH */ |
| #endif /* V7 */ |
| #endif /* BSD44 */ |
| |
| #ifdef ANYBSD |
| long timezone = 0L; |
| static struct timeb tbp; |
| #endif /* ANYBSD */ |
| |
| #ifdef BEBOX |
| long timezone = 0L; |
| #endif /* BEBOX */ |
| |
| debug(F111,"zstrdt",date,len); |
| |
| if ((len == 0) |
| || (len != 17) |
| || (date[8] != ' ') |
| || (date[11] != ':') |
| || (date[14] != ':') ) { |
| debug(F111,"Bad creation date ",date,len); |
| return(-1); |
| } |
| debug(F111,"zstrdt date check 1",date,len); |
| for(i = 0; i < 8; i++) { |
| if (!isdigit(date[i])) { |
| debug(F111,"Bad creation date ",date,len); |
| return(-1); |
| } |
| } |
| debug(F111,"zstrdt date check 2",date,len); |
| i++; |
| |
| for (; i < 16; i += 3) { |
| if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { |
| debug(F111,"Bad creation date ",date,len); |
| return(-1); |
| } |
| } |
| debug(F111,"zstrdt date check 3",date,len); |
| |
| |
| #ifdef COMMENT /* was BSD44 */ |
| /* |
| man gettimeofday on BSDI 3.1 says: |
| "The timezone field is no longer used; timezone information is stored out- |
| side the kernel. See ctime(3) for more information." So this chunk of |
| code is effectively a no-op, at least in BSDI 3.x. |
| */ |
| { |
| int x; |
| struct timezone tzp; |
| x = gettimeofday(NULL, &tzp); |
| debug(F101,"zstrdt BSD44 gettimeofday","",x); |
| if (x > -1) |
| xtimezone = tzp.tz_minuteswest * 60L; |
| else |
| xtimezone = 0L; |
| debug(F101,"zstrdt BSD44 timezone","",xtimezone); |
| } |
| #else |
| #ifdef ANYBSD |
| debug(F100,"zstrdt BSD calling ftime","",0); |
| ftime(&tbp); |
| debug(F100,"zstrdt BSD back from ftime","",0); |
| timezone = tbp.timezone * 60L; |
| debug(F101,"zstrdt BSD timezone","",timezone); |
| #else |
| #ifdef SVORPOSIX |
| tzset(); /* Set timezone */ |
| #else |
| #ifdef V7 |
| if ((tz = getenv("TZ")) == NULL) |
| timezone = 0; /* UTC/GMT */ |
| else |
| timezone = atoi(&tz[3]); /* Set 'timezone'. */ |
| timezone *= 60L; |
| #endif /* V7 */ |
| #endif /* SVORPOSIX */ |
| #endif /* ANYBSD */ |
| #endif /* COMMENT (was BSD44) */ |
| |
| debug(F100,"zstrdt so far so good","",0); |
| |
| s[4] = '\0'; |
| for (i = 0; i < 4; i++) /* Fix the year */ |
| s[i] = date[i]; |
| |
| n = atoi(s); |
| debug(F111,"zstrdt year",s,n); |
| if (n < 1970) { |
| debug(F100,"zstrdt fails - year","",n); |
| return(-1); |
| } |
| |
| /* Previous year's leap days. This won't work after year 2100. */ |
| |
| isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); |
| days = (long) (n - 1970) * 365; |
| days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; |
| |
| s[2] = '\0'; |
| |
| for (i = 4; i < 16; i += 2) { |
| s[0] = date[i]; |
| s[1] = date[i + 1]; |
| n = atoi(s); |
| switch (i) { |
| case 4: /* MM: month */ |
| if ((n < 1 ) || ( n > 12)) { |
| debug(F111,"zstrdt 4 bad date ",date,len); |
| return(-1); |
| } |
| days += monthdays [n]; |
| if (isleapyear && n > 2) |
| ++days; |
| continue; |
| |
| case 6: /* DD: day */ |
| if ((n < 1 ) || ( n > 31)) { |
| debug(F111,"zstrdt 6 bad date ",date,len); |
| return(-1); |
| } |
| tmx = (days + n - 1) * 24L * 60L * 60L; |
| i++; /* Skip the space */ |
| continue; |
| |
| case 9: /* hh: hour */ |
| if ((n < 0 ) || ( n > 23)) { |
| debug(F111,"zstrdt 9 bad date ",date,len); |
| return(-1); |
| } |
| tmx += n * 60L * 60L; |
| i++; /* Skip the colon */ |
| continue; |
| |
| case 12: /* mm: minute */ |
| if ((n < 0 ) || ( n > 59)) { |
| debug(F111,"zstrdt 12 bad date ",date,len); |
| return(-1); |
| } |
| #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */ |
| tmx += xtimezone; |
| debug(F101,"zstrdt BSD44 tmx","",tmx); |
| #else |
| #ifdef ANYBSD |
| tmx += timezone; |
| #else |
| #ifndef CONVEX9 /* Don't yet know how to do this here */ |
| #ifdef ultrix |
| tmx += (long) timezone; |
| #else |
| #ifdef Plan9 |
| { |
| extern time_t tzoffset; |
| tmx += tzoffset; |
| } |
| #else |
| #ifndef BSD44 |
| tmx += timezone; |
| #endif /* BSD44 */ |
| #endif /* Plan9 */ |
| #endif /* ultrix */ |
| #endif /* CONVEX9 */ |
| #endif /* ANYBSD */ |
| #endif /* COMMENT (was BSD44) */ |
| tmx += n * 60L; |
| i++; /* Skip the colon */ |
| continue; |
| |
| case 15: /* ss: second */ |
| if ((n < 0 ) || ( n > 59)) { |
| debug(F111,"zstrdt 15 bad date ",date,len); |
| return(-1); |
| } |
| tmx += n; |
| } |
| time_stamp = localtime(&tmx); |
| debug(F101,"zstrdt tmx 1","",tmx); |
| if (!time_stamp) |
| return(-1); |
| #ifdef COMMENT |
| /* Why was this here? */ |
| time_stamp = localtime(&tmx); |
| debug(F101,"zstrdt tmx 2","",tmx); |
| #endif /* COMMENT */ |
| #ifdef BSD44 |
| { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */ |
| long zz; |
| zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */ |
| debug(F101,"zstrdt BSD44 tm_gmtoff","",zz); |
| tmx -= zz; |
| debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx); |
| } |
| #else |
| /* |
| Daylight Savings Time adjustment. |
| Do this everywhere BUT in BSD44 because in BSD44, |
| tm_gmtoff also includes the DST adjustment. |
| */ |
| if (time_stamp->tm_isdst) { |
| tmx -= 60L * 60L; |
| debug(F101,"zstrdt tmx 3 (DST)","",tmx); |
| } |
| #endif /* BSD44 */ |
| n = time_stamp->tm_year; |
| if (n < 300) { |
| n += 1900; |
| } |
| } |
| return(tmx); |
| } |
| |
| |
| #ifdef ZLOCALTIME |
| /* Z L O C A L T I M E -- GMT/UTC time string to local time string */ |
| |
| /* |
| Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time. |
| Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure. |
| */ |
| static char zltimbuf[64]; |
| |
| char * |
| zlocaltime(gmtstring) char * gmtstring; { |
| #ifdef M_UNIX |
| /* |
| SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime(). |
| ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in |
| dependence on the XPG4 supplement presence. So always use |
| what the system header file supplies in ODT 3.0... |
| */ |
| #ifndef ODT30 |
| #ifndef _SCO_DS |
| extern void ftime(); /* extern void ftime(struct timeb *) */ |
| #endif /* _SCO_DS */ |
| #endif /* ODT30 */ |
| #else |
| #ifndef M_XENIX |
| extern int ftime(); |
| #endif /* M_XENIX */ |
| #endif /* M_UNIX */ |
| extern struct tm * localtime(); |
| |
| /* And this should have been declared always through a header file */ |
| #ifdef HPUX10 |
| time_t tmx; |
| long days; |
| #else |
| #ifdef BSD44 |
| time_t tmx; |
| long days; |
| #else |
| long tmx, days; |
| #endif /* BSD44 */ |
| #endif /* HPUX10 */ |
| int i, n, x, isleapyear; |
| /* J F M A M J J A S O N D */ |
| /* 31 28 31 30 31 30 31 31 30 31 30 31 */ |
| static |
| int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 }; |
| char s[5]; |
| struct tm *time_stamp; |
| |
| #ifdef BSD44 |
| struct timeval tp[2]; |
| #else |
| #ifdef V7 |
| struct utimbuf { |
| time_t timep[2]; /* New access and modificaton time */ |
| } tp; |
| #else |
| #ifdef SYSUTIMEH |
| struct utimbuf tp; |
| #else |
| struct utimbuf { |
| time_t atime; |
| time_t mtime; |
| } tp; |
| #endif /* SYSUTIMEH */ |
| #endif /* V7 */ |
| #endif /* BSD44 */ |
| |
| #ifdef ANYBSD |
| static struct timeb tbp; |
| #endif /* ANYBSD */ |
| |
| char * date = gmtstring; |
| int len; |
| |
| len = strlen(date); |
| debug(F111,"zlocaltime",date,len); |
| |
| if ((len == 0) |
| || (len != 17) |
| || (date[8] != ' ') |
| || (date[11] != ':') |
| || (date[14] != ':') ) { |
| debug(F111,"Bad creation date ",date,len); |
| return(NULL); |
| } |
| debug(F111,"zlocaltime date check 1",date,len); |
| for(i = 0; i < 8; i++) { |
| if (!isdigit(date[i])) { |
| debug(F111,"Bad creation date ",date,len); |
| return(NULL); |
| } |
| } |
| debug(F111,"zlocaltime date check 2",date,len); |
| i++; |
| |
| for (; i < 16; i += 3) { |
| if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) { |
| debug(F111,"Bad creation date ",date,len); |
| return(NULL); |
| } |
| } |
| debug(F111,"zlocaltime date check 3",date,len); |
| |
| debug(F100,"zlocaltime so far so good","",0); |
| |
| s[4] = '\0'; |
| for (i = 0; i < 4; i++) /* Fix the year */ |
| s[i] = date[i]; |
| |
| n = atoi(s); |
| debug(F111,"zlocaltime year",s,n); |
| if (n < 1970) { |
| debug(F100,"zlocaltime fails - year","",n); |
| return(NULL); |
| } |
| |
| /* Previous year's leap days. This won't work after year 2100. */ |
| |
| isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0); |
| days = (long) (n - 1970) * 365; |
| days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400; |
| |
| s[2] = '\0'; |
| |
| for (i = 4; i < 16; i += 2) { |
| s[0] = date[i]; |
| s[1] = date[i + 1]; |
| n = atoi(s); |
| switch (i) { |
| case 4: /* MM: month */ |
| if ((n < 1 ) || ( n > 12)) { |
| debug(F111,"zlocaltime 4 bad date ",date,len); |
| return(NULL); |
| } |
| days += monthdays [n]; |
| if (isleapyear && n > 2) |
| ++days; |
| continue; |
| |
| case 6: /* DD: day */ |
| if ((n < 1 ) || ( n > 31)) { |
| debug(F111,"zlocaltime 6 bad date ",date,len); |
| return(NULL); |
| } |
| tmx = (days + n - 1) * 24L * 60L * 60L; |
| i++; /* Skip the space */ |
| continue; |
| |
| case 9: /* hh: hour */ |
| if ((n < 0 ) || ( n > 23)) { |
| debug(F111,"zlocaltime 9 bad date ",date,len); |
| return(NULL); |
| } |
| tmx += n * 60L * 60L; |
| i++; /* Skip the colon */ |
| continue; |
| |
| case 12: /* mm: minute */ |
| if ((n < 0 ) || ( n > 59)) { |
| debug(F111,"zlocaltime 12 bad date ",date,len); |
| return(NULL); |
| } |
| tmx += n * 60L; |
| i++; /* Skip the colon */ |
| continue; |
| |
| case 15: /* ss: second */ |
| if ((n < 0 ) || ( n > 59)) { |
| debug(F111,"zlocaltime 15 bad date ",date,len); |
| return(NULL); |
| } |
| tmx += n; |
| } |
| |
| /* |
| At this point tmx is the time_t representation of the argument date-time |
| string without any timezone or DST adjustments. Therefore it should be |
| the same as the time_t representation of the GMT/UTC time. Now we should |
| be able to feed it to localtime() and have it converted to a struct tm |
| representing the local time equivalent of the given UTC time. |
| */ |
| time_stamp = localtime(&tmx); |
| if (!time_stamp) |
| return(NULL); |
| } |
| |
| /* Now we simply reformat the struct tm to a string */ |
| |
| x = time_stamp->tm_year; |
| if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099) |
| return(NULL); |
| if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11) |
| return(NULL); |
| if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31) |
| return(NULL); |
| if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24) |
| return(NULL); |
| if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60) |
| return(NULL); |
| if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60) |
| return(NULL); |
| sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d", |
| time_stamp->tm_year + 1900, |
| time_stamp->tm_mon + 1, |
| time_stamp->tm_mday, |
| time_stamp->tm_hour, |
| time_stamp->tm_min, |
| time_stamp->tm_sec |
| ); |
| return((char *)zltimbuf); |
| } |
| #endif /* ZLOCALTIME */ |
| #endif /* NOTIMESTAMP */ |
| |
| /* Z S T I M E -- Set modification date/time+permissions for incoming file */ |
| /* |
| Call with: |
| f = pointer to name of existing file. |
| yy = pointer to a Kermit file attribute structure in which yy->date.val |
| is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00. |
| yy->lprotect.val & yy->gprotect.val are permission/protection values. |
| x = is a function code: 0 means to set the file's attributes as given. |
| 1 means compare the date in struct yy with the file creation date. |
| Returns: |
| -1 on any kind of error. |
| 0 if x is 0 and the attributes were set successfully. |
| 0 if x is 1 and date from attribute structure <= file creation date. |
| 1 if x is 1 and date from attribute structure > file creation date. |
| */ |
| int |
| zstime(f,yy,x) |
| char *f; struct zattr *yy; int x; |
| /* zstime */ { |
| int r = -1; /* Return code */ |
| #ifdef CK_PERMS |
| int setperms = 0; |
| #endif /* CK_PERMS */ |
| int setdate = 0; |
| |
| /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */ |
| |
| #ifdef TIMESTAMP |
| #ifdef BSD44 |
| extern int utimes(); |
| #else |
| extern int utime(); |
| #endif /* BSD44 */ |
| |
| struct stat sb; |
| |
| /* At least, the declarations for int functions are not needed anyway */ |
| |
| #ifdef BSD44 |
| struct timeval tp[2]; |
| long xtimezone; |
| #else |
| #ifdef V7 |
| struct utimbuf { |
| time_t timep[2]; /* New access and modificaton time */ |
| } tp; |
| char *tz; |
| long timezone; /* In case not defined in .h file */ |
| #else |
| #ifdef SYSUTIMEH |
| struct utimbuf tp; |
| #else |
| struct utimbuf { |
| time_t atime; |
| time_t mtime; |
| } tp; |
| #endif /* SYSUTIMEH */ |
| #endif /* V7 */ |
| #endif /* BSD44 */ |
| |
| long tm = 0L; |
| |
| if (!f) f = ""; |
| if (!*f) return(-1); |
| if (!yy) return(-1); |
| |
| debug(F110,"zstime",f,0); |
| debug(F111,"zstime date",yy->date.val,yy->date.len); |
| |
| #ifdef CKROOT |
| debug(F111,"zstime setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(f)) { |
| debug(F110,"zstime setroot violation",f,0); |
| return(0); |
| } |
| #endif /* CKROOT */ |
| |
| if (yy->date.len == 0) { /* No date in struct */ |
| if (yy->lprotect.len != 0) { /* So go do permissions */ |
| goto zsperms; |
| } else { |
| debug(F100,"zstime: nothing to do","",0); |
| return(0); |
| } |
| } |
| if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) { |
| debug(F101,"zstime: zstrdt fails","",0); |
| return(-1); |
| } |
| debug(F101,"zstime: tm","",tm); |
| debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len); |
| |
| if (stat(f,&sb)) { /* Get the time for the file */ |
| debug(F101,"STAT","",14); |
| debug(F111,"zstime: Can't stat file:",f,errno); |
| return(-1); |
| } |
| debug(F101,"STAT","",15); |
| setdate = 1; |
| |
| zsperms: |
| #ifdef CK_PERMS |
| { |
| int i, x = 0, xx, flag = 0; |
| char * s; |
| #ifdef DEBUG |
| char obuf[24]; |
| if (deblog) { |
| debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len); |
| debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len); |
| debug(F110,"zstime system id",yy->systemid.val,0); |
| sprintf(obuf,"%o",sb.st_mode); |
| debug(F110,"zstime file perms before",obuf,0); |
| } |
| #endif /* DEBUG */ |
| |
| #ifdef CK_LOGIN |
| debug(F101,"zstime isguest","",isguest); |
| debug(F101,"zstime ckxperms","",ckxperms); |
| if (isguest) { |
| #ifdef COMMENT |
| /* Clear owner permissions */ |
| sb.st_mode &= (unsigned) 0177077; /* (16 bits) */ |
| #else |
| /* Set permissions from ckxperms variable */ |
| sb.st_mode = ckxperms; |
| #endif /* COMMENT */ |
| debug(F101,"zstime isguest sb.st_mode","",sb.st_mode); |
| #ifdef COMMENT |
| /* We already set them in zopeno() */ |
| setperms = 1; |
| #endif /* COMMENT */ |
| flag = 0; |
| } else |
| #endif /* CK_LOGIN */ |
| if ((yy->lprotect.len > 0 && /* Have local-format permissions */ |
| yy->systemid.len > 0 && /* from A-packet... */ |
| #ifdef UNIX |
| !strcmp(yy->systemid.val,"U1") /* AND you are same as me */ |
| #else |
| 0 |
| #endif /* UNIX */ |
| ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */ |
| ) { |
| flag = 1; |
| s = yy->lprotect.val; /* UNIX filemode */ |
| xx = yy->lprotect.len; |
| if (xx < 0) /* len < 0 means inheritance */ |
| xx = 0 - xx; |
| for (i = 0; i < xx; i++) { /* Decode octal string */ |
| if (*s <= '7' && *s >= '0') { |
| x = 8 * x + (int)(*s) - '0'; |
| } else { |
| flag = 0; |
| break; |
| } |
| s++; |
| } |
| #ifdef DEBUG |
| sprintf(obuf,"%o",x); |
| debug(F110,"zstime octal lperm",obuf,0); |
| #endif /* DEBUG */ |
| } else if (!flag && yy->gprotect.len > 0) { |
| int g; |
| #ifdef CK_SCO32V4 |
| mode_t mask; |
| #else |
| int mask; |
| #endif /* CK_SCO32V4 */ |
| mask = umask(0); /* Get umask */ |
| debug(F101,"zstime mask 1","",mask); |
| umask(mask); /* Put it back */ |
| mask ^= 0777; /* Flip the bits */ |
| debug(F101,"zstime mask 2","",mask); |
| g = xunchar(*(yy->gprotect.val)); /* Decode generic protection */ |
| debug(F101,"zstime gprotect","",g); |
| #ifdef S_IRUSR |
| debug(F100,"zstime S_IRUSR","",0); |
| if (g & 1) x |= S_IRUSR; /* Read permission */ |
| flag = 1; |
| #endif /* S_IRUSR */ |
| #ifdef S_IWUSR |
| debug(F100,"zstime S_IWUSR","",0); |
| if (g & 2) x |= S_IWUSR; /* Write permission */ |
| if (g & 16) x |= S_IWUSR; /* Delete permission */ |
| flag = 1; |
| #endif /* S_IWUSR */ |
| #ifdef S_IXUSR |
| debug(F100,"zstime S_IXUSR","",0); |
| if (g & 4) /* Has execute permission bit */ |
| x |= S_IXUSR; |
| else /* Doesn't have it */ |
| mask &= 0666; /* so also clear it out of mask */ |
| flag = 1; |
| #endif /* S_IXUSR */ |
| debug(F101,"zstime mask x","",x); |
| x |= mask; |
| debug(F101,"zstime mask x|mask","",x); |
| } |
| debug(F101,"zstime flag","",flag); |
| if (flag) { |
| #ifdef S_IFMT |
| debug(F101,"zstime S_IFMT x","",x); |
| sb.st_mode = (sb.st_mode & S_IFMT) | x; |
| setperms = 1; |
| #else |
| #ifdef _IFMT |
| debug(F101,"zstime _IFMT x","",x); |
| sb.st_mode = (sb.st_mode & _IFMT) | x; |
| setperms = 1; |
| #endif /* _IFMT */ |
| #endif /* S_IFMT */ |
| } |
| #ifdef DEBUG |
| sprintf(obuf,"%04o",sb.st_mode); |
| debug(F111,"zstime file perms after",obuf,setperms); |
| #endif /* DEBUG */ |
| } |
| #endif /* CK_PERMS */ |
| |
| debug(F101,"zstime: sb.st_atime","",sb.st_atime); |
| |
| #ifdef BSD44 |
| tp[0].tv_sec = sb.st_atime; /* Access time first */ |
| tp[1].tv_sec = tm; /* Update time second */ |
| debug(F100,"zstime: BSD44 modtime","",0); |
| #else |
| #ifdef V7 |
| tp.timep[0] = tm; /* Set modif. time to creation date */ |
| tp.timep[1] = sb.st_atime; /* Don't change the access time */ |
| debug(F100,"zstime: V7 modtime","",0); |
| #else |
| #ifdef SYSUTIMEH |
| tp.modtime = tm; /* Set modif. time to creation date */ |
| tp.actime = sb.st_atime; /* Don't change the access time */ |
| debug(F100,"zstime: SYSUTIMEH modtime","",0); |
| #else |
| tp.mtime = tm; /* Set modif. time to creation date */ |
| tp.atime = sb.st_atime; /* Don't change the access time */ |
| debug(F100,"zstime: default modtime","",0); |
| #endif /* SYSUTIMEH */ |
| #endif /* V7 */ |
| #endif /* BSD44 */ |
| |
| switch (x) { /* Execute desired function */ |
| case 0: /* Set the creation date of the file */ |
| #ifdef CK_PERMS /* And permissions */ |
| /* |
| NOTE: If we are inheriting permissions from a previous file, and the |
| previous file was a directory, this would turn the new file into a directory |
| too, but it's not, so we try to unset the right bit. Luckily, this code |
| will probably never be executed since the upper level modules do not allow |
| reception of a file that has the same name as a directory. |
| |
| NOTE 2: We change the permissions *before* we change the modification time, |
| otherwise changing the permissions would set the mod time to the present |
| time. |
| */ |
| { |
| int x; |
| debug(F101,"zstime setperms","",setperms); |
| if (S_ISDIR(sb.st_mode)) { |
| debug(F101,"zstime DIRECTORY bit on","",sb.st_mode); |
| sb.st_mode ^= 0040000; |
| debug(F101,"zstime DIRECTORY bit off","",sb.st_mode); |
| } |
| if (setperms) { |
| x = chmod(f,sb.st_mode); |
| debug(F101,"zstime chmod","",x); |
| } |
| } |
| if (x < 0) return(-1); |
| #endif /* CK_PERMS */ |
| |
| if (!setdate) /* We don't have a date */ |
| return(0); /* so skip the following... */ |
| |
| if ( |
| #ifdef BSD44 |
| utimes(f,tp) |
| #else |
| utime(f,&tp) |
| #endif /* BSD44 */ |
| ) { /* Fix modification time */ |
| debug(F111,"zstime 0: can't set modtime for file",f,errno); |
| r = -1; |
| } else { |
| /* Including the modtime here is not portable */ |
| debug(F110,"zstime 0: modtime set for file",f,0); |
| r = 0; |
| } |
| break; |
| |
| case 1: /* Compare the dates */ |
| /* |
| This was st_atime, which was wrong. We want the file-data modification |
| time, st_mtime. |
| */ |
| debug(F111,"zstime 1: compare",f,sb.st_mtime); |
| debug(F111,"zstime 1: compare","packet",tm); |
| |
| r = (sb.st_mtime < tm) ? 0 : 1; |
| break; |
| |
| default: /* Error */ |
| r = -1; |
| } |
| #endif /* TIMESTAMP */ |
| return(r); |
| } |
| |
| /* Find initialization file. */ |
| |
| #ifdef NOTUSED |
| int |
| zkermini() { |
| /* nothing here for Unix. This function added for benefit of VMS Kermit. */ |
| return(0); |
| } |
| #endif /* NOTUSED */ |
| |
| #ifndef UNIX |
| /* Historical -- not used in Unix any more (2001-11-03) */ |
| #ifndef NOFRILLS |
| int |
| zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */ |
| /* |
| Returns 0 on success |
| 2 if mail delivered but temp file can't be deleted |
| -2 if mail can't be delivered |
| -1 on file access error |
| The UNIX version always returns 0 because it can't get a good return |
| code from zsyscmd. |
| */ |
| int n; |
| |
| #ifdef CK_LOGIN |
| if (isguest) |
| return(-2); |
| #endif /* CK_LOGIN */ |
| |
| if (!f) f = ""; |
| if (!*f) return(-1); |
| |
| #ifdef CKROOT |
| debug(F111,"zmail setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(f)) { |
| debug(F110,"zmail setroot violation",f,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef BSD4 |
| /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */ |
| /* a subject line can be included with -s. Since we can't depend on the */ |
| /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */ |
| /* and even if Mail has been moved to somewhere else, this should still */ |
| /* find it... The search could be made more reliable by actually using */ |
| /* access() to see if /usr/ucb/Mail exists. */ |
| |
| n = strlen(f); |
| n = n + n + 15 + (int)strlen(p); |
| |
| if (n > ZMBUFLEN) |
| return(-2); |
| |
| #ifdef DGUX540 |
| sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); |
| #else |
| sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f); |
| #endif /* DGUX540 */ |
| zsyscmd(zmbuf); |
| #else |
| #ifdef SVORPOSIX |
| #ifndef OXOS |
| sprintf(zmbuf,"mail %s < %s", p, f); |
| #else /* OXOS */ |
| sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f); |
| #endif /* OXOS */ |
| zsyscmd(zmbuf); |
| #else |
| *zmbuf = '\0'; |
| #endif |
| #endif |
| return(0); |
| } |
| #endif /* NOFRILLS */ |
| #endif /* UNIX */ |
| |
| #ifndef NOFRILLS |
| int |
| zprint(p,f) char *p; char *f; { /* Print file f with options p */ |
| extern char * printername; /* From ckuus3.c */ |
| extern int printpipe; |
| int n; |
| |
| #ifdef CK_LOGIN |
| if (isguest) |
| return(-2); |
| #endif /* CK_LOGIN */ |
| |
| if (!f) f = ""; |
| if (!*f) return(-1); |
| |
| #ifdef CKROOT |
| debug(F111,"zprint setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(f)) { |
| debug(F110,"zprint setroot violation",f,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| debug(F110,"zprint file",f,0); |
| debug(F110,"zprint flags",p,0); |
| debug(F110,"zprint printername",printername,0); |
| debug(F101,"zprint printpipe","",printpipe); |
| |
| #ifdef UNIX |
| /* |
| Note use of standard input redirection. In some systems, lp[r] runs |
| setuid to lp (or ...?), so if user has sent a file into a directory |
| that lp does not have read access to, it can't be printed unless it is |
| fed to lp[r] as standard input. |
| */ |
| if (printpipe && printername) { |
| n = 8 + (int)strlen(f) + (int)strlen(printername); |
| if (n > ZMBUFLEN) |
| return(-2); |
| sprintf(zmbuf,"cat %s | %s", f, printername); |
| } else if (printername) { |
| n = 8 + (int)strlen(f) + (int)strlen(printername); |
| if (n > ZMBUFLEN) |
| return(-2); |
| sprintf(zmbuf,"cat %s >> %s", f, printername); |
| } else { |
| n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f); |
| if (n > ZMBUFLEN) |
| return(-2); |
| sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f); |
| } |
| debug(F110,"zprint command",zmbuf,0); |
| zsyscmd(zmbuf); |
| #else /* Not UNIX */ |
| *zmbuf = '\0'; |
| #endif /* UNIX */ |
| return(0); |
| } |
| #endif /* NOFRILLS */ |
| |
| /* Wildcard expansion functions... */ |
| |
| static char scratch[MAXPATH+4]; /* Used by both methods */ |
| |
| static int oldmtchs = 0; /* Let shell (ls) expand them. */ |
| #ifdef COMMENT |
| static char *lscmd = "/bin/ls -d"; /* Command to use. */ |
| #else |
| static char *lscmd = "echo"; /* Command to use. */ |
| #endif /* COMMENT */ |
| |
| #ifndef NOPUSH |
| int |
| shxpand(pat,namlst,len) char *pat, *namlst[]; int len; { |
| char *fgbuf = NULL; /* Buffer for forming ls command */ |
| char *p, *q; /* Workers */ |
| |
| int i, x, retcode, itsadir; |
| char c; |
| |
| x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */ |
| for (i = 0; i < oldmtchs; i++) { /* Free previous file list */ |
| if (namlst[i] ) { /* If memory is allocated */ |
| free(namlst[i]); /* Free the memory */ |
| namlst[i] = NULL ; /* Remember no memory is allocated */ |
| } |
| } |
| oldmtchs = 0 ; /* Remember there are no matches */ |
| fgbuf = malloc(x); /* Get buffer for command */ |
| if (!fgbuf) return(-1); /* Fail if cannot */ |
| ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */ |
| zxcmd(ZIFILE,fgbuf); /* Start the command */ |
| i = 0; /* File counter */ |
| p = scratch; /* Point to scratch area */ |
| retcode = -1; /* Assume failure */ |
| while ((x = zminchar()) != -1) { /* Read characters from command */ |
| c = (char) x; |
| if (c == ' ' || c == '\n') { /* Got newline or space? */ |
| *p = '\0'; /* Yes, terminate string */ |
| p = scratch; /* Point back to beginning */ |
| if (zchki(p) == -1) /* Does file exist? */ |
| continue; /* No, continue */ |
| itsadir = isdir(p); /* Yes, is it a directory? */ |
| if (xdironly && !itsadir) /* Want only dirs but this isn't */ |
| continue; /* so skip. */ |
| if (xfilonly && itsadir) /* It's a dir but want only files */ |
| continue; /* so skip. */ |
| x = (int)strlen(p); /* Keep - get length of name */ |
| q = malloc(x+1); /* Allocate space for it */ |
| if (!q) goto shxfin; /* Fail if space can't be obtained */ |
| strcpy(q,scratch); /* (safe) Copy name to space */ |
| namlst[i++] = q; /* Copy pointer to name into array */ |
| if (i >= len) goto shxfin; /* Fail if too many */ |
| } else { /* Regular character */ |
| *p++ = c; /* Copy it into scratch area */ |
| } |
| } |
| retcode = i; /* Return number of matching files */ |
| shxfin: /* Common exit point */ |
| free(fgbuf); /* Free command buffer */ |
| fgbuf = NULL; |
| zclosf(ZIFILE); /* Delete the command fork. */ |
| oldmtchs = i; /* Remember how many files */ |
| return(retcode); |
| } |
| #endif /* NOPUSH */ |
| |
| /* |
| Directory-reading functions for UNIX originally written for C-Kermit 4.0 |
| by Jeff Damens, CUCCA, 1984. |
| */ |
| static char * xpat = NULL; /* Global copy of fgen() pattern */ |
| static char * xpatlast = NULL; /* Rightmost segment of pattern*/ |
| static int xpatslash = 0; /* Slash count in pattern */ |
| static int xpatwild = 0; /* Original pattern is wild */ |
| static int xleafwild = 0; /* Last segment of pattern is wild */ |
| static int xpatabsolute = 0; |
| |
| #ifdef aegis |
| static char bslash; |
| #endif /* aegis */ |
| |
| |
| /* S P L I T P A T H */ |
| |
| /* |
| Splits the slash-separated portions of the argument string into |
| a list of path structures. Returns the head of the list. The |
| structures are allocated by malloc, so they must be freed. |
| Splitpath is used internally by the filename generator. |
| |
| Input: |
| A path string. |
| |
| Returns: |
| A linked list of the slash-separated segments of the input. |
| */ |
| static struct path * |
| splitpath(p) char *p; { |
| struct path *head,*cur,*prv; |
| int i; |
| |
| debug(F111,"splitpath",p,xrecursive); |
| head = prv = NULL; |
| |
| if (!p) return(NULL); |
| if (!*p) return(NULL); |
| |
| if (!strcmp(p,"**")) { /* Fix this */ |
| p = "*"; |
| } |
| if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */ |
| |
| /* Make linked list of path segments from pattern */ |
| |
| while (*p) { |
| cur = (struct path *) malloc(sizeof (struct path)); |
| debug(F101,"splitpath malloc","",cur); |
| if (cur == NULL) { |
| debug(F100,"splitpath malloc failure","",0); |
| prv -> fwd = NULL; |
| return((struct path *)NULL); |
| } |
| cur -> fwd = NULL; |
| if (head == NULL) /* First, make list head */ |
| head = cur; |
| else /* Not first, link into chain */ |
| prv -> fwd = cur; |
| prv = cur; /* Link from previous to this one */ |
| |
| #ifdef aegis |
| /* treat backslash as "../" */ |
| if (bslash && *p == bslash) { |
| strcpy(cur->npart, ".."); /* safe */ |
| ++p; |
| } else { |
| for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++) |
| cur -> npart[i] = *p++; |
| cur -> npart[i] = '\0'; /* end this segment */ |
| if (i >= MAXNAMLEN) |
| while (*p && *p != '/' && *p != bslash) |
| p++; |
| } |
| if (*p == '/') p++; |
| #else |
| /* General case (UNIX) */ |
| for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) { |
| cur -> npart[i] = *p++; |
| } |
| |
| cur -> npart[i] = '\0'; /* End this path segment */ |
| if (i >= MAXNAMLEN) |
| while (!ISDIRSEP(*p) && *p != '\0') p++; |
| if (ISDIRSEP(*p)) |
| p++; |
| |
| #endif /* aegis */ |
| } |
| if (prv) { |
| makestr(&xpatlast,prv -> npart); |
| debug(F110,"splitpath xpatlast",xpatlast,0); |
| } |
| #ifdef DEBUG |
| /* Show original path list */ |
| if (deblog) { |
| for (i = 0, cur = head; cur; i++) { |
| debug(F111,"SPLITPATH",cur -> npart, i); |
| cur = cur -> fwd; |
| } |
| } |
| #endif /* DEBUG */ |
| return(head); |
| } |
| |
| /* F G E N -- Generate File List */ |
| |
| /* |
| File name generator. It is passed a string, possibly containing wildcards, |
| and an array of character pointers. It finds all the matching filenames and |
| stores pointers to them in the array. The returned strings are allocated |
| from a static buffer local to this module (so the caller doesn't have to |
| worry about deallocating them); this means that successive calls to fgen |
| will wipe out the results of previous calls. |
| |
| Input: |
| A wildcard string, an array to write names to, the length of the array. |
| |
| Returns: |
| The number of matches. |
| The array is filled with filenames that matched the pattern. |
| If there wasn't enough room in the array, -1 is returned. |
| |
| Originally by: Jeff Damens, CUCCA, 1984. Many changes since then. |
| */ |
| static int |
| fgen(pat,resarry,len) char *pat,*resarry[]; int len; { |
| struct path *head; |
| char *sptr, *s; |
| int n; |
| |
| #ifdef aegis |
| char *namechars; |
| int tilde = 0, bquote = 0; |
| |
| if ((namechars = getenv("NAMECHARS")) != NULL) { |
| if (ckstrchr(namechars, '~' ) != NULL) tilde = '~'; |
| if (ckstrchr(namechars, '\\') != NULL) bslash = '\\'; |
| if (ckstrchr(namechars, '`' ) != NULL) bquote = '`'; |
| } else { |
| tilde = '~'; bslash = '\\'; bquote = '`'; |
| } |
| sptr = scratch; |
| |
| /* copy "`node_data", etc. anchors */ |
| if (bquote && *pat == bquote) |
| while (*pat && *pat != '/' && *pat != bslash) |
| *sptr++ = *pat++; |
| else if (tilde && *pat == tilde) |
| *sptr++ = *pat++; |
| while (*pat == '/') |
| *sptr++ = *pat++; |
| if (sptr == scratch) { |
| strcpy(scratch,"./"); /* safe */ |
| sptr = scratch+2; |
| } |
| if (!(head = splitpath(pat))) return(-1); |
| |
| #else /* not aegis */ |
| |
| debug(F111,"fgen pat",pat,len); |
| debug(F110,"fgen current directory",zgtdir(),0); |
| debug(F101,"fgen stathack","",stathack); |
| |
| scratch[0] = '\0'; |
| xpatwild = 0; |
| xleafwild = 0; |
| xpatabsolute = 0; |
| |
| if (!(head = splitpath(pat))) /* Make the path segment list */ |
| return(-1); |
| |
| sptr = scratch; |
| |
| #ifdef COMMENT |
| if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) { |
| #endif /* COMMENT */ |
| if (!ISDIRSEP(*pat)) /* If name is not absolute */ |
| *sptr++ = '.'; /* put "./" in front. */ |
| *sptr++ = DIRSEP; |
| #ifdef COMMENT |
| } |
| #endif /* COMMENT */ |
| *sptr = '\0'; |
| #endif /* aegis */ |
| |
| makestr(&xpat,pat); /* Save copy of original pattern */ |
| debug(F110,"fgen scratch",scratch,0); |
| |
| for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */ |
| if (*s == DIRSEP) /* since these are fences for */ |
| n++; /* pattern matching */ |
| xpatslash = n; |
| debug(F101,"fgen xpatslash","",xpatslash); |
| |
| numfnd = 0; /* None found yet */ |
| |
| if (initspace(resarry,ssplen) < 0) |
| return(-1); |
| |
| xpatwild = iswild(xpat); /* Original pattern is wild? */ |
| xpatabsolute = isabsolute(xpat); |
| xleafwild = iswild(xpatlast); |
| |
| debug(F111,"fgen xpat",xpat,xpatwild); |
| debug(F111,"fgen xpatlast",xpatlast,xleafwild); |
| debug(F101,"fgen xpatabsolute","",xpatabsolute); |
| |
| traverse(head,scratch,sptr); /* Go walk the directory tree. */ |
| while (head != NULL) { /* Done - free path segment list. */ |
| struct path *next = head -> fwd; |
| free((char *)head); |
| head = next; |
| } |
| debug(F101,"fgen","",numfnd); |
| return(numfnd); /* Return the number of matches */ |
| } |
| |
| /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */ |
| /* LONGFN can also be defined on the cc command line. */ |
| |
| #ifdef BSD29 |
| #ifndef LONGFN |
| #define LONGFN |
| #endif |
| #endif |
| |
| #ifdef BSD42 |
| #ifndef LONGFN |
| #define LONGFN |
| #endif |
| #endif |
| |
| /* |
| T R A V E R S E -- Traverse a directory tree. |
| |
| Walks the directory tree looking for matches to its arguments. |
| The algorithm is, briefly: |
| |
| If the current pattern segment contains no wildcards, that |
| segment is added to what we already have. If the name so far |
| exists, we call ourselves recursively with the next segment |
| in the pattern string; otherwise, we just return. |
| |
| If the current pattern segment contains wildcards, we open the name |
| we've accumulated so far (assuming it is really a directory), then read |
| each filename in it, and, if it matches the wildcard pattern segment, add |
| that filename to what we have so far and call ourselves recursively on |
| the next segment. |
| |
| Finally, when no more pattern segments remain, we add what's accumulated |
| so far to the result array and increment the number of matches. |
| |
| Inputs: |
| A pattern path list (as generated by splitpath), a string pointer that |
| points to what we've traversed so far (this can be initialized to "/" |
| to start the search at the root directory, or to "./" to start the |
| search at the current directory), and a string pointer to the end of |
| the string in the previous argument, plus the global "recursive", |
| "xmatchdot", and "xdironly" flags. |
| |
| Returns: void, with: |
| mtchs[] containing the array of filename string pointers, and: |
| numfnd containing the number of filenames. |
| |
| Although it might be poor practice, the mtchs[] array is revealed to the |
| outside in case it needs it; for example, to be sorted prior to use. |
| (It is poor practice because not all platforms implement file lists the |
| same way; some don't use an array at all.) |
| |
| Note that addresult() acts as a second-level filter; due to selection |
| criteria outside of the pattern, it might decline to add files that |
| this routine asks it to, e.g. because we are collecting only directory |
| names but not the names of regular files. |
| |
| WARNING: In the course of C-Kermit 7.0 development, this routine became |
| ridiculously complex, in order to meet approximately sixty specific |
| requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me; |
| it is not possible to fix anything in it without breaking something else. |
| This routine badly needs a total redesign and rewrite. Note: There may |
| be some good applications for realpath() and/or scandir() and/or fts_blah() |
| here, on platforms where they are available. |
| */ |
| static VOID |
| traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; { |
| |
| /* Appropriate declarations for directory routines and structures */ |
| /* #define OPENDIR means to use opendir(), readdir(), closedir() */ |
| /* If OPENDIR not defined, we use open(), read(), close() */ |
| |
| #ifdef DIRENT /* New way, <dirent.h> */ |
| #define OPENDIR |
| DIR *fd, *opendir(); |
| struct dirent *dirbuf; |
| struct dirent *readdir(); |
| #else /* !DIRENT */ |
| #ifdef LONGFN /* Old way, <dir.h> with opendir() */ |
| #define OPENDIR |
| DIR *fd, *opendir(); |
| struct direct *dirbuf; |
| #else /* !LONGFN */ |
| int fd; /* Old way, <dir.h> with open() */ |
| struct direct dir_entry; |
| struct direct *dirbuf = &dir_entry; |
| #endif /* LONGFN */ |
| #endif /* DIRENT */ |
| int mopts = 0; /* ckmatch() opts */ |
| int depth = 0; /* Directory tree depth */ |
| |
| char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */ |
| int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ; |
| struct stat statbuf; /* For file info. */ |
| |
| debug(F101,"STAT","",16); |
| if (pl == NULL) { /* End of path-segment list */ |
| *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ |
| debug(F110,"traverse add: end of path segment",sofar,0); |
| addresult(sofar,-1); |
| return; |
| } |
| if (stathack) { |
| /* This speeds up the search a lot and we still get good results */ |
| /* but it breaks the tagging of directory names done in addresult */ |
| if (xrecursive || xfilonly || xdironly || xpatslash) { |
| itsadir = xisdir(sofar); |
| debug(F101,"STAT","",17); |
| } else |
| itsadir = (strncmp(sofar,"./",2) == 0); |
| } else { |
| itsadir = xisdir(sofar); |
| debug(F101,"STAT","",18); |
| } |
| debug(F111,"traverse entry sofar",sofar,itsadir); |
| |
| #ifdef CKSYMLINK /* We're doing symlinks? */ |
| #ifdef USE_LSTAT /* OK to use lstat()? */ |
| if (itsadir && xnolinks) { /* If not following symlinks */ |
| int x; |
| struct stat buf; |
| x = lstat(sofar,&buf); |
| debug(F111,"traverse lstat 1",sofar,x); |
| if (x > -1 && |
| #ifdef S_ISLNK |
| S_ISLNK(buf.st_mode) |
| #else |
| #ifdef _IFLNK |
| ((_IFMT & buf.st_mode) == _IFLNK) |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| ) |
| itsadir = 0; |
| } |
| #endif /* USE_LSTAT */ |
| #endif /* CKSYMLINK */ |
| |
| if (!xmatchdot && xpatlast[0] == '.') |
| xmatchdot = 1; |
| if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.') |
| xmatchdot = 1; |
| |
| /* ckmatch() options */ |
| |
| if (xmatchdot) mopts |= 1; /* Match dot */ |
| if (!xrecursive) mopts |= 2; /* Dirsep is fence */ |
| |
| debug(F111,"traverse entry xpat",xpat,xpatslash); |
| debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot); |
| debug(F110,"traverse entry pl -> npart",pl -> npart,0); |
| |
| #ifdef RECURSIVE |
| if (xrecursive > 0 && !itsadir) { |
| char * s; /* Recursive descent and this is a regular file */ |
| *--endcur = '\0'; /* Terminate string, overwrite trailing slash */ |
| |
| /* Find the nth slash from the right and match from there... */ |
| /* (n == the number of slashes in the original pattern - see fgen) */ |
| if (*sofar == '/') { |
| debug(F110,"traverse xpatslash absolute",sofar,0); |
| s = sofar; |
| } else { |
| debug(F111,"traverse xpatslash relative",sofar,xpatslash); |
| for (s = endcur - 1, n = 0; s >= sofar; s--) { |
| if (*s == '/') { |
| if (++n >= xpatslash) { |
| s++; |
| break; |
| } |
| } |
| } |
| } |
| #ifndef NOSKIPMATCH |
| /* This speeds things up a bit. */ |
| /* If it causes trouble define NOSKIPMATCH and rebuild. */ |
| if (xpat[0] == '*' && !xpat[1]) |
| x = xmatchdot ? 1 : (s[0] != '.'); |
| else |
| #endif /* NOSKIPMATCH */ |
| x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */ |
| debug(F111,"traverse xpatslash ckmatch",s,x); |
| if (x > 0) { |
| debug(F110,"traverse add: recursive, match, && !isdir",sofar,0); |
| addresult(sofar,itsadir); |
| } |
| return; |
| } |
| #endif /* RECURSIVE */ |
| |
| debug(F111,"traverse sofar 2",sofar,0); |
| |
| segisdir = ((pl -> fwd) == NULL) ? 0 : 1; |
| itswild = iswild(pl -> npart); |
| |
| debug(F111,"traverse segisdir",sofar,segisdir); |
| debug(F111,"traverse itswild ",pl -> npart,itswild); |
| |
| #ifdef RECURSIVE |
| if (xrecursive > 0) { /* If recursing and... */ |
| if (segisdir && itswild) /* this is a dir and npart is wild */ |
| goto blah; /* or... */ |
| else if (!xpatabsolute && !xpatwild) /* search object is nonwild */ |
| goto blah; /* then go recurse */ |
| } |
| #endif /* RECURSIVE */ |
| |
| if (!itswild) { /* This path segment not wild? */ |
| #ifdef COMMENT |
| strcpy(endcur,pl -> npart); /* (safe) Append next part. */ |
| endcur += (int)strlen(pl -> npart); /* Advance end pointer */ |
| #else |
| /* |
| strcpy() does not account for quoted metacharacters. |
| We must remove the quotes before doing the stat(). |
| */ |
| { |
| int quote = 0; |
| char c, * s; |
| s = pl -> npart; |
| while ((c = *s++)) { |
| if (!quote) { |
| if (c == CMDQ) { |
| quote = 1; |
| continue; |
| } |
| } |
| *endcur++ = c; |
| quote = 0; |
| } |
| } |
| #endif /* COMMENT */ |
| *endcur = '\0'; /* End new current string. */ |
| |
| if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */ |
| debug(F110,"traverse exists",sofar,0); |
| *endcur++ = DIRSEP; /* add slash to end */ |
| *endcur = '\0'; /* and end the string again. */ |
| traverse(pl -> fwd, sofar, endcur); |
| } |
| #ifdef DEBUG |
| else debug(F110,"traverse not found", sofar, 0); |
| #endif /* DEBUG */ |
| return; |
| } |
| |
| *endcur = '\0'; /* End current string */ |
| debug(F111,"traverse sofar 3",sofar,0); |
| |
| if (!itsadir) |
| return; |
| |
| /* Search is recursive or ... */ |
| /* path segment contains wildcards, have to open and search directory. */ |
| |
| blah: |
| |
| debug(F110,"traverse opening directory", sofar, 0); |
| |
| #ifdef OPENDIR |
| debug(F110,"traverse opendir()",sofar,0); |
| if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */ |
| debug(F101,"traverse opendir() failed","",errno); |
| return; |
| } |
| while ((dirbuf = readdir(fd))) |
| #else /* !OPENDIR */ |
| debug(F110,"traverse directory open()",sofar,0); |
| if ((fd = open(sofar,O_RDONLY)) < 0) { |
| debug(F101,"traverse directory open() failed","",errno); |
| return; |
| } |
| while (read(fd, (char *)dirbuf, sizeof dir_entry)) |
| #endif /* OPENDIR */ |
| { /* Read each entry in this directory */ |
| int exists; |
| char *eos, *s; |
| exists = 0; |
| |
| /* On some platforms, the read[dir]() can return deleted files, */ |
| /* e.g. HP-UX 5.00. There is no point in grinding through this */ |
| /* routine when the file doesn't exist... */ |
| |
| if ( /* There actually is an inode... */ |
| #ifdef BSD42 |
| dirbuf->d_ino != -1 |
| #else |
| #ifdef unos |
| dirbuf->d_ino != -1 |
| #else |
| #ifdef QNX |
| dirbuf->d_stat.st_ino != 0 |
| #else |
| #ifdef SOLARIS |
| dirbuf->d_ino != 0 |
| #else |
| #ifdef sun |
| dirbuf->d_fileno != 0 |
| #else |
| #ifdef bsdi |
| dirbuf->d_fileno != 0 |
| #else |
| #ifdef __386BSD__ |
| dirbuf->d_fileno != 0 |
| #else |
| #ifdef __FreeBSD__ |
| dirbuf->d_fileno != 0 |
| #else |
| #ifdef ultrix |
| dirbuf->gd_ino != 0 |
| #else |
| #ifdef Plan9 |
| 1 |
| #else |
| dirbuf->d_ino != 0 |
| #endif /* Plan9 */ |
| #endif /* ultrix */ |
| #endif /* __FreeBSD__ */ |
| #endif /* __386BSD__ */ |
| #endif /* bsdi */ |
| #endif /* sun */ |
| #endif /* SOLARIS */ |
| #endif /* QNX */ |
| #endif /* unos */ |
| #endif /* BSD42 */ |
| ) |
| exists = 1; |
| if (!exists) |
| continue; |
| |
| ckstrncpy(nambuf, /* Copy the name */ |
| dirbuf->d_name, |
| MAXNAMLEN |
| ); |
| if (nambuf[0] == '.') { |
| if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) { |
| debug(F110,"traverse skipping",nambuf,0); |
| continue; /* skip "." and ".." */ |
| } |
| } |
| s = nambuf; /* Copy name to end of sofar */ |
| eos = endcur; |
| while ((*eos = *s)) { |
| s++; |
| eos++; |
| } |
| /* |
| Now we check the file for (a) whether it is a directory, and (b) whether |
| its name matches our pattern. If it is a directory, and if we have been |
| told to build a recursive list, then we must descend regardless of whether |
| it matches the pattern. If it is not a directory and it does not match |
| our pattern, we skip it. Note: sofar is the full pathname, nambuf is |
| the name only. |
| */ |
| /* Do this first to save pointless function calls */ |
| if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */ |
| continue; |
| if (stathack) { |
| if (xrecursive || xfilonly || xdironly || xpatslash) { |
| itsadir = xisdir(sofar); /* See if it's a directory */ |
| debug(F101,"STAT","",19); |
| } else { |
| itsadir = 0; |
| } |
| } else { |
| itsadir = xisdir(sofar); |
| debug(F101,"STAT","",20); |
| } |
| |
| #ifdef CKSYMLINK |
| #ifdef USE_LSTAT |
| if (itsadir && xnolinks) { /* If not following symlinks */ |
| int x; |
| struct stat buf; |
| x = lstat(sofar,&buf); |
| debug(F111,"traverse lstat 2",sofar,x); |
| if (x > -1 && |
| #ifdef S_ISLNK |
| S_ISLNK(buf.st_mode) |
| #else |
| #ifdef _IFLNK |
| ((_IFMT & buf.st_mode) == _IFLNK) |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| ) |
| itsadir = 0; |
| } |
| #endif /* USE_LSTAT */ |
| #endif /* CKSYMLINK */ |
| |
| #ifdef RECURSIVE |
| if (xrecursive > 0 && itsadir && |
| (xpatlast[0] == '*') && !xpatlast[1] |
| ) { |
| debug(F110, |
| "traverse add: recursive && isdir && segisdir or match", |
| sofar, |
| segisdir |
| ); |
| addresult(sofar,itsadir); |
| if (numfnd < 0) return; |
| } |
| #endif /* RECURSIVE */ |
| |
| debug(F111,"traverse mresult xpat",xpat,xrecursive); |
| debug(F111,"traverse mresult pl -> npart", |
| pl -> npart, |
| ((pl -> fwd) ? 9999 : 0) |
| ); |
| debug(F111,"traverse mresult sofar segisdir",sofar,segisdir); |
| debug(F111,"traverse mresult sofar itsadir",sofar,itsadir); |
| debug(F101,"traverse mresult xmatchdot","",xmatchdot); |
| /* |
| Match the path so far with the pattern after stripping any leading "./" |
| from either or both. The pattern chosen is the full original pattern if |
| the match candidate (sofar) is not a directory, or else just the name part |
| (pl->npart) if it is. |
| */ |
| { |
| char * s1; /* The pattern */ |
| char * s2 = sofar; /* The path so far */ |
| char * s3; /* Worker */ |
| int opts; /* Match options */ |
| |
| s1 = itsadir ? pl->npart : xpat; |
| |
| #ifndef COMMENT |
| /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */ |
| if (itsadir && !xrecursive && xpatslash > 0 && |
| segisdir == 0 && itswild) { |
| s1 = xpat; |
| debug(F110,"traverse mresult s1 kludge",s1,0); |
| } |
| #endif /* COMMENT */ |
| |
| if (xrecursive && xpatslash == 0) |
| s2 = nambuf; |
| while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */ |
| s1 += 2; |
| while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */ |
| s2 += 2; |
| opts = mopts; /* Match options */ |
| if (itsadir) /* Current segment is a directory */ |
| opts = mopts & 1; /* No fences */ |
| s3 = s2; /* Get segment depth */ |
| depth = 0; |
| while (*s3) { if (*s3++ == '/') depth++; } |
| #ifndef NOSKIPMATCH |
| /* This speeds things up a bit. */ |
| /* If it causes trouble define NOSKIPMATCH and rebuild. */ |
| if (depth == 0 && (s1[0] == '*') && !s1[1]) |
| mresult = xmatchdot ? 1 : (s2[0] != '.'); |
| else |
| #endif /* NOSKIPMATCH */ |
| mresult = ckmatch(s1,s2,1,opts); /* Match */ |
| } |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F111,"traverse mresult depth",sofar,depth); |
| debug(F101,"traverse mresult xpatslash","",xpatslash); |
| debug(F111,"traverse mresult nambuf",nambuf,mresult); |
| debug(F111,"traverse mresult itswild",pl -> npart,itswild); |
| debug(F111,"traverse mresult segisdir",pl -> npart,segisdir); |
| } |
| #endif /* DEBUG */ |
| if (mresult || /* If match succeeded */ |
| xrecursive || /* Or search is recursive */ |
| depth < xpatslash /* Or not deep enough to match... */ |
| ) { |
| if ( /* If it's not a directory... */ |
| /* |
| The problem here is that segisdir is apparently not set appropriately. |
| If I leave in the !segisdir test, then "dir /recursive blah" (where blah is |
| a directory name) misses some regular files because sometimes segisdir |
| is set and sometimes it's not. But if I comment it out, then |
| "dir <star>/<star>.txt lists every file in * and does not even open up the |
| subdirectories. However, "dir /rec <star>/<star>.txt" works right. |
| */ |
| #ifdef COMMENT |
| mresult && (!itsadir && !segisdir) |
| #else |
| mresult && /* Matched */ |
| !itsadir && /* sofar is not a directory */ |
| ((!xrecursive && !segisdir) || xrecursive) |
| #endif /* COMMENT */ |
| ) { |
| debug(F110, |
| "traverse add: match && !itsadir",sofar,0); |
| addresult(sofar,itsadir); |
| if (numfnd < 0) return; |
| } else if (itsadir && (xrecursive || mresult)) { |
| struct path * xx = NULL; |
| *eos++ = DIRSEP; /* Add directory separator */ |
| *eos = '\0'; /* to end of segment */ |
| #ifdef RECURSIVE |
| /* Copy previous pattern segment to this new directory */ |
| |
| if (xrecursive > 0 && !(pl -> fwd)) { |
| xx = (struct path *) malloc(sizeof (struct path)); |
| pl -> fwd = xx; |
| if (xx) { |
| xx -> fwd = NULL; |
| strcpy(xx -> npart, pl -> npart); /* safe */ |
| } |
| } |
| #endif /* RECURSIVE */ |
| traverse(pl -> fwd, sofar, eos); /* Traverse new directory */ |
| } |
| } |
| } |
| #ifdef OPENDIR |
| closedir(fd); |
| #else /* !OPENDIR */ |
| close(fd); |
| #endif /* OPENDIR */ |
| } |
| |
| /* |
| * addresult: |
| * Adds a result string to the result array. Increments the number |
| * of matches found, copies the found string into our string |
| * buffer, and puts a pointer to the buffer into the caller's result |
| * array. Our free buffer pointer is updated. If there is no |
| * more room in the caller's array, the number of matches is set to -1. |
| * Input: a result string. |
| * Returns: nothing. |
| */ |
| static VOID |
| addresult(str,itsadir) char *str; int itsadir; { |
| int len; |
| |
| if (!freeptr) { |
| debug(F100,"addresult string space not init'd","",0); |
| initspace(mtchs,ssplen); |
| } |
| if (!str) str = ""; |
| debug(F111,"addresult",str,itsadir); |
| if (!*str) |
| return; |
| |
| if (itsadir < 0) { |
| itsadir = xisdir(str); |
| } |
| if ((xdironly && !itsadir) || (xfilonly && itsadir)) { |
| debug(F111,"addresult skip",str,itsadir); |
| return; |
| } |
| while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */ |
| str += 2; |
| if (--remlen < 0) { /* Elements left in array of names */ |
| debug(F111,"addresult ARRAY FULL",str,numfnd); |
| numfnd = -1; |
| return; |
| } |
| len = (int)strlen(str); /* Space this will use */ |
| debug(F111,"addresult len",str,len); |
| |
| if (len < 1) |
| return; |
| |
| if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) { |
| debug(F111,"addresult OUT OF SPACE",str,numfnd); |
| #ifdef DYNAMIC |
| printf( |
| "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen); |
| #else |
| printf("?String space %d exhausted\n",ssplen); |
| #endif /* DYNAMIC */ |
| numfnd = -1; /* Do not record if not enough space */ |
| return; |
| } |
| strcpy(freeptr,str); /* safe */ |
| |
| /* Tag directory names by putting '/' at the end */ |
| |
| if (itsadir && (freeptr[len-1] == '/')) { |
| freeptr[len++] = DIRSEP; |
| freeptr[len] = '\0'; |
| } |
| if (numfnd >= maxnames) { |
| #ifdef DYNAMIC |
| printf( |
| "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames); |
| #else |
| printf("?Too many files - %d max\n",maxnames); |
| #endif /* DYNAMIC */ |
| numfnd = -1; |
| return; |
| } |
| str = freeptr; |
| *resptr++ = freeptr; |
| freeptr += (len + 1); |
| numfnd++; |
| debug(F111,"addresult ADD",str,numfnd); |
| } |
| |
| #ifdef COMMENT |
| /* |
| * match(pattern,string): |
| * pattern matcher. Takes a string and a pattern possibly containing |
| * the wildcard characters '*' and '?'. Returns true if the pattern |
| * matches the string, false otherwise. |
| * Orignally by: Jeff Damens, CUCCA, 1984 |
| * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c). |
| * |
| * Input: a string and a wildcard pattern. |
| * Returns: 1 if match, 0 if no match. |
| */ |
| static int |
| match(pattern, string) char *pattern, *string; { |
| char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */ |
| int q = 0; /* Quote flag */ |
| |
| if (*string == '.' && *pattern != '.' && !xmatchdot) { |
| debug(F110,"match skip",string,0); |
| return(0); |
| } |
| while (1) { |
| for (; *pattern == *string; pattern++,string++) /* Skip first */ |
| if (*string == '\0') return(1); /* End of strings, succeed */ |
| |
| if (*pattern == '\\' && q == 0) { /* Watch out for quoted */ |
| q = 1; /* metacharacters */ |
| pattern++; /* advance past quote */ |
| if (*pattern != *string) return(0); |
| continue; |
| } else q = 0; |
| |
| if (q) { |
| return(0); |
| } else { |
| if (*string != '\0' && *pattern == '?') { |
| pattern++; /* '?', let it match */ |
| string++; |
| } else if (*pattern == '*') { /* '*' ... */ |
| psave = ++pattern; /* remember where we saw it */ |
| ssave = string; /* let it match 0 chars */ |
| } else if (ssave != NULL && *ssave != '\0') { /* if not at end */ |
| /* ...have seen a star */ |
| string = ++ssave; /* skip 1 char from string */ |
| pattern = psave; /* and back up pattern */ |
| } else return(0); /* otherwise just fail */ |
| } |
| } |
| } |
| #endif /* COMMENT */ |
| |
| /* |
| The following two functions are for expanding tilde in filenames |
| Contributed by Howie Kaye, CUCCA, developed for CCMD package. |
| */ |
| |
| /* W H O A M I -- Get user's username. */ |
| |
| /* |
| 1) Get real uid |
| 2) See if the $USER environment variable is set ($LOGNAME on AT&T) |
| 3) If $USER's uid is the same as ruid, realname is $USER |
| 4) Otherwise get logged in user's name |
| 5) If that name has the same uid as the real uid realname is loginname |
| 6) Otherwise, get a name for ruid from /etc/passwd |
| */ |
| char * |
| whoami() { |
| #ifdef DTILDE |
| #ifdef pdp11 |
| #define WHOLEN 100 |
| #else |
| #define WHOLEN 257 |
| #endif /* pdp11 */ |
| static char realname[UIDBUFLEN+1]; /* user's name */ |
| static int ruid = -1; /* user's real uid */ |
| char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */ |
| char *c; |
| struct passwd *p; |
| _PROTOTYP(extern char * getlogin, (void) ); |
| |
| if (ruid != -1) |
| return(realname); |
| |
| ruid = real_uid(); /* get our uid */ |
| |
| /* how about $USER or $LOGNAME? */ |
| if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */ |
| ckstrncpy(envname, c, 255); |
| if ((p = getpwnam(envname)) != NULL) { |
| if (p->pw_uid == ruid) { /* get passwd entry for envname */ |
| ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */ |
| return(realname); |
| } |
| } |
| } |
| |
| /* can we use loginname() ? */ |
| |
| if ((c = getlogin()) != NULL) { /* name from utmp file */ |
| ckstrncpy (loginname, c, UIDBUFLEN); |
| if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */ |
| if (p->pw_uid == ruid) /* for loginname */ |
| ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */ |
| } |
| |
| /* Use first name we get for ruid */ |
| |
| if ((p = getpwuid(ruid)) == NULL) { /* name for uid */ |
| realname[0] = '\0'; /* no user name */ |
| ruid = -1; |
| return(NULL); |
| } |
| ckstrncpy(realname, p->pw_name, UIDBUFLEN); |
| return(realname); |
| #else |
| return(NULL); |
| #endif /* DTILDE */ |
| } |
| |
| /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */ |
| |
| char * |
| tilde_expand(dirname) char *dirname; { |
| #ifdef DTILDE |
| #ifdef pdp11 |
| #define BUFLEN 100 |
| #else |
| #define BUFLEN 257 |
| #endif /* pdp11 */ |
| struct passwd *user; |
| static char olddir[BUFLEN+1]; |
| static char oldrealdir[BUFLEN+1]; |
| static char temp[BUFLEN+1]; |
| int i, j; |
| |
| debug(F111,"tilde_expand",dirname,dirname[0]); |
| |
| if (dirname[0] != '~') /* Not a tilde...return param */ |
| return(dirname); |
| if (!strcmp(olddir,dirname)) { /* Same as last time */ |
| return(oldrealdir); /* so return old answer. */ |
| } else { |
| j = (int)strlen(dirname); |
| for (i = 0; i < j; i++) /* find username part of string */ |
| if (!ISDIRSEP(dirname[i])) |
| temp[i] = dirname[i]; |
| else break; |
| temp[i] = '\0'; /* tie off with a NULL */ |
| if (i == 1) { /* if just a "~" */ |
| #ifdef IKSD |
| if (inserver) |
| user = getpwnam(uidbuf); /* Get info on current user */ |
| else |
| #endif /* IKSD */ |
| { |
| char * p = whoami(); |
| if (p) |
| user = getpwnam(p); |
| else |
| user = NULL; |
| } |
| } else { |
| user = getpwnam(&temp[1]); /* otherwise on the specified user */ |
| } |
| } |
| if (user != NULL) { /* valid user? */ |
| ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */ |
| ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */ |
| ckstrncat(oldrealdir,&dirname[i], BUFLEN); |
| oldrealdir[BUFLEN] = '\0'; |
| return(oldrealdir); |
| } else { /* invalid? */ |
| ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */ |
| ckstrncpy(oldrealdir, dirname, BUFLEN); |
| return(oldrealdir); |
| } |
| #else |
| return(NULL); |
| #endif /* DTILDE */ |
| } |
| |
| /* |
| Functions for executing system commands. |
| zsyscmd() executes the system command in the normal, default way for |
| the system. In UNIX, it does what system() does. Thus, its results |
| are always predictable. |
| zshcmd() executes the command using the user's preferred shell. |
| */ |
| int |
| zsyscmd(s) char *s; { |
| #ifdef aegis |
| if (nopush) return(-1); |
| if (!priv_chk()) return(system(s)); |
| #else |
| PID_T shpid; |
| #ifdef COMMENT |
| /* This doesn't work... */ |
| WAIT_T status; |
| #else |
| int status; |
| #endif /* COMMENT */ |
| |
| if (nopush) return(-1); |
| if ((shpid = fork())) { |
| if (shpid < (PID_T)0) return(-1); /* Parent */ |
| while (shpid != (PID_T) wait(&status)) |
| ; |
| return(status); |
| } |
| if (priv_can()) { /* Child: cancel any priv's */ |
| printf("?Privilege cancellation failure\n"); |
| _exit(255); |
| } |
| restorsigs(); /* Restore ignored signals */ |
| #ifdef HPUX10 |
| execl("/usr/bin/sh","sh","-c",s,NULL); |
| perror("/usr/bin/sh"); |
| #else |
| #ifdef Plan9 |
| execl("/bin/rc", "rc", "-c", s, NULL); |
| perror("/bin/rc"); |
| #else |
| execl("/bin/sh","sh","-c",s,NULL); |
| perror("/bin/sh"); |
| #endif /* Plan9 */ |
| #endif /* HPUX10 */ |
| _exit(255); |
| return(0); /* Shut up ANSI compilers. */ |
| #endif /* aegis */ |
| } |
| |
| |
| /* Z _ E X E C -- Overlay ourselves with another program */ |
| |
| #ifndef NOZEXEC |
| #ifdef HPUX5 |
| #define NOZEXEC |
| #else |
| #ifdef ATT7300 |
| #define NOZEXEC |
| #endif /* ATT7300 */ |
| #endif /* HPUX5 */ |
| #endif /* NOZEXEC */ |
| |
| VOID |
| z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */ |
| #ifdef NOZEXEC |
| printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n"); |
| debug(F110,"z_exec NOT IMPLEMENTED",p,0); |
| #else |
| int x; |
| extern int ttyfd; |
| debug(F110,"z_exec command",p,0); |
| debug(F110,"z_exec arg 0",s[0],0); |
| debug(F110,"z_exec arg 1",s[1],0); |
| debug(F101,"z_exec t","",t); |
| errno = 0; |
| if (t) { |
| if (ttyfd > 2) { |
| dup2(ttyfd, 0); |
| dup2(ttyfd, 1); |
| /* dup2(ttyfd, 2); */ |
| close(ttyfd); |
| } |
| } |
| restorsigs(); /* Restore ignored signals */ |
| x = execvp(p,s); |
| if (x < 0) debug(F101,"z_exec errno","",errno); |
| #endif /* NOZEXEC */ |
| } |
| |
| /* |
| Z S H C M D -- Execute a shell command (or program thru the shell). |
| |
| Original UNIX code by H. Fischer; copyright rights assigned to Columbia U. |
| Adapted to use getpwuid to find login shell because many systems do not |
| have SHELL in environment, and to use direct calling of shell rather |
| than intermediate system() call. -- H. Fischer (1985); many changes since |
| then. Call with s pointing to command to execute. Returns: |
| -1 on failure to start the command (can't find, can't fork, can't run). |
| 1 if command ran and gave an exit status of 0. |
| 0 if command ran and gave a nonzero exit status. |
| with pexitstatus containing the command's exit status. |
| */ |
| int |
| zshcmd(s) char *s; { |
| PID_T pid; |
| |
| #ifdef NOPUSH |
| return(0); |
| #else |
| if (nopush) return(-1); |
| debug(F110,"zshcmd command",s,0); |
| |
| #ifdef aegis |
| if ((pid = vfork()) == 0) { /* Make child quickly */ |
| char *shpath, *shname, *shptr; /* For finding desired shell */ |
| |
| if (priv_can()) exit(1); /* Turn off privs. */ |
| if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh"; |
| |
| #else /* All Unix systems */ |
| if ((pid = fork()) == 0) { /* Make child */ |
| char *shpath, *shname, *shptr; /* For finding desired shell */ |
| struct passwd *p; |
| #ifdef HPUX10 /* Default */ |
| char *defshell = "/usr/bin/sh"; |
| #else |
| #ifdef Plan9 |
| char *defshell = "/bin/rc"; |
| #else |
| char *defshell = "/bin/sh"; |
| #endif /* Plan9 */ |
| #endif /* HPUX10 */ |
| if (priv_can()) exit(1); /* Turn off privs. */ |
| #ifdef COMMENT |
| /* Old way always used /etc/passwd shell */ |
| p = getpwuid(real_uid()); /* Get login data */ |
| if (p == (struct passwd *) NULL || !*(p->pw_shell)) |
| shpath = defshell; |
| else |
| shpath = p->pw_shell; |
| #else |
| /* New way lets user override with SHELL variable, but does not rely on it. */ |
| /* This allows user to specify a different shell. */ |
| shpath = getenv("SHELL"); /* What shell? */ |
| debug(F110,"zshcmd SHELL",shpath,0); |
| if (shpath == NULL) { |
| p = getpwuid( real_uid() ); /* Get login data */ |
| if (p == (struct passwd *)NULL || !*(p->pw_shell)) |
| shpath = defshell; |
| else shpath = p->pw_shell; |
| debug(F110,"zshcmd shpath",shpath,0); |
| } |
| #endif /* COMMENT */ |
| #endif /* aegis */ |
| shptr = shname = shpath; |
| while (*shptr != '\0') |
| if (*shptr++ == DIRSEP) |
| shname = shptr; |
| restorsigs(); /* Restore ignored signals */ |
| debug(F110,"zshcmd shname",shname,0); |
| if (s == NULL || *s == '\0') { /* Interactive shell requested? */ |
| execl(shpath,shname,"-i",NULL); /* Yes, do that */ |
| } else { /* Otherwise, */ |
| execl(shpath,shname,"-c",s,NULL); /* exec the given command */ |
| } /* If execl() failed, */ |
| exit(BAD_EXIT); /* return bad return code. */ |
| |
| } else { /* Parent */ |
| |
| int wstat; /* ... must wait for child */ |
| #ifdef CK_CHILD |
| int child; /* Child's exit status */ |
| #endif /* CK_CHILD */ |
| SIGTYP (*istat)(), (*qstat)(); |
| |
| if (pid == (PID_T) -1) return(-1); /* fork() failed? */ |
| |
| istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */ |
| qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */ |
| |
| #ifdef CK_CHILD |
| while (((wstat = wait(&child)) != pid) && (wstat != -1)) |
| #else |
| while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1)) |
| #endif /* CK_CHILD */ |
| ; /* Wait for fork */ |
| signal(SIGINT,istat); /* Restore interrupts */ |
| signal(SIGQUIT,qstat); |
| #ifdef CK_CHILD |
| pexitstat = (child & 0xff) ? child : child >> 8; |
| debug(F101,"zshcmd exit status","",pexitstat); |
| return(child == 0 ? 1 : 0); /* Return child's status */ |
| #endif /* CK_CHILD */ |
| } |
| return(1); |
| #endif /* NOPUSH */ |
| } |
| |
| /* I S W I L D -- Check if filespec is "wild" */ |
| |
| /* |
| Returns: |
| 0 if argument is empty or is the name of a single file; |
| 1 if it contains wildcard characters. |
| Note: must match the algorithm used by match(), hence no [a-z], etc. |
| */ |
| int |
| iswild(filespec) char *filespec; { |
| char c, *p, *f; int x; |
| int quo = 0; |
| if (!filespec) |
| return(0); |
| f = filespec; |
| if (wildxpand) { /* Shell handles wildcarding */ |
| if ((x = nzxpand(filespec,0)) > 1) |
| return(1); |
| if (x == 0) return(0); /* File does not exist */ |
| p = malloc(MAXNAMLEN + 20); |
| znext(p); |
| x = (strcmp(filespec,p) != 0); |
| free(p); |
| p = NULL; |
| return(x); |
| } else { /* We do it ourselves */ |
| while ((c = *filespec++) != '\0') { |
| if (c == '\\' && quo == 0) { |
| quo = 1; |
| continue; |
| } |
| if (!quo && (c == '*' || c == '?' |
| #ifdef CKREGEX |
| #ifndef VMS |
| || c == '[' |
| #endif /* VMS */ |
| || c == '{' |
| #endif /* CKREGEX */ |
| )) { |
| debug(F111,"iswild",f,1); |
| return(1); |
| } |
| quo = 0; |
| } |
| debug(F111,"iswild",f,0); |
| return(0); |
| } |
| } |
| |
| /* |
| I S D I R -- Is a Directory. |
| |
| Tell if string pointer s is the name of an existing directory. Returns 1 if |
| directory, 0 if not a directory. |
| |
| The following no longer applies: |
| |
| If the file is a symlink, we return 1 if |
| it is a directory OR if it is a link to a directory and the "xrecursive" flag |
| is NOT set. This is to allow parsing a link to a directory as if it were a |
| directory (e.g. in the CD or IF DIRECTORY command) but still prevent |
| recursive traversal from visiting the same directory twice. |
| */ |
| |
| #ifdef ISDIRCACHE |
| /* This turns out to be unsafe and gives little benefit anyway. */ |
| /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */ |
| |
| static char prevpath[CKMAXPATH+4] = { '\0', '\0' }; |
| static int prevstat = -1; |
| int |
| clrdircache() { |
| debug(F100,"CLEAR ISDIR CACHE","",0); |
| prevstat = -1; |
| prevpath[0] = NUL; |
| } |
| #endif /* ISDIRCACHE */ |
| |
| int |
| isdir(s) char *s; { |
| int x, needrlink = 0, islink = 0; |
| struct stat statbuf; |
| char fnam[CKMAXPATH+4]; |
| |
| if (!s) return(0); |
| if (!*s) return(0); |
| |
| #ifdef ISDIRCACHE |
| if (prevstat > -1) { |
| if (s[0] == prevpath[0]) { |
| if (!strcmp(s,prevpath)) { |
| debug(F111,"isdir cache hit",s,prevstat); |
| return(prevstat); |
| } |
| } |
| } |
| #endif /* ISDIRCACHE */ |
| |
| #ifdef CKSYMLINK |
| #ifdef COMMENT |
| /* |
| The following over-clever bit has been commented out because it presumes |
| to know when a symlink might be redundant, which it can't possibly know. |
| Using plain old stat() gives Kermit the same results as ls and ls -R, which |
| is just fine: no surprises. |
| */ |
| #ifdef USE_LSTAT |
| if (xrecursive) { |
| x = lstat(s,&statbuf); |
| debug(F111,"isdir lstat",s,x); |
| } else { |
| #endif /* USE_LSTAT */ |
| x = stat(s,&statbuf); |
| debug(F111,"isdir stat",s,x); |
| #ifdef USE_LSTAT |
| } |
| #endif /* USE_LSTAT */ |
| #else |
| x = stat(s,&statbuf); |
| debug(F111,"isdir stat",s,x); |
| #endif /* COMMENT */ |
| if (x == -1) { |
| debug(F101,"isdir errno","",errno); |
| return(0); |
| } |
| islink = 0; |
| if (xrecursive) { |
| #ifdef NOLINKBITS |
| needrlink = 1; |
| #else |
| #ifdef S_ISLNK |
| islink = S_ISLNK(statbuf.st_mode); |
| debug(F101,"isdir S_ISLNK islink","",islink); |
| #else |
| #ifdef _IFLNK |
| islink = (_IFMT & statbuf.st_mode) == _IFLNK; |
| debug(F101,"isdir _IFLNK islink","",islink); |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| #endif /* NOLINKBITS */ |
| if (needrlink) { |
| if (readlink(s,fnam,CKMAXPATH) > -1) |
| islink = 1; |
| } |
| } |
| #else |
| x = stat(s,&statbuf); |
| if (x == -1) { |
| debug(F101,"isdir errno","",errno); |
| return(0); |
| } |
| debug(F111,"isdir stat",s,x); |
| #endif /* CKSYMLINK */ |
| debug(F101,"isdir islink","",islink); |
| debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode); |
| x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0); |
| #ifdef ISDIRCACHE |
| prevstat = x; |
| ckstrncpy(prevpath,s,CKMAXPATH+1); |
| #endif /* ISDIRCACHE */ |
| return(x); |
| } |
| |
| #ifdef CK_MKDIR |
| /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */ |
| |
| /* Z M K D I R -- Create directory(s) if necessary */ |
| /* |
| Call with: |
| A pointer to a file specification that might contain directory |
| information. The filename is expected to be included. |
| If the file specification does not include any directory separators, |
| then it is assumed to be a plain file. |
| If one or more directories are included in the file specification, |
| this routine tries to create them if they don't already exist. |
| Returns: |
| 0 or greater on success, i.e. the number of directories created. |
| -1 on failure to create the directory |
| */ |
| int |
| zmkdir(path) char *path; { |
| char *xp, *tp, c; |
| int x, count = 0; |
| |
| if (!path) path = ""; |
| if (!*path) return(-1); |
| |
| #ifdef CKROOT |
| debug(F111,"zmkdir setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(path)) { |
| debug(F110,"zmkdir setroot violation",path,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| x = strlen(path); |
| debug(F111,"zmkdir",path,x); |
| if (x < 1 || x > MAXPATH) /* Check length */ |
| return(-1); |
| if (!(tp = malloc(x+1))) /* Make a temporary copy */ |
| return(-1); |
| strcpy(tp,path); /* safe (prechecked) */ |
| #ifdef DTILDE |
| if (*tp == '~') { /* Starts with tilde? */ |
| xp = tilde_expand(tp); /* Attempt to expand tilde */ |
| if (!xp) xp = ""; |
| if (*xp) { |
| char *zp; |
| debug(F110,"zmkdir tilde_expand",xp,0); |
| if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */ |
| free(tp); |
| tp = NULL; |
| return(-1); |
| } |
| free(tp); /* Free previous buffer */ |
| tp = zp; /* Point to new one */ |
| strcpy(tp,xp); /* Copy expanded name to new buffer */ |
| } |
| } |
| #endif /* DTILDE */ |
| debug(F110,"zmkdir tp after tilde_expansion",tp,0); |
| xp = tp; |
| if (ISDIRSEP(*xp)) /* Don't create root directory! */ |
| xp++; |
| |
| /* Go thru filespec from left to right... */ |
| |
| for (; *xp; xp++) { /* Create parts that don't exist */ |
| if (!ISDIRSEP(*xp)) /* Find next directory separator */ |
| continue; |
| c = *xp; /* Got one. */ |
| *xp = NUL; /* Make this the end of the string. */ |
| if (!isdir(tp)) { /* This directory exists already? */ |
| #ifdef CK_LOGIN |
| if (isguest) /* Not allowed for guests */ |
| return(-1); |
| #ifndef NOXFER |
| /* Nor if MKDIR and/or CD are disabled */ |
| else |
| #endif /* CK_LOGIN */ |
| if ((server |
| #ifdef IKSD |
| || inserver |
| #endif /* IKSD */ |
| ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd))) |
| return(-1); |
| #endif /* IKSD */ |
| |
| debug(F110,"zmkdir making",tp,0); |
| x = /* No, try to create it */ |
| #ifdef NOMKDIR |
| -1 /* Systems without mkdir() */ |
| #else |
| mkdir(tp,0777) /* UNIX */ |
| #endif /* NOMKDIR */ |
| ; |
| if (x < 0) { |
| debug(F101,"zmkdir failed, errno","",errno); |
| free(tp); /* Free temporary buffer. */ |
| tp = NULL; |
| return(-1); /* Return failure code. */ |
| } else |
| count++; |
| } |
| *xp = c; /* Replace the separator. */ |
| } |
| free(tp); /* Free temporary buffer. */ |
| return(count); /* Return success code. */ |
| } |
| #endif /* CK_MKDIR */ |
| |
| int |
| zrmdir(path) char *path; { |
| #ifdef CK_LOGIN |
| if (isguest) |
| return(-1); |
| #endif /* CK_LOGIN */ |
| |
| if (!path) path = ""; |
| if (!*path) return(-1); |
| |
| #ifdef CKROOT |
| debug(F111,"zrmdir setroot",ckroot,ckrootset); |
| if (ckrootset) if (!zinroot(path)) { |
| debug(F110,"zrmdir setroot violation",path,0); |
| return(-1); |
| } |
| #endif /* CKROOT */ |
| |
| #ifndef NOMKDIR |
| return(rmdir(path)); |
| #else |
| return(-1); |
| #endif /* NOMKDIR */ |
| } |
| |
| /* Z F S E E K -- Position input file pointer */ |
| /* |
| Call with: |
| Long int, 0-based, indicating desired position. |
| Returns: |
| 0 on success. |
| -1 on failure. |
| */ |
| #ifndef NORESEND |
| int |
| #ifdef CK_ANSIC |
| zfseek(long pos) |
| #else |
| zfseek(pos) long pos; |
| #endif /* CK_ANSIC */ |
| /* zfseek */ { |
| zincnt = -1; /* Must empty the input buffer */ |
| debug(F101,"zfseek","",pos); |
| return(fseek(fp[ZIFILE], pos, 0)?-1:0); |
| } |
| #endif /* NORESEND */ |
| |
| /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */ |
| |
| static struct zfnfp fnfp = { 0, NULL, NULL }; |
| |
| struct zfnfp * |
| zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; { |
| char * s; |
| int len; |
| #ifdef MAXPATHLEN |
| char zfntmp[MAXPATHLEN+4]; |
| #else |
| char zfntmp[CKMAXPATH+4]; |
| #endif /* MAXPATHLEN */ |
| |
| char sb[32], * tmp; |
| int i = 0, j = 0, k = 0, x = 0, y = 0; |
| int itsadir = 0; |
| |
| s = fname; |
| if (!s) |
| return(NULL); |
| if (!*s) |
| return(NULL); |
| if (!buf) |
| return(NULL); |
| |
| /* Initialize the data structure */ |
| |
| fnfp.len = ckstrncpy(buf,fname,buflen); |
| fnfp.fpath = buf; |
| fnfp.fname = NULL; |
| len = buflen; |
| debug(F111,"zfnqfp fname",fname,len); |
| |
| #ifdef DTILDE |
| if (*s == '~') { /* Starts with tilde? */ |
| char * xp; |
| xp = tilde_expand(s); /* Attempt to expand tilde */ |
| debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */ |
| if (!xp) xp = ""; |
| if (*xp) |
| s = xp; |
| } |
| #endif /* DTILDE */ |
| |
| #ifdef CKREALPATH |
| |
| /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */ |
| /* otherwise we write over memory. */ |
| |
| if (!realpath(s,zfntmp)) { |
| debug(F111,"zfnqfp realpath fails",s,errno); |
| #ifdef COMMENT |
| if (errno != ENOENT) |
| return(NULL); |
| #else |
| /* If realpath() fails use the do-it-yourself method */ |
| /* 16 Jan 2002 */ |
| goto norealpath; |
| #endif /* COMMENT */ |
| } |
| len = strlen(zfntmp); |
| if (len > buflen) { |
| debug(F111,"zfnqfp result too long",ckitoa(buflen),len); |
| return(NULL); |
| } else { |
| ckstrncpy(buf,zfntmp,buflen); |
| } |
| if (buf[len-1] != '/') { |
| if ((itsadir = isdir(buf)) && len < (buflen - 1)) { |
| buf[len++] = '/'; |
| buf[len] = NUL; |
| } |
| } |
| fnfp.len = len; |
| fnfp.fpath = buf; |
| debug(F110,"zfnqfp realpath path",fnfp.fpath,0); |
| tmp = buf + fnfp.len - 1; |
| if (!itsadir) { |
| while (tmp >= buf) { |
| if (*tmp == '/') { |
| fnfp.fname = tmp + 1; |
| debug(F110,"zfnqfp realpath name",fnfp.fname,0); |
| break; |
| } |
| tmp--; |
| } |
| } |
| return(&fnfp); |
| |
| #endif /* CKREALPATH */ |
| |
| norealpath: |
| |
| tmp = zfntmp; |
| while (*s) { /* Remove leading "./" (0 or more) */ |
| debug(F110,"zfnqfp while *s",s,0); |
| if (*s == '.' && *(s+1) == '/') { |
| s += 2; |
| while (*s == '/') s++; |
| } else |
| break; |
| } |
| if (!*s) return(NULL); |
| if (*s == '/') { /* Pathname is absolute */ |
| ckstrncpy(buf,s,len); |
| x = strlen(buf); |
| y = 0; |
| } else { /* Pathname is relative */ |
| char * p; |
| if (p = zgtdir()) { /* So get current directory */ |
| debug(F110,"zfnqfp zgtdir",p,0); |
| x = ckstrncpy(buf,p,len); |
| buf[x++] = '/'; |
| debug(F110,"zfnqfp buf 1",buf,0); |
| len -= x; /* How much room left in buffer */ |
| if ((y = (int)strlen(s)) > len) /* If enough room... */ |
| return(NULL); |
| ckstrncpy(buf+x,s,len); /* ... append the filename */ |
| debug(F110,"zfnqfp buf 2",buf,0); |
| } else { |
| return(NULL); |
| } |
| } |
| |
| /* Buf now holds full path but maybe containing some . or .. tricks */ |
| |
| j = x + y; /* Length of what's in buf */ |
| len = j; |
| debug(F101,"zfnqfp len","",len); |
| |
| /* Catch dangling "/." or "/.." */ |
| if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') || |
| (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) { |
| if (j < buflen - 2) { |
| buf[j] = '/'; |
| buf[j+1] = NUL; |
| } |
| } |
| j = -1; /* j = position of rightmost "/" */ |
| i = 0; /* i = destination index */ |
| tmp[i] = NUL; /* destination is temporary buffer */ |
| |
| for (x = 0; x < len; x++) { /* x = source index */ |
| if (buf[x] == '/') { |
| for (k = 0; k < 4; k++) { |
| sb[k] = buf[x+k]; |
| sb[k+1] = '\0'; |
| if (!sb[k]) break; |
| } |
| if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */ |
| x += 1; |
| continue; |
| } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */ |
| continue; |
| } else if (!strncmp(sb,"/../",4)) { /* ".." in path */ |
| for (k = i - 1; k >= 0; k--) { /* Back up one level */ |
| if (tmp[k] == '/') { |
| i = k; |
| tmp[i] = NUL; |
| break; |
| } |
| } |
| x += 2; |
| continue; |
| } |
| } |
| if (i >= (buflen - 1)) { |
| debug(F111,"zfnqfp overflow",tmp,i); |
| return(NULL); |
| } |
| tmp[i++] = buf[x]; /* Regular character, copy */ |
| tmp[i] = NUL; |
| if (buf[x] == '/') /* Remember rightmost "/" */ |
| j = i; |
| } |
| ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */ |
| |
| buf[buflen-1] = NUL; |
| if (!buf[0]) { /* If empty, say root */ |
| buf[0] = '/'; |
| buf[2] = NUL; |
| j = 0; |
| i = 1; |
| } |
| if ((itsadir = isdir(buf))) { |
| if (buf[i-1] != '/' && i < (buflen - 1)) { |
| buf[i++] = '/'; |
| buf[i] = NUL; |
| } |
| } |
| if (!itsadir && (j > -1)) { /* Set pointer to basename */ |
| fnfp.fname = (char *)(buf + j); |
| fnfp.fpath = (char *)buf; |
| fnfp.len = i; |
| debug(F111,"zfnqfp path",fnfp.fpath,i); |
| debug(F110,"zfnqfp name",fnfp.fname,0); |
| return(&fnfp); |
| } |
| return(NULL); |
| } |
| |
| /* Z C M P F N -- Compare two filenames */ |
| |
| /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */ |
| |
| int |
| zcmpfn(s1,s2) char * s1, * s2; { |
| char buf1[CKMAXPATH+1]; |
| char buf2[CKMAXPATH+1]; |
| |
| #ifdef USE_LSTAT |
| char linkname[CKMAXPATH+1]; |
| struct stat buf; |
| #endif /* USE_LSTAT */ |
| int x, rc = 0; |
| |
| if (!s1) s1 = ""; |
| if (!s2) s2 = ""; |
| if (!*s1 || !*s2) return(0); |
| |
| #ifdef CKSYMLINK /* We're doing symlinks? */ |
| #ifdef USE_LSTAT /* OK to use lstat()? */ |
| x = lstat(s1,&buf); |
| if (x > -1 && /* Now see if it's a symlink */ |
| #ifdef S_ISLNK |
| S_ISLNK(buf.st_mode) |
| #else |
| #ifdef _IFLNK |
| ((_IFMT & buf.st_mode) == _IFLNK) |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| ) { |
| linkname[0] = '\0'; /* Get the name */ |
| x = readlink(s1,linkname,CKMAXPATH); |
| if (x > -1 && x < CKMAXPATH) { /* It's a link */ |
| linkname[x] = '\0'; |
| s1 = linkname; |
| } |
| } |
| #endif /* USE_LSTAT */ |
| #endif /* CKSYMLINK */ |
| |
| if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */ |
| |
| #ifdef CKSYMLINK /* Same deal for second name... */ |
| #ifdef USE_LSTAT |
| x = lstat(s2,&buf); |
| if (x > -1 && |
| #ifdef S_ISLNK |
| S_ISLNK(buf.st_mode) |
| #else |
| #ifdef _IFLNK |
| ((_IFMT & buf.st_mode) == _IFLNK) |
| #endif /* _IFLNK */ |
| #endif /* S_ISLNK */ |
| ) { |
| linkname[0] = '\0'; |
| x = readlink(s2,linkname,CKMAXPATH); |
| if (x > -1 && x < CKMAXPATH) { |
| linkname[x] = '\0'; |
| s2 = linkname; |
| } |
| } |
| #endif /* USE_LSTAT */ |
| #endif /* CKSYMLINK */ |
| if (zfnqfp(s2,CKMAXPATH,buf2)) { |
| debug(F110,"zcmpfn s1",buf1,0); |
| debug(F110,"zcmpfn s2",buf2,0); |
| if (!strncmp(buf1,buf2,CKMAXPATH)) |
| rc = 1; |
| } |
| } |
| debug(F101,"zcmpfn result","",rc); |
| return(rc); |
| } |
| |
| #ifdef CKROOT |
| |
| /* User-mode chroot() implementation */ |
| |
| int |
| zsetroot(s) char * s; { /* Sets the root */ |
| char buf[CKMAXPATH+1]; |
| if (!s) return(-1); |
| if (!*s) return(-1); |
| debug(F110,"zsetroot",s,0); |
| if (!isdir(s)) return(-2); |
| if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */ |
| return(-3); |
| if (access(buf,R_OK) < 0) { /* Check access */ |
| debug(F110,"zsetroot access denied",buf,0); |
| return(-4); |
| } |
| s = buf; |
| if (ckrootset) { /* If root already set */ |
| if (!zinroot(s)) { /* make sure new root is in it */ |
| debug(F110,"zsetroot new root not in root",ckroot,0); |
| return(-5); |
| } |
| } |
| if (zchdir(buf) < 1) return(-4); /* Change directory to new root */ |
| ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */ |
| if (ckroot[ckrootset-1] != '/') { |
| ckroot[ckrootset++] = '/'; |
| ckroot[ckrootset] = '\0'; |
| } |
| debug(F111,"zsetroot rootset",ckroot,ckrootset); |
| ckrooterr = 0; /* Reset error flag */ |
| return(1); |
| } |
| |
| char * |
| zgetroot() { /* Returns the root */ |
| if (!ckrootset) |
| return(NULL); |
| return((char *)ckroot); |
| } |
| |
| int |
| zinroot(s) char * s; { /* Checks if file s is in the root */ |
| int x, n; |
| struct zfnfp * f = NULL; |
| char buf[CKMAXPATH+2]; |
| |
| debug(F111,"zinroot setroot",ckroot,ckrootset); |
| ckrooterr = 0; /* Reset global error flag */ |
| if (!ckrootset) /* Root not set */ |
| return(1); /* so it's ok - no need to check */ |
| if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */ |
| return(0); /* Fail if we can't */ |
| n = f->len; /* Length of full pathname */ |
| debug(F111,"zinroot n",buf,n); |
| if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */ |
| ckrooterr = 1; /* Fail */ |
| return(0); |
| } |
| if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */ |
| buf[n++] = '/'; /* make sure it ends with '/' */ |
| buf[n] = '\0'; |
| } |
| x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */ |
| debug(F111,"zinroot checked",buf,x); |
| if (x == 0) /* OK */ |
| return(1); |
| ckrooterr = 1; /* Not OK */ |
| return(0); |
| } |
| #endif /* CKROOT */ |
| |
| #ifdef CK_LOGIN |
| /* |
| The following code provides support for user login and logout |
| including anonymous accounts. If this feature is to be supported |
| outside of UNIX, it should be spread out among the ck?fio.c modules... |
| */ |
| #ifndef _PATH_BSHELL |
| #define _PATH_BSHELL "/usr/bin/bash" |
| #endif /* _PATH_BSHELL */ |
| #ifndef _PATH_FTPUSERS |
| #define _PATH_FTPUSERS "/etc/ftpusers" |
| #endif /* _PATH_FTPUSERS */ |
| |
| /* |
| * Helper function for sgetpwnam(). |
| */ |
| char * |
| sgetsave(s) char *s; { |
| char *new = malloc((unsigned) strlen(s) + 1); |
| if (new == NULL) { |
| printf("?Local resource failure: malloc\n"); |
| exit(1); |
| /* NOTREACHED */ |
| } |
| (void) strcpy(new, s); /* safe */ |
| return (new); |
| } |
| |
| /* |
| * Save the result of getpwnam(). Used for USER command, since |
| * the data returned must not be clobbered by any other command |
| * (e.g., globbing). |
| */ |
| struct passwd * |
| sgetpwnam(name) char *name; { |
| static struct passwd save; |
| register struct passwd *p; |
| #ifdef CK_SHADOW |
| register struct spwd *sp; |
| #endif /* CK_SHADOW */ |
| char *sgetsave(); |
| |
| #ifdef HPUX10_TRUSTED |
| struct pr_passwd *pr; |
| #endif /* HPUX10_TRUSTED */ |
| |
| #ifdef CK_SHADOW |
| sp = getspnam(name); |
| debug(F111,"sgetpwnam","getspnam()",sp); |
| if (sp == NULL) |
| return (NULL); |
| #endif /* CK_SHADOW */ |
| |
| #ifdef HPUX10_TRUSTED |
| if ((pr = getprpwnam(name)) == NULL) |
| return(NULL); |
| #endif /* HPUX10_TRUSTED */ |
| |
| p = getpwnam(name); |
| debug(F111,"sgetpwnam","getpwnam()",p); |
| if (p == NULL) |
| return(NULL); |
| if (save.pw_name) { |
| free(save.pw_name); |
| free(save.pw_passwd); |
| free(save.pw_gecos); |
| free(save.pw_dir); |
| free(save.pw_shell); |
| } |
| save = *p; |
| save.pw_name = sgetsave(p->pw_name); |
| #ifdef CK_SHADOW |
| save.pw_passwd = sgetsave(sp->sp_pwdp); |
| #else /* CK_SHADOW */ |
| #ifdef HPUX10_TRUSTED |
| if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt) |
| save.pw_passwd = sgetsave(pr->ufld.fd_encrypt); |
| else |
| save.pw_passwd = sgetsave(""); |
| #else /* HPUX10_TRUSTED */ |
| save.pw_passwd = sgetsave(p->pw_passwd); |
| #endif /* HPUX10_TRUSTED */ |
| #endif /* CK_SHADOW */ |
| save.pw_gecos = sgetsave(p->pw_gecos); |
| save.pw_dir = sgetsave(p->pw_dir); |
| save.pw_shell = sgetsave(p->pw_shell); |
| return(&save); |
| } |
| |
| #define CKXLOGBSIZ 256 |
| |
| struct passwd * pw = NULL; |
| char * home = NULL; /* Home directory pointer for glob */ |
| #ifdef CMASK |
| #undef CMASK |
| #endif /* CMASK */ |
| |
| #define CMASK 027 |
| |
| int defumask = CMASK; /* Default umask value */ |
| |
| /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */ |
| |
| /* Sets global passwd pointer pw if named account exists and is acceptable; |
| * sets askpasswd if a PASS command is expected. If logged in previously, |
| * need to reset state. If name is "ftp" or "anonymous", the name is not in |
| * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return. |
| * If account doesn't exist, ask for passwd anyway. Otherwise, check user |
| * requesting login privileges. Disallow anyone who does not have a standard |
| * shell as returned by getusershell(). Disallow anyone mentioned in the file |
| * _PATH_FTPUSERS to allow people such as root and uucp to be avoided. |
| */ |
| _PROTOTYP(static int checkuser, (char *) ); |
| |
| char zvuname[64] = { NUL, NUL }; |
| char zvhome[CKMAXPATH+1] = { NUL, NUL }; |
| #define ZENVUSER 70 |
| #define ZENVHOME CKMAXPATH+12 |
| #define ZENVLOGNAME 74 |
| static char zenvuser[ZENVUSER]; |
| static char zenvhome[ZENVHOME]; |
| static char zenvlogname[ZENVLOGNAME]; |
| |
| #ifdef CK_PAM |
| static char pam_data[500]; |
| struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */ |
| struct pam_handle * pamh = NULL; /* PAM reference handle */ |
| #endif /* CK_PAM */ |
| |
| int |
| zvuser(name) char *name; { |
| register char *cp = NULL; |
| int x; |
| char *shell; |
| #ifdef GETUSERSHELL |
| char *getusershell(); |
| #endif /* GETUSERSHELL */ |
| |
| #ifdef CK_PAM |
| int pam_status; |
| const char * reply = NULL; |
| #endif /* CK_PAM */ |
| |
| debug(F111,"user",name,logged_in); |
| |
| if (!name) name = ""; |
| zvuname[0] = NUL; |
| |
| debug(F101,"zvuser ckxsyslog","",ckxsyslog); |
| |
| #ifdef CKSYSLOG |
| debug(F100,"zvuser CKSYSLOG defined","",0); |
| #endif /* CKSYSLOG */ |
| |
| if (logged_in) /* Should not be called if logged in */ |
| return(0); |
| |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: user %s",name |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| |
| guest = 0; /* Assume not guest */ |
| askpasswd = 0; |
| |
| if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) { |
| debug(F101,"zvuser anonymous ckxanon","",ckxanon); |
| if (!ckxanon) { /* Anonymous login not allowed */ |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: anonymous login not allowed: %s", |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } |
| if (checkuser("ftp") || checkuser("anonymous")) { |
| debug(F100,"zvuser anon forbidden by ftpusers file","",0); |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: anonymous login forbidden by ftpusers file: %s", |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } else if ((pw = sgetpwnam("ftp")) != NULL) { |
| debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0); |
| guest = 1; |
| askpasswd = 1; |
| ckstrncpy(zvuname,"anonymous",64); |
| return(1); |
| } else { |
| debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0); |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: anonymous getpwnam(ftp) failed: %s", |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } |
| } |
| pw = sgetpwnam(name); |
| if (pw) { |
| /* |
| Of course some UNIX platforms (like AIX) don't have getusershell(). |
| In that case we can't check if the user's account has been "turned off" |
| or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch, |
| which runs (usually just printing a message and exiting), but which is |
| not listed in /etc/shells. For that matter, if getusershell() is not |
| available, then probably neither is /etc/shells. |
| */ |
| debug(F100,"zvuser sgetpwnam ok","",0); |
| shell = pw->pw_shell; |
| if (!shell) shell = ""; |
| if (!*shell) |
| shell = _PATH_BSHELL; |
| debug(F110,"zvuser shell",shell,0); |
| #ifdef GETUSERSHELL |
| while ((cp = getusershell()) != NULL) { |
| debug(F110,"zvuser getusershell",cp,0); |
| if (strcmp(cp, shell) == 0) |
| break; |
| } |
| debug(F100,"zvuser endusershell 1","",0); |
| endusershell(); |
| debug(F100,"zvuser endusershell 2","",0); |
| #else /* GETUSERSHELL */ |
| cp = ""; /* Do not refuse if we cannot check */ |
| #endif /* GETUSERSHELL */ |
| x = checkuser(name); |
| debug(F101,"zvuser checkuser","",x); |
| if (cp == NULL) { |
| debug(F100,"zvuser refused 1","",0); |
| pw = (struct passwd *) NULL; |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: invalid shell %s for %s %s",shell, name, |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } else if (x) { |
| debug(F100,"zvuser refused 2","",0); |
| pw = (struct passwd *) NULL; |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: %s login forbidden by ftpusers file: %s", |
| name, clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } else { |
| x = 0; |
| #ifdef CK_PAM |
| /* Get PAM authentication details */ |
| debug(F110,"zvuser","calling pam_start",0); |
| if ((pam_status = |
| pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh)) |
| != PAM_SUCCESS) { |
| reply = pam_strerror(NULL, pam_status); |
| debug(F110,"zvuser PAM failure",reply,0); |
| printf("%s\n",reply); |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: %s refused by PAM \"%s\": %s", |
| name,reply, |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } |
| #endif /* CK_PAM */ |
| askpasswd = 1; |
| ckstrncpy(zvuname,name,64); |
| return(1); |
| } |
| } else { |
| x = 0; |
| debug(F100,"zvuser sgetpwnam NULL","",0); |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: getpwnam(%s) failed: %s",name, |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } |
| |
| #ifdef FTP_KERBEROS |
| if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) { |
| #ifdef COMMENT |
| /* Why sprintf and then printf? */ |
| /* Also, what is kerb_ok? And is the test on it right? */ |
| char buf[CKXLOGBSIZ]; |
| sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s", |
| kdata.pname, *kdata.pinst ? "." : "", |
| kdata.pinst, kdata.prealm, |
| (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", |
| name, kerb_ok ? "" : "; Password required."); |
| printf("%s", buf); |
| #else |
| printf("Kerberos user %s%s%s@%s is%s authorized as %s%s", |
| kdata.pname, *kdata.pinst ? "." : "", |
| kdata.pinst, kdata.prealm, |
| (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not", |
| name, kerb_ok ? "" : "; Password required."); |
| #endif /* COMMENT */ |
| if (kerb_ok) return(1); |
| } else |
| return(0); |
| #endif /* FTP_KERBEROS */ |
| } |
| |
| /* Check if the given user is in the forbidden-user file */ |
| |
| static int |
| checkuser(name) char *name; { |
| extern char * userfile; |
| FILE *fd; |
| int i; |
| char line[CKXLOGBSIZ+1]; |
| |
| if (!name) |
| name = ""; |
| i = strlen(name); |
| debug(F111,"checkuser name",name,i); |
| if (!*name) |
| return(1); |
| |
| fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r"); |
| debug(F111,"checkuser userfile",userfile,fd); |
| if (fd) { |
| line[0] = '\0'; |
| while (fgets(line, sizeof(line), fd)) { |
| debug(F110,"checkuser line",line,0); |
| if (line[0] <= '#') |
| continue; |
| if (strncmp(line, name, i) == 0) { |
| debug(F110,"checkuser REFUSED",name,0); |
| return(1); |
| } |
| line[0] = '\0'; |
| } |
| (VOID) fclose(fd); |
| } |
| debug(F110,"checkuser OK",name,0); |
| return(0); |
| } |
| |
| /* Z V L O G O U T -- Log out from Internet Kermit Service */ |
| |
| VOID |
| zvlogout() { |
| #ifdef COMMENT |
| /* This could be dangerous */ |
| if (setuid((UID_T)0) < 0) { |
| debug(F100,"zvlogout setuid FAILED","",0); |
| goto bad; |
| } |
| debug(F100,"zvlogout setuid OK","",0); |
| #endif /* COMMENT */ |
| #ifdef CKSYSLOG |
| if (ckxsyslog >= SYSLG_LI && ckxlogging) { |
| cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost); |
| } |
| #endif /* CKSYSLOG */ |
| #ifdef CKWTMP |
| debug(F110,"WTMP logout",cksysline,logged_in); |
| if (logged_in) |
| logwtmp(cksysline, "", ""); |
| #endif /* CKWTMP */ |
| pw = NULL; |
| logged_in = 0; |
| guest = 0; |
| isguest = 0; |
| } |
| |
| #ifdef FTP_KERBEROS |
| kpass(name, p) char *name, *p; { |
| char instance[INST_SZ]; |
| char realm[REALM_SZ]; |
| char tkt_file[20]; |
| KTEXT_ST ticket; |
| AUTH_DAT authdata; |
| unsigned long faddr; |
| struct hostent *hp; |
| |
| if (krb_get_lrealm(realm, 1) != KSUCCESS) |
| return(0); |
| |
| ckstrncpy(tkt_file, TKT_ROOT, 20); |
| ckstrncat(tkt_file, "_ftpdXXXXXX", 20); |
| krb_set_tkt_string(mktemp(tkt_file)); |
| |
| (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance)); |
| |
| if ((hp = gethostbyname(instance)) == NULL) |
| return(0); |
| |
| #ifdef HADDRLIST |
| hp = ck_copyhostent(hp); /* safe copy that won't change */ |
| #endif /* HADDRLIST */ |
| bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr)); |
| |
| if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) || |
| krb_mk_req(&ticket, "rcmd", instance, realm, 33) || |
| krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") || |
| kuserok(&authdata, name)) { |
| dest_tkt(); |
| return(0); |
| } |
| dest_tkt(); |
| return(1); |
| } |
| #endif /* FTP_KERBEROS */ |
| |
| VOID |
| zsyslog() { |
| #ifdef CKSYSLOG |
| if (ckxsyslog && !ckxlogging) { |
| #ifdef LOG_DAEMON |
| openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON); |
| #else |
| openlog(inserver ? "iksd" : "ckermit", LOG_PID); |
| #endif /* LOG_DAEMON */ |
| ckxlogging = 1; |
| debug(F100,"zsyslog syslog opened","",0); |
| } |
| #endif /* CKSYSLOG */ |
| } |
| |
| /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */ |
| |
| #ifndef AUTH_USER |
| #define AUTH_USER 3 |
| #endif /* AUTH_USER */ |
| #ifndef AUTH_VALID |
| #define AUTH_VALID 4 |
| #endif /* AUTH_VALID */ |
| |
| int |
| zvpass(p) char *p; { |
| char *xpasswd, *salt; |
| char * dir = NULL; |
| #ifdef CK_PAM |
| int pam_status; |
| const char * reply = NULL; |
| #endif /* CK_PAM */ |
| |
| if (logged_in || askpasswd == 0) { |
| return(0); |
| } |
| debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest); |
| if (!p) p = ""; |
| askpasswd = 0; |
| if (guest && !*p) { /* Guests must specify a password */ |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: anonymous guests must specify a password" |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| return(0); |
| } |
| if (!guest |
| #ifdef CK_AUTHENTICATION |
| && ck_tn_auth_valid() != AUTH_VALID |
| #endif /* CK_AUTHENTICATION */ |
| ) { /* "ftp" is only account allowed no password */ |
| #ifdef CK_PAM |
| debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0); |
| if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) { |
| reply = pam_strerror(pamh, pam_status); |
| debug(F110,"zvpass PAM failure",reply,0); |
| /* if no password given treat as non-fatal error */ |
| /* pam will prompt for password in pam_authenticate() */ |
| if (!p) { |
| printf("%s\n",reply); |
| pam_end(pamh, 0); |
| debug(F100,"zvpass denied","",0); |
| pw = NULL; |
| zvuname[0] = NUL; |
| return(0); |
| } |
| } |
| debug(F110,"zvpass","calling pam_authenticate",0); |
| if (*p) |
| pam_pw = p; |
| if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { |
| reply = pam_strerror(pamh, pam_status); |
| debug(F110,"zvpass PAM failure",reply,0); |
| printf("%s\n",reply); |
| pam_end(pamh, 0); |
| debug(F100,"zvpass denied","",0); |
| pam_pw = NULL; |
| pw = NULL; |
| zvuname[0] = NUL; |
| return(0); |
| } |
| pam_pw = NULL; |
| debug(F110,"zvpass","calling pam_acct_mgmt",0); |
| if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) { |
| reply = pam_strerror(pamh, pam_status); |
| debug(F110,"zvpass PAM failure",reply,0); |
| printf("%s\n",reply); |
| pam_end(pamh, 0); |
| debug(F100,"zvpass denied","",0); |
| pw = NULL; |
| zvuname[0] = NUL; |
| return(0); |
| } |
| debug(F110,"zvpass","PAM validates OK",0); |
| pam_end(pamh,0); |
| #else /* CK_PAM */ |
| if (pw == NULL) |
| salt = "xx"; |
| else |
| salt = pw->pw_passwd; |
| |
| #ifdef HPUX10_TRUSTED |
| xpasswd = bigcrypt(p, salt); |
| #else |
| /* |
| On 64-bit platforms this can give "cast to pointer from integer of |
| different size" warning, but I'm not sure what the effect is at runtime, |
| or what to do about it. |
| */ |
| xpasswd = (char *)crypt(p, salt); |
| #endif /* HPUX10_TRUSTED */ |
| |
| if ( |
| #ifdef FTP_KERBEROS |
| /* null pw_passwd ok if Kerberos password ok */ |
| pw == NULL || |
| ((*pw->pw_passwd != '\0' || |
| strcmp(xpasswd, pw->pw_passwd)) |
| && !kpass(pw->pw_name, p)) |
| #else |
| #ifdef CK_SRP |
| /* check with tpasswd first if there */ |
| pw == NULL || *pw->pw_passwd == '\0' || |
| t_verifypw (pw->pw_name, p) == 0 || |
| (t_verifypw (pw->pw_name, p) < 0 && |
| strcmp (xpasswd, pw->pw_passwd)) |
| #else /* CK_SRP */ |
| /* The strcmp does not catch null passwords! */ |
| (pw == NULL) || (*pw->pw_passwd == '\0') || |
| strcmp(xpasswd, pw->pw_passwd) |
| #endif /* CK_SRP */ |
| #endif /* FTP_KERBEROS */ |
| ) { |
| debug(F100,"zvpass denied","",0); |
| pw = NULL; |
| zvuname[0] = NUL; |
| return(0); |
| } |
| #endif /* CK_PAM */ |
| } |
| |
| (VOID) setgid((GID_T)pw->pw_gid); /* Set group ID */ |
| |
| #ifndef NOINITGROUPS |
| (VOID) initgroups(pw->pw_name, pw->pw_gid); |
| #endif /* NOINITGROUPS */ |
| |
| logged_in = 1; |
| dir = pw->pw_dir; |
| |
| #ifdef CKWTMP |
| /* Open wtmp before chroot */ |
| if (ckxwtmp) { |
| sprintf(cksysline,"iks_%04x", getpid()); /* safe */ |
| logwtmp(cksysline, pw->pw_name, |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| debug(F110,"WTMP login",cksysline,logged_in); |
| } |
| #endif /* CKWTMP */ |
| /* |
| For anonymous users, we chroot to user ftp's home directory unless |
| started with --anonroot:xxx, in which case we chroot to xxx. We must |
| immediately chdir() to the same directory we chroot() to or else the |
| old current directory remains accessible as "." outside the new root. |
| */ |
| if (guest) { |
| if (anonroot) /* Non-default anonymous root */ |
| dir = anonroot; |
| else |
| makestr(&anonroot,dir); |
| errno = 0; |
| debug(F110,"zvpass anon chroot",dir,0); |
| if (chroot(dir) < 0) { |
| debug(F111,"zvpass anon chroot FAILED",dir,errno); |
| goto bad; |
| } |
| errno = 0; |
| if (chdir("/") < 0) { |
| debug(F111,"zvpass anon chdir FAILED",dir,errno); |
| goto bad; |
| } |
| debug(F110,"zvpass anon chroot/chdir OK",dir,0); |
| } else if (chdir(dir) < 0) { /* Not guest */ |
| #ifdef COMMENT |
| if (chdir("/") < 0) { |
| debug(F110,"Non-guest chdir FAILED",dir,0); |
| goto bad; |
| } else |
| printf("?No directory! Logging in with home=/\n"); |
| #else |
| debug(F110,"zvpass non-guest chdir FAILED",dir,0); |
| goto bad; /* Be conservative at first */ |
| #endif /* COMMENT */ |
| } |
| debug(F110,"zvpass non-guest chdir OK",dir,0); |
| if (setuid((UID_T)pw->pw_uid) < 0) { |
| debug(F101,"zvpass setuid FAILED","",pw->pw_uid); |
| goto bad; |
| } |
| debug(F101,"zvpass setuid OK","",pw->pw_uid); |
| |
| guestpass[0] = '\0'; |
| if (guest) { |
| extern int fncact; |
| isguest = 1; |
| fncact = XYFX_R; /* FILE COLLISION = RENAME */ |
| debug(F110,"GUEST fncact=R",p,0); |
| lset(guestpass,"anonymous:",10,32); |
| ckstrncpy(&guestpass[10],p,GUESTPASS-10); |
| home = "/"; |
| printf("Anonymous login.\r\n"); |
| |
| #ifdef SETPROCTITLE |
| /* proctitle declared where? Obviously this code is never compiled. */ |
| sprintf(proctitle, "%s: anonymous/%.*s", |
| clienthost ? clienthost : "(unk)", |
| sizeof(proctitle) - sizeof(clienthost) - |
| sizeof(": anonymous/"), p); |
| setproctitle(proctitle); |
| #endif /* SETPROCTITLE */ |
| |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) { |
| syslog(LOG_INFO, |
| "login: anonymous %s %s", |
| clienthost ? clienthost : "(unknown host)", |
| p |
| ); |
| } |
| #endif /* CKSYSLOG */ |
| |
| } else { /* Real user */ |
| isguest = 0; |
| home = dir; |
| ckstrncpy(guestpass,zvuname,GUESTPASS); |
| |
| printf("User %s logged in.\r\n", pw->pw_name); |
| #ifdef SETPROCTITLE |
| /* not used */ |
| sprintf(proctitle, "%s: %s", |
| clienthost ? clienthost : "(unk)", |
| pw->pw_name |
| ); |
| setproctitle(proctitle); |
| #endif /* SETPROCTITLE */ |
| |
| #ifdef CKSYSLOG |
| if (ckxsyslog && ckxlogging) |
| syslog(LOG_INFO, "login: %s %s", |
| pw->pw_name, |
| clienthost ? clienthost : "(unknown host)" |
| ); |
| #endif /* CKSYSLOG */ |
| } |
| ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */ |
| #ifndef NOPUTENV |
| |
| ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL); |
| putenv((char *)zenvuser); |
| ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL); |
| putenv((char *)zenvlogname); |
| ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL); |
| putenv((char *)zenvhome); |
| #endif /* NOPUTENV */ |
| /* homdir = (char *)zvhome; */ |
| ckstrncpy((char *)uidbuf,(char *)zvuname,64); |
| (VOID) umask(defumask); |
| #ifdef IKSDB |
| if (ikdbopen) { |
| char * p2; |
| int k; |
| extern char dbrec[]; |
| extern unsigned long myflags; |
| extern unsigned int mydbslot; |
| extern struct iksdbfld dbfld[]; |
| #ifdef CK_AUTHENTICATION |
| extern unsigned long myamode, myatype; |
| #endif /* CK_AUTHENTICATION */ |
| myflags |= DBF_LOGGED; |
| #ifdef DEBUG |
| if (deblog) { |
| debug(F101,"zvpass guest","",guest); |
| debug(F111,"zvpass zvuname",zvuname,0); |
| debug(F110,"zvpass guestpass",guestpass,0); |
| debug(F110,"zvpass dir",dir,0); |
| debug(F110,"zvpass home",home,0); |
| debug(F110,"zvpass anonroot",anonroot,0); |
| } |
| #endif /* DEBUG */ |
| p2 = guest ? guestpass : zvuname; |
| if (guest) { |
| p2 = (char *)guestpass; |
| myflags &= ~DBF_USER; |
| } else { |
| p2 = (char *)zvuname; |
| myflags |= DBF_USER; |
| } |
| k = strlen(p2); |
| strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4); |
| lset(&dbrec[dbfld[db_USER].off],p2,1024,32); |
| strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4); |
| #ifdef CK_AUTHENTICATION |
| myamode = ck_tn_auth_valid(); |
| strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4); |
| myatype = ck_tn_authenticated(); |
| strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4); |
| #endif /* CK_AUTHENTICATION */ |
| if (guest) { |
| p2 = dir; |
| } else { |
| p2 = zgtdir(); |
| if (!p2) p2 = ""; |
| if (!*p2) p2 = home; |
| } |
| strncpy(&dbrec[DB_DLEN], |
| ulongtohex((unsigned long)strlen(p2),4), |
| 4 |
| ); |
| lset(&dbrec[dbfld[db_DIR].off],p2,1024,32); |
| updslot(mydbslot); |
| } |
| #endif /* IKSDB */ |
| return(1); |
| |
| bad: /* Common failure exit */ |
| zvuname[0] = NUL; |
| zvlogout(); |
| return(0); |
| } |
| #endif /* CK_LOGIN */ |
| |
| /* Buggy Xenix 2.3.4 cc needs this line after the endif */ |