1/* uuxqt.c
2   Run uux commands.
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
28const char uuxqt_rcsid[] = "$Id: uuxqt.c,v 1.92 2002/03/05 19:10:42 ian Rel $";
29#endif
30
31#include <errno.h>
32#include <ctype.h>
33
34#include "getopt.h"
35
36#include "uudefs.h"
37#include "uuconf.h"
38#include "system.h"
39
40/* Static variables used to unlock things if we get a fatal error.  */
41static int iQlock_seq = -1;
42static const char *zQunlock_cmd;
43static const char *zQunlock_file;
44static boolean fQunlock_directory;
45int cQmaxuuxqts;
46
47/* Static variables to free in uqcleanup.  */
48static char *zQoutput;
49static char *zQmail;
50
51/* Local functions.  */
52static void uqusage P((void));
53static void uqhelp P((void));
54static void uqabort P((void));
55static void uqdo_xqt_file P((pointer puuconf, const char *zfile,
56			     const char *zbase,
57			     const struct uuconf_system *qsys,
58			     const char *zlocalname,
59			     const char *zcmd, boolean *pfprocessed));
60static void uqcleanup P((const char *zfile, int iflags));
61static int isave_files P((const struct uuconf_system *, const char *zmail,
62			  const char *zfile, int iclean));
63static boolean fqforward P((const char *zfile, char **pzallowed,
64			    const char *zlog, const char *zmail));
65
66/* Long getopt options.  */
67static const struct option asQlongopts[] =
68{
69  { "command", required_argument, 0, 'c' },
70  { "system", required_argument, 0, 's' },
71  { "config", required_argument, NULL, 'I' },
72  { "debug", required_argument, NULL, 'x' },
73  { "version", no_argument, NULL, 'v' },
74  { "help", no_argument, NULL, 1 },
75  { NULL, 0, NULL, 0 }
76};
77
78int
79main (argc, argv)
80     int argc;
81     char **argv;
82{
83  /* The type of command to execute (NULL for any type).  */
84  const char *zcmd = NULL;
85  /* The configuration file name.  */
86  const char *zconfig = NULL;
87  /* The system to execute commands for.  */
88  const char *zdosys = NULL;
89  int iopt;
90  pointer puuconf;
91  int iuuconf;
92  const char *zlocalname;
93  boolean fany;
94  char *z, *zgetsys;
95  boolean ferr;
96  boolean fsys;
97  struct uuconf_system ssys;
98
99  if (argc < 1)
100  {
101      zProgram = "uuxqt";
102      uqusage ();
103  }
104
105  zProgram = argv[0];
106
107  while ((iopt = getopt_long (argc, argv, "c:I:s:vx:", asQlongopts,
108			      (int *) NULL)) != EOF)
109    {
110      switch (iopt)
111	{
112	case 'c':
113	  /* Set the type of command to execute.  */
114	  zcmd = optarg;
115	  break;
116
117	case 'I':
118	  /* Set the configuration file name.  */
119	  if (fsysdep_other_config (optarg))
120	    zconfig = optarg;
121	  break;
122
123	case 's':
124	  zdosys = optarg;
125	  break;
126
127	case 'x':
128#if DEBUG > 1
129	  /* Set the debugging level.  */
130	  iDebug |= idebug_parse (optarg);
131#endif
132	  break;
133
134	case 'v':
135	  /* Print version and exit.  */
136	  printf ("uuxqt (Taylor UUCP) %s\n", VERSION);
137	  printf ("Copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n");
138	  printf ("This program is free software; you may redistribute it under the terms of\n");
139	  printf ("the GNU General Public LIcense.  This program has ABSOLUTELY NO WARRANTY.\n");
140	  exit (EXIT_SUCCESS);
141	  /*NOTREACHED*/
142
143	case 1:
144	  /* --help.  */
145	  uqhelp ();
146	  exit (EXIT_SUCCESS);
147	  /*NOTREACHED*/
148
149	case 0:
150	  /* Long option found and flag set.  */
151	  break;
152
153	default:
154	  uqusage ();
155	  break;
156	}
157    }
158
159  if (optind != argc)
160    uqusage ();
161
162  iuuconf = uuconf_init (&puuconf, (const char *) NULL, zconfig);
163  if (iuuconf != UUCONF_SUCCESS)
164    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
165
166#if DEBUG > 1
167  {
168    const char *zdebug;
169
170    iuuconf = uuconf_debuglevel (puuconf, &zdebug);
171    if (iuuconf != UUCONF_SUCCESS)
172      ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
173    if (zdebug != NULL)
174      iDebug |= idebug_parse (zdebug);
175  }
176#endif
177
178  iuuconf = uuconf_maxuuxqts (puuconf, &cQmaxuuxqts);
179  if (iuuconf != UUCONF_SUCCESS)
180    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
181
182#ifdef SIGINT
183  usysdep_signal (SIGINT);
184#endif
185#ifdef SIGHUP
186  usysdep_signal (SIGHUP);
187#endif
188#ifdef SIGQUIT
189  usysdep_signal (SIGQUIT);
190#endif
191#ifdef SIGTERM
192  usysdep_signal (SIGTERM);
193#endif
194#ifdef SIGPIPE
195  usysdep_signal (SIGPIPE);
196#endif
197
198  usysdep_initialize (puuconf, INIT_SUID);
199
200  ulog_to_file (puuconf, TRUE);
201  ulog_fatal_fn (uqabort);
202
203  iuuconf = uuconf_localname (puuconf, &zlocalname);
204  if (iuuconf == UUCONF_NOT_FOUND)
205    {
206      zlocalname = zsysdep_localname ();
207      if (zlocalname == NULL)
208	exit (EXIT_FAILURE);
209    }
210  else if (iuuconf != UUCONF_SUCCESS)
211    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
212
213  fsys = FALSE;
214
215  /* If we were given a system name, canonicalize it.  */
216  if (zdosys != NULL)
217    {
218      iuuconf = uuconf_system_info (puuconf, zdosys, &ssys);
219      if (iuuconf != UUCONF_SUCCESS)
220	{
221	  if (iuuconf != UUCONF_NOT_FOUND)
222	    ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
223
224	  if (strcmp (zdosys, zlocalname) == 0)
225	    {
226	      iuuconf = uuconf_system_local (puuconf, &ssys);
227	      if (iuuconf != UUCONF_SUCCESS)
228		ulog_uuconf (LOG_FATAL, puuconf, iuuconf);
229	      ssys.uuconf_zname = (char *) zlocalname;
230	    }
231	  else
232	    {
233	      if (! funknown_system (puuconf, zdosys, &ssys))
234		ulog (LOG_FATAL, "%s: system not found", zdosys);
235	    }
236	}
237
238      zdosys = zbufcpy (ssys.uuconf_zname);
239      fsys = TRUE;
240    }
241
242  /* Limit the number of uuxqt processes, and make sure we're the only
243     uuxqt daemon running for this command.  */
244  iQlock_seq = ixsysdep_lock_uuxqt (zcmd, cQmaxuuxqts);
245  if (iQlock_seq < 0)
246    {
247      ulog_close ();
248      usysdep_exit (TRUE);
249    }
250  zQunlock_cmd = zcmd;
251
252  /* Keep scanning the execute files until we don't process any of
253     them.  */
254  do
255    {
256      fany = FALSE;
257
258      /* Look for each execute file, and run it.  */
259
260      if (! fsysdep_get_xqt_init (zdosys))
261	{
262	  ulog_close ();
263	  usysdep_exit (FALSE);
264	}
265
266      while ((z = zsysdep_get_xqt (zdosys, &zgetsys, &ferr)) != NULL)
267	{
268	  const char *zloc;
269	  boolean fprocessed;
270	  char *zbase;
271
272	  /* Get the system information for the system returned by
273	     zsysdep_get_xqt.  */
274	  if (! fsys || strcmp (ssys.uuconf_zname, zgetsys) != 0)
275	    {
276	      if (fsys)
277		(void) uuconf_system_free (puuconf, &ssys);
278
279	      iuuconf = uuconf_system_info (puuconf, zgetsys,
280					    &ssys);
281	      if (iuuconf != UUCONF_SUCCESS)
282		{
283		  if (iuuconf != UUCONF_NOT_FOUND)
284		    {
285		      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
286		      ubuffree (z);
287		      ubuffree (zgetsys);
288		      continue;
289		    }
290		  else if (strcmp (zgetsys, zlocalname) == 0)
291		    {
292		      iuuconf = uuconf_system_local (puuconf, &ssys);
293		      if (iuuconf != UUCONF_SUCCESS)
294			{
295			  ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
296			  ubuffree (z);
297			  ubuffree (zgetsys);
298			  continue;
299			}
300		      ssys.uuconf_zname = (char *) zlocalname;
301		    }
302		  else
303		    {
304		      if (! funknown_system (puuconf, zgetsys, &ssys))
305			{
306			  ulog (LOG_ERROR,
307				"%s: Execute file for unknown system %s",
308				z, zgetsys);
309			  (void) remove (z);
310			  ubuffree (z);
311			  ubuffree (zgetsys);
312			  continue;
313			}
314		    }
315		}
316
317	      fsys = TRUE;
318	    }
319
320	  /* If we've received a signal, get out of the loop.  */
321	  if (FGOT_SIGNAL ())
322	    {
323	      ubuffree (z);
324	      ubuffree (zgetsys);
325	      break;
326	    }
327
328	  /* Make sure we are supposed to be executing jobs for this
329	     system.  */
330	  if (zdosys != NULL && strcmp (zdosys, ssys.uuconf_zname) != 0)
331	    {
332	      ubuffree (z);
333	      ubuffree (zgetsys);
334	      continue;
335	    }
336
337	  zloc = ssys.uuconf_zlocalname;
338	  if (zloc == NULL)
339	    zloc = zlocalname;
340
341	  ulog_system (ssys.uuconf_zname);
342	  zbase = zsysdep_base_name (z);
343	  uqdo_xqt_file (puuconf, z, zbase, &ssys, zloc, zcmd, &fprocessed);
344	  ubuffree (zbase);
345	  ulog_system ((const char *) NULL);
346	  ulog_user ((const char *) NULL);
347
348	  if (fprocessed)
349	    fany = TRUE;
350	  ubuffree (z);
351	  ubuffree (zgetsys);
352	}
353
354      usysdep_get_xqt_free (zdosys);
355    }
356  while (fany && ! FGOT_SIGNAL ());
357
358  (void) fsysdep_unlock_uuxqt (iQlock_seq, zcmd, cQmaxuuxqts);
359  iQlock_seq = -1;
360
361  ulog_close ();
362
363  if (FGOT_SIGNAL ())
364    ferr = TRUE;
365
366  usysdep_exit (! ferr);
367
368  /* Avoid errors about not returning a value.  */
369  return 0;
370}
371
372static void
373uqhelp ()
374{
375  printf ("Taylor UUCP %s, copyright (C) 1991, 92, 93, 94, 1995, 2002 Ian Lance Taylor\n",
376	   VERSION);
377  printf ("Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram);
378  printf (" -c,--command cmd: Set type of command to execute\n");
379  printf (" -s,--system system: Execute commands only for named system\n");
380  printf (" -x,--debug debug: Set debugging level\n");
381#if HAVE_TAYLOR_CONFIG
382  printf (" -I,--config file: Set configuration file to use\n");
383#endif /* HAVE_TAYLOR_CONFIG */
384  printf (" -v,--version: Print version and exit\n");
385  printf (" --help: Print help and exit\n");
386  printf ("Report bugs to taylor-uucp@gnu.org\n");
387}
388
389static void
390uqusage ()
391{
392  fprintf (stderr,
393	   "Usage: %s [-c,--command cmd] [-s,--system system]\n", zProgram);
394  fprintf (stderr, "Use %s --help for help\n", zProgram);
395  exit (EXIT_FAILURE);
396}
397
398/* This is the abort function called when we get a fatal error.  */
399
400static void
401uqabort ()
402{
403#if ! HAVE_HDB_LOGGING
404  /* When using HDB logging, it's a pain to have no system name.  */
405  ulog_system ((const char *) NULL);
406#endif
407
408  ulog_user ((const char *) NULL);
409
410  if (fQunlock_directory)
411    (void) fsysdep_unlock_uuxqt_dir (iQlock_seq);
412
413  if (zQunlock_file != NULL)
414    (void) fsysdep_unlock_uuxqt_file (zQunlock_file);
415
416  if (iQlock_seq >= 0)
417    (void) fsysdep_unlock_uuxqt (iQlock_seq, zQunlock_cmd, cQmaxuuxqts);
418
419  ulog_close ();
420
421  usysdep_exit (FALSE);
422}
423
424/* An execute file is a series of lines.  The first character of each
425   line is a command.  The following commands are defined:
426
427   C command-line
428   I standard-input
429   O standard-output [ system ]
430   F required-file filename-to-use
431   R requestor-address
432   U user system
433   Z (acknowledge if command failed; default)
434   N (no acknowledgement on failure)
435   n (acknowledge if command succeeded)
436   B (return command input on error)
437   e (process with sh)
438   E (process with exec)
439   M status-file
440   Q (C, I, O, F, R, U, M arguments are backslash quoted)
441   W change working directory
442   # comment
443
444   Unrecognized commands are ignored.  We actually do not recognize
445   the Z command, since it requests default behaviour.  We always send
446   mail on failure, unless the N command appears.  We never send mail
447   on success, unless the n command appears.
448
449   This code does not currently support the B or M commands.  */
450
451/* Command arguments.  */
452static char **azQargs;
453/* Command as a complete string.  */
454static char *zQcmd;
455/* Working directory */
456static char *zQwd;
457/* Standard input file name.  */
458static char *zQinput;
459/* Standard output file name.  */
460static char *zQoutfile;
461/* Standard output system.  */
462static char *zQoutsys;
463/* Number of required files.  */
464static int cQfiles;
465/* Names of required files.  */
466static char **azQfiles;
467/* Names required files should be renamed to (NULL if original is OK).  */
468static char **azQfiles_to;
469/* Requestor address (this is where mail should be sent).  */
470static char *zQrequestor;
471/* User name.  */
472static const char *zQuser;
473/* System name.  */
474static const char *zQsystem;
475/* This is set by the N flag, meaning that no acknowledgement should
476   be mailed on failure.  */
477static boolean fQno_ack;
478/* This is set by the n flag, meaning that acknowledgement should be
479   mailed if the command succeeded.  */
480static boolean fQsuccess_ack;
481/* This is set by the B flag, meaning that command input should be
482   mailed to the requestor if an error occurred.  */
483static boolean fQsend_input;
484/* This is set by the E flag, meaning that exec should be used to
485   execute the command.  */
486static boolean fQuse_exec;
487/* The status should be copied to this file on the requesting host.  */
488static const char *zQstatus_file;
489/* Whether the entries in the file are backslash quoted.  */
490static boolean fQquoted;
491#if ALLOW_SH_EXECUTION
492/* This is set by the e flag, meaning that sh should be used to
493   execute the command.  */
494static boolean fQuse_sh;
495#endif /* ALLOW_SH_EXECUTION */
496
497static int iqcmd P((pointer puuconf, int argc, char **argv, pointer pvar,
498		    pointer pinfo));
499static int iqout P((pointer puuconf, int argc, char **argv, pointer pvar,
500		    pointer pinfo));
501static int iqfile P((pointer puuconf, int argc, char **argv, pointer pvar,
502		     pointer pinfo));
503static int iqrequestor P((pointer puuconf, int argc, char **argv,
504			  pointer pvar, pointer pinfo));
505static int iquser P((pointer puuconf, int argc, char **argv, pointer pvar,
506		     pointer pinfo));
507static int iqset P((pointer puuconf, int argc, char **argv, pointer pvar,
508		    pointer pinfo));
509
510/* We are lax about the number of arguments the functions accept,
511   because there is a lot of variation in what other (buggy) UUCP
512   packages generate.  Unused arguments are ignored.  */
513
514static const struct uuconf_cmdtab asQcmds[] =
515{
516  { "C", UUCONF_CMDTABTYPE_FN | 0, NULL, iqcmd },
517  { "I", UUCONF_CMDTABTYPE_STRING, (pointer) &zQinput, NULL },
518  { "O", UUCONF_CMDTABTYPE_FN | 0, NULL, iqout },
519  { "F", UUCONF_CMDTABTYPE_FN | 0, NULL, iqfile },
520  { "R", UUCONF_CMDTABTYPE_FN | 0, NULL, iqrequestor },
521  { "U", UUCONF_CMDTABTYPE_FN | 0, NULL, iquser },
522  { "N", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQno_ack, iqset },
523  { "n", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsuccess_ack, iqset },
524  { "B", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQsend_input, iqset },
525#if ALLOW_SH_EXECUTION
526  { "e", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_sh, iqset },
527#endif
528  { "E", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQuse_exec, iqset },
529  { "M", UUCONF_CMDTABTYPE_STRING, (pointer) &zQstatus_file, NULL },
530  { "Q", UUCONF_CMDTABTYPE_FN | 0, (pointer) &fQquoted, iqset },
531  { "W", UUCONF_CMDTABTYPE_STRING | 0, (pointer) &zQwd, NULL },
532  { NULL, 0, NULL, NULL }
533};
534
535/* Handle the C command: store off the arguments.  */
536
537/*ARGSUSED*/
538static int
539iqcmd (puuconf, argc, argv, pvar, pinfo)
540     pointer puuconf ATTRIBUTE_UNUSED;
541     int argc;
542     char **argv;
543     pointer pvar ATTRIBUTE_UNUSED;
544     pointer pinfo ATTRIBUTE_UNUSED;
545{
546  int i;
547  size_t clen;
548
549  if (argc <= 1)
550    return UUCONF_CMDTABRET_CONTINUE;
551
552  azQargs = (char **) xmalloc (argc * sizeof (char *));
553  clen = 0;
554  for (i = 1; i < argc; i++)
555    {
556      azQargs[i - 1] = zbufcpy (argv[i]);
557      clen += strlen (argv[i]) + 1;
558    }
559  azQargs[i - 1] = NULL;
560
561  zQcmd = (char *) xmalloc (clen);
562  zQcmd[0] = '\0';
563  for (i = 1; i < argc - 1; i++)
564    {
565      strcat (zQcmd, argv[i]);
566      strcat (zQcmd, " ");
567    }
568  strcat (zQcmd, argv[i]);
569
570  return UUCONF_CMDTABRET_CONTINUE;
571}
572
573/* Handle the O command, which may have one or two arguments.  */
574
575/*ARGSUSED*/
576static int
577iqout (puuconf, argc, argv, pvar, pinfo)
578     pointer puuconf ATTRIBUTE_UNUSED;
579     int argc;
580     char **argv;
581     pointer pvar ATTRIBUTE_UNUSED;
582     pointer pinfo ATTRIBUTE_UNUSED;
583{
584  if (argc > 1)
585    zQoutfile = zbufcpy (argv[1]);
586  if (argc > 2)
587    zQoutsys = zbufcpy (argv[2]);
588
589  return UUCONF_CMDTABRET_CONTINUE;
590}
591
592/* Handle the F command, which may have one or two arguments.  */
593
594/*ARGSUSED*/
595static int
596iqfile (puuconf, argc, argv, pvar, pinfo)
597     pointer puuconf ATTRIBUTE_UNUSED;
598     int argc;
599     char **argv;
600     pointer pvar ATTRIBUTE_UNUSED;
601     pointer pinfo ATTRIBUTE_UNUSED;
602{
603  if (argc < 2)
604    return UUCONF_CMDTABRET_CONTINUE;
605
606  /* If this file is not in the spool directory, just ignore it.  */
607  if (! fspool_file (argv[1]))
608    return UUCONF_CMDTABRET_CONTINUE;
609
610  ++cQfiles;
611  azQfiles = (char **) xrealloc ((pointer) azQfiles,
612				 cQfiles * sizeof (char *));
613  azQfiles_to = (char **) xrealloc ((pointer) azQfiles_to,
614				    cQfiles * sizeof (char *));
615
616  azQfiles[cQfiles - 1] = zbufcpy (argv[1]);
617  if (argc > 2)
618    azQfiles_to[cQfiles - 1] = zbufcpy (argv[2]);
619  else
620    azQfiles_to[cQfiles - 1] = NULL;
621
622  return UUCONF_CMDTABRET_CONTINUE;
623}
624
625/* Handle the R command, which may have one or two arguments.  */
626
627/*ARGSUSED*/
628static int
629iqrequestor (puuconf, argc, argv, pvar, pinfo)
630     pointer puuconf ATTRIBUTE_UNUSED;
631     int argc;
632     char **argv;
633     pointer pvar ATTRIBUTE_UNUSED;
634     pointer pinfo ATTRIBUTE_UNUSED;
635{
636  /* We normally have a single argument, which is the ``requestor''
637     address, to which we should send any success or error messages.
638     Apparently the DOS program UUPC sends two arguments, which are
639     the username and the host.  */
640  if (argc == 2)
641    zQrequestor = zbufcpy (argv[1]);
642  else if (argc > 2)
643    {
644      zQrequestor = zbufalc (strlen (argv[1]) + strlen (argv[2])
645			     + sizeof "!");
646      sprintf (zQrequestor, "%s!%s", argv[2], argv[1]);
647    }
648
649  return UUCONF_CMDTABRET_CONTINUE;
650}
651
652/* Handle the U command, which takes two arguments.  */
653
654/*ARGSUSED*/
655static int
656iquser (puuconf, argc, argv, pvar, pinfo)
657     pointer puuconf ATTRIBUTE_UNUSED;
658     int argc;
659     char **argv;
660     pointer pvar ATTRIBUTE_UNUSED;
661     pointer pinfo ATTRIBUTE_UNUSED;
662{
663  if (argc > 1)
664    zQuser = argv[1];
665  if (argc > 2)
666    zQsystem = argv[2];
667  return UUCONF_CMDTABRET_KEEP;
668}
669
670/* Handle various commands which just set boolean variables.  */
671
672/*ARGSUSED*/
673static int
674iqset (puuconf, argc, argv, pvar, pinfo)
675     pointer puuconf ATTRIBUTE_UNUSED;
676     int argc ATTRIBUTE_UNUSED;
677     char **argv ATTRIBUTE_UNUSED;
678     pointer pvar;
679     pointer pinfo ATTRIBUTE_UNUSED;
680{
681  boolean *pf = (boolean *) pvar;
682
683  *pf = TRUE;
684  return UUCONF_CMDTABRET_CONTINUE;
685}
686
687/* The execution processing does a lot of things that have to be
688   cleaned up.  Rather than try to add the appropriate statements
689   to each return point, we keep a set of flags indicating what
690   has to be cleaned up.  The actual clean up is done by the
691   function uqcleanup.  */
692#define REMOVE_FILE (01)
693#define REMOVE_NEEDED (02)
694#define FREE_QINPUT (04)
695#define REMOVE_QINPUT (010)
696#define FREE_OUTPUT (020)
697#define FREE_MAIL (040)
698
699static void ulog_azQargs(char *msg) {
700    char *buf = NULL;
701    int i;
702    for(i = 0; azQargs[i]; i++) {
703	char *tmp = NULL;
704	asprintf(&tmp, "%s [%d] '%s'", buf ? buf : "", i, azQargs[i]);
705	free(buf);
706	buf = tmp;
707    }
708    ulog(LOG_ERROR, "%s %s", msg, buf);
709    free(buf);
710}
711
712char *strbufdup(char *src) {
713    char *ret = zbufalc(strlen(src));
714    strcpy(ret, src);
715    return ret;
716}
717
718/* Process an execute file.  The zfile argument is the name of the
719   execute file.  The zbase argument is the base name of zfile.  The
720   qsys argument describes the system it came from.  The zcmd argument
721   is the name of the command we are executing (from the -c option) or
722   NULL if any command is OK.  This sets *pfprocessed to TRUE if the
723   file is ready to be executed.  */
724
725static void
726uqdo_xqt_file (puuconf, zfile, zbase, qsys, zlocalname, zcmd, pfprocessed)
727     pointer puuconf;
728     const char *zfile;
729     const char *zbase;
730     const struct uuconf_system *qsys;
731     const char *zlocalname;
732     const char *zcmd;
733     boolean *pfprocessed;
734{
735  char *zabsolute;
736  boolean ferr;
737  FILE *e;
738  int iuuconf;
739  int i;
740  int iclean;
741  const char *zmail;
742  char *zoutput;
743  char *zinput;
744  boolean fbadname;
745  char abtemp[CFILE_NAME_LEN];
746  char abdata[CFILE_NAME_LEN];
747  char *zerror;
748  struct uuconf_system soutsys;
749  const struct uuconf_system *qoutsys;
750  boolean fshell;
751  size_t clen;
752  char *zfullcmd;
753  boolean ftemp;
754
755  *pfprocessed = FALSE;
756
757  e = fopen (zfile, "r");
758  if (e == NULL)
759    return;
760
761  azQargs = NULL;
762  zQcmd = NULL;
763  zQwd = NULL;
764  zQinput = NULL;
765  zQoutfile = NULL;
766  zQoutsys = NULL;
767  cQfiles = 0;
768  azQfiles = NULL;
769  azQfiles_to = NULL;
770  zQrequestor = NULL;
771  zQuser = NULL;
772  zQsystem = NULL;
773  fQno_ack = FALSE;
774  fQsuccess_ack = FALSE;
775  fQsend_input = FALSE;
776  fQuse_exec = FALSE;
777  zQstatus_file = NULL;
778  fQquoted = FALSE;
779#if ALLOW_SH_EXECUTION
780  fQuse_sh = FALSE;
781#endif
782
783  iuuconf = uuconf_cmd_file (puuconf, e, asQcmds, (pointer) zbase,
784			     (uuconf_cmdtabfn) NULL,
785			     (UUCONF_CMDTABFLAG_CASE
786			      | UUCONF_CMDTABFLAG_NOCOMMENTS),
787			     (pointer) NULL);
788  (void) fclose (e);
789
790  if (iuuconf != UUCONF_SUCCESS)
791    {
792      ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
793
794      /* If we got a non-transient error, we notify the administrator.
795	 We can't bounce it back to the original requestor, because we
796	 don't know how to read the file to figure out who it is (it
797	 would probably be possible to read the file and work it out,
798	 but it doesn't seem worth it for such an unlikely error).  */
799      if (UUCONF_ERROR_VALUE (iuuconf) == UUCONF_SYNTAX_ERROR
800	  || UUCONF_ERROR_VALUE (iuuconf) == UUCONF_UNKNOWN_COMMAND)
801	{
802	  const char *az[20];
803	  char *znew;
804
805	  i = 0;
806	  az[i++] = "The execution file\n\t";
807	  az[i++] = zfile;
808	  az[i++] = "\nfor system\n\t";
809	  az[i++] = qsys->uuconf_zname;
810	  az[i++] = "\nwas corrupt.  ";
811	  znew = zsysdep_save_corrupt_file (zfile);
812	  if (znew == NULL)
813	    {
814	      az[i++] = "The file could not be preserved.\n";
815	      (void) remove (zfile);
816	    }
817	  else
818	    {
819	      az[i++] = "It has been moved to\n\t";
820	      az[i++] = znew;
821	      az[i++] = "\n";
822	    }
823	  (void) fsysdep_mail (OWNER, "Corrupt execution file", i, az);
824	  ubuffree (znew);
825	}
826
827      return;
828    }
829
830  if (fQquoted)
831    {
832      if (azQargs != NULL)
833	{
834	  for (i = 0; azQargs[i] != NULL; ++i)
835	    (void) cescape (azQargs[i]);
836	}
837      if (zQcmd != NULL)
838	(void) cescape (zQcmd);
839      if (zQinput != NULL)
840	(void) cescape (zQinput);
841      if (zQoutfile != NULL)
842	(void) cescape (zQoutfile);
843      if (zQoutsys != NULL)
844	(void) cescape (zQoutsys);
845      for (i = 0; i < cQfiles; ++i)
846	{
847	  (void) cescape (azQfiles[i]);
848	  if (azQfiles_to[i] != NULL)
849	    (void) cescape (azQfiles_to[i]);
850	}
851      if (zQrequestor != NULL)
852	(void) cescape (zQrequestor);
853      if (zQuser != NULL)
854	(void) cescape ((char *) zQuser);
855      if (zQsystem != NULL)
856	(void) cescape ((char *) zQsystem);
857      if (zQstatus_file != NULL)
858	(void) cescape ((char *) zQstatus_file);
859    }
860
861  iclean = 0;
862
863  if (azQargs == NULL)
864    {
865      ulog (LOG_ERROR, "%s: No command given", zbase);
866      uqcleanup (zfile, iclean | REMOVE_FILE);
867      return;
868    }
869
870  if (zcmd != NULL)
871    {
872      if (strcmp (zcmd, azQargs[0]) != 0)
873	{
874	  uqcleanup (zfile, iclean);
875	  return;
876	}
877    }
878  else
879    {
880      /* If there is a lock file for this particular command already,
881	 it means that some other uuxqt is supposed to handle it.  */
882      if (fsysdep_uuxqt_locked (azQargs[0]))
883	{
884	  uqcleanup (zfile, iclean);
885	  return;
886	}
887    }
888
889  /* Lock this particular file.  */
890  if (! fsysdep_lock_uuxqt_file (zfile))
891    {
892      uqcleanup (zfile, iclean);
893      return;
894    }
895
896  zQunlock_file = zfile;
897
898  /* Now that we have the file locked, make sure it still exists.
899     Otherwise another uuxqt could have just finished processing it
900     and removed the lock file.  */
901  if (! fsysdep_file_exists (zfile))
902    {
903      uqcleanup (zfile, iclean);
904      return;
905    }
906
907  if (zQuser != NULL)
908    ulog_user (zQuser);
909  else if (zQrequestor != NULL)
910    ulog_user (zQrequestor);
911  else
912    ulog_user ("unknown");
913
914  /* zQsystem, if it is set, comes from the execution file, which
915     means that we do not trust it.  We only retain it if
916     qsys->uuconf_zname is a prefix of it, since that can happen with
917     a job from an anonymous system on certain spool directory types,
918     and is unlikely to cause any trouble anyhow.  */
919  if (zQsystem == NULL
920      || strncmp (zQsystem, qsys->uuconf_zname,
921		  strlen (qsys->uuconf_zname)) != 0)
922    zQsystem = qsys->uuconf_zname;
923
924  /* Make sure that all the required files exist, and get their
925     full names in the spool directory.  */
926  for (i = 0; i < cQfiles; i++)
927    {
928      char *zreal;
929
930      zreal = zsysdep_spool_file_name (qsys, azQfiles[i], (pointer) NULL);
931      if (zreal == NULL)
932	{
933	  uqcleanup (zfile, iclean);
934	  return;
935	}
936      if (! fsysdep_file_exists (zreal))
937	{
938	  uqcleanup (zfile, iclean);
939	  return;
940	}
941      ubuffree (azQfiles[i]);
942      azQfiles[i] = zbufcpy (zreal);
943      ubuffree (zreal);
944    }
945
946  /* Lock the execution directory.  */
947  if (! fsysdep_lock_uuxqt_dir (iQlock_seq))
948    {
949      ulog (LOG_ERROR, "Could not lock execute directory");
950      uqcleanup (zfile, iclean);
951      return;
952    }
953  fQunlock_directory = TRUE;
954
955  iclean |= REMOVE_FILE | REMOVE_NEEDED;
956  *pfprocessed = TRUE;
957
958  /* Get the address to mail results to.  Prepend the system from
959     which the execute file originated, since mail addresses are
960     relative to it.  */
961  zmail = NULL;
962  if (zQrequestor != NULL)
963    zmail = zQrequestor;
964  else if (zQuser != NULL)
965    zmail = zQuser;
966  if (zmail != NULL
967#if HAVE_INTERNET_MAIL
968      && strchr (zmail, '@') == NULL
969#endif
970      && strcmp (zQsystem, zlocalname) != 0)
971    {
972      char *zset;
973
974      zset = zbufalc (strlen (zQsystem) + strlen (zmail) + 2);
975      sprintf (zset, "%s!%s", zQsystem, zmail);
976      zmail = zset;
977      zQmail = zset;
978      iclean |= FREE_MAIL;
979    }
980
981  /* The command "uucp" is handled specially.  We make sure that the
982     appropriate forwarding is permitted, and we add a -u argument to
983     specify the user.  */
984  if (strcmp (azQargs[0], "uucp") == 0)
985    {
986      char *zfrom, *zto;
987      boolean fmany;
988      boolean finoptions;
989      char **azargs;
990      const char *zuser;
991
992      zfrom = NULL;
993      zto = NULL;
994      fmany = FALSE;
995      finoptions = TRUE;
996
997      /* Skip all the options, and get the from and to specs.  We
998	 don't permit multiple arguments.  We have to do mini-getopt
999	 processing here.  */
1000      for (i = 1; azQargs[i] != NULL; i++)
1001	{
1002	  if (azQargs[i][0] == '-' && finoptions)
1003	    {
1004	      if (azQargs[i][1] == '-')
1005		{
1006		  if (azQargs[i][2] == '\0')
1007		    finoptions = FALSE;
1008		  /* The --grade, --notify, and --status options take
1009                     an argument.  */
1010		  else if (strncmp (azQargs[i] + 2, "g", 1) == 0
1011		      || strncmp (azQargs[i] + 2, "not", 3) == 0
1012		      || strncmp (azQargs[i] + 2, "s", 1) == 0)
1013		    {
1014		      if (strchr (azQargs[i] + 2, '=') == NULL)
1015			++i;
1016		    }
1017		  /* The --config, --user, and --debug options are not
1018                     permitted.  */
1019		  else if (strncmp (azQargs[i] + 2, "con", 3) == 0
1020			   || strncmp (azQargs[i] + 2, "us", 2) == 0
1021			   || strncmp (azQargs[i] + 2, "de", 2) == 0)
1022		    {
1023		      azQargs[i][1] = 'r';
1024		      azQargs[i][2] = '\0';
1025		      if (strchr (azQargs[i] + 3, '=') == NULL)
1026			{
1027			  ++i;
1028			  azQargs[i] = zbufcpy ("-r");
1029			}
1030		    }
1031		}
1032	      else
1033		{
1034		  char *zopts;
1035
1036		  for (zopts = azQargs[i] + 1; *zopts != '\0'; zopts++)
1037		    {
1038		      /* The -g, -n, and -s options take an argument.  */
1039		      if (*zopts == 'g' || *zopts == 'n' || *zopts == 's' || *zopts == 'x')
1040			{
1041			  if (zopts[1] == '\0')
1042			    ++i;
1043			  break;
1044			}
1045		      /* The -I, -u and -x options are not permitted.  */
1046		      if (*zopts == 'I' || *zopts == 'u' || *zopts == 'x')
1047			{
1048			  *zopts = 'r';
1049			  if (zopts[1] != '\0')
1050			    zopts[1] = '\0';
1051			  else
1052			    {
1053			      ++i;
1054			      azQargs[i] = zbufcpy ("-r");
1055			    }
1056			  break;
1057			}
1058		    }
1059		}
1060	    }
1061	  else if (zfrom == NULL)
1062	    zfrom = azQargs[i];
1063	  else if (zto == NULL)
1064	    zto = azQargs[i];
1065	  else
1066	    {
1067	      fmany = TRUE;
1068	      break;
1069	    }
1070	}
1071
1072#define USE_KTRACE 0
1073      /* Add the -u argument.  This is required to let uucp do the
1074	 correct permissions checking on the file transfer.  */
1075      for (i = 0; azQargs[i] != NULL; i++)
1076	;
1077      azargs = (char **) xmalloc ((i + 2 + USE_KTRACE) * sizeof (char *));
1078      ulog(LOG_ERROR, "USE_KTRACE %d azargs %p azQargs %p", USE_KTRACE, azargs, azQargs);
1079      azargs[0] = azQargs[0];
1080      zuser = zQuser;
1081      if (zuser == NULL)
1082	zuser = "uucp";
1083      azargs[1 + USE_KTRACE] = zbufalc (strlen (zQsystem) + strlen (zuser)
1084			   + sizeof "-u!");
1085      sprintf (azargs[1 + USE_KTRACE], "-u%s!%s", zQsystem, zuser);
1086      memcpy (azargs + 2 + USE_KTRACE, azQargs + 1 + USE_KTRACE, i * sizeof (char *));
1087      xfree ((pointer) azQargs);
1088      azQargs = azargs;
1089
1090#if USE_KTRACE
1091      ulog(LOG_ERROR, "KTRACE");
1092	azQargs[0] = strbufdup("ktrace");
1093	azQargs[1] = strbufdup("-f");
1094	azQargs[2] = strbufdup("/tmp/uu.kt");
1095	azQargs[3] = zsysdep_find_command ("uucp", qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr);
1096
1097      ulog_azQargs("USE_KTRACE ");
1098#endif
1099
1100      /* Find the uucp binary.  */
1101      zabsolute = zsysdep_find_command (USE_KTRACE ? "ktrace" : "uucp",
1102        qsys->uuconf_pzcmds, qsys->uuconf_pzpath, &ferr);
1103      ulog(LOG_ERROR, "zabsolute %s", zabsolute);
1104      if (zabsolute == NULL && ! ferr)
1105	{
1106	  const char *azcmds[2];
1107
1108	  /* If "uucp" is not a permitted command, then the forwarding
1109	     entries must be set.  */
1110	  if (! fqforward (zfrom, qsys->uuconf_pzforward_from, "from", zmail)
1111	      || ! fqforward (zto, qsys->uuconf_pzforward_to, "to", zmail))
1112	    {
1113	      uqcleanup (zfile, iclean);
1114	      return;
1115	    }
1116
1117	  /* If "uucp" is not a permitted command, then only uucp
1118	     requests with a single source are permitted, since that
1119	     is all that will be generated by uucp or uux.  */
1120	  if (fmany || zfrom == NULL || zto == NULL)
1121	    {
1122	      ulog (LOG_ERROR, "Bad uucp request %s", zQcmd);
1123
1124	      if (zmail != NULL && ! fQno_ack)
1125		{
1126		  const char *az[20];
1127
1128		  i = 0;
1129		  az[i++] = "Your execution request failed because it was an";
1130		  az[i++] = " unsupported uucp request.\n";
1131		  az[i++] = "Execution requested was:\n\t";
1132		  az[i++] = zQcmd;
1133		  az[i++] = "\n";
1134
1135		  (void) fsysdep_mail (zmail, "Execution failed", i, az);
1136		}
1137
1138	      uqcleanup (zfile, iclean);
1139	      return;
1140	    }
1141
1142	  azcmds[0] = "uucp";
1143	  azcmds[1] = NULL;
1144	  zabsolute = zsysdep_find_command ("uucp", (char **) azcmds,
1145					    qsys->uuconf_pzpath, &ferr);
1146	}
1147      if (zabsolute == NULL)
1148	{
1149	  if (! ferr)
1150	    ulog (LOG_ERROR, "Can't find uucp executable");
1151
1152	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1153	  *pfprocessed = FALSE;
1154	  return;
1155	}
1156    }
1157  else
1158    {
1159      /* Get the pathname to execute.  */
1160      zabsolute = zsysdep_find_command (azQargs[0], qsys->uuconf_pzcmds,
1161					qsys->uuconf_pzpath,
1162					&ferr);
1163      if (zabsolute == NULL)
1164	{
1165	  if (ferr)
1166	    {
1167	      /* If we get an error, try again later.  */
1168	      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1169	      *pfprocessed = FALSE;
1170	      return;
1171	    }
1172
1173	  /* Not permitted.  Send mail to requestor.  */
1174	  ulog (LOG_ERROR, "Not permitted to execute %s",
1175		azQargs[0]);
1176
1177	  if (zmail != NULL && ! fQno_ack)
1178	    {
1179	      const char *az[20];
1180
1181	      i = 0;
1182	      az[i++] = "Your execution request failed because you are not";
1183	      az[i++] = " permitted to execute\n\t";
1184	      az[i++] = azQargs[0];
1185	      az[i++] = "\non this system.\n";
1186	      az[i++] = "Execution requested was:\n\t";
1187	      az[i++] = zQcmd;
1188	      az[i++] = "\n";
1189
1190	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1191	    }
1192
1193	  iclean = isave_files (qsys, zmail, zfile, iclean);
1194
1195	  uqcleanup (zfile, iclean);
1196	  return;
1197	}
1198    }
1199
1200  ulog(LOG_ERROR, "Eh?");
1201  ubuffree (azQargs[0]);
1202  azQargs[0] = zabsolute;
1203  ulog_azQargs("zabsolute");
1204  ulog(LOG_ERROR, "azQargs[1] %p", azQargs[i]);
1205
1206  for (i = 1; azQargs[i] != NULL; i++)
1207    {
1208      char *zlocal;
1209
1210      zlocal = zsysdep_xqt_local_file (qsys, azQargs[i]);
1211      if (zlocal != NULL)
1212	{
1213	  ulog(LOG_ERROR, "[%d] zlocal %s, a %s", i, zlocal, azQargs[i]);
1214	  ubuffree (azQargs[i]);
1215	  azQargs[i] = zlocal;
1216	}
1217    }
1218  ulog_azQargs("zsysdep_xqt_local_file");
1219
1220#if ! ALLOW_FILENAME_ARGUMENTS
1221
1222  /* Check all the arguments to make sure they don't try to specify
1223     files they are not permitted to access.  */
1224  for (i = 1; azQargs[i] != NULL; i++)
1225    {
1226      if (! fsysdep_xqt_check_file (qsys, azQargs[i]))
1227	{
1228	  if (zmail != NULL && ! fQno_ack)
1229	    {
1230	      const char *az[20];
1231	      const char *zfailed;
1232
1233	      zfailed = azQargs[i];
1234	      i = 0;
1235	      az[i++] = "Your execution request failed because you are not";
1236	      az[i++] = " permitted to refer to file\n\t";
1237	      az[i++] = zfailed;
1238	      az[i++] = "\non this system.\n";
1239	      az[i++] = "Execution requested was:\n\t";
1240	      az[i++] = zQcmd;
1241	      az[i++] = "\n";
1242
1243	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1244	    }
1245
1246	  iclean = isave_files (qsys, zmail, zfile, iclean);
1247
1248	  uqcleanup (zfile, iclean);
1249	  return;
1250	}
1251    }
1252
1253#endif /* ! ALLOW_FILENAME_ARGUMENTS */
1254
1255  ulog(LOG_ERROR, "Executing %s (%s)", zbase, zQcmd);
1256  ulog (LOG_NORMAL, "Executing %s (%s)", zbase, zQcmd);
1257  ulog(LOG_ERROR, "zQoutsys %p zQinput %p", zQoutsys, zQinput);
1258
1259  if (zQinput != NULL)
1260    {
1261      boolean fspool;
1262      char *zreal;
1263
1264      fspool = fspool_file (zQinput);
1265      if (! fspool)
1266	zreal = zsysdep_local_file (zQinput, qsys->uuconf_zpubdir, &fbadname);
1267      else
1268	{
1269	  zreal = zsysdep_spool_file_name (qsys, zQinput, (pointer) NULL);
1270	  fbadname = FALSE;
1271	}
1272      if (zreal == NULL && ! fbadname)
1273	{
1274	  /* If we get an error, try again later.  */
1275	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1276	  *pfprocessed = FALSE;
1277	  return;
1278	}
1279
1280      if (zreal != NULL)
1281	{
1282	  zQinput = zreal;
1283	  iclean |= FREE_QINPUT;
1284	  if (fspool)
1285	    iclean |= REMOVE_QINPUT;
1286	}
1287
1288      if (zreal == NULL
1289	  || (! fspool
1290	      && ! fin_directory_list (zQinput, qsys->uuconf_pzremote_send,
1291				       qsys->uuconf_zpubdir, TRUE, TRUE,
1292				       (const char *) NULL)))
1293	{
1294	  ulog (LOG_ERROR, "Not permitted to read %s", zQinput);
1295
1296	  if (zmail != NULL && ! fQno_ack)
1297	    {
1298	      const char *az[20];
1299
1300	      i = 0;
1301	      az[i++] = "Your execution request failed because you are";
1302	      az[i++] = " not permitted to read\n\t";
1303	      az[i++] = zQinput;
1304	      az[i++] = "\non this system.\n";
1305	      az[i++] = "Execution requested was:\n\t";
1306	      az[i++] = zQcmd;
1307	      az[i++] = "\n";
1308
1309	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1310	    }
1311
1312	  uqcleanup (zfile, iclean);
1313	  return;
1314	}
1315    }
1316
1317  zoutput = NULL;
1318  if (zQoutfile == NULL)
1319    qoutsys = NULL;
1320  else if (zQoutsys != NULL
1321	   && strcmp (zQoutsys, zlocalname) != 0)
1322    {
1323      char *zdata;
1324
1325      /* The output file is destined for some other system, so we must
1326	 use a temporary file to catch standard output.  */
1327      if (strcmp (zQoutsys, qsys->uuconf_zname) == 0)
1328	qoutsys = qsys;
1329      else
1330	{
1331	  iuuconf = uuconf_system_info (puuconf, zQoutsys, &soutsys);
1332	  if (iuuconf != UUCONF_SUCCESS)
1333	    {
1334	      if (iuuconf != UUCONF_NOT_FOUND)
1335		{
1336		  ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
1337		  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1338		  *pfprocessed = FALSE;
1339		  return;
1340		}
1341
1342	      if (! funknown_system (puuconf, zQoutsys, &soutsys))
1343		{
1344		  ulog (LOG_ERROR,
1345			"Can't send standard output to unknown system %s",
1346			zQoutsys);
1347		  /* We don't send mail to unknown systems, either.
1348		     Maybe we should.  */
1349		  uqcleanup (zfile, iclean);
1350		  return;
1351		}
1352	    }
1353
1354	  qoutsys = &soutsys;
1355	}
1356
1357      zdata = zsysdep_data_file_name (qoutsys, zlocalname,
1358				      BDEFAULT_UUX_GRADE, FALSE, abtemp,
1359				      abdata, (char *) NULL);
1360      if (zdata == NULL)
1361	{
1362	  /* If we get an error, try again later.  */
1363	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1364	  *pfprocessed = FALSE;
1365	  return;
1366	}
1367      zoutput = zdata;
1368      zQoutput = zoutput;
1369      iclean |= FREE_OUTPUT;
1370    }
1371  else
1372    {
1373      boolean fok;
1374
1375      qoutsys = NULL;
1376
1377      /* If we permitted the standard output to be redirected into
1378	 the spool directory, people could set up phony commands.  */
1379      if (fspool_file (zQoutfile))
1380	fok = FALSE;
1381      else
1382	{
1383	  zoutput = zsysdep_local_file (zQoutfile, qsys->uuconf_zpubdir,
1384					&fbadname);
1385	  if (zoutput == NULL)
1386	    {
1387	      if (! fbadname)
1388		{
1389		  /* If we get an error, try again later.  */
1390		  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1391		  *pfprocessed = FALSE;
1392		  return;
1393		}
1394	      fok = FALSE;
1395	    }
1396	  else
1397	    {
1398	      ubuffree (zQoutfile);
1399	      zQoutfile = zoutput;
1400
1401	      /* Make sure it's OK to receive this file.  */
1402	      fok = fin_directory_list (zQoutfile,
1403					qsys->uuconf_pzremote_receive,
1404					qsys->uuconf_zpubdir, TRUE, FALSE,
1405					(const char *) NULL);
1406	    }
1407	}
1408
1409      if (! fok)
1410	{
1411	  ulog (LOG_ERROR, "Not permitted to write to %s", zQoutfile);
1412
1413	  if (zmail != NULL && ! fQno_ack)
1414	    {
1415	      const char *az[20];
1416
1417	      i = 0;
1418	      az[i++] = "Your execution request failed because you are";
1419	      az[i++] = " not permitted to write to\n\t";
1420	      az[i++] = zQoutfile;
1421	      az[i++] = "\non this system.\n";
1422	      az[i++] = "Execution requested was:\n\t";
1423	      az[i++] = zQcmd;
1424	      az[i++] = "\n";
1425
1426	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1427	    }
1428
1429	  uqcleanup (zfile, iclean);
1430	  return;
1431	}
1432    }
1433
1434  ulog(LOG_ERROR, "fsysdep_copy_uuxqt_files...");
1435  /* Move the required files to the execution directory if necessary.  */
1436  zinput = zQinput;
1437  if (! fsysdep_copy_uuxqt_files (cQfiles, (const char **) azQfiles,
1438				  (const char **) azQfiles_to,
1439				  iQlock_seq, &zinput))
1440    {
1441      /* If we get an error, try again later.  */
1442      uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1443      *pfprocessed = FALSE;
1444      return;
1445    }
1446  if (zQinput != NULL && strcmp (zQinput, zinput) != 0)
1447    {
1448      if ((iclean & FREE_QINPUT) != 0)
1449	ubuffree (zQinput);
1450      zQinput = zinput;
1451      iclean |= FREE_QINPUT;
1452    }
1453
1454#if ALLOW_SH_EXECUTION
1455  fshell = fQuse_sh;
1456#else
1457  fshell = FALSE;
1458#endif
1459
1460  /* Get a shell command which uses the full path of the command to
1461     execute.  */
1462  clen = 0;
1463  for (i = 0; azQargs[i] != NULL; i++) {
1464    clen += strlen (azQargs[i]) + 1;
1465  }
1466  zfullcmd = zbufalc (clen);
1467  strcpy (zfullcmd, azQargs[0]);
1468  for (i = 1; azQargs[i] != NULL; i++)
1469    {
1470      strcat (zfullcmd, " ");
1471      strcat (zfullcmd, azQargs[i]);
1472    }
1473
1474  ulog(LOG_ERROR, "fsysdep_execute fshell %d zfullcmd: %s", fshell, zfullcmd);
1475  if (! fsysdep_execute (qsys,
1476			 zQuser == NULL ? (const char *) "uucp" : zQuser,
1477			 (const char **) azQargs, zfullcmd, zQinput,
1478			 zoutput, zQwd, fshell, iQlock_seq, &zerror, &ftemp))
1479    {
1480      ubuffree (zfullcmd);
1481
1482      if (ftemp)
1483	{
1484	  ulog (LOG_NORMAL, "Will retry later (%s)", zbase);
1485	  if (zoutput != NULL)
1486	    (void) remove (zoutput);
1487	  if (zerror != NULL)
1488	    {
1489	      (void) remove (zerror);
1490	      ubuffree (zerror);
1491	    }
1492	  uqcleanup (zfile, iclean &~ (REMOVE_FILE | REMOVE_NEEDED));
1493	  *pfprocessed = FALSE;
1494	  return;
1495	}
1496
1497      ulog (LOG_NORMAL, "Execution failed (%s)", zbase);
1498
1499      if (zmail != NULL && ! fQno_ack)
1500	{
1501	  const char **pz;
1502	  int cgot;
1503	  FILE *eerr;
1504	  int istart;
1505
1506	  cgot = 20;
1507	  pz = (const char **) xmalloc (cgot * sizeof (const char *));
1508	  i = 0;
1509	  pz[i++] = "Execution request failed:\n\t";
1510	  pz[i++] = zQcmd;
1511	  pz[i++] = "\n";
1512
1513	  if (zerror == NULL)
1514	    eerr = NULL;
1515	  else
1516	    eerr = fopen (zerror, "r");
1517	  if (eerr == NULL)
1518	    {
1519	      pz[i++] = "There was no output on standard error\n";
1520	      istart = i;
1521	    }
1522	  else
1523	    {
1524	      char *zline;
1525	      size_t cline;
1526
1527	      pz[i++] = "Standard error output was:\n";
1528	      istart = i;
1529
1530	      zline = NULL;
1531	      cline = 0;
1532	      while (getline (&zline, &cline, eerr) > 0)
1533		{
1534		  if (i >= cgot)
1535		    {
1536		      cgot += 20;
1537		      pz = ((const char **)
1538			    xrealloc ((pointer) pz,
1539				      cgot * sizeof (const char *)));
1540		    }
1541		  pz[i++] = zbufcpy (zline);
1542		}
1543
1544	      (void) fclose (eerr);
1545	      xfree ((pointer) zline);
1546	    }
1547
1548	  (void) fsysdep_mail (zmail, "Execution failed", i, pz);
1549
1550	  for (; istart < i; istart++)
1551	    ubuffree ((char *) pz[istart]);
1552	  xfree ((pointer) pz);
1553	}
1554
1555      if (qoutsys != NULL)
1556	(void) remove (zoutput);
1557
1558      iclean = isave_files (qsys, zmail, zfile, iclean);
1559    }
1560  else
1561    {
1562      ubuffree (zfullcmd);
1563
1564      if (zmail != NULL && fQsuccess_ack)
1565	{
1566	  const char *az[20];
1567
1568	  i = 0;
1569	  az[i++] = "\nExecution request succeeded:\n\t";
1570	  az[i++] = zQcmd;
1571	  az[i++] = "\n";
1572
1573	  (void) fsysdep_mail (zmail, "Execution succeded", i, az);
1574	}
1575
1576      /* Now we may have to uucp the output to some other machine.  */
1577
1578      if (qoutsys != NULL)
1579	{
1580	  struct scmd s;
1581
1582	  /* Fill in the command structure.  */
1583
1584	  s.bcmd = 'S';
1585	  s.bgrade = BDEFAULT_UUX_GRADE;
1586	  s.pseq = NULL;
1587	  s.zfrom = abtemp;
1588	  s.zto = zQoutfile;
1589	  if (zQuser != NULL)
1590	    s.zuser = zQuser;
1591	  else
1592	    s.zuser = "uucp";
1593	  if (zmail != NULL && fQsuccess_ack)
1594	    s.zoptions = "Cn";
1595	  else
1596	    s.zoptions = "C";
1597	  s.ztemp = abtemp;
1598	  s.imode = 0666;
1599	  if (zmail != NULL && fQsuccess_ack)
1600	    s.znotify = zmail;
1601	  else
1602	    s.znotify = "";
1603	  s.cbytes = -1;
1604	  s.zcmd = NULL;
1605	  s.ipos = 0;
1606
1607	  ubuffree (zsysdep_spool_commands (qoutsys, BDEFAULT_UUX_GRADE,
1608					    1, &s, (boolean *) NULL));
1609	}
1610    }
1611
1612    /* XXX the umask doesn't seem to work, maybe a launchd problem,
1613      revisit if UUCP is ever really added to the system */
1614    chmod(zoutput, 0666);
1615
1616  if (zerror != NULL)
1617    {
1618      (void) remove (zerror);
1619      ubuffree (zerror);
1620    }
1621
1622  uqcleanup (zfile, iclean);
1623}
1624
1625/* If we have enough disk space, save the data files so that the UUCP
1626   administrator can examine them.  Send a mail message listing the
1627   saved files.  */
1628
1629static int
1630isave_files (qsys, zmail, zfile, iclean)
1631     const struct uuconf_system *qsys;
1632     const char *zmail;
1633     const char *zfile;
1634     int iclean;
1635{
1636  long cspace;
1637  char *zsavecmd;
1638  char **pzsave;
1639  int c;
1640  int ifile;
1641  char *zsaveinput;
1642  const char **pz;
1643  int i;
1644
1645  /* Save the files if there is 1.5 times the amount of required free
1646     space.  */
1647  cspace = csysdep_bytes_free (zfile);
1648  if (cspace == -1)
1649    cspace = FREE_SPACE_DELTA;
1650  cspace -= qsys->uuconf_cfree_space + qsys->uuconf_cfree_space / 2;
1651  if (cspace < 0)
1652    return iclean;
1653
1654  zsavecmd = zsysdep_save_failed_file (zfile);
1655  if (zsavecmd == NULL)
1656    return iclean;
1657
1658  c = 1;
1659
1660  pzsave = (char **) xmalloc (cQfiles * sizeof (char *));
1661  for (ifile = 0; ifile < cQfiles; ifile++)
1662    {
1663      if (azQfiles[ifile] != NULL)
1664	{
1665	  ++c;
1666	  pzsave[ifile] = zsysdep_save_failed_file (azQfiles[ifile]);
1667	  if (pzsave[ifile] == NULL)
1668	    {
1669	      ubuffree (zsavecmd);
1670	      for (i = 0; i < ifile; i++)
1671		if (azQfiles[i] != NULL)
1672		  ubuffree (pzsave[i]);
1673	      xfree ((pointer) pzsave);
1674	      return iclean;
1675	    }
1676	}
1677    }
1678
1679  zsaveinput = NULL;
1680  if ((iclean & REMOVE_QINPUT) != 0
1681      && fsysdep_file_exists (zQinput))
1682    {
1683      zsaveinput = zsysdep_save_failed_file (zQinput);
1684      if (zsaveinput == NULL)
1685	{
1686	  ubuffree (zsavecmd);
1687	  for (i = 0; i < cQfiles; i++)
1688	    if (azQfiles[i] != NULL)
1689	      ubuffree  (pzsave[i]);
1690	  xfree ((pointer) pzsave);
1691	  return iclean;
1692	}
1693    }
1694
1695  pz = (const char **) xmalloc ((20 + 2 * cQfiles) * sizeof (char *));
1696  i = 0;
1697
1698  pz[i++] = "A UUCP execution request failed:\n\t";
1699  pz[i++] = zQcmd;
1700  if (zmail != NULL)
1701    {
1702      pz[i++] = "\nThe request was made by\n\t";
1703      pz[i++] = zmail;
1704    }
1705  else
1706    {
1707      pz[i++] = "\nThe request came from system\n\t";
1708      pz[i++] = qsys->uuconf_zname;
1709    }
1710  if (c == 1 && zsaveinput == NULL)
1711    pz[i++] = "\nThe following file has been saved:\n\t";
1712  else
1713    pz[i++] = "\nThe following files have been saved:\n\t";
1714  pz[i++] = zsavecmd;
1715  for (ifile = 0; ifile < cQfiles; ifile++)
1716    {
1717      if (azQfiles[ifile] != NULL)
1718	{
1719	  pz[i++] = "\n\t";
1720	  pz[i++] = pzsave[ifile];
1721	}
1722    }
1723  if (zsaveinput != NULL)
1724    {
1725      pz[i++] = "\n\t";
1726      pz[i++] = zsaveinput;
1727    }
1728  pz[i++] = "\n";
1729
1730  (void) fsysdep_mail (OWNER,
1731		       "UUCP execution files saved after failure",
1732		       i, pz);
1733
1734  xfree ((pointer) pz);
1735
1736  ubuffree (zsavecmd);
1737  for (ifile = 0; ifile < cQfiles; ifile++)
1738    if (azQfiles[ifile] != NULL)
1739      ubuffree (pzsave[ifile]);
1740  xfree ((pointer) pzsave);
1741  ubuffree (zsaveinput);
1742
1743  return iclean &~ (REMOVE_FILE | REMOVE_NEEDED);
1744}
1745
1746/* Clean up the results of uqdo_xqt_file.  */
1747
1748static void
1749uqcleanup (zfile, iflags)
1750     const char *zfile;
1751     int iflags;
1752{
1753  int i;
1754
1755  DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
1756		  "uqcleanup: %s, %d", zfile, iflags);
1757
1758  if ((iflags & REMOVE_FILE) != 0)
1759    (void) remove (zfile);
1760
1761  if ((iflags & REMOVE_NEEDED) != 0)
1762    {
1763      for (i = 0; i < cQfiles; i++)
1764	{
1765	  if (azQfiles[i] != NULL)
1766	    (void) remove (azQfiles[i]);
1767	}
1768      if ((iflags & REMOVE_QINPUT) != 0)
1769	(void) remove (zQinput);
1770    }
1771
1772  if (zQunlock_file != NULL)
1773    {
1774      (void) fsysdep_unlock_uuxqt_file (zQunlock_file);
1775      zQunlock_file = NULL;
1776    }
1777
1778  if ((iflags & FREE_QINPUT) != 0)
1779    ubuffree (zQinput);
1780
1781  if ((iflags & FREE_OUTPUT) != 0)
1782    ubuffree (zQoutput);
1783  if ((iflags & FREE_MAIL) != 0)
1784    ubuffree (zQmail);
1785
1786  if (fQunlock_directory)
1787    {
1788      (void) fsysdep_unlock_uuxqt_dir (iQlock_seq);
1789      fQunlock_directory = FALSE;
1790    }
1791
1792  for (i = 0; i < cQfiles; i++)
1793    {
1794      ubuffree (azQfiles[i]);
1795      ubuffree (azQfiles_to[i]);
1796    }
1797
1798  ubuffree (zQoutfile);
1799  ubuffree (zQoutsys);
1800  ubuffree (zQrequestor);
1801
1802  if (azQargs != NULL)
1803    {
1804      for (i = 0; azQargs[i] != NULL; i++)
1805	ubuffree (azQargs[i]);
1806      xfree ((pointer) azQargs);
1807      azQargs = NULL;
1808    }
1809
1810  xfree ((pointer) zQcmd);
1811  zQcmd = NULL;
1812
1813  xfree ((pointer) azQfiles);
1814  azQfiles = NULL;
1815
1816  xfree ((pointer) azQfiles_to);
1817  azQfiles_to = NULL;
1818}
1819
1820/* Check whether forwarding is permitted.  */
1821
1822static boolean
1823fqforward (zfile, pzallowed, zlog, zmail)
1824     const char *zfile;
1825     char **pzallowed;
1826     const char *zlog;
1827     const char *zmail;
1828{
1829  const char *zexclam;
1830
1831  if (zfile == NULL)
1832    return TRUE;
1833
1834  zexclam = strchr (zfile, '!');
1835  if (zexclam != NULL)
1836    {
1837      size_t clen;
1838      char *zsys;
1839      boolean fret;
1840
1841      clen = zexclam - zfile;
1842      zsys = zbufalc (clen + 1);
1843      memcpy (zsys, zfile, clen);
1844      zsys[clen] = '\0';
1845
1846      fret = FALSE;
1847      if (pzallowed != NULL)
1848	{
1849	  char **pz;
1850
1851	  for (pz = pzallowed; *pz != NULL; pz++)
1852	    {
1853	      if (strcmp (*pz, "ANY") == 0
1854		  || strcmp (*pz, zsys) == 0)
1855		{
1856		  fret = TRUE;
1857		  break;
1858		}
1859	    }
1860	}
1861
1862      if (! fret)
1863	{
1864	  ulog (LOG_ERROR, "Not permitted to forward %s %s (%s)",
1865		zlog, zsys, zQcmd);
1866
1867	  if (zmail != NULL && ! fQno_ack)
1868	    {
1869	      int i;
1870	      const char *az[20];
1871
1872	      i = 0;
1873	      az[i++] = "Your execution request failed because you are";
1874	      az[i++] = " not permitted to forward files\n";
1875	      az[i++] = zlog;
1876	      az[i++] = " the system\n\t";
1877	      az[i++] = zsys;
1878	      az[i++] = "\n";
1879	      az[i++] = "Execution requested was:\n\t";
1880	      az[i++] = zQcmd;
1881	      az[i++] = "\n";
1882
1883	      (void) fsysdep_mail (zmail, "Execution failed", i, az);
1884	    }
1885	}
1886
1887      ubuffree (zsys);
1888
1889      return fret;
1890    }
1891
1892  return TRUE;
1893}
1894