blob: 298c48e3f52087835f4de4fc4118f5e7a20c41a9 [file] [log] [blame]
/* C K U F I O -- Kermit file system support for UNIX, Aegis, and Plan 9 */
#define CK_NONBLOCK /* See zoutdump() */
#ifdef aegis
char *ckzv = "Aegis File support, 8.0.200, 4 Mar 2004";
#else
#ifdef Plan9
char *ckzv = "Plan 9 File support, 8.0.200, 4 Mar 2004";
#else
char *ckzv = "UNIX File support, 8.0.200, 4 Mar 2004";
#endif /* Plan9 */
#endif /* aegis */
/*
Author: Frank da Cruz <fdc@columbia.edu>,
Columbia University Academic Information Systems, New York City,
and others noted in the comments below. Note: CUCCA = Previous name of
Columbia University Academic Information Systems.
Copyright (C) 1985, 2004,
Trustees of Columbia University in the City of New York.
All rights reserved. See the C-Kermit COPYING.TXT file or the
copyright text in the ckcmai.c module for disclaimer and permissions.
*/
/*
NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
compatible with C preprocessors that support only #ifdef, #else, #endif,
#define, and #undef. Please do not use #if, logical operators, or other
preprocessor features in any of the portable C-Kermit modules. You can,
of course, use these constructions in platform-specific modules where you
know they are supported.
*/
/* Include Files */
#ifdef MINIX2
#define _MINIX
#endif /* MINIX2 */
#include "ckcsym.h"
#include "ckcdeb.h"
#include "ckcasc.h"
#ifndef NOCSETS
#include "ckcxla.h"
#endif /* NOCSETS */
#ifdef COMMENT
/* This causes trouble in C-Kermit 8.0. I don't remember the original */
/* reason for this being here but it must have been needed at the time... */
#ifdef OSF13
#ifdef CK_ANSIC
#ifdef _NO_PROTO
#undef _NO_PROTO
#endif /* _NO_PROTO */
#endif /* CK_ANSIC */
#endif /* OSF13 */
#endif /* COMMENT */
#include <errno.h>
#include <signal.h>
#ifdef MINIX2
#undef MINIX
#undef CKSYSLOG
#include <limits.h>
#include <time.h>
#define NOFILEH
#endif /* MINIX2 */
#ifdef MINIX
#include <limits.h>
#include <sys/types.h>
#include <time.h>
#else
#ifdef POSIX
#include <limits.h>
#else
#ifdef SVR3
#include <limits.h>
#endif /* SVR3 */
#endif /* POSIX */
#endif /* MINIX */
/*
Directory Separator macros, to allow this module to work with both UNIX and
OS/2: Because of ambiguity with the command line editor escape \ character,
the directory separator is currently left as / for OS/2 too, because the
OS/2 kernel also accepts / as directory separator. But this is subject to
change in future versions to conform to the normal OS/2 style.
*/
#ifndef DIRSEP
#define DIRSEP '/'
#endif /* DIRSEP */
#ifndef ISDIRSEP
#define ISDIRSEP(c) ((c)=='/')
#endif /* ISDIRSEP */
#ifdef SDIRENT
#define DIRENT
#endif /* SDIRENT */
#ifdef XNDIR
#include <sys/ndir.h>
#else /* !XNDIR */
#ifdef NDIR
#include <ndir.h>
#else /* !NDIR, !XNDIR */
#ifdef RTU
#include "/usr/lib/ndir.h"
#else /* !RTU, !NDIR, !XNDIR */
#ifdef DIRENT
#ifdef SDIRENT
#include <sys/dirent.h>
#else
#include <dirent.h>
#endif /* SDIRENT */
#else
#include <sys/dir.h>
#endif /* DIRENT */
#endif /* RTU */
#endif /* NDIR */
#endif /* XNDIR */
#ifdef UNIX /* Pointer arg to wait() allowed */
#define CK_CHILD /* Assume this is safe in all UNIX */
#endif /* UNIX */
extern int binary, recursive, stathack;
#ifdef CK_CTRLZ
extern int eofmethod;
#endif /* CK_CTRLZ */
#include <pwd.h> /* Password file for shell name */
#ifdef CK_SRP
#include <t_pwd.h> /* SRP Password file */
#endif /* CK_SRP */
#ifdef HPUX10_TRUSTED
#include <hpsecurity.h>
#include <prot.h>
#endif /* HPUX10_TRUSTED */
#ifdef COMMENT
/* Moved to ckcdeb.h */
#ifdef POSIX
#define UTIMEH
#else
#ifdef HPUX9
#define UTIMEH
#endif /* HPUX9 */
#endif /* POSIX */
#endif /* COMMENT */
#ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
#include <sys/utime.h> /* for extra fields required by */
#else /* 88Open spec. */
#ifdef UTIMEH /* or <utime.h> if requested */
#include <utime.h> /* (SVR4, POSIX) */
#ifndef BSD44
#ifndef V7
/* Not sure why this is here. What it implies is that the code bracketed
by SYSUTIMEH is valid on all platforms on which we support time
functionality. But we know that is not true because the BSD44 and V7
platforms do not support sys/utime.h and the data structures which
are defined in them. Now this worked before because prior to today's
changes the UTIMEH definition for BSD44 and V7 did not take place
until after SYSUTIMEH was defined. It also would not have been a
problem if the ordering of all the time blocks was consistent. All but
one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case
is where this problem was triggered.
*/
#define SYSUTIMEH /* Use this for both cases. */
#endif /* V7 */
#endif /* BSD44 */
#endif /* UTIMEH */
#endif /* SYSUTIMEH */
#ifndef NOTIMESTAMP
#ifdef POSIX
#ifndef AS400
#define TIMESTAMP
#endif /* AS400 */
#endif /* POSIX */
#ifdef BSD44 /* BSD 4.4 */
#ifndef TIMESTAMP
#define TIMESTAMP /* Can do file dates */
#endif /* TIMESTAMP */
#include <sys/time.h>
#include <sys/timeb.h>
#else /* Not BSD44 */
#ifdef BSD4 /* BSD 4.3 and below */
#define TIMESTAMP /* Can do file dates */
#include <time.h> /* Need this */
#include <sys/timeb.h> /* Need this if really BSD */
#else /* Not BSD 4.3 and below */
#ifdef SVORPOSIX /* System V or POSIX */
#ifndef TIMESTAMP
#define TIMESTAMP
#endif /* TIMESTAMP */
#include <time.h>
/* void tzset(); (the "void" type upsets some compilers) */
#ifndef IRIX60
#ifndef ultrix
#ifndef CONVEX9
/* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
#ifndef Plan9
extern long timezone;
#endif /* Plan9 */
#endif /* CONVEX9 */
#endif /* ultrix */
#endif /* IRIX60 */
#endif /* SVORPOSIX */
#endif /* BSD4 */
#endif /* BSD44 */
#ifdef COHERENT
#include <time.h>
#endif /* COHERENT */
/* Is `y' a leap year? */
#define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
/* Number of leap years from 1970 to `y' (not including `y' itself). */
#define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
#endif /* NOTIMESTAMP */
#ifdef CIE
#include <stat.h> /* File status */
#else
#include <sys/stat.h>
#endif /* CIE */
/* Macro to alleviate isdir() calls internal to this module */
static struct stat STATBUF;
#define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
extern char uidbuf[];
extern int xferlog;
extern char * xferfile;
int iklogopen = 0;
static time_t timenow;
#define IKSDMSGLEN CKMAXPATH+512
static char iksdmsg[IKSDMSGLEN];
extern int local;
extern int server, en_mkd, en_cwd, en_del;
/*
Functions (n is one of the predefined file numbers from ckcker.h):
zopeni(n,name) -- Opens an existing file for input.
zopeno(n,name,attr,fcb) -- Opens a new file for output.
zclose(n) -- Closes a file.
zchin(n,&c) -- Gets the next character from an input file.
zsinl(n,&s,x) -- Read a line from file n, max len x, into address s.
zsout(n,s) -- Write a null-terminated string to output file, buffered.
zsoutl(n,s) -- Like zsout, but appends a line terminator.
zsoutx(n,s,x) -- Write x characters to output file, unbuffered.
zchout(n,c) -- Add a character to an output file, unbuffered.
zchki(name) -- Check if named file exists and is readable, return size.
zchko(name) -- Check if named file can be created.
zchkspa(name,n) -- Check if n bytes available to create new file, name.
znewn(name,s) -- Make a new unique file name based on the given name.
zdelet(name) -- Delete the named file.
zxpand(string) -- Expands the given wildcard string into a list of files.
znext(string) -- Returns the next file from the list in "string".
zxrewind() -- Rewind zxpand list.
zxcmd(n,cmd) -- Execute the command in a lower fork on file number n.
zclosf() -- Close input file associated with zxcmd()'s lower fork.
zrtol(n1,n2) -- Convert remote filename into local form.
zltor(n1,n2) -- Convert local filename into remote form.
zchdir(dirnam) -- Change working directory.
zhome() -- Return pointer to home directory name string.
zkself() -- Kill self, log out own job.
zsattr(struct zattr *) -- Return attributes for file which is being sent.
zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
zrename(old, new) -- Rename a file.
zcopy(source,destination) -- Copy a file.
zmkdir(path) -- Create the directory path if possible
zfnqfp(fname,len,fullpath) - Determine full path for file name.
zgetfs(name) -- return file size regardless of accessibility
zchkpid(pid) -- tell if PID is valid and active
*/
/* Kermit-specific includes */
/*
Definitions here supersede those from system include files.
ckcdeb.h is included above.
*/
#include "ckcker.h" /* Kermit definitions */
#include "ckucmd.h" /* For keyword tables */
#include "ckuver.h" /* Version herald */
char *ckzsys = HERALD;
/*
File access checking ... There are two calls to access() in this module.
If this program is installed setuid or setgid on a Berkeley-based UNIX
system that does NOT incorporate the saved-original-effective-uid/gid
feature, then, when we have swapped the effective and original uid/gid,
access() fails because it uses what it thinks are the REAL ids, but we have
swapped them. This occurs on systems where ANYBSD is defined, NOSETREU
is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take
care of this situation like so:
ifdef ANYBSD
ifndef NOSETREU
ifndef SAVEDUID
define SW_ACC_ID
endif
endif
endif
But we can't test such a general scheme everywhere, so let's only do this
when we know we have to...
*/
#ifdef NEXT /* NeXTSTEP 1.0-3.0 */
#define SW_ACC_ID
#endif /* NEXT */
/* Support for tilde-expansion in file and directory names */
#ifdef POSIX
#define NAMEENV "LOGNAME"
#else
#ifdef BSD4
#define NAMEENV "USER"
#else
#ifdef ATTSV
#define NAMEENV "LOGNAME"
#endif /* ATTSV */
#endif /* BSD4 */
#endif /* POSIX */
/* Berkeley Unix Version 4.x */
/* 4.1bsd support from Charles E Brooks, EDN-VAX */
#ifdef BSD4
#ifdef MAXNAMLEN
#define BSD42
#endif /* MAXNAMLEN */
#endif /* BSD4 */
/* Definitions of some system commands */
char *DELCMD = "rm -f "; /* For file deletion */
char *CPYCMD = "cp "; /* For file copy */
char *RENCMD = "mv "; /* For file rename */
char *PWDCMD = "pwd "; /* For saying where I am */
#ifdef COMMENT
#ifdef HPUX10
char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */
char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */
#else
char *DIRCMD = "/bin/ls -l "; /* For directory listing */
char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */
#endif /* HPUX10 */
#else
char *DIRCMD = "ls -l "; /* For directory listing */
char *DIRCM2 = "ls -l "; /* For directory listing, no args */
#endif /* COMMENT */
char *TYPCMD = "cat "; /* For typing a file */
#ifdef HPUX
char *MAILCMD = "mailx"; /* For sending mail */
#else
#ifdef DGUX540
char *MAILCMD = "mailx";
#else
#ifdef UNIX
#ifdef CK_MAILCMD
char *MAILCMD = CK_MAILCMD; /* CFLAGS override */
#else
char *MAILCMD = "Mail"; /* Default */
#endif /* CK_MAILCMD */
#else
char *MAILCMD = "";
#endif /* UNIX */
#endif /* HPUX */
#endif /* DGUX40 */
#ifdef UNIX
#ifdef ANYBSD /* BSD uses lpr to spool */
#ifdef DGUX540 /* And DG/UX */
char * PRINTCMD = "lp";
#else
char * PRINTCMD = "lpr";
#endif /* DGUX540 */
#else /* Sys V uses lp */
#ifdef TRS16 /* except for Tandy-16/6000... */
char * PRINTCMD = "lpr";
#else
char * PRINTCMD = "lp";
#endif /* TRS16 */
#endif /* ANYBSD */
#else /* Not UNIX */
#define PRINTCMD ""
#endif /* UNIX */
#ifdef FT18 /* Fortune For:Pro 1.8 */
#undef BSD4
#endif /* FT18 */
#ifdef BSD4
char *SPACMD = "pwd ; df ."; /* Space in current directory */
#else
#ifdef FT18
char *SPACMD = "pwd ; du ; df .";
#else
char *SPACMD = "df ";
#endif /* FT18 */
#endif /* BSD4 */
char *SPACM2 = "df "; /* For space in specified directory */
#ifdef FT18
#define BSD4
#endif /* FT18 */
#ifdef BSD4
char *WHOCMD = "finger ";
#else
char *WHOCMD = "who ";
#endif /* BSD4 */
/* More system-dependent includes, which depend on symbols defined */
/* in the Kermit-specific includes. Oh what a tangled web we weave... */
#ifdef COHERENT /* <sys/file.h> */
#define NOFILEH
#endif /* COHERENT */
#ifdef MINIX
#define NOFILEH
#endif /* MINIX */
#ifdef aegis
#define NOFILEH
#endif /* aegis */
#ifdef unos
#define NOFILEH
#endif /* unos */
#ifndef NOFILEH
#include <sys/file.h>
#endif /* NOFILEH */
#ifndef is68k /* Whether to include <fcntl.h> */
#ifndef BSD41 /* All but a couple UNIXes have it. */
#ifndef FT18
#ifndef COHERENT
#include <fcntl.h>
#endif /* COHERENT */
#endif /* FT18 */
#endif /* BSD41 */
#endif /* is68k */
#ifdef COHERENT
#ifdef _I386
#include <fcntl.h>
#else
#include <sys/fcntl.h>
#endif /* _I386 */
#endif /* COHERENT */
extern int inserver; /* I am IKSD */
int guest = 0; /* Anonymous user */
#ifdef IKSD
extern int isguest;
extern char * anonroot;
#endif /* IKSD */
#ifdef CK_LOGIN
#define GUESTPASS 256
static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
static int logged_in = 0; /* Set when user is logged in */
static int askpasswd = 0; /* Have OK user, must ask for passwd */
#endif /* CK_LOGIN */
#ifdef CKROOT
static char ckroot[CKMAXPATH+1] = { NUL, NUL };
static int ckrootset = 0;
int ckrooterr = 0;
#endif /* CKROOT */
_PROTOTYP( VOID ignorsigs, (void) );
_PROTOTYP( VOID restorsigs, (void) );
/*
Change argument to "(const char *)" if this causes trouble.
Or... if it causes trouble, then maybe it was already declared
in a header file after all, so you can remove this prototype.
*/
#ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
#ifndef _POSIX_SOURCE
#ifndef NEXT
#ifndef SVR4
/* POSIX <pwd.h> already gave prototypes for these. */
#ifdef IRIX40
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
#ifdef IRIX51
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
#ifdef M_UNIX
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
#ifdef HPUX9
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
#ifdef HPUX10
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
#ifdef DCGPWNAM
_PROTOTYP( struct passwd * getpwnam, (const char *) );
#else
_PROTOTYP( struct passwd * getpwnam, (char *) );
#endif /* DCGPWNAM */
#endif /* HPUX10 */
#endif /* HPUX9 */
#endif /* M_UNIX */
#endif /* IRIX51 */
#endif /* IRIX40 */
#ifndef SUNOS4
#ifndef HPUX9
#ifndef HPUX10
#ifndef _SCO_DS
_PROTOTYP( struct passwd * getpwuid, (PWID_T) );
#endif /* _SCO_DS */
#endif /* HPUX10 */
#endif /* HPUX9 */
#endif /* SUNOS4 */
_PROTOTYP( struct passwd * getpwent, (void) );
#endif /* SVR4 */
#endif /* NEXT */
#endif /* _POSIX_SOURCE */
#endif /* NDGPWNAM */
#ifdef CK_SHADOW /* Shadow Passwords... */
#include <shadow.h>
#endif /* CK_SHADOW */
#ifdef CK_PAM /* PAM... */
#include <security/pam_appl.h>
#ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
#define PAM_SERVICE_TYPE "kermit"
#endif /* PAM_SERVICE_TYPE */
#ifdef SOLARIS
#define PAM_CONST
#else /* SOLARIS */
#define PAM_CONST CONST
#endif
static char * pam_pw = NULL;
int
#ifdef CK_ANSIC
pam_cb(int num_msg,
PAM_CONST struct pam_message **msg,
struct pam_response **resp,
void *appdata_ptr
)
#else /* CK_ANSIC */
pam_cb(num_msg, msg, resp, appdata_ptr)
int num_msg;
PAM_CONST struct pam_message **msg;
struct pam_response **resp;
void *appdata_ptr;
#endif /* CK_ANSIC */
{
int i;
debug(F111,"pam_cb","num_msg",num_msg);
for (i = 0; i < num_msg; i++) {
char message[PAM_MAX_MSG_SIZE];
/* Issue prompt and get response */
debug(F111,"pam_cb","Message",i);
debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
if (msg[i]->msg_style == PAM_ERROR_MSG) {
debug(F111,"pam_cb","PAM ERROR",0);
fprintf(stdout,"%s\n", msg[i]->msg);
return(0);
} else if (msg[i]->msg_style == PAM_TEXT_INFO) {
debug(F111,"pam_cb","PAM TEXT INFO",0);
fprintf(stdout,"%s\n", msg[i]->msg);
return(0);
} else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
debug(F111,"pam_cb","Reading response, no echo",0);
/* Ugly hack. We check to see if a password has been pushed */
/* into zvpasswd(). This would be true if the password was */
/* received by REMOTE LOGIN. */
if (pam_pw) {
ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
} else
readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
} else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
debug(F111,"pam_cb","Reading response, with echo",0);
readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
} else {
debug(F111,"pam_cb","unknown style",0);
return(0);
}
/* Allocate space for this message's response structure */
resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
if (!resp[i]) {
int j;
debug(F110,"pam_cb","malloc failure",0);
for (j = 0; j < i; j++) {
free(resp[j]->resp);
free(resp[j]);
}
return(0);
}
/* Allocate a buffer for the response */
resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
if (!resp[i]->resp) {
int j;
debug(F110,"pam_cb","malloc failure",0);
for (j = 0; j < i; j++) {
free(resp[j]->resp);
free(resp[j]);
}
free(resp[i]);
return(0);
}
/* Return the results back to PAM */
strcpy(resp[i]->resp, message); /* safe (prechecked) */
resp[i]->resp_retcode = 0;
}
debug(F110,"pam_cb","Exiting",0);
return(0);
}
#endif /* CK_PAM */
/* Define macros for getting file type */
#ifdef OXOS
/*
Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
incorrectly, so we force their redefinition.
*/
#undef S_ISREG
#undef S_ISDIR
#endif /* OXOS */
#ifdef UTSV /* Same deal for Amdahl UTSV */
#undef S_ISREG
#undef S_ISDIR
#endif /* UTSV */
#ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
#undef S_ISREG
#undef S_ISDIR
#endif /* UNISYS52 */
#ifdef ICLSVR3 /* And for old ICL versions */
#undef S_ISREG
#undef S_ISDIR
#endif /* ICLSVR3 */
#ifdef ISDIRBUG /* Also allow this from command line */
#ifdef S_ISREG
#undef S_ISREG
#endif /* S_ISREG */
#ifdef S_ISDIR
#undef S_ISDIR
#endif /* S_ISDIR */
#endif /* ISDIRBUG */
#ifndef _IFMT
#ifdef S_IFMT
#define _IFMT S_IFMT
#else
#define _IFMT 0170000
#endif /* S_IFMT */
#endif /* _IFMT */
#ifndef S_ISREG
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif /* S_ISREG */
#ifndef S_ISDIR
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif /* S_ISDIR */
/* The following mainly for NeXTSTEP... */
#ifndef S_IWUSR
#define S_IWUSR 0000200
#endif /* S_IWUSR */
#ifndef S_IRGRP
#define S_IRGRP 0000040
#endif /* S_IRGRP */
#ifndef S_IWGRP
#define S_IWGRP 0000020
#endif /* S_IWGRP */
#ifndef S_IXGRP
#define S_IXGRP 0000010
#endif /* S_IXGRP */
#ifndef S_IROTH
#define S_IROTH 0000004
#endif /* S_IROTH */
#ifndef S_IWOTH
#define S_IWOTH 0000002
#endif /* S_IWOTH */
#ifndef S_IXOTH
#define S_IXOTH 0000001
#endif /* S_IXOTH */
/*
Define maximum length for a file name if not already defined.
NOTE: This applies to a path segment (directory or file name),
not the entire path string, which can be CKMAXPATH bytes long.
*/
#ifdef QNX
#ifdef _MAX_FNAME
#define MAXNAMLEN _MAX_FNAME
#else
#define MAXNAMLEN 48
#endif /* _MAX_FNAME */
#else
#ifndef MAXNAMLEN
#ifdef sun
#define MAXNAMLEN 255
#else
#ifdef FILENAME_MAX
#define MAXNAMLEN FILENAME_MAX
#else
#ifdef NAME_MAX
#define MAXNAMLEN NAME_MAX
#else
#ifdef _POSIX_NAME_MAX
#define MAXNAMLEN _POSIX_NAME_MAX
#else
#ifdef _D_NAME_MAX
#define MAXNAMLEN _D_NAME_MAX
#else
#ifdef DIRSIZ
#define MAXNAMLEN DIRSIZ
#else
#define MAXNAMLEN 14
#endif /* DIRSIZ */
#endif /* _D_NAME_MAX */
#endif /* _POSIX_NAME_MAX */
#endif /* NAME_MAX */
#endif /* FILENAME_MAX */
#endif /* sun */
#endif /* MAXNAMLEN */
#endif /* QNX */
#ifdef COMMENT
/* As of 2001-11-03 this is handled in ckcdeb.h */
/* Longest pathname ... */
/*
Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
There is not necessarily even a definition for it anywhere, or it might have
another name. If you get it wrong, bad things happen with getcwd() and/or
getwd(). If you allocate a buffer that is too short, getwd() might write
over memory and getcwd() will fail with ERANGE. The definitions of these
functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
maximum path length in order to allocate a buffer that is the right size.
*/
#ifdef BSD44
#include <sys/param.h> /* For MAXPATHLEN */
#endif /* BSD44 */
#ifdef COHERENT
#include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
#endif /* COHERENT */
#endif /* COMMENT */
#ifdef MAXPATHLEN
#ifdef MAXPATH
#undef MAXPATH
#endif /* MAXPATH */
#define MAXPATH MAXPATHLEN
#else
#ifdef PATH_MAX
#define MAXPATH PATH_MAX
#else
#ifdef _POSIX_PATH_MAX
#define MAXPATH _POSIX_PATH_MAX
#else
#ifdef BSD42
#define MAXPATH 1024
#else
#ifdef SVR4
#define MAXPATH 1024
#else
#define MAXPATH 255
#endif /* SVR4 */
#endif /* BSD42 */
#endif /* _POSIX_PATH_MAX */
#endif /* PATH_MAX */
#endif /* MAXPATHLEN */
/* Maximum number of filenames for wildcard expansion */
#ifndef MAXWLD
/* Already defined in ckcdeb.h so the following is superfluous. */
/* Don't expect changing them to have any effect. */
#ifdef CK_SMALL
#define MAXWLD 50
#else
#ifdef BIGBUFOK
#define MAXWLD 102400
#else
#define MAXWLD 8192
#endif /* BIGBUFOK */
#endif /* CK_SMALL */
#endif /* MAXWLD */
static int maxnames = MAXWLD;
/* Define the size of the string space for filename expansion. */
#ifndef DYNAMIC
#ifdef PROVX1
#define SSPACE 500
#else
#ifdef BSD29
#define SSPACE 500
#else
#ifdef pdp11
#define SSPACE 500
#else
#ifdef aegis
#define SSPACE 10000 /* Size of string-generating buffer */
#else /* Default static buffer size */
#ifdef BIGBUFOK
#define SSPACE 65000 /* Size of string-generating buffer */
#else
#define SSPACE 2000 /* size of string-generating buffer */
#endif /* BIGBUFOK */
#endif /* aegis */
#endif /* pdp11 */
#endif /* BSD29 */
#endif /* PROVX1 */
static char sspace[SSPACE]; /* Buffer for generating filenames */
#else /* is DYNAMIC */
#ifdef BIGBUFOK
#define SSPACE 500000
#else
#define SSPACE 10000
#endif /* BIGBUFOK */
char *sspace = (char *)0;
#endif /* DYNAMIC */
static int ssplen = SSPACE; /* Length of string space buffer */
#ifdef DCLFDOPEN
/* fdopen() needs declaring because it's not declared in <stdio.h> */
_PROTOTYP( FILE * fdopen, (int, char *) );
#endif /* DCLFDOPEN */
#ifdef DCLPOPEN
/* popen() needs declaring because it's not declared in <stdio.h> */
_PROTOTYP( FILE * popen, (char *, char *) );
#endif /* DCLPOPEN */
extern int nopush;
/* More internal function prototypes */
/*
* The path structure is used to represent the name to match.
* Each slash-separated segment of the name is kept in one
* such structure, and they are linked together, to make
* traversing the name easier.
*/
struct path {
char npart[MAXNAMLEN+4]; /* name part of path segment */
struct path *fwd; /* forward ptr */
};
#ifndef NOPUSH
_PROTOTYP( int shxpand, (char *, char *[], int ) );
#endif /* NOPUSH */
_PROTOTYP( static int fgen, (char *, char *[], int ) );
_PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
_PROTOTYP( static VOID addresult, (char *, int) );
#ifdef COMMENT
/* Replaced by ckmatch() */
_PROTOTYP( static int match, (char *, char *) );
#endif /* COMMENT */
_PROTOTYP( char * whoami, (void) );
_PROTOTYP( UID_T real_uid, (void) );
_PROTOTYP( static struct path *splitpath, (char *p) );
_PROTOTYP( char * zdtstr, (time_t) );
_PROTOTYP( time_t zstrdt, (char *, int) );
/* Some systems define these symbols in include files, others don't... */
#ifndef R_OK
#define R_OK 4 /* For access */
#endif /* R_OK */
#ifndef W_OK
#define W_OK 2
#endif /* W_OK */
#ifndef X_OK
#define X_OK 1
#endif /* X_OK */
#ifndef O_RDONLY
#define O_RDONLY 000
#endif /* O_RDONLY */
/* syslog and wtmp items for Internet Kermit Service */
extern char * clienthost; /* From ckcmai.c. */
static char fullname[CKMAXPATH+1];
static char tmp2[CKMAXPATH+1];
extern int ckxlogging;
#ifdef CKXPRINTF /* Our printf macro conflicts with */
#undef printf /* use of "printf" in syslog.h */
#endif /* CKXPRINTF */
#ifdef CKSYSLOG
#ifdef RTAIX
#include <sys/syslog.h>
#else /* RTAIX */
#include <syslog.h>
#endif /* RTAIX */
#endif /* CKSYSLOG */
#ifdef CKXPRINTF
#define printf ckxprintf
#endif /* CKXPRINTF */
int ckxanon = 1; /* Anonymous login ok */
int ckxperms = 0040; /* Anonymous file permissions */
int ckxpriv = 1; /* Priv'd login ok */
#ifndef XFERFILE
#define XFERFILE "/var/log/iksd.log"
#endif /* XFERFILE */
/* wtmp logging for IKSD... */
#ifndef CKWTMP /* wtmp logging not selected */
int ckxwtmp = 0; /* Know this at runtime */
#else /* wtmp file details */
int ckxwtmp = 1;
#ifdef UTMPBUG /* Unfortunately... */
/*
Some versions of Linux have a <utmp.h> file that contains
"enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
any program that uses any of these words as variable names, function
names, macro names, etc. (Other versions of Linux have this declaration
within #if 0 ... #endif.) There is nothing we can do about this other
than to not include the stupid file. But we need stuff from it, so...
*/
#include <features.h>
#include <sys/types.h>
#define UT_LINESIZE 32
#define UT_NAMESIZE 32
#define UT_HOSTSIZE 256
struct timeval {
time_t tv_sec;
time_t tv_usec;
};
struct exit_status {
short int e_termination; /* Process termination status. */
short int e_exit; /* Process exit status. */
};
struct utmp {
short int ut_type; /* Type of login */
pid_t ut_pid; /* Pid of login process */
char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
char ut_id[4]; /* Inittab id */
char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
struct exit_status ut_exit; /* Exit status */
long ut_session; /* Session ID, used for windowing */
struct timeval ut_tv; /* Time entry was made */
int32_t ut_addr_v6[4]; /* Internet address of remote host */
char pad[20]; /* Reserved */
};
#define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
#define ut_name ut_user /* ... */
extern void
logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
__const char *__ut_host));
#else /* Not UTMPBUG */
#ifndef HAVEUTMPX /* Who has <utmpx.h> */
#ifdef SOLARIS
#define HAVEUTMPX
#else
#ifdef IRIX60
#define HAVEUTMPX
#else
#ifdef CK_SCOV5
#define HAVEUTMPX
#else
#ifdef HPUX100
#define HAVEUTMPX
#else
#ifdef UNIXWARE
#define HAVEUTMPX
#endif /* UNIXWARE */
#endif /* HPUX100 */
#endif /* CK_SCOV5 */
#endif /* IRIX60 */
#endif /* SOLARIS */
#endif /* HAVEUTMPX */
#ifdef HAVEUTMPX
#include <utmpx.h>
#else
#ifdef OSF50
/* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
#define __V40_OBJ_COMPAT 1
#endif /* OSF50 */
#include <utmp.h>
#ifdef OSF50
#undef __V40_OBJ_COMPAT
#endif /* OSF50 */
#endif /* HAVEUTMPX */
#endif /* UTMPBUG */
#ifndef WTMPFILE
#ifdef QNX
#define WTMPFILE "/usr/adm/wtmp.1"
#else
#ifdef LINUX
#define WTMPFILE "/var/log/wtmp"
#else
#define WTMPFILE "/usr/adm/wtmp"
#endif /* QNX */
#endif /* LINUX */
#endif /* WTMPFILE */
char * wtmpfile = NULL;
static int wtmpfd = 0;
static char cksysline[32] = { NUL, NUL };
#ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
#ifdef HAVEUTMPX /* utmpx always does */
#define HAVEUTHOST
#else
#ifdef LINUX /* Linux does */
#define HAVEUTHOST
#else
#ifdef SUNOS4 /* SunOS does */
#define HAVEUTHOST
#else
#ifdef AIX41 /* AIX 4.1 and later do */
#define HAVEUTHOST
#endif /* AIX41 */
#endif /* SUNOS4 */
#endif /* LINUX */
#endif /* HAVEUTMPX */
#endif /* HAVEUTHOST */
#ifdef UW200
PID_T _vfork() { /* To satisfy a library foulup */
return(fork()); /* in Unixware 2.0.x */
}
#endif /* UW200 */
VOID
#ifdef CK_ANSIC
logwtmp(const char * line, const char * name, const char * host)
#else
logwtmp(line, name, host) char *line, *name, *host;
#endif /* CK_ANSIC */
/* logwtmp */ {
#ifdef HAVEUTMPX
struct utmpx ut; /* Needed for ut_host[] */
#else
struct utmp ut;
#endif /* HAVEUTMPX */
struct stat buf;
/* time_t time(); */
if (!ckxwtmp)
return;
if (!wtmpfile)
makestr(&wtmpfile,WTMPFILE);
if (!line) line = "";
if (!name) name = "";
if (!host) host = "";
if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
ckxwtmp = 0;
debug(F110,"WTMP open failed",line,0);
return;
}
if (!fstat(wtmpfd, &buf)) {
ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
#ifdef HAVEUTHOST
/* Not portable */
ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
#endif /* HAVEUTHOST */
#ifdef HAVEUTMPX
time(&ut.ut_tv.tv_sec);
#else
#ifdef LINUX
/* In light of the following comment perhaps the previous line should */
/* be "#ifndef COMMENT". */
{
/*
* On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
* are not the same and attempt to use an address of
* ut.ut_time as an argument to time() call may cause
* "unaligned access" trap.
*/
time_t zz;
time(&zz);
ut.ut_time = zz;
}
#else
time(&ut.ut_time);
#endif /* LINUX */
#endif /* HAVEUTMPX */
if (write(wtmpfd, (char *)&ut, sizeof(struct utmp)) !=
sizeof(struct utmp)) {
#ifndef NOFTRUNCATE
#ifndef COHERENT
ftruncate(wtmpfd, buf.st_size); /* Error, undo any partial write */
#else
chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
#endif /* COHERENT */
#endif /* NOFTRUNCATE */
debug(F110,"WTMP write error",line,0);
} else {
debug(F110,"WTMP record OK",line,0);
return;
}
}
}
#endif /* CKWTMP */
#ifdef CKSYSLOG
/*
C K S Y S L O G -- C-Kermit system logging function,
For use by other modules.
This module can, but doesn't have to, use it.
Call with:
n = SYSLG_xx values defined in ckcdeb.h
s1, s2, s3: strings.
*/
VOID
cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
int level;
if (!ckxlogging) /* syslogging */
return;
if (!s1) s1 = ""; /* Fix null args */
if (!s2) s2 = "";
if (!s3) s3 = "";
switch (n) { /* Translate Kermit level */
case SYSLG_DB: /* to syslog level */
level = LOG_DEBUG;
break;
default:
level = m ? LOG_INFO : LOG_ERR;
}
debug(F110,"cksyslog s1",s1,0);
debug(F110,"cksyslog s2",s2,0);
debug(F110,"cksyslog s3",s3,0);
errno = 0;
syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
debug(F101,"cksyslog errno","",errno);
}
#endif /* CKSYSLOG */
/* Declarations */
int maxnam = MAXNAMLEN; /* Available to the outside */
int maxpath = MAXPATH;
int ck_znewn = -1;
#ifdef UNIX
char startupdir[MAXPATH+1];
#endif /* UNIX */
int pexitstat = -2; /* Process exit status */
FILE *fp[ZNFILS] = { /* File pointers */
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
/* Flags for each file indicating whether it was opened with popen() */
int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* Buffers and pointers used in buffered file input and output. */
#ifdef DYNAMIC
extern char *zinbuffer, *zoutbuffer;
#else
extern char zinbuffer[], zoutbuffer[];
#endif /* DYNAMIC */
extern char *zinptr, *zoutptr;
extern int zincnt, zoutcnt;
extern int wildxpand;
static long iflen = -1L; /* Input file length */
static PID_T pid = 0; /* pid of child fork */
static int fcount = 0; /* Number of files in wild group */
static int nxpand = 0; /* Copy of fcount */
static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
#ifndef NOFRILLS
#define ZMBUFLEN 200
static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
#endif /* NOFRILLS */
char **mtchs = NULL; /* Matches found for filename */
char **mtchptr = NULL; /* Pointer to current match */
/* Z K S E L F -- Kill Self: log out own job, if possible. */
/* Note, should get current pid, but if your system doesn't have */
/* getppid(), then just kill(0,9)... */
#ifndef SVR3
#ifndef POSIX
#ifndef OSFPC
/* Already declared in unistd.h for SVR3 and POSIX */
#ifdef CK_ANSIC
extern PID_T getppid(void);
#else
#ifndef PS2AIX10
#ifndef COHERENT
extern PID_T getppid();
#endif /* COHERENT */
#endif /* PS2AIX10 */
#endif /* CK_ANSIC */
#endif /* OSFPC */
#endif /* POSIX */
#endif /* SVR3 */
int
zkself() { /* For "bye", but no guarantee! */
#ifdef PROVX1
return(kill(0,9));
#else
#ifdef V7
return(kill(0,9));
#else
#ifdef TOWER1
return(kill(0,9));
#else
#ifdef FT18
return(kill(0,9));
#else
#ifdef aegis
return(kill(0,9));
#else
#ifdef COHERENT
return(kill((PID_T)getpid(),1));
#else
#ifdef PID_T
exit(kill((PID_T)getppid(),1));
return(0);
#else
exit(kill(getppid(),1));
return(0);
#endif
#endif
#endif
#endif
#endif
#endif
#endif
}
static VOID
getfullname(name) char * name; {
char *p = (char *)fullname;
int len = 0;
fullname[0] = '\0';
/* If necessary we could also chase down symlinks here... */
#ifdef COMMENT
/* This works but is incompatible with wuftpd */
if (isguest && anonroot) {
ckstrncpy(fullname,anonroot,CKMAXPATH);
len = strlen(fullname);
if (len > 0)
if (fullname[len-1] == '/')
len--;
}
p += len;
#endif /* COMMENT */
zfnqfp(name, CKMAXPATH - len, p);
while (*p) {
if (*p < '!') *p = '_';
p++;
}
}
/* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
VOID /* Called in ckcmai.c */
doiklog() {
if (iklogopen) /* Already open? */
return;
if (xferlog) { /* Open iksd log if requested */
if (!xferfile) /* If no pathname given */
makestr(&xferfile,XFERFILE); /* use this default */
if (*xferfile) {
xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
debug(F101,"doiklog open","",xferlog);
if (xferlog < 0) {
#ifdef CKSYSLOG
syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
#endif /* CKSYSLOG */
debug(F101,"doiklog open errno","",errno);
xferlog = 0;
} else
iklogopen = 1;
} else
xferlog = 0;
#ifdef CKSYSLOG
if (xferlog && ckxlogging)
syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
#endif /* CKSYSLOG */
}
}
/* Z O P E N I -- Open an existing file for input. */
/* Returns 1 on success, 0 on failure */
int
zopeni(n,name) int n; char *name; {
int x;
debug(F111,"zopeni",name,n);
if ((x = chkfn(n)) != 0) {
debug(F111,"zopeni chkfn",ckitoa(n),x);
return(0);
}
zincnt = 0; /* Reset input buffer */
if (n == ZSYSFN) { /* Input from a system function? */
#ifdef COMMENT
/*** Note, this function should not be called with ZSYSFN ***/
/*** Always call zxcmd() directly, and give it the real file number ***/
/*** you want to use. ***/
return(zxcmd(n,name)); /* Try to fork the command */
#else
debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
*nambuf = '\0'; /* No filename. */
return(0); /* fail. */
#endif /* COMMENT */
}
if (n == ZSTDIO) { /* Standard input? */
if (is_a_tty(0)) {
fprintf(stderr,"Terminal input not allowed");
debug(F110,"zopeni: attempts input from unredirected stdin","",0);
return(0);
}
fp[ZIFILE] = stdin;
ispipe[ZIFILE] = 0;
return(1);
}
#ifdef CKROOT
debug(F111,"zopeni setroot",ckroot,ckrootset);
if (ckrootset) if (!zinroot(name)) {
debug(F110,"zopeni setroot violation",name,0);
return(0);
}
#endif /* CKROOT */
fp[n] = fopen(name,"r"); /* Real file, open it. */
debug(F111,"zopeni fopen", name, fp[n]);
#ifdef ZDEBUG
printf("ZOPENI fp[%d]=%ld\n",n,fp[n]);
#endif /* ZDEBUG */
ispipe[n] = 0;
if (xferlog
#ifdef CKSYSLOG
|| ((ckxsyslog >= SYSLG_FA) && ckxlogging)
#endif /* CKSYSLOG */
) {
getfullname(name);
debug(F110,"zopeni fullname",fullname,0);
}
if (fp[n] == NULL) {
#ifdef CKSYSLOG
if (ckxsyslog >= SYSLG_FA && ckxlogging) {
syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
perror(fullname);
} else
#endif /* CKSYSLOG */
perror(name);
return(0);
} else {
#ifdef CKSYSLOG
if (ckxsyslog >= SYSLG_FA && ckxlogging)
syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
#endif /* CKSYSLOG */
clearerr(fp[n]);
return(1);
}
}
#ifdef QNX
#define DONDELAY
#else
#ifdef O_NDELAY
#define DONDELAY
#endif /* O_NDELAY */
#endif /* QNX */
/* Z O P E N O -- Open a new file for output. */
/*ARGSUSED*/ /* zz not used */
int
zopeno(n,name,zz,fcb)
/* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
char p[8];
int append = 0;
/* As of Version 5A, the attribute structure and the file information */
/* structure are included in the arglist. */
#ifdef DEBUG
debug(F111,"zopeno",name,n);
if (fcb) {
debug(F101,"zopeno fcb disp","",fcb->dsp);
debug(F101,"zopeno fcb type","",fcb->typ);
debug(F101,"zopeno fcb char","",fcb->cs);
} else {
debug(F100,"zopeno fcb is NULL","",0);
}
#endif /* DEBUG */
if (chkfn(n) != 0) /* Already open? */
return(0); /* Nothing to do. */
if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
fp[ZOFILE] = stdout;
ispipe[ZOFILE] = 0;
#ifdef COMMENT
/* This seems right but it breaks client server ops */
fp[n] = stdout;
ispipe[n] = 0;
#endif /* COMMENT */
#ifdef DEBUG
if (n != ZDFILE)
debug(F101,"zopeno fp[n]=stdout","",fp[n]);
#endif /* DEBUG */
zoutcnt = 0;
zoutptr = zoutbuffer;
return(1);
}
/* A real file. Open it in desired mode (create or append). */
#ifdef CKROOT
debug(F111,"zopeno setroot",ckroot,ckrootset);
if (ckrootset) if (!zinroot(name)) {
debug(F110,"zopeno setroot violation",name,0);
return(0);
}
#endif /* CKROOT */
ckstrncpy(p,"w",8); /* Assume write/create mode */
if (fcb) { /* If called with an FCB... */
if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
ckstrncpy(p,"a",8); /* Yes. */
debug(F100,"zopeno append","",0);
append = 1;
}
}
if (xferlog
#ifdef CKSYSLOG
|| ((ckxsyslog >= SYSLG_FC) && ckxlogging)
#endif /* CKSYSLOG */
) {
getfullname(name);
debug(F110,"zopeno fullname",fullname,0);
}
debug(F110,"zopeno fopen arg",p,0);
fp[n] = fopen(name,p); /* Try to open the file */
ispipe[ZIFILE] = 0;
#ifdef ZDEBUG
printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
#endif /* ZDEBUG */
if (fp[n] == NULL) { /* Failed */
debug(F101,"zopeno failed errno","",errno);
#ifdef CKSYSLOG
if (ckxsyslog >= SYSLG_FC && ckxlogging)
syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
n,
fullname,
append ? "append" : "create"
);
#endif /* CKSYSLOG */
#ifdef COMMENT /* Let upper levels print message. */
perror("Can't open output file");
#endif /* COMMENT */
} else { /* Succeeded */
extern int zofbuffer, zofblock, zobufsize;
debug(F101, "zopeno zobufsize", "", zobufsize);
if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
setbuf(fp[n],NULL); /* make it unbuffered. */
#ifdef DONDELAY
} else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
int flags;
if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
fcntl(fileno(fp[n]),F_SETFL, flags |
#ifdef QNX
O_NONBLOCK
#else
O_NDELAY
#endif /* QNX */
);
debug(F100,"zopeno ZOFILE nonblocking","",0);
#endif /* DONDELAY */
} else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
setbuf(fp[n],NULL);
debug(F100,"zopeno ZOFILE unbuffered","",0);
}
#ifdef CK_LOGIN
/* Enforce anonymous file-creation permission */
if (isguest)
if (n == ZWFILE || n == ZMFILE ||
n == ZOFILE || n == ZDFILE ||
n == ZTFILE || n == ZPFILE ||
n == ZSFILE)
chmod(name,ckxperms);
#endif /* CK_LOGIN */
#ifdef CKSYSLOG
if (ckxsyslog >= SYSLG_FC && ckxlogging)
syslog(LOG_INFO, "file[%d] %s: %s ok",
n,
fullname,
append ? "append" : "create"
);
#endif /* CKSYSLOG */
debug(F100, "zopeno ok", "", 0);
}
zoutcnt = 0; /* (PWP) reset output buffer */
zoutptr = zoutbuffer;
return((fp[n] != NULL) ? 1 : 0);
}
/* Z C L O S E -- Close the given file. */
/* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
int
zclose(n) int n; {
int x = 0, x2 = 0;
extern long ffc;
debug(F101,"zclose file number","",n);
if (chkfn(n) < 1) return(0); /* Check range of n */
if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
x2 = zoutdump();
if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
#ifndef NOPUSH
x = zclosf(n); /* do it specially */
#else
x = EOF;
#endif /* NOPUSH */
debug(F101,"zclose zclosf","",x);
debug(F101,"zclose zclosf fp[n]","",fp[n]);
} else {
if ((fp[n] != stdout) && (fp[n] != stdin))
x = fclose(fp[n]);
fp[n] = NULL;
#ifdef COMMENT
if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
if (fp[ZOFILE] == stdout)
fp[ZOFILE] = NULL;
#endif /* COMMENT */
}
iflen = -1L; /* Invalidate file length */
if (x == EOF) { /* if we got a close error */
debug(F101,"zclose fclose fails","",x);
return(-1);
} else if (x2 < 0) { /* or error flushing last buffer */
debug(F101,"zclose error flushing last buffer","",x2);
return(-1); /* then return an error */
} else {
/* Print log record compatible with wu-ftpd */
if (xferlog && (n == ZIFILE || n == ZOFILE)) {
char * s, *p;
extern char ttname[];
if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
debug(F101,"zclose iklogopen","",iklogopen);
if (iklogopen) {
int len;
char * fnam;
timenow = time(NULL);
#ifdef CK_LOGIN
if (logged_in)
s = clienthost;
else
#endif /* CK_LOGIN */
s = (char *)ttname;
if (!s) s = "";
if (!*s) s = "*";
#ifdef CK_LOGIN
if (logged_in) {
p = guestpass;
if (!*p) p = "*";
} else
#endif /* CK_LOGIN */
p = whoami();
len = 24 + 12 + (int)strlen(s) + 16
+ (int)strlen(fullname) + 1 + 1 + 1 + 1
+ (int)strlen(p) + 6 + 2 + 12;
fnam = fullname;
if (!*fnam) fnam = "(pipe)";
if (len > IKSDMSGLEN)
sprintf(iksdmsg, /* SAFE */
"%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
else
sprintf(iksdmsg, /* SAFE */
"%.24s %d %s %ld %s %c %s %c %c %s %s %d %s\n",
ctime(&timenow), /* date/time */
gtimer(), /* elapsed secs */
s, /* peer name */
ffc, /* byte count */
fnam, /* full pathname of file */
(binary ? 'b' : 'a'), /* binary or ascii */
"_", /* options = none */
n == ZIFILE ? 'o' : 'i', /* in/out */
#ifdef CK_LOGIN
(isguest ? 'a' : 'r'), /* User type */
#else
'r',
#endif /* CK_LOGIN */
p, /* Username or guest passwd */
#ifdef CK_LOGIN
logged_in ? "iks" : "kermit", /* Record ID */
#else
"kermit",
#endif /* CK_LOGIN */
0, /* User ID on client system unknown */
"*" /* Ditto */
);
debug(F110,"zclose iksdmsg",iksdmsg,0);
write(xferlog, iksdmsg, (int)strlen(iksdmsg));
}
}
debug(F101,"zclose returns","",1);
return(1);
}
}
/* Z C H I N -- Get a character from the input file. */
/* Returns -1 if EOF, 0 otherwise with character returned in argument */
int
zchin(n,c) int n; int *c; {
int a;
#ifdef IKSD
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
a = coninc(0);
if (*c < 0)
return(-1);
} else
#endif /* IKSD */
/* (PWP) Just in case this gets called when it shouldn't. */
if (n == ZIFILE) {
a = zminchar(); /* Note: this catches Ctrl-Z */
if (a < 0) /* (See zinfill()...) */
return(-1);
} else {
a = getc(fp[n]);
if (a == EOF) return(-1);
#ifdef CK_CTRLZ
/* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
return(-1);
#endif /* CK_CTRLZ */
}
*c = (CHAR) a & 0377;
return(0);
}
/* Z S I N L -- Read a line from a file */
/*
Writes the line into the address provided by the caller.
n is the Kermit "channel number".
Writing terminates when newline is encountered, newline is not copied.
Writing also terminates upon EOF or if length x is exhausted.
Returns 0 on success, -1 on EOF or error.
*/
int
zsinl(n,s,x) int n, x; char *s; {
int a, z = 0; /* z is return code. */
int count = 0;
int len = 0;
char *buf;
extern CHAR feol; /* Line terminator */
if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
return(-1);
buf = s;
s[0] = '\0'; /* Don't return junk */
a = -1; /* Current character, none yet. */
while (x--) { /* Up to given length */
int old = 0;
if (feol) /* Previous character */
old = a;
if (zchin(n,&a) < 0) { /* Read a character from the file */
debug(F101,"zsinl zchin fail","",count);
if (count == 0)
z = -1; /* EOF or other error */
break;
} else
count++;
if (feol) { /* Single-character line terminator */
if (a == feol)
break;
} else { /* CRLF line terminator */
if (a == '\015') /* CR, get next character */
continue;
if (old == '\015') { /* Previous character was CR */
if (a == '\012') { /* This one is LF, so we have a line */
break;
} else { /* Not LF, deposit CR */
*s++ = '\015';
x--;
len++;
}
}
}
*s = a; /* Deposit character */
s++;
len++;
}
*s = '\0'; /* Terminate the string */
debug(F011,"zsinl",buf,len);
return(z);
}
/* Z X I N -- Read x bytes from a file */
/*
Reads x bytes (or less) from channel n and writes them
to the address provided by the caller.
Returns number of bytes read on success, 0 on EOF or error.
*/
int
zxin(n,s,x) int n, x; char *s; {
#ifdef IKSD
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
int a, i;
a = ttchk();
if (a < 1) return(0);
for (i = 0; i < a && i < x; i++)
s[i] = coninc(0);
return(i);
}
#endif /* IKSD */
return(fread(s, sizeof (char), x, fp[n]));
}
/*
Z I N F I L L -- Buffered file input.
(re)fill the file input buffer with data. All file input
should go through this routine, usually by calling the zminchar()
macro defined in ckcker.h. Returns:
Value 0..255 on success, the character that was read.
-1 on end of file.
-2 on any kind of error other than end of file.
-3 timeout when reading from pipe (Kermit packet mode only).
*/
int
zinfill() {
extern int kactive, srvping;
errno = 0;
#ifdef ZDEBUG
printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
#endif /* ZDEBUG */
#ifdef IKSD
if (inserver && !local && fp[ZIFILE] == stdin) {
int a, i;
a = ttchk();
if (a < 0) return(-2);
for (i = 0; i < a && i < INBUFSIZE; i++) {
zinbuffer[i] = coninc(0);
}
zincnt = i;
/* set pointer to beginning, (== &zinbuffer[0]) */
zinptr = zinbuffer;
if (zincnt == 0) return(-1);
zincnt--; /* One less char in buffer */
return((int)(*zinptr++) & 0377); /* because we return the first */
}
#endif /* IKSD */
debug(F101,"zinfill kactive","",kactive);
if (!(kactive && ispipe[ZIFILE])) {
if (feof(fp[ZIFILE])) {
debug(F100,"ZINFILL feof","",0);
#ifdef ZDEBUG
printf("ZINFILL EOF\n");
#endif /* ZDEBUG */
return(-1);
}
}
clearerr(fp[ZIFILE]);
#ifdef SELECT
/* Here we can call select() to get a timeout... */
if (kactive && ispipe[ZIFILE]) {
int secs, z = 0;
#ifndef NOXFER
if (srvping) {
secs = 1;
debug(F101,"zinfill calling ttwait","",secs);
z = ttwait(fileno(fp[ZIFILE]),secs);
debug(F101,"zinfill ttwait","",z);
}
#endif /* NOXFER */
if (z == 0)
return(-3);
}
#endif /* SELECT */
#ifdef DEBUG
if (deblog) {
int i;
debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
#ifdef USE_MEMCPY
memset(zinbuffer, 0xFF, INBUFSIZE);
#else
for (i = 0; i < INBUFSIZE; i++) {
zinbuffer[i] = 0xFF;
#ifdef COMMENT /* Too much! */
debug(F101,"ZINFILL zinbuffer[i]","",i);
#endif /* COMMENT */
}
#endif /* USE_MEMCPY */
ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer);
}
#endif /* DEBUG */
/*
Note: The following read MUST be nonblocking when reading from a pipe
and we want timeouts to work. See zxcmd().
*/
zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
#ifdef ZDEBUG
printf("FREAD=%d\n",zincnt);
#endif /* ZDEBUG */
#ifdef CK_CTRLZ
/* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
register int i;
for (i = 0; i < zincnt; i++) {
if (zinbuffer[i] == SUB) {
zincnt = i; /* Stop at first Ctrl-Z */
if (i == 0)
return(-1);
break;
}
}
}
#endif /* CK_CTRLZ */
if (zincnt == 0) { /* Got nothing? */
if (ferror(fp[ZIFILE])) {
debug(F100,"ZINFILL ferror","",0);
debug(F101,"ZINFILL errno","",errno);
#ifdef ZDEBUG
printf("ZINFILL errno=%d\n",errno);
#endif /* ZDEBUG */
#ifdef EWOULDBLOCK
return((errno == EWOULDBLOCK) ? -3 : -2);
#else
return(-2);
#endif /* EWOULDBLOCK */
}
/* In case feof() didn't work just above -- sometimes it doesn't... */
if (feof(fp[ZIFILE]) ) {
debug(F100,"ZINFILL count 0 EOF return -1","",0);
return (-1);
} else {
debug(F100,"ZINFILL count 0 not EOF return -2","",0);
return(-2);
}
}
zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
zincnt--; /* One less char in buffer */
return((int)(*zinptr++) & 0377); /* because we return the first */
}
/* Z S O U T -- Write a string out to the given file, buffered. */
int
zsout(n,s) int n; char *s; {
int rc = 0;
rc = chkfn(n);
if (rc < 1) return(-1); /* Keep this, prevents memory faults */
if (!s) return(0); /* Null pointer, do nothing, succeed */
if (!*s) return(0); /* empty string, ditto */
#ifdef IKSD
/*
This happens with client-side Kermit server when a REMOTE command
was sent from the server to the client and the server is supposed to
display the text, but of course there is no place to display it
since it is in remote mode executing Kermit protocol.
*/
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
#ifdef COMMENT
return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
#else
return(0);
#endif /* COMMENT */
}
#endif /* IKSD */
if (n == ZSFILE)
return(write(fileno(fp[n]),s,(int)strlen(s)));
rc = fputs(s,fp[n]) == EOF ? -1 : 0;
if (n == ZWFILE)
fflush(fp[n]);
return(rc);
}
/* Z S O U T L -- Write string to file, with line terminator, buffered */
int
zsoutl(n,s) int n; char *s; {
if (zsout(n,s) < 0)
return(-1);
#ifdef IKSD
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
#ifdef COMMENT
return(ttoc(LF));
#else
return(0); /* See comments in zsout() */
#endif /* COMMENT */
}
#endif /* IKSD */
if (n == ZSFILE) /* Session log is unbuffered */
return(write(fileno(fp[n]),"\n",1));
else if (fputs("\n",fp[n]) == EOF)
return(-1);
if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
fflush(fp[n]);
return(0);
}
/* Z S O U T X -- Write x characters to file, unbuffered. */
int
zsoutx(n,s,x) int n, x; char *s; {
#ifdef IKSD
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
#ifdef COMMENT
return(ttol(s,x)); /* See comments in zsout() */
#else
return(x);
#endif /* COMMENT */
}
#endif /* IKSD */
#ifdef COMMENT
if (chkfn(n) < 1) return(-1);
return(write(fp[n]->_file,s,x));
#endif /* COMMENT */
return(write(fileno(fp[n]),s,x) == x ? x : -1);
}
/* Z C H O U T -- Add a character to the given file. */
/* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
int
#ifdef CK_ANSIC
zchout(register int n, char c)
#else
zchout(n,c) register int n; char c;
#endif /* CK_ANSIC */
/* zchout() */ {
/* if (chkfn(n) < 1) return(-1); */
#ifdef IKSD
if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
#ifdef COMMENT
return(ttoc(c));
#else
return(0); /* See comments in zsout() */
#endif /* COMMENT */
}
#endif /* IKSD */
if (n == ZSFILE) /* Use unbuffered for session log */
return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
/* Buffered for everything else */
if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
return(ferror(fp[n])?-1:0); /* Check to make sure */
else /* Otherwise... */
return(0); /* There was no error. */
}
/* (PWP) buffered character output routine to speed up file IO */
int
zoutdump() {
int x;
char * zp;
zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
#ifdef DEBUG
if (deblog)
debug(F101,"zoutdump zoutcnt","",zoutcnt);
#endif /* DEBUG */
if (zoutcnt == 0) { /* Nothing to output */
return(0);
} else if (zoutcnt < 0) { /* Unexpected negative argument */
zoutcnt = 0; /* Reset output buffer count */
return(-1); /* and fail. */
}
#ifdef IKSD
if (inserver && !local && fp[ZOFILE] == stdout) {
#ifdef COMMENT
x = ttol(zoutbuffer,zoutcnt);
#else
x = 1; /* See comments in zsout() */
#endif /* COMMENT */
zoutcnt = 0;
return(x > 0 ? 0 : -1);
}
#endif /* IKSD */
/*
Frank Prindle suggested that replacing this fwrite() by an fflush()
followed by a write() would improve the efficiency, especially when
writing to stdout. Subsequent tests showed a 5-fold improvement.
*/
#ifdef COMMENT
if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
#endif /* COMMENT */
#ifndef CK_NONBLOCK
fflush(fp[ZOFILE]);
#endif /* CK_NONBLOCK */
zp = zoutbuffer;
while (zoutcnt > 0) {
if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
#ifdef DEBUG
if (deblog) /* Save a function call... */
debug(F101,"zoutdump wrote","",x);
#endif /* DEBUG */
zoutcnt -= x; /* Adjust output buffer count */
zp += x; /* and pointer */
} else {
#ifdef DEBUG
if (deblog) {
debug(F101,"zoutdump write error","",errno);
debug(F101,"zoutdump write returns","",x);
}
#endif /* DEBUG */
zoutcnt = 0; /* Reset output buffer count */
return(-1); /* write() failed */
}
}
return(0);
}
/* C H K F N -- Internal function to verify file number is ok */
/*
Returns:
-1: File number n is out of range
0: n is in range, but file is not open
1: n in range and file is open
*/
int
chkfn(n) int n; {
/* if (n != ZDFILE) debug(F101,"chkfn","",n); */
if (n < 0 || n >= ZNFILS) {
if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
return(-1);
} else {
/* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
return((fp[n] == NULL) ? 0 : 1);
}
}
/* Z G E T F S -- Return file size regardless of accessibility */
/*
Used for directory listings, etc.
Returns:
The size of the file in bytes, 0 or greater, if the size can be learned.
-1 if the file size can not be obtained.
Also (and this is a hack just for UNIX):
If the argument is the name of a symbolic link,
the global variable issymlink is set to 1,
and the global buffer linkname[] gets the link value.
And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
This lets us avoid numerous redundant calls to stat().
*/
int zgfs_link = 0;
int zgfs_dir = 0;
time_t zgfs_mtime = 0;
unsigned int zgfs_mode = 0;
#ifdef CKSYMLINK
char linkname[CKMAXPATH+1];
#ifndef _IFLNK
#define _IFLNK 0120000
#endif /* _IFLNK */
#endif /* CKSYMLINK */
long
zgetfs(name) char *name; {
struct stat buf;
char fnam[CKMAXPATH+4];
long size = -1L;
int x;
int needrlink = 0;
char * s;
if (!name) name = "";
if (!*name) return(-1);
#ifdef UNIX
x = strlen(name);
if (x == 9 && !strcmp(name,"/dev/null"))
return(0);
#endif /* UNIX */
s = name;
#ifdef DTILDE
if (*s == '~') {
s = tilde_expand(s);
if (!s) s = "";
if (!*s) s = name;
}
#endif /* DTILDE */
x = ckstrncpy(fnam,s,CKMAXPATH);
s = fnam;
debug(F111,"zgetfs fnam",s,x);
if (x > 0 && s[x-1] == '/')
s[x-1] = '\0';
zgfs_dir = 0; /* Assume it's not a directory */
zgfs_link = 0; /* Assume it's not a symlink */
zgfs_mtime = 0; /* No time yet */
zgfs_mode = 0; /* No permission bits yet */
#ifdef CKSYMLINK /* We're doing symlinks? */
#ifdef USE_LSTAT /* OK to use lstat()? */
x = lstat(s,&buf);
debug(F101,"STAT","",1);
if (x < 0) /* stat() failed */
return(-1);
if ( /* Now see if it's a symlink */
#ifdef S_ISLNK
S_ISLNK(buf.st_mode)
#else
#ifdef _IFLNK
((_IFMT & buf.st_mode) == _IFLNK)
#endif /* _IFLNK */
#endif /* S_ISLNK */
) {
zgfs_link = 1; /* It's a symlink */
linkname[0] = '\0'; /* Get the name */
x = readlink(s,linkname,CKMAXPATH);
debug(F101,"zgetfs readlink",s,x);
if (x > -1 && x < CKMAXPATH) { /* It's a link */
linkname[x] = '\0';
size = buf.st_size; /* Remember size of link */
x = stat(s,&buf); /* Now stat the linked-to file */
debug(F101,"STAT","",2);
if (x < 0) /* so we can see if it's a directory */
return(-1);
} else {
ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
}
}
#else /* !USE_LSTAT */
x = stat(s,&buf); /* No lstat(), use stat() instead */
debug(F101,"STAT","",3);
if (x < 0)
return(-1);
#endif /* USE_LSTAT */
/* Do we need to call readlink()? */
#ifdef NOLINKBITS
/*
lstat() does not work in SCO operating systems. From "man NS lstat":
lstat obtains information about the file named by path. In the case of a
symbolic link, lstat returns information about the link, and not the file
named by the link. It is only used by the NFS automount daemon and should
not be utilized by users.
*/
needrlink = 1;
debug(F101,"zgetfs forced needrlink","",needrlink);
#else
#ifdef S_ISLNK
needrlink = S_ISLNK(buf.st_mode);
debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
#else
#ifdef _IFLNK
needrlink = (_IFMT & buf.st_mode) == _IFLNK;
debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
#else
needrlink = 1;
debug(F101,"zgetfs default needrlink","",needrlink);
#endif /* _IFLNK */
#endif /* S_ISLNK */
#endif /* NOLINKBITS */
if (needrlink) {
linkname[0] = '\0';
errno = 0;
x = readlink(s,linkname,CKMAXPATH);
#ifdef DEBUG
debug(F111,"zgetfs readlink",s,x);
if (x < 0)
debug(F101,"zgetfs readlink errno","",errno);
else
debug(F110,"zgetfs readlink result",linkname,0);
#endif /* DEBUG */
if (x > -1 && x < CKMAXPATH) {
zgfs_link = 1;
linkname[x] = '\0';
}
}
#else /* !CKSYMLINK */
x = stat(s,&buf); /* Just stat the file */
debug(F111,"zgetfs stat",s,x);
if (x < 0) /* and get the size */
return(-1);
#endif /* CKSYMLINK */
zgfs_mtime = buf.st_mtime;
zgfs_mode = buf.st_mode;
zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
debug(F111,"zgetfs size",s,size);
debug(F111,"zgetfs st_size",s,buf.st_size);
return((size < 0L) ? buf.st_size : size); /* Return the size */
}
/* Z C H K I -- Check if input file exists and is readable */
/*
Returns:
>= 0 if the file can be read (returns the size).
-1 if file doesn't exist or can't be accessed,
-2 if file exists but is not readable (e.g. a directory file).
-3 if file exists but protected against read access.
For Berkeley Unix, a file must be of type "regular" to be readable.
Directory files, special files, and symbolic links are not readable.
*/
long
zchki(name) char *name; {
struct stat buf;
char * s;
int x, itsadir = 0;
extern int zchkid, diractive, matchfifo;
if (!name)
return(-1);
x = strlen(name);
if (x < 1)
return(-1);
s = name;
#ifdef UNIX
if (x == 9 && !strcmp(s,"/dev/null"))
return(0);
if (x == 8 && !strcmp(s,"/dev/tty"))
return(0);
#endif /* UNIX */
#ifdef DTILDE
if (*s == '~') {
s = tilde_expand(s);
if (!s) s = "";
if (!*s) s = name;
}
#endif /* DTILDE */
#ifdef CKROOT
debug(F111,"zchki setroot",ckroot,ckrootset);
if (ckrootset) if (!zinroot(name)) {
debug(F110,"zchki setroot violation",name,0);
return(-1);
}
#endif /* CKROOT */
x = stat(s,&buf);
debug(F101,"STAT","",5);
if (x < 0) {
debug(F111,"zchki stat fails",s,errno);
return(-1);
}
if (S_ISDIR (buf.st_mode))
itsadir = 1;
if (!(itsadir && zchkid)) { /* Unless this... */
if (!S_ISREG (buf.st_mode) /* Must be regular file */
#ifdef S_ISFIFO
&& (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
#endif /* S_ISFIFO */
) {
debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
return(-2);
}
}
debug(F111,"zchki stat ok:",s,x);
if (diractive) { /* If listing don't check access */
x = 1;
} else {
#ifdef SW_ACC_ID
debug(F100,"zchki swapping ids for access()","",0);
priv_on();
#endif /* SW_ACC_ID */
if ((x = access(s,R_OK)) < 0)
x = access(s,X_OK); /* For RUN-class commands */
#ifdef SW_ACC_ID
priv_off();
debug(F100,"zchki swapped ids restored","",0);
#endif /* SW_ACC_ID */
}
if (x < 0) { /* Is the file accessible? */
debug(F111,"zchki access failed:",s,x); /* No */
return(-3);
} else {
iflen = buf.st_size; /* Yes, remember size */
ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
debug(F111,"zchki access ok:",s,iflen);
return((iflen > -1L) ? iflen : 0L);
}
}
/* Z C H K O -- Check if output file can be created */
/*
Returns -1 if write permission for the file would be denied, 0 otherwise.
NOTE: The design is flawed. There is no distinction among:
. Can I overwrite an existing file?
. Can I create a file (or directory) in an existing directory?
. Can I create a file (or directory) and its parent(s)?
*/
int
zchko(name) char *name; {
int i, x, itsadir = 0;
char *s;
char * oname;
extern int zchkod; /* Used by IF WRITEABLE */
debug(F110,"zchko entry",name,0);
if (!name) return(-1); /* Watch out for null pointer. */
oname = name;
#ifdef CKROOT
debug(F111,"zchko setroot",ckroot,ckrootset);
if (ckrootset) if (!zinroot(name)) {
debug(F110,"zchko setroot violation",name,0);
errno = EACCES;
return(-1);
}
#endif /* CKROOT */
x = (int)strlen(name); /* Get length of filename */
debug(F111,"zchko len",name,x);
debug(F111,"zchko zchkod",name,zchkod);
#ifdef UNIX
/*
Writing to null device is OK.
*/
if (x == 9 && !strcmp(name,"/dev/null"))
return(0);
if (x == 8 && !strcmp(name,"/dev/tty"))
return(0);
#endif /* UNIX */
s = name;
#ifdef DTILDE
if (*s == '~') {
s = tilde_expand(s);
if (!s) s = "";
if (!*s) s = name;
x = strlen(s);
}
#endif /* DTILDE */
name = s;
s = NULL;
/*
zchkod is a global flag meaning we're checking not to see if the directory
file is writeable, but if it's OK to create files IN the directory.
*/
if (!zchkod && isdir(name)) /* Directories are not writeable */
return(-1);
s = malloc(x+3); /* Must copy because we can't */
if (!s) { /* write into our argument. */
fprintf(stderr,"zchko: Malloc error 46\n");
return(-1);
}
ckstrncpy(s,name,x+3);
for (i = x; i > 0; i--) { /* Strip filename from right. */
if (ISDIRSEP(s[i-1])) {
itsadir = 1;
break;
}
}
debug(F101,"zchko i","",i);
debug(F101,"zchko itsadir","",itsadir);
#ifdef COMMENT
/* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
if (i == 0) /* If no path, use current directory */
strcpy(s,"./");
else /* Otherwise, use given one. */
s[i] = '\0';
#else
#ifdef COMMENT
/*
The following does not work for "foo/bar" where the foo directory does
not exist even though we could create it: access("foo/.") fails, but
access("foo") works OK.
*/
/* So now we use "path/." if path given, or "." if no path given. */
s[i++] = '.'; /* Append "." to path. */
s[i] = '\0';
#else
/* So NOW we strip path segments from the right as long as they don't */
/* exist -- we only call access() for path segments that *do* exist.. */
/* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
/* succeeds when I have write access to foo and bar but baz doesn't exit.) */
if (itsadir && i > 0) {
s[i-1] = '\0';
while (s[0] && !isdir(s)) {
for (i = (int)strlen(s); i > 0; i--) {
if (ISDIRSEP(s[i-1])) {
s[i-1] = '\0';
break;
}
}
if (i == 0)
s[0] = '\0';
}
} else {
s[i++] = '.'; /* Append "." to path. */
s[i] = '\0';
}
#endif /* COMMENT */
#endif /* COMMENT */
if (!s[0])
ckstrncpy(s,".",x+3);
#ifdef SW_ACC_ID
debug(F100,"zchko swapping ids for access()","",0);
priv_on();
#endif /* SW_ACC_ID */
x = access(s,W_OK); /* Check access of path. */
#ifdef SW_ACC_ID
priv_off();
debug(F100,"zchko swapped ids restored","",0);
#endif /* SW_ACC_ID */
if (x < 0)
debug(F111,"zchko access failed:",s,errno);
else
debug(F111,"zchko access ok:",s,x);
free(s); /* Free temporary storage */
return((x < 0) ? -1 : 0); /* and return. */
}
/* Z D E L E T -- Delete the named file. */
/* Returns: -1 on error, 0 on success */
int
zdelet(name) char *name; {
int x;
#ifdef CK_LOGIN
if (isguest)
return(-1);
#endif /* CK_LOGIN */
#ifdef CKROOT
debug(F111,"zdelet setroot",ckroot,ckrootset);
if (ckrootset) if (!zinroot(name)) {
debug(F110,"zdelet setroot violation",name,0);
return(-1);
}
#endif /* CKROOT */
x = unlink(name);
debug(F111,"zdelet",name,x);
#ifdef CKSYSLOG
if (ckxsyslog >= SYSLG_FC && ckxlogging) {
fullname[0] = '\0';
zfnqfp(name,CKMAXPATH,fullname);
debug(F110,"zdelet fullname",fullname,0);
if (x < 0)
syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
else
syslog(LOG_INFO, "file[] %s: delete ok", fullname);
}
#endif /* CKSYSLOG */
return(x);
}
/* Z R T O L -- Convert remote filename into local form */
VOID
zrtol(name,name2) char *name, *name2; {
nzrtol(name,name2,1,0,CKMAXPATH);
}
VOID
nzrtol(name,name2,fncnv,fnrpath,max)
char *name, *name2; int fncnv, fnrpath, max;
{ /* nzrtol */
char *s, *p;
int flag = 0, n = 0;
char fullname[CKMAXPATH+1];
int devnull = 0;
int acase = 0;
if (!name2) return;
if (!name) name = "";
debug(F111,"nzrtol name",name,fncnv);
#ifdef DTILDE
s = name;
if (*s == '~') {
s = tilde_expand(s);
if (!s) s = "";
if (*s) name = s;
}
#endif /* DTILDE */
/* Handle the path -- we don't have to convert its format, since */
/* the standard path format and our (UNIX) format are the same. */
fullname[0] = NUL;
devnull = !strcmp(name,"/dev/null");
if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
zstrip(name,&p);
strncpy(fullname,p,CKMAXPATH);
} else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
strncpy(fullname,name,CKMAXPATH);
} else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
} else { /* Ditto */
ckstrncpy(fullname,name,CKMAXPATH);
}
fullname[CKMAXPATH] = NUL;
debug(F110,"nzrtol fullname",fullname,0);
#ifndef NOTRUNCATE
/*
The maximum length for any segment of a filename is MAXNAMLEN, defined
above. On some platforms (at least QNX) if a segment exceeds this limit,
the open fails with ENAMETOOLONG, so we must prevent it by truncating each
overlong name segment to the maximum segment length before passing the
name to open(). This must be done even when file names are literal, so as
not to halt a file transfer unnecessarily.
*/
{
char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
char *p = fullname; /* Source and */
char *s = buf; /* destination pointers */
int i = 0, n = 0;
debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
if (++i > MAXNAMLEN) { /* If this segment too long */
while (*p && *p != '/') /* skip past the rest... */
p++;
i = 0; /* and reset counter. */
} else if (*p == '/') { /* End of this segment. */
i = 0; /* Reset counter. */
}
*s++ = *p++; /* Copy this character. */
n++;
}
*s = NUL;
ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
debug(F111,"nzrtol sizing",fullname,n);
}
#endif /* NOTRUNCATE */
if (!fncnv || devnull) { /* Not converting */
ckstrncpy(name2,fullname,max); /* We're done. */
return;
}
name = fullname; /* Converting */
p = name2;
for (; *name != '\0' && n < maxnam; name++) {
if (*name > SP) flag = 1; /* Strip leading blanks and controls */
if (flag == 0 && *name < '!')
continue;
if (fncnv > 0) {
if (*name == SP) {
*p++ = '_';
n++;
continue;
}
if (isupper(*name)) /* Check for mixed case */
acase |= 1;
else if (islower(*name))
acase |= 2;
}
*p++ = *name;
n++;
}
*p-- = '\0'; /* Terminate */
while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
*p-- = '\0';
if (*name2 == '\0') { /* Nothing left? */
ckstrncpy(name2,"NONAME",max); /* do this... */
} else if (acase == 1) { /* All uppercase? */
p = name2; /* So convert all letters to lower */
while (*p) {
if (isupper(*p))
*p = tolower(*p);
p++;
}
}
debug(F110,"nzrtol new name",name2,0);
}
/* Z S T R I P -- Strip device & directory name from file specification */
/* Strip pathname from filename "name", return pointer to result in name2 */
static char work[CKMAXPATH+1];
VOID
zstrip(name,name2) char *name, **name2; {
char *cp, *pp;
int n = 0;
debug(F110,"zstrip before",name,0);
if (!name) { *name2 = ""; return; }
pp = work;
#ifdef DTILDE
/* Strip leading tilde */
if (*name == '~') name++;
debug(F110,"zstrip after tilde-stripping",name,0);
#endif /* DTILDE */
for (cp = name; *cp; cp++) {
if (ISDIRSEP(*cp)) {
pp = work;
n = 0;
} else {
*pp++ = *cp;
if (n++ >= CKMAXPATH)
break;
}
}
*pp = '\0'; /* Terminate the string */
*name2 = work;
debug(F110,"zstrip after",*name2,0);
}
/* Z L T O R -- Local TO Remote */
VOID
zltor(name,name2) char *name, *name2; {
nzltor(name,name2,1,0,CKMAXPATH);
}
/* N Z L T O R -- New Local TO Remote */
/*
fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
*/
VOID
nzltor(name,name2,fncnv,fnspath,max)
char *name, *name2; int fncnv, fnspath, max;
{ /* nzltor */
char *cp, *pp;
#ifdef COMMENT
int dc = 0;
#endif /* COMMENT */
int n = 0;
char *dotp = NULL;
char *dirp = NULL;
char fullname[CKMAXPATH+1];
char *p;
CHAR c;
#ifndef NOCSETS
extern int fcharset, /* tcharset, */ language;
int langsv;
_PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
#ifdef CK_ANSIC
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
#else
extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
#endif /* CK_ANSIC */
langsv = language;
language = L_USASCII;
#ifdef COMMENT
/* Proper translation of filenames must be done elsewhere */
n = tcharset ? tcharset : TC_USASCII;
sxo = xls[n][fcharset];
#else
sxo = xls[TC_USASCII][fcharset];
#endif /* COMMENT */
#endif /* NOCSETS */
debug(F110,"nzltor name",name,0);
/* Handle pathname */
fullname[0] = NUL;
if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
zstrip(name,&p);
ckstrncpy(fullname,p,CKMAXPATH);
} else { /* PATHNAMES RELATIVE or ABSOLUTE */
char * p = name;
while (1) {
if (!strncmp(p,"../",3))
p += 3;
else if (!strncmp(p,"./",2))
p += 2;
else