1 /* uucp.c
2    Prepare to copy a file to or from a remote system.
3 
4    Copyright (C) 1991, 1992, 1993, 1994, 1995, 2002 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
21 
22    The author of the program may be contacted at ian@airs.com.
23    */
24 
25 #include "uucp.h"
26 
27 #if USE_RCS_ID
28 const char uucp_rcsid[] = "$FreeBSD$";
29 #endif
30 
31 #include <ctype.h>
32 #include <errno.h>
33 
34 #include "getopt.h"
35 
36 #include "uudefs.h"
37 #include "uuconf.h"
38 #include "system.h"
39 
40 /* Local functions.  */
41 
42 static void ucusage P((void));
43 static void uchelp P((void));
44 static void ucdirfile P((const char *zdir, const char *zfile,
45 			 pointer pinfo));
46 static void uccopy P((const char *zfile, const char *zdest,
47 		      boolean fforcelocal));
48 static void ucadd_cmd P((const struct uuconf_system *qsys,
49 			 const struct scmd *qcmd, const char *zlog));
50 static void ucspool_cmds P((boolean fjobid));
51 static const char *zcone_system P((boolean *pfany));
52 static void ucrecord_file P((const char *zfile));
53 static void ucabort P((void));
54 
55 /* Long getopt options.  Note that any changes here must be reflected
56    in the code in uuxqt.c which checks the options used for the uucp
57    command.  */
58 static const struct option asClongopts[] =
59 {
60   { "copy", no_argument, NULL, 'C' },
61   { "nocopy", no_argument, NULL, 'c' },
62   { "directories", no_argument, NULL, 'd' },
63   { "nodirectories", no_argument, NULL, 'f' },
64   { "grade", required_argument, NULL, 'g' },
65   { "jobid", no_argument, NULL, 'j' },
66   { "mail", no_argument, NULL, 'm' },
67   { "notify", required_argument, NULL, 'n' },
68   { "nouucico", no_argument, NULL, 'r' },
69   { "recursive", no_argument, NULL, 'R' },
70   { "status", required_argument, NULL, 's' },
71   { "uuto", no_argument, NULL, 't' },
72   { "user", required_argument, NULL, 'u' },
73   { "noexpand", no_argument, NULL, 'W' },
74   { "config", required_argument, NULL, 'I' },
75   { "debug", required_argument, NULL, 'x' },
76   { "version", no_argument, NULL, 'v' },
77   { "help", no_argument, NULL, 1 },
78   { NULL, 0, NULL, 0 }
79 };
80 
81 /* Local variables.  There are a bunch of these, mostly set by the
82    options and the last (the destination) argument.  These have file
83    scope so that they may be easily passed into uccopy; they could for
84    the most part also be wrapped up in a structure and passed in.  */
85 
86 /* The uuconf global pointer.  */
87 static pointer pCuuconf;
88 
89 /* TRUE if source files should be copied to the spool directory.  */
90 static boolean fCcopy = TRUE;
91 
92 /* Grade to use.  */
93 static char bCgrade = BDEFAULT_UUCP_GRADE;
94 
95 /* Whether to send mail to the requesting user when the copy is
96    complete.  */
97 static boolean fCmail = FALSE;
98 
99 /* User to notify on remote system.  */
100 static const char *zCnotify = "";
101 
102 /* TRUE if remote files should be prefixed with the current working
103    directory.  */
104 static boolean fCexpand = TRUE;
105 
106 /* TRUE if necessary directories should be created on the destination
107    system.  */
108 static boolean fCmkdirs = TRUE;
109 
110 /* Local name.  */
111 static const char *zClocalname;
112 
113 /* User name.  */
114 static const char *zCuser = NULL;
115 
116 /* TRUE if this is a remote request.  */
117 static boolean fCremote = FALSE;
118 
119 /* TRUE if the destination is this system.  */
120 static boolean fClocaldest;
121 
122 /* Destination system.  */
123 static struct uuconf_system sCdestsys;
124 
125 /* Systems to forward to, if not NULL.  */
126 static char *zCforward;
127 
128 /* Options to use when sending a file.  */
129 static char abCsend_options[20];
130 
131 /* Options to use when receiving a file.  */
132 static char abCrec_options[20];
133 
134 /* TRUE if the current file being copied from is in the cwd.  */
135 static boolean fCneeds_cwd;
136 
137 /* The main program.  */
138 
139 int
main(argc,argv)140 main (argc, argv)
141      int argc;
142      char **argv;
143 {
144   /* -I: configuration file name.  */
145   const char *zconfig = NULL;
146   /* -j: output job id.  */
147   boolean fjobid = FALSE;
148   /* -r: don't start uucico when finished.  */
149   boolean fuucico = TRUE;
150   /* -R: copy directories recursively.  */
151   boolean frecursive = FALSE;
152   /* -s: report status to named file.  */
153   const char *zstatus_file = NULL;
154   /* -t: emulate uuto.  */
155   boolean fuuto = FALSE;
156   int iopt;
157   pointer puuconf;
158   int iuuconf;
159   int i;
160   boolean fgetcwd;
161   struct uuconf_system slocalsys;
162   char *zexclam;
163   char *zdestfile;
164   const char *zdestsys;
165   char *zoptions;
166   boolean fexit;
167 
168   zProgram = argv[0];
169 
170   while ((iopt = getopt_long (argc, argv, "cCdfg:I:jmn:prRs:tu:Wvx:",
171 			      asClongopts, (int *) NULL)) != EOF)
172     {
173       switch (iopt)
174 	{
175 	case 'c':
176 	  /* Do not copy local files to spool directory.  */
177 	  fCcopy = FALSE;
178 	  break;
179 
180 	case 'p':
181 	case 'C':
182 	  /* Copy local files to spool directory.  */
183 	  fCcopy = TRUE;
184 	  break;
185 
186 	case 'd':
187 	  /* Create directories if necessary.  */
188 	  fCmkdirs = TRUE;
189 	  break;
190 
191 	case 'f':
192 	  /* Do not create directories if they don't exist.  */
193 	  fCmkdirs = FALSE;
194 	  break;
195 
196 	case 'g':
197 	  /* Set job grade.  */
198 	  bCgrade = optarg[0];
199 	  break;
200 
201 	case 'I':
202 	  /* Name configuration file.  */
203 	  if (fsysdep_other_config (optarg))
204 	    zconfig = optarg;
205 	  break;
206 
207 	case 'j':
208 	  /* Output job id.  */
209 	  fjobid = TRUE;
210 	  break;
211 
212 	case 'm':
213 	  /* Mail to requesting user.  */
214 	  fCmail = TRUE;
215 	  break;
216 
217 	case 'n':
218 	  /* Notify remote user.  */
219 	  zCnotify = optarg;
220 	  break;
221 
222 	case 'r':
223 	  /* Don't start uucico when finished.  */
224 	  fuucico = FALSE;
225 	  break;
226 
227 	case 'R':
228 	  /* Copy directories recursively.  */
229 	  frecursive = TRUE;
230 	  break;
231 
232 	case 's':
233 	  /* Report status to named file.  */
234 	  zstatus_file = optarg;
235 	  break;
236 
237 	case 't':
238 	  /* Emulate uuto.  */
239 	  fuuto = TRUE;
240 	  break;
241 
242 	case 'u':
243 	  /* Set user name.  */
244 	  zCuser = optarg;
245 	  break;
246 
247 	case 'W':
248 	  /* Expand only local file names.  */
249 	  fCexpand = FALSE;
250 	  break;
251 
252 	case 'x':
253 #if DEBUG > 1
254 	  /* Set debugging level.  */
255 	  iDebug |= idebug_parse (optarg);
256 #endif
257 	  break;
258 
259 	case 'v':
260 	  /* Print version and exit.  */
261 	  printf ("uucp (Taylor UUCP) %s\n", VERSION);
262 	  printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n");
263 	  printf ("This program is free software; you may redistribute it under the terms of\n");
264 	  printf ("the GNU General Public LIcense.  This program has ABSOLUTELY NO WARRANTY.\n");
265 	  exit (EXIT_SUCCESS);
266 	  /*NOTREACHED*/
267 
268 	case 1:
269 	  /* --help.  */
270 	  uchelp ();
271 	  exit (EXIT_SUCCESS);
272 	  /*NOTREACHED*/
273 
274 	case 0:
275 	  /* Long option found and flag set.  */
276 	  break;
277 
278 	default:
279 	  ucusage ();
280 	  /*NOTREACHED*/
281 	}
282     }
283 
284   if (! UUCONF_GRADE_LEGAL (bCgrade)
285       || ((bCgrade < '0' || bCgrade > '9')
286 	  && (bCgrade < 'a' || bCgrade > 'z')
287 	  && (bCgrade < 'A' || bCgrade > 'Z')))
288     {
289       ulog (LOG_ERROR, "Ignoring illegal grade");
290       bCgrade = BDEFAULT_UUCP_GRADE;
291     }
292 
293   /* The user name must contain a '!', which is treated as a remote
294      name, to avoid spoofing of other users (there is no advantage to
295      spoofing remote users, except to send them random bits of mail,
296      which you can do anyhow).  */
297   if (zCuser != NULL)
298     {
299       if (strchr (zCuser, '!') != NULL)
300 	fCremote = TRUE;
301       else
302 	{
303 	  ulog (LOG_ERROR, "Ignoring local user name");
304 	  zCuser = NULL;
305 	}
306     }
307 
308   if (argc - optind < 2)
309     ucusage ();
310 
311   iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
312   if (iuuconf != UUCONF_SUCCESS)
313     ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
314   pCuuconf = puuconf;
315 
316 #if DEBUG > 1
317   {
318     const char *zdebug;
319 
320     iuuconf = uuconf_debuglevel (puuconf, &zdebug);
321     if (iuuconf != UUCONF_SUCCESS)
322       ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
323     if (zdebug != NULL)
324       iDebug |= idebug_parse (zdebug);
325   }
326 #endif
327 
328   /* See if we are going to need to know the current directory.  We
329      just check each argument to see whether it's an absolute
330      pathname.  We actually aren't going to need the cwd if fCexpand
331      is FALSE and the file is remote, but so what.  */
332   fgetcwd = FALSE;
333   for (i = optind; i < argc; i++)
334     {
335       zexclam = strrchr (argv[i], '!');
336       if (zexclam == NULL)
337 	zexclam = argv[i];
338       else
339 	++zexclam;
340       if (fsysdep_needs_cwd (zexclam))
341 	{
342 	  fgetcwd = TRUE;
343 	  break;
344 	}
345     }
346 
347 #ifdef SIGINT
348   usysdep_signal (SIGINT);
349 #endif
350 #ifdef SIGHUP
351   usysdep_signal (SIGHUP);
352 #endif
353 #ifdef SIGQUIT
354   usysdep_signal (SIGQUIT);
355 #endif
356 #ifdef SIGTERM
357   usysdep_signal (SIGTERM);
358 #endif
359 #ifdef SIGPIPE
360   usysdep_signal (SIGPIPE);
361 #endif
362 
363   usysdep_initialize (puuconf, INIT_SUID | (fgetcwd ? INIT_GETCWD : 0));
364 
365   ulog_fatal_fn (ucabort);
366 
367   if (zCuser == NULL)
368     zCuser = zsysdep_login_name ();
369 
370   iuuconf = uuconf_localname (puuconf, &zClocalname);
371   if (iuuconf == UUCONF_NOT_FOUND)
372     {
373       zClocalname = zsysdep_localname ();
374       if (zClocalname == NULL)
375 	exit (EXIT_FAILURE);
376     }
377   else if (iuuconf != UUCONF_SUCCESS)
378     ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
379 
380   /* Get the local system information.  */
381   iuuconf = uuconf_system_info (puuconf, zClocalname, &slocalsys);
382   if (iuuconf != UUCONF_SUCCESS)
383     {
384       if (iuuconf != UUCONF_NOT_FOUND)
385 	ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
386       iuuconf = uuconf_system_local (puuconf, &slocalsys);
387       if (iuuconf != UUCONF_SUCCESS)
388 	ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
389       slocalsys.uuconf_zname = (char *) zClocalname;
390     }
391 
392   /* If we are emulating uuto, translate the destination argument, and
393      notify the destination user.  This had better not turn into
394      something that requires the current directory, or we may have
395      passed INIT_GETCWD incorrectly.  */
396   if (fuuto)
397     {
398       if (*zCnotify == '\0')
399 	{
400 	  zexclam = strrchr (argv[argc - 1], '!');
401 	  if (zexclam == NULL)
402 	    ucusage ();
403 	  zCnotify = zexclam + 1;
404 	}
405       argv[argc - 1] = zsysdep_uuto (argv[argc - 1], zClocalname);
406       if (argv[argc - 1] == NULL)
407 	ucusage ();
408     }
409 
410   /* Set up the file transfer options.  */
411   zoptions = abCsend_options;
412   if (fCcopy)
413     *zoptions++ = 'C';
414   else
415     *zoptions++ = 'c';
416   if (fCmkdirs)
417     *zoptions++ = 'd';
418   else
419     *zoptions++ = 'f';
420   if (fCmail)
421     *zoptions++ = 'm';
422   if (*zCnotify != '\0')
423     *zoptions++ = 'n';
424   *zoptions = '\0';
425 
426   zoptions = abCrec_options;
427   if (fCmkdirs)
428     *zoptions++ = 'd';
429   else
430     *zoptions++ = 'f';
431   if (fCmail)
432     *zoptions++ = 'm';
433   *zoptions = '\0';
434 
435   argv[argc - 1] = zremove_local_sys (&slocalsys, argv[argc - 1]);
436 
437   zexclam = strchr (argv[argc - 1], '!');
438   if (zexclam == NULL)
439     {
440       zdestsys = zClocalname;
441       zdestfile = argv[argc - 1];
442       fClocaldest = TRUE;
443     }
444   else
445     {
446       size_t clen;
447       char *zcopy;
448 
449       clen = zexclam - argv[argc - 1];
450       zcopy = zbufalc (clen + 1);
451       memcpy (zcopy, argv[argc - 1], clen);
452       zcopy[clen] = '\0';
453       zdestsys = zcopy;
454 
455       zdestfile = zexclam + 1;
456 
457       fClocaldest = FALSE;
458     }
459 
460   iuuconf = uuconf_system_info (puuconf, zdestsys, &sCdestsys);
461   if (iuuconf != UUCONF_SUCCESS)
462     {
463       if (iuuconf != UUCONF_NOT_FOUND)
464 	ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
465       if (fClocaldest)
466 	{
467 	  iuuconf = uuconf_system_local (puuconf, &sCdestsys);
468 	  if (iuuconf != UUCONF_SUCCESS)
469 	    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
470 	  sCdestsys.uuconf_zname = (char *) zClocalname;
471 	}
472       else
473 	{
474 	  if (! funknown_system (puuconf, zdestsys, &sCdestsys))
475 	    ulog (LOG_FATAL, "%s: System not found", zdestsys);
476 	}
477     }
478 
479   /* Here zdestfile is the destination file name following the
480      destination system name (if any); it may contain other systems to
481      forward the files through.  Isolate the file from the list of
482      systems.  */
483   zexclam = strrchr (zdestfile, '!');
484   if (zexclam == NULL)
485     zCforward = NULL;
486   else
487     {
488       size_t clen;
489 
490 #if DEBUG > 0
491       if (fClocaldest)
492 	ulog (LOG_FATAL, "Can't happen");
493 #endif
494       clen = zexclam - zdestfile;
495       zCforward = zbufalc (clen + 1);
496       memcpy (zCforward, zdestfile, clen);
497       zCforward[clen] = '\0';
498       zdestfile = zexclam + 1;
499     }
500 
501   /* Turn the destination into an absolute path, unless it is on a
502      remote system and -W was used.  */
503   if (fClocaldest)
504     zdestfile = zsysdep_local_file_cwd (zdestfile, sCdestsys.uuconf_zpubdir,
505 					(boolean *) NULL);
506   else if (fCexpand)
507     zdestfile = zsysdep_add_cwd (zdestfile);
508   if (zdestfile == NULL)
509     {
510       ulog_close ();
511       usysdep_exit (FALSE);
512     }
513 
514   /* Process each source argument.  */
515   for (i = optind; i < argc - 1 && ! FGOT_SIGNAL (); i++)
516     {
517       boolean flocal;
518       char *zfrom;
519 
520       fCneeds_cwd = FALSE;
521 
522       argv[i] = zremove_local_sys (&slocalsys, argv[i]);
523 
524       if (strchr (argv[i], '!') != NULL)
525 	{
526 	  flocal = FALSE;
527 	  zfrom = zbufcpy (argv[i]);
528 	}
529       else
530 	{
531 	  /* This is a local file.  Make sure we get it out of the
532 	     original directory.  We don't support local wildcards,
533 	     leaving that to the shell.  */
534 	  flocal = TRUE;
535 	  if (fsysdep_needs_cwd (argv[i]))
536 	    fCneeds_cwd = TRUE;
537 	  zfrom = zsysdep_local_file_cwd (argv[i],
538 					  sCdestsys.uuconf_zpubdir,
539 					  (boolean *) NULL);
540 	  if (zfrom == NULL)
541 	    ucabort ();
542 	}
543 
544       if (! flocal || ! fsysdep_directory (zfrom))
545 	uccopy (zfrom, zdestfile, FALSE);
546       else
547 	{
548 	  char *zbase, *zindir;
549 
550 	  if (! frecursive)
551 	    ulog (LOG_FATAL, "%s: directory without -R", zfrom);
552 
553 	  zbase = zsysdep_base_name (zfrom);
554 	  if (zbase == NULL)
555 	    ucabort ();
556 	  zindir = zsysdep_in_dir (zdestfile, zbase);
557 	  ubuffree (zbase);
558 	  if (zindir == NULL)
559 	    ucabort ();
560 	  usysdep_walk_tree (zfrom, ucdirfile, zindir);
561 	  ubuffree (zindir);
562 	}
563 
564       ubuffree (zfrom);
565     }
566 
567   /* See if we got an interrupt, presumably from the user.  */
568   if (FGOT_SIGNAL ())
569     ucabort ();
570 
571   /* Now push out the actual commands, making log entries for them.  */
572   ulog_to_file (puuconf, TRUE);
573   ulog_user (zCuser);
574 
575   ucspool_cmds (fjobid);
576 
577   ulog_close ();
578 
579   if (! fuucico)
580     fexit = TRUE;
581   else
582     {
583       const char *zsys;
584       boolean fany;
585 
586       zsys = zcone_system (&fany);
587 
588       if (zsys == NULL && ! fany)
589 	fexit = TRUE;
590       else
591 	{
592 	  const char *zarg;
593 	  char *zconfigarg;
594 
595 	  if (zsys == NULL)
596 	    zarg = "-r1";
597 	  else
598 	    {
599 	      char *z;
600 
601 	      z = zbufalc (sizeof "-Cs" + strlen (zsys));
602 	      sprintf (z, "-Cs%s", zsys);
603 	      zarg = z;
604 	    }
605 
606 	  if (zconfig == NULL)
607 	    zconfigarg = NULL;
608 	  else
609 	    {
610 	      zconfigarg = zbufalc (sizeof "-I" + strlen (zconfig));
611 	      sprintf (zconfigarg, "-I%s", zconfig);
612 	    }
613 
614 	  fexit = fsysdep_run (FALSE, "uucico", zarg, zconfigarg);
615 	}
616     }
617 
618   usysdep_exit (fexit);
619 
620   /* Avoid error about not returning.  */
621   return 0;
622 }
623 
624 /* Print usage message and die.  */
625 
626 static void
ucusage()627 ucusage ()
628 {
629   fprintf (stderr,
630 	   "Usage: %s [options] file1 [file2 ...] dest\n", zProgram);
631   fprintf (stderr, "Use %s --help for help\n", zProgram);
632   exit (EXIT_FAILURE);
633 }
634 
635 /* Print help message.  */
636 
637 static void
uchelp()638 uchelp ()
639 {
640   printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n",
641 	   VERSION);
642   printf ("Usage: %s [options] file1 [file2 ...] dest\n", zProgram);
643   printf (" -c,--nocopy: Do not copy local files to spool directory\n");
644   printf (" -C,-p,--copy: Copy local files to spool directory (default)\n");
645   printf (" -d,--directories: Create necessary directories (default)\n");
646   printf (" -f,--nodirectories: Do not create directories (fail if they do not exist)\n");
647   printf (" -g,--grade grade: Set job grade (must be alphabetic)\n");
648   printf (" -m,--mail: Report status of copy by mail\n");
649   printf (" -n,--notify user: Report status of copy by mail to remote user\n");
650   printf (" -R,--recursive: Copy directories recursively\n");
651   printf (" -r,--nouucico: Do not start uucico daemon\n");
652   printf (" -s,--status file: Report completion status to file\n");
653   printf (" -j,--jobid: Report job id\n");
654   printf (" -W,--noexpand: Do not add current directory to remote filenames\n");
655   printf (" -t,--uuto: Emulate uuto\n");
656   printf (" -u,--user name: Set user name\n");
657   printf (" -x,--debug debug: Set debugging level\n");
658 #if HAVE_TAYLOR_CONFIG
659   printf (" -I,--config file: Set configuration file to use\n");
660 #endif /* HAVE_TAYLOR_CONFIG */
661   printf (" -v,--version: Print version and exit\n");
662   printf (" --help: Print help and exit\n");
663   printf ("Report bugs to taylor-uucp@gnu.org\n");
664 }
665 
666 /* This is called for each file in a directory heirarchy.  */
667 
668 static void
ucdirfile(zfull,zrelative,pinfo)669 ucdirfile (zfull, zrelative, pinfo)
670      const char *zfull;
671      const char *zrelative;
672      pointer pinfo;
673 {
674   const char *zdestfile = (const char *) pinfo;
675   char *zto;
676 
677   zto = zsysdep_in_dir (zdestfile, zrelative);
678   if (zto == NULL)
679     ucabort ();
680 
681   uccopy (zfull, zto, TRUE);
682 
683   ubuffree (zto);
684 }
685 
686 /* Handle the copying of one regular file.  The zdest argument is the
687    destination file; if we are recursively copying a directory, it
688    will be extended by any subdirectory names.  Note that zdest is an
689    absolute path.  */
690 
691 static void
uccopy(zfile,zdest,fforcelocal)692 uccopy (zfile, zdest, fforcelocal)
693      const char *zfile;
694      const char *zdest;
695      boolean fforcelocal;
696 {
697   struct scmd s;
698   char *zexclam;
699   char *zto;
700 
701   zexclam = strchr (zfile, '!');
702 
703   if (zexclam == NULL || fforcelocal)
704     {
705       openfile_t efrom;
706 
707       /* Copy from a local file.  Make sure the user has access to
708 	 this file, since we are running setuid.  */
709       if (! fsysdep_access (zfile))
710 	ucabort ();
711 
712       /* If this copy is being requested by a remote system, we may
713 	 transfer the file if it needs the current working directory
714 	 (meaning, I hope, that it is in the execution directory) or
715 	 it is on the permitted transfer list.  Note that unlike most
716 	 of the other checks, this one is not double-checked by
717 	 uucico.  */
718       if (fCremote
719 	  && ! fCneeds_cwd
720 	  && ! fin_directory_list (zfile, sCdestsys.uuconf_pzremote_send,
721 				   sCdestsys.uuconf_zpubdir, TRUE,
722 				   TRUE, (const char *) NULL))
723 	ulog (LOG_FATAL, "Not permitted to send %s", zfile);
724 
725       if (fClocaldest)
726 	{
727 	  boolean fok;
728 	  unsigned int imode;
729 
730 	  /* Copy one local file to another.  */
731 
732 	  /* Check that we have permission to receive into the desired
733 	     directory.  */
734 	  if (fCremote)
735 	    fok = fin_directory_list (zdest,
736 				      sCdestsys.uuconf_pzremote_receive,
737 				      sCdestsys.uuconf_zpubdir, TRUE,
738 				      FALSE, (const char *) NULL);
739 	  else
740 	    fok = fin_directory_list (zdest,
741 				      sCdestsys.uuconf_pzlocal_receive,
742 				      sCdestsys.uuconf_zpubdir, TRUE,
743 				      FALSE, zCuser);
744 	  if (! fok)
745 	    ulog (LOG_FATAL, "Not permitted to receive to %s", zdest);
746 
747 	  zto = zsysdep_add_base (zdest, zfile);
748 	  if (zto == NULL)
749 	    ucabort ();
750 
751 	  efrom = esysdep_user_fopen (zfile, TRUE, TRUE);
752 	  if (! ffileisopen (efrom))
753 	    ucabort ();
754 	  if (! fcopy_open_file (efrom, zto, FALSE, fCmkdirs, TRUE))
755 	    ucabort ();
756 	  (void) ffileclose (efrom);
757 	  ubuffree (zto);
758 
759 	  imode = ixsysdep_user_file_mode (zfile);
760 	  if (imode != 0)
761 	    (void) fsysdep_change_mode (zto, imode);
762 	}
763       else
764 	{
765 	  const char *zloc;
766 	  char abtname[CFILE_NAME_LEN];
767 	  unsigned int imode;
768 	  char *ztemp;
769 
770 	  /* Copy a local file to a remote file.  We may have to
771 	     copy the local file to the spool directory.  */
772 	  imode = ixsysdep_user_file_mode (zfile);
773 	  if (imode == 0)
774 	    ucabort ();
775 
776 	  zloc = sCdestsys.uuconf_zlocalname;
777 	  if (zloc == NULL)
778 	    zloc = zClocalname;
779 
780 	  ztemp = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade,
781 					  FALSE, abtname, (char *) NULL,
782 					  (char *) NULL);
783 	  if (ztemp == NULL)
784 	    ucabort ();
785 
786 	  if (! fCcopy)
787 	    {
788 	      /* If we are copying the file, we don't actually use the
789 		 temporary file; we still want to get a name for the
790 		 other system to use as a key for file restart.  */
791 	      ubuffree (ztemp);
792 
793 	      /* Make sure the daemon will be permitted to send
794 		 this file.  */
795 	      if (! fsysdep_daemon_access (zfile))
796 		ucabort ();
797 	      if (! fin_directory_list (zfile, sCdestsys.uuconf_pzlocal_send,
798 					sCdestsys.uuconf_zpubdir, TRUE, TRUE,
799 					(fCremote
800 					 ? (const char *) NULL
801 					 : zCuser)))
802 		ulog (LOG_FATAL,
803 		      "Daemon not permitted to send %s (suggest --copy)",
804 		      zfile);
805 	    }
806 	  else
807 	    {
808 	      efrom = esysdep_user_fopen (zfile, TRUE, TRUE);
809 	      if (! ffileisopen (efrom))
810 		ucabort ();
811 	      ucrecord_file (ztemp);
812 	      if (! fcopy_open_file (efrom, ztemp, FALSE, TRUE, TRUE))
813 		ucabort ();
814 	      (void) ffileclose (efrom);
815 	    }
816 
817 	  if (zCforward == NULL)
818 	    {
819 	      /* We're not forwarding.  Just send the file.  */
820 	      s.bcmd = 'S';
821 	      s.bgrade = bCgrade;
822 	      s.pseq = NULL;
823 	      s.zfrom = zbufcpy (zfile);
824 	      s.zto = zbufcpy (zdest);
825 	      s.zuser = zCuser;
826 	      s.zoptions = abCsend_options;
827 	      s.ztemp = zbufcpy (abtname);
828 	      s.imode = imode;
829 	      s.znotify = zCnotify;
830 	      s.cbytes = -1;
831 	      s.zcmd = NULL;
832 	      s.ipos = 0;
833 
834 	      ucadd_cmd (&sCdestsys, &s, (const char *) NULL);
835 	    }
836 	  else
837 	    {
838 	      char *zbase;
839 	      char *zxqt;
840 	      char abxtname[CFILE_NAME_LEN];
841 	      char abdname[CFILE_NAME_LEN];
842 	      char abxname[CFILE_NAME_LEN];
843 	      FILE *e;
844 	      char *zlog;
845 
846 	      /* We want to forward this file through sCdestsys to
847 		 some other system(s).  We set up a remote execution
848 		 of uucp on sCdestsys to forward the file along.  */
849 	      zbase = zsysdep_base_name (zfile);
850 	      if (zbase == NULL)
851 		ucabort ();
852 
853 	      zxqt = zsysdep_data_file_name (&sCdestsys, zloc, bCgrade,
854 					     TRUE, abxtname, abdname,
855 					     abxname);
856 	      if (zxqt == NULL)
857 		ucabort ();
858 	      e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
859 	      if (e == NULL)
860 		ucabort ();
861 	      ucrecord_file (zxqt);
862 
863 	      fprintf (e, "U %s %s\n", zCuser, zloc);
864 	      fprintf (e, "F %s %s\n", abdname, zbase);
865 	      fprintf (e, "C uucp -C");
866 	      if (fCmkdirs)
867 		fprintf (e, " -d");
868 	      else
869 		fprintf (e, " -f");
870 	      fprintf (e, " -g %c", bCgrade);
871 	      if (fCmail)
872 		fprintf (e, " -m");
873 	      if (*zCnotify != '\0')
874 		fprintf (e, " -n %s", zCnotify);
875 	      if (! fCexpand)
876 		fprintf (e, " -W");
877 	      fprintf (e, " %s %s!%s\n", zbase, zCforward, zdest);
878 
879 	      ubuffree (zbase);
880 
881 	      if (! fstdiosync (e, zxqt))
882 		ulog (LOG_FATAL, "fsync failed");
883 	      if (fclose (e) != 0)
884 		ulog (LOG_FATAL, "fclose: %s", strerror (errno));
885 
886 	      /* Send the execution file.  */
887 	      s.bcmd = 'S';
888 	      s.bgrade = bCgrade;
889 	      s.pseq = NULL;
890 	      s.zfrom = zbufcpy (abxtname);
891 	      s.zto = zbufcpy (abxname);
892 	      s.zuser = zCuser;
893  	      s.zoptions = "C";
894 	      s.ztemp = s.zfrom;
895 	      s.imode = 0666;
896 	      s.znotify = NULL;
897 	      s.cbytes = -1;
898 	      s.zcmd = NULL;
899 	      s.ipos = 0;
900 
901 	      zlog = zbufalc (sizeof "Queuing uucp  !" + strlen (zfile)
902 			      + strlen (zCforward) + strlen (zdest));
903 	      sprintf (zlog, "Queuing uucp %s %s!%s", zfile, zCforward,
904 		       zdest);
905 
906 	      ucadd_cmd (&sCdestsys, &s, zlog);
907 
908 	      /* Send the data file.  */
909 	      s.bcmd = 'S';
910 	      s.bgrade = bCgrade;
911 	      s.pseq = NULL;
912 	      s.zfrom = zbufcpy (zfile);
913 	      s.zto = zbufcpy (abdname);
914 	      s.zuser = zCuser;
915  	      s.zoptions = fCcopy ? "C" : "c";
916 	      s.ztemp = zbufcpy (abtname);
917 	      s.imode = 0666;
918 	      s.znotify = NULL;
919 	      s.cbytes = -1;
920 	      s.zcmd = NULL;
921 	      s.ipos = 0;
922 
923 	      ucadd_cmd (&sCdestsys, &s, "");
924 	    }
925 	}
926     }
927   else
928     {
929       char *zfrom;
930       char *zforward;
931       size_t clen;
932       char *zcopy;
933       struct uuconf_system *qfromsys;
934       int iuuconf;
935       const char *zloc;
936 
937       /* Copy from a remote file.  Get the file name after any systems
938 	 we may need to forward the file from.  */
939       zfrom = strrchr (zfile, '!');
940       if (zfrom == zexclam)
941 	zforward = NULL;
942       else
943 	{
944 	  clen = zfrom - zexclam - 1;
945 	  zforward = zbufalc (clen + 1);
946 	  memcpy (zforward, zexclam + 1, clen);
947 	  zforward[clen] = '\0';
948 	}
949 
950       ++zfrom;
951       if (fCexpand)
952 	{
953 	  /* Add the current directory to the filename if it's not
954 	     already there.  */
955 	  zfrom = zsysdep_add_cwd (zfrom);
956 	  if (zfrom == NULL)
957 	    ucabort ();
958 	}
959 
960       /* Read the system information.  */
961       clen = zexclam - zfile;
962       zcopy = zbufalc (clen + 1);
963       memcpy (zcopy, zfile, clen);
964       zcopy[clen] = '\0';
965 
966       qfromsys = ((struct uuconf_system *)
967 		  xmalloc (sizeof (struct uuconf_system)));
968 
969       iuuconf = uuconf_system_info (pCuuconf, zcopy, qfromsys);
970       if (iuuconf == UUCONF_NOT_FOUND)
971 	{
972 	  if (! funknown_system (pCuuconf, zcopy, qfromsys))
973 	    ulog (LOG_FATAL, "%s: System not found", zcopy);
974 	}
975       else if (iuuconf != UUCONF_SUCCESS)
976 	ulog_uuconf (LOG_FATAL, pCuuconf, iuuconf);
977       ubuffree (zcopy);
978 
979       zloc = qfromsys->uuconf_zlocalname;
980       if (zloc == NULL)
981 	zloc = zClocalname;
982 
983       if (zforward == NULL && fClocaldest)
984 	{
985 	  boolean fok;
986 
987 	  /* The file is to come directly from qfromsys to the local
988 	     system.  */
989 
990 	  /* Check that we have permission to receive into the desired
991 	     directory.  If we don't have permission, uucico will
992 	     fail.  */
993 	  if (fCremote)
994 	    fok = fin_directory_list (zdest,
995 				      qfromsys->uuconf_pzremote_receive,
996 				      qfromsys->uuconf_zpubdir, TRUE,
997 				      FALSE, (const char *) NULL);
998 	  else
999 	    fok = fin_directory_list (zdest,
1000 				      qfromsys->uuconf_pzlocal_receive,
1001 				      qfromsys->uuconf_zpubdir, TRUE,
1002 				      FALSE, zCuser);
1003 	  if (! fok)
1004 	    ulog (LOG_FATAL, "Not permitted to receive to %s", zdest);
1005 
1006 	  /* If the remote filespec is wildcarded, we must generate an
1007 	     'X' request.  We currently check for Unix shell
1008 	     wildcards.  Note that it should do no harm to mistake a
1009 	     non-wildcard for a wildcard.  */
1010 	  if (zfrom[strcspn (zfrom, "*?[")] != '\0')
1011 	    {
1012 	      s.bcmd = 'X';
1013 	      zto = zbufalc (strlen (zloc) + strlen (zdest) + sizeof "!");
1014 	      sprintf (zto, "%s!%s", zloc, zdest);
1015 	    }
1016 	  else
1017 	    {
1018 	      s.bcmd = 'R';
1019 	      zto = zbufcpy (zdest);
1020 	    }
1021 
1022 	  s.bgrade = bCgrade;
1023 	  s.pseq = NULL;
1024 	  s.zfrom = zfrom;
1025 	  s.zto = zto;
1026 	  s.zuser = zCuser;
1027 	  s.zoptions = abCrec_options;
1028 	  s.ztemp = "";
1029 	  s.imode = 0;
1030 	  s.znotify = "";
1031 	  s.cbytes = -1;
1032 	  s.zcmd = NULL;
1033 	  s.ipos = 0;
1034 
1035 	  ucadd_cmd (qfromsys, &s, (const char *) NULL);
1036 	}
1037       else
1038 	{
1039 	  char *zxqt;
1040 	  char abtname[CFILE_NAME_LEN];
1041 	  char abxname[CFILE_NAME_LEN];
1042 	  FILE *e;
1043 	  char *zcmd;
1044 	  char *zlog;
1045 
1046 	  /* The file either comes from some other system through
1047 	     qfromsys or is intended for some other system.  Send an
1048 	     execution request to qfromsys to handle everything.  */
1049 	  zxqt = zsysdep_data_file_name (qfromsys, zloc, bCgrade, TRUE,
1050 					 abtname, (char *) NULL,
1051 					 abxname);
1052 	  if (zxqt == NULL)
1053 	    ucabort ();
1054 	  e = esysdep_fopen (zxqt, FALSE, FALSE, TRUE);
1055 	  if (e == NULL)
1056 	    ucabort ();
1057 	  ucrecord_file (zxqt);
1058 
1059 	  fprintf (e, "U %s %s\n", zCuser, zloc);
1060 	  fprintf (e, "C uucp -C");
1061 	  if (fCmkdirs)
1062 	    fprintf (e, " -d");
1063 	  else
1064 	    fprintf (e, " -f");
1065 	  fprintf (e, " -g %c", bCgrade);
1066 	  if (fCmail)
1067 	    fprintf (e, " -m");
1068 	  if (*zCnotify != '\0')
1069 	    fprintf (e, " -n %s", zCnotify);
1070 	  if (! fCexpand)
1071 	    fprintf (e, " -W");
1072 
1073 	  clen = (strlen (zfrom) + strlen (zloc)
1074 		  + strlen (sCdestsys.uuconf_zname) + strlen (zdest));
1075 	  if (zforward != NULL)
1076 	    clen += strlen (zforward);
1077 	  if (zCforward != NULL)
1078 	    clen += strlen (zCforward);
1079 	  zcmd = zbufalc (sizeof "! !!!" + clen);
1080 	  *zcmd = '\0';
1081 	  if (zforward != NULL)
1082 	    sprintf (zcmd + strlen (zcmd), "%s!", zforward);
1083 	  sprintf (zcmd + strlen (zcmd), "%s %s!", zfrom, zloc);
1084 	  if (! fClocaldest)
1085 	    sprintf (zcmd + strlen (zcmd), "%s!", sCdestsys.uuconf_zname);
1086 	  if (zCforward != NULL)
1087 	    sprintf (zcmd + strlen (zcmd), "%s!", zCforward);
1088 	  sprintf (zcmd + strlen (zcmd), "%s", zdest);
1089 
1090 	  fprintf (e, " %s\n", zcmd);
1091 
1092 	  if (! fstdiosync (e, zxqt))
1093 	    ulog (LOG_FATAL, "fsync failed");
1094 	  if (fclose (e) != 0)
1095 	    ulog (LOG_FATAL, "fclose: %s", strerror (errno));
1096 
1097 	  /* Send the execution file.  */
1098 	  s.bcmd = 'S';
1099 	  s.bgrade = bCgrade;
1100 	  s.pseq = NULL;
1101 	  s.zfrom = zbufcpy (abtname);
1102 	  s.zto = zbufcpy (abxname);
1103 	  s.zuser = zCuser;
1104 	  s.zoptions = "C";
1105 	  s.ztemp = s.zfrom;
1106 	  s.imode = 0666;
1107 	  s.znotify = NULL;
1108 	  s.cbytes = -1;
1109 	  s.zcmd = NULL;
1110 	  s.ipos = 0;
1111 
1112 	  zlog = zbufalc (sizeof "Queueing uucp " + strlen (zcmd));
1113 	  sprintf (zlog, "Queueing uucp %s", zcmd);
1114 
1115 	  ucadd_cmd (qfromsys, &s, zlog);
1116 
1117 	  ubuffree (zcmd);
1118 	  ubuffree (zforward);
1119 	}
1120     }
1121 }
1122 
1123 /* We keep a list of jobs for each system.  */
1124 
1125 struct sjob
1126 {
1127   struct sjob *qnext;
1128   const struct uuconf_system *qsys;
1129   int ccmds;
1130   struct scmd *pascmds;
1131   const char **pazlogs;
1132 };
1133 
1134 static struct sjob *qCjobs;
1135 
1136 static void
ucadd_cmd(qsys,qcmd,zlog)1137 ucadd_cmd (qsys, qcmd, zlog)
1138      const struct uuconf_system *qsys;
1139      const struct scmd *qcmd;
1140      const char *zlog;
1141 {
1142   struct sjob *qjob;
1143 
1144   if (! qsys->uuconf_fcall_transfer
1145       && ! qsys->uuconf_fcalled_transfer)
1146     ulog (LOG_FATAL, "Not permitted to transfer files to or from %s",
1147 	  qsys->uuconf_zname);
1148 
1149   for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext)
1150     if (strcmp (qjob->qsys->uuconf_zname, qsys->uuconf_zname) == 0)
1151       break;
1152 
1153   if (qjob == NULL)
1154     {
1155       qjob = (struct sjob *) xmalloc (sizeof (struct sjob));
1156       qjob->qnext = qCjobs;
1157       qjob->qsys = qsys;
1158       qjob->ccmds = 0;
1159       qjob->pascmds = NULL;
1160       qjob->pazlogs = NULL;
1161       qCjobs = qjob;
1162     }
1163 
1164   qjob->pascmds = ((struct scmd *)
1165 		   xrealloc ((pointer) qjob->pascmds,
1166 			     (qjob->ccmds + 1) * sizeof (struct scmd)));
1167   qjob->pascmds[qjob->ccmds] = *qcmd;
1168   qjob->pazlogs = ((const char **)
1169 		   xrealloc ((pointer) qjob->pazlogs,
1170 			     (qjob->ccmds + 1) * sizeof (const char *)));
1171   qjob->pazlogs[qjob->ccmds] = zlog;
1172   ++qjob->ccmds;
1173 }
1174 
1175 static void
ucspool_cmds(fjobid)1176 ucspool_cmds (fjobid)
1177      boolean fjobid;
1178 {
1179   struct sjob *qjob;
1180   char *zjobid;
1181 
1182   for (qjob = qCjobs; qjob != NULL; qjob = qjob->qnext)
1183     {
1184       ulog_system (qjob->qsys->uuconf_zname);
1185       zjobid = zsysdep_spool_commands (qjob->qsys, bCgrade, qjob->ccmds,
1186 				       qjob->pascmds, (boolean *) NULL);
1187       if (zjobid != NULL)
1188 	{
1189 	  int i;
1190 	  struct scmd *qcmd;
1191 	  const char **pz;
1192 
1193 	  for (i = 0, qcmd = qjob->pascmds, pz = qjob->pazlogs;
1194 	       i < qjob->ccmds;
1195 	       i++, qcmd++, pz++)
1196 	    {
1197 	      if (*pz != NULL)
1198 		{
1199 		  if (**pz != '\0')
1200 		    ulog (LOG_NORMAL, "%s", *pz);
1201 		}
1202 	      else if (qcmd->bcmd == 'S')
1203 		ulog (LOG_NORMAL, "Queuing send of %s to %s",
1204 		      qcmd->zfrom, qcmd->zto);
1205 	      else if (qcmd->bcmd == 'R')
1206 		ulog (LOG_NORMAL, "Queuing request of %s to %s",
1207 		      qcmd->zfrom, qcmd->zto);
1208 	      else
1209 		{
1210 		  const char *zto;
1211 
1212 		  zto = strrchr (qcmd->zto, '!');
1213 		  if (zto != NULL)
1214 		    ++zto;
1215 		  else
1216 		    zto = qcmd->zto;
1217 		  ulog (LOG_NORMAL, "Queuing request of %s to %s",
1218 			qcmd->zfrom, zto);
1219 		}
1220 	    }
1221 
1222 	  if (fjobid)
1223 	    printf ("%s\n", zjobid);
1224 
1225 	  ubuffree (zjobid);
1226 	}
1227     }
1228 }
1229 
1230 /* Return the system name for which we have created commands, or NULL
1231    if we've created commands for more than one system.  Set *pfany to
1232    FALSE if we didn't create work for any system.  */
1233 
1234 static const char *
zcone_system(pfany)1235 zcone_system (pfany)
1236      boolean *pfany;
1237 {
1238   if (qCjobs == NULL)
1239     {
1240       *pfany = FALSE;
1241       return NULL;
1242     }
1243 
1244   *pfany = TRUE;
1245 
1246   if (qCjobs->qnext == NULL)
1247     return qCjobs->qsys->uuconf_zname;
1248   else
1249     return NULL;
1250 }
1251 
1252 /* Keep track of all files we have created so that we can delete them
1253    if we get a signal.  The argument will be on the heap.  */
1254 
1255 static int cCfiles;
1256 static const char **pCaz;
1257 
1258 static void
ucrecord_file(zfile)1259 ucrecord_file (zfile)
1260      const char *zfile;
1261 {
1262   pCaz = (const char **) xrealloc ((pointer) pCaz,
1263 				   (cCfiles + 1) * sizeof (const char *));
1264   pCaz[cCfiles] = zfile;
1265   ++cCfiles;
1266 }
1267 
1268 /* Delete all the files we have recorded and exit.  */
1269 
1270 static void
ucabort()1271 ucabort ()
1272 {
1273   int i;
1274 
1275   for (i = 0; i < cCfiles; i++)
1276     (void) remove (pCaz[i]);
1277   ulog_close ();
1278   usysdep_exit (FALSE);
1279 }
1280