1 /* C K U F I O -- Kermit file system support for UNIX, Aegis, and Plan 9 */
2
3 #define CK_NONBLOCK /* See zoutdump() */
4
5 #ifdef aegis
6 char *ckzv = "Aegis File support, 9.0.224, 28 Sep 2020";
7 #else
8 #ifdef Plan9
9 char *ckzv = "Plan 9 File support, 9.0.224, 28 Sep 2020";
10 #else
11 char *ckzv = "UNIX File support, 9.0.224, 28 Sep 2020";
12 #endif /* Plan9 */
13 #endif /* aegis */
14 /*
15 Author: Frank da Cruz <fdc@columbia.edu>,
16 Columbia University 1974-2011; The Kermit Project 2011-????.
17
18 Copyright (C) 1985, 2020,
19 Trustees of Columbia University in the City of New York.
20
21 1767All rights reserved. See the C-Kermit COPYING.TXT file or the
22 copyright text in the ckcmai.c module for disclaimer and permissions.
23 */
24
25 /*
26 NOTE TO CONTRIBUTORS: This file, and all the other C-Kermit files, must be
27 compatible with C preprocessors that support only #ifdef, #else, #endif,
28 #define, and #undef. Please do not use #if, logical operators, or other
29 preprocessor features in any of the portable C-Kermit modules. You can,
30 of course, use these constructions in platform-specific modules where you
31 know they are supported.
32 */
33 /* Include Files */
34
35 #ifdef MINIX2
36 #define _MINIX
37 #endif /* MINIX2 */
38
39 #include "ckcsym.h"
40 #include "ckcdeb.h"
41 #include "ckcasc.h"
42
43 #ifndef NOCSETS
44 #include "ckcxla.h"
45 #endif /* NOCSETS */
46
47 /* To avoid pulling in all of ckuusr.h so we copy the few needed prototypes */
48
49 struct mtab { /* Macro table, like keyword table */
50 char *kwd; /* But with pointers for vals */
51 char *mval; /* instead of ints. */
52 short flgs;
53 };
54 _PROTOTYP( int mlook, (struct mtab [], char *, int) );
55 _PROTOTYP( int dodo, (int, char *, int) );
56 _PROTOTYP( int parser, ( int ) );
57
58 #ifdef COMMENT
59 /* This causes trouble in C-Kermit 8.0. I don't remember the original */
60 /* reason for this being here but it must have been needed at the time... */
61 #ifdef OSF13
62 #ifdef CK_ANSIC
63 #ifdef _NO_PROTO
64 #undef _NO_PROTO
65 #endif /* _NO_PROTO */
66 #endif /* CK_ANSIC */
67 #endif /* OSF13 */
68 #endif /* COMMENT */
69
70 #ifndef HPUXPRE65
71 #include <errno.h> /* Error number symbols */
72 #else
73 #ifndef ERRNO_INCLUDED
74 #include <errno.h> /* Error number symbols */
75 #endif /* ERRNO_INCLUDED */
76 #endif /* HPUXPRE65 */
77
78 #include <signal.h>
79
80 #ifdef MINIX2
81 #undef MINIX
82 #undef CKSYSLOG
83 #include <limits.h>
84 #include <time.h>
85 #define NOFILEH
86 #endif /* MINIX2 */
87
88 #ifdef MINIX
89 #include <limits.h>
90 #include <sys/types.h>
91 #include <time.h>
92 #else
93 #ifdef POSIX
94 #include <limits.h>
95 #else
96 #ifdef SVR3
97 #include <limits.h>
98 #endif /* SVR3 */
99 #endif /* POSIX */
100 #endif /* MINIX */
101 /*
102 Directory Separator macros, to allow this module to work with both UNIX and
103 OS/2: Because of ambiguity with the command line editor escape \ character,
104 the directory separator is currently left as / for OS/2 too, because the
105 OS/2 kernel also accepts / as directory separator.
106 */
107 #ifndef DIRSEP
108 #define DIRSEP '/'
109 #endif /* DIRSEP */
110 #ifndef ISDIRSEP
111 #define ISDIRSEP(c) ((c)=='/')
112 #endif /* ISDIRSEP */
113
114 #ifdef SDIRENT
115 #define DIRENT
116 #endif /* SDIRENT */
117
118 #ifdef XNDIR
119 #include <sys/ndir.h>
120 #else /* !XNDIR */
121 #ifdef NDIR
122 #include <ndir.h>
123 #else /* !NDIR, !XNDIR */
124 #ifdef RTU
125 #include "/usr/lib/ndir.h"
126 #else /* !RTU, !NDIR, !XNDIR */
127 #ifdef DIRENT
128 #ifdef SDIRENT
129 #include <sys/dirent.h>
130 #else
131 #include <dirent.h>
132 #endif /* SDIRENT */
133 #else
134 #include <sys/dir.h>
135 #endif /* DIRENT */
136 #endif /* RTU */
137 #endif /* NDIR */
138 #endif /* XNDIR */
139
140 #ifdef __NetBSD__
141 #include <sys/wait.h>
142 #endif /* __NetBSD__ */
143
144 #ifdef UNIX /* Pointer arg to wait() allowed */
145 #define CK_CHILD /* Assume this is safe in all UNIX */
146 #endif /* UNIX */
147
148 extern int binary, recursive, stathack;
149 #ifdef CK_CTRLZ
150 extern int eofmethod;
151 #endif /* CK_CTRLZ */
152
153 #include <pwd.h> /* Password file for shell name */
154 #ifdef CK_SRP
155 #include <t_pwd.h> /* SRP Password file */
156 #endif /* CK_SRP */
157
158 #ifdef HPUX10_TRUSTED
159 #include <hpsecurity.h>
160 #include <prot.h>
161 #endif /* HPUX10_TRUSTED */
162
163 #ifdef COMMENT
164 /* Moved to ckcdeb.h */
165 #ifdef POSIX
166 #define UTIMEH
167 #else
168 #ifdef HPUX9
169 #define UTIMEH
170 #endif /* HPUX9 */
171 #endif /* POSIX */
172 #endif /* COMMENT */
173
174 #ifdef SYSUTIMEH /* <sys/utime.h> if requested, */
175 #include <sys/utime.h> /* for extra fields required by */
176 #else /* 88Open spec. */
177 #ifdef UTIMEH /* or <utime.h> if requested */
178 #include <utime.h> /* (SVR4, POSIX) */
179 #ifndef BSD44
180 #ifndef V7
181 /* Not sure why this is here. What it implies is that the code bracketed
182 by SYSUTIMEH is valid on all platforms on which we support time
183 functionality. But we know that is not true because the BSD44 and V7
184 platforms do not support sys/utime.h and the data structures which
185 are defined in them. Now this worked before because prior to today's
186 changes the UTIMEH definition for BSD44 and V7 did not take place
187 until after SYSUTIMEH was defined. It also would not have been a
188 problem if the ordering of all the time blocks was consistent. All but
189 one of the blocks were BSD44, V7, SYSUTIMEH, <OTHER>. That one case
190 is where this problem was triggered.
191 */
192 #define SYSUTIMEH /* Use this for both cases. */
193 #endif /* V7 */
194 #endif /* BSD44 */
195 #endif /* UTIMEH */
196 #endif /* SYSUTIMEH */
197
198 #ifndef NOTIMESTAMP
199 #ifdef POSIX
200 #ifndef AS400
201 #define TIMESTAMP
202 #endif /* AS400 */
203 #endif /* POSIX */
204
205 #ifdef BSD44 /* BSD 4.4 */
206 #ifndef TIMESTAMP
207 #define TIMESTAMP /* Can do file dates */
208 #endif /* TIMESTAMP */
209 #include <sys/time.h>
210 #include <sys/timeb.h>
211
212 #else /* Not BSD44 */
213
214 #ifdef BSD4 /* BSD 4.3 and below */
215 #define TIMESTAMP /* Can do file dates */
216 #include <time.h> /* Need this */
217 #include <sys/timeb.h> /* Need this if really BSD */
218
219 #else /* Not BSD 4.3 and below */
220
221 #ifdef SVORPOSIX /* System V or POSIX */
222 #ifndef TIMESTAMP
223 #define TIMESTAMP
224 #endif /* TIMESTAMP */
225 #include <time.h>
226
227 /* void tzset(); (the "void" type upsets some compilers) */
228 #ifndef IRIX60
229 #ifndef ultrix
230 #ifndef CONVEX9
231 /* ConvexOS 9.0, supposedly POSIX, has extern char *timezone(int,int) */
232 #ifndef Plan9
233 extern long timezone;
234 #endif /* Plan9 */
235 #endif /* CONVEX9 */
236 #endif /* ultrix */
237 #endif /* IRIX60 */
238 #endif /* SVORPOSIX */
239 #endif /* BSD4 */
240 #endif /* BSD44 */
241
242 #ifdef COHERENT
243 #include <time.h>
244 #endif /* COHERENT */
245
246 /* Is `y' a leap year? */
247 #define leap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)
248
249 /* Number of leap years from 1970 to `y' (not including `y' itself). */
250 #define nleap(y) (((y) - 1969) / 4 - ((y) - 1901) / 100 + ((y) - 1601) / 400)
251
252 #endif /* NOTIMESTAMP */
253
254 #ifdef CIE
255 #include <stat.h> /* File status */
256 #else
257 #include <sys/stat.h>
258 #endif /* CIE */
259
260 #ifdef __DragonFly__
261 #include <sys/wait.h> /* for wait() */
262 #endif
263
264 /* Macro to alleviate isdir() calls internal to this module */
265
266 static struct stat STATBUF;
267 #define xisdir(a) ((stat(a,&STATBUF)==-1)?0:(S_ISDIR(STATBUF.st_mode)?1:0))
268
269 extern char uidbuf[];
270 extern int xferlog;
271 extern char * xferfile;
272 int iklogopen = 0;
273 static time_t timenow;
274
275 #define IKSDMSGLEN CKMAXPATH+512
276
277 static char iksdmsg[IKSDMSGLEN];
278
279 extern int local;
280
281 extern int server, en_mkd, en_cwd, en_del;
282
283 /*
284 Functions (n is one of the predefined file numbers from ckcker.h):
285
286 zopeni(n,name) -- Opens an existing file for input.
287 zopeno(n,name,attr,fcb) -- Opens a new file for output.
288 zclose(n) -- Closes a file.
289 zchin(n,&c) -- Gets the next character from an input file.
290 zsinl(n,&s,x) -- Read a line from file n, max len x, into address s.
291 zsout(n,s) -- Write a null-terminated string to output file, buffered.
292 zsoutl(n,s) -- Like zsout, but appends a line terminator.
293 zsoutx(n,s,x) -- Write x characters to output file, unbuffered.
294 zchout(n,c) -- Add a character to an output file, unbuffered.
295 zchki(name) -- Check if named file exists and is readable, return size.
296 zchko(name) -- Check if named file can be created.
297 zchkspa(name,n) -- Check if n bytes available to create new file, name.
298 znewn(name,s) -- Make a new unique file name based on the given name.
299 zdelet(name) -- Delete the named file.
300 zxpand(string) -- Expands the given wildcard string into a list of files.
301 znext(string) -- Returns the next file from the list in "string".
302 zxrewind() -- Rewind zxpand list.
303 zxcmd(n,cmd) -- Execute the command in a lower fork on file number n.
304 zclosf() -- Close input file associated with zxcmd()'s lower fork.
305 zrtol(n1,n2) -- Convert remote filename into local form.
306 zltor(n1,n2) -- Convert local filename into remote form.
307 zchdir(dirnam) -- Change working directory.
308 zhome() -- Return pointer to home directory name string.
309 zkself() -- Kill self, log out own job.
310 zsattr(struct zattr *) -- Return attributes for file which is being sent.
311 zstime(f, struct zattr *, x) - Set file creation date from attribute packet.
312 zrename(old, new) -- Rename a file.
313 zcopy(source,destination) -- Copy a file.
314 zmkdir(path) -- Create the directory path if possible
315 zfnqfp(fname,len,fullpath) - Determine full path for file name.
316 zgetfs(name) -- return file size regardless of accessibility
317 zchkpid(pid) -- tell if PID is valid and active
318 */
319
320 /* Kermit-specific includes */
321 /*
322 Definitions here supersede those from system include files.
323 ckcdeb.h is included above.
324 */
325 #include "ckcker.h" /* Kermit definitions */
326 #include "ckucmd.h" /* For keyword tables */
327 #include "ckuver.h" /* Version herald */
328
329 char *ckzsys = HERALD;
330
331 /*
332 File access checking ... There are two calls to access() in this module.
333 If this program is installed setuid or setgid on a Berkeley-based UNIX
334 system that does NOT incorporate the saved-original-effective-uid/gid
335 feature, then, when we have swapped the effective and original uid/gid,
336 access() fails because it uses what it thinks are the REAL ids, but we have
337 swapped them. This occurs on systems where ANYBSD is defined, NOSETREU
338 is NOT defined, and SAVEDUID is NOT defined. So, in theory, we should take
339 care of this situation like so:
340
341 ifdef ANYBSD
342 ifndef NOSETREU
343 ifndef SAVEDUID
344 define SW_ACC_ID
345 endif
346 endif
347 endif
348
349 But we can't test such a general scheme everywhere, so let's only do this
350 when we know we have to...
351 */
352 #ifdef NEXT /* NeXTSTEP 1.0-3.0 */
353 #define SW_ACC_ID
354 #endif /* NEXT */
355
356 /* Support for tilde-expansion in file and directory names */
357
358 #ifdef POSIX
359 #define NAMEENV "LOGNAME"
360 #else
361 #ifdef BSD4
362 #define NAMEENV "USER"
363 #else
364 #ifdef ATTSV
365 #define NAMEENV "LOGNAME"
366 #endif /* ATTSV */
367 #endif /* BSD4 */
368 #endif /* POSIX */
369
370 /* Berkeley Unix Version 4.x */
371 /* 4.1bsd support from Charles E Brooks, EDN-VAX */
372
373 #ifdef BSD4
374 #ifdef MAXNAMLEN
375 #define BSD42
376 #endif /* MAXNAMLEN */
377 #endif /* BSD4 */
378
379 /* Definitions of some system commands */
380
381 char *DELCMD = "rm -f "; /* For file deletion */
382 char *CPYCMD = "cp "; /* For file copy */
383 char *RENCMD = "mv "; /* For file rename */
384 char *PWDCMD = "pwd "; /* For saying where I am */
385
386 #ifdef COMMENT
387 #ifdef HPUX10
388 char *DIRCMD = "/usr/bin/ls -l "; /* For directory listing */
389 char *DIRCM2 = "/usr/bin/ls -l "; /* For directory listing, no args */
390 #else
391 char *DIRCMD = "/bin/ls -l "; /* For directory listing */
392 char *DIRCM2 = "/bin/ls -l "; /* For directory listing, no args */
393 #endif /* HPUX10 */
394 #else
395 char *DIRCMD = "ls -l "; /* For directory listing */
396 char *DIRCM2 = "ls -l "; /* For directory listing, no args */
397 #endif /* COMMENT */
398
399 char *TYPCMD = "cat "; /* For typing a file */
400
401 #ifdef HPUX
402 char *MAILCMD = "mailx"; /* For sending mail */
403 #else
404 #ifdef DGUX540
405 char *MAILCMD = "mailx";
406 #else
407 #ifdef UNIX
408 #ifdef CK_MAILCMD
409 char *MAILCMD = CK_MAILCMD; /* CFLAGS override */
410 #else
411 char *MAILCMD = "Mail"; /* Default */
412 #endif /* CK_MAILCMD */
413 #else
414 char *MAILCMD = "";
415 #endif /* UNIX */
416 #endif /* HPUX */
417 #endif /* DGUX40 */
418
419 #ifdef UNIX
420 #ifdef ANYBSD /* BSD uses lpr to spool */
421 #ifdef DGUX540 /* And DG/UX */
422 char * PRINTCMD = "lp";
423 #else
424 char * PRINTCMD = "lpr";
425 #endif /* DGUX540 */
426 #else /* Sys V uses lp */
427 #ifdef TRS16 /* except for Tandy-16/6000... */
428 char * PRINTCMD = "lpr";
429 #else
430 char * PRINTCMD = "lp";
431 #endif /* TRS16 */
432 #endif /* ANYBSD */
433 #else /* Not UNIX */
434 #define PRINTCMD ""
435 #endif /* UNIX */
436
437 #ifdef FT18 /* Fortune For:Pro 1.8 */
438 #undef BSD4
439 #endif /* FT18 */
440
441 #ifdef BSD4
442 char *SPACMD = "pwd ; df ."; /* Space in current directory */
443 #else
444 #ifdef FT18
445 char *SPACMD = "pwd ; du ; df .";
446 #else
447 char *SPACMD = "df ";
448 #endif /* FT18 */
449 #endif /* BSD4 */
450
451 char *SPACM2 = "df "; /* For space in specified directory */
452
453 #ifdef FT18
454 #define BSD4
455 #endif /* FT18 */
456
457 #ifdef BSD4
458 char *WHOCMD = "finger ";
459 #else
460 char *WHOCMD = "who ";
461 #endif /* BSD4 */
462
463 /* More system-dependent includes, which depend on symbols defined */
464 /* in the Kermit-specific includes. Oh what a tangled web we weave... */
465
466 #ifdef COHERENT /* <sys/file.h> */
467 #define NOFILEH
468 #endif /* COHERENT */
469
470 #ifdef MINIX
471 #define NOFILEH
472 #endif /* MINIX */
473
474 #ifdef aegis
475 #define NOFILEH
476 #endif /* aegis */
477
478 #ifdef unos
479 #define NOFILEH
480 #endif /* unos */
481
482 #ifndef NOFILEH
483 #include <sys/file.h>
484 #endif /* NOFILEH */
485
486 #ifndef is68k /* Whether to include <fcntl.h> */
487 #ifndef BSD41 /* All but a couple UNIXes have it. */
488 #ifndef FT18
489 #ifndef COHERENT
490 #include <fcntl.h>
491 #endif /* COHERENT */
492 #endif /* FT18 */
493 #endif /* BSD41 */
494 #endif /* is68k */
495
496 #ifdef COHERENT
497 #ifdef _I386
498 #include <fcntl.h>
499 #else
500 #include <sys/fcntl.h>
501 #endif /* _I386 */
502 #endif /* COHERENT */
503
504 extern int inserver; /* I am IKSD */
505 int guest = 0; /* Anonymous user */
506
507 #ifdef IKSD
508 extern int isguest;
509 extern char * anonroot;
510 #endif /* IKSD */
511
512 #ifdef CK_LOGIN
513 #define GUESTPASS 256
514 static char guestpass[GUESTPASS] = { NUL, NUL }; /* Anonymous "password" */
515 static int logged_in = 0; /* Set when user is logged in */
516 static int askpasswd = 0; /* Have OK user, must ask for passwd */
517 #ifdef CK_PAM
518 extern int gotemptypasswd;
519 #endif /* CK_PAM */
520 #endif /* CK_LOGIN */
521
522 #ifdef CKROOT
523 static char ckroot[CKMAXPATH+1] = { NUL, NUL };
524 static int ckrootset = 0;
525 int ckrooterr = 0;
526 #endif /* CKROOT */
527
528 _PROTOTYP( VOID ignorsigs, (void) );
529 _PROTOTYP( VOID restorsigs, (void) );
530 #ifdef SELECT
531 _PROTOTYP( int ttwait, (int, int) ); /* ckutio.c */
532 #endif /* SELECT */
533
534 /*
535 Change argument to "(const char *)" if this causes trouble.
536 Or... if it causes trouble, then maybe it was already declared
537 in a header file after all, so you can remove this prototype.
538 */
539 #ifndef NDGPWNAM /* If not defined No Declare getpwnam... */
540 #ifndef _POSIX_SOURCE
541 #ifndef NEXT
542 #ifndef SVR4
543 /* POSIX <pwd.h> already gave prototypes for these. */
544 #ifdef IRIX40
545 _PROTOTYP( struct passwd * getpwnam, (const char *) );
546 #else
547 #ifdef IRIX51
548 _PROTOTYP( struct passwd * getpwnam, (const char *) );
549 #else
550 #ifdef M_UNIX
551 _PROTOTYP( struct passwd * getpwnam, (const char *) );
552 #else
553 #ifdef HPUX9
554 _PROTOTYP( struct passwd * getpwnam, (const char *) );
555 #else
556 #ifdef HPUX10
557 _PROTOTYP( struct passwd * getpwnam, (const char *) );
558 #else
559 #ifdef DCGPWNAM
560 _PROTOTYP( struct passwd * getpwnam, (const char *) );
561 #else
562 _PROTOTYP( struct passwd * getpwnam, (char *) );
563 #endif /* DCGPWNAM */
564 #endif /* HPUX10 */
565 #endif /* HPUX9 */
566 #endif /* M_UNIX */
567 #endif /* IRIX51 */
568 #endif /* IRIX40 */
569 #ifndef SUNOS4
570 #ifndef HPUX9
571 #ifndef HPUX10
572 #ifndef _SCO_DS
573 _PROTOTYP( struct passwd * getpwuid, (PWID_T) );
574 #endif /* _SCO_DS */
575 #endif /* HPUX10 */
576 #endif /* HPUX9 */
577 #endif /* SUNOS4 */
578 _PROTOTYP( struct passwd * getpwent, (void) );
579 #endif /* SVR4 */
580 #endif /* NEXT */
581 #endif /* _POSIX_SOURCE */
582 #endif /* NDGPWNAM */
583
584 #ifdef CK_SHADOW /* Shadow Passwords... */
585 #include <shadow.h>
586 #endif /* CK_SHADOW */
587 #ifdef CK_PAM /* PAM... */
588 #ifdef MACOSX
589 #include <pam/pam_appl.h>
590 #else /* MACOSX */
591 #include <security/pam_appl.h>
592 #endif /* MACOSX */
593 #ifndef PAM_SERVICE_TYPE /* Defines which PAM service we are */
594 #define PAM_SERVICE_TYPE "kermit"
595 #endif /* PAM_SERVICE_TYPE */
596
597 #ifdef SOLARIS
598 #define PAM_CONST
599 #else /* SOLARIS */
600 #define PAM_CONST CONST
601 #endif
602
603 static char * pam_pw = NULL;
604
605 int
606 #ifdef CK_ANSIC
pam_cb(int num_msg,PAM_CONST struct pam_message ** msg,struct pam_response ** resp,void * appdata_ptr)607 pam_cb(int num_msg,
608 PAM_CONST struct pam_message **msg,
609 struct pam_response **resp,
610 void *appdata_ptr
611 )
612 #else /* CK_ANSIC */
613 pam_cb(num_msg, msg, resp, appdata_ptr)
614 int num_msg;
615 PAM_CONST struct pam_message **msg;
616 struct pam_response **resp;
617 void *appdata_ptr;
618 #endif /* CK_ANSIC */
619 {
620 int i;
621
622 debug(F111,"pam_cb","num_msg",num_msg);
623
624 for (i = 0; i < num_msg; i++) {
625 char message[PAM_MAX_MSG_SIZE];
626
627 /* Issue prompt and get response */
628 debug(F111,"pam_cb","Message",i);
629 debug(F111,"pam_cb",msg[i]->msg,msg[i]->msg_style);
630 if (msg[i]->msg_style == PAM_ERROR_MSG) {
631 debug(F111,"pam_cb","PAM ERROR",0);
632 fprintf(stdout,"%s\n", msg[i]->msg);
633 return(0);
634 } else if (msg[i]->msg_style == PAM_TEXT_INFO) {
635 debug(F111,"pam_cb","PAM TEXT INFO",0);
636 fprintf(stdout,"%s\n", msg[i]->msg);
637 return(0);
638 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
639 debug(F111,"pam_cb","Reading response, no echo",0);
640 /* Ugly hack. We check to see if a password has been pushed */
641 /* into zvpasswd(). This would be true if the password was */
642 /* received by REMOTE LOGIN. */
643 if (pam_pw) {
644 ckstrncpy(message,pam_pw,PAM_MAX_MSG_SIZE);
645 } else
646 readpass((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
647 } else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
648 debug(F111,"pam_cb","Reading response, with echo",0);
649 readtext((char *)msg[i]->msg,message,PAM_MAX_MSG_SIZE);
650 } else {
651 debug(F111,"pam_cb","unknown style",0);
652 return(0);
653 }
654
655 /* Allocate space for this message's response structure */
656 resp[i] = (struct pam_response *) malloc(sizeof (struct pam_response));
657 if (!resp[i]) {
658 int j;
659 debug(F110,"pam_cb","malloc failure",0);
660 for (j = 0; j < i; j++) {
661 free(resp[j]->resp);
662 free(resp[j]);
663 }
664 return(0);
665 }
666
667 /* Allocate a buffer for the response */
668 resp[i]->resp = (char *) malloc((int)strlen(message) + 1);
669 if (!resp[i]->resp) {
670 int j;
671 debug(F110,"pam_cb","malloc failure",0);
672 for (j = 0; j < i; j++) {
673 free(resp[j]->resp);
674 free(resp[j]);
675 }
676 free(resp[i]);
677 return(0);
678 }
679 /* Return the results back to PAM */
680 strcpy(resp[i]->resp, message); /* safe (prechecked) */
681 resp[i]->resp_retcode = 0;
682 }
683 debug(F110,"pam_cb","Exiting",0);
684 return(0);
685 }
686 #endif /* CK_PAM */
687
688 /* Define macros for getting file type */
689
690 #ifdef OXOS
691 /*
692 Olivetti X/OS 2.3 has S_ISREG and S_ISDIR defined
693 incorrectly, so we force their redefinition.
694 */
695 #undef S_ISREG
696 #undef S_ISDIR
697 #endif /* OXOS */
698
699 #ifdef UTSV /* Same deal for Amdahl UTSV */
700 #undef S_ISREG
701 #undef S_ISDIR
702 #endif /* UTSV */
703
704 #ifdef UNISYS52 /* And for UNISYS UTS V 5.2 */
705 #undef S_ISREG
706 #undef S_ISDIR
707 #endif /* UNISYS52 */
708
709 #ifdef ICLSVR3 /* And for old ICL versions */
710 #undef S_ISREG
711 #undef S_ISDIR
712 #endif /* ICLSVR3 */
713
714 #ifdef ISDIRBUG /* Also allow this from command line */
715 #ifdef S_ISREG
716 #undef S_ISREG
717 #endif /* S_ISREG */
718 #ifdef S_ISDIR
719 #undef S_ISDIR
720 #endif /* S_ISDIR */
721 #endif /* ISDIRBUG */
722
723 #ifndef _IFMT
724 #ifdef S_IFMT
725 #define _IFMT S_IFMT
726 #else
727 #define _IFMT 0170000
728 #endif /* S_IFMT */
729 #endif /* _IFMT */
730
731 #ifndef S_ISREG
732 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
733 #endif /* S_ISREG */
734 #ifndef S_ISDIR
735 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
736 #endif /* S_ISDIR */
737
738 /* The following mainly for NeXTSTEP... */
739
740 #ifndef S_IWUSR
741 #define S_IWUSR 0000200
742 #endif /* S_IWUSR */
743
744 #ifndef S_IRGRP
745 #define S_IRGRP 0000040
746 #endif /* S_IRGRP */
747
748 #ifndef S_IWGRP
749 #define S_IWGRP 0000020
750 #endif /* S_IWGRP */
751
752 #ifndef S_IXGRP
753 #define S_IXGRP 0000010
754 #endif /* S_IXGRP */
755
756 #ifndef S_IROTH
757 #define S_IROTH 0000004
758 #endif /* S_IROTH */
759
760 #ifndef S_IWOTH
761 #define S_IWOTH 0000002
762 #endif /* S_IWOTH */
763
764 #ifndef S_IXOTH
765 #define S_IXOTH 0000001
766 #endif /* S_IXOTH */
767 /*
768 Define maximum length for a file name if not already defined.
769 NOTE: This applies to a path segment (directory or file name),
770 not the entire path string, which can be CKMAXPATH bytes long.
771 */
772 #ifdef QNX
773 #ifdef _MAX_FNAME
774 #define MAXNAMLEN _MAX_FNAME
775 #else
776 #define MAXNAMLEN 48
777 #endif /* _MAX_FNAME */
778 #else
779 #ifndef MAXNAMLEN
780 #ifdef sun
781 #define MAXNAMLEN 255
782 #else
783 #ifdef FILENAME_MAX
784 #define MAXNAMLEN FILENAME_MAX
785 #else
786 #ifdef NAME_MAX
787 #define MAXNAMLEN NAME_MAX
788 #else
789 #ifdef _POSIX_NAME_MAX
790 #define MAXNAMLEN _POSIX_NAME_MAX
791 #else
792 #ifdef _D_NAME_MAX
793 #define MAXNAMLEN _D_NAME_MAX
794 #else
795 #ifdef DIRSIZ
796 #define MAXNAMLEN DIRSIZ
797 #else
798 #define MAXNAMLEN 14
799 #endif /* DIRSIZ */
800 #endif /* _D_NAME_MAX */
801 #endif /* _POSIX_NAME_MAX */
802 #endif /* NAME_MAX */
803 #endif /* FILENAME_MAX */
804 #endif /* sun */
805 #endif /* MAXNAMLEN */
806 #endif /* QNX */
807
808 #ifdef COMMENT
809 /* As of 2001-11-03 this is handled in ckcdeb.h */
810 /* Longest pathname ... */
811 /*
812 Beware: MAXPATHLEN is one of UNIX's dirty little secrets. Where is it
813 defined? Who knows... <param.h>, <mod.h>, <unistd.h>, <limits.h>, ...
814 There is not necessarily even a definition for it anywhere, or it might have
815 another name. If you get it wrong, bad things happen with getcwd() and/or
816 getwd(). If you allocate a buffer that is too short, getwd() might write
817 over memory and getcwd() will fail with ERANGE. The definitions of these
818 functions (e.g. in SVID or POSIX.1) do not tell you how to determine the
819 maximum path length in order to allocate a buffer that is the right size.
820 */
821 #ifdef BSD44
822 #include <sys/param.h> /* For MAXPATHLEN */
823 #endif /* BSD44 */
824 #ifdef COHERENT
825 #include <sys/param.h> /* for MAXPATHLEN, needed for -DDIRENT */
826 #endif /* COHERENT */
827 #endif /* COMMENT */
828
829 #ifdef MAXPATHLEN
830 #ifdef MAXPATH
831 #undef MAXPATH
832 #endif /* MAXPATH */
833 #define MAXPATH MAXPATHLEN
834 #else
835 #ifdef PATH_MAX
836 #define MAXPATH PATH_MAX
837 #else
838 #ifdef _POSIX_PATH_MAX
839 #define MAXPATH _POSIX_PATH_MAX
840 #else
841 #ifdef BSD42
842 #define MAXPATH 1024
843 #else
844 #ifdef SVR4
845 #define MAXPATH 1024
846 #else
847 #define MAXPATH 255
848 #endif /* SVR4 */
849 #endif /* BSD42 */
850 #endif /* _POSIX_PATH_MAX */
851 #endif /* PATH_MAX */
852 #endif /* MAXPATHLEN */
853
854 /* Maximum number of filenames for wildcard expansion */
855
856 #ifndef MAXWLD
857 /* Already defined in ckcdeb.h so the following is superfluous. */
858 /* Don't expect changing them to have any effect. */
859 #ifdef CK_SMALL
860 #define MAXWLD 50
861 #else
862 #ifdef BIGBUFOK
863 #define MAXWLD 102400
864 #else
865 #define MAXWLD 8192
866 #endif /* BIGBUFOK */
867 #endif /* CK_SMALL */
868 #endif /* MAXWLD */
869
870 static int maxnames = MAXWLD;
871
872 /* Define the size of the string space for filename expansion. */
873
874 #ifndef DYNAMIC
875 #ifdef PROVX1
876 #define SSPACE 500
877 #else
878 #ifdef BSD29
879 #define SSPACE 500
880 #else
881 #ifdef pdp11
882 #define SSPACE 500
883 #else
884 #ifdef aegis
885 #define SSPACE 10000 /* Size of string-generating buffer */
886 #else /* Default static buffer size */
887 #ifdef BIGBUFOK
888 #define SSPACE 65000 /* Size of string-generating buffer */
889 #else
890 #define SSPACE 2000 /* size of string-generating buffer */
891 #endif /* BIGBUFOK */
892 #endif /* aegis */
893 #endif /* pdp11 */
894 #endif /* BSD29 */
895 #endif /* PROVX1 */
896 static char sspace[SSPACE]; /* Buffer for generating filenames */
897 #else /* is DYNAMIC */
898 #ifdef CK_64BIT
899 #define SSPACE 2000000000 /* Two billion bytes */
900 #else
901 #ifdef BIGBUFOK
902 #define SSPACE 10000000 /* Ten million */
903 #else
904 #define SSPACE 10000 /* Ten thousand */
905 #endif /* BIGBUFOK */
906 #endif /* CK_64BIT */
907 char *sspace = (char *)0;
908 #endif /* DYNAMIC */
909 static int ssplen = SSPACE; /* Length of string space buffer */
910
911 #ifdef DCLFDOPEN
912 /* fdopen() needs declaring because it's not declared in <stdio.h> */
913 _PROTOTYP( FILE * fdopen, (int, char *) );
914 #endif /* DCLFDOPEN */
915
916 #ifdef DCLPOPEN
917 /* popen() needs declaring because it's not declared in <stdio.h> */
918 _PROTOTYP( FILE * popen, (char *, char *) );
919 #endif /* DCLPOPEN */
920
921 extern int nopush;
922
923 /* More internal function prototypes */
924 /*
925 * The path structure is used to represent the name to match.
926 * Each slash-separated segment of the name is kept in one
927 * such structure, and they are linked together, to make
928 * traversing the name easier.
929 */
930 struct path {
931 char npart[MAXNAMLEN+4]; /* name part of path segment */
932 struct path *fwd; /* forward ptr */
933 };
934 #ifndef NOPUSH
935 _PROTOTYP( int shxpand, (char *, char *[], int ) );
936 #endif /* NOPUSH */
937 _PROTOTYP( static int fgen, (char *, char *[], int ) );
938 _PROTOTYP( static VOID traverse, (struct path *, char *, char *) );
939 _PROTOTYP( static VOID addresult, (char *, int) );
940 #ifdef COMMENT
941 /* Replaced by ckmatch() */
942 _PROTOTYP( static int match, (char *, char *) );
943 #endif /* COMMENT */
944 _PROTOTYP( char * whoami, (void) );
945 _PROTOTYP( UID_T real_uid, (void) );
946 _PROTOTYP( static struct path *splitpath, (char *p) );
947 _PROTOTYP( char * zdtstr, (time_t) );
948 _PROTOTYP( time_t zstrdt, (char *, int) );
949
950 /* Some systems define these symbols in include files, others don't... */
951
952 #ifndef R_OK
953 #define R_OK 4 /* For access */
954 #endif /* R_OK */
955
956 #ifndef W_OK
957 #define W_OK 2
958 #endif /* W_OK */
959
960 #ifndef X_OK
961 #define X_OK 1
962 #endif /* X_OK */
963
964 #ifndef O_RDONLY
965 #define O_RDONLY 000
966 #endif /* O_RDONLY */
967
968 /* syslog and wtmp items for Internet Kermit Service */
969
970 extern char * clienthost; /* From ckcmai.c. */
971
972 static char fullname[CKMAXPATH+1];
973 static char tmp2[CKMAXPATH+1];
974
975 extern int ckxlogging;
976
977 #ifdef CKXPRINTF /* Our printf macro conflicts with */
978 #undef printf /* use of "printf" in syslog.h */
979 #endif /* CKXPRINTF */
980 #ifdef CKSYSLOG
981 #ifdef RTAIX
982 #include <sys/syslog.h>
983 #else /* RTAIX */
984 #include <syslog.h>
985 #endif /* RTAIX */
986 #endif /* CKSYSLOG */
987 #ifdef CKXPRINTF
988 #define printf ckxprintf
989 #endif /* CKXPRINTF */
990
991 int ckxanon = 1; /* Anonymous login ok */
992 int ckxperms = 0040; /* Anonymous file permissions */
993 int ckxpriv = 1; /* Priv'd login ok */
994
995 #ifndef XFERFILE
996 #define XFERFILE "/var/log/iksd.log"
997 #endif /* XFERFILE */
998
999 /* wtmp logging for IKSD... */
1000
1001 #ifndef CKWTMP /* wtmp logging not selected */
1002 int ckxwtmp = 0; /* Know this at runtime */
1003 #else /* wtmp file details */
1004 int ckxwtmp = 1;
1005 #ifdef UTMPBUG /* Unfortunately... */
1006 /*
1007 Some versions of Linux have a <utmp.h> file that contains
1008 "enum utlogin { local, telnet, rlogin, screen, ... };" This clobbers
1009 any program that uses any of these words as variable names, function
1010 names, macro names, etc. (Other versions of Linux have this declaration
1011 within #if 0 ... #endif.) There is nothing we can do about this other
1012 than to not include the stupid file. But we need stuff from it, so...
1013 */
1014 #include <features.h>
1015 #include <sys/types.h>
1016 #define UT_LINESIZE 32
1017 #define UT_NAMESIZE 32
1018 #define UT_HOSTSIZE 256
1019
1020 struct timeval {
1021 time_t tv_sec;
1022 time_t tv_usec;
1023 };
1024
1025 struct exit_status {
1026 short int e_termination; /* Process termination status. */
1027 short int e_exit; /* Process exit status. */
1028 };
1029
1030 struct utmp {
1031 short int ut_type; /* Type of login */
1032 pid_t ut_pid; /* Pid of login process */
1033 char ut_line[UT_LINESIZE]; /* NUL-terminated devicename of tty */
1034 char ut_id[4]; /* Inittab id */
1035 char ut_user[UT_NAMESIZE]; /* Username (not NUL terminated) */
1036
1037 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login */
1038 struct exit_status ut_exit; /* Exit status */
1039 long ut_session; /* Session ID, used for windowing */
1040 struct timeval ut_tv; /* Time entry was made */
1041 int32_t ut_addr_v6[4]; /* Internet address of remote host */
1042 char pad[20]; /* Reserved */
1043 };
1044
1045 #define ut_time ut_tv.tv_sec /* Why should Linux be like anything else? */
1046 #define ut_name ut_user /* ... */
1047
1048 extern void
1049 logwtmp __P ((__const char *__ut_line, __const char *__ut_name,
1050 __const char *__ut_host));
1051
1052 #else /* Not UTMPBUG */
1053
1054 #ifndef HAVEUTMPX /* Who has <utmpx.h> */
1055 #ifdef SOLARIS
1056 #define HAVEUTMPX
1057 #else
1058 #ifdef IRIX60
1059 #define HAVEUTMPX
1060 #else
1061 #ifdef CK_SCOV5
1062 #define HAVEUTMPX
1063 #else
1064 #ifdef HPUX100
1065 #define HAVEUTMPX
1066 #else
1067 #ifdef UNIXWARE
1068 #define HAVEUTMPX
1069 #endif /* UNIXWARE */
1070 #endif /* HPUX100 */
1071 #endif /* CK_SCOV5 */
1072 #endif /* IRIX60 */
1073 #endif /* SOLARIS */
1074 #endif /* HAVEUTMPX */
1075
1076 #ifdef HAVEUTMPX
1077 #include <utmpx.h>
1078 #else
1079 #ifdef OSF50
1080 /* Because the time_t in the utmp struct is 64 bits but time() wants 32 */
1081 #define __V40_OBJ_COMPAT 1
1082 #endif /* OSF50 */
1083 #include <utmp.h>
1084 #ifdef OSF50
1085 #undef __V40_OBJ_COMPAT
1086 #endif /* OSF50 */
1087 #endif /* HAVEUTMPX */
1088 #endif /* UTMPBUG */
1089
1090 #ifdef HAVEUTMPX
1091 #define UTMPSTRUCT utmpx
1092 #else
1093 #define UTMPSTRUCT utmp
1094 #endif /* HAVEUTMPX */
1095
1096 #ifndef WTMPFILE
1097 #ifdef QNX
1098 #define WTMPFILE "/usr/adm/wtmp.1"
1099 #else
1100 #ifdef LINUX
1101 #define WTMPFILE "/var/log/wtmp"
1102 #else
1103 #define WTMPFILE "/usr/adm/wtmp"
1104 #endif /* QNX */
1105 #endif /* LINUX */
1106 #endif /* WTMPFILE */
1107 char * wtmpfile = NULL;
1108
1109 static int wtmpfd = 0;
1110 static char cksysline[32] = { NUL, NUL };
1111
1112 #ifndef HAVEUTHOST /* Does utmp include ut_host[]? */
1113 #ifdef HAVEUTMPX /* utmpx always does */
1114 #define HAVEUTHOST
1115 #else
1116 #ifdef LINUX /* Linux does */
1117 #define HAVEUTHOST
1118 #else
1119 #ifdef SUNOS4 /* SunOS does */
1120 #define HAVEUTHOST
1121 #else
1122 #ifdef AIX41 /* AIX 4.1 and later do */
1123 #define HAVEUTHOST
1124 #endif /* AIX41 */
1125 #endif /* SUNOS4 */
1126 #endif /* LINUX */
1127 #endif /* HAVEUTMPX */
1128 #endif /* HAVEUTHOST */
1129
1130 #ifdef UW200
_vfork()1131 PID_T _vfork() { /* To satisfy a library foulup */
1132 return(fork()); /* in Unixware 2.0.x */
1133 }
1134 #endif /* UW200 */
1135
1136 VOID
1137 #ifdef CK_ANSIC
logwtmp(const char * line,const char * name,const char * host)1138 logwtmp(const char * line, const char * name, const char * host)
1139 #else
1140 logwtmp(line, name, host) char *line, *name, *host;
1141 #endif /* CK_ANSIC */
1142 /* logwtmp */ {
1143 struct UTMPSTRUCT ut;
1144 struct stat buf;
1145 int dummy;
1146 /* time_t time(); */
1147
1148 if (!ckxwtmp)
1149 return;
1150
1151 if (!wtmpfile)
1152 makestr(&wtmpfile,WTMPFILE);
1153
1154 if (!line) line = "";
1155 if (!name) name = "";
1156 if (!host) host = "";
1157
1158 if (!wtmpfd && (wtmpfd = open(wtmpfile, O_WRONLY|O_APPEND, 0)) < 0) {
1159 ckxwtmp = 0;
1160 debug(F110,"WTMP open failed",line,0);
1161 return;
1162 }
1163 if (!fstat(wtmpfd, &buf)) {
1164 ckstrncpy(ut.ut_line, line, sizeof(ut.ut_line));
1165 #ifdef FREEBSD9
1166 ckstrncpy(ut.ut_user, name, sizeof(ut.ut_user));
1167 #else
1168 ckstrncpy(ut.ut_name, name, sizeof(ut.ut_name));
1169 #endif /* FREEBSD9 */
1170 #ifdef HAVEUTHOST
1171 /* Not portable */
1172 ckstrncpy(ut.ut_host, host, sizeof(ut.ut_host));
1173 #endif /* HAVEUTHOST */
1174 #ifdef HAVEUTMPX
1175 time(&ut.ut_tv.tv_sec);
1176 #else
1177 #ifdef LINUX
1178 /* In light of the following comment perhaps the previous line should */
1179 /* be "#ifndef COMMENT". */
1180 {
1181 /*
1182 * On 64-bit platforms sizeof(time_t) and sizeof(ut.ut_time)
1183 * are not the same and attempt to use an address of
1184 * ut.ut_time as an argument to time() call may cause
1185 * "unaligned access" trap.
1186 */
1187 time_t zz;
1188 time(&zz);
1189 ut.ut_time = zz;
1190 }
1191 #else
1192 #ifdef CK_64BIT
1193 {
1194 /* Now (Jan 2006) we can do this for any 64-bit build */
1195 time_t zz;
1196 time(&zz);
1197 ut.ut_time = zz;
1198 }
1199 #else
1200 time(&ut.ut_time);
1201 #endif /* CK_64BIT */
1202 #endif /* LINUX */
1203 #endif /* HAVEUTMPX */
1204 if (write(wtmpfd, (char *)&ut, sizeof(struct UTMPSTRUCT)) !=
1205 sizeof(struct UTMPSTRUCT)) {
1206 #ifndef NOFTRUNCATE
1207 #ifndef COHERENT
1208 dummy =
1209 ftruncate(wtmpfd, buf.st_size); /* Error, undo partial write */
1210 #else
1211 chsize(wtmpfd, buf.st_size); /* Error, undo any partial write */
1212 #endif /* COHERENT */
1213 #endif /* NOFTRUNCATE */
1214 debug(F110,"WTMP write error",line,0);
1215 } else {
1216 debug(F110,"WTMP record OK",line,0);
1217 return;
1218 }
1219 }
1220 }
1221 #endif /* CKWTMP */
1222
1223 #ifdef CKSYSLOG
1224 /*
1225 C K S Y S L O G -- C-Kermit system logging function,
1226
1227 For use by other modules.
1228 This module can, but doesn't have to, use it.
1229 Call with:
1230 n = SYSLG_xx values defined in ckcdeb.h
1231 s1, s2, s3: strings.
1232 */
1233 VOID
cksyslog(n,m,s1,s2,s3)1234 cksyslog(n, m, s1, s2, s3) int n, m; char * s1, * s2, * s3; {
1235 int level;
1236
1237 if (!ckxlogging) /* syslogging */
1238 return;
1239 if (!s1) s1 = ""; /* Fix null args */
1240 if (!s2) s2 = "";
1241 if (!s3) s3 = "";
1242 switch (n) { /* Translate Kermit level */
1243 case SYSLG_DB: /* to syslog level */
1244 level = LOG_DEBUG;
1245 break;
1246 default:
1247 level = m ? LOG_INFO : LOG_ERR;
1248 }
1249 debug(F110,"cksyslog s1",s1,0);
1250 debug(F110,"cksyslog s2",s2,0);
1251 debug(F110,"cksyslog s3",s3,0);
1252 errno = 0;
1253 syslog(level, "%s: %s %s", s1, s2, s3); /* Write syslog record */
1254 debug(F101,"cksyslog errno","",errno);
1255 }
1256 #endif /* CKSYSLOG */
1257
1258
1259 /* Declarations */
1260
1261 int maxnam = MAXNAMLEN; /* Available to the outside */
1262 int maxpath = MAXPATH;
1263 int ck_znewn = -1;
1264
1265 #ifdef UNIX
1266 char startupdir[MAXPATH+1];
1267 #endif /* UNIX */
1268
1269 int pexitstat = -2; /* Process exit status */
1270
1271 FILE *fp[ZNFILS] = { /* File pointers */
1272 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
1273 };
1274
1275 /* Flags for each file indicating whether it was opened with popen() */
1276 int ispipe[ZNFILS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1277
1278 /* Buffers and pointers used in buffered file input and output. */
1279 #ifdef DYNAMIC
1280 extern char *zinbuffer, *zoutbuffer;
1281 #else
1282 extern char zinbuffer[], zoutbuffer[];
1283 #endif /* DYNAMIC */
1284 extern char *zinptr, *zoutptr;
1285 extern int zincnt, zoutcnt;
1286 extern int wildxpand, wildena; /* Wildcard handling */
1287
1288 static CK_OFF_T iflen = (CK_OFF_T)-1; /* Input file length */
1289
1290 static PID_T pid = 0; /* pid of child fork */
1291 static int fcount = 0; /* Number of files in wild group */
1292 static int nxpand = 0; /* Copy of fcount */
1293 static char nambuf[CKMAXPATH+4]; /* Buffer for a pathname */
1294
1295 #ifndef NOFRILLS
1296 #define ZMBUFLEN 200
1297 static char zmbuf[ZMBUFLEN]; /* For mail, remote print strings */
1298 #endif /* NOFRILLS */
1299
1300 char **mtchs = NULL; /* Matches found for filename */
1301 char **mtchptr = NULL; /* Pointer to current match */
1302
1303 /* Z K S E L F -- Kill Self: log out own job, if possible. */
1304
1305 /* Note, should get current pid, but if your system doesn't have */
1306 /* getppid(), then just kill(0,9)... */
1307
1308 #ifndef SVR3
1309 #ifndef POSIX
1310 #ifndef OSFPC
1311 /* Already declared in unistd.h for SVR3 and POSIX */
1312 #ifdef CK_ANSIC
1313 extern PID_T getppid(void);
1314 #else
1315 #ifndef PS2AIX10
1316 #ifndef COHERENT
1317 extern PID_T getppid();
1318 #endif /* COHERENT */
1319 #endif /* PS2AIX10 */
1320 #endif /* CK_ANSIC */
1321 #endif /* OSFPC */
1322 #endif /* POSIX */
1323 #endif /* SVR3 */
1324
1325 int
zkself()1326 zkself() { /* For "bye", but no guarantee! */
1327 #ifdef PROVX1
1328 return(kill(0,9));
1329 #else
1330 #ifdef V7
1331 return(kill(0,9));
1332 #else
1333 #ifdef TOWER1
1334 return(kill(0,9));
1335 #else
1336 #ifdef FT18
1337 return(kill(0,9));
1338 #else
1339 #ifdef aegis
1340 return(kill(0,9));
1341 #else
1342 #ifdef COHERENT
1343 return(kill((PID_T)getpid(),1));
1344 #else
1345 #ifdef PID_T
1346 exit(kill((PID_T)getppid(),1));
1347 return(0);
1348 #else
1349 exit(kill(getppid(),1));
1350 return(0);
1351 #endif
1352 #endif
1353 #endif
1354 #endif
1355 #endif
1356 #endif
1357 #endif
1358 }
1359
1360 static VOID
getfullname(name)1361 getfullname(name) char * name; {
1362 char *p = (char *)fullname;
1363 int len = 0;
1364 fullname[0] = '\0';
1365 /* If necessary we could also chase down symlinks here... */
1366 #ifdef COMMENT
1367 /* This works but is incompatible with wuftpd */
1368 if (isguest && anonroot) {
1369 ckstrncpy(fullname,anonroot,CKMAXPATH);
1370 len = strlen(fullname);
1371 if (len > 0)
1372 if (fullname[len-1] == '/')
1373 len--;
1374 }
1375 p += len;
1376 #endif /* COMMENT */
1377 zfnqfp(name, CKMAXPATH - len, p);
1378 while (*p) {
1379 if (*p < '!') *p = '_';
1380 p++;
1381 }
1382 }
1383
1384 /* D O I K L O G -- Open Kermit-specific ftp-like transfer log. */
1385
1386 VOID /* Called in ckcmai.c */
doiklog()1387 doiklog() {
1388 if (iklogopen) /* Already open? */
1389 return;
1390 if (xferlog) { /* Open iksd log if requested */
1391 if (!xferfile) /* If no pathname given */
1392 makestr(&xferfile,XFERFILE); /* use this default */
1393 if (*xferfile) {
1394 xferlog = open(xferfile, O_WRONLY | O_APPEND | O_CREAT, 0660);
1395 debug(F101,"doiklog open","",xferlog);
1396 if (xferlog < 0) {
1397 #ifdef CKSYSLOG
1398 syslog(LOG_ERR, "xferlog open failure %s: %m", xferfile);
1399 #endif /* CKSYSLOG */
1400 debug(F101,"doiklog open errno","",errno);
1401 xferlog = 0;
1402 } else
1403 iklogopen = 1;
1404 } else
1405 xferlog = 0;
1406 #ifdef CKSYSLOG
1407 if (xferlog && ckxlogging)
1408 syslog(LOG_INFO, "xferlog: %s open ok", xferfile);
1409 #endif /* CKSYSLOG */
1410 }
1411 }
1412
1413 /* Z O P E N I -- Open an existing file for input. */
1414
1415 /* Returns 1 on success, 0 on failure */
1416
1417 int
zopeni(n,name)1418 zopeni(n,name) int n; char *name; {
1419 int x;
1420
1421 debug(F111,"zopeni",name,n);
1422 if ((x = chkfn(n)) != 0) {
1423 debug(F111,"zopeni chkfn",ckitoa(n),x);
1424 return(0);
1425 }
1426 zincnt = 0; /* Reset input buffer */
1427 if (n == ZSYSFN) { /* Input from a system function? */
1428 #ifdef COMMENT
1429 /*** Note, this function should not be called with ZSYSFN ***/
1430 /*** Always call zxcmd() directly, and give it the real file number ***/
1431 /*** you want to use. ***/
1432 return(zxcmd(n,name)); /* Try to fork the command */
1433 #else
1434 debug(F110,"zopeni called with ZSYSFN, failing!",name,0);
1435 *nambuf = '\0'; /* No filename. */
1436 return(0); /* fail. */
1437 #endif /* COMMENT */
1438 }
1439 if (n == ZSTDIO) { /* Standard input? */
1440 if (is_a_tty(0)) {
1441 fprintf(stderr,"Terminal input not allowed");
1442 debug(F110,"zopeni: attempts input from unredirected stdin","",0);
1443 return(0);
1444 }
1445 fp[ZIFILE] = stdin;
1446 ispipe[ZIFILE] = 0;
1447 return(1);
1448 }
1449 #ifdef CKROOT
1450 debug(F111,"zopeni setroot",ckroot,ckrootset);
1451 if (ckrootset) if (!zinroot(name)) {
1452 debug(F110,"zopeni setroot violation",name,0);
1453 return(0);
1454 }
1455 #endif /* CKROOT */
1456 fp[n] = fopen(name,"r"); /* Real file, open it. */
1457 /* debug(F111,"zopeni fopen", name, fp[n]); */
1458 #ifdef ZDEBUG
1459 /* printf("ZOPENI fp[%d]=%ld\n",n,fp[n]); */
1460 #endif /* ZDEBUG */
1461 ispipe[n] = 0;
1462
1463 if (xferlog
1464 #ifdef CKSYSLOG
1465 || ((ckxsyslog >= SYSLG_FA) && ckxlogging)
1466 #endif /* CKSYSLOG */
1467 ) {
1468 getfullname(name);
1469 debug(F110,"zopeni fullname",fullname,0);
1470 }
1471 if (fp[n] == NULL) {
1472 #ifdef CKSYSLOG
1473 if (ckxsyslog >= SYSLG_FA && ckxlogging) {
1474 syslog(LOG_INFO, "file[%d] %s: open failed (%m)", n, fullname);
1475 perror(fullname);
1476 } else
1477 #endif /* CKSYSLOG */
1478 perror(name);
1479 return(0);
1480 } else {
1481 #ifdef CKSYSLOG
1482 if (ckxsyslog >= SYSLG_FA && ckxlogging)
1483 syslog(LOG_INFO, "file[%d] %s: open read ok", n, fullname);
1484 #endif /* CKSYSLOG */
1485 clearerr(fp[n]);
1486 return(1);
1487 }
1488 }
1489
1490 #ifdef QNX
1491 #define DONDELAY
1492 #else
1493 #ifdef O_NDELAY
1494 #define DONDELAY
1495 #endif /* O_NDELAY */
1496 #endif /* QNX */
1497
1498 /* Z O P E N O -- Open a new file for output. */
1499
1500 /*ARGSUSED*/ /* zz not used */
1501 int
zopeno(n,name,zz,fcb)1502 zopeno(n,name,zz,fcb)
1503 /* zopeno */ int n; char *name; struct zattr *zz; struct filinfo *fcb; {
1504
1505 char p[8];
1506 int append = 0;
1507 int istty = 0, filefd = 0;
1508
1509 /* As of Version 5A, the attribute structure and the file information */
1510 /* structure are included in the arglist. */
1511
1512 #ifdef DEBUG
1513 debug(F111,"zopeno",name,n);
1514 if (fcb) {
1515 debug(F101,"zopeno fcb disp","",fcb->dsp);
1516 debug(F101,"zopeno fcb type","",fcb->typ);
1517 debug(F101,"zopeno fcb char","",fcb->cs);
1518 } else {
1519 debug(F100,"zopeno fcb is NULL","",0);
1520 }
1521 #endif /* DEBUG */
1522
1523 if (chkfn(n) != 0) /* Already open? */
1524 return(0); /* Nothing to do. */
1525
1526 if ((n == ZCTERM) || (n == ZSTDIO)) { /* Terminal or standard output */
1527 fp[ZOFILE] = stdout;
1528 ispipe[ZOFILE] = 0;
1529 #ifdef COMMENT
1530 /* This seems right but it breaks client server ops */
1531 fp[n] = stdout;
1532 ispipe[n] = 0;
1533 #endif /* COMMENT */
1534 #ifdef COMMENT
1535 #ifdef DEBUG
1536 if (n != ZDFILE)
1537 debug(F101,"zopeno fp[n]=stdout","",fp[n]);
1538 #endif /* DEBUG */
1539 #endif /* COMMENT */
1540 zoutcnt = 0;
1541 zoutptr = zoutbuffer;
1542 return(1);
1543 }
1544
1545 /* A real file. Open it in desired mode (create or append). */
1546
1547 #ifdef CKROOT
1548 debug(F111,"zopeno setroot",ckroot,ckrootset);
1549 if (ckrootset) if (!zinroot(name)) {
1550 debug(F110,"zopeno setroot violation",name,0);
1551 return(0);
1552 }
1553 #endif /* CKROOT */
1554
1555 ckstrncpy(p,"w",8); /* Assume write/create mode */
1556 if (fcb) { /* If called with an FCB... */
1557 if (fcb->dsp == XYFZ_A) { /* Does it say Append? */
1558 ckstrncpy(p,"a",8); /* Yes. */
1559 debug(F100,"zopeno append","",0);
1560 append = 1;
1561 }
1562 }
1563 if (xferlog
1564 #ifdef CKSYSLOG
1565 || ((ckxsyslog >= SYSLG_FC) && ckxlogging)
1566 #endif /* CKSYSLOG */
1567 ) {
1568 getfullname(name);
1569 debug(F110,"zopeno fullname",fullname,0);
1570 }
1571 {
1572 /* Allow tty devices to opened as output files 2009/10/20 */
1573 int fd, flags = 0;
1574 debug(F110,"zopeno attempting to open",name,0);
1575 if (!ckstrcmp(name,"/dev/",5,1)) { /* If it's a tty... */
1576 #ifdef O_NONBLOCK
1577 flags = O_NONBLOCK;
1578 #else
1579 #ifdef O_NDELAY
1580 flags = O_NDELAY;
1581 #else
1582 #ifdef FNDELAY
1583 flags = FNDELAY;
1584 #endif /* FNDELAY */
1585 #endif /* O_NDELAY */
1586 #endif /* O_NONBLOCK */
1587 }
1588 debug(F111,"zopeno open flags",name,flags);
1589 fd = open(name,O_WRONLY|flags,0600);
1590 debug(F111,"zopeno open",name,fd);
1591 if (fd > -1) {
1592 if (isatty(fd)) {
1593 filefd = fd;
1594 istty++;
1595 }
1596 }
1597 }
1598 debug(F111,"zopeno istty",name,istty);
1599 debug(F110,"zopeno fopen arg",p,0);
1600 if (istty)
1601 fp[n] = fdopen(filefd,p);
1602 else
1603 fp[n] = fopen(name,p); /* Try to open the file */
1604 ispipe[ZIFILE] = 0;
1605
1606 #ifdef ZDEBUG
1607 printf("ZOPENO fp[%d]=%ld\n",n,fp[n]);
1608 #endif /* ZDEBUG */
1609
1610 if (fp[n] == NULL) { /* Failed */
1611 debug(F101,"zopeno failed errno","",errno);
1612 if (istty) close(filefd);
1613 #ifdef CKSYSLOG
1614 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1615 syslog(LOG_INFO, "file[%d] %s: %s failed (%m)",
1616 n,
1617 fullname,
1618 append ? "append" : "create"
1619 );
1620 #endif /* CKSYSLOG */
1621 #ifdef COMMENT /* Let upper levels print message. */
1622 perror("Can't open output file");
1623 #endif /* COMMENT */
1624 } else { /* Succeeded */
1625 extern int zofbuffer, zofblock, zobufsize;
1626 debug(F101, "zopeno zobufsize", "", zobufsize);
1627 if (n == ZDFILE || n == ZTFILE) { /* If debug or transaction log */
1628 setbuf(fp[n],NULL); /* make it unbuffered. */
1629 #ifdef DONDELAY
1630 } else if (n == ZOFILE && !zofblock) { /* blocking or nonblocking */
1631 int flags;
1632 if ((flags = fcntl(fileno(fp[n]),F_GETFL,0)) > -1)
1633 fcntl(fileno(fp[n]),F_SETFL, flags |
1634 #ifdef QNX
1635 O_NONBLOCK
1636 #else
1637 O_NDELAY
1638 #endif /* QNX */
1639 );
1640 debug(F100,"zopeno ZOFILE nonblocking","",0);
1641 #endif /* DONDELAY */
1642 } else if (n == ZOFILE && !zofbuffer) { /* buffered or unbuffered */
1643 setbuf(fp[n],NULL);
1644 debug(F100,"zopeno ZOFILE unbuffered","",0);
1645 }
1646
1647 #ifdef CK_LOGIN
1648 /* Enforce anonymous file-creation permission */
1649 if (isguest)
1650 if (n == ZWFILE || n == ZMFILE ||
1651 n == ZOFILE || n == ZDFILE ||
1652 n == ZTFILE || n == ZPFILE ||
1653 n == ZSFILE)
1654 chmod(name,ckxperms);
1655 #endif /* CK_LOGIN */
1656 #ifdef CKSYSLOG
1657 if (ckxsyslog >= SYSLG_FC && ckxlogging)
1658 syslog(LOG_INFO, "file[%d] %s: %s ok",
1659 n,
1660 fullname,
1661 append ? "append" : "create"
1662 );
1663 #endif /* CKSYSLOG */
1664 debug(F100, "zopeno ok", "", 0);
1665 }
1666 zoutcnt = 0; /* (PWP) reset output buffer */
1667 zoutptr = zoutbuffer;
1668 return((fp[n] != NULL) ? 1 : 0);
1669 }
1670
1671 /* Z C L O S E -- Close the given file. */
1672
1673 /* Returns 0 if arg out of range, 1 if successful, -1 if close failed. */
1674
1675 int
zclose(n)1676 zclose(n) int n; {
1677 int x = 0, x2 = 0;
1678 int dummy;
1679 extern CK_OFF_T ffc;
1680
1681 debug(F101,"zclose file number","",n);
1682 if (chkfn(n) < 1) return(0); /* Check range of n */
1683 if ((n == ZOFILE) && (zoutcnt > 0)) /* (PWP) output leftovers */
1684 x2 = zoutdump();
1685
1686 if (fp[ZSYSFN] || ispipe[n]) { /* If file is really pipe */
1687 #ifndef NOPUSH
1688 x = zclosf(n); /* do it specially */
1689 #else
1690 x = EOF;
1691 #endif /* NOPUSH */
1692 debug(F101,"zclose zclosf","",x);
1693 /* debug(F101,"zclose zclosf fp[n]","",fp[n]); */
1694 } else {
1695 if ((fp[n] != stdout) && (fp[n] != stdin))
1696 x = fclose(fp[n]);
1697 fp[n] = NULL;
1698 #ifdef COMMENT
1699 if (n == ZCTERM || n == ZSTDIO) /* See zopeno() */
1700 if (fp[ZOFILE] == stdout)
1701 fp[ZOFILE] = NULL;
1702 #endif /* COMMENT */
1703 }
1704 iflen = -1L; /* Invalidate file length */
1705 if (x == EOF) { /* if we got a close error */
1706 debug(F101,"zclose fclose fails","",x);
1707 return(-1);
1708 } else if (x2 < 0) { /* or error flushing last buffer */
1709 debug(F101,"zclose error flushing last buffer","",x2);
1710 return(-1); /* then return an error */
1711 } else {
1712 /* Print log record compatible with wu-ftpd */
1713 if (xferlog && (n == ZIFILE || n == ZOFILE)) {
1714 char * s, *p;
1715 extern char ttname[];
1716 if (!iklogopen) (VOID) doiklog(); /* Open log if necessary */
1717 debug(F101,"zclose iklogopen","",iklogopen);
1718 if (iklogopen) {
1719 int len;
1720 char * fnam;
1721
1722 timenow = time(NULL);
1723 #ifdef CK_LOGIN
1724 if (logged_in)
1725 s = clienthost;
1726 else
1727 #endif /* CK_LOGIN */
1728 s = (char *)ttname;
1729 if (!s) s = "";
1730 if (!*s) s = "*";
1731 #ifdef CK_LOGIN
1732 if (logged_in) {
1733 p = guestpass;
1734 if (!*p) p = "*";
1735 } else
1736 #endif /* CK_LOGIN */
1737 p = whoami();
1738
1739 len = 24 + 12 + (int)strlen(s) + 16
1740 + (int)strlen(fullname) + 1 + 1 + 1 + 1
1741 + (int)strlen(p) + 6 + 2 + 12;
1742 fnam = fullname;
1743 if (!*fnam) fnam = "(pipe)";
1744
1745 if (len > IKSDMSGLEN)
1746 sprintf(iksdmsg, /* SAFE */
1747 "%.24s [BUFFER WOULD OVERFLOW]\n",ctime(&timenow));
1748 else
1749 sprintf(iksdmsg, /* SAFE */
1750 "%.24s %d %s %s %s %c %s %c %c %s %s %d %s\n",
1751 ctime(&timenow), /* date/time */
1752 gtimer(), /* elapsed secs */
1753 s, /* peer name */
1754 ckfstoa(ffc), /* byte count */
1755 fnam, /* full pathname of file */
1756 (binary ? 'b' : 'a'), /* binary or ascii */
1757 "_", /* options = none */
1758 n == ZIFILE ? 'o' : 'i', /* in/out */
1759 #ifdef CK_LOGIN
1760 (isguest ? 'a' : 'r'), /* User type */
1761 #else
1762 'r',
1763 #endif /* CK_LOGIN */
1764 p, /* Username or guest passwd */
1765 #ifdef CK_LOGIN
1766 logged_in ? "iks" : "kermit", /* Record ID */
1767 #else
1768 "kermit",
1769 #endif /* CK_LOGIN */
1770 0, /* User ID on client system unknown */
1771 "*" /* Ditto */
1772 );
1773 debug(F110,"zclose iksdmsg",iksdmsg,0);
1774 dummy = write(xferlog, iksdmsg, (int)strlen(iksdmsg));
1775 }
1776 }
1777 debug(F101,"zclose returns","",1);
1778 return(1);
1779 }
1780 }
1781
1782 /* Z C H I N -- Get a character from the input file. */
1783
1784 /* Returns -1 if EOF, 0 otherwise with character returned in argument */
1785
1786 int
zchin(n,c)1787 zchin(n,c) int n; int *c; {
1788 int a;
1789
1790 #ifdef IKSD
1791 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1792 a = coninc(0);
1793 if (*c < 0)
1794 return(-1);
1795 } else
1796 #endif /* IKSD */
1797 /* (PWP) Just in case this gets called when it shouldn't. */
1798 if (n == ZIFILE) {
1799 a = zminchar(); /* Note: this catches Ctrl-Z */
1800 if (a < 0) /* (See zinfill()...) */
1801 return(-1);
1802 } else {
1803 a = getc(fp[n]);
1804 if (a == EOF) return(-1);
1805 #ifdef CK_CTRLZ
1806 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1807 if (!binary && a == 0x1A && eofmethod == XYEOF_Z)
1808 return(-1);
1809 #endif /* CK_CTRLZ */
1810 }
1811 *c = (CHAR) a & 0377;
1812 return(0);
1813 }
1814
1815 /* Z S I N L -- Read a line from a file */
1816
1817 /*
1818 Writes the line into the address provided by the caller.
1819 n is the Kermit "channel number".
1820 Writing terminates when newline is encountered, newline is not copied.
1821 Writing also terminates upon EOF or if length x is exhausted.
1822 Returns 0 on success, -1 on EOF or error.
1823 */
1824 int
zsinl(n,s,x)1825 zsinl(n,s,x) int n, x; char *s; {
1826 int a, z = 0; /* z is return code. */
1827 int count = 0;
1828 int len = 0;
1829 char *buf;
1830 extern CHAR feol; /* Line terminator */
1831
1832 if (!s || chkfn(n) < 1) /* Make sure file is open, etc */
1833 return(-1);
1834 buf = s;
1835 s[0] = '\0'; /* Don't return junk */
1836
1837 a = -1; /* Current character, none yet. */
1838 while (x--) { /* Up to given length */
1839 int old = 0;
1840 if (feol) /* Previous character */
1841 old = a;
1842 if (zchin(n,&a) < 0) { /* Read a character from the file */
1843 debug(F101,"zsinl zchin fail","",count);
1844 if (count == 0)
1845 z = -1; /* EOF or other error */
1846 break;
1847 } else
1848 count++;
1849 if (feol) { /* Single-character line terminator */
1850 if (a == feol)
1851 break;
1852 } else { /* CRLF line terminator */
1853 if (a == '\015') /* CR, get next character */
1854 continue;
1855 if (old == '\015') { /* Previous character was CR */
1856 if (a == '\012') { /* This one is LF, so we have a line */
1857 break;
1858 } else { /* Not LF, deposit CR */
1859 *s++ = '\015';
1860 x--;
1861 len++;
1862 }
1863 }
1864 }
1865 *s = a; /* Deposit character */
1866 s++;
1867 len++;
1868 }
1869 *s = '\0'; /* Terminate the string */
1870 debug(F011,"zsinl",buf,len);
1871 return(z);
1872 }
1873
1874 /* Z X I N -- Read x bytes from a file */
1875
1876 /*
1877 Reads x bytes (or less) from channel n and writes them
1878 to the address provided by the caller.
1879 Returns number of bytes read on success, 0 on EOF or error.
1880 */
1881 int
zxin(n,s,x)1882 zxin(n,s,x) int n, x; char *s; {
1883 #ifdef IKSD
1884 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
1885 int a, i;
1886 a = ttchk();
1887 if (a < 1) return(0);
1888 for (i = 0; i < a && i < x; i++)
1889 s[i] = coninc(0);
1890 return(i);
1891 }
1892 #endif /* IKSD */
1893
1894 return(fread(s, sizeof (char), x, fp[n]));
1895 }
1896
1897 /*
1898 Z I N F I L L -- Buffered file input.
1899
1900 (re)fill the file input buffer with data. All file input
1901 should go through this routine, usually by calling the zminchar()
1902 macro defined in ckcker.h. Returns:
1903
1904 Value 0..255 on success, the character that was read.
1905 -1 on end of file.
1906 -2 on any kind of error other than end of file.
1907 -3 timeout when reading from pipe (Kermit packet mode only).
1908 */
1909 int
zinfill()1910 zinfill() {
1911 extern int kactive, srvping;
1912 errno = 0;
1913
1914 #ifdef ZDEBUG
1915 printf("ZINFILL fp[%d]=%ld\n",ZIFILE,fp[ZIFILE]);
1916 #endif /* ZDEBUG */
1917
1918 #ifdef IKSD
1919 if (inserver && !local && fp[ZIFILE] == stdin) {
1920 int a, i;
1921 a = ttchk();
1922 if (a < 0) return(-2);
1923 for (i = 0; i < a && i < INBUFSIZE; i++) {
1924 zinbuffer[i] = coninc(0);
1925 }
1926 zincnt = i;
1927 /* set pointer to beginning, (== &zinbuffer[0]) */
1928 zinptr = zinbuffer;
1929 if (zincnt == 0) return(-1);
1930 zincnt--; /* One less char in buffer */
1931 return((int)(*zinptr++) & 0377); /* because we return the first */
1932 }
1933 #endif /* IKSD */
1934
1935 debug(F101,"zinfill kactive","",kactive);
1936
1937 if (!(kactive && ispipe[ZIFILE])) {
1938 if (feof(fp[ZIFILE])) {
1939 debug(F100,"ZINFILL feof","",0);
1940 #ifdef ZDEBUG
1941 printf("ZINFILL EOF\n");
1942 #endif /* ZDEBUG */
1943 return(-1);
1944 }
1945 }
1946 clearerr(fp[ZIFILE]);
1947
1948 #ifdef SELECT
1949 /* Here we can call select() to get a timeout... */
1950 if (kactive && ispipe[ZIFILE]) {
1951 int secs, z = 0;
1952 #ifndef NOXFER
1953 if (srvping) {
1954 secs = 1;
1955 debug(F101,"zinfill calling ttwait","",secs);
1956 z = ttwait(fileno(fp[ZIFILE]),secs);
1957 debug(F101,"zinfill ttwait","",z);
1958 }
1959 #endif /* NOXFER */
1960 if (z == 0)
1961 return(-3);
1962 }
1963 #endif /* SELECT */
1964
1965 #ifdef DEBUG
1966 if (deblog) {
1967 int i;
1968 debug(F101,"ZINFILL INBUFSIZE","",INBUFSIZE);
1969 #ifdef USE_MEMCPY
1970 memset(zinbuffer, 0xFF, INBUFSIZE);
1971 #else
1972 for (i = 0; i < INBUFSIZE; i++) {
1973 zinbuffer[i] = 0xFF;
1974 #ifdef COMMENT /* Too much! */
1975 debug(F101,"ZINFILL zinbuffer[i]","",i);
1976 #endif /* COMMENT */
1977 }
1978 #endif /* USE_MEMCPY */
1979 ckstrncpy(zinbuffer,"zinbuffer is a valid buffer",INBUFSIZE);
1980 /* debug(F111,"ZINFILL about to call fread",zinbuffer,zinbuffer); */
1981 }
1982 #endif /* DEBUG */
1983
1984 /*
1985 Note: The following read MUST be nonblocking when reading from a pipe
1986 and we want timeouts to work. See zxcmd().
1987 */
1988 zincnt = fread(zinbuffer, sizeof (char), INBUFSIZE, fp[ZIFILE]);
1989 debug(F101,"ZINFILL fread","",zincnt); /* Just the size */
1990 #ifdef ZDEBUG
1991 printf("FREAD=%d\n",zincnt);
1992 #endif /* ZDEBUG */
1993 #ifdef CK_CTRLZ
1994 /* If SET FILE EOF CTRL-Z, first Ctrl-Z marks EOF */
1995 if (zincnt > 0 && !binary && eofmethod == XYEOF_Z) {
1996 register int i;
1997 for (i = 0; i < zincnt; i++) {
1998 if (zinbuffer[i] == SUB) {
1999 zincnt = i; /* Stop at first Ctrl-Z */
2000 if (i == 0)
2001 return(-1);
2002 break;
2003 }
2004 }
2005 }
2006 #endif /* CK_CTRLZ */
2007
2008 if (zincnt == 0) { /* Got nothing? */
2009 if (ferror(fp[ZIFILE])) {
2010 debug(F100,"ZINFILL ferror","",0);
2011 debug(F101,"ZINFILL errno","",errno);
2012 #ifdef ZDEBUG
2013 printf("ZINFILL errno=%d\n",errno);
2014 #endif /* ZDEBUG */
2015 #ifdef EWOULDBLOCK
2016 return((errno == EWOULDBLOCK) ? -3 : -2);
2017 #else
2018 return(-2);
2019 #endif /* EWOULDBLOCK */
2020 }
2021
2022 /* In case feof() didn't work just above -- sometimes it doesn't... */
2023
2024 if (feof(fp[ZIFILE]) ) {
2025 debug(F100,"ZINFILL count 0 EOF return -1","",0);
2026 return (-1);
2027 } else {
2028 debug(F100,"ZINFILL count 0 not EOF return -2","",0);
2029 return(-2);
2030 }
2031 }
2032 zinptr = zinbuffer; /* set pointer to beginning, (== &zinbuffer[0]) */
2033 zincnt--; /* One less char in buffer */
2034 return((int)(*zinptr++) & 0377); /* because we return the first */
2035 }
2036
2037 /* Z S O U T -- Write a string out to the given file, buffered. */
2038
2039 /* Returns 0 on success, -1 on failure */
2040
2041 int
zsout(n,s)2042 zsout(n,s) int n; char *s; {
2043 int rc = 0;
2044 rc = chkfn(n);
2045 if (rc < 1) return(-1); /* Keep this, prevents memory faults */
2046 if (!s) return(0); /* Null pointer, do nothing, succeed */
2047 if (!*s) return(0); /* empty string, ditto */
2048
2049 #ifdef IKSD
2050 /*
2051 This happens with client-side Kermit server when a REMOTE command
2052 was sent from the server to the client and the server is supposed to
2053 display the text, but of course there is no place to display it
2054 since it is in remote mode executing Kermit protocol.
2055 */
2056 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2057 #ifdef COMMENT
2058 return(ttol(s,((int)strlen(s)) < 0) ? -1 : 0);
2059 #else
2060 return(0);
2061 #endif /* COMMENT */
2062 }
2063 #endif /* IKSD */
2064
2065 if (n == ZSFILE) {
2066 int k;
2067 k = strlen(s);
2068 rc = write(fileno(fp[n]),s,k);
2069 return((rc == k) ? 0 : -1);
2070 }
2071 rc = fputs(s,fp[n]) == EOF ? -1 : 0;
2072 if (n == ZWFILE)
2073 fflush(fp[n]);
2074 return(rc);
2075 }
2076
2077 /* Z S O U T L -- Write string to file, with line terminator, buffered */
2078
2079 /* Returns 0 on success, -1 on failure */
2080
2081 int
zsoutl(n,s)2082 zsoutl(n,s) int n; char *s; {
2083 if (zsout(n,s) < 0)
2084 return(-1);
2085
2086 #ifdef IKSD
2087 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2088 #ifdef COMMENT
2089 return(ttoc(LF));
2090 #else
2091 return(0); /* See comments in zsout() */
2092 #endif /* COMMENT */
2093 }
2094 #endif /* IKSD */
2095
2096 if (n == ZSFILE) /* Session log is unbuffered */
2097 return(write(fileno(fp[n]),"\n",1) == 1 ? 0 : -1);
2098 else if (fputs("\n",fp[n]) == EOF)
2099 return(-1);
2100 if (n == ZDIFIL || n == ZWFILE) /* Flush connection log records */
2101 fflush(fp[n]);
2102 return(0);
2103 }
2104
2105 /* Z S O U T X -- Write x characters to file, unbuffered. */
2106
2107 /* Returns number of characters written on success, -1 on failure */
2108
2109 int
zsoutx(n,s,x)2110 zsoutx(n,s,x) int n, x; char *s; {
2111 int k;
2112 if (!s) return(0);
2113 if (!*s) return(0);
2114
2115 #ifdef IKSD
2116 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2117 #ifdef COMMENT
2118 return(ttol(s,x)); /* See comments in zsout() */
2119 #else
2120 return(x);
2121 #endif /* COMMENT */
2122 }
2123 #endif /* IKSD */
2124
2125 if ((k = (int)strlen(s)) > x) x = k; /* Nothing else would make sense */
2126 #ifdef COMMENT
2127 if (chkfn(n) < 1) return(-1);
2128 return(write(fp[n]->_file,s,x));
2129 #endif /* COMMENT */
2130 return(write(fileno(fp[n]),s,x) == x ? x : -1);
2131 }
2132
2133 /* Z C H O U T -- Add a character to the given file. */
2134
2135 /* Should return 0 or greater on success, -1 on failure (e.g. disk full) */
2136
2137 int
2138 #ifdef CK_ANSIC
zchout(register int n,char c)2139 zchout(register int n, char c)
2140 #else
2141 zchout(n,c) register int n; char c;
2142 #endif /* CK_ANSIC */
2143 /* zchout() */ {
2144 /* if (chkfn(n) < 1) return(-1); */
2145
2146 #ifdef IKSD
2147 if (inserver && !local && (n == ZCTERM || n == ZSTDIO)) {
2148 #ifdef COMMENT
2149 return(ttoc(c));
2150 #else
2151 return(0); /* See comments in zsout() */
2152 #endif /* COMMENT */
2153 }
2154 #endif /* IKSD */
2155
2156 if (n == ZSFILE) /* Use unbuffered for session log */
2157 return(write(fileno(fp[n]),&c,1) == 1 ? 0 : -1);
2158 /* Buffered for everything else */
2159 if (putc(c,fp[n]) == EOF) /* If true, maybe there was an error */
2160 return(ferror(fp[n])?-1:0); /* Check to make sure */
2161 else /* Otherwise... */
2162 return(0); /* There was no error. */
2163 }
2164
2165 /* (PWP) buffered character output routine to speed up file IO */
2166
2167 int
zoutdump()2168 zoutdump() {
2169 int x;
2170 char * zp;
2171 zoutptr = zoutbuffer; /* Reset buffer pointer in all cases */
2172 #ifdef DEBUG
2173 if (deblog)
2174 debug(F101,"zoutdump zoutcnt","",zoutcnt);
2175 #endif /* DEBUG */
2176 if (zoutcnt == 0) { /* Nothing to output */
2177 return(0);
2178 } else if (zoutcnt < 0) { /* Unexpected negative argument */
2179 zoutcnt = 0; /* Reset output buffer count */
2180 return(-1); /* and fail. */
2181 }
2182
2183 #ifdef IKSD
2184 if (inserver && !local && fp[ZOFILE] == stdout) {
2185 #ifdef COMMENT
2186 x = ttol(zoutbuffer,zoutcnt);
2187 #else
2188 x = 1; /* See comments in zsout() */
2189 #endif /* COMMENT */
2190 zoutcnt = 0;
2191 return(x > 0 ? 0 : -1);
2192 }
2193 #endif /* IKSD */
2194
2195 /*
2196 Frank Prindle suggested that replacing this fwrite() by an fflush()
2197 followed by a write() would improve the efficiency, especially when
2198 writing to stdout. Subsequent tests showed a 5-fold improvement.
2199 */
2200 #ifdef COMMENT
2201 if (x = fwrite(zoutbuffer, 1, zoutcnt, fp[ZOFILE])) ...
2202 #endif /* COMMENT */
2203
2204 #ifndef CK_NONBLOCK
2205 fflush(fp[ZOFILE]);
2206 #endif /* CK_NONBLOCK */
2207 zp = zoutbuffer;
2208 while (zoutcnt > 0) {
2209 if ((x = write(fileno(fp[ZOFILE]),zp,zoutcnt)) > -1) {
2210 #ifdef DEBUG
2211 if (deblog) /* Save a function call... */
2212 debug(F101,"zoutdump wrote","",x);
2213 #endif /* DEBUG */
2214 zoutcnt -= x; /* Adjust output buffer count */
2215 zp += x; /* and pointer */
2216 } else {
2217 #ifdef DEBUG
2218 if (deblog) {
2219 debug(F101,"zoutdump write error","",errno);
2220 debug(F101,"zoutdump write returns","",x);
2221 }
2222 #endif /* DEBUG */
2223 zoutcnt = 0; /* Reset output buffer count */
2224 return(-1); /* write() failed */
2225 }
2226 }
2227 return(0);
2228 }
2229
2230 /* C H K F N -- Internal function to verify file number is ok */
2231
2232 /*
2233 Returns:
2234 -1: File number n is out of range
2235 0: n is in range, but file is not open
2236 1: n in range and file is open
2237 */
2238 int
chkfn(n)2239 chkfn(n) int n; {
2240 /* if (n != ZDFILE) debug(F101,"chkfn","",n); */
2241 if (n < 0 || n >= ZNFILS) {
2242 if (n != ZDFILE) debug(F101,"chkfn out of range","",n);
2243 return(-1);
2244 } else {
2245 /* if (n != ZDFILE) debug(F101,"chkfn fp[n]","",fp[n]); */
2246 return((fp[n] == NULL) ? 0 : 1);
2247 }
2248 }
2249
2250 /* Z G E T F S -- Return file size regardless of accessibility */
2251 /*
2252 Used for directory listings, etc.
2253 Returns:
2254 The size of the file in bytes, 0 or greater, if the size can be learned.
2255 -1 if the file size can not be obtained.
2256 Also (and this is a hack just for UNIX):
2257 If the argument is the name of a symbolic link,
2258 the global variable zgfs_link is set to 1,
2259 and the global buffer linkname[] gets the link value.
2260 And it sets zgfs_dir to 1 if it's a directory, otherwise 0.
2261 This lets us avoid numerous redundant calls to stat().
2262 */
2263 int zgfs_link = 0;
2264 int zgfs_dir = 0;
2265 time_t zgfs_mtime = 0;
2266 unsigned int zgfs_mode = 0;
2267
2268 #ifdef CKSYMLINK
2269 char linkname[CKMAXPATH+1];
2270 #ifndef _IFLNK
2271 #define _IFLNK 0120000
2272 #endif /* _IFLNK */
2273 #endif /* CKSYMLINK */
2274
2275 CK_OFF_T
zgetfs(name)2276 zgetfs(name) char *name; {
2277 struct stat buf;
2278 char fnam[CKMAXPATH+4];
2279 CK_OFF_T size = (CK_OFF_T)-1;
2280 int x;
2281 int needrlink = 0;
2282 char * s;
2283
2284 if (!name) name = "";
2285 if (!*name) return(-1);
2286
2287 #ifdef UNIX
2288 x = strlen(name);
2289 if (x == 9 && !strcmp(name,"/dev/null"))
2290 return(0);
2291 #endif /* UNIX */
2292
2293 s = name;
2294 #ifdef DTILDE
2295 if (*s == '~') {
2296 s = tilde_expand(s);
2297 if (!s) s = "";
2298 if (!*s) s = name;
2299 }
2300 #endif /* DTILDE */
2301 x = ckstrncpy(fnam,s,CKMAXPATH);
2302 s = fnam;
2303 debug(F111,"zgetfs fnam",s,x);
2304 if (x > 0 && s[x-1] == '/')
2305 s[x-1] = '\0';
2306
2307 zgfs_dir = 0; /* Assume it's not a directory */
2308 zgfs_link = 0; /* Assume it's not a symlink */
2309 zgfs_mtime = 0; /* No time yet */
2310 zgfs_mode = 0; /* No permission bits yet */
2311
2312 #ifdef CKSYMLINK /* We're doing symlinks? */
2313 #ifdef USE_LSTAT /* OK to use lstat()? */
2314 x = lstat(s,&buf);
2315 debug(F101,"STAT","",1);
2316 if (x < 0) /* stat() failed */
2317 return(-1);
2318 if ( /* Now see if it's a symlink */
2319 #ifdef S_ISLNK
2320 S_ISLNK(buf.st_mode)
2321 #else
2322 #ifdef _IFLNK
2323 ((_IFMT & buf.st_mode) == _IFLNK)
2324 #endif /* _IFLNK */
2325 #endif /* S_ISLNK */
2326 ) {
2327 zgfs_link = 1; /* It's a symlink */
2328 linkname[0] = '\0'; /* Get the name */
2329 x = readlink(s,linkname,CKMAXPATH);
2330 debug(F101,"zgetfs readlink",s,x);
2331 if (x > -1 && x < CKMAXPATH) { /* It's a link */
2332 linkname[x] = '\0';
2333 size = buf.st_size; /* Remember size of link */
2334 x = stat(s,&buf); /* Now stat the linked-to file */
2335 debug(F101,"STAT","",2);
2336 if (x < 0) /* so we can see if it's a directory */
2337 return(-1);
2338 } else {
2339 ckstrncpy(linkname,"(lookup failed)",CKMAXPATH);
2340 }
2341 }
2342 #else /* !USE_LSTAT */
2343 x = stat(s,&buf); /* No lstat(), use stat() instead */
2344 debug(F101,"STAT","",3);
2345 if (x < 0)
2346 return(-1);
2347 #endif /* USE_LSTAT */
2348
2349 /* Do we need to call readlink()? */
2350
2351 #ifdef NOLINKBITS
2352 /*
2353 lstat() does not work in SCO operating systems. From "man NS lstat":
2354
2355 lstat obtains information about the file named by path. In the case of a
2356 symbolic link, lstat returns information about the link, and not the file
2357 named by the link. It is only used by the NFS automount daemon and should
2358 not be utilized by users.
2359 */
2360 needrlink = 1;
2361 debug(F101,"zgetfs forced needrlink","",needrlink);
2362 #else
2363 #ifdef S_ISLNK
2364 needrlink = S_ISLNK(buf.st_mode);
2365 debug(F101,"zgetfs S_ISLNK needrlink","",needrlink);
2366 #else
2367 #ifdef _IFLNK
2368 needrlink = (_IFMT & buf.st_mode) == _IFLNK;
2369 debug(F101,"zgetfs _IFLNK needrlink","",needrlink);
2370 #else
2371 needrlink = 1;
2372 debug(F101,"zgetfs default needrlink","",needrlink);
2373 #endif /* _IFLNK */
2374 #endif /* S_ISLNK */
2375 #endif /* NOLINKBITS */
2376
2377 if (needrlink) {
2378 linkname[0] = '\0';
2379 errno = 0;
2380 x = readlink(s,linkname,CKMAXPATH);
2381 #ifdef DEBUG
2382 debug(F111,"zgetfs readlink",s,x);
2383 if (x < 0)
2384 debug(F101,"zgetfs readlink errno","",errno);
2385 else
2386 debug(F110,"zgetfs readlink result",linkname,0);
2387 #endif /* DEBUG */
2388 if (x > -1 && x < CKMAXPATH) {
2389 zgfs_link = 1;
2390 linkname[x] = '\0';
2391 }
2392 }
2393 #else /* !CKSYMLINK */
2394 x = stat(s,&buf); /* Just stat the file */
2395 debug(F111,"zgetfs stat",s,x);
2396 if (x < 0) /* and get the size */
2397 return(-1);
2398 #endif /* CKSYMLINK */
2399
2400 zgfs_mtime = buf.st_mtime;
2401 zgfs_mode = buf.st_mode;
2402 zgfs_dir = (S_ISDIR(buf.st_mode)) ? 1 : 0; /* Set "is directory" flag */
2403 debug(F111,"zgetfs size",s,size);
2404 debug(F111,"zgetfs st_size",s,buf.st_size);
2405 return((size < 0L) ? buf.st_size : size); /* Return the size */
2406 }
2407
2408 /* Z C H K I -- Check if input file exists and is readable */
2409
2410 /*
2411 Returns:
2412 >= 0 if the file can be read (returns the size).
2413 -1 if file doesn't exist or can't be accessed,
2414 -2 if file exists but is not readable (e.g. a directory file).
2415 -3 if file exists but protected against read access.
2416
2417 For Berkeley Unix, a file must be of type "regular" to be readable.
2418 Directory files, special files, and symbolic links are not readable.
2419 */
2420 CK_OFF_T
zchki(name)2421 zchki(name) char *name; {
2422 struct stat buf;
2423 char * s;
2424 int x, itsadir = 0;
2425 extern int zchkid, diractive, matchfifo;
2426
2427 if (!name)
2428 return(-1);
2429 x = strlen(name);
2430 if (x < 1)
2431 return(-1);
2432 s = name;
2433
2434 #ifdef UNIX
2435 if (x == 9 && !strcmp(s,"/dev/null"))
2436 return(0);
2437 if (x == 8 && !strcmp(s,"/dev/tty"))
2438 return(0);
2439 #endif /* UNIX */
2440
2441 #ifdef DTILDE
2442 if (*s == '~') {
2443 s = tilde_expand(s);
2444 if (!s) s = "";
2445 if (!*s) s = name;
2446 }
2447 #endif /* DTILDE */
2448
2449 #ifdef CKROOT
2450 debug(F111,"zchki setroot",ckroot,ckrootset);
2451 if (ckrootset) if (!zinroot(name)) {
2452 debug(F110,"zchki setroot violation",name,0);
2453 return(-1);
2454 }
2455 #endif /* CKROOT */
2456
2457 x = stat(s,&buf);
2458 debug(F101,"STAT","",5);
2459 if (x < 0) {
2460 debug(F111,"zchki stat fails",s,errno);
2461 return(-1);
2462 }
2463 if (S_ISDIR (buf.st_mode))
2464 itsadir = 1;
2465
2466 if (!(itsadir && zchkid)) { /* Unless this... */
2467 if (!S_ISREG (buf.st_mode) /* Must be regular file */
2468 #ifdef S_ISFIFO
2469 && (!matchfifo || !S_ISFIFO (buf.st_mode)) /* or FIFO */
2470 #endif /* S_ISFIFO */
2471 ) {
2472 debug(F111,"zchki not regular file (or fifo)",s,matchfifo);
2473 return(-2);
2474 }
2475 }
2476 debug(F111,"zchki stat ok:",s,x);
2477
2478 if (diractive) { /* If listing don't check access */
2479 x = 1;
2480 } else {
2481 #ifdef SW_ACC_ID
2482 debug(F100,"zchki swapping ids for access()","",0);
2483 priv_on();
2484 #endif /* SW_ACC_ID */
2485 if ((x = access(s,R_OK)) < 0)
2486 x = access(s,X_OK); /* For RUN-class commands */
2487 #ifdef SW_ACC_ID
2488 priv_off();
2489 debug(F100,"zchki swapped ids restored","",0);
2490 #endif /* SW_ACC_ID */
2491 }
2492 if (x < 0) { /* Is the file accessible? */
2493 debug(F111,"zchki access failed:",s,x); /* No */
2494 return(-3);
2495 } else {
2496 iflen = buf.st_size; /* Yes, remember size */
2497 ckstrncpy(nambuf,s,CKMAXPATH); /* and name globally. */
2498 debug(F111,"zchki access ok:",s,iflen);
2499 return((iflen > -1L) ? iflen : 0L);
2500 }
2501 }
2502
2503 /* Z C H K O -- Check if output file can be created */
2504
2505 /*
2506 Returns -1 if write permission for the file would be denied, 0 otherwise.
2507
2508 NOTE: The design is flawed. There is no distinction among:
2509 . Can I overwrite an existing file?
2510 . Can I create a file (or directory) in an existing directory?
2511 . Can I create a file (or directory) and its parent(s)?
2512 */
2513 int
zchko(name)2514 zchko(name) char *name; {
2515 int i, x, itsadir = 0;
2516 char *s = NULL;
2517 char * oname;
2518 extern int zchkod; /* Used by IF WRITEABLE */
2519
2520 debug(F110,"zchko entry",name,0);
2521
2522 if (!name) return(-1); /* Watch out for null pointer. */
2523
2524 oname = name;
2525
2526 #ifdef CKROOT
2527 debug(F111,"zchko setroot",ckroot,ckrootset);
2528 if (ckrootset) if (!zinroot(name)) {
2529 debug(F110,"zchko setroot violation",name,0);
2530 errno = EACCES;
2531 return(-1);
2532 }
2533 #endif /* CKROOT */
2534
2535 x = (int)strlen(name); /* Get length of filename */
2536 debug(F111,"zchko len",name,x);
2537 debug(F111,"zchko zchkod",name,zchkod);
2538
2539 #ifdef UNIX
2540 /*
2541 Writing to null device is OK.
2542 */
2543 if (x == 9 && !strcmp(name,"/dev/null"))
2544 return(0);
2545 if (x == 8 && !strcmp(name,"/dev/tty"))
2546 return(0);
2547 #endif /* UNIX */
2548
2549 s = name;
2550 #ifdef DTILDE
2551 if (*s == '~') {
2552 s = tilde_expand(s);
2553 if (!s) s = "";
2554 if (!*s) s = name;
2555 x = strlen(s);
2556 }
2557 #endif /* DTILDE */
2558 name = s;
2559 s = NULL;
2560 /*
2561 zchkod is a global flag meaning we're checking not to see if the directory
2562 file is writeable, but if it's OK to create files IN the directory.
2563 */
2564 if (!zchkod && isdir(name)) { /* Directories are not writeable */
2565 debug(F111,"zchko isdir",name,1);
2566 return(-1);
2567 }
2568 s = malloc(x+3); /* Must copy because we can't */
2569 if (!s) { /* write into our argument. */
2570 fprintf(stderr,"zchko: Malloc error 46\n");
2571 return(-1);
2572 }
2573 ckstrncpy(s,name,x+3);
2574 #ifdef UNIX
2575 #ifdef NOUUCP
2576 { /* 2009/10/20 */
2577 /* Allow tty devices to opened as output files */
2578 int fd, istty = 0, flags = 0;
2579 debug(F110,"zchko attempting to open",name,0);
2580 if (!ckstrcmp(name,"/dev/",5,1)) { /* If tty (2016/02/16) */
2581 /* Don't block on lack of Carrier or other modem signals */
2582 #ifdef O_NONBLOCK
2583 flags = O_NONBLOCK;
2584 #else
2585 #ifdef O_NDELAY
2586 flags = O_NDELAY;
2587 #else
2588 #ifdef FNDELAY
2589 flags = FNDELAY;
2590 #endif /* FNDELAY */
2591 #endif /* O_NDELAY */
2592 #endif /* O_NONBLOCK */
2593 }
2594 debug(F111,"zchko open mode",name,flags);
2595 /* Must attempt to open it */
2596 fd = open(name,O_WRONLY|O_CREAT|flags,0600);
2597 debug(F111,"zchko open",name,fd);
2598 if (fd > -1) { /* to get a file descriptor */
2599 if (isatty(fd)) /* for isatty() */
2600 istty++;
2601 debug(F111,"zchko isatty",name,istty);
2602 fd = close(fd);
2603 if (istty) {
2604 goto doaccess;
2605 }
2606 } else {
2607 debug(F101,"zchko open errno","",errno);
2608 x = -1;
2609 goto xzchko; /* fdc 2015/01/12 */
2610 /* previously control fell through causing core dumps */
2611 /* on builds with -DNOUUCP */
2612 }
2613 }
2614 #endif /* NOUUCP */
2615 #endif /* UNIX */
2616
2617 if (zchkod) goto doaccess; /* fdc 20160129 */
2618 /*
2619 The following code gets the name of the containing directory so we
2620 can use access() to check if we are allowed to create files in it.
2621 */
2622 for (i = x; i > 0; i--) { /* Strip filename from right. */
2623 if (ISDIRSEP(s[i-1])) {
2624 itsadir = 1;
2625 break;
2626 }
2627 }
2628 debug(F101,"zchko i","",i);
2629 debug(F101,"zchko itsadir","",itsadir);
2630
2631 #ifdef COMMENT
2632 /* X/OPEN XPG3-compliant systems fail if argument ends with "/"... */
2633 if (i == 0) /* If no path, use current directory */
2634 strcpy(s,"./");
2635 else /* Otherwise, use given one. */
2636 s[i] = '\0';
2637 #else
2638 #ifdef COMMENT
2639 /*
2640 The following does not work for "foo/bar" where the foo directory does
2641 not exist even though we could create it: access("foo/.") fails, but
2642 access("foo") works OK.
2643 */
2644 /* So now we use "path/." if path given, or "." if no path given. */
2645 s[i++] = '.'; /* Append "." to path. */
2646 s[i] = '\0';
2647 #else
2648 /* So NOW we strip path segments from the right as long as they don't */
2649 /* exist -- we only call access() for path segments that *do* exist.. */
2650 /* (But this isn't quite right either since now zchko(/foo/bar/baz/xxx) */
2651 /* succeeds when I have write access to foo and bar but baz doesn't exit.) */
2652
2653 if (itsadir && i > 0) {
2654 s[i-1] = '\0';
2655 while (s[0] && !isdir(s)) {
2656 for (i = (int)strlen(s); i > 0; i--) {
2657 if (ISDIRSEP(s[i-1])) {
2658 s[i-1] = '\0';
2659 break;
2660 }
2661 }
2662 if (i == 0)
2663 s[0] = '\0';
2664 }
2665 } else {
2666 s[i++] = '.'; /* Append "." to path. */
2667 s[i] = '\0';
2668 }
2669 #endif /* COMMENT */
2670 #endif /* COMMENT */
2671
2672 if (!s[0])
2673 ckstrncpy(s,".",x+3);
2674
2675 doaccess:
2676
2677 #ifdef SW_ACC_ID
2678 debug(F110,"zchko swapping ids for access()",s,0);
2679 priv_on();
2680 #endif /* SW_ACC_ID */
2681
2682 x = access(s,W_OK); /* Check access of path. */
2683 debug(F110,"zchko access",s,x);
2684
2685 #ifdef SW_ACC_ID
2686 priv_off();
2687 debug(F100,"zchko swapped ids restored","",0);
2688 #endif /* SW_ACC_ID */
2689
2690 xzchko: /* Exit point */
2691 if (x < 0)
2692 debug(F111,"zchko access failed:",s,errno);
2693 else
2694 debug(F111,"zchko access ok:",s,x);
2695 if (s) free(s); /* Free temporary storage */
2696
2697 return((x < 0) ? -1 : 0); /* and return. */
2698 }
2699
2700 /* Z D E L E T -- Delete the named file. */
2701
2702 /* Returns: -1 on error, 0 on success */
2703
2704 int
zdelet(name)2705 zdelet(name) char *name; {
2706 int x;
2707 #ifdef CK_LOGIN
2708 if (isguest)
2709 return(-1);
2710 #endif /* CK_LOGIN */
2711
2712 #ifdef CKROOT
2713 debug(F111,"zdelet setroot",ckroot,ckrootset);
2714 if (ckrootset) if (!zinroot(name)) {
2715 debug(F110,"zdelet setroot violation",name,0);
2716 return(-1);
2717 }
2718 #endif /* CKROOT */
2719
2720 x = unlink(name);
2721 debug(F111,"zdelet",name,x);
2722 #ifdef CKSYSLOG
2723 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
2724 fullname[0] = '\0';
2725 zfnqfp(name,CKMAXPATH,fullname);
2726 debug(F110,"zdelet fullname",fullname,0);
2727 if (x < 0)
2728 syslog(LOG_INFO, "file[] %s: delete failed (%m)", fullname);
2729 else
2730 syslog(LOG_INFO, "file[] %s: delete ok", fullname);
2731 }
2732 #endif /* CKSYSLOG */
2733 return(x);
2734 }
2735
2736 /* Z R T O L -- Convert remote filename into local form */
2737
2738 VOID
zrtol(name,name2)2739 zrtol(name,name2) char *name, *name2; {
2740 nzrtol(name,name2,1,0,CKMAXPATH);
2741 }
2742
2743 VOID
nzrtol(name,name2,fncnv,fnrpath,max)2744 nzrtol(name,name2,fncnv,fnrpath,max)
2745 char *name, *name2; int fncnv, fnrpath, max;
2746 { /* nzrtol */
2747 char *s, *p;
2748 int flag = 0, n = 0;
2749 char fullname[CKMAXPATH+1];
2750 int devnull = 0;
2751 int acase = 0;
2752 if (!name2) return;
2753 if (!name) name = "";
2754
2755 debug(F111,"nzrtol name",name,fncnv);
2756
2757 #ifdef DTILDE
2758 s = name;
2759 if (*s == '~') {
2760 s = tilde_expand(s);
2761 if (!s) s = "";
2762 if (*s) name = s;
2763 }
2764 #endif /* DTILDE */
2765
2766 /* Handle the path -- we don't have to convert its format, since */
2767 /* the standard path format and our (UNIX) format are the same. */
2768
2769 fullname[0] = NUL;
2770 devnull = !strcmp(name,"/dev/null");
2771
2772 if (!devnull && fnrpath == PATH_OFF) { /* RECEIVE PATHNAMES OFF */
2773 zstrip(name,&p);
2774 strncpy(fullname,p,CKMAXPATH);
2775 } else if (!devnull && fnrpath == PATH_ABS) { /* REC PATHNAMES ABSOLUTE */
2776 strncpy(fullname,name,CKMAXPATH);
2777 } else if (!devnull && isabsolute(name)) { /* RECEIVE PATHNAMES RELATIVE */
2778 ckmakmsg(fullname,CKMAXPATH,".",name,NULL,NULL);
2779 } else { /* Ditto */
2780 ckstrncpy(fullname,name,CKMAXPATH);
2781 }
2782 fullname[CKMAXPATH] = NUL;
2783 debug(F110,"nzrtol fullname",fullname,0);
2784
2785 #ifndef NOTRUNCATE
2786 /*
2787 The maximum length for any segment of a filename is MAXNAMLEN, defined
2788 above. On some platforms (at least QNX) if a segment exceeds this limit,
2789 the open fails with ENAMETOOLONG, so we must prevent it by truncating each
2790 overlong name segment to the maximum segment length before passing the
2791 name to open(). This must be done even when file names are literal, so as
2792 not to halt a file transfer unnecessarily.
2793 */
2794 {
2795 char buf[CKMAXPATH+1]; /* New temporary buffer on stack */
2796 char *p = fullname; /* Source and */
2797 char *s = buf; /* destination pointers */
2798 int i = 0, n = 0;
2799 debug(F101,"nzrtol sizing MAXNAMLEN","",MAXNAMLEN);
2800 while (*p && n < CKMAXPATH) { /* Copy name to new buffer */
2801 if (++i > MAXNAMLEN) { /* If this segment too long */
2802 while (*p && *p != '/') /* skip past the rest... */
2803 p++;
2804 i = 0; /* and reset counter. */
2805 } else if (*p == '/') { /* End of this segment. */
2806 i = 0; /* Reset counter. */
2807 }
2808 *s++ = *p++; /* Copy this character. */
2809 n++;
2810 }
2811 *s = NUL;
2812 ckstrncpy(fullname,buf,CKMAXPATH); /* Copy back to original buffer. */
2813 debug(F111,"nzrtol sizing",fullname,n);
2814 }
2815 #endif /* NOTRUNCATE */
2816
2817 if (!fncnv || devnull) { /* Not converting */
2818 ckstrncpy(name2,fullname,max); /* We're done. */
2819 return;
2820 }
2821 name = fullname; /* Converting */
2822
2823 p = name2;
2824 for (; *name != '\0' && n < maxnam; name++) {
2825 if (*name > SP) flag = 1; /* Strip leading blanks and controls */
2826 if (flag == 0 && *name < '!')
2827 continue;
2828 if (fncnv > 0) {
2829 if (*name == SP) {
2830 *p++ = '_';
2831 n++;
2832 continue;
2833 }
2834 if (isupper(*name)) /* Check for mixed case */
2835 acase |= 1;
2836 else if (islower(*name))
2837 acase |= 2;
2838 }
2839 *p++ = *name;
2840 n++;
2841 }
2842 *p-- = '\0'; /* Terminate */
2843 while (*p < '!' && p > name2) /* Strip trailing blanks & controls */
2844 *p-- = '\0';
2845
2846 if (*name2 == '\0') { /* Nothing left? */
2847 ckstrncpy(name2,"NONAME",max); /* do this... */
2848 } else if (acase == 1) { /* All uppercase? */
2849 p = name2; /* So convert all letters to lower */
2850 while (*p) {
2851 if (isupper(*p))
2852 *p = tolower(*p);
2853 p++;
2854 }
2855 }
2856 debug(F110,"nzrtol new name",name2,0);
2857 }
2858
2859
2860 /* Z S T R I P -- Strip device & directory name from file specification */
2861
2862 /* Strip pathname from filename "name", return pointer to result in name2 */
2863
2864 static char work[CKMAXPATH+1];
2865
2866 VOID
zstrip(name,name2)2867 zstrip(name,name2) char *name, **name2; {
2868 char *cp, *pp;
2869 int n = 0;
2870
2871 debug(F110,"zstrip before",name,0);
2872 if (!name) { *name2 = ""; return; }
2873 pp = work;
2874 #ifdef DTILDE
2875 /* Strip leading tilde */
2876 if (*name == '~') name++;
2877 debug(F110,"zstrip after tilde-stripping",name,0);
2878 #endif /* DTILDE */
2879 for (cp = name; *cp; cp++) {
2880 if (ISDIRSEP(*cp)) {
2881 pp = work;
2882 n = 0;
2883 } else {
2884 *pp++ = *cp;
2885 if (n++ >= CKMAXPATH)
2886 break;
2887 }
2888 }
2889 *pp = '\0'; /* Terminate the string */
2890 *name2 = work;
2891 debug(F110,"zstrip after",*name2,0);
2892 }
2893
2894 /* Z L T O R -- Local TO Remote */
2895
2896 VOID
zltor(name,name2)2897 zltor(name,name2) char *name, *name2; {
2898 nzltor(name,name2,1,0,CKMAXPATH);
2899 }
2900
2901 /* N Z L T O R -- New Local TO Remote */
2902
2903 /*
2904 fncnv = 0 for no conversion, > 0 for regular conversion, < 0 for minimal.
2905 */
2906 VOID
nzltor(name,name2,fncnv,fnspath,max)2907 nzltor(name,name2,fncnv,fnspath,max)
2908 char *name, *name2; int fncnv, fnspath, max;
2909 { /* nzltor */
2910 char *cp, *pp;
2911 #ifdef COMMENT
2912 int dc = 0;
2913 #endif /* COMMENT */
2914 int n = 0;
2915 char *dotp = NULL;
2916 char *dirp = NULL;
2917 char fullname[CKMAXPATH+1];
2918 char *p;
2919 CHAR c;
2920
2921 #ifndef NOCSETS
2922 extern int fcharset, /* tcharset, */ language;
2923 int langsv;
2924 _PROTOTYP ( CHAR (*sxo), (CHAR) ) = NULL; /* Translation functions */
2925 #ifdef CK_ANSIC
2926 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])(CHAR);
2927 #else
2928 extern CHAR (*xls[MAXTCSETS+1][MAXFCSETS+1])();
2929 #endif /* CK_ANSIC */
2930 langsv = language;
2931 language = L_USASCII;
2932 #ifdef COMMENT
2933 /* Proper translation of filenames must be done elsewhere */
2934 n = tcharset ? tcharset : TC_USASCII;
2935 sxo = xls[n][fcharset];
2936 #else
2937 sxo = xls[TC_USASCII][fcharset];
2938 #endif /* COMMENT */
2939 #endif /* NOCSETS */
2940
2941 debug(F110,"nzltor name",name,0);
2942
2943 /* Handle pathname */
2944
2945 fullname[0] = NUL;
2946 if (fnspath == PATH_OFF) { /* PATHNAMES OFF */
2947 zstrip(name,&p);
2948 ckstrncpy(fullname,p,CKMAXPATH);
2949 } else { /* PATHNAMES RELATIVE or ABSOLUTE */
2950 char * p = name;
2951 while (1) {
2952 if (!strncmp(p,"../",3))
2953 p += 3;
2954 else if (!strncmp(p,"./",2))
2955 p += 2;
2956 else
2957 break;
2958 }
2959 if (fnspath == PATH_ABS) { /* ABSOLUTE */
2960 zfnqfp(p,CKMAXPATH,fullname);
2961 } else { /* RELATIVE */
2962 ckstrncpy(fullname,p,CKMAXPATH);
2963 }
2964 }
2965 debug(F110,"nzltor fullname",fullname,0);
2966
2967 if (!fncnv) { /* Not converting */
2968 ckstrncpy(name2,fullname,max); /* We're done. */
2969 #ifndef NOCSETS
2970 langsv = language;
2971 #endif /* NOCSETS */
2972 return;
2973 }
2974 name = fullname; /* Converting */
2975
2976 #ifdef aegis
2977 char *namechars;
2978 int tilde = 0, bslash = 0;
2979
2980 if ((namechars = getenv("NAMECHARS")) != NULL) {
2981 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
2982 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
2983 } else {
2984 tilde = '~';
2985 bslash = '\\';
2986 }
2987 #endif /* aegis */
2988
2989 pp = work; /* Output buffer */
2990 for (cp = name, n = 0; *cp && n < max; cp++,n++) { /* Convert name chars */
2991 c = *cp;
2992 #ifndef NOCSETS
2993 if (sxo) c = (*sxo)(c); /* Convert to ASCII */
2994 #endif /* NOCSETS */
2995 if (fncnv > 0 && islower(c)) /* Uppercase letters */
2996 *pp++ = toupper(c); /* Change tilde to hyphen */
2997 else if (c == '~')
2998 *pp++ = '-';
2999 else if (fncnv > 0 && c == '#') /* Change number sign to 'X' */
3000 *pp++ = 'X';
3001 else if (c == '*' || c == '?') /* Change wildcard chars to 'X' */
3002 *pp++ = 'X';
3003 else if (c == ' ') /* Change space to underscore */
3004 *pp++ = '_';
3005 else if (c < ' ') /* Change controls to 'X' */
3006 *pp++ = 'X';
3007 else if (fncnv > 0 && c == '.') { /* Change dot to underscore */
3008 dotp = pp; /* Remember where we last did this */
3009 *pp++ = '_';
3010 } else {
3011 if (c == '/')
3012 dirp = pp;
3013 *pp++ = c;
3014 }
3015 }
3016 *pp = NUL; /* Tie it off. */
3017 #ifdef COMMENT
3018 if (dotp) *dotp = '.'; /* Restore last dot (if any) */
3019 #else
3020 if (dotp > dirp) *dotp = '.'; /* Restore last dot in file name */
3021 #endif /* COMMENT */
3022 cp = name2; /* If nothing before dot, */
3023 if (*work == '.') *cp++ = 'X'; /* insert 'X' */
3024 ckstrncpy(cp,work,max);
3025 #ifndef NOCSETS
3026 language = langsv;
3027 #endif /* NOCSETS */
3028 debug(F110,"nzltor name2",name2,0);
3029 }
3030
3031
3032 /* Z C H D I R -- Change directory */
3033 /*
3034 Call with:
3035 dirnam = pointer to name of directory to change to,
3036 which may be "" or NULL to indicate user's home directory.
3037 Returns:
3038 0 on failure
3039 1 on success
3040 */
3041 int
zchdir(dirnam)3042 zchdir(dirnam) char *dirnam; {
3043 char *hd, *sp;
3044 #ifdef IKSDB
3045 _PROTOTYP (int slotdir,(char *,char *));
3046 #endif /* IKSDB */
3047 #ifndef NOSPL
3048 extern struct mtab *mactab; /* Main macro table */
3049 extern int nmac; /* Number of macros */
3050 #endif /* NOSPL */
3051
3052 debug(F110,"zchdir",dirnam,0);
3053 if (!dirnam) dirnam = "";
3054 if (!*dirnam) /* If argument is null or empty, */
3055 dirnam = zhome(); /* use user's home directory. */
3056 sp = dirnam;
3057 debug(F110,"zchdir 2",dirnam,0);
3058
3059 #ifdef DTILDE
3060 hd = tilde_expand(dirnam); /* Attempt to expand tilde */
3061 if (!hd) hd = "";
3062 if (*hd == '\0') hd = dirnam; /* in directory name. */
3063 #else
3064 hd = dirnam;
3065 #endif /* DTILDE */
3066 debug(F110,"zchdir 3",hd,0);
3067
3068 #ifdef CKROOT
3069 debug(F111,"zchdir setroot",ckroot,ckrootset);
3070 if (ckrootset) if (!zinroot(hd)) {
3071 debug(F110,"zchdir setroot violation",hd,0);
3072 return(0);
3073 }
3074 #endif /* CKROOT */
3075
3076 #ifdef pdp11
3077 /* Just to save some space */
3078 return((chdir(hd) == 0) ? 1 : 0);
3079 #else
3080 if (chdir(hd) == 0) { /* Try to cd */
3081 #ifdef IKSDB
3082 #ifdef CK_LOGIN
3083 if (inserver && ikdbopen)
3084 slotdir(isguest ? anonroot : "", zgtdir());
3085 #endif /* CK_LOGIN */
3086 #endif /* IKSDB */
3087
3088 #ifndef NOSPL
3089 if (nmac) { /* Any macros defined? */
3090 int k; /* Yes */
3091 static int on_cd = 0;
3092 if (!on_cd) {
3093 on_cd = 1;
3094 k = mlook(mactab,"on_cd",nmac); /* Look this up */
3095 if (k >= 0) { /* If found, */
3096 if (dodo(k,zgtdir(),0) > -1) /* set it up, */
3097 parser(1); /* and execute it */
3098 }
3099 on_cd = 0;
3100 }
3101 }
3102 #endif /* NOSPL */
3103 return(1);
3104 }
3105 return(0);
3106 #endif /* pdp11 */
3107 }
3108
3109 int
3110 #ifdef CK_ANSIC
zchkpid(unsigned long xpid)3111 zchkpid(unsigned long xpid)
3112 #else
3113 zchkpid(xpid) unsigned long xpid;
3114 #endif /* CK_ANSIC */
3115 {
3116 return((kill((PID_T)xpid,0) < 0) ? 0 : 1);
3117 }
3118
3119
3120 /* Z H O M E -- Return pointer to user's home directory */
3121
3122 static char * zhomdir = NULL;
3123
3124 char *
zhome()3125 zhome() {
3126 char * home;
3127
3128 #ifdef CKROOT
3129 if (ckrootset)
3130 return((char *)ckroot);
3131 #endif /* CKROOT */
3132
3133 #ifdef Plan9
3134 home = getenv("home");
3135 #else
3136 home = getenv("HOME");
3137 #endif /* Plan9 */
3138 makestr(&zhomdir,home);
3139 return(home ? zhomdir : ".");
3140 }
3141
3142 /* Z G T D I R -- Returns a pointer to the current directory */
3143
3144 /*
3145 The "preferred" interface for getting the current directory in modern UNIX
3146 is getcwd() [POSIX 1003.1 5.2.2]. However, on certain platforms (such as
3147 SunOS), it is implemented by forking a shell, feeding it the pwd command,
3148 and returning the result, which is not only inefficient but also can result
3149 in stray messages to the terminal. In such cases -- as well as when
3150 getcwd() is not available at all -- getwd() can be used instead by defining
3151 USE_GETWD. However, note that getwd() provides no buffer-length argument
3152 and therefore no safeguard against memory leaks.
3153 */
3154 #ifndef USE_GETWD
3155 #ifdef BSD42
3156 #define USE_GETWD
3157 #else
3158 #ifdef SUNOS4
3159 #define USE_GETWD
3160 #endif /* SUNOS4 */
3161 #endif /* BSD42 */
3162 #endif /* USE_GETWD */
3163
3164 #ifdef pdp11
3165 #define CWDBL 80 /* Save every byte we can... */
3166 #else
3167 #define CWDBL CKMAXPATH
3168 #endif /* pdp11 */
3169 static char cwdbuf[CWDBL+2];
3170 /*
3171 NOTE: The getcwd() prototypes are commented out on purpose. If you get
3172 compile-time warnings, search through your system's header files to see
3173 which one has the needed prototype, and #include it. Usually it is
3174 <unistd.h>. See the section for including <unistd.h> in ckcdeb.h and
3175 make any needed adjustments there (and report them).
3176 */
3177 char *
zgtdir()3178 zgtdir() {
3179 char * buf = cwdbuf;
3180 char * s;
3181
3182 #ifdef USE_GETWD
3183 extern char *getwd();
3184 s = getwd(buf);
3185 debug(F110,"zgtdir BSD4 getwd()",s,0);
3186 if (!s) s = "./";
3187 return(s);
3188 #else
3189 #ifdef BSD44
3190 #ifdef DCLGETCWD
3191 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3192 #endif /* DCLGETCWD */
3193 debug(F101,"zgtdir BSD44 CWDBL","",CWDBL);
3194 s = getcwd(buf,CWDBL);
3195 if (!s) s = "./";
3196 return(s);
3197 #else
3198 #ifdef MINIX2
3199 #ifdef DCLGETCWD
3200 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3201 #endif /* DCLGETCWD */
3202 debug(F101,"zgtdir MINIX2 CWDBL","",CWDBL);
3203 s = getcwd(buf,CWDBL);
3204 if (!s) s = "./";
3205 return(s);
3206 #else
3207 #ifdef SVORPOSIX
3208 #ifdef COMMENT
3209 /* This non-ANSI prototype can be fatal at runtime! (e.g. in SCO3.2v5.0.5). */
3210 /* Anyway it's already prototyped in some header file that we have included. */
3211 extern char *getcwd();
3212 #else
3213 #ifdef DCLGETCWD
3214 _PROTOTYP( char * getcwd, (char *, SIZE_T) );
3215 #endif /* DCLGETCWD */
3216 #endif /* COMMENT */
3217 debug(F101,"zgtdir SVORPOSIX CWDBL","",CWDBL);
3218 s = getcwd(buf,CWDBL);
3219 if (!s) s = "./";
3220 return(s);
3221 #else
3222 #ifdef COHERENT
3223 #ifdef _I386
3224 #ifdef DCLGETCWD
3225 extern char *getcwd();
3226 #endif /* DCLGETCWD */
3227 debug(F101,"zgtdir COHERENT _I386 CWDBL","",CWDBL);
3228 s = getcwd(buf,CWDBL);
3229 if (!s) s = "./";
3230 return(s);
3231 #else
3232 extern char *getwd();
3233 debug(F101,"zgtdir COHERENT CWDBL","",CWDBL);
3234 s = getwd(buf);
3235 if (!s) s = "./";
3236 return(s);
3237 #endif /* _I386 */
3238 #else
3239 #ifdef SUNOS4
3240 debug(F101,"zgtdir SUNOS CWDBL","",CWDBL);
3241 s = getcwd(buf,CWDBL);
3242 if (!s) s = "./";
3243 return(s);
3244 #else
3245 return("./");
3246 #endif /* SUNOS4 */
3247 #endif /* COHERENT */
3248 #endif /* SYSVORPOSIX */
3249 #endif /* MINIX2 */
3250 #endif /* BSD44 */
3251 #endif /* USE_GETWD */
3252 }
3253
3254 /* Z X C M D -- Run a system command so its output can be read like a file */
3255
3256 #ifndef NOPUSH
3257 int
zxcmd(filnum,comand)3258 zxcmd(filnum,comand) int filnum; char *comand; {
3259 int out;
3260 int pipes[2];
3261 extern int kactive; /* From ckcpro.w and ckcmai.c */
3262
3263 if (nopush) {
3264 debug(F100,"zxcmd fails: nopush","",0);
3265 return(-1);
3266 }
3267 debug(F111,"zxcmd",comand,filnum);
3268 if (chkfn(filnum) < 0) return(-1); /* Need a valid Kermit file number. */
3269 if (filnum == ZSTDIO || filnum == ZCTERM) /* But not one of these. */
3270 return(0);
3271
3272 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3273 debug(F101,"zxcmd out",comand,out);
3274
3275 /* Output to a command */
3276
3277 if (out) { /* Need popen() to do this. */
3278 ckstrncpy(fullname,"(pipe)",CKMAXPATH);
3279 #ifdef NOPOPEN
3280 return(0); /* no popen(), fail. */
3281 #else
3282 /* Use popen() to run the command. */
3283
3284 #ifdef _POSIX_SOURCE
3285 /* Strictly speaking, popen() is not available in POSIX.1 */
3286 #define DCLPOPEN
3287 #endif /* _POSIX_SOURCE */
3288
3289 debug(F110,"zxcmd out",comand,0);
3290
3291 if (priv_chk()) {
3292 debug(F100,"zxcmd priv_chk failed","",0);
3293 return(0);
3294 }
3295 errno = 0;
3296 fp[filnum] = popen(comand,"w");
3297 debug(F111,"zxcmd popen",fp[filnum] ? "OK" : "Failed", errno);
3298 if (fp[filnum] == NULL)
3299 return(0);
3300 #ifdef COMMENT
3301 /* I wonder what this is all about... */
3302 close(pipes[0]); /* Don't need the input side */
3303 fp[filnum] = fdopen(pipes[1],"w"); /* Open output stream. */
3304 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3305 #endif /* COMMENT */
3306 ispipe[filnum] = 1;
3307 zoutcnt = 0; /* (PWP) reset input buffer */
3308 zoutptr = zoutbuffer;
3309 return(1);
3310 #endif /* NOPOPEN */
3311 }
3312
3313 /* Input from a command */
3314
3315 #ifdef SNI541
3316 /* SINIX-L 5.41 does not like fdopen() */
3317 return(0);
3318 #else
3319 if (pipe(pipes) != 0) {
3320 debug(F100,"zxcmd pipe failure","",0);
3321 return(0); /* can't make pipe, fail */
3322 }
3323
3324 /* Create a fork in which to run the named process */
3325
3326 if ((
3327 #ifdef aegis
3328 pid = vfork() /* child */
3329 #else
3330 pid = fork() /* child */
3331 #endif /* aegis */
3332 ) == 0) {
3333
3334 /* We're in the fork. */
3335
3336 char *shpath, *shname, *shptr; /* Find user's preferred shell */
3337 #ifndef aegis
3338 struct passwd *p;
3339 char *defshell;
3340 #ifdef HPUX10 /* Default shell */
3341 defshell = "/usr/bin/sh";
3342 #else
3343 #ifdef Plan9
3344 defshell = "/bin/rc";
3345 #else
3346 defshell = "/bin/sh";
3347 #endif /* Plan9 */
3348 #endif /* HPUX10 */
3349 #endif /* aegis */
3350 if (priv_can()) exit(1); /* Turn off any privileges! */
3351 debug(F101,"zxcmd pid","",pid);
3352 close(pipes[0]); /* close input side of pipe */
3353 close(0); /* close stdin */
3354 if (open("/dev/null",0) < 0) return(0); /* replace input by null */
3355 #ifndef OXOS
3356 #ifndef SVORPOSIX
3357 dup2(pipes[1],1); /* BSD: replace stdout & stderr */
3358 dup2(pipes[1],2); /* by the pipe */
3359 #else
3360 close(1); /* AT&T: close stdout */
3361 if (dup(pipes[1]) != 1) /* Send stdout to the pipe */
3362 return(0);
3363 close(2); /* Send stderr to the pipe */
3364 if (dup(pipes[1]) != 2)
3365 return(0);
3366 #endif /* SVORPOSIX */
3367 #else /* OXOS */
3368 dup2(pipes[1],1);
3369 dup2(pipes[1],2);
3370 #endif /* OXOS */
3371 close(pipes[1]); /* Don't need this any more. */
3372
3373 #ifdef aegis
3374 if ((shpath = getenv("SERVERSHELL")) == NULL)
3375 shpath = "/bin/sh";
3376 #else
3377 shpath = getenv("SHELL"); /* What shell? */
3378 if (shpath == NULL) {
3379 p = getpwuid( real_uid() ); /* Get login data */
3380 /* debug(F111,"zxcmd shpath","getpwuid()",p); */
3381 if (p == (struct passwd *)NULL || !*(p->pw_shell))
3382 shpath = defshell;
3383 else shpath = p->pw_shell;
3384 }
3385 #endif /* aegis */
3386 shptr = shname = shpath;
3387 while (*shptr != '\0')
3388 if (*shptr++ == '/')
3389 shname = shptr;
3390 debug(F110,shpath,shname,0);
3391 restorsigs(); /* Restore ignored signals */
3392 execl(shpath,shname,"-c",comand,(char *)NULL); /* Execute the cmd */
3393 exit(0); /* just punt if it failed. */
3394 } else if (pid == (PID_T) -1) {
3395 debug(F100,"zxcmd fork failure","",0);
3396 return(0);
3397 }
3398 debug(F101,"zxcmd pid","",pid);
3399 close(pipes[1]); /* Don't need the output side */
3400 ispipe[filnum] = 1; /* Remember it's a pipe */
3401 fp[filnum] = fdopen(pipes[0],"r"); /* Open a stream for input. */
3402
3403 #ifdef DONDELAY
3404 #ifdef SELECT
3405 if (filnum == ZIFILE && kactive) { /* Make pipe reads nonblocking */
3406 int flags, x;
3407 if ((flags = fcntl(fileno(fp[filnum]),F_GETFL,0)) > -1) {
3408 debug(F101,"zxcmd fcntl 1 pipe flags","",flags);
3409 x = fcntl(fileno(fp[filnum]),F_SETFL, flags |
3410 #ifdef QNX
3411 O_NONBLOCK
3412 #else
3413 O_NDELAY
3414 #endif /* QNX */
3415 );
3416 debug(F101,"zxcmd fcntl 2 result","",x);
3417 }
3418 }
3419 #endif /* SELECT */
3420 #endif /* DONDELAY */
3421 #endif /* SNI541 */
3422 fp[ZSYSFN] = fp[filnum]; /* Remember. */
3423 zincnt = 0; /* (PWP) reset input buffer */
3424 zinptr = zinbuffer;
3425 fullname[0] = '\0';
3426 return(1);
3427 } /* zxcmd */
3428
3429 /* Z C L O S F - wait for the child fork to terminate and close the pipe. */
3430
3431 /* Used internally by zclose - returns -1 on failure, 1 on success. */
3432
3433 int
zclosf(filnum)3434 zclosf(filnum) int filnum; {
3435 int wstat, out;
3436 int statusp;
3437
3438 debug(F101,"zclosf filnum","",filnum);
3439 out = (filnum == ZIFILE || filnum == ZRFILE) ? 0 : 1 ;
3440 debug(F101,"zclosf out","",out);
3441
3442 #ifndef NOPOPEN
3443 if (ispipe[filnum]
3444 /* In UNIX we use popen() only for output files */
3445 && out
3446 ) {
3447 int x;
3448 x = pclose(fp[filnum]);
3449 pexitstat = x >> 8;
3450 debug(F101,"zclosf pclose","",x);
3451 debug(F101,"zclosf pexitstat","",pexitstat);
3452 fp[filnum] = fp[ZSYSFN] = NULL;
3453 ispipe[filnum] = 0;
3454 return((x != 0) ? -1 : 1);
3455 }
3456 #endif /* NOPOPEN */
3457 /* debug(F101,"zclosf fp[filnum]","", fp[filnum]); */
3458 /* debug(F101,"zclosf fp[ZSYSFN]","", fp[ZSYSFN]); */
3459
3460 if (pid != (PID_T) 0) {
3461 debug(F101,"zclosf killing pid","",pid);
3462 #ifdef Plan9
3463 kill(pid, SIGKILL);
3464 #else
3465 kill(pid,9);
3466 #endif /* Plan9 */
3467
3468 #ifndef CK_CHILD
3469 /*
3470 This is the original code (before 20 April 1997) and has proven totally
3471 portable. But it does not give us the process's return code.
3472 */
3473 while ((wstat = wait((WAIT_T *)0)) != pid && wstat != -1) ;
3474 #else
3475 /* Here we try to get the return code. Let's hope this is portable too. */
3476 while ((wstat = wait(&statusp)) != pid && wstat != -1) ;
3477 pexitstat = (statusp & 0xff) ? statusp : statusp >> 8;
3478 debug(F101,"zclosf wait statusp","",statusp);
3479 debug(F101,"zclosf wait pexitstat","",pexitstat);
3480 #endif /* CK_CHILD */
3481 pid = 0;
3482 }
3483 fclose(fp[filnum]);
3484 fp[filnum] = fp[ZSYSFN] = NULL;
3485
3486 ispipe[filnum] = 0;
3487 /* debug(F101,"zclosf fp[filnum]","",fp[filnum]); */
3488 #ifdef CK_CHILD
3489 return(pexitstat == 0 ? 1 : -1);
3490 #else
3491 return(1);
3492 #endif /* CK_CHILD */
3493 }
3494
3495 #else /* NOPUSH */
3496
3497 int
zxcmd(filnum,comand)3498 zxcmd(filnum,comand) int filnum; char *comand; {
3499 return(0);
3500 }
3501 int
zclosf(filnum)3502 zclosf(filnum) int filnum; {
3503 return(EOF);
3504 }
3505 #endif /* NOPUSH */
3506
3507
3508 /* Z X P A N D -- Expand a wildcard string into an array of strings */
3509 /*
3510 As of C-Kermit 7.0, this API is obsolete, replaced by nzxpand(), and this
3511 function is only used internally. See nzxpand() below.
3512
3513 Returns the number of files that match fnarg, with data structures set up
3514 so that first file (if any) will be returned by the next znext() call.
3515
3516 Depends on external variable wildxpand: 0 means we expand wildcards
3517 internally, nonzero means we call the shell to do it.
3518
3519 AND in C-Kermit 8.0.212 and later, on extern wildena: 1 means wildcards
3520 are enabled, 0 means disabled, the characters are taken literally.
3521 */
3522 static int xdironly = 0;
3523 static int xfilonly = 0;
3524 static int xmatchdot = 0;
3525 static int xrecursive = 0;
3526 static int xnobackup = 0;
3527 static int xnolinks = 0;
3528
3529 static char *freeptr = NULL, **resptr = NULL; /* Copies of caller's args */
3530 static int remlen; /* Remaining space in caller's array */
3531 static int numfnd = 0; /* Number of matches found */
3532
3533 #define MINSPACE 1024
3534
3535 static int
initspace(resarry,len)3536 initspace(resarry,len) char * resarry[]; int len; {
3537 #ifdef DYNAMIC
3538 if (len < MINSPACE) len = MINSPACE;
3539 if (!sspace) { /* Need to allocate string space? */
3540 while (len >= MINSPACE) {
3541 if ((sspace = malloc(len+2))) { /* Got it. */
3542 debug(F101,"fgen string space","",len);
3543 break;
3544 }
3545 len = (len / 2) + (len / 4); /* Didn't, reduce by 3/4 */
3546 }
3547 if (len <= MINSPACE) { /* Did we get it? */
3548 fprintf(stderr,"fgen can't malloc string space\n");
3549 return(-1);
3550 }
3551 ssplen = len;
3552 }
3553 #endif /* DYNAMIC */
3554
3555 freeptr = sspace; /* This is where matches are copied. */
3556 resptr = resarry; /* Static copies of these so */
3557 remlen = len; /* recursive calls can alter them. */
3558 debug(F101,"initspace ssplen","",ssplen);
3559 return(0);
3560 }
3561
3562 /*
3563 Z S E T F I L -- Query or change the size of file list buffers.
3564
3565 fc = 1: Change current string space to n, return new size.
3566 fc = 2: Return current string space size.
3567 fc = 3: Change current maxnames to n, return new maxnames.
3568 fc = 4: Return current maxnames.
3569 Returns < 0 on error.
3570 */
3571 int
zsetfil(n,fc)3572 zsetfil(n, fc) int n, fc; {
3573 #ifdef DYNAMIC
3574 switch (fc) {
3575 case 1: /* Stringspace */
3576 if (sspace) {
3577 free(sspace);
3578 sspace = NULL;
3579 }
3580 if (initspace(mtchs,n) < 0)
3581 return(-1);
3582 case 2: /* Fall thru deliberately */
3583 return(ssplen);
3584 case 3: /* Listsize */
3585 if (mtchs) {
3586 free((char *)mtchs);
3587 mtchs = NULL;
3588 }
3589 mtchs = (char **)malloc(n * sizeof(char *));
3590 if (!mtchs)
3591 return(-1);
3592 maxnames = n;
3593 case 4: /* Fall thru deliberately */
3594 return(maxnames);
3595 }
3596 #endif /* DYNAMIC */
3597 return(-1);
3598 }
3599
3600
3601
3602 #ifndef NONZXPAND
3603 #ifndef pdp11
3604 static
3605 #endif /* pdp11 */
3606 #endif /* NONZXPAND */
3607 int
zxpand(fnarg)3608 zxpand(fnarg) char *fnarg; {
3609 extern int diractive;
3610 char fnbuf[CKMAXPATH+8], * fn, * p;
3611
3612 #ifdef DTILDE /* Built with tilde-expansion? */
3613 char *tnam;
3614 #endif /* DTILDE */
3615 int x;
3616 int haveonedir = 0;
3617
3618 if (!fnarg) { /* If no argument provided */
3619 nxpand = fcount = 0;
3620 return(0); /* Return zero files found */
3621 }
3622 debug(F110,"zxpand entry",fnarg,0);
3623 debug(F101,"zxpand xdironly","",xdironly);
3624 debug(F101,"zxpand xfilonly","",xfilonly);
3625
3626 if (!*fnarg) { /* If no argument provided */
3627 nxpand = fcount = 0;
3628 return(0); /* Return zero files found */
3629 }
3630
3631 #ifdef CKROOT
3632 debug(F111,"zxpand setroot",ckroot,ckrootset);
3633 if (ckrootset) if (!zinroot(fnarg)) {
3634 debug(F110,"zxpand setroot violation",fnarg,0);
3635 nxpand = fcount = 0;
3636 return(0);
3637 }
3638 #endif /* CKROOT */
3639
3640 #ifdef COMMENT
3641 /*
3642 This would have been perfect, except it makes us return fully qualified
3643 pathnames for all files.
3644 */
3645 zfnqfp(fnarg,CKMAXPATH,fnbuf);
3646 debug(F110,"zxpand zfnqfp",fnbuf,0);
3647 s = zgtdir();
3648 debug(F110,"zxpand zgtdir",s,0);
3649 p = fnbuf;
3650 while (*p && *s) /* Make it relative */
3651 if (*s++ != *p++)
3652 break;
3653 fn = (*s) ? fnbuf : p;
3654 debug(F110,"zxpand fn 0",fn,0);
3655 if (!*fn) {
3656 fn = fnbuf;
3657 fnbuf[0] = '*';
3658 fnbuf[1] = '\0';
3659 }
3660 debug(F110,"zxpand fn 0.5",fn,0);
3661 #else
3662 #ifdef DTILDE /* Built with tilde-expansion? */
3663 if (*fnarg == '~') { /* Starts with tilde? */
3664 tnam = tilde_expand(fnarg); /* Try to expand it. */
3665 ckstrncpy(fnbuf,tnam,CKMAXPATH);
3666 } else
3667 #endif /* DTILDE */
3668 ckstrncpy(fnbuf,fnarg,CKMAXPATH);
3669 fn = fnbuf; /* Point to what we'll work with */
3670 #endif /* COMMENT */
3671 debug(F110,"zxpand fn 1",fn,0);
3672
3673 if (!*fn) /* But make sure something is there */
3674 return(0);
3675
3676 p = fn + (int)strlen(fn) - 1;
3677 if (*p == '/') { /* If last char = / it must be a dir */
3678 if (!xfilonly && !iswild(p)) haveonedir++;
3679 ckstrncat(fn, "*", CKMAXPATH+8); /* so append '*' */
3680 } else if (p > fn) { /* If ends in "/." */
3681 if (*(p-1) == '/' && *p == '.') /* change '.' to '*' */
3682 *p = '*';
3683 } else if (p == fn) { /* If it's '.' alone */
3684 if (*p == '.') /* change '.' to '*' */
3685 *p = '*';
3686 }
3687 debug(F110,"zxpand fn 2",fn,0);
3688 x = isdir(fn); /* Is it a directory? */
3689 debug(F111,"zxpand isdir 1",fn,x);
3690 if (x) { /* If so, make it into a wildcard */
3691 if (!xfilonly && !iswild(p))
3692 haveonedir++;
3693 if ((x = strlen(fn)) > 0) {
3694 if (!ISDIRSEP(fn[x-1]))
3695 fn[x++] = DIRSEP;
3696 fn[x++] = '*';
3697 fn[x] = '\0';
3698 }
3699 }
3700 debug(F111,"zxpand fn 3 haveonedir",fn,haveonedir);
3701 /*
3702 The following allows us to parse a single directory name without opening
3703 the directory and looking at its contents. The diractive flag is a horrible
3704 hack (especially since DIR /NORECURSIVE turns it off), but otherwise we'd
3705 have to change the API.
3706 */
3707 debug(F111,"zxpand fn 3 diractive",fn,diractive);
3708 if (!diractive && haveonedir) {
3709 fcount = 0;
3710 if (!mtchs) {
3711 mtchs = (char **)malloc(maxnames * sizeof(*mtchs));
3712 if (!mtchs)
3713 return(nxpand = fcount);
3714 }
3715 fcount = 1;
3716 debug(F110,"zxpand haveonedir A1",fnarg,0);
3717 initspace(mtchs,ssplen);
3718 addresult(fnarg,1);
3719 if (numfnd < 0) return(-1);
3720 mtchptr = mtchs; /* Save pointer for next. */
3721 debug(F111,"zxpand haveonedir A2",*mtchptr,numfnd);
3722 return(nxpand = fcount);
3723 }
3724
3725 #ifndef NOPUSH
3726 if (!nopush && wildxpand) /* Who is expanding wildcards? */
3727 fcount = (mtchs == NULL && /* Shell */
3728 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3729 ? 0
3730 : shxpand(fn,mtchs,maxnames);
3731 else
3732 #endif /* NOPUSH */
3733 fcount = (mtchs == NULL && /* Kermit */
3734 (mtchs = (char **)malloc(maxnames * sizeof(*mtchs))) == NULL)
3735 ? 0
3736 : fgen(fn,mtchs,maxnames); /* Look up the file. */
3737
3738 if (fcount == 0 && haveonedir) {
3739 fcount = 1;
3740 debug(F110,"zxpand haveonedir B",fnarg,0);
3741 addresult(fnarg,1);
3742 if (numfnd < 0) return(-1);
3743 }
3744 mtchptr = mtchs; /* Save pointer for next. */
3745 nxpand = fcount;
3746
3747 #ifdef DEBUG
3748 if (deblog) {
3749 if (fcount > 1)
3750 debug(F111,"zxpand ok",mtchs[0],fcount);
3751 else
3752 debug(F101,"zxpand fcount","",fcount);
3753 }
3754 #endif /* DEBUG */
3755 return(fcount);
3756 }
3757
3758 #ifndef NONZXPAND
3759 /* N Z X P A N D -- Expand a file list, with options. */
3760 /*
3761 Call with:
3762 s = pointer to filename or pattern.
3763 flags = option bits:
3764
3765 flags & ZX_FILONLY Match regular files
3766 flags & ZX_DIRONLY Match directories
3767 flags & ZX_RECURSE Descend through directory tree
3768 flags & ZX_MATCHDOT Match "dot files"
3769 flags & ZX_NOBACKUP Don't match "backup files"
3770 flags & ZX_NOLINKS Don't follow symlinks.
3771
3772 Returns the number of files that match s, with data structures set up
3773 so that first file (if any) will be returned by the next znext() call.
3774 */
3775 int
nzxpand(s,flags)3776 nzxpand(s,flags) char * s; int flags; {
3777 char * p;
3778 int x;
3779
3780 debug(F111,"nzxpand",s,flags);
3781 x = flags & (ZX_DIRONLY|ZX_FILONLY);
3782 xdironly = (x == ZX_DIRONLY);
3783 xfilonly = (x == ZX_FILONLY);
3784 if (xdironly && xfilonly) {
3785 xdironly = 0;
3786 xfilonly = 0;
3787 }
3788 xmatchdot = (flags & ZX_MATCHDOT);
3789 debug(F111,"nzxpand xmatchdot 1",s,xmatchdot);
3790 /* If xmatchdot not set by caller but pattern implies it, set it anyway */
3791 if (!xmatchdot && ((p = ckstrchr(s,'.')))) {
3792 if (p == s && p[1] != '/') {
3793 xmatchdot = 1;
3794 debug(F111,"nzxpand xmatchdot 2",s,xmatchdot);
3795 } else if (p > s) {
3796 xmatchdot = (*(p-1) == ',') || (*(p-1) == '{') || (*(p-1) == '/');
3797 debug(F111,"nzxpand xmatchdot 3",s,xmatchdot);
3798 }
3799 }
3800 xrecursive = (flags & ZX_RECURSE);
3801 xnobackup = (flags & ZX_NOBACKUP);
3802 xnolinks = (flags & ZX_NOLINKS);
3803
3804 #ifdef DEBUG
3805 if (deblog) {
3806 debug(F101,"nzxpand xdironly","",xdironly);
3807 debug(F101,"nzxpand xfilonly","",xfilonly);
3808 debug(F101,"nzxpand xmatchdot","",xmatchdot);
3809 debug(F101,"nzxpand xrecursive","",xrecursive);
3810 debug(F101,"nzxpand xnobackup","",xnobackup);
3811 debug(F101,"nzxpand xnolinks","",xnolinks);
3812 }
3813 #endif /* DEBUG */
3814
3815 x = zxpand(s);
3816 if (x > 1)
3817 sh_sort(mtchs,NULL,x,0,0,1); /* Alphabetize the list */
3818 xdironly = 0;
3819 xfilonly = 0;
3820 xmatchdot = 0;
3821 xrecursive = 0;
3822 xnobackup = 0;
3823 xnolinks = 0;
3824 return(x);
3825 }
3826 #endif /* NONZXPAND */
3827
3828 #ifndef NOZXREWIND
3829 /* Z X R E W I N D -- Rewinds the zxpand() list */
3830
3831 int
zxrewind()3832 zxrewind() {
3833 /* if (!mtchs) return(-1); */
3834 fcount = nxpand;
3835 mtchptr = mtchs;
3836 return(nxpand);
3837 }
3838 #endif /* NOZXREWIND */
3839
3840 /* Z N E X T -- Get name of next file from list created by zxpand(). */
3841 /*
3842 Returns >0 if there's another file, with its name copied into the arg string,
3843 or 0 if no more files in list.
3844 */
3845 int
znext(fn)3846 znext(fn) char *fn; {
3847 if (fcount-- > 0) {
3848 ckstrncpy(fn,*mtchptr++,CKMAXPATH);
3849 } else {
3850 fn[0] = '\0';
3851 }
3852 #ifndef COMMENT
3853 debug(F111,"znext",fn,fcount+1);
3854 return(fcount+1);
3855 #else
3856 debug(F111,"znext",fn,fcount); /* Return 0 if no filename to return */
3857 return(fcount);
3858 #endif /* COMMENT */
3859 }
3860
3861 /* Z C H K S P A -- Check if there is enough space to store the file */
3862
3863 /*
3864 Call with file specification f, size n in bytes.
3865 Returns -1 on error, 0 if not enough space, 1 if enough space.
3866 */
3867 /*ARGSUSED*/
3868 int
3869 #ifdef CK_ANSIC
zchkspa(char * f,CK_OFF_T n)3870 zchkspa(char *f, CK_OFF_T n)
3871 #else
3872 zchkspa(f,n) char *f; CK_OFF_T n;
3873 #endif /* CK_ANSIC */
3874 /* zchkspa() */ {
3875 /* In UNIX there is no good (and portable) way. */
3876 return(1); /* Always say OK. */
3877 }
3878
3879 #ifdef COMMENT /* (not used) */
3880
3881 /* I S B A C K U P -- Tells if given file has a backup suffix */
3882 /*
3883 Returns:
3884 -1: Invalid argument
3885 0: File does not have a backup suffix
3886 >0: Backup suffix number
3887 */
3888 int
isbackup(fn)3889 isbackup(fn) char * fn; { /* Get backup suffix number */
3890 int i, j, k, x, state, flag;
3891
3892 if (!fn) /* Watch out for null pointers. */
3893 return(-1);
3894 if (!*fn) /* And empty names. */
3895 return(-1);
3896
3897 flag = state = 0;
3898 for (i = (int)strlen(fn) - 1; (!flag && (i > 0)); i--) {
3899 switch (state) {
3900 case 0: /* State 0 - final char */
3901 if (fn[i] == '~') /* Is tilde */
3902 state = 1; /* Switch to next state */
3903 else /* Otherwise */
3904 flag = 1; /* Quit - no backup suffix. */
3905 break;
3906 case 1: /* State 1 - digits */
3907 if (fn[i] == '~' && fn[i-1] == '.') { /* Have suffix */
3908 return(atoi(&fn[i+1]));
3909 } else if (fn[i] >= '0' && fn[i] <= '9') { /* In number part */
3910 continue; /* Keep going */
3911 } else { /* Something else */
3912 flag = 1; /* Not a backup suffix - quit. */
3913 }
3914 break;
3915 }
3916 }
3917 return(0);
3918 }
3919 #endif /* COMMENT */
3920
3921
3922 /* Z N E W N -- Make a new name for the given file */
3923
3924 /*
3925 Given the name, fn, of a file that already exists, this function builds a
3926 new name of the form "<oldname>.~<n>~", where <oldname> is argument name
3927 (fn), and <n> is a version number, one higher than any existing version
3928 number for that file, up to 99999. This format is consistent with that used
3929 by GNU EMACS. If the constructed name is too long for the system's maximum,
3930 enough characters are truncated from the end of <fn> to allow the version
3931 number to fit. If no free version numbers exist between 1 and 99999, a
3932 version number of "xxxx" is used. Returns a pointer to the new name in
3933 argument s.
3934 */
3935 #ifdef pdp11
3936 #define ZNEWNBL 63 /* Name buffer length */
3937 #define ZNEWNMD 3 /* Max digits for version number */
3938 #else
3939 #define ZNEWNBL CKMAXPATH
3940 #define ZNEWNMD 4
3941 #endif /* pdp11 */
3942
3943 #define MAXBUDIGITS 5
3944
3945 static char znewbuf[ZNEWNBL+12];
3946
3947 VOID
znewn(fn,s)3948 znewn(fn,s) char *fn, **s; {
3949 char * buf; /* Pointer to buffer for new name */
3950 char * xp, * namepart = NULL; /* Pointer to filename part */
3951 struct zfnfp * fnfp; /* znfqfp() result struct pointer */
3952 int d = 0, t, fnlen, buflen;
3953 int n, i, k, flag, state;
3954 int max = MAXNAMLEN; /* Maximum name length */
3955 char * dname = NULL;
3956
3957 buf = znewbuf;
3958 *s = NULL; /* Initialize return value */
3959 if (!fn) fn = ""; /* Check filename argument */
3960 i = strlen(fn);
3961
3962 /* If incoming file already has a backup suffix, remove it. */
3963 /* Then we'll tack a new on later, which will be the highest for this file. */
3964
3965 if (i <= max && i > 0 && fn[i-1] == '~') {
3966 char * p;
3967 i--;
3968 debug(F111,"znewn suffix removal",fn,i);
3969 if ((dname = (char *)malloc(i+1))) {
3970 ckstrncpy(dname,fn,i+1);
3971 p = dname;
3972 for (flag = state = 0; (!flag && (i > 0)); i--) {
3973 switch (state) {
3974 case 0: /* State 0 - final char */
3975 if (p[i] == '~') /* Is tilde */
3976 state = 1; /* Switch to next state */
3977 else /* Otherwise */
3978 flag = 1; /* Quit - no backup suffix. */
3979 break;
3980 case 1: /* State 1 - digits */
3981 if (p[i] == '~' && p[i-1] == '.') { /* Have suffix */
3982 p[i-1] = NUL; /* Trim it */
3983 fn = dname;
3984 debug(F111,"znewn suffix removal 2",fn,i);
3985 flag = 1; /* done */
3986 } else if (p[i] >= '0' && p[i] <= '9') { /* Number part */
3987 continue; /* Keep going */
3988 } else { /* Something else */
3989 flag = 1; /* Not a backup suffix - quit. */
3990 }
3991 break;
3992 }
3993 }
3994 }
3995 }
3996 if ((fnlen = strlen(fn)) < 1) { /* Get length */
3997 if (dname) free(dname);
3998 return;
3999 }
4000 debug(F111,"znewn",fn,fnlen);
4001
4002 debug(F101,"znewn max 1","",max);
4003 if (max < 14) max = 14; /* Make max reasonable for any UNIX */
4004 if (max > ZNEWNBL) max = ZNEWNBL;
4005 debug(F101,"znewn max 2","",max);
4006
4007 if ((fnfp = zfnqfp(fn, ZNEWNBL, buf))) { /* Get fully qualified name */
4008 namepart = fnfp->fname; /* Isolate the filename */
4009 k = strlen(fn); /* Length of name part */
4010 debug(F111,"znewn namepart",namepart,k);
4011 } else {
4012 if (dname) free(dname);
4013 return;
4014 }
4015 buflen = fnfp->len; /* Length of fully qualified name */
4016 debug(F111,"znewn len",buf,buflen);
4017
4018 if (k + MAXBUDIGITS + 3 < max) { /* Backup name fits - no overflow */
4019 /* Make pattern for backup names */
4020 ckstrncpy(buf+buflen,".~*~",ZNEWNBL+12-buflen);
4021 n = nzxpand(buf,ZX_FILONLY); /* Expand the pattern */
4022 debug(F111,"znewn A matches",buf,n);
4023 while (n-- > 0) { /* Find any existing name.~n~ files */
4024 xp = *mtchptr++; /* Point at matching name */
4025 t = atoi(xp+buflen+2); /* Get number */
4026 if (t > d) d = t; /* Save d = highest version number */
4027 }
4028 sprintf(buf+buflen,".~%d~",d+1); /* Yes, make "name.~<d+1>~" */
4029 debug(F110,"znewn A newname",buf,0);
4030 } else { /* Backup name would be too long */
4031 int xlen; /* So we have to eat back into it */
4032 int delta;
4033 char buf2[ZNEWNBL+12];
4034
4035 delta = max - k;
4036 debug(F101,"znewn B delta","",delta);
4037
4038 for (i = MAXBUDIGITS; i > 0; i--) { /* In this case the format of */
4039 ckstrncpy(buf2,buf,ZNEWNBL+12); /* the backup name depends on */
4040 xlen = buflen - i - 3 + delta; /* how many digits are in the */
4041 ckstrncpy(buf2+xlen,".~*~",ZNEWNBL+12-xlen); /* backup number */
4042 n = nzxpand(buf2,ZX_FILONLY);
4043 debug(F111,"znewn B matches",buf2,n);
4044 if (n > 0)
4045 break;
4046 }
4047 while (n-- > 0) { /* Find any existing name.~n~ files */
4048 xp = *mtchptr++; /* Point at matching name */
4049 t = atoi(xp+xlen+2); /* Get number */
4050 if (t > d) d = t; /* Save d = highest version number */
4051 }
4052 if (d > 0) /* If the odometer turned over... */
4053 if ((d % 10) == 9) /* back up one space. */
4054 xlen--;
4055 sprintf(buf2+xlen,".~%d~",d+1); /* This just fits */
4056 ckstrncpy(buf,buf2,ZNEWNBL+12); /* (we could be more clever here...) */
4057 debug(F110,"znewn B new name",buf,0);
4058 }
4059 *s = buf; /* Point to new name */
4060 ck_znewn = d+1; /* Also make it available globally */
4061 if (dname) free(dname);
4062 return;
4063 }
4064
4065 /* Z R E N A M E -- Rename a file */
4066 /*
4067 Call with old and new names.
4068 If new name is the name of a directory, the 'old' file is moved to
4069 that directory.
4070 Returns 0 on success, -1 on failure.
4071 */
4072 int
zrename(old,new)4073 zrename(old,new) char *old, *new; {
4074 char *p, *s;
4075 int x;
4076
4077 if (!old) old = "";
4078 if (!new) new = "";
4079 debug(F110,"zrename old",old,0);
4080 debug(F110,"zrename new",new,0);
4081 if (!*old) return(-1);
4082 if (!*new) return(-1);
4083
4084 #ifdef IKSD
4085 #ifdef CK_LOGIN
4086 if (inserver && isguest)
4087 return(-1);
4088 #endif /* CK_LOGIN */
4089 #endif /* IKSD */
4090
4091 #ifdef CKROOT
4092 debug(F111,"zrename setroot",ckroot,ckrootset);
4093 if (ckrootset) {
4094 if (!zinroot(old)) {
4095 debug(F110,"zrename old: setroot violation",old,0);
4096 return(-1);
4097 }
4098 if (!zinroot(new)) {
4099 debug(F110,"zrename new: setroot violation",new,0);
4100 return(-1);
4101 }
4102 }
4103 #endif /* CKROOT */
4104
4105 p = NULL;
4106 s = new;
4107
4108 if (isdir(new)) {
4109 char *q = NULL;
4110 x = strlen(new);
4111 if (!(p = malloc(strlen(new) + strlen(old) + 2)))
4112 return(-1);
4113 strcpy(p,new); /* (safe) Directory part */
4114 if (!ISDIRSEP(*(new+x-1))) /* Separator, if needed */
4115 strcat(p,"/"); /* (safe) */
4116 zstrip(old,&q); /* Strip path part from old name */
4117 strcat(p,q); /* cat to new directory (safe) */
4118 s = p;
4119 debug(F110,"zrename dir",s,0);
4120 }
4121 #ifdef DEBUG
4122 else debug(F110,"zrename no dir",s,0);
4123 #endif /* DEBUG */
4124
4125 #ifdef IKSD
4126 if (inserver && (!ENABLED(en_del))) {
4127 if (zchki(s) > -1) /* Destination file exists? */
4128 return(-1);
4129 }
4130 #endif /* IKSD */
4131
4132 x = -1; /* Return code. */
4133 #ifdef RENAME
4134 /* Atomic, preferred, uses a single system call, rename(), if available. */
4135 x = rename(old,s);
4136 debug(F111,"zrename rename()",old,x);
4137 if (x) x = -1;
4138 #endif /* RENAME */
4139
4140 /* If rename() failed or not available try link()/unlink() */
4141
4142 if (x < 0) {
4143 if (zchko(old) > -1) { /* Requires write access to orignal */
4144 x = link(old,s);
4145 debug(F111,"zrename link()",old,x);
4146 if (x > -1) { /* Make a link with the new name. */
4147 x = unlink(old);
4148 debug(F111,"zrename unlink()",old,x);
4149 }
4150 /* If link/unlink failed copy and delete */
4151 if (x < 0) {
4152 x = zcopy(old,s);
4153 debug(F111,"zrename zcopy()",old,x);
4154 if (x > -1) {
4155 x = zdelet(old);
4156 debug(F111,"zrename zdelet()",old,x);
4157 }
4158 }
4159 }
4160 }
4161 fullname[0] = '\0'; /* Clear this out for next time. */
4162
4163 #ifdef CKSYSLOG
4164 if (ckxsyslog >= SYSLG_FC && ckxlogging) {
4165 zfnqfp(old,CKMAXPATH,fullname);
4166 tmp2[0] = '\0';
4167 zfnqfp(s,CKMAXPATH,tmp2);
4168 if (x > -1)
4169 syslog(LOG_INFO,"file[] %s: renamed to %s ok", fullname, tmp2);
4170 else
4171 syslog(LOG_INFO,"file[] %s: rename to %s failed (%m)",fullname,tmp2);
4172 }
4173 #endif /* CKSYSLOG */
4174
4175 if (p) free(p);
4176 return(x);
4177 }
4178
4179 /* Z C O P Y -- Copy a single file. */
4180 /*
4181 Call with source and destination names.
4182 If destination is a directory, the source file is
4183 copied to that directory with its original name.
4184 Returns:
4185 0 on success.
4186 <0 on failure:
4187 -2 = source file is not a regular file.
4188 -3 = source file not found.
4189 -4 = permission denied.
4190 -5 = source and destination are the same file.
4191 -6 = i/o error.
4192 -1 = other error.
4193 */
4194 int
zcopy(source,destination)4195 zcopy(source,destination) char *source, *destination; {
4196 char *src, *dst; /* Local pointers to filenames */
4197 int x, y, rc; /* Workers */
4198 int in = -1, out = -1; /* i/o file descriptors */
4199 struct stat srcbuf; /* Source file info buffer */
4200 int perms; /* Output file permissions */
4201 char buf[1024]; /* File copying buffer */
4202
4203 if (!source) source = "";
4204 if (!destination) destination = "";
4205
4206 debug(F110,"zcopy src arg",source,0);
4207 debug(F110,"zcopy dst arg",destination,0);
4208
4209 if (!*source) return(-1);
4210 if (!*destination) return(-1);
4211
4212 #ifdef IKSD
4213 #ifdef CK_LOGIN
4214 if (inserver && isguest)
4215 return(-4);
4216 #endif /* CK_LOGIN */
4217 #endif /* IKSD */
4218
4219 #ifdef CKROOT
4220 debug(F111,"zcopy setroot",ckroot,ckrootset);
4221 if (ckrootset) {
4222 if (!zinroot(source)) {
4223 debug(F110,"zcopy source: setroot violation",source,0);
4224 return(-1);
4225 }
4226 if (!zinroot(destination)) {
4227 debug(F110,"zcopy destination: setroot violation",destination,0);
4228 return(-1);
4229 }
4230 }
4231 #endif /* CKROOT */
4232
4233 src = source;
4234 dst = destination;
4235
4236 if (stat(src,&srcbuf) == 0) { /* Get source file info */
4237 struct stat dstbuf; /* Destination file info buffer */
4238 debug(F101,"STAT","",6);
4239 if (stat(dst,&dstbuf) == 0) {
4240 debug(F101,"STAT","",7);
4241 if (srcbuf.st_dev == dstbuf.st_dev)
4242 if (srcbuf.st_ino == dstbuf.st_ino) {
4243 debug(F100,"zcopy files identical: stat()","",0);
4244 return(-5);
4245 }
4246 }
4247 } else { /* stat() failed... */
4248 debug(F101,"STAT","",8);
4249 debug(F111,"source file not found",src,errno);
4250 return(-3);
4251 }
4252 fullname[0] = '\0'; /* Get full pathnames */
4253 if (zfnqfp(source,CKMAXPATH,fullname))
4254 src = fullname;
4255 debug(F110,"zcopy src",src,0);
4256 tmp2[0] = '\0';
4257 if (zfnqfp(destination,CKMAXPATH,tmp2))
4258 dst = tmp2;
4259 debug(F110,"zcopy dst 1",dst,0);
4260 if (!strcmp(src,dst)) { /* Src and dst are same file? */
4261 debug(F100,"zcopy files identical: strcmp()","",0); /* This... */
4262 return(-5); /* should not happen. */
4263 }
4264 if (isdir(src)) { /* Source file is a directory? */
4265 debug(F110,"zcopy source is directory",src,0);
4266 return(-2); /* Fail */
4267 }
4268 if (isdir(dst)) { /* Destination is a directory? */
4269 char *q = NULL; /* Yes, add filename to it. */
4270 x = strlen(dst);
4271 if (x < 1) return(-1);
4272 if (!ISDIRSEP(*(dst+x-1))) { /* Add separator if needed */
4273 tmp2[x++] = '/';
4274 tmp2[x] = '\0';
4275 }
4276 debug(F111,"zcopy dst 2",dst,x);
4277 zstrip(src,&q); /* Strip path part from old name */
4278 ckstrncpy(tmp2+x,q,CKMAXPATH-x); /* Concatenate it to new name */
4279 }
4280 debug(F110,"zcopy dst 3",dst,0);
4281
4282 #ifdef IKSD
4283 if (inserver && (!ENABLED(en_del))) {
4284 if (zchki(dst) > -1) /* Destination file exists? */
4285 return(-4);
4286 }
4287 #endif /* IKSD */
4288
4289 perms = umask(0); /* Get user's umask */
4290 umask(perms); /* Put it back! */
4291 perms ^= 0777; /* Flip the bits */
4292 perms &= 0666; /* Zero execute bits from umask */
4293 perms |= (srcbuf.st_mode & 0111); /* OR in source file's execute bits */
4294 rc = -1; /* Default return code */
4295 errno = 0; /* Reset errno */
4296 in = open(src, O_RDONLY, 0); /* Open source file */
4297 debug(F111,"zcopy open source",src,in);
4298 if (in > -1) { /* If open... */
4299 /* Open destination file */
4300 #ifdef O_TRUNC
4301 out = open(dst, O_WRONLY|O_CREAT|O_TRUNC, perms);
4302 #else
4303 out = open(dst, O_WRONLY|O_CREAT, perms);
4304 #endif /* O_TRUNC */
4305 debug(F111,"zcopy open dest",dst,out);
4306 if (out > -1) { /* If open... */
4307 while ((x = read(in,buf,1024)) > 0) { /* Copy in 1K blocks */
4308 y = write(out,buf,x);
4309 if (y < 0) { /* On write failure */
4310 x = -1;
4311 rc = -6; /* Indicate i/o error */
4312 break;
4313 }
4314 }
4315 debug(F101,"zcopy final read","",x);
4316 debug(F101,"zcopy errno","",errno);
4317 rc = (x == 0) ? 0 : -6; /* In case of read failure */
4318 }
4319 }
4320 if (in > -1) close(in); /* Close files */
4321 if (out > -1) close(out);
4322 if (rc == -1) { /* Set return code */
4323 switch (errno) {
4324 case ENOENT: rc = -3; break;
4325 case EACCES: rc = -4; break;
4326 case EIO: rc = -6;
4327 }
4328 }
4329
4330 #ifdef CKSYSLOG
4331 if (rc > -1 && ckxsyslog >= SYSLG_FC && ckxlogging) {
4332 if (rc)
4333 syslog(LOG_INFO,"file[] %s: copy to %s failed (%m)", fullname, tmp2);
4334 else
4335 syslog(LOG_INFO,"file[] %s: copy to %s ok", fullname, tmp2);
4336 }
4337 #endif /* CKSYSLOG */
4338
4339 return(rc);
4340 }
4341
4342 /* Z S A T T R */
4343 /*
4344 Fills in a Kermit file attribute structure for the file which is to be sent.
4345 Returns 0 on success with the structure filled in, or -1 on failure.
4346 If any string member is null, then it should be ignored.
4347 If any numeric member is -1, then it should be ignored.
4348 */
4349 #ifdef CK_PERMS
4350
4351 #ifdef CK_GPERMS
4352 #undef CK_GPERMS
4353 #endif /* CK_GPERMS */
4354
4355 #ifdef UNIX
4356 #ifndef S_IRUSR
4357 #define S_IRUSR 0400
4358 #endif /* S_IRUSR */
4359 #ifndef S_IWUSR
4360 #define S_IXUSR 0200
4361 #endif /* S_IWUSR */
4362 #ifndef S_IXUSR
4363 #define S_IXUSR 0100
4364 #endif /* S_IXUSR */
4365 #endif /* UNIX */
4366
4367 #ifdef S_IRUSR
4368 #ifdef S_IWUSR
4369 #ifdef S_IXUSR
4370 #define CK_GPERMS
4371 #endif /* S_IXUSR */
4372 #endif /* S_IWUSR */
4373 #endif /* S_IRUSR */
4374
4375 static char gperms[2];
4376
4377 #endif /* CK_GPERMS */
4378
4379 static char lperms[24];
4380
4381 #ifdef CK_PERMS
4382 static char xlperms[24];
4383
4384 /* Z S E T P E R M -- Set permissions of a file */
4385
4386 int
zsetperm(f,code)4387 zsetperm(f,code) char * f; int code; {
4388 int x;
4389 #ifdef CK_SCO32V4
4390 mode_t mask;
4391 #else
4392 int mask;
4393 #endif /* CK_SCO32V4 */
4394 mask = code;
4395 if (inserver && guest) {
4396 debug(F110,"zsetperm guest",f,0);
4397 return(0);
4398 }
4399 x = chmod(f,mask);
4400 if (x < 0) {
4401 debug(F111,"zsetperm error",f,errno);
4402 return(0);
4403 }
4404 debug(F111,"zsetperm ok",f,mask);
4405 return(1);
4406 }
4407
4408 /* Z G P E R M -- Get permissions of a file as an octal string */
4409
4410 char *
zgperm(f)4411 zgperm(f) char *f; {
4412 extern int diractive;
4413 int x; char *s = (char *)xlperms;
4414 struct stat buf;
4415 debug(F110,"zgperm",f,0);
4416 if (!f) return("----------");
4417 if (!*f) return("----------");
4418
4419 #ifdef DTILDE /* Built with tilde-expansion? */
4420 if (*f == '~') { /* Starts with tilde? */
4421 f = tilde_expand(f); /* Try to expand it. */
4422 }
4423 #endif /* DTILDE */
4424
4425 #ifdef CKROOT
4426 debug(F111,"zgperm setroot",ckroot,ckrootset);
4427 if (ckrootset) if (!zinroot(f)) {
4428 debug(F110,"zgperm setroot violation",f,0);
4429 return("----------");
4430 }
4431 #endif /* CKROOT */
4432
4433 #ifdef USE_LSTAT
4434 if (diractive)
4435 x = lstat(f,&buf);
4436 else
4437 #endif /* USE_LSTAT */
4438 x = stat(f,&buf);
4439 debug(F101,"STAT","",9);
4440 if (x < 0)
4441 return("----------");
4442 sprintf(s,"%o",buf.st_mode);
4443 debug(F110,"zgperm",s,0);
4444 return(s);
4445 }
4446
4447 /* Like zgperm() but returns permissions in "ls -l" string format */
4448
4449 static char xsperms[24];
4450
4451 char *
ziperm(f)4452 ziperm(f) char * f; {
4453 extern int diractive;
4454 int x; char *s = (char *)xsperms;
4455 struct stat buf;
4456 unsigned int perms = 0;
4457
4458 debug(F110,"ziperm",f,0);
4459
4460 if (!f) return(NULL);
4461 if (!*f) return(NULL);
4462
4463 #ifdef DTILDE /* Built with tilde-expansion? */
4464 if (*f == '~') { /* Starts with tilde? */
4465 f = tilde_expand(f); /* Try to expand it. */
4466 }
4467 #endif /* DTILDE */
4468
4469 if (diractive && zgfs_mode != 0) {
4470 perms = zgfs_mode; /* zgetfs() already got them */
4471 } else {
4472 #ifdef USE_LSTAT
4473 if (diractive)
4474 x = lstat(f,&buf);
4475 else
4476 #endif /* USE_LSTAT */
4477 x = stat(f,&buf);
4478 debug(F101,"STAT","",10);
4479 if (x < 0)
4480 return("----------");
4481 perms = buf.st_mode;
4482 }
4483 switch (perms & S_IFMT) {
4484 case S_IFDIR:
4485 *s++ = 'd';
4486 break;
4487 case S_IFCHR: /* Character special */
4488 *s++ = 'c';
4489 break;
4490 case S_IFBLK: /* Block special */
4491 *s++ = 'b';
4492 break;
4493 case S_IFREG: /* Regular */
4494 *s++ = '-';
4495 break;
4496 #ifdef S_IFLNK
4497 case S_IFLNK: /* Symbolic link */
4498 *s++ = 'l';
4499 break;
4500 #endif /* S_IFLNK */
4501 #ifdef S_IFSOCK
4502 case S_IFSOCK: /* Socket */
4503 *s++ = 's';
4504 break;
4505 #endif /* S_IFSOCK */
4506 #ifdef S_IFIFO
4507 #ifndef Plan9
4508 #ifndef COHERENT
4509 case S_IFIFO: /* FIFO */
4510 *s++ = 'p';
4511 break;
4512 #endif /* COHERENT */
4513 #endif /* Plan9 */
4514 #endif /* S_IFIFO */
4515 #ifdef S_IFWHT
4516 case S_IFWHT: /* Whiteout */
4517 *s++ = 'w';
4518 break;
4519 #endif /* S_IFWHT */
4520 default: /* Unknown */
4521 *s++ = '?';
4522 break;
4523 }
4524 if (perms & S_IRUSR) /* Owner's permissions */
4525 *s++ = 'r';
4526 else
4527 *s++ = '-';
4528 if (perms & S_IWUSR)
4529 *s++ = 'w';
4530 else
4531 *s++ = '-';
4532 switch (perms & (S_IXUSR | S_ISUID)) {
4533 case 0:
4534 *s++ = '-';
4535 break;
4536 case S_IXUSR:
4537 *s++ = 'x';
4538 break;
4539 case S_ISUID:
4540 *s++ = 'S';
4541 break;
4542 case S_IXUSR | S_ISUID:
4543 *s++ = 's';
4544 break;
4545 }
4546 if (perms & S_IRGRP) /* Group permissions */
4547 *s++ = 'r';
4548 else
4549 *s++ = '-';
4550 if (perms & S_IWGRP)
4551 *s++ = 'w';
4552 else
4553 *s++ = '-';
4554 switch (perms & (S_IXGRP | S_ISGID)) {
4555 case 0:
4556 *s++ = '-';
4557 break;
4558 case S_IXGRP:
4559 *s++ = 'x';
4560 break;
4561 case S_ISGID:
4562 *s++ = 'S';
4563 break;
4564 case S_IXGRP | S_ISGID:
4565 *s++ = 's';
4566 break;
4567 }
4568 if (perms & S_IROTH) /* World permissions */
4569 *s++ = 'r';
4570 else
4571 *s++ = '-';
4572 if (perms & S_IWOTH)
4573 *s++ = 'w';
4574 else
4575 *s++ = '-';
4576 switch (
4577 #ifdef Plan9
4578 perms & (S_IXOTH)
4579 #else
4580 perms & (S_IXOTH | S_ISVTX)
4581 #endif
4582 ) {
4583 case 0:
4584 *s++ = '-';
4585 break;
4586 case S_IXOTH:
4587 *s++ = 'x';
4588 break;
4589 #ifndef Plan9
4590 case S_ISVTX:
4591 *s++ = 'T';
4592 break;
4593 case S_IXOTH | S_ISVTX:
4594 *s++ = 't';
4595 break;
4596 #endif /* Plan9 */
4597 }
4598 *s = '\0';
4599 debug(F110,"ziperm",xsperms,0);
4600 return((char *)xsperms);
4601 }
4602
4603 #else
4604
4605 char *
zgperm(f)4606 zgperm(f) char *f; {
4607 return("----------");
4608 }
4609 char *
ziperms(f)4610 ziperms(f) char *f; {
4611 return("----------");
4612 }
4613 #endif /* CK_PERMS */
4614
4615 int
zsattr(xx)4616 zsattr(xx) struct zattr *xx; {
4617 CK_OFF_T k; int x;
4618 struct stat buf;
4619
4620 k = iflen % 1024; /* File length in K */
4621 if (k) k = 1L;
4622 xx->lengthk = (iflen / 1024) + k;
4623 xx->type.len = 0; /* File type can't be filled in here */
4624 xx->type.val = "";
4625 if (*nambuf) {
4626 xx->date.val = zfcdat(nambuf); /* File creation date */
4627 xx->date.len = (int)strlen(xx->date.val);
4628 } else {
4629 xx->date.len = 0;
4630 xx->date.val = "";
4631 }
4632 xx->creator.len = 0; /* File creator */
4633 xx->creator.val = "";
4634 xx->account.len = 0; /* File account */
4635 xx->account.val = "";
4636 xx->area.len = 0; /* File area */
4637 xx->area.val = "";
4638 xx->password.len = 0; /* Area password */
4639 xx->password.val = "";
4640 xx->blksize = -1L; /* File blocksize */
4641 xx->xaccess.len = 0; /* File access */
4642 xx->xaccess.val = "";
4643 xx->encoding.len = 0; /* Transfer syntax */
4644 xx->encoding.val = 0;
4645 xx->disp.len = 0; /* Disposition upon arrival */
4646 xx->disp.val = "";
4647 xx->lprotect.len = 0; /* Local protection */
4648 xx->lprotect.val = "";
4649 xx->gprotect.len = 0; /* Generic protection */
4650 xx->gprotect.val = "";
4651 x = -1;
4652 if (*nambuf) x = stat(nambuf,&buf);
4653 debug(F101,"STAT","",11);
4654 if (x >= 0) {
4655 debug(F111,"zsattr buf.st_mode & 0777",nambuf,buf.st_mode & 0777);
4656 /* UNIX filemode as an octal string without filetype bits */
4657 sprintf(lperms,"%o",buf.st_mode & 0777);
4658 xx->lprotect.len = (int)strlen(lperms);
4659 xx->lprotect.val = (char *)lperms;
4660 x = 0;
4661 #ifdef CK_GPERMS
4662 /* Generic permissions only if we have stat.h symbols defined */
4663 if (buf.st_mode & S_IRUSR) x |= 1; /* Read */
4664 if (buf.st_mode & S_IWUSR) x |= (2+16); /* Write and Delete */
4665 if (buf.st_mode & S_IXUSR) x |= 4; /* Execute */
4666 gperms[0] = tochar(x);
4667 gperms[1] = NUL;
4668 xx->gprotect.len = 1;
4669 xx->gprotect.val = (char *)gperms;
4670 #endif /* CK_GPERMS */
4671 }
4672 debug(F111,"zsattr lperms",xx->lprotect.val,xx->lprotect.len);
4673 debug(F111,"zsattr gperms",xx->gprotect.val,xx->gprotect.len);
4674 xx->systemid.val = "U1"; /* U1 = UNIX */
4675 xx->systemid.len = 2; /* System ID */
4676 xx->recfm.len = 0; /* Record format */
4677 xx->recfm.val = "";
4678 xx->sysparam.len = 0; /* System-dependent parameters */
4679 xx->sysparam.val = "";
4680 xx->length = iflen; /* Length */
4681 return(0);
4682 }
4683
4684 /* Z F C D A T -- Get file creation date */
4685 /*
4686 Call with pointer to filename.
4687 On success, returns pointer to modification date in yyyymmdd hh:mm:ss format.
4688 On failure, returns pointer to null string.
4689 */
4690 static char datbuf[40];
4691
4692 char *
4693 #ifdef CK_ANSIC
zdtstr(time_t timearg)4694 zdtstr(time_t timearg)
4695 #else
4696 zdtstr(timearg) time_t timearg;
4697 #endif /* CK_ANSIC */
4698 /* zdtstr */ {
4699 #ifndef TIMESTAMP
4700 return("");
4701 #else
4702 struct tm * time_stamp;
4703 struct tm * localtime();
4704 int yy, ss;
4705
4706 debug(F101,"zdtstr timearg","",timearg);
4707 if (timearg < 0)
4708 return("");
4709 time_stamp = localtime(&(timearg));
4710 if (!time_stamp) {
4711 debug(F100,"localtime returns null","",0);
4712 return("");
4713 }
4714 /*
4715 We assume that tm_year is ALWAYS years since 1900.
4716 Any platform where this is not the case will have problems
4717 starting in 2000.
4718 */
4719 yy = time_stamp->tm_year; /* Year - 1900 */
4720 debug(F101,"zdtstr tm_year","",time_stamp->tm_year);
4721 if (yy > 1000) {
4722 debug(F101,"zstrdt YEAR-2000 ALERT 1: localtime year","",yy);
4723 }
4724 yy += 1900;
4725 debug(F101,"zdatstr year","",yy);
4726
4727 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
4728 return("");
4729 if (time_stamp->tm_mday < 0 || time_stamp->tm_mday > 31)
4730 return("");
4731 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 23)
4732 return("");
4733 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 59)
4734 return("");
4735 ss = time_stamp->tm_sec; /* Seconds */
4736 if (ss < 0 || ss > 59) /* Some systems give a BIG number */
4737 ss = 0;
4738 sprintf(datbuf,
4739 #ifdef pdp11
4740 /* For some reason, 2.1x BSD sprintf gets the last field wrong. */
4741 "%04d%02d%02d %02d:%02d:00",
4742 #else
4743 "%04d%02d%02d %02d:%02d:%02d",
4744 #endif /* pdp11 */
4745 yy,
4746 time_stamp->tm_mon + 1,
4747 time_stamp->tm_mday,
4748 time_stamp->tm_hour,
4749 time_stamp->tm_min
4750 #ifndef pdp11
4751 , ss
4752 #endif /* pdp11 */
4753 );
4754 yy = (int)strlen(datbuf);
4755 debug(F111,"zdatstr",datbuf,yy);
4756 if (yy > 17) datbuf[17] = '\0';
4757 return(datbuf);
4758 #endif /* TIMESTAMP */
4759 }
4760
4761 char *
zfcdat(name)4762 zfcdat(name) char *name; {
4763 #ifdef TIMESTAMP
4764 struct stat buffer;
4765 extern int diractive;
4766 unsigned int mtime;
4767 int x;
4768 char * s;
4769
4770 if (!name)
4771 return("");
4772 s = name;
4773 if (!*s)
4774 return("");
4775
4776 #ifdef CKROOT
4777 debug(F111,"zfcdat setroot",ckroot,ckrootset);
4778 if (ckrootset) if (!zinroot(name)) {
4779 debug(F110,"zfcdat setroot violation",name,0);
4780 return("");
4781 }
4782 #endif /* CKROOT */
4783
4784 #ifdef DTILDE
4785 if (*s == '~') {
4786 s = tilde_expand(s);
4787 if (!s) s = "";
4788 if (!*s) s = name;
4789 }
4790 #endif /* DTILDE */
4791
4792 datbuf[0] = '\0';
4793 x = 0;
4794 debug(F111,"zfcdat",s,diractive);
4795
4796 if (diractive && zgfs_mtime) {
4797 mtime = zgfs_mtime;
4798 } else {
4799 #ifdef USE_LSTAT
4800 if (diractive) {
4801 x = lstat(s,&buffer);
4802 debug(F101,"STAT","",12);
4803 debug(F101,"zfcdat lstat","",x);
4804 } else {
4805 #endif /* USE_LSTAT */
4806 x = stat(s,&buffer);
4807 debug(F101,"STAT","",13);
4808 debug(F101,"zfcdat stat","",x);
4809 #ifdef USE_LSTAT
4810 }
4811 #endif /* USE_LSTAT */
4812 if (x != 0) {
4813 #ifdef USE_LSTAT
4814 debug(F111,"zfcdat stat failed",s,errno);
4815 #else
4816 debug(F111,"zfcdat lstat failed",s,errno);
4817 #endif /* USE_LSTAT */
4818 return("");
4819 }
4820 debug(F101,"zfcdat buffer.st_mtime","",buffer.st_mtime);
4821 mtime = buffer.st_mtime;
4822 }
4823 return(zdtstr(mtime));
4824 #else
4825 return("");
4826 #endif /* TIMESTAMP */
4827 }
4828
4829 #ifndef NOTIMESTAMP
4830
4831 /* Z S T R D T -- Converts local date string to internal representation */
4832 /*
4833 In our case (UNIX) this is seconds since midnite 1 Jan 1970 UTC,
4834 suitable for comparison with UNIX file dates. As far as I know, there is
4835 no library or system call -- at least nothing reasonably portable -- to
4836 convert local time to UTC.
4837 */
4838 time_t
zstrdt(date,len)4839 zstrdt(date,len) char * date; int len; {
4840 #ifdef M_UNIX
4841 /*
4842 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
4843 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
4844 dependence on the XPG4 supplement presence. So always use
4845 what the system header file supplies in ODT 3.0...
4846 */
4847 #ifndef ODT30
4848 #ifndef _SCO_DS
4849 extern void ftime(); /* extern void ftime(struct timeb *) */
4850 #endif /* _SCO_DS */
4851 #endif /* ODT30 */
4852 #else
4853 #ifndef M_XENIX
4854 extern int ftime();
4855 #endif /* M_XENIX */
4856 #endif /* M_UNIX */
4857 extern struct tm * localtime();
4858
4859 /* And this should have been declared always through a header file */
4860 #ifdef HPUX10
4861 time_t tmx;
4862 long days;
4863 #else
4864 #ifdef BSD44
4865 time_t tmx;
4866 long days;
4867 #else
4868 long tmx, days;
4869 #endif /* BSD44 */
4870 #endif /* HPUX10 */
4871 int i, n, isleapyear;
4872 /* J F M A M J J A S O N D */
4873 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
4874 static
4875 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
4876 char s[5];
4877 struct tm *time_stamp;
4878
4879 #ifdef BSD44
4880 struct timeval tp[2];
4881 long xtimezone = 0L;
4882 #else
4883 #ifdef V7
4884 struct utimbuf {
4885 time_t timep[2]; /* New access and modificaton time */
4886 } tp;
4887 char *tz;
4888 long timezone; /* In case timezone not defined in .h file */
4889 #else
4890 #ifdef SYSUTIMEH
4891 struct utimbuf tp;
4892 #else
4893 struct utimbuf {
4894 time_t atime;
4895 time_t mtime;
4896 } tp;
4897 #endif /* SYSUTIMEH */
4898 #endif /* V7 */
4899 #endif /* BSD44 */
4900
4901 #ifdef ANYBSD
4902 long timezone = 0L;
4903 static struct timeb tbp;
4904 #endif /* ANYBSD */
4905
4906 #ifdef BEBOX
4907 long timezone = 0L;
4908 #endif /* BEBOX */
4909
4910 debug(F111,"zstrdt",date,len);
4911
4912 if ((len == 0)
4913 || (len != 17)
4914 || (date[8] != ' ')
4915 || (date[11] != ':')
4916 || (date[14] != ':') ) {
4917 debug(F111,"Bad creation date ",date,len);
4918 return(-1);
4919 }
4920 debug(F111,"zstrdt date check 1",date,len);
4921 for(i = 0; i < 8; i++) {
4922 if (!isdigit(date[i])) {
4923 debug(F111,"Bad creation date ",date,len);
4924 return(-1);
4925 }
4926 }
4927 debug(F111,"zstrdt date check 2",date,len);
4928 i++;
4929
4930 for (; i < 16; i += 3) {
4931 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
4932 debug(F111,"Bad creation date ",date,len);
4933 return(-1);
4934 }
4935 }
4936 debug(F111,"zstrdt date check 3",date,len);
4937
4938
4939 #ifdef COMMENT /* was BSD44 */
4940 /*
4941 man gettimeofday on BSDI 3.1 says:
4942 "The timezone field is no longer used; timezone information is stored out-
4943 side the kernel. See ctime(3) for more information." So this chunk of
4944 code is effectively a no-op, at least in BSDI 3.x.
4945 */
4946 {
4947 int x;
4948 struct timezone tzp;
4949 x = gettimeofday(NULL, &tzp);
4950 debug(F101,"zstrdt BSD44 gettimeofday","",x);
4951 if (x > -1)
4952 xtimezone = tzp.tz_minuteswest * 60L;
4953 else
4954 xtimezone = 0L;
4955 debug(F101,"zstrdt BSD44 timezone","",xtimezone);
4956 }
4957 #else
4958 #ifdef ANYBSD
4959 debug(F100,"zstrdt BSD calling ftime","",0);
4960 ftime(&tbp);
4961 debug(F100,"zstrdt BSD back from ftime","",0);
4962 timezone = tbp.timezone * 60L;
4963 debug(F101,"zstrdt BSD timezone","",timezone);
4964 #else
4965 #ifdef SVORPOSIX
4966 tzset(); /* Set timezone */
4967 #else
4968 #ifdef V7
4969 if ((tz = getenv("TZ")) == NULL)
4970 timezone = 0; /* UTC/GMT */
4971 else
4972 timezone = atoi(&tz[3]); /* Set 'timezone'. */
4973 timezone *= 60L;
4974 #endif /* V7 */
4975 #endif /* SVORPOSIX */
4976 #endif /* ANYBSD */
4977 #endif /* COMMENT (was BSD44) */
4978
4979 debug(F100,"zstrdt so far so good","",0);
4980
4981 s[4] = '\0';
4982 for (i = 0; i < 4; i++) /* Fix the year */
4983 s[i] = date[i];
4984
4985 n = atoi(s);
4986 debug(F111,"zstrdt year",s,n);
4987 if (n < 1970) {
4988 debug(F100,"zstrdt fails - year","",n);
4989 return(-1);
4990 }
4991
4992 /* Previous year's leap days. This won't work after year 2100. */
4993
4994 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
4995 days = (long) (n - 1970) * 365;
4996 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
4997
4998 s[2] = '\0';
4999
5000 for (i = 4; i < 16; i += 2) {
5001 s[0] = date[i];
5002 s[1] = date[i + 1];
5003 n = atoi(s);
5004 switch (i) {
5005 case 4: /* MM: month */
5006 if ((n < 1 ) || ( n > 12)) {
5007 debug(F111,"zstrdt 4 bad date ",date,len);
5008 return(-1);
5009 }
5010 days += monthdays [n];
5011 if (isleapyear && n > 2)
5012 ++days;
5013 continue;
5014
5015 case 6: /* DD: day */
5016 if ((n < 1 ) || ( n > 31)) {
5017 debug(F111,"zstrdt 6 bad date ",date,len);
5018 return(-1);
5019 }
5020 tmx = (days + n - 1) * 24L * 60L * 60L;
5021 i++; /* Skip the space */
5022 continue;
5023
5024 case 9: /* hh: hour */
5025 if ((n < 0 ) || ( n > 23)) {
5026 debug(F111,"zstrdt 9 bad date ",date,len);
5027 return(-1);
5028 }
5029 tmx += n * 60L * 60L;
5030 i++; /* Skip the colon */
5031 continue;
5032
5033 case 12: /* mm: minute */
5034 if ((n < 0 ) || ( n > 59)) {
5035 debug(F111,"zstrdt 12 bad date ",date,len);
5036 return(-1);
5037 }
5038 #ifdef COMMENT /* (was BSD44) */ /* Correct for time zone */
5039 tmx += xtimezone;
5040 debug(F101,"zstrdt BSD44 tmx","",tmx);
5041 #else
5042 #ifdef ANYBSD
5043 tmx += timezone;
5044 #else
5045 #ifndef CONVEX9 /* Don't yet know how to do this here */
5046 #ifdef ultrix
5047 tmx += (long) timezone;
5048 #else
5049 #ifdef Plan9
5050 {
5051 extern time_t tzoffset;
5052 tmx += tzoffset;
5053 }
5054 #else
5055 #ifndef BSD44
5056 #ifndef NOTIMEZONE
5057 tmx += timezone;
5058 #endif /* NOTIMEZONE */
5059 #endif /* BSD44 */
5060 #endif /* Plan9 */
5061 #endif /* ultrix */
5062 #endif /* CONVEX9 */
5063 #endif /* ANYBSD */
5064 #endif /* COMMENT (was BSD44) */
5065 tmx += n * 60L;
5066 i++; /* Skip the colon */
5067 continue;
5068
5069 case 15: /* ss: second */
5070 if ((n < 0 ) || ( n > 59)) {
5071 debug(F111,"zstrdt 15 bad date ",date,len);
5072 return(-1);
5073 }
5074 tmx += n;
5075 }
5076 time_stamp = localtime(&tmx);
5077 debug(F101,"zstrdt tmx 1","",tmx);
5078 if (!time_stamp)
5079 return(-1);
5080 #ifdef COMMENT
5081 /* Why was this here? */
5082 time_stamp = localtime(&tmx);
5083 debug(F101,"zstrdt tmx 2","",tmx);
5084 #endif /* COMMENT */
5085 #ifdef BSD44
5086 { /* New to 7.0 - Works in at at least BSDI 3.1 and FreeBSD 2.2.7 */
5087 long zz;
5088 zz = time_stamp->tm_gmtoff; /* Seconds away from Zero Meridian */
5089 debug(F101,"zstrdt BSD44 tm_gmtoff","",zz);
5090 tmx -= zz;
5091 debug(F101,"zstrdt BSD44 tmx 3 (GMT)","",tmx);
5092 }
5093 #else
5094 /*
5095 Daylight Savings Time adjustment.
5096 Do this everywhere BUT in BSD44 because in BSD44,
5097 tm_gmtoff also includes the DST adjustment.
5098 */
5099 if (time_stamp->tm_isdst) {
5100 tmx -= 60L * 60L;
5101 debug(F101,"zstrdt tmx 3 (DST)","",tmx);
5102 }
5103 #endif /* BSD44 */
5104 n = time_stamp->tm_year;
5105 if (n < 300) {
5106 n += 1900;
5107 }
5108 }
5109 return(tmx);
5110 }
5111
5112
5113 #ifdef ZLOCALTIME
5114 /* Z L O C A L T I M E -- GMT/UTC time string to local time string */
5115
5116 /*
5117 Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
5118 Returns: "yyyymmdd hh:mm:ss" local date-time on success, NULL on failure.
5119 */
5120 static char zltimbuf[64];
5121
5122 char *
zlocaltime(gmtstring)5123 zlocaltime(gmtstring) char * gmtstring; {
5124 #ifdef M_UNIX
5125 /*
5126 SCO UNIX 3.2v2.0 and ODT 2.0 lack prototypes for ftime().
5127 ODT 3.0 (3.2v4.2 OS) has a prototype, which may vary in
5128 dependence on the XPG4 supplement presence. So always use
5129 what the system header file supplies in ODT 3.0...
5130 */
5131 #ifndef ODT30
5132 #ifndef _SCO_DS
5133 extern void ftime(); /* extern void ftime(struct timeb *) */
5134 #endif /* _SCO_DS */
5135 #endif /* ODT30 */
5136 #else
5137 #ifndef M_XENIX
5138 extern int ftime();
5139 #endif /* M_XENIX */
5140 #endif /* M_UNIX */
5141 extern struct tm * localtime();
5142
5143 /* And this should have been declared always through a header file */
5144 #ifdef HPUX10
5145 time_t tmx;
5146 long days;
5147 #else
5148 #ifdef BSD44
5149 time_t tmx;
5150 long days;
5151 #else
5152 long tmx, days;
5153 #endif /* BSD44 */
5154 #endif /* HPUX10 */
5155 int i, n, x, isleapyear;
5156 /* J F M A M J J A S O N D */
5157 /* 31 28 31 30 31 30 31 31 30 31 30 31 */
5158 static
5159 int monthdays [13] = { 0,0,31,59,90,120,151,181,212,243,273,304,334 };
5160 char s[5];
5161 struct tm *time_stamp;
5162
5163 #ifdef BSD44
5164 struct timeval tp[2];
5165 #else
5166 #ifdef V7
5167 struct utimbuf {
5168 time_t timep[2]; /* New access and modificaton time */
5169 } tp;
5170 #else
5171 #ifdef SYSUTIMEH
5172 struct utimbuf tp;
5173 #else
5174 struct utimbuf {
5175 time_t atime;
5176 time_t mtime;
5177 } tp;
5178 #endif /* SYSUTIMEH */
5179 #endif /* V7 */
5180 #endif /* BSD44 */
5181
5182 #ifdef ANYBSD
5183 static struct timeb tbp;
5184 #endif /* ANYBSD */
5185
5186 char * date = gmtstring;
5187 int len;
5188
5189 len = strlen(date);
5190 debug(F111,"zlocaltime",date,len);
5191
5192 if ((len == 0)
5193 || (len != 17)
5194 || (date[8] != ' ')
5195 || (date[11] != ':')
5196 || (date[14] != ':') ) {
5197 debug(F111,"Bad creation date ",date,len);
5198 return(NULL);
5199 }
5200 debug(F111,"zlocaltime date check 1",date,len);
5201 for(i = 0; i < 8; i++) {
5202 if (!isdigit(date[i])) {
5203 debug(F111,"Bad creation date ",date,len);
5204 return(NULL);
5205 }
5206 }
5207 debug(F111,"zlocaltime date check 2",date,len);
5208 i++;
5209
5210 for (; i < 16; i += 3) {
5211 if ((!isdigit(date[i])) || (!isdigit(date[i + 1]))) {
5212 debug(F111,"Bad creation date ",date,len);
5213 return(NULL);
5214 }
5215 }
5216 debug(F111,"zlocaltime date check 3",date,len);
5217
5218 debug(F100,"zlocaltime so far so good","",0);
5219
5220 s[4] = '\0';
5221 for (i = 0; i < 4; i++) /* Fix the year */
5222 s[i] = date[i];
5223
5224 n = atoi(s);
5225 debug(F111,"zlocaltime year",s,n);
5226 if (n < 1970) {
5227 debug(F100,"zlocaltime fails - year","",n);
5228 return(NULL);
5229 }
5230
5231 /* Previous year's leap days. This won't work after year 2100. */
5232
5233 isleapyear = (( n % 4 == 0 && n % 100 !=0) || n % 400 == 0);
5234 days = (long) (n - 1970) * 365;
5235 days += (n - 1968 - 1) / 4 - (n - 1900 - 1) / 100 + (n - 1600 - 1) / 400;
5236
5237 s[2] = '\0';
5238
5239 for (i = 4; i < 16; i += 2) {
5240 s[0] = date[i];
5241 s[1] = date[i + 1];
5242 n = atoi(s);
5243 switch (i) {
5244 case 4: /* MM: month */
5245 if ((n < 1 ) || ( n > 12)) {
5246 debug(F111,"zlocaltime 4 bad date ",date,len);
5247 return(NULL);
5248 }
5249 days += monthdays [n];
5250 if (isleapyear && n > 2)
5251 ++days;
5252 continue;
5253
5254 case 6: /* DD: day */
5255 if ((n < 1 ) || ( n > 31)) {
5256 debug(F111,"zlocaltime 6 bad date ",date,len);
5257 return(NULL);
5258 }
5259 tmx = (days + n - 1) * 24L * 60L * 60L;
5260 i++; /* Skip the space */
5261 continue;
5262
5263 case 9: /* hh: hour */
5264 if ((n < 0 ) || ( n > 23)) {
5265 debug(F111,"zlocaltime 9 bad date ",date,len);
5266 return(NULL);
5267 }
5268 tmx += n * 60L * 60L;
5269 i++; /* Skip the colon */
5270 continue;
5271
5272 case 12: /* mm: minute */
5273 if ((n < 0 ) || ( n > 59)) {
5274 debug(F111,"zlocaltime 12 bad date ",date,len);
5275 return(NULL);
5276 }
5277 tmx += n * 60L;
5278 i++; /* Skip the colon */
5279 continue;
5280
5281 case 15: /* ss: second */
5282 if ((n < 0 ) || ( n > 59)) {
5283 debug(F111,"zlocaltime 15 bad date ",date,len);
5284 return(NULL);
5285 }
5286 tmx += n;
5287 }
5288
5289 /*
5290 At this point tmx is the time_t representation of the argument date-time
5291 string without any timezone or DST adjustments. Therefore it should be
5292 the same as the time_t representation of the GMT/UTC time. Now we should
5293 be able to feed it to localtime() and have it converted to a struct tm
5294 representing the local time equivalent of the given UTC time.
5295 */
5296 time_stamp = localtime(&tmx);
5297 if (!time_stamp)
5298 return(NULL);
5299 }
5300
5301 /* Now we simply reformat the struct tm to a string */
5302
5303 x = time_stamp->tm_year;
5304 if (time_stamp->tm_year < 70 || time_stamp->tm_year > 8099)
5305 return(NULL);
5306 if (time_stamp->tm_mon < 0 || time_stamp->tm_mon > 11)
5307 return(NULL);
5308 if (time_stamp->tm_mday < 1 || time_stamp->tm_mday > 31)
5309 return(NULL);
5310 if (time_stamp->tm_hour < 0 || time_stamp->tm_hour > 24)
5311 return(NULL);
5312 if (time_stamp->tm_min < 0 || time_stamp->tm_min > 60)
5313 return(NULL);
5314 if (time_stamp->tm_sec < 0 || time_stamp->tm_sec > 60)
5315 return(NULL);
5316 sprintf(zltimbuf,"%04d%02d%02d %02d:%02d:%02d",
5317 time_stamp->tm_year + 1900,
5318 time_stamp->tm_mon + 1,
5319 time_stamp->tm_mday,
5320 time_stamp->tm_hour,
5321 time_stamp->tm_min,
5322 time_stamp->tm_sec
5323 );
5324 return((char *)zltimbuf);
5325 }
5326 #endif /* ZLOCALTIME */
5327 #endif /* NOTIMESTAMP */
5328
5329 /* Z S T I M E -- Set modification date/time+permissions for incoming file */
5330 /*
5331 Call with:
5332 f = pointer to name of existing file.
5333 yy = pointer to a Kermit file attribute structure in which yy->date.val
5334 is a date of the form yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00.
5335 yy->lprotect.val & yy->gprotect.val are permission/protection values.
5336 x = is a function code: 0 means to set the file's attributes as given.
5337 1 means compare the date in struct yy with the file creation date.
5338 IMPORTANT: if you are calling this routine only to set a certain attribute
5339 but not others, you MUST set yy->blah.len to 0 for each blah not
5340 being set.
5341 Returns:
5342 -1 on any kind of error.
5343 0 if x is 0 and the attributes were set successfully.
5344 0 if x is 1 and date from attribute structure <= file creation date.
5345 1 if x is 1 and date from attribute structure > file creation date.
5346 */
5347 int
zstime(f,yy,x)5348 zstime(f,yy,x)
5349 char *f; struct zattr *yy; int x;
5350 /* zstime */ {
5351 int r = -1; /* Return code */
5352 #ifdef CK_PERMS
5353 int setperms = 0;
5354 #endif /* CK_PERMS */
5355 int setdate = 0;
5356
5357 /* It is ifdef'd TIMESTAMP because it might not work on V7. bk@kullmar.se. */
5358
5359 #ifdef TIMESTAMP
5360 #ifdef BSD44
5361 extern int utimes();
5362 #else
5363 extern int utime();
5364 #endif /* BSD44 */
5365
5366 struct stat sb;
5367
5368 /* At least, the declarations for int functions are not needed anyway */
5369
5370 #ifdef BSD44
5371 struct timeval tp[2];
5372 long xtimezone;
5373 #else
5374 #ifdef V7
5375 struct utimbuf {
5376 time_t timep[2]; /* New access and modificaton time */
5377 } tp;
5378 char *tz;
5379 long timezone; /* In case not defined in .h file */
5380 #else
5381 #ifdef SYSUTIMEH
5382 struct utimbuf tp;
5383 #else
5384 struct utimbuf {
5385 time_t atime;
5386 time_t mtime;
5387 } tp;
5388 #endif /* SYSUTIMEH */
5389 #endif /* V7 */
5390 #endif /* BSD44 */
5391
5392 long tm = 0L;
5393
5394 if (!f) f = "";
5395 if (!*f) return(-1);
5396 if (!yy) return(-1);
5397
5398 #ifdef CKROOT
5399 debug(F111,"zstime setroot",ckroot,ckrootset);
5400 if (ckrootset) if (!zinroot(f)) {
5401 debug(F110,"zstime setroot violation",f,0);
5402 return(0);
5403 }
5404 #endif /* CKROOT */
5405
5406 if (yy->date.len == 0) { /* No date in struct */
5407 if (yy->lprotect.len > 0) { /* So go do permissions */
5408 goto zsperms;
5409 } else {
5410 debug(F100,"zstime: nothing to do","",0);
5411 return(0);
5412 }
5413 }
5414 if ((tm = zstrdt(yy->date.val,yy->date.len)) < 0) {
5415 debug(F101,"zstime: zstrdt fails","",0);
5416 return(-1);
5417 }
5418 debug(F101,"zstime: tm","",tm);
5419 debug(F111,"zstime: A-pkt date ok ",yy->date.val,yy->date.len);
5420
5421 if (stat(f,&sb)) { /* Get the time for the file */
5422 debug(F101,"STAT","",14);
5423 debug(F111,"zstime: Can't stat file:",f,errno);
5424 return(-1);
5425 }
5426 debug(F101,"STAT","",15);
5427 setdate = 1;
5428
5429 zsperms:
5430 #ifdef CK_PERMS
5431 {
5432 int i, x = 0, xx, flag = 0;
5433 char * s;
5434 char obuf[24];
5435 #ifdef COMMENT
5436 #ifdef DEBUG
5437 if (deblog) {
5438 debug(F111,"zstime lperms",yy->lprotect.val,yy->lprotect.len);
5439 debug(F111,"zstime gperms",yy->gprotect.val,yy->gprotect.len);
5440 debug(F110,"zstime system id",yy->systemid.val,0);
5441 sprintf(obuf,"%o",sb.st_mode);
5442 debug(F110,"zstime file perms before",obuf,0);
5443 }
5444 #endif /* DEBUG */
5445 #endif /* COMMENT */
5446
5447 #ifdef CK_LOGIN
5448 debug(F101,"zstime isguest","",isguest);
5449 debug(F101,"zstime ckxperms","",ckxperms);
5450 if (isguest) {
5451 #ifdef COMMENT
5452 /* Clear owner permissions */
5453 sb.st_mode &= (unsigned) 0177077; /* (16 bits) */
5454 #else
5455 /* Set permissions from ckxperms variable */
5456 sb.st_mode = ckxperms;
5457 #endif /* COMMENT */
5458 debug(F101,"zstime isguest sb.st_mode","",sb.st_mode);
5459 #ifdef COMMENT
5460 /* We already set them in zopeno() */
5461 setperms = 1;
5462 #endif /* COMMENT */
5463 flag = 0;
5464 } else
5465 #endif /* CK_LOGIN */
5466 if ((yy->lprotect.len > 0 && /* Have local-format permissions */
5467 yy->systemid.len > 0 && /* from A-packet... */
5468 #ifdef UNIX
5469 !strcmp(yy->systemid.val,"U1") /* AND you are same as me */
5470 #else
5471 0
5472 #endif /* UNIX */
5473 ) || (yy->lprotect.len < 0) /* OR by inheritance from old file */
5474 ) {
5475 flag = 1;
5476 s = yy->lprotect.val; /* UNIX filemode */
5477 xx = yy->lprotect.len;
5478 if (xx < 0) /* len < 0 means inheritance */
5479 xx = 0 - xx;
5480 for (i = 0; i < xx; i++) { /* Decode octal string */
5481 if (*s <= '7' && *s >= '0') {
5482 x = 8 * x + (int)(*s) - '0';
5483 } else {
5484 flag = 0;
5485 break;
5486 }
5487 s++;
5488 }
5489 #ifdef DEBUG
5490 sprintf(obuf,"%o",x);
5491 debug(F110,"zstime octal lperm",obuf,0);
5492 #endif /* DEBUG */
5493 } else if (!flag && yy->gprotect.len > 0) {
5494 int g;
5495 #ifdef CK_SCO32V4
5496 mode_t mask;
5497 #else
5498 int mask;
5499 #endif /* CK_SCO32V4 */
5500 mask = umask(0); /* Get umask */
5501 debug(F101,"zstime mask 1","",mask);
5502 umask(mask); /* Put it back */
5503 mask ^= 0777; /* Flip the bits */
5504 debug(F101,"zstime mask 2","",mask);
5505 debug(F101,"zstime yy->gprotect.len","",yy->gprotect.len);
5506 /* Decode generic protection */
5507 if (yy->gprotect.len < 1 || yy->gprotect.len > 99) {
5508 g = 0; /* protect against bogus value */
5509 } else {
5510 debug(F110,"zstime yy->gprotect.val",yy->gprotect.val,0);
5511 g = xunchar(*(yy->gprotect.val));
5512 }
5513 debug(F101,"zstime gprotect","",g);
5514 #ifdef S_IRUSR
5515 debug(F100,"zstime S_IRUSR","",0);
5516 if (g & 1) x |= S_IRUSR; /* Read permission */
5517 flag = 1;
5518 #endif /* S_IRUSR */
5519 #ifdef S_IWUSR
5520 debug(F100,"zstime S_IWUSR","",0);
5521 if (g & 2) x |= S_IWUSR; /* Write permission */
5522 if (g & 16) x |= S_IWUSR; /* Delete permission */
5523 flag = 1;
5524 #endif /* S_IWUSR */
5525 #ifdef S_IXUSR
5526 debug(F100,"zstime S_IXUSR","",0);
5527 if (g & 4) /* Has execute permission bit */
5528 x |= S_IXUSR;
5529 else /* Doesn't have it */
5530 mask &= 0666; /* so also clear it out of mask */
5531 flag = 1;
5532 #endif /* S_IXUSR */
5533 debug(F101,"zstime mask x","",x);
5534 x |= mask;
5535 debug(F101,"zstime mask x|mask","",x);
5536 }
5537 debug(F101,"zstime flag","",flag);
5538 if (flag) {
5539 #ifdef S_IFMT
5540 debug(F101,"zstime S_IFMT x","",x);
5541 sb.st_mode = (sb.st_mode & S_IFMT) | x;
5542 setperms = 1;
5543 #else
5544 #ifdef _IFMT
5545 debug(F101,"zstime _IFMT x","",x);
5546 sb.st_mode = (sb.st_mode & _IFMT) | x;
5547 setperms = 1;
5548 #endif /* _IFMT */
5549 #endif /* S_IFMT */
5550 }
5551 #ifdef DEBUG
5552 sprintf(obuf,"%04o",sb.st_mode);
5553 debug(F111,"zstime file perms after",obuf,setperms);
5554 #endif /* DEBUG */
5555 }
5556 #endif /* CK_PERMS */
5557
5558 debug(F101,"zstime: sb.st_atime","",sb.st_atime);
5559
5560 #ifdef BSD44
5561 tp[0].tv_sec = sb.st_atime; /* Access time first */
5562 tp[1].tv_sec = tm; /* Update time second */
5563 debug(F100,"zstime: BSD44 modtime","",0);
5564 #else
5565 #ifdef V7
5566 tp.timep[0] = tm; /* Set modif. time to creation date */
5567 tp.timep[1] = sb.st_atime; /* Don't change the access time */
5568 debug(F100,"zstime: V7 modtime","",0);
5569 #else
5570 #ifdef SYSUTIMEH
5571 tp.modtime = tm; /* Set modif. time to creation date */
5572 tp.actime = sb.st_atime; /* Don't change the access time */
5573 debug(F100,"zstime: SYSUTIMEH modtime","",0);
5574 #else
5575 tp.mtime = tm; /* Set modif. time to creation date */
5576 tp.atime = sb.st_atime; /* Don't change the access time */
5577 debug(F100,"zstime: default modtime","",0);
5578 #endif /* SYSUTIMEH */
5579 #endif /* V7 */
5580 #endif /* BSD44 */
5581
5582 switch (x) { /* Execute desired function */
5583 case 0: /* Set the creation date of the file */
5584 #ifdef CK_PERMS /* And permissions */
5585 /*
5586 NOTE: If we are inheriting permissions from a previous file, and the
5587 previous file was a directory, this would turn the new file into a directory
5588 too, but it's not, so we try to unset the right bit. Luckily, this code
5589 will probably never be executed since the upper level modules do not allow
5590 reception of a file that has the same name as a directory.
5591
5592 NOTE 2: We change the permissions *before* we change the modification time,
5593 otherwise changing the permissions would set the mod time to the present
5594 time.
5595 */
5596 {
5597 int x;
5598 debug(F101,"zstime setperms","",setperms);
5599 if (S_ISDIR(sb.st_mode)) {
5600 debug(F101,"zstime DIRECTORY bit on","",sb.st_mode);
5601 sb.st_mode ^= 0040000;
5602 debug(F101,"zstime DIRECTORY bit off","",sb.st_mode);
5603 }
5604 if (setperms) {
5605 x = chmod(f,sb.st_mode);
5606 debug(F101,"zstime chmod","",x);
5607 }
5608 }
5609 if (x < 0) return(-1);
5610 #endif /* CK_PERMS */
5611
5612 if (!setdate) /* We don't have a date */
5613 return(0); /* so skip the following... */
5614
5615 if (
5616 #ifdef BSD44
5617 utimes(f,tp)
5618 #else
5619 utime(f,&tp)
5620 #endif /* BSD44 */
5621 ) { /* Fix modification time */
5622 debug(F111,"zstime 0: can't set modtime for file",f,errno);
5623 r = -1;
5624 } else {
5625 /* Including the modtime here is not portable */
5626 debug(F110,"zstime 0: modtime set for file",f,0);
5627 r = 0;
5628 }
5629 break;
5630
5631 case 1: /* Compare the dates */
5632 /*
5633 This was st_atime, which was wrong. We want the file-data modification
5634 time, st_mtime.
5635 */
5636 debug(F111,"zstime 1: compare",f,sb.st_mtime);
5637 debug(F111,"zstime 1: compare","packet",tm);
5638
5639 r = (sb.st_mtime < tm) ? 0 : 1;
5640 break;
5641
5642 default: /* Error */
5643 r = -1;
5644 }
5645 #endif /* TIMESTAMP */
5646 return(r);
5647 }
5648
5649 /* Find initialization file. */
5650
5651 #ifdef NOTUSED
5652 int
zkermini()5653 zkermini() {
5654 /* nothing here for Unix. This function added for benefit of VMS Kermit. */
5655 return(0);
5656 }
5657 #endif /* NOTUSED */
5658
5659 #ifndef UNIX
5660 /* Historical -- not used in Unix any more (2001-11-03) */
5661 #ifndef NOFRILLS
5662 int
zmail(p,f)5663 zmail(p,f) char *p; char *f; { /* Send file f as mail to address p */
5664 /*
5665 Returns 0 on success
5666 2 if mail delivered but temp file can't be deleted
5667 -2 if mail can't be delivered
5668 -1 on file access error
5669 The UNIX version always returns 0 because it can't get a good return
5670 code from zsyscmd.
5671 */
5672 int n;
5673
5674 #ifdef CK_LOGIN
5675 if (isguest)
5676 return(-2);
5677 #endif /* CK_LOGIN */
5678
5679 if (!f) f = "";
5680 if (!*f) return(-1);
5681
5682 #ifdef CKROOT
5683 debug(F111,"zmail setroot",ckroot,ckrootset);
5684 if (ckrootset) if (!zinroot(f)) {
5685 debug(F110,"zmail setroot violation",f,0);
5686 return(-1);
5687 }
5688 #endif /* CKROOT */
5689
5690 #ifdef BSD4
5691 /* The idea is to use /usr/ucb/mail, rather than regular mail, so that */
5692 /* a subject line can be included with -s. Since we can't depend on the */
5693 /* user's path, we use the convention that /usr/ucb/Mail = /usr/ucb/mail */
5694 /* and even if Mail has been moved to somewhere else, this should still */
5695 /* find it... The search could be made more reliable by actually using */
5696 /* access() to see if /usr/ucb/Mail exists. */
5697
5698 n = strlen(f);
5699 n = n + n + 15 + (int)strlen(p);
5700
5701 if (n > ZMBUFLEN)
5702 return(-2);
5703
5704 #ifdef DGUX540
5705 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5706 #else
5707 sprintf(zmbuf,"Mail -s %c%s%c %s < %s", '"', f, '"', p, f);
5708 #endif /* DGUX540 */
5709 zsyscmd(zmbuf);
5710 #else
5711 #ifdef SVORPOSIX
5712 #ifndef OXOS
5713 sprintf(zmbuf,"mail %s < %s", p, f);
5714 #else /* OXOS */
5715 sprintf(zmbuf,"mailx -s %c%s%c %s < %s", '"', f, '"', p, f);
5716 #endif /* OXOS */
5717 zsyscmd(zmbuf);
5718 #else
5719 *zmbuf = '\0';
5720 #endif
5721 #endif
5722 return(0);
5723 }
5724 #endif /* NOFRILLS */
5725 #endif /* UNIX */
5726
5727 #ifndef NOFRILLS
5728 int
zprint(p,f)5729 zprint(p,f) char *p; char *f; { /* Print file f with options p */
5730 extern char * printername; /* From ckuus3.c */
5731 extern int printpipe;
5732 int n;
5733
5734 #ifdef CK_LOGIN
5735 if (isguest)
5736 return(-2);
5737 #endif /* CK_LOGIN */
5738
5739 if (!f) f = "";
5740 if (!*f) return(-1);
5741
5742 #ifdef CKROOT
5743 debug(F111,"zprint setroot",ckroot,ckrootset);
5744 if (ckrootset) if (!zinroot(f)) {
5745 debug(F110,"zprint setroot violation",f,0);
5746 return(-1);
5747 }
5748 #endif /* CKROOT */
5749
5750 debug(F110,"zprint file",f,0);
5751 debug(F110,"zprint flags",p,0);
5752 debug(F110,"zprint printername",printername,0);
5753 debug(F101,"zprint printpipe","",printpipe);
5754
5755 #ifdef UNIX
5756 /*
5757 Note use of standard input redirection. In some systems, lp[r] runs
5758 setuid to lp (or ...?), so if user has sent a file into a directory
5759 that lp does not have read access to, it can't be printed unless it is
5760 fed to lp[r] as standard input.
5761 */
5762 if (printpipe && printername) {
5763 n = 8 + (int)strlen(f) + (int)strlen(printername);
5764 if (n > ZMBUFLEN)
5765 return(-2);
5766 sprintf(zmbuf,"cat %s | %s", f, printername);
5767 } else if (printername) {
5768 n = 8 + (int)strlen(f) + (int)strlen(printername);
5769 if (n > ZMBUFLEN)
5770 return(-2);
5771 sprintf(zmbuf,"cat %s >> %s", f, printername);
5772 } else {
5773 n = 4 + (int)strlen(PRINTCMD) + (int)strlen(p) + (int)strlen(f);
5774 if (n > ZMBUFLEN)
5775 return(-2);
5776 sprintf(zmbuf,"%s %s < %s", PRINTCMD, p, f);
5777 }
5778 debug(F110,"zprint command",zmbuf,0);
5779 zsyscmd(zmbuf);
5780 #else /* Not UNIX */
5781 *zmbuf = '\0';
5782 #endif /* UNIX */
5783 return(0);
5784 }
5785 #endif /* NOFRILLS */
5786
5787 /* Wildcard expansion functions... */
5788
5789 static char scratch[MAXPATH+4]; /* Used by both methods */
5790
5791 static int oldmtchs = 0; /* Let shell (ls) expand them. */
5792 #ifdef COMMENT
5793 static char *lscmd = "/bin/ls -d"; /* Command to use. */
5794 #else
5795 static char *lscmd = "echo"; /* Command to use. */
5796 #endif /* COMMENT */
5797
5798 #ifndef NOPUSH
5799 int
shxpand(pat,namlst,len)5800 shxpand(pat,namlst,len) char *pat, *namlst[]; int len; {
5801 char *fgbuf = NULL; /* Buffer for forming ls command */
5802 char *p, *q; /* Workers */
5803
5804 int i, x, retcode, itsadir;
5805 char c;
5806
5807 x = (int)strlen(pat) + (int)strlen(lscmd) + 3; /* Length of ls command */
5808 for (i = 0; i < oldmtchs; i++) { /* Free previous file list */
5809 if (namlst[i] ) { /* If memory is allocated */
5810 free(namlst[i]); /* Free the memory */
5811 namlst[i] = NULL ; /* Remember no memory is allocated */
5812 }
5813 }
5814 oldmtchs = 0 ; /* Remember there are no matches */
5815 fgbuf = malloc(x); /* Get buffer for command */
5816 if (!fgbuf) return(-1); /* Fail if cannot */
5817 ckmakmsg(fgbuf,x,lscmd," ",pat,NULL); /* Form the command */
5818 zxcmd(ZIFILE,fgbuf); /* Start the command */
5819 i = 0; /* File counter */
5820 p = scratch; /* Point to scratch area */
5821 retcode = -1; /* Assume failure */
5822 while ((x = zminchar()) != -1) { /* Read characters from command */
5823 c = (char) x;
5824 if (c == ' ' || c == '\n') { /* Got newline or space? */
5825 *p = '\0'; /* Yes, terminate string */
5826 p = scratch; /* Point back to beginning */
5827 if (zchki(p) == -1) /* Does file exist? */
5828 continue; /* No, continue */
5829 itsadir = isdir(p); /* Yes, is it a directory? */
5830 if (xdironly && !itsadir) /* Want only dirs but this isn't */
5831 continue; /* so skip. */
5832 if (xfilonly && itsadir) /* It's a dir but want only files */
5833 continue; /* so skip. */
5834 x = (int)strlen(p); /* Keep - get length of name */
5835 q = malloc(x+1); /* Allocate space for it */
5836 if (!q) goto shxfin; /* Fail if space can't be obtained */
5837 strcpy(q,scratch); /* (safe) Copy name to space */
5838 namlst[i++] = q; /* Copy pointer to name into array */
5839 if (i >= len) goto shxfin; /* Fail if too many */
5840 } else { /* Regular character */
5841 *p++ = c; /* Copy it into scratch area */
5842 }
5843 }
5844 retcode = i; /* Return number of matching files */
5845 shxfin: /* Common exit point */
5846 free(fgbuf); /* Free command buffer */
5847 fgbuf = NULL;
5848 zclosf(ZIFILE); /* Delete the command fork. */
5849 oldmtchs = i; /* Remember how many files */
5850 return(retcode);
5851 }
5852 #endif /* NOPUSH */
5853
5854 /*
5855 Directory-reading functions for UNIX originally written for C-Kermit 4.0
5856 by Jeff Damens, CUCCA, 1984.
5857 */
5858 static char * xpat = NULL; /* Global copy of fgen() pattern */
5859 static char * xpatlast = NULL; /* Rightmost segment of pattern*/
5860 static int xpatslash = 0; /* Slash count in pattern */
5861 static int xpatwild = 0; /* Original pattern is wild */
5862 static int xleafwild = 0; /* Last segment of pattern is wild */
5863 static int xpatabsolute = 0;
5864
5865 #ifdef aegis
5866 static char bslash;
5867 #endif /* aegis */
5868
5869
5870 /* S P L I T P A T H */
5871
5872 /*
5873 Splits the slash-separated portions of the argument string into
5874 a list of path structures. Returns the head of the list. The
5875 structures are allocated by malloc, so they must be freed.
5876 Splitpath is used internally by the filename generator.
5877
5878 Input:
5879 A path string.
5880
5881 Returns:
5882 A linked list of the slash-separated segments of the input.
5883 */
5884 static struct path *
splitpath(p)5885 splitpath(p) char *p; {
5886 struct path *head,*cur,*prv;
5887 int i;
5888
5889 debug(F111,"splitpath",p,xrecursive);
5890 head = prv = NULL;
5891
5892 if (!p) return(NULL);
5893 if (!*p) return(NULL);
5894
5895 if (!strcmp(p,"**")) { /* Fix this */
5896 p = "*";
5897 }
5898 if (ISDIRSEP(*p)) p++; /* Skip leading slash if any */
5899
5900 /* Make linked list of path segments from pattern */
5901
5902 while (*p) {
5903 cur = (struct path *) malloc(sizeof (struct path));
5904 /* debug(F101,"splitpath malloc","",cur); */
5905 if (cur == NULL) {
5906 debug(F100,"splitpath malloc failure","",0);
5907 prv -> fwd = NULL;
5908 return((struct path *)NULL);
5909 }
5910 cur -> fwd = NULL;
5911 if (head == NULL) /* First, make list head */
5912 head = cur;
5913 else /* Not first, link into chain */
5914 prv -> fwd = cur;
5915 prv = cur; /* Link from previous to this one */
5916
5917 #ifdef aegis
5918 /* treat backslash as "../" */
5919 if (bslash && *p == bslash) {
5920 strcpy(cur->npart, ".."); /* safe */
5921 ++p;
5922 } else {
5923 for (i=0; i < MAXNAMLEN && *p && *p != '/' && *p != bslash; i++)
5924 cur -> npart[i] = *p++;
5925 cur -> npart[i] = '\0'; /* end this segment */
5926 if (i >= MAXNAMLEN)
5927 while (*p && *p != '/' && *p != bslash)
5928 p++;
5929 }
5930 if (*p == '/') p++;
5931 #else
5932 /* General case (UNIX) */
5933 for (i = 0; i < MAXNAMLEN && !ISDIRSEP(*p) && *p != '\0'; i++) {
5934 cur -> npart[i] = *p++;
5935 }
5936
5937 cur -> npart[i] = '\0'; /* End this path segment */
5938 if (i >= MAXNAMLEN)
5939 while (!ISDIRSEP(*p) && *p != '\0') p++;
5940 if (ISDIRSEP(*p))
5941 p++;
5942
5943 #endif /* aegis */
5944 }
5945 if (prv) {
5946 makestr(&xpatlast,prv -> npart);
5947 debug(F110,"splitpath xpatlast",xpatlast,0);
5948 }
5949 #ifdef DEBUG
5950 /* Show original path list */
5951 if (deblog) {
5952 for (i = 0, cur = head; cur; i++) {
5953 debug(F111,"SPLITPATH",cur -> npart, i);
5954 cur = cur -> fwd;
5955 }
5956 }
5957 #endif /* DEBUG */
5958 return(head);
5959 }
5960
5961 /* F G E N -- Generate File List */
5962
5963 /*
5964 File name generator. It is passed a string, possibly containing wildcards,
5965 and an array of character pointers. It finds all the matching filenames and
5966 stores pointers to them in the array. The returned strings are allocated
5967 from a static buffer local to this module (so the caller doesn't have to
5968 worry about deallocating them); this means that successive calls to fgen
5969 will wipe out the results of previous calls.
5970
5971 Input:
5972 A wildcard string, an array to write names to, the length of the array.
5973
5974 Returns:
5975 The number of matches.
5976 The array is filled with filenames that matched the pattern.
5977 If there wasn't enough room in the array, -1 is returned.
5978
5979 Originally by: Jeff Damens, CUCCA, 1984. Many changes since then.
5980 */
5981 static int
fgen(pat,resarry,len)5982 fgen(pat,resarry,len) char *pat,*resarry[]; int len; {
5983 struct path *head;
5984 char *sptr, *s;
5985 int n;
5986
5987 #ifdef aegis
5988 char *namechars;
5989 int tilde = 0, bquote = 0;
5990
5991 if ((namechars = getenv("NAMECHARS")) != NULL) {
5992 if (ckstrchr(namechars, '~' ) != NULL) tilde = '~';
5993 if (ckstrchr(namechars, '\\') != NULL) bslash = '\\';
5994 if (ckstrchr(namechars, '`' ) != NULL) bquote = '`';
5995 } else {
5996 tilde = '~'; bslash = '\\'; bquote = '`';
5997 }
5998 sptr = scratch;
5999
6000 /* copy "`node_data", etc. anchors */
6001 if (bquote && *pat == bquote)
6002 while (*pat && *pat != '/' && *pat != bslash)
6003 *sptr++ = *pat++;
6004 else if (tilde && *pat == tilde)
6005 *sptr++ = *pat++;
6006 while (*pat == '/')
6007 *sptr++ = *pat++;
6008 if (sptr == scratch) {
6009 strcpy(scratch,"./"); /* safe */
6010 sptr = scratch+2;
6011 }
6012 if (!(head = splitpath(pat))) return(-1);
6013
6014 #else /* not aegis */
6015
6016 debug(F111,"fgen pat",pat,len);
6017 debug(F110,"fgen current directory",zgtdir(),0);
6018 debug(F101,"fgen stathack","",stathack);
6019
6020 scratch[0] = '\0';
6021 xpatwild = 0;
6022 xleafwild = 0;
6023 xpatabsolute = 0;
6024
6025 if (!(head = splitpath(pat))) /* Make the path segment list */
6026 return(-1);
6027
6028 sptr = scratch;
6029
6030 #ifdef COMMENT
6031 if (strncmp(pat,"./",2) && strncmp(pat,"../",3)) {
6032 #endif /* COMMENT */
6033 if (!ISDIRSEP(*pat)) /* If name is not absolute */
6034 *sptr++ = '.'; /* put "./" in front. */
6035 *sptr++ = DIRSEP;
6036 #ifdef COMMENT
6037 }
6038 #endif /* COMMENT */
6039 *sptr = '\0';
6040 #endif /* aegis */
6041
6042 makestr(&xpat,pat); /* Save copy of original pattern */
6043 debug(F110,"fgen scratch",scratch,0);
6044
6045 for (n = 0, s = xpat; *s; s++) /* How many slashes in the pattern */
6046 if (*s == DIRSEP) /* since these are fences for */
6047 n++; /* pattern matching */
6048 xpatslash = n;
6049 debug(F101,"fgen xpatslash","",xpatslash);
6050
6051 numfnd = 0; /* None found yet */
6052
6053 if (initspace(resarry,ssplen) < 0)
6054 return(-1);
6055
6056 xpatwild = iswild(xpat); /* Original pattern is wild? */
6057 xpatabsolute = isabsolute(xpat);
6058 xleafwild = iswild(xpatlast);
6059
6060 debug(F111,"fgen xpat",xpat,xpatwild);
6061 debug(F111,"fgen xpatlast",xpatlast,xleafwild);
6062 debug(F101,"fgen xpatabsolute","",xpatabsolute);
6063
6064 traverse(head,scratch,sptr); /* Go walk the directory tree. */
6065 while (head != NULL) { /* Done - free path segment list. */
6066 struct path *next = head -> fwd;
6067 free((char *)head);
6068 head = next;
6069 }
6070 debug(F101,"fgen","",numfnd);
6071 return(numfnd); /* Return the number of matches */
6072 }
6073
6074 /* Define LONGFN (long file names) automatically for BSD 2.9 and 4.2 */
6075 /* LONGFN can also be defined on the cc command line. */
6076
6077 #ifdef BSD29
6078 #ifndef LONGFN
6079 #define LONGFN
6080 #endif
6081 #endif
6082
6083 #ifdef BSD42
6084 #ifndef LONGFN
6085 #define LONGFN
6086 #endif
6087 #endif
6088
6089 /*
6090 T R A V E R S E -- Traverse a directory tree.
6091
6092 Walks the directory tree looking for matches to its arguments.
6093 The algorithm is, briefly:
6094
6095 If the current pattern segment contains no wildcards, that
6096 segment is added to what we already have. If the name so far
6097 exists, we call ourselves recursively with the next segment
6098 in the pattern string; otherwise, we just return.
6099
6100 If the current pattern segment contains wildcards, we open the name
6101 we've accumulated so far (assuming it is really a directory), then read
6102 each filename in it, and, if it matches the wildcard pattern segment, add
6103 that filename to what we have so far and call ourselves recursively on
6104 the next segment.
6105
6106 Finally, when no more pattern segments remain, we add what's accumulated
6107 so far to the result array and increment the number of matches.
6108
6109 Inputs:
6110 A pattern path list (as generated by splitpath), a string pointer that
6111 points to what we've traversed so far (this can be initialized to "/"
6112 to start the search at the root directory, or to "./" to start the
6113 search at the current directory), and a string pointer to the end of
6114 the string in the previous argument, plus the global "recursive",
6115 "xmatchdot", and "xdironly" flags.
6116
6117 Returns: void, with:
6118 mtchs[] containing the array of filename string pointers, and:
6119 numfnd containing the number of filenames.
6120
6121 Although it might be poor practice, the mtchs[] array is revealed to the
6122 outside in case it needs it; for example, to be sorted prior to use.
6123 (It is poor practice because not all platforms implement file lists the
6124 same way; some don't use an array at all.)
6125
6126 Note that addresult() acts as a second-level filter; due to selection
6127 criteria outside of the pattern, it might decline to add files that
6128 this routine asks it to, e.g. because we are collecting only directory
6129 names but not the names of regular files.
6130
6131 WARNING: In the course of C-Kermit 7.0 development, this routine became
6132 ridiculously complex, in order to meet approximately sixty specific
6133 requirements. DON'T EVEN THINK ABOUT MODIFYING THIS ROUTINE! Trust me;
6134 it is not possible to fix anything in it without breaking something else.
6135 This routine badly needs a total redesign and rewrite. Note: There may
6136 be some good applications for realpath() and/or scandir() and/or fts_blah()
6137 here, on platforms where they are available.
6138 */
6139 static VOID
traverse(pl,sofar,endcur)6140 traverse(pl,sofar,endcur) struct path *pl; char *sofar, *endcur; {
6141
6142 /* Appropriate declarations for directory routines and structures */
6143 /* #define OPENDIR means to use opendir(), readdir(), closedir() */
6144 /* If OPENDIR not defined, we use open(), read(), close() */
6145
6146 #ifdef DIRENT /* New way, <dirent.h> */
6147 #define OPENDIR
6148 DIR *fd, *opendir();
6149 struct dirent *dirbuf;
6150 struct dirent *readdir();
6151 #else /* !DIRENT */
6152 #ifdef LONGFN /* Old way, <dir.h> with opendir() */
6153 #define OPENDIR
6154 DIR *fd, *opendir();
6155 struct direct *dirbuf;
6156 #else /* !LONGFN */
6157 int fd; /* Old way, <dir.h> with open() */
6158 struct direct dir_entry;
6159 struct direct *dirbuf = &dir_entry;
6160 #endif /* LONGFN */
6161 #endif /* DIRENT */
6162 int mopts = 0; /* ckmatch() opts */
6163 int depth = 0; /* Directory tree depth */
6164
6165 char nambuf[MAXNAMLEN+4]; /* Buffer for a filename */
6166 int itsadir = 0, segisdir = 0, itswild = 0, mresult, n, x /* , y */ ;
6167 struct stat statbuf; /* For file info. */
6168
6169 debug(F101,"STAT","",16);
6170 if (pl == NULL) { /* End of path-segment list */
6171 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6172 debug(F110,"traverse add: end of path segment",sofar,0);
6173 addresult(sofar,-1);
6174 return;
6175 }
6176 if (stathack) {
6177 /* This speeds up the search a lot and we still get good results */
6178 /* but it breaks the tagging of directory names done in addresult */
6179 if (xrecursive || xfilonly || xdironly || xpatslash) {
6180 itsadir = xisdir(sofar);
6181 debug(F101,"STAT","",17);
6182 } else
6183 itsadir = (strncmp(sofar,"./",2) == 0);
6184 } else {
6185 itsadir = xisdir(sofar);
6186 debug(F101,"STAT","",18);
6187 }
6188 debug(F111,"traverse entry sofar",sofar,itsadir);
6189
6190 #ifdef CKSYMLINK /* We're doing symlinks? */
6191 #ifdef USE_LSTAT /* OK to use lstat()? */
6192 if (itsadir && xnolinks) { /* If not following symlinks */
6193 int x;
6194 struct stat buf;
6195 x = lstat(sofar,&buf);
6196 debug(F111,"traverse lstat 1",sofar,x);
6197 if (x > -1 &&
6198 #ifdef S_ISLNK
6199 S_ISLNK(buf.st_mode)
6200 #else
6201 #ifdef _IFLNK
6202 ((_IFMT & buf.st_mode) == _IFLNK)
6203 #endif /* _IFLNK */
6204 #endif /* S_ISLNK */
6205 )
6206 itsadir = 0;
6207 }
6208 #endif /* USE_LSTAT */
6209 #endif /* CKSYMLINK */
6210
6211 if (!xmatchdot && xpatlast[0] == '.')
6212 xmatchdot = 1;
6213 if (!xmatchdot && xpat[0] == '.' && xpat[1] != '/' && xpat[1] != '.')
6214 xmatchdot = 1;
6215
6216 /* ckmatch() options */
6217
6218 if (xmatchdot) mopts |= 1; /* Match dot */
6219 if (!xrecursive) mopts |= 2; /* Dirsep is fence */
6220
6221 debug(F111,"traverse entry xpat",xpat,xpatslash);
6222 debug(F111,"traverse entry xpatlast",xpatlast,xmatchdot);
6223 debug(F110,"traverse entry pl -> npart",pl -> npart,0);
6224
6225 #ifdef RECURSIVE
6226 if (xrecursive > 0 && !itsadir) {
6227 char * s; /* Recursive descent and this is a regular file */
6228 *--endcur = '\0'; /* Terminate string, overwrite trailing slash */
6229
6230 /* Find the nth slash from the right and match from there... */
6231 /* (n == the number of slashes in the original pattern - see fgen) */
6232 if (*sofar == '/') {
6233 debug(F110,"traverse xpatslash absolute",sofar,0);
6234 s = sofar;
6235 } else {
6236 debug(F111,"traverse xpatslash relative",sofar,xpatslash);
6237 for (s = endcur - 1, n = 0; s >= sofar; s--) {
6238 if (*s == '/') {
6239 if (++n >= xpatslash) {
6240 s++;
6241 break;
6242 }
6243 }
6244 }
6245 }
6246 #ifndef NOSKIPMATCH
6247 /* This speeds things up a bit. */
6248 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6249 if (xpat[0] == '*' && !xpat[1])
6250 x = xmatchdot ? 1 : (s[0] != '.');
6251 else
6252 #endif /* NOSKIPMATCH */
6253 x = ckmatch(xpat, s, 1, mopts); /* Match with original pattern */
6254 debug(F111,"traverse xpatslash ckmatch",s,x);
6255 if (x > 0) {
6256 debug(F110,"traverse add: recursive, match, && !isdir",sofar,0);
6257 addresult(sofar,itsadir);
6258 }
6259 return;
6260 }
6261 #endif /* RECURSIVE */
6262
6263 debug(F111,"traverse sofar 2",sofar,0);
6264
6265 segisdir = ((pl -> fwd) == NULL) ? 0 : 1;
6266 itswild = wildena ? (iswild(pl -> npart)) : 0; /* 15 Jun 2005 */
6267
6268 debug(F111,"traverse segisdir",sofar,segisdir);
6269 debug(F111,"traverse itswild ",pl -> npart,itswild);
6270
6271 #ifdef RECURSIVE
6272 if (xrecursive > 0) { /* If recursing and... */
6273 if (segisdir && itswild) /* this is a dir and npart is wild */
6274 goto blah; /* or... */
6275 else if (!xpatabsolute && !xpatwild) /* search object is nonwild */
6276 goto blah; /* then go recurse */
6277 }
6278 #endif /* RECURSIVE */
6279
6280 if (!itswild) { /* This path segment not wild? */
6281 #ifdef COMMENT
6282 strcpy(endcur,pl -> npart); /* (safe) Append next part. */
6283 endcur += (int)strlen(pl -> npart); /* Advance end pointer */
6284 #else
6285 /*
6286 strcpy() does not account for quoted metacharacters.
6287 We must remove the quotes before doing the stat().
6288 */
6289 {
6290 int quote = 0;
6291 char c, * s;
6292 s = pl -> npart;
6293 while ((c = *s++)) {
6294 if (!quote) {
6295 if (c == CMDQ) {
6296 quote = 1;
6297 continue;
6298 }
6299 }
6300 *endcur++ = c;
6301 quote = 0;
6302 }
6303 }
6304 #endif /* COMMENT */
6305 *endcur = '\0'; /* End new current string. */
6306
6307 if (stat(sofar,&statbuf) == 0) { /* If this piece exists... */
6308 debug(F110,"traverse exists",sofar,0);
6309 *endcur++ = DIRSEP; /* add slash to end */
6310 *endcur = '\0'; /* and end the string again. */
6311 traverse(pl -> fwd, sofar, endcur);
6312 }
6313 #ifdef DEBUG
6314 else debug(F110,"traverse not found", sofar, 0);
6315 #endif /* DEBUG */
6316 return;
6317 }
6318
6319 *endcur = '\0'; /* End current string */
6320 debug(F111,"traverse sofar 3",sofar,0);
6321
6322 if (!itsadir)
6323 return;
6324
6325 /* Search is recursive or ... */
6326 /* path segment contains wildcards, have to open and search directory. */
6327
6328 blah:
6329
6330 debug(F110,"traverse opening directory", sofar, 0);
6331
6332 #ifdef OPENDIR
6333 debug(F110,"traverse opendir()",sofar,0);
6334 if ((fd = opendir(sofar)) == NULL) { /* Can't open, fail. */
6335 debug(F101,"traverse opendir() failed","",errno);
6336 return;
6337 }
6338 while ((dirbuf = readdir(fd)))
6339 #else /* !OPENDIR */
6340 debug(F110,"traverse directory open()",sofar,0);
6341 if ((fd = open(sofar,O_RDONLY)) < 0) {
6342 debug(F101,"traverse directory open() failed","",errno);
6343 return;
6344 }
6345 while (read(fd, (char *)dirbuf, sizeof dir_entry) > 0)
6346 #endif /* OPENDIR */
6347 { /* Read each entry in this directory */
6348 int exists;
6349 char *eos, *s;
6350 exists = 0;
6351
6352 /* On some platforms, the read[dir]() can return deleted files, */
6353 /* e.g. HP-UX 5.00. There is no point in grinding through this */
6354 /* routine when the file doesn't exist... */
6355
6356 if ( /* There actually is an inode... */
6357 #ifdef BSD42
6358 dirbuf->d_ino != -1
6359 #else
6360 #ifdef unos
6361 dirbuf->d_ino != -1
6362 #else
6363 #ifdef QNX
6364 dirbuf->d_stat.st_ino != 0
6365 #else
6366 #ifdef SOLARIS
6367 dirbuf->d_ino != 0
6368 #else
6369 #ifdef sun
6370 dirbuf->d_fileno != 0
6371 #else
6372 #ifdef bsdi
6373 dirbuf->d_fileno != 0
6374 #else
6375 #ifdef __386BSD__
6376 dirbuf->d_fileno != 0
6377 #else
6378 #ifdef __FreeBSD__
6379 dirbuf->d_fileno != 0
6380 #else
6381 #ifdef ultrix
6382 dirbuf->gd_ino != 0
6383 #else
6384 #ifdef Plan9
6385 1
6386 #else
6387 dirbuf->d_ino != 0
6388 #endif /* Plan9 */
6389 #endif /* ultrix */
6390 #endif /* __FreeBSD__ */
6391 #endif /* __386BSD__ */
6392 #endif /* bsdi */
6393 #endif /* sun */
6394 #endif /* SOLARIS */
6395 #endif /* QNX */
6396 #endif /* unos */
6397 #endif /* BSD42 */
6398 )
6399 exists = 1;
6400 if (!exists)
6401 continue;
6402
6403 ckstrncpy(nambuf, /* Copy the name */
6404 dirbuf->d_name,
6405 MAXNAMLEN
6406 );
6407 if (nambuf[0] == '.') {
6408 if (!nambuf[1] || (nambuf[1] == '.' && !nambuf[2])) {
6409 debug(F110,"traverse skipping",nambuf,0);
6410 continue; /* skip "." and ".." */
6411 }
6412 }
6413 s = nambuf; /* Copy name to end of sofar */
6414 eos = endcur;
6415 while ((*eos = *s)) {
6416 s++;
6417 eos++;
6418 }
6419 /*
6420 Now we check the file for (a) whether it is a directory, and (b) whether
6421 its name matches our pattern. If it is a directory, and if we have been
6422 told to build a recursive list, then we must descend regardless of whether
6423 it matches the pattern. If it is not a directory and it does not match
6424 our pattern, we skip it. Note: sofar is the full pathname, nambuf is
6425 the name only.
6426 */
6427 /* Do this first to save pointless function calls */
6428 if (nambuf[0] == '.' && !xmatchdot) /* Dir name starts with '.' */
6429 continue;
6430 if (stathack) {
6431 if (xrecursive || xfilonly || xdironly || xpatslash) {
6432 itsadir = xisdir(sofar); /* See if it's a directory */
6433 debug(F101,"STAT","",19);
6434 } else {
6435 itsadir = 0;
6436 }
6437 } else {
6438 itsadir = xisdir(sofar);
6439 debug(F101,"STAT","",20);
6440 }
6441
6442 #ifdef CKSYMLINK
6443 #ifdef USE_LSTAT
6444 if (itsadir && xnolinks) { /* If not following symlinks */
6445 int x;
6446 struct stat buf;
6447 x = lstat(sofar,&buf);
6448 debug(F111,"traverse lstat 2",sofar,x);
6449 if (x > -1 &&
6450 #ifdef S_ISLNK
6451 S_ISLNK(buf.st_mode)
6452 #else
6453 #ifdef _IFLNK
6454 ((_IFMT & buf.st_mode) == _IFLNK)
6455 #endif /* _IFLNK */
6456 #endif /* S_ISLNK */
6457 )
6458 itsadir = 0;
6459 }
6460 #endif /* USE_LSTAT */
6461 #endif /* CKSYMLINK */
6462
6463 #ifdef RECURSIVE
6464 if (xrecursive > 0 && itsadir &&
6465 (xpatlast[0] == '*') && !xpatlast[1]
6466 ) {
6467 debug(F110,
6468 "traverse add: recursive && isdir && segisdir or match",
6469 sofar,
6470 segisdir
6471 );
6472 addresult(sofar,itsadir);
6473 if (numfnd < 0) return;
6474 }
6475 #endif /* RECURSIVE */
6476
6477 debug(F111,"traverse mresult xpat",xpat,xrecursive);
6478 debug(F111,"traverse mresult pl -> npart",
6479 pl -> npart,
6480 ((pl -> fwd) ? 9999 : 0)
6481 );
6482 debug(F111,"traverse mresult sofar segisdir",sofar,segisdir);
6483 debug(F111,"traverse mresult sofar itsadir",sofar,itsadir);
6484 debug(F101,"traverse mresult xmatchdot","",xmatchdot);
6485 /*
6486 Match the path so far with the pattern after stripping any leading "./"
6487 from either or both. The pattern chosen is the full original pattern if
6488 the match candidate (sofar) is not a directory, or else just the name part
6489 (pl->npart) if it is.
6490 */
6491 {
6492 char * s1; /* The pattern */
6493 char * s2 = sofar; /* The path so far */
6494 char * s3; /* Worker */
6495 int opts; /* Match options */
6496
6497 s1 = itsadir ? pl->npart : xpat;
6498
6499 #ifndef COMMENT
6500 /* I can't explain this but it unbreaks "cd blah/sub<Esc>" */
6501 if (itsadir && !xrecursive && xpatslash > 0 &&
6502 segisdir == 0 && itswild) {
6503 s1 = xpat;
6504 debug(F110,"traverse mresult s1 kludge",s1,0);
6505 }
6506 #endif /* COMMENT */
6507
6508 if (xrecursive && xpatslash == 0)
6509 s2 = nambuf;
6510 while ((s1[0] == '.') && (s1[1] == '/')) /* Strip "./" */
6511 s1 += 2;
6512 while ((s2[0] == '.') && (s2[1] == '/')) /* Ditto */
6513 s2 += 2;
6514 opts = mopts; /* Match options */
6515 if (itsadir) /* Current segment is a directory */
6516 opts = mopts & 1; /* No fences */
6517 s3 = s2; /* Get segment depth */
6518 depth = 0;
6519 while (*s3) { if (*s3++ == '/') depth++; }
6520 #ifndef NOSKIPMATCH
6521 /* This speeds things up a bit. */
6522 /* If it causes trouble define NOSKIPMATCH and rebuild. */
6523 if (depth == 0 && (s1[0] == '*') && !s1[1])
6524 mresult = xmatchdot ? 1 : (s2[0] != '.');
6525 else
6526 #endif /* NOSKIPMATCH */
6527 mresult = ckmatch(s1,s2,1,opts); /* Match */
6528 }
6529 #ifdef DEBUG
6530 if (deblog) {
6531 debug(F111,"traverse mresult depth",sofar,depth);
6532 debug(F101,"traverse mresult xpatslash","",xpatslash);
6533 debug(F111,"traverse mresult nambuf",nambuf,mresult);
6534 debug(F111,"traverse mresult itswild",pl -> npart,itswild);
6535 debug(F111,"traverse mresult segisdir",pl -> npart,segisdir);
6536 }
6537 #endif /* DEBUG */
6538 if (mresult || /* If match succeeded */
6539 xrecursive || /* Or search is recursive */
6540 depth < xpatslash /* Or not deep enough to match... */
6541 ) {
6542 if ( /* If it's not a directory... */
6543 /*
6544 The problem here is that segisdir is apparently not set appropriately.
6545 If I leave in the !segisdir test, then "dir /recursive blah" (where blah is
6546 a directory name) misses some regular files because sometimes segisdir
6547 is set and sometimes it's not. But if I comment it out, then
6548 "dir <star>/<star>.txt lists every file in * and does not even open up the
6549 subdirectories. However, "dir /rec <star>/<star>.txt" works right.
6550 */
6551 #ifdef COMMENT
6552 mresult && (!itsadir && !segisdir)
6553 #else
6554 mresult && /* Matched */
6555 !itsadir && /* sofar is not a directory */
6556 ((!xrecursive && !segisdir) || xrecursive)
6557 #endif /* COMMENT */
6558 ) {
6559 debug(F110,
6560 "traverse add: match && !itsadir",sofar,0);
6561 addresult(sofar,itsadir);
6562 if (numfnd < 0) return;
6563 } else if (itsadir && (xrecursive || mresult)) {
6564 struct path * xx = NULL;
6565 *eos++ = DIRSEP; /* Add directory separator */
6566 *eos = '\0'; /* to end of segment */
6567 #ifdef RECURSIVE
6568 /* Copy previous pattern segment to this new directory */
6569
6570 if (xrecursive > 0 && !(pl -> fwd)) {
6571 xx = (struct path *) malloc(sizeof (struct path));
6572 pl -> fwd = xx;
6573 if (xx) {
6574 xx -> fwd = NULL;
6575 strcpy(xx -> npart, pl -> npart); /* safe */
6576 }
6577 }
6578 #endif /* RECURSIVE */
6579 traverse(pl -> fwd, sofar, eos); /* Traverse new directory */
6580 }
6581 }
6582 }
6583 #ifdef OPENDIR
6584 closedir(fd);
6585 #else /* !OPENDIR */
6586 close(fd);
6587 #endif /* OPENDIR */
6588 }
6589
6590 /*
6591 * addresult:
6592 * Adds a result string to the result array. Increments the number
6593 * of matches found, copies the found string into our string
6594 * buffer, and puts a pointer to the buffer into the caller's result
6595 * array. Our free buffer pointer is updated. If there is no
6596 * more room in the caller's array, the number of matches is set to -1.
6597 * Input: a result string.
6598 * Returns: nothing.
6599 */
6600 static VOID
addresult(str,itsadir)6601 addresult(str,itsadir) char *str; int itsadir; {
6602 int len;
6603
6604 if (!freeptr) {
6605 debug(F100,"addresult string space not init'd","",0);
6606 initspace(mtchs,ssplen);
6607 }
6608 if (!str) str = "";
6609 debug(F111,"addresult",str,itsadir);
6610 if (!*str)
6611 return;
6612
6613 if (itsadir < 0) {
6614 itsadir = xisdir(str);
6615 }
6616 if ((xdironly && !itsadir) || (xfilonly && itsadir)) {
6617 debug(F111,"addresult skip",str,itsadir);
6618 return;
6619 }
6620 while (str[0] == '.' && ISDIRSEP(str[1])) /* Strip all "./" from front */
6621 str += 2;
6622 if (--remlen < 0) { /* Elements left in array of names */
6623 debug(F111,"addresult ARRAY FULL",str,numfnd);
6624 numfnd = -1;
6625 return;
6626 }
6627 len = (int)strlen(str); /* Space this will use */
6628 debug(F111,"addresult len",str,len);
6629
6630 if (len < 1)
6631 return;
6632
6633 if ((freeptr + len + itsadir + 1) > (sspace + ssplen)) {
6634 debug(F111,"addresult OUT OF SPACE",str,numfnd);
6635 #ifdef DYNAMIC
6636 printf(
6637 "?String space %d exhausted - use SET FILE STRINGSPACE to increase\n",ssplen);
6638 #else
6639 printf("?String space %d exhausted\n",ssplen);
6640 #endif /* DYNAMIC */
6641 numfnd = -1; /* Do not record if not enough space */
6642 return;
6643 }
6644 strcpy(freeptr,str); /* safe */
6645
6646 /* Tag directory names by putting '/' at the end */
6647
6648 if (itsadir && (freeptr[len-1] == '/')) {
6649 freeptr[len++] = DIRSEP;
6650 freeptr[len] = '\0';
6651 }
6652 if (numfnd >= maxnames) {
6653 #ifdef DYNAMIC
6654 printf(
6655 "?Too many files (%d max) - use SET FILE LISTSIZE to increase\n",maxnames);
6656 #else
6657 printf("?Too many files - %d max\n",maxnames);
6658 #endif /* DYNAMIC */
6659 numfnd = -1;
6660 return;
6661 }
6662 str = freeptr;
6663 *resptr++ = freeptr;
6664 freeptr += (len + 1);
6665 numfnd++;
6666 debug(F111,"addresult ADD",str,numfnd);
6667 }
6668
6669 #ifdef COMMENT
6670 /*
6671 * match(pattern,string):
6672 * pattern matcher. Takes a string and a pattern possibly containing
6673 * the wildcard characters '*' and '?'. Returns true if the pattern
6674 * matches the string, false otherwise.
6675 * Orignally by: Jeff Damens, CUCCA, 1984
6676 * No longer used as of C-Kermit 7.0, now we use ckmatch() instead (ckclib.c).
6677 *
6678 * Input: a string and a wildcard pattern.
6679 * Returns: 1 if match, 0 if no match.
6680 */
6681 static int
match(pattern,string)6682 match(pattern, string) char *pattern, *string; {
6683 char *psave = NULL, *ssave = NULL; /* Backup pointers for failure */
6684 int q = 0; /* Quote flag */
6685
6686 if (*string == '.' && *pattern != '.' && !xmatchdot) {
6687 debug(F110,"match skip",string,0);
6688 return(0);
6689 }
6690 while (1) {
6691 for (; *pattern == *string; pattern++,string++) /* Skip first */
6692 if (*string == '\0') return(1); /* End of strings, succeed */
6693
6694 if (*pattern == '\\' && q == 0) { /* Watch out for quoted */
6695 q = 1; /* metacharacters */
6696 pattern++; /* advance past quote */
6697 if (*pattern != *string) return(0);
6698 continue;
6699 } else q = 0;
6700
6701 if (q) {
6702 return(0);
6703 } else {
6704 if (*string != '\0' && *pattern == '?') {
6705 pattern++; /* '?', let it match */
6706 string++;
6707 } else if (*pattern == '*') { /* '*' ... */
6708 psave = ++pattern; /* remember where we saw it */
6709 ssave = string; /* let it match 0 chars */
6710 } else if (ssave != NULL && *ssave != '\0') { /* if not at end */
6711 /* ...have seen a star */
6712 string = ++ssave; /* skip 1 char from string */
6713 pattern = psave; /* and back up pattern */
6714 } else return(0); /* otherwise just fail */
6715 }
6716 }
6717 }
6718 #endif /* COMMENT */
6719
6720 /*
6721 The following two functions are for expanding tilde in filenames
6722 Contributed by Howie Kaye, CUCCA, developed for CCMD package.
6723 */
6724
6725 /* W H O A M I -- Get user's username. */
6726
6727 /*
6728 1) Get real uid
6729 2) See if the $USER environment variable is set ($LOGNAME on AT&T)
6730 3) If $USER's uid is the same as ruid, realname is $USER
6731 4) Otherwise get logged in user's name
6732 5) If that name has the same uid as the real uid realname is loginname
6733 6) Otherwise, get a name for ruid from /etc/passwd
6734 */
6735 char *
whoami()6736 whoami() {
6737 #ifdef DTILDE
6738 #ifdef pdp11
6739 #define WHOLEN 100
6740 #else
6741 #define WHOLEN 257
6742 #endif /* pdp11 */
6743 static char realname[UIDBUFLEN+1]; /* user's name */
6744 static int ruid = -1; /* user's real uid */
6745 char loginname[UIDBUFLEN+1], envname[256]; /* temp storage */
6746 char *c;
6747 struct passwd *p;
6748 _PROTOTYP(extern char * getlogin, (void) );
6749
6750 debug(F111,"whoami ruid A",realname,ruid);
6751
6752 if (ruid != -1)
6753 return(realname);
6754
6755 ruid = real_uid(); /* get our uid */
6756 debug(F101,"whoami ruid B","",ruid);
6757 if (ruid < 0) ruid = getuid();
6758 debug(F101,"whoami ruid C","",ruid);
6759
6760 /* how about $USER or $LOGNAME? */
6761 if ((c = getenv(NAMEENV)) != NULL) { /* check the env variable */
6762 ckstrncpy(envname, c, 255);
6763 debug(F110,"whoami envname",envname,0);
6764 if ((p = getpwnam(envname)) != NULL) {
6765 if (p->pw_uid == ruid) { /* get passwd entry for envname */
6766 ckstrncpy(realname, envname, UIDBUFLEN); /* uid's are same */
6767 debug(F110,"whoami realname",realname,0);
6768 return(realname);
6769 }
6770 }
6771 }
6772
6773 /* can we use loginname() ? */
6774
6775 if ((c = getlogin()) != NULL) { /* name from utmp file */
6776 ckstrncpy (loginname, c, UIDBUFLEN);
6777 debug(F110,"whoami loginname",loginname,0);
6778 if ((p = getpwnam(loginname)) != NULL) /* get passwd entry */
6779 if (p->pw_uid == ruid) /* for loginname */
6780 ckstrncpy(realname, envname, UIDBUFLEN); /* if uid's are same */
6781 }
6782
6783 /* Use first name we get for ruid */
6784
6785 if ((p = getpwuid(ruid)) == NULL) { /* name for uid */
6786 debug(F101,"whoami no username for ruid","",ruid);
6787 realname[0] = '\0'; /* no user name */
6788 ruid = -1;
6789 return(NULL);
6790 }
6791 ckstrncpy(realname, p->pw_name, UIDBUFLEN);
6792 debug(F110,"whoami realname from getpwuid",realname,0);
6793 return(realname);
6794 #else
6795 return(NULL);
6796 #endif /* DTILDE */
6797 }
6798
6799 /* T I L D E _ E X P A N D -- expand ~user to the user's home directory. */
6800
6801 char *
tilde_expand(dirname)6802 tilde_expand(dirname) char *dirname; {
6803 #ifdef DTILDE
6804 #ifdef pdp11
6805 #define BUFLEN 100
6806 #else
6807 #define BUFLEN 257
6808 #endif /* pdp11 */
6809 struct passwd *user;
6810 static char olddir[BUFLEN+1];
6811 static char oldrealdir[BUFLEN+1];
6812 static char temp[BUFLEN+1];
6813 int i, j;
6814
6815 debug(F111,"tilde_expand",dirname,dirname[0]);
6816
6817 if (dirname[0] != '~') { /* Not a tilde...return param */
6818 debug(F000,"tilde_expand NOT TILDE","",dirname[0]);
6819 return(dirname);
6820 }
6821 if (!strcmp(olddir,dirname)) { /* Same as last time */
6822 debug(F110,"tilde_expand same as previous",oldrealdir,0);
6823 return(oldrealdir); /* so return old answer. */
6824 } else {
6825 debug(F110,"tilde_expand working...","",0);
6826 j = (int)strlen(dirname);
6827 for (i = 0; i < j; i++) /* find username part of string */
6828 if (!ISDIRSEP(dirname[i]))
6829 temp[i] = dirname[i];
6830 else break;
6831 temp[i] = '\0'; /* tie off with a NULL */
6832 debug(F111,"tilde_expand first part",temp,i);
6833 if (i == 1) { /* if just a "~" */
6834 #ifdef IKSD
6835 if (inserver)
6836 user = getpwnam(uidbuf); /* Get info on current user */
6837 else
6838 #endif /* IKSD */
6839 {
6840 char * p = whoami();
6841 debug(F110,"tilde_expand p",p,0);
6842 if (p) {
6843 user = getpwnam(p);
6844 debug(F110,"tilde_expand getpwpam ~",user,0);
6845 } else {
6846 user = NULL;
6847 }
6848 }
6849 } else {
6850 debug(F110,"tilde_expand ~user",&temp[1],0);
6851 user = getpwnam(&temp[1]); /* otherwise on the specified user */
6852 debug(F110,"tilde_expand getpwpam user",user,0);
6853 }
6854
6855 }
6856 if (user != NULL) { /* valid user? */
6857 ckstrncpy(olddir, dirname, BUFLEN); /* remember the directory */
6858 ckstrncpy(oldrealdir,user->pw_dir, BUFLEN); /* and home directory */
6859 ckstrncat(oldrealdir,&dirname[i], BUFLEN);
6860 oldrealdir[BUFLEN] = '\0';
6861 return(oldrealdir);
6862 } else { /* invalid? */
6863 ckstrncpy(olddir, dirname, BUFLEN); /* remember for next time */
6864 ckstrncpy(oldrealdir, dirname, BUFLEN);
6865 return(oldrealdir);
6866 }
6867 #else
6868 return(NULL);
6869 #endif /* DTILDE */
6870 }
6871
6872 /*
6873 Functions for executing system commands.
6874 zsyscmd() executes the system command in the normal, default way for
6875 the system. In UNIX, it does what system() does. Thus, its results
6876 are always predictable.
6877 zshcmd() executes the command using the user's preferred shell.
6878 */
6879 int
zsyscmd(s)6880 zsyscmd(s) char *s; {
6881 #ifdef aegis
6882 if (nopush) return(-1);
6883 if (!priv_chk()) return(system(s));
6884 #else
6885 PID_T shpid;
6886 #ifdef COMMENT
6887 /* This doesn't work... */
6888 WAIT_T status;
6889 #else
6890 int status;
6891 #endif /* COMMENT */
6892
6893 if (nopush) return(-1);
6894 if ((shpid = fork())) {
6895 if (shpid < (PID_T)0) return(-1); /* Parent */
6896 while (shpid != (PID_T) wait(&status))
6897 ;
6898 return(status);
6899 }
6900 if (priv_can()) { /* Child: cancel any priv's */
6901 printf("?Privilege cancellation failure\n");
6902 _exit(255);
6903 }
6904 restorsigs(); /* Restore ignored signals */
6905 #ifdef HPUX10
6906 execl("/usr/bin/sh","sh","-c",s,NULL);
6907 perror("/usr/bin/sh");
6908 #else
6909 #ifdef Plan9
6910 execl("/bin/rc", "rc", "-c", s, NULL);
6911 perror("/bin/rc");
6912 #else
6913 execl("/bin/sh","sh","-c",s,NULL);
6914 perror("/bin/sh");
6915 #endif /* Plan9 */
6916 #endif /* HPUX10 */
6917 _exit(255);
6918 return(0); /* Shut up ANSI compilers. */
6919 #endif /* aegis */
6920 }
6921
6922
6923 /* Z _ E X E C -- Overlay ourselves with another program */
6924
6925 #ifndef NOZEXEC
6926 #ifdef HPUX5
6927 #define NOZEXEC
6928 #else
6929 #ifdef ATT7300
6930 #define NOZEXEC
6931 #endif /* ATT7300 */
6932 #endif /* HPUX5 */
6933 #endif /* NOZEXEC */
6934
6935 VOID
z_exec(p,s,t)6936 z_exec(p,s,t) char * p, ** s; int t; { /* Overlay ourselves with "p s..." */
6937 #ifdef NOZEXEC
6938 printf("EXEC /REDIRECT NOT IMPLEMENTED IN THIS VERSION OF C-KERMIT\n");
6939 debug(F110,"z_exec NOT IMPLEMENTED",p,0);
6940 #else
6941 int x;
6942 extern int ttyfd;
6943 debug(F110,"z_exec command",p,0);
6944 debug(F110,"z_exec arg 0",s[0],0);
6945 debug(F110,"z_exec arg 1",s[1],0);
6946 debug(F101,"z_exec t","",t);
6947 errno = 0;
6948 if (t) {
6949 if (ttyfd > 2) {
6950 dup2(ttyfd, 0);
6951 dup2(ttyfd, 1);
6952 /* dup2(ttyfd, 2); */
6953 close(ttyfd);
6954 }
6955 }
6956 restorsigs(); /* Restore ignored signals */
6957 x = execvp(p,s);
6958 if (x < 0) debug(F101,"z_exec errno","",errno);
6959 #endif /* NOZEXEC */
6960 }
6961
6962 /*
6963 Z S H C M D -- Execute a shell command (or program thru the shell).
6964
6965 Original UNIX code by H. Fischer; copyright rights assigned to Columbia U.
6966 Adapted to use getpwuid to find login shell because many systems do not
6967 have SHELL in environment, and to use direct calling of shell rather
6968 than intermediate system() call. -- H. Fischer (1985); many changes since
6969 then. Call with s pointing to command to execute. Returns:
6970 -1 on failure to start the command (can't find, can't fork, can't run).
6971 1 if command ran and gave an exit status of 0.
6972 0 if command ran and gave a nonzero exit status.
6973 with pexitstatus containing the command's exit status.
6974 */
6975 int
zshcmd(s)6976 zshcmd(s) char *s; {
6977 PID_T pid;
6978
6979 #ifdef NOPUSH
6980 return(0);
6981 #else
6982 if (nopush) return(-1);
6983 if (!s) return(-1);
6984 while (*s == ' ') s++;
6985
6986 debug(F110,"zshcmd command",s,0);
6987
6988 #ifdef aegis
6989 if ((pid = vfork()) == 0) { /* Make child quickly */
6990 char *shpath, *shname, *shptr; /* For finding desired shell */
6991
6992 if (priv_can()) exit(1); /* Turn off privs. */
6993 if ((shpath = getenv("SHELL")) == NULL) shpath = "/com/sh";
6994
6995 #else /* All Unix systems */
6996 if ((pid = fork()) == 0) { /* Make child */
6997 char *shpath, *shname, *shptr; /* For finding desired shell */
6998 struct passwd *p;
6999 #ifdef HPUX10 /* Default */
7000 char *defshell = "/usr/bin/sh";
7001 #else
7002 #ifdef Plan9
7003 char *defshell = "/bin/rc";
7004 #else
7005 char *defshell = "/bin/sh";
7006 #endif /* Plan9 */
7007 #endif /* HPUX10 */
7008 if (priv_can()) exit(1); /* Turn off privs. */
7009 #ifdef COMMENT
7010 /* Old way always used /etc/passwd shell */
7011 p = getpwuid(real_uid()); /* Get login data */
7012 if (p == (struct passwd *) NULL || !*(p->pw_shell))
7013 shpath = defshell;
7014 else
7015 shpath = p->pw_shell;
7016 #else
7017 /* New way lets user override with SHELL variable, but does not rely on it. */
7018 /* This allows user to specify a different shell. */
7019 shpath = getenv("SHELL"); /* What shell? */
7020 debug(F110,"zshcmd SHELL",shpath,0);
7021 {
7022 int x = 0;
7023 if (!shpath) {
7024 x++;
7025 } else if (!*shpath) {
7026 x++;
7027 }
7028 if (x) {
7029 debug(F100,"zshcmd SHELL not defined","",0);
7030 p = getpwuid( real_uid() ); /* Get login data */
7031 if (p == (struct passwd *)NULL || !*(p->pw_shell)) {
7032 shpath = defshell;
7033 } else {
7034 shpath = p->pw_shell;
7035 }
7036 debug(F110,"zshcmd shpath from getpwuid",shpath,0);
7037 }
7038 }
7039 #endif /* COMMENT */
7040 #endif /* aegis */
7041 shptr = shname = shpath;
7042 while (*shptr != '\0')
7043 if (*shptr++ == DIRSEP)
7044 shname = shptr;
7045 restorsigs(); /* Restore ignored signals */
7046 debug(F110,"zshcmd execl shpath",shpath,0);
7047 debug(F110,"zshcmd execl shname",shname,0);
7048 if (s == NULL || *s == '\0') { /* Interactive shell requested? */
7049 debug(F100,"zshcmd execl interactive","",0);
7050 execl(shpath,shname,"-i",NULL); /* Yes, do that */
7051 } else { /* Otherwise, */
7052 debug(F110,"zshcmd execl command",s,0);
7053 execl(shpath,shname,"-c",s,NULL); /* exec the given command */
7054 } /* If execl() failed, */
7055 debug(F101,"zshcmd errno","",errno);
7056 perror(shpath); /* print reason and */
7057 exit(BAD_EXIT); /* return bad return code. */
7058
7059 } else { /* Parent */
7060
7061 int wstat; /* ... must wait for child */
7062 #ifdef CK_CHILD
7063 int child; /* Child's exit status */
7064 #endif /* CK_CHILD */
7065 SIGTYP (*istat)(), (*qstat)();
7066
7067 if (pid == (PID_T) -1) return(-1); /* fork() failed? */
7068
7069 istat = signal(SIGINT,SIG_IGN); /* Let the fork handle keyboard */
7070 qstat = signal(SIGQUIT,SIG_IGN); /* interrupts itself... */
7071
7072 debug(F110,"zshcmd parent waiting for child",s,0);
7073 #ifdef CK_CHILD
7074 while (((wstat = wait(&child)) != pid) && (wstat != -1))
7075 #else
7076 while (((wstat = wait((WAIT_T *)0)) != pid) && (wstat != -1))
7077 #endif /* CK_CHILD */
7078 ; /* Wait for fork */
7079 signal(SIGINT,istat); /* Restore interrupts */
7080 signal(SIGQUIT,qstat);
7081 #ifdef CK_CHILD
7082 pexitstat = (child & 0xff) ? child : child >> 8;
7083 debug(F101,"zshcmd exit status","",pexitstat);
7084 return(child == 0 ? 1 : 0); /* Return child's status */
7085 #endif /* CK_CHILD */
7086 }
7087 return(1);
7088 #endif /* NOPUSH */
7089 }
7090
7091 /* I S W I L D -- Check if filespec is "wild" */
7092
7093 /*
7094 Returns:
7095 0 wildcards disabled or argument is empty or is the name of a single file;
7096 1 if it contains wildcard characters.
7097 Note: must match the algorithm used by match(), hence no [a-z], etc.
7098 */
7099 int
iswild(filespec)7100 iswild(filespec) char *filespec; {
7101 char c, *p, *f; int x;
7102 int quo = 0;
7103 if (!filespec) /* Safety */
7104 return(0);
7105 if (!wildena) /* Wildcards disabled - 12 Jun 2005 */
7106 return(0);
7107 f = filespec;
7108 if (wildxpand) { /* Shell handles wildcarding */
7109 if ((x = nzxpand(filespec,0)) > 1)
7110 return(1);
7111 if (x == 0) return(0); /* File does not exist */
7112 p = malloc(MAXNAMLEN + 20);
7113 znext(p);
7114 x = (strcmp(filespec,p) != 0);
7115 free(p);
7116 p = NULL;
7117 return(x);
7118 } else { /* We do it ourselves */
7119 while ((c = *filespec++) != '\0') {
7120 if (c == '\\' && quo == 0) {
7121 quo = 1;
7122 continue;
7123 }
7124 if (!quo && (c == '*' || c == '?'
7125 #ifdef CKREGEX
7126 #ifndef VMS
7127 || c == '['
7128 #endif /* VMS */
7129 || c == '{'
7130 #endif /* CKREGEX */
7131 )) {
7132 debug(F111,"iswild",f,1);
7133 return(1);
7134 }
7135 quo = 0;
7136 }
7137 debug(F111,"iswild",f,0);
7138 return(0);
7139 }
7140 }
7141
7142 /*
7143 I S D I R -- Is a Directory.
7144
7145 Tell if string pointer s is the name of an existing directory. Returns 1 if
7146 directory, 0 if not a directory.
7147
7148 The following no longer applies:
7149
7150 If the file is a symlink, we return 1 if
7151 it is a directory OR if it is a link to a directory and the "xrecursive" flag
7152 is NOT set. This is to allow parsing a link to a directory as if it were a
7153 directory (e.g. in the CD or IF DIRECTORY command) but still prevent
7154 recursive traversal from visiting the same directory twice.
7155 */
7156
7157 #ifdef ISDIRCACHE
7158 /* This turns out to be unsafe and gives little benefit anyway. */
7159 /* See notes 28 Sep 2003. Thus ISDIRCACHE is not defined. */
7160
7161 static char prevpath[CKMAXPATH+4] = { '\0', '\0' };
7162 static int prevstat = -1;
7163 int
clrdircache()7164 clrdircache() {
7165 debug(F100,"CLEAR ISDIR CACHE","",0);
7166 prevstat = -1;
7167 prevpath[0] = NUL;
7168 }
7169 #endif /* ISDIRCACHE */
7170
7171 int
isalink(s)7172 isalink(s) char *s; {
7173 #ifndef CKSYMLINK
7174 return(0);
7175 #else
7176 int r = 0;
7177 char filbuf[CKMAXPATH+4];
7178 if (readlink(s,filbuf,CKMAXPATH) > -1)
7179 r = 1;
7180 debug(F110,"isalink readlink",s,r);
7181 return(r);
7182 #endif /* CKSYMLINK */
7183 }
7184
7185 int
isdir(s)7186 isdir(s) char *s; {
7187 int x, needrlink = 0, islink = 0;
7188 struct stat statbuf;
7189 char fnam[CKMAXPATH+4];
7190
7191 if (!s) return(0);
7192 debug(F110,"isdir entry",s,0);
7193 #ifdef DTILDE /* 2005-08-13 */
7194 if (*s == '~') { /* Starts with tilde? */
7195 s = tilde_expand(s); /* Attempt to expand tilde */
7196 if (!s) s = "";
7197 debug(F110,"isdir tilde_expand",s,0);
7198 }
7199 #endif /* DTILDE */
7200 if (!*s) return(0);
7201
7202 #ifdef ISDIRCACHE
7203 if (prevstat > -1) {
7204 if (s[0] == prevpath[0]) {
7205 if (!strcmp(s,prevpath)) {
7206 debug(F111,"isdir cache hit",s,prevstat);
7207 return(prevstat);
7208 }
7209 }
7210 }
7211 #endif /* ISDIRCACHE */
7212
7213 #ifdef CKSYMLINK
7214 #ifdef COMMENT
7215 /*
7216 The following over-clever bit has been commented out because it presumes
7217 to know when a symlink might be redundant, which it can't possibly know.
7218 Using plain old stat() gives Kermit the same results as ls and ls -R, which
7219 is just fine: no surprises.
7220 */
7221 #ifdef USE_LSTAT
7222 if (xrecursive) {
7223 x = lstat(s,&statbuf);
7224 debug(F111,"isdir lstat",s,x);
7225 } else {
7226 #endif /* USE_LSTAT */
7227 x = stat(s,&statbuf);
7228 debug(F111,"isdir stat",s,x);
7229 #ifdef USE_LSTAT
7230 }
7231 #endif /* USE_LSTAT */
7232 #else
7233 x = stat(s,&statbuf);
7234 debug(F111,"isdir stat",s,x);
7235 #endif /* COMMENT */
7236 if (x == -1) {
7237 debug(F101,"isdir errno","",errno);
7238 return(0);
7239 }
7240 islink = 0;
7241 if (xrecursive) {
7242 #ifdef NOLINKBITS
7243 needrlink = 1;
7244 #else
7245 #ifdef S_ISLNK
7246 islink = S_ISLNK(statbuf.st_mode);
7247 debug(F101,"isdir S_ISLNK islink","",islink);
7248 #else
7249 #ifdef _IFLNK
7250 islink = (_IFMT & statbuf.st_mode) == _IFLNK;
7251 debug(F101,"isdir _IFLNK islink","",islink);
7252 #endif /* _IFLNK */
7253 #endif /* S_ISLNK */
7254 #endif /* NOLINKBITS */
7255 if (needrlink) {
7256 if (readlink(s,fnam,CKMAXPATH) > -1)
7257 islink = 1;
7258 }
7259 }
7260 #else
7261 x = stat(s,&statbuf);
7262 if (x == -1) {
7263 debug(F101,"isdir errno","",errno);
7264 return(0);
7265 }
7266 debug(F111,"isdir stat",s,x);
7267 #endif /* CKSYMLINK */
7268 debug(F101,"isdir islink","",islink);
7269 debug(F101,"isdir statbuf.st_mode","",statbuf.st_mode);
7270 x = islink ? 0 : (S_ISDIR (statbuf.st_mode) ? 1 : 0);
7271 #ifdef ISDIRCACHE
7272 prevstat = x;
7273 ckstrncpy(prevpath,s,CKMAXPATH+1);
7274 #endif /* ISDIRCACHE */
7275 return(x);
7276 }
7277
7278 #ifdef CK_MKDIR
7279 /* Some systems don't have mkdir(), e.g. Tandy Xenix 3.2.. */
7280
7281 /* Z M K D I R -- Create directory(s) if necessary */
7282 /*
7283 Call with:
7284 A pointer to a file specification that might contain directory
7285 information. The filename is expected to be included.
7286 If the file specification does not include any directory separators,
7287 then it is assumed to be a plain file.
7288 If one or more directories are included in the file specification,
7289 this routine tries to create them if they don't already exist.
7290 Returns:
7291 0 or greater on success, i.e. the number of directories created.
7292 -1 on failure to create the directory
7293 */
7294 int
zmkdir(path)7295 zmkdir(path) char *path; {
7296 char *xp, *tp, c;
7297 int x, count = 0;
7298
7299 if (!path) path = "";
7300 if (!*path) return(-1);
7301
7302 #ifdef CKROOT
7303 debug(F111,"zmkdir setroot",ckroot,ckrootset);
7304 if (ckrootset) if (!zinroot(path)) {
7305 debug(F110,"zmkdir setroot violation",path,0);
7306 return(-1);
7307 }
7308 #endif /* CKROOT */
7309
7310 x = strlen(path);
7311 debug(F111,"zmkdir",path,x);
7312 if (x < 1 || x > MAXPATH) /* Check length */
7313 return(-1);
7314 if (!(tp = malloc(x+1))) /* Make a temporary copy */
7315 return(-1);
7316 strcpy(tp,path); /* safe (prechecked) */
7317 #ifdef DTILDE
7318 if (*tp == '~') { /* Starts with tilde? */
7319 xp = tilde_expand(tp); /* Attempt to expand tilde */
7320 if (!xp) xp = "";
7321 if (*xp) {
7322 char *zp;
7323 debug(F110,"zmkdir tilde_expand",xp,0);
7324 if (!(zp = malloc(strlen(xp) + 1))) { /* Make a place for it */
7325 free(tp);
7326 tp = NULL;
7327 return(-1);
7328 }
7329 free(tp); /* Free previous buffer */
7330 tp = zp; /* Point to new one */
7331 strcpy(tp,xp); /* Copy expanded name to new buffer */
7332 }
7333 }
7334 #endif /* DTILDE */
7335 debug(F110,"zmkdir tp after tilde_expansion",tp,0);
7336 xp = tp;
7337 if (ISDIRSEP(*xp)) /* Don't create root directory! */
7338 xp++;
7339
7340 /* Go thru filespec from left to right... */
7341
7342 for (; *xp; xp++) { /* Create parts that don't exist */
7343 if (!ISDIRSEP(*xp)) /* Find next directory separator */
7344 continue;
7345 c = *xp; /* Got one. */
7346 *xp = NUL; /* Make this the end of the string. */
7347 if (!isdir(tp)) { /* This directory exists already? */
7348 #ifdef CK_LOGIN
7349 if (isguest) /* Not allowed for guests */
7350 return(-1);
7351 #ifndef NOXFER
7352 /* Nor if MKDIR and/or CD are disabled */
7353 else
7354 #endif /* CK_LOGIN */
7355 if ((server
7356 #ifdef IKSD
7357 || inserver
7358 #endif /* IKSD */
7359 ) && (!ENABLED(en_mkd) || !ENABLED(en_cwd)))
7360 return(-1);
7361 #endif /* IKSD */
7362
7363 debug(F110,"zmkdir making",tp,0);
7364 x = /* No, try to create it */
7365 #ifdef NOMKDIR
7366 -1 /* Systems without mkdir() */
7367 #else
7368 mkdir(tp,0777) /* UNIX */
7369 #endif /* NOMKDIR */
7370 ;
7371 if (x < 0) {
7372 debug(F101,"zmkdir failed, errno","",errno);
7373 free(tp); /* Free temporary buffer. */
7374 tp = NULL;
7375 return(-1); /* Return failure code. */
7376 } else
7377 count++;
7378 }
7379 *xp = c; /* Replace the separator. */
7380 }
7381 free(tp); /* Free temporary buffer. */
7382 return(count); /* Return success code. */
7383 }
7384 #endif /* CK_MKDIR */
7385
7386 int
zrmdir(path)7387 zrmdir(path) char *path; {
7388 #ifdef CK_LOGIN
7389 if (isguest)
7390 return(-1);
7391 #endif /* CK_LOGIN */
7392
7393 if (!path) path = "";
7394 if (!*path) return(-1);
7395
7396 #ifdef CKROOT
7397 debug(F111,"zrmdir setroot",ckroot,ckrootset);
7398 if (ckrootset) if (!zinroot(path)) {
7399 debug(F110,"zrmdir setroot violation",path,0);
7400 return(-1);
7401 }
7402 #endif /* CKROOT */
7403
7404 #ifndef NOMKDIR
7405 return(rmdir(path));
7406 #else
7407 return(-1);
7408 #endif /* NOMKDIR */
7409 }
7410
7411 /* Z F S E E K -- Position input file pointer */
7412 /*
7413 Call with:
7414 CK_OFF_T (32 or 64 bits), 0-based, indicating desired position.
7415 Returns:
7416 0 on success.
7417 -1 on failure.
7418 */
7419 #ifndef NORESEND
7420 int
7421 #ifdef CK_ANSIC
zfseek(CK_OFF_T pos)7422 zfseek(CK_OFF_T pos)
7423 #else
7424 zfseek(pos) CK_OFF_T pos;
7425 #endif /* CK_ANSIC */
7426 /* zfseek */ {
7427 zincnt = -1; /* Must empty the input buffer */
7428 debug(F101,"zfseek","",pos);
7429 return(CKFSEEK(fp[ZIFILE], pos, 0)?-1:0);
7430 }
7431 #endif /* NORESEND */
7432
7433 /* Z F N Q F P -- Convert filename to fully qualified absolute pathname */
7434
7435 /*
7436 Given a possibly unqualified or relative file specification fn, zfnqfp()
7437 returns the fully qualified filespec for the same file, returning a struct
7438 that contains the length (len) of the result, a pointer (fpath) to the
7439 whole result, and a pointer (fname) to where the filename starts.
7440 Hint: how to get just the directory path, without the filename, into buf[]:
7441 fp = zfnqfp(filename,MAXPATHLEN,buf);
7442 if (fp) buf[fp->fname - fp->fpath] = '\0';
7443 */
7444 static struct zfnfp fnfp = { 0, NULL, NULL };
7445
7446 struct zfnfp *
zfnqfp(fname,buflen,buf)7447 zfnqfp(fname, buflen, buf) char * fname; int buflen; char * buf; {
7448 char * s;
7449 int len;
7450 #ifdef MAXPATHLEN
7451 char zfntmp[MAXPATHLEN+4];
7452 #else
7453 char zfntmp[CKMAXPATH+4];
7454 #endif /* MAXPATHLEN */
7455
7456 char sb[32], * tmp;
7457 int i = 0, j = 0, k = 0, x = 0, y = 0;
7458 int itsadir = 0;
7459
7460 s = fname;
7461 if (!s)
7462 return(NULL);
7463 if (!*s)
7464 return(NULL);
7465 if (!buf)
7466 return(NULL);
7467
7468 /* Initialize the data structure */
7469
7470 fnfp.len = ckstrncpy(buf,fname,buflen);
7471 fnfp.fpath = buf;
7472 fnfp.fname = NULL;
7473 len = buflen;
7474 debug(F111,"zfnqfp fname",fname,len);
7475
7476 #ifdef DTILDE
7477 if (*s == '~') { /* Starts with tilde? */
7478 char * xp;
7479 xp = tilde_expand(s); /* Attempt to expand tilde */
7480 debug(F110,"zfnqfp xp",xp,0); /* (realpath() doesn't do this) */
7481 if (!xp) xp = "";
7482 if (*xp)
7483 s = xp;
7484 }
7485 #endif /* DTILDE */
7486
7487 #ifdef CKREALPATH
7488
7489 /* N.B.: The realpath() result buffer MUST be MAXPATHLEN bytes long */
7490 /* otherwise we write over memory. */
7491
7492 if (!realpath(s,zfntmp)) {
7493 debug(F111,"zfnqfp realpath fails",s,errno);
7494 #ifdef COMMENT
7495 if (errno != ENOENT)
7496 return(NULL);
7497 #else
7498 /* If realpath() fails use the do-it-yourself method */
7499 /* 16 Jan 2002 */
7500 goto norealpath;
7501 #endif /* COMMENT */
7502 }
7503 len = strlen(zfntmp);
7504 if (len > buflen) {
7505 debug(F111,"zfnqfp result too long",ckitoa(buflen),len);
7506 return(NULL);
7507 } else {
7508 ckstrncpy(buf,zfntmp,buflen);
7509 }
7510 if (buf[len-1] != '/') {
7511 if ((itsadir = isdir(buf)) && len < (buflen - 1)) {
7512 buf[len++] = '/';
7513 buf[len] = NUL;
7514 }
7515 }
7516 fnfp.len = len;
7517 fnfp.fpath = buf;
7518 debug(F110,"zfnqfp realpath path",fnfp.fpath,0);
7519 tmp = buf + fnfp.len - 1;
7520 if (!itsadir) {
7521 while (tmp >= buf) {
7522 if (*tmp == '/') {
7523 fnfp.fname = tmp + 1;
7524 debug(F110,"zfnqfp realpath name",fnfp.fname,0);
7525 break;
7526 }
7527 tmp--;
7528 }
7529 }
7530 return(&fnfp);
7531
7532 #endif /* CKREALPATH */
7533
7534 norealpath:
7535
7536 tmp = zfntmp;
7537 while (*s) { /* Remove leading "./" (0 or more) */
7538 debug(F110,"zfnqfp while *s",s,0);
7539 if (*s == '.' && *(s+1) == '/') {
7540 s += 2;
7541 while (*s == '/') s++;
7542 } else
7543 break;
7544 }
7545 if (!*s) return(NULL);
7546 if (*s == '/') { /* Pathname is absolute */
7547 ckstrncpy(buf,s,len);
7548 x = strlen(buf);
7549 y = 0;
7550 } else { /* Pathname is relative */
7551 char * p;
7552 if (p = zgtdir()) { /* So get current directory */
7553 debug(F110,"zfnqfp zgtdir",p,0);
7554 x = ckstrncpy(buf,p,len);
7555 buf[x++] = '/';
7556 debug(F110,"zfnqfp buf 1",buf,0);
7557 len -= x; /* How much room left in buffer */
7558 if ((y = (int)strlen(s)) > len) /* If enough room... */
7559 return(NULL);
7560 ckstrncpy(buf+x,s,len); /* ... append the filename */
7561 debug(F110,"zfnqfp buf 2",buf,0);
7562 } else {
7563 return(NULL);
7564 }
7565 }
7566
7567 /* Buf now holds full path but maybe containing some . or .. tricks */
7568
7569 j = x + y; /* Length of what's in buf */
7570 len = j;
7571 debug(F101,"zfnqfp len","",len);
7572
7573 /* Catch dangling "/." or "/.." */
7574 if ((j > 1 && buf[j-1] == '.' && buf[j-2] == '/') ||
7575 (j > 2 && buf[j-1] == '.' && buf[j-2] == '.' && buf[j-3] == '/')) {
7576 if (j < buflen - 2) {
7577 buf[j] = '/';
7578 buf[j+1] = NUL;
7579 }
7580 }
7581 j = -1; /* j = position of rightmost "/" */
7582 i = 0; /* i = destination index */
7583 tmp[i] = NUL; /* destination is temporary buffer */
7584
7585 for (x = 0; x < len; x++) { /* x = source index */
7586 if (buf[x] == '/') {
7587 for (k = 0; k < 4; k++) {
7588 sb[k] = buf[x+k];
7589 sb[k+1] = '\0';
7590 if (!sb[k]) break;
7591 }
7592 if (!strncmp(sb,"/./",3)) { /* Eliminate "./" in "/./" */
7593 x += 1;
7594 continue;
7595 } else if (!strncmp(sb,"//",2)) { /* Change "//" to "/" */
7596 continue;
7597 } else if (!strncmp(sb,"/../",4)) { /* ".." in path */
7598 for (k = i - 1; k >= 0; k--) { /* Back up one level */
7599 if (tmp[k] == '/') {
7600 i = k;
7601 tmp[i] = NUL;
7602 break;
7603 }
7604 }
7605 x += 2;
7606 continue;
7607 }
7608 }
7609 if (i >= (buflen - 1)) {
7610 debug(F111,"zfnqfp overflow",tmp,i);
7611 return(NULL);
7612 }
7613 tmp[i++] = buf[x]; /* Regular character, copy */
7614 tmp[i] = NUL;
7615 if (buf[x] == '/') /* Remember rightmost "/" */
7616 j = i;
7617 }
7618 ckstrncpy(buf,tmp,buflen-1); /* Copy the result back */
7619
7620 buf[buflen-1] = NUL;
7621 if (!buf[0]) { /* If empty, say root */
7622 buf[0] = '/';
7623 buf[2] = NUL;
7624 j = 0;
7625 i = 1;
7626 }
7627 if ((itsadir = isdir(buf))) {
7628 if (buf[i-1] != '/' && i < (buflen - 1)) {
7629 buf[i++] = '/';
7630 buf[i] = NUL;
7631 }
7632 }
7633 if (!itsadir && (j > -1)) { /* Set pointer to basename */
7634 fnfp.fname = (char *)(buf + j);
7635 fnfp.fpath = (char *)buf;
7636 fnfp.len = i;
7637 debug(F111,"zfnqfp path",fnfp.fpath,i);
7638 debug(F110,"zfnqfp name",fnfp.fname,0);
7639 return(&fnfp);
7640 }
7641 return(NULL);
7642 }
7643
7644 /* Z C M P F N -- Compare two filenames */
7645
7646 /* Returns 1 if the two names refer to the same existing file, 0 otherwise. */
7647
7648 int
zcmpfn(s1,s2)7649 zcmpfn(s1,s2) char * s1, * s2; {
7650 char buf1[CKMAXPATH+1];
7651 char buf2[CKMAXPATH+1];
7652
7653 #ifdef USE_LSTAT
7654 char linkname[CKMAXPATH+1];
7655 struct stat buf;
7656 #endif /* USE_LSTAT */
7657 int x, rc = 0;
7658
7659 if (!s1) s1 = "";
7660 if (!s2) s2 = "";
7661 if (!*s1 || !*s2) return(0);
7662
7663 #ifdef CKSYMLINK /* We're doing symlinks? */
7664 #ifdef USE_LSTAT /* OK to use lstat()? */
7665 x = lstat(s1,&buf);
7666 if (x > -1 && /* Now see if it's a symlink */
7667 #ifdef S_ISLNK
7668 S_ISLNK(buf.st_mode)
7669 #else
7670 #ifdef _IFLNK
7671 ((_IFMT & buf.st_mode) == _IFLNK)
7672 #endif /* _IFLNK */
7673 #endif /* S_ISLNK */
7674 ) {
7675 linkname[0] = '\0'; /* Get the name */
7676 x = readlink(s1,linkname,CKMAXPATH);
7677 if (x > -1 && x < CKMAXPATH) { /* It's a link */
7678 linkname[x] = '\0';
7679 s1 = linkname;
7680 }
7681 }
7682 #endif /* USE_LSTAT */
7683 #endif /* CKSYMLINK */
7684
7685 if (zfnqfp(s1,CKMAXPATH,buf1)) { /* Convert to full pathname */
7686
7687 #ifdef CKSYMLINK /* Same deal for second name... */
7688 #ifdef USE_LSTAT
7689 x = lstat(s2,&buf);
7690 if (x > -1 &&
7691 #ifdef S_ISLNK
7692 S_ISLNK(buf.st_mode)
7693 #else
7694 #ifdef _IFLNK
7695 ((_IFMT & buf.st_mode) == _IFLNK)
7696 #endif /* _IFLNK */
7697 #endif /* S_ISLNK */
7698 ) {
7699 linkname[0] = '\0';
7700 x = readlink(s2,linkname,CKMAXPATH);
7701 if (x > -1 && x < CKMAXPATH) {
7702 linkname[x] = '\0';
7703 s2 = linkname;
7704 }
7705 }
7706 #endif /* USE_LSTAT */
7707 #endif /* CKSYMLINK */
7708 if (zfnqfp(s2,CKMAXPATH,buf2)) {
7709 debug(F110,"zcmpfn s1",buf1,0);
7710 debug(F110,"zcmpfn s2",buf2,0);
7711 if (!strncmp(buf1,buf2,CKMAXPATH))
7712 rc = 1;
7713 }
7714 }
7715 debug(F101,"zcmpfn result","",rc);
7716 return(rc);
7717 }
7718
7719 #ifdef CKROOT
7720
7721 /* User-mode chroot() implementation */
7722
7723 int
zsetroot(s)7724 zsetroot(s) char * s; { /* Sets the root */
7725 char buf[CKMAXPATH+1];
7726 if (!s) return(-1);
7727 if (!*s) return(-1);
7728 debug(F110,"zsetroot",s,0);
7729 if (!isdir(s)) return(-2);
7730 if (!zfnqfp(s,CKMAXPATH,buf)) /* Get full, real path */
7731 return(-3);
7732 if (access(buf,R_OK) < 0) { /* Check access */
7733 debug(F110,"zsetroot access denied",buf,0);
7734 return(-4);
7735 }
7736 s = buf;
7737 if (ckrootset) { /* If root already set */
7738 if (!zinroot(s)) { /* make sure new root is in it */
7739 debug(F110,"zsetroot new root not in root",ckroot,0);
7740 return(-5);
7741 }
7742 }
7743 if (zchdir(buf) < 1) return(-4); /* Change directory to new root */
7744 ckrootset = ckstrncpy(ckroot,buf,CKMAXPATH); /* Now set the new root */
7745 if (ckroot[ckrootset-1] != '/') {
7746 ckroot[ckrootset++] = '/';
7747 ckroot[ckrootset] = '\0';
7748 }
7749 debug(F111,"zsetroot rootset",ckroot,ckrootset);
7750 ckrooterr = 0; /* Reset error flag */
7751 return(1);
7752 }
7753
7754 char *
zgetroot()7755 zgetroot() { /* Returns the root */
7756 if (!ckrootset)
7757 return(NULL);
7758 return((char *)ckroot);
7759 }
7760
7761 int
zinroot(s)7762 zinroot(s) char * s; { /* Checks if file s is in the root */
7763 int x, n;
7764 struct zfnfp * f = NULL;
7765 char buf[CKMAXPATH+2];
7766
7767 debug(F111,"zinroot setroot",ckroot,ckrootset);
7768 ckrooterr = 0; /* Reset global error flag */
7769 if (!ckrootset) /* Root not set */
7770 return(1); /* so it's ok - no need to check */
7771 if (!(f = zfnqfp(s,CKMAXPATH,buf))) /* Get full and real pathname */
7772 return(0); /* Fail if we can't */
7773 n = f->len; /* Length of full pathname */
7774 debug(F111,"zinroot n",buf,n);
7775 if (n < (ckrootset - 1) || n > CKMAXPATH) { /* Bad length */
7776 ckrooterr = 1; /* Fail */
7777 return(0);
7778 }
7779 if (isdir(buf) && buf[n-1] != '/') { /* If it's a directory name */
7780 buf[n++] = '/'; /* make sure it ends with '/' */
7781 buf[n] = '\0';
7782 }
7783 x = strncmp(buf,ckroot,ckrootset); /* Compare, case-sensitive */
7784 debug(F111,"zinroot checked",buf,x);
7785 if (x == 0) /* OK */
7786 return(1);
7787 ckrooterr = 1; /* Not OK */
7788 return(0);
7789 }
7790 #endif /* CKROOT */
7791
7792 #ifdef CK_LOGIN
7793 /*
7794 The following code provides support for user login and logout
7795 including anonymous accounts. If this feature is to be supported
7796 outside of UNIX, it should be spread out among the ck?fio.c modules...
7797 */
7798 #ifndef _PATH_BSHELL
7799 #define _PATH_BSHELL "/usr/bin/bash"
7800 #endif /* _PATH_BSHELL */
7801 #ifndef _PATH_FTPUSERS
7802 #define _PATH_FTPUSERS "/etc/ftpusers"
7803 #endif /* _PATH_FTPUSERS */
7804
7805 /*
7806 * Helper function for sgetpwnam().
7807 */
7808 char *
sgetsave(s)7809 sgetsave(s) char *s; {
7810 char *new = malloc((unsigned) strlen(s) + 1);
7811 if (new == NULL) {
7812 printf("?Local resource failure: malloc\n");
7813 exit(1);
7814 /* NOTREACHED */
7815 }
7816 (void) strcpy(new, s); /* safe */
7817 return (new);
7818 }
7819
7820 /*
7821 * Save the result of getpwnam(). Used for USER command, since
7822 * the data returned must not be clobbered by any other command
7823 * (e.g., globbing).
7824 */
7825 struct passwd *
sgetpwnam(name)7826 sgetpwnam(name) char *name; {
7827 static struct passwd save;
7828 register struct passwd *p;
7829 #ifdef CK_SHADOW
7830 register struct spwd *sp;
7831 #endif /* CK_SHADOW */
7832 char *sgetsave();
7833
7834 #ifdef HPUX10_TRUSTED
7835 struct pr_passwd *pr;
7836 #endif /* HPUX10_TRUSTED */
7837
7838 #ifdef CK_SHADOW
7839 sp = getspnam(name);
7840 if (sp == NULL) {
7841 debug(F110,"sgetpwnam","getspnam() fails",0);
7842 return (NULL);
7843 }
7844 #endif /* CK_SHADOW */
7845
7846 #ifdef HPUX10_TRUSTED
7847 if ((pr = getprpwnam(name)) == NULL)
7848 return(NULL);
7849 #endif /* HPUX10_TRUSTED */
7850
7851 p = getpwnam(name);
7852 /* debug(F111,"sgetpwnam","getpwnam()",p); */
7853 if (p == NULL)
7854 return(NULL);
7855 if (save.pw_name) {
7856 free(save.pw_name);
7857 free(save.pw_passwd);
7858 #ifndef ANDROID
7859 free(save.pw_gecos);
7860 #endif
7861 free(save.pw_dir);
7862 free(save.pw_shell);
7863 }
7864 save = *p;
7865 save.pw_name = sgetsave(p->pw_name);
7866 #ifdef CK_SHADOW
7867 save.pw_passwd = sgetsave(sp->sp_pwdp);
7868 #else /* CK_SHADOW */
7869 #ifdef HPUX10_TRUSTED
7870 if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
7871 save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
7872 else
7873 save.pw_passwd = sgetsave("");
7874 #else /* HPUX10_TRUSTED */
7875 save.pw_passwd = sgetsave(p->pw_passwd);
7876 #endif /* HPUX10_TRUSTED */
7877 #endif /* CK_SHADOW */
7878 #ifndef ANDROID
7879 save.pw_gecos = sgetsave(p->pw_gecos);
7880 #endif /* ANDROID */
7881 save.pw_dir = sgetsave(p->pw_dir);
7882 save.pw_shell = sgetsave(p->pw_shell);
7883 return(&save);
7884 }
7885
7886 #define CKXLOGBSIZ 256
7887
7888 struct passwd * pw = NULL;
7889 char * home = NULL; /* Home directory pointer for glob */
7890 #ifdef CMASK
7891 #undef CMASK
7892 #endif /* CMASK */
7893
7894 #define CMASK 027
7895
7896 int defumask = CMASK; /* Default umask value */
7897
7898 /* Z V U S E R -- Verify user, Returns 1 if user OK, 0 otherwise. */
7899
7900 /* Sets global passwd pointer pw if named account exists and is acceptable;
7901 * sets askpasswd if a PASS command is expected. If logged in previously,
7902 * need to reset state. If name is "ftp" or "anonymous", the name is not in
7903 * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
7904 * If account doesn't exist, ask for passwd anyway. Otherwise, check user
7905 * requesting login privileges. Disallow anyone who does not have a standard
7906 * shell as returned by getusershell(). Disallow anyone mentioned in the file
7907 * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
7908 */
7909 _PROTOTYP(static int checkuser, (char *) );
7910
7911 char zvuname[64] = { NUL, NUL };
7912 char zvhome[CKMAXPATH+1] = { NUL, NUL };
7913 #define ZENVUSER 70
7914 #define ZENVHOME CKMAXPATH+12
7915 #define ZENVLOGNAME 74
7916 static char zenvuser[ZENVUSER];
7917 static char zenvhome[ZENVHOME];
7918 static char zenvlogname[ZENVLOGNAME];
7919
7920 #ifdef CK_PAM
7921 static char pam_data[500];
7922 struct pam_conv pam_conv = {pam_cb, pam_data}; /* PAM structure */
7923 struct pam_handle * pamh = NULL; /* PAM reference handle */
7924 #endif /* CK_PAM */
7925
7926 int
zvuser(name)7927 zvuser(name) char *name; {
7928 register char *cp = NULL;
7929 int x;
7930 char *shell;
7931 #ifdef GETUSERSHELL
7932 _PROTOTYP(char * getusershell, (void) );
7933 #endif /* GETUSERSHELL */
7934 #ifndef NODCLENDUSERSHELL
7935 _PROTOTYP(VOID endusershell, (void) );
7936 #endif /* NODCLENDUSERSHELL */
7937
7938 #ifdef CK_PAM
7939 int pam_status;
7940 const char * reply = NULL;
7941 #endif /* CK_PAM */
7942
7943 debug(F111,"user",name,logged_in);
7944
7945 if (!name) name = "";
7946 zvuname[0] = NUL;
7947
7948 debug(F101,"zvuser ckxsyslog","",ckxsyslog);
7949
7950 #ifdef CKSYSLOG
7951 debug(F100,"zvuser CKSYSLOG defined","",0);
7952 #endif /* CKSYSLOG */
7953
7954 if (logged_in) /* Should not be called if logged in */
7955 return(0);
7956
7957 #ifdef CKSYSLOG
7958 if (ckxsyslog && ckxlogging) {
7959 syslog(LOG_INFO,
7960 "login: user %s",name
7961 );
7962 }
7963 #endif /* CKSYSLOG */
7964
7965 guest = 0; /* Assume not guest */
7966 askpasswd = 0;
7967
7968 if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
7969 debug(F101,"zvuser anonymous ckxanon","",ckxanon);
7970 if (!ckxanon) { /* Anonymous login not allowed */
7971 #ifdef CKSYSLOG
7972 if (ckxsyslog && ckxlogging) {
7973 syslog(LOG_INFO,
7974 "login: anonymous login not allowed: %s",
7975 clienthost ? clienthost : "(unknown host)"
7976 );
7977 }
7978 #endif /* CKSYSLOG */
7979 return(0);
7980 }
7981 if (checkuser("ftp") || checkuser("anonymous")) {
7982 debug(F100,"zvuser anon forbidden by ftpusers file","",0);
7983 #ifdef CKSYSLOG
7984 if (ckxsyslog && ckxlogging) {
7985 syslog(LOG_INFO,
7986 "login: anonymous login forbidden by ftpusers file: %s",
7987 clienthost ? clienthost : "(unknown host)"
7988 );
7989 }
7990 #endif /* CKSYSLOG */
7991 return(0);
7992 } else if ((pw = sgetpwnam("ftp")) != NULL) {
7993 debug(F100,"zvuser anon sgetpwnam(ftp) OK","",0);
7994 guest = 1;
7995 askpasswd = 1;
7996 ckstrncpy(zvuname,"anonymous",64);
7997 return(1);
7998 } else {
7999 debug(F100,"zvuser anon sgetpwnam(ftp) FAILED","",0);
8000 #ifdef CKSYSLOG
8001 if (ckxsyslog && ckxlogging) {
8002 syslog(LOG_INFO,
8003 "login: anonymous getpwnam(ftp) failed: %s",
8004 clienthost ? clienthost : "(unknown host)"
8005 );
8006 }
8007 #endif /* CKSYSLOG */
8008 return(0);
8009 }
8010 }
8011 pw = sgetpwnam(name);
8012 if (pw) {
8013 /*
8014 Of course some UNIX platforms (like AIX) don't have getusershell().
8015 In that case we can't check if the user's account has been "turned off"
8016 or somesuch, e.g. by setting their shell to "/etc/nologin" or somesuch,
8017 which runs (usually just printing a message and exiting), but which is
8018 not listed in /etc/shells. For that matter, if getusershell() is not
8019 available, then probably neither is /etc/shells.
8020 */
8021 debug(F100,"zvuser sgetpwnam ok","",0);
8022 shell = pw->pw_shell;
8023 if (!shell) shell = "";
8024 if (!*shell)
8025 shell = _PATH_BSHELL;
8026 debug(F110,"zvuser shell",shell,0);
8027 #ifdef GETUSERSHELL
8028 while ((cp = getusershell()) != NULL) {
8029 debug(F110,"zvuser getusershell",cp,0);
8030 if ((int)strcmp(cp, shell) == 0)
8031 break;
8032 }
8033 debug(F100,"zvuser endusershell 1","",0);
8034 #ifndef NODCLENDUSERSHELL
8035 (VOID) endusershell();
8036 #else
8037 endusershell();
8038 #endif /* NODCLENDUSERSHELL */
8039 debug(F100,"zvuser endusershell 2","",0);
8040 #else /* GETUSERSHELL */
8041 cp = ""; /* Do not refuse if we cannot check */
8042 #endif /* GETUSERSHELL */
8043 x = checkuser(name);
8044 debug(F101,"zvuser checkuser","",x);
8045 if (cp == NULL) {
8046 debug(F100,"zvuser refused 1","",0);
8047 pw = (struct passwd *) NULL;
8048 #ifdef CKSYSLOG
8049 if (ckxsyslog && ckxlogging) {
8050 syslog(LOG_INFO,
8051 "login: invalid shell %s for %s %s",shell, name,
8052 clienthost ? clienthost : "(unknown host)"
8053 );
8054 }
8055 #endif /* CKSYSLOG */
8056 return(0);
8057 } else if (x) {
8058 debug(F100,"zvuser refused 2","",0);
8059 pw = (struct passwd *) NULL;
8060 #ifdef CKSYSLOG
8061 if (ckxsyslog && ckxlogging) {
8062 syslog(LOG_INFO,
8063 "login: %s login forbidden by ftpusers file: %s",
8064 name, clienthost ? clienthost : "(unknown host)"
8065 );
8066 }
8067 #endif /* CKSYSLOG */
8068 return(0);
8069 } else {
8070 x = 0;
8071 #ifdef CK_PAM
8072 /* Get PAM authentication details */
8073 debug(F110,"zvuser","calling pam_start",0);
8074 if ((pam_status =
8075 pam_start(PAM_SERVICE_TYPE,name,&pam_conv,&pamh))
8076 != PAM_SUCCESS) {
8077 reply = pam_strerror(NULL, pam_status);
8078 debug(F110,"zvuser PAM failure",reply,0);
8079 printf("%s\n",reply);
8080 #ifdef CKSYSLOG
8081 if (ckxsyslog && ckxlogging) {
8082 syslog(LOG_INFO,
8083 "login: %s refused by PAM \"%s\": %s",
8084 name,reply,
8085 clienthost ? clienthost : "(unknown host)"
8086 );
8087 }
8088 #endif /* CKSYSLOG */
8089 return(0);
8090 }
8091 #endif /* CK_PAM */
8092 askpasswd = 1;
8093 ckstrncpy(zvuname,name,64);
8094 return(1);
8095 }
8096 } else {
8097 x = 0;
8098 debug(F100,"zvuser sgetpwnam NULL","",0);
8099 #ifdef CKSYSLOG
8100 if (ckxsyslog && ckxlogging) {
8101 syslog(LOG_INFO,
8102 "login: getpwnam(%s) failed: %s",name,
8103 clienthost ? clienthost : "(unknown host)"
8104 );
8105 }
8106 #endif /* CKSYSLOG */
8107 return(0);
8108 }
8109
8110 #ifdef FTP_KERBEROS
8111 if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
8112 #ifdef COMMENT
8113 /* Why sprintf and then printf? */
8114 /* Also, what is kerb_ok? And is the test on it right? */
8115 char buf[CKXLOGBSIZ];
8116 sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
8117 kdata.pname, *kdata.pinst ? "." : "",
8118 kdata.pinst, kdata.prealm,
8119 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8120 name, kerb_ok ? "" : "; Password required.");
8121 printf("%s", buf);
8122 #else
8123 printf("Kerberos user %s%s%s@%s is%s authorized as %s%s",
8124 kdata.pname, *kdata.pinst ? "." : "",
8125 kdata.pinst, kdata.prealm,
8126 (kerb_ok = kuserok(&kdata,name) == 0) ? "" : " not",
8127 name, kerb_ok ? "" : "; Password required.");
8128 #endif /* COMMENT */
8129 if (kerb_ok) return(1);
8130 } else
8131 return(0);
8132 #endif /* FTP_KERBEROS */
8133 }
8134
8135 /* Check if the given user is in the forbidden-user file */
8136
8137 static int
checkuser(name)8138 checkuser(name) char *name; {
8139 extern char * userfile;
8140 FILE *fd;
8141 int i;
8142 char line[CKXLOGBSIZ+1];
8143
8144 if (!name)
8145 name = "";
8146 i = strlen(name);
8147 debug(F111,"checkuser name",name,i);
8148 if (!*name)
8149 return(1);
8150
8151 fd = fopen(userfile ? userfile : _PATH_FTPUSERS, "r");
8152 /* debug(F111,"checkuser userfile",userfile,fd); */
8153 if (fd) {
8154 line[0] = '\0';
8155 while (fgets(line, sizeof(line), fd)) {
8156 debug(F110,"checkuser line",line,0);
8157 if (line[0] <= '#')
8158 continue;
8159 if (strncmp(line, name, i) == 0) {
8160 debug(F110,"checkuser REFUSED",name,0);
8161 return(1);
8162 }
8163 line[0] = '\0';
8164 }
8165 (VOID) fclose(fd);
8166 }
8167 debug(F110,"checkuser OK",name,0);
8168 return(0);
8169 }
8170
8171 /* Z V L O G O U T -- Log out from Internet Kermit Service */
8172
8173 VOID
zvlogout()8174 zvlogout() {
8175 #ifdef COMMENT
8176 /* This could be dangerous */
8177 if (setuid((UID_T)0) < 0) {
8178 debug(F100,"zvlogout setuid FAILED","",0);
8179 goto bad;
8180 }
8181 debug(F100,"zvlogout setuid OK","",0);
8182 #endif /* COMMENT */
8183 #ifdef CKSYSLOG
8184 if (ckxsyslog >= SYSLG_LI && ckxlogging) {
8185 cksyslog(SYSLG_LI, 1, "logout",(char *) uidbuf, clienthost);
8186 }
8187 #endif /* CKSYSLOG */
8188 #ifdef CKWTMP
8189 debug(F110,"WTMP logout",cksysline,logged_in);
8190 if (logged_in)
8191 logwtmp(cksysline, "", "");
8192 #endif /* CKWTMP */
8193 pw = NULL;
8194 logged_in = 0;
8195 guest = 0;
8196 isguest = 0;
8197 }
8198
8199 #ifdef FTP_KERBEROS
kpass(name,p)8200 kpass(name, p) char *name, *p; {
8201 char instance[INST_SZ];
8202 char realm[REALM_SZ];
8203 char tkt_file[20];
8204 KTEXT_ST ticket;
8205 AUTH_DAT authdata;
8206 unsigned long faddr;
8207 struct hostent *hp;
8208
8209 if (krb_get_lrealm(realm, 1) != KSUCCESS)
8210 return(0);
8211
8212 ckstrncpy(tkt_file, TKT_ROOT, 20);
8213 ckstrncat(tkt_file, "_ftpdXXXXXX", 20);
8214 krb_set_tkt_string(mktemp(tkt_file));
8215
8216 (VOID) ckstrncpy(instance, krb_get_phost(hostname), sizeof(instance));
8217
8218 if ((hp = gethostbyname(instance)) == NULL)
8219 return(0);
8220
8221 #ifdef HADDRLIST
8222 hp = ck_copyhostent(hp); /* safe copy that won't change */
8223 #endif /* HADDRLIST */
8224 bcopy((char *)hp->h_addr, (char *) &faddr, sizeof(faddr));
8225
8226 if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, p) ||
8227 krb_mk_req(&ticket, "rcmd", instance, realm, 33) ||
8228 krb_rd_req(&ticket, "rcmd", instance, faddr, &authdata, "") ||
8229 kuserok(&authdata, name)) {
8230 dest_tkt();
8231 return(0);
8232 }
8233 dest_tkt();
8234 return(1);
8235 }
8236 #endif /* FTP_KERBEROS */
8237
8238 VOID
zsyslog()8239 zsyslog() {
8240 #ifdef CKSYSLOG
8241 if (ckxsyslog && !ckxlogging) {
8242 #ifdef LOG_DAEMON
8243 openlog(inserver ? "iksd" : "ckermit", LOG_PID, LOG_DAEMON);
8244 #else
8245 openlog(inserver ? "iksd" : "ckermit", LOG_PID);
8246 #endif /* LOG_DAEMON */
8247 ckxlogging = 1;
8248 debug(F100,"zsyslog syslog opened","",0);
8249 }
8250 #endif /* CKSYSLOG */
8251 }
8252
8253 /* Z V P A S S -- Verify password; returns 1 if OK, 0 otherwise */
8254
8255 #ifndef AUTH_USER
8256 #define AUTH_USER 3
8257 #endif /* AUTH_USER */
8258 #ifndef AUTH_VALID
8259 #define AUTH_VALID 4
8260 #endif /* AUTH_VALID */
8261
8262 #ifdef __FreeBSD__ /* 299 This was necessary in */
8263 #ifndef NODCLINITGROUPS /* FreeBSD 4.4, don't know */
8264 #define NODCLINITGROUPS /* about other versions... */
8265 #endif /* NODCLINITGROUPS */
8266 #endif /* __FreeBSD__ */
8267
8268 int
zvpass(p)8269 zvpass(p) char *p; {
8270 #ifndef NODCLINITGROUPS
8271 _PROTOTYP(int initgroups, (const char *, gid_t) );
8272 #endif /* NODCLINITGROUPS */
8273
8274 char *xpasswd, *salt;
8275 char * dir = NULL;
8276 #ifdef CK_PAM
8277 int pam_status;
8278 const char * reply = NULL;
8279 #endif /* CK_PAM */
8280 int dummy;
8281
8282 if (logged_in || askpasswd == 0) {
8283 return(0);
8284 }
8285 debug(F111,"zvpass",p ? (guest ? p : "xxxxxx") : "(null)",guest);
8286 if (!p) p = "";
8287 askpasswd = 0;
8288 if (guest && !*p) { /* Guests must specify a password */
8289 #ifdef CKSYSLOG
8290 if (ckxsyslog && ckxlogging) {
8291 syslog(LOG_INFO,
8292 "login: anonymous guests must specify a password"
8293 );
8294 }
8295 #endif /* CKSYSLOG */
8296 return(0);
8297 }
8298 if (!guest
8299 #ifdef CK_AUTHENTICATION
8300 && ck_tn_auth_valid() != AUTH_VALID
8301 #endif /* CK_AUTHENTICATION */
8302 ) { /* "ftp" is only account allowed no password */
8303 #ifdef CK_PAM
8304 debug(F110,"zvpass","calling pam_set_item(AUTHTOK)",0);
8305 if ((pam_status = pam_set_item(pamh,PAM_AUTHTOK,p)) != PAM_SUCCESS) {
8306 reply = pam_strerror(pamh, pam_status);
8307 debug(F110,"zvpass PAM failure",reply,0);
8308 /* if no password given treat as non-fatal error */
8309 /* pam will prompt for password in pam_authenticate() */
8310 if (!p) {
8311 printf("%s\n",reply);
8312 pam_end(pamh, 0);
8313 debug(F100,"zvpass denied","",0);
8314 pw = NULL;
8315 zvuname[0] = NUL;
8316 return(0);
8317 }
8318 }
8319 debug(F110,"zvpass","calling pam_authenticate",0);
8320 #ifdef COMMENT
8321 if (*p)
8322 pam_pw = p;
8323 #else
8324 /*
8325 Make IKSD authentication (using PAM) ask for a password when an
8326 invalid username has been given, to avoid disclosing which account
8327 names are valid. See #417247 (Debian).
8328 */
8329 if (*p
8330 #ifdef CK_LOGIN
8331 || gotemptypasswd
8332 #endif /* CK_LOGIN */
8333 )
8334 pam_pw = p;
8335 #endif /* COMMENT */
8336 if ((pam_status = pam_authenticate(pamh, 0)) != PAM_SUCCESS) {
8337 reply = pam_strerror(pamh, pam_status);
8338 debug(F110,"zvpass PAM failure",reply,0);
8339 printf("%s\n",reply);
8340 pam_end(pamh, 0);
8341 debug(F100,"zvpass denied","",0);
8342 pam_pw = NULL;
8343 pw = NULL;
8344 zvuname[0] = NUL;
8345 return(0);
8346 }
8347 pam_pw = NULL;
8348 debug(F110,"zvpass","calling pam_acct_mgmt",0);
8349 if ((pam_status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) {
8350 reply = pam_strerror(pamh, pam_status);
8351 debug(F110,"zvpass PAM failure",reply,0);
8352 printf("%s\n",reply);
8353 pam_end(pamh, 0);
8354 debug(F100,"zvpass denied","",0);
8355 pw = NULL;
8356 zvuname[0] = NUL;
8357 return(0);
8358 }
8359 debug(F110,"zvpass","PAM validates OK",0);
8360 pam_end(pamh,0);
8361 #else /* CK_PAM */
8362 if (pw == NULL)
8363 salt = "xx";
8364 else
8365 salt = pw->pw_passwd;
8366
8367 #ifdef HPUX10_TRUSTED
8368 xpasswd = bigcrypt(p, salt);
8369 #else
8370 /*
8371 On 64-bit platforms this can give "cast to pointer from integer of
8372 different size" warning, but I'm not sure what the effect is at runtime,
8373 or what to do about it.
8374 */
8375 xpasswd = (char *)crypt(p, salt);
8376 #endif /* HPUX10_TRUSTED */
8377
8378 if (
8379 #ifdef FTP_KERBEROS
8380 /* null pw_passwd ok if Kerberos password ok */
8381 pw == NULL ||
8382 ((*pw->pw_passwd != '\0' ||
8383 strcmp(xpasswd, pw->pw_passwd))
8384 && !kpass(pw->pw_name, p))
8385 #else
8386 #ifdef CK_SRP
8387 /* check with tpasswd first if there */
8388 pw == NULL || *pw->pw_passwd == '\0' ||
8389 t_verifypw (pw->pw_name, p) == 0 ||
8390 (t_verifypw (pw->pw_name, p) < 0 &&
8391 strcmp (xpasswd, pw->pw_passwd))
8392 #else /* CK_SRP */
8393 /* The strcmp does not catch null passwords! */
8394 (pw == NULL) || (*pw->pw_passwd == '\0') ||
8395 strcmp(xpasswd, pw->pw_passwd)
8396 #endif /* CK_SRP */
8397 #endif /* FTP_KERBEROS */
8398 ) {
8399 debug(F100,"zvpass denied","",0);
8400 pw = NULL;
8401 zvuname[0] = NUL;
8402 return(0);
8403 }
8404 #endif /* CK_PAM */
8405 }
8406
8407 dummy = setgid((GID_T)pw->pw_gid); /* Set group ID */
8408
8409 #ifndef NOINITGROUPS
8410 dummy = initgroups(pw->pw_name, pw->pw_gid);
8411 #endif /* NOINITGROUPS */
8412
8413 logged_in = 1;
8414 dir = pw->pw_dir;
8415
8416 #ifdef CKWTMP
8417 /* Open wtmp before chroot */
8418 if (ckxwtmp) {
8419 sprintf(cksysline,"iks_%04x", getpid()); /* safe */
8420 logwtmp(cksysline, pw->pw_name,
8421 clienthost ? clienthost : "(unknown host)"
8422 );
8423 debug(F110,"WTMP login",cksysline,logged_in);
8424 }
8425 #endif /* CKWTMP */
8426 /*
8427 For anonymous users, we chroot to user ftp's home directory unless
8428 started with --anonroot:xxx, in which case we chroot to xxx. We must
8429 immediately chdir() to the same directory we chroot() to or else the
8430 old current directory remains accessible as "." outside the new root.
8431 */
8432 if (guest) {
8433 if (anonroot) /* Non-default anonymous root */
8434 dir = anonroot;
8435 else
8436 makestr(&anonroot,dir);
8437 errno = 0;
8438 debug(F110,"zvpass anon chroot",dir,0);
8439 if (chroot(dir) < 0) {
8440 debug(F111,"zvpass anon chroot FAILED",dir,errno);
8441 goto bad;
8442 }
8443 errno = 0;
8444 if (chdir("/") < 0) {
8445 debug(F111,"zvpass anon chdir FAILED",dir,errno);
8446 goto bad;
8447 }
8448 debug(F110,"zvpass anon chroot/chdir OK",dir,0);
8449 } else if (chdir(dir) < 0) { /* Not guest */
8450 #ifdef COMMENT
8451 if (chdir("/") < 0) {
8452 debug(F110,"Non-guest chdir FAILED",dir,0);
8453 goto bad;
8454 } else
8455 printf("?No directory! Logging in with home=/\n");
8456 #else
8457 debug(F110,"zvpass non-guest chdir FAILED",dir,0);
8458 goto bad; /* Be conservative at first */
8459 #endif /* COMMENT */
8460 }
8461 debug(F110,"zvpass non-guest chdir OK",dir,0);
8462 if (setuid((UID_T)pw->pw_uid) < 0) {
8463 debug(F101,"zvpass setuid FAILED","",pw->pw_uid);
8464 goto bad;
8465 }
8466 debug(F101,"zvpass setuid OK","",pw->pw_uid);
8467
8468 guestpass[0] = '\0';
8469 if (guest) {
8470 extern int fncact;
8471 isguest = 1;
8472 fncact = XYFX_R; /* FILE COLLISION = RENAME */
8473 debug(F110,"GUEST fncact=R",p,0);
8474 lset(guestpass,"anonymous:",10,32);
8475 ckstrncpy(&guestpass[10],p,GUESTPASS-10);
8476 home = "/";
8477 printf("Anonymous login.\r\n");
8478
8479 #ifdef SETPROCTITLE
8480 /* proctitle declared where? Obviously this code is never compiled. */
8481 sprintf(proctitle, "%s: anonymous/%.*s",
8482 clienthost ? clienthost : "(unk)",
8483 sizeof(proctitle) - sizeof(clienthost) -
8484 sizeof(": anonymous/"), p);
8485 setproctitle(proctitle);
8486 #endif /* SETPROCTITLE */
8487
8488 #ifdef CKSYSLOG
8489 if (ckxsyslog && ckxlogging) {
8490 syslog(LOG_INFO,
8491 "login: anonymous %s %s",
8492 clienthost ? clienthost : "(unknown host)",
8493 p
8494 );
8495 }
8496 #endif /* CKSYSLOG */
8497
8498 } else { /* Real user */
8499 isguest = 0;
8500 home = dir;
8501 ckstrncpy(guestpass,zvuname,GUESTPASS);
8502
8503 printf("User %s logged in.\r\n", pw->pw_name);
8504 #ifdef SETPROCTITLE
8505 /* not used */
8506 sprintf(proctitle, "%s: %s",
8507 clienthost ? clienthost : "(unk)",
8508 pw->pw_name
8509 );
8510 setproctitle(proctitle);
8511 #endif /* SETPROCTITLE */
8512
8513 #ifdef CKSYSLOG
8514 if (ckxsyslog && ckxlogging)
8515 syslog(LOG_INFO, "login: %s %s",
8516 pw->pw_name,
8517 clienthost ? clienthost : "(unknown host)"
8518 );
8519 #endif /* CKSYSLOG */
8520 }
8521 ckstrncpy(zvhome,home,CKMAXPATH); /* Set environment variables */
8522 #ifndef NOPUTENV
8523
8524 ckmakmsg(zenvuser,ZENVUSER,"USER=",zvuname,NULL,NULL);
8525 putenv((char *)zenvuser);
8526 ckmakmsg(zenvlogname,ZENVLOGNAME,"LOGNAME=",zvuname,NULL,NULL);
8527 putenv((char *)zenvlogname);
8528 ckmakmsg(zenvhome,ZENVHOME,"HOME=",zvhome,NULL,NULL);
8529 putenv((char *)zenvhome);
8530 #endif /* NOPUTENV */
8531 /* homdir = (char *)zvhome; */
8532 ckstrncpy((char *)uidbuf,(char *)zvuname,64);
8533 (VOID) umask(defumask);
8534 #ifdef IKSDB
8535 if (ikdbopen) {
8536 char * p2;
8537 int k;
8538 extern char dbrec[];
8539 extern unsigned long myflags;
8540 extern unsigned int mydbslot;
8541 extern struct iksdbfld dbfld[];
8542 #ifdef CK_AUTHENTICATION
8543 extern unsigned long myamode, myatype;
8544 #endif /* CK_AUTHENTICATION */
8545 myflags |= DBF_LOGGED;
8546 #ifdef DEBUG
8547 if (deblog) {
8548 debug(F101,"zvpass guest","",guest);
8549 debug(F111,"zvpass zvuname",zvuname,0);
8550 debug(F110,"zvpass guestpass",guestpass,0);
8551 debug(F110,"zvpass dir",dir,0);
8552 debug(F110,"zvpass home",home,0);
8553 debug(F110,"zvpass anonroot",anonroot,0);
8554 }
8555 #endif /* DEBUG */
8556 p2 = guest ? guestpass : zvuname;
8557 if (guest) {
8558 p2 = (char *)guestpass;
8559 myflags &= ~DBF_USER;
8560 } else {
8561 p2 = (char *)zvuname;
8562 myflags |= DBF_USER;
8563 }
8564 k = strlen(p2);
8565 strncpy(&dbrec[DB_ULEN],ulongtohex((unsigned long)k,4),4);
8566 lset(&dbrec[dbfld[db_USER].off],p2,1024,32);
8567 strncpy(&dbrec[DB_FLAGS],ulongtohex(myflags,4),4);
8568 #ifdef CK_AUTHENTICATION
8569 myamode = ck_tn_auth_valid();
8570 strncpy(&dbrec[DB_AMODE],ulongtohex(myamode,4),4);
8571 myatype = ck_tn_authenticated();
8572 strncpy(&dbrec[DB_ATYPE],ulongtohex(myatype,4),4);
8573 #endif /* CK_AUTHENTICATION */
8574 if (guest) {
8575 p2 = dir;
8576 } else {
8577 p2 = zgtdir();
8578 if (!p2) p2 = "";
8579 if (!*p2) p2 = home;
8580 }
8581 strncpy(&dbrec[DB_DLEN],
8582 ulongtohex((unsigned long)strlen(p2),4),
8583 4
8584 );
8585 lset(&dbrec[dbfld[db_DIR].off],p2,1024,32);
8586 updslot(mydbslot);
8587 }
8588 #endif /* IKSDB */
8589 return(1);
8590
8591 bad: /* Common failure exit */
8592 zvuname[0] = NUL;
8593 zvlogout();
8594 return(0);
8595 }
8596 #endif /* CK_LOGIN */
8597
8598 /* Buggy Xenix 2.3.4 cc needs this line after the endif */
8599