X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=.pc%2F040_pam-password-prompting.patch%2Fckufio.c;fp=.pc%2F040_pam-password-prompting.patch%2Fckufio.c;h=298c48e3f52087835f4de4fc4118f5e7a20c41a9;hb=5bbbe0ad1ea84675d10762abb5d6698cd830de18;hp=0000000000000000000000000000000000000000;hpb=2788625b3cd3bff4a649667f38a3bcc52b71af24;p=ckermit.git diff --git a/.pc/040_pam-password-prompting.patch/ckufio.c b/.pc/040_pam-password-prompting.patch/ckufio.c new file mode 100644 index 0000000..298c48e --- /dev/null +++ b/.pc/040_pam-password-prompting.patch/ckufio.c @@ -0,0 +1,8310 @@ +/* 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 , + 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 +#include + +#ifdef MINIX2 +#undef MINIX +#undef CKSYSLOG +#include +#include +#define NOFILEH +#endif /* MINIX2 */ + +#ifdef MINIX +#include +#include +#include +#else +#ifdef POSIX +#include +#else +#ifdef SVR3 +#include +#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 +#else /* !XNDIR */ +#ifdef NDIR +#include +#else /* !NDIR, !XNDIR */ +#ifdef RTU +#include "/usr/lib/ndir.h" +#else /* !RTU, !NDIR, !XNDIR */ +#ifdef DIRENT +#ifdef SDIRENT +#include +#else +#include +#endif /* SDIRENT */ +#else +#include +#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 /* Password file for shell name */ +#ifdef CK_SRP +#include /* SRP Password file */ +#endif /* CK_SRP */ + +#ifdef HPUX10_TRUSTED +#include +#include +#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 /* if requested, */ +#include /* for extra fields required by */ +#else /* 88Open spec. */ +#ifdef UTIMEH /* or if requested */ +#include /* (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, . 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 +#include + +#else /* Not BSD44 */ + +#ifdef BSD4 /* BSD 4.3 and below */ +#define TIMESTAMP /* Can do file dates */ +#include /* Need this */ +#include /* 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 + +/* 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 +#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 /* File status */ +#else +#include +#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 /* */ +#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 +#endif /* NOFILEH */ + +#ifndef is68k /* Whether to include */ +#ifndef BSD41 /* All but a couple UNIXes have it. */ +#ifndef FT18 +#ifndef COHERENT +#include +#endif /* COHERENT */ +#endif /* FT18 */ +#endif /* BSD41 */ +#endif /* is68k */ + +#ifdef COHERENT +#ifdef _I386 +#include +#else +#include +#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 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 +#endif /* CK_SHADOW */ +#ifdef CK_PAM /* PAM... */ +#include +#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... , , , , ... + 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 /* For MAXPATHLEN */ +#endif /* BSD44 */ +#ifdef COHERENT +#include /* 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 */ +_PROTOTYP( FILE * fdopen, (int, char *) ); +#endif /* DCLFDOPEN */ + +#ifdef DCLPOPEN +/* popen() needs declaring because it's not declared in */ +_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 +#else /* RTAIX */ +#include +#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 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 +#include +#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 */ +#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 +#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 +#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 + . See the section for including 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 ".~~", where is argument name + (fn), and 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 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.~~" */ + 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, */ +#define OPENDIR + DIR *fd, *opendir(); + struct dirent *dirbuf; + struct dirent *readdir(); +#else /* !DIRENT */ +#ifdef LONGFN /* Old way, with opendir() */ +#define OPENDIR + DIR *fd, *opendir(); + struct direct *dirbuf; +#else /* !LONGFN */ + int fd; /* Old way, 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" */ + 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 /.txt lists every file in * and does not even open up the + subdirectories. However, "dir /rec /.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 */