/* sysinf.c Functions to read system information for the UUCP package. Copyright (C) 1991, 1992 Ian Lance Taylor This file is part of the Taylor UUCP package. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The author of the program may be contacted at ian@airs.com or c/o AIRS, P.O. Box 520, Waltham, MA 02254. $Log: sysinf.c,v $ Revision 1.1.1.1 1993/03/21 09:45:37 cgd initial import of 386bsd-0.1 sources Revision 1.24 1992/04/02 22:51:09 ian Add gcc 2.0 format checking to ulog, and fixed discovered problems Revision 1.23 1992/03/30 04:49:10 ian Niels Baggesen: added debugging types abnormal and uucp-proto Revision 1.22 1992/03/28 21:47:55 ian David J. MacKenzie: allow backslash to quote newline in config files Revision 1.21 1992/03/28 20:31:55 ian Franc,ois Pinard: allow a name to be given to an alternate Revision 1.20 1992/03/24 17:18:33 ian Fixed handling of alternates in file-wide defaults Revision 1.19 1992/03/16 04:30:57 ian Permit a retry time for the time and timegrade commands Revision 1.18 1992/03/13 16:17:42 ian Chip Salzenberg: set default login chat script timeout to 10 seconds Revision 1.17 1992/03/12 19:54:43 ian Debugging based on types rather than number Revision 1.16 1992/03/09 20:14:37 ian Ted Lindgreen: added max-remote-debug command Revision 1.15 1992/03/07 02:56:30 ian Rewrote time routines Revision 1.14 1992/03/03 06:06:48 ian T. William Wells: don't complain about missing configuration files Revision 1.13 1992/02/23 19:50:50 ian Handle READ and WRITE in Permissions correctly Revision 1.12 1992/02/08 03:54:18 ian Include only in , added 1992 copyright Revision 1.11 1992/01/13 05:17:30 ian Mike Park: wrong number of arguments to ulog call Revision 1.10 1992/01/07 15:23:50 ian Niels Baggesen: allocate number of protocol parameters correctly Revision 1.9 1991/12/28 03:49:23 ian Added HAVE_MEMFNS and HAVE_BFNS; changed uses of memset to bzero Revision 1.8 1991/12/23 05:15:54 ian David Nugent: set debugging level for a specific system Revision 1.7 1991/12/17 17:08:02 ian Marc Unangst: allow true and false for boolean strings as documented Revision 1.6 1991/12/15 04:17:11 ian Added chat-seven-bit command to control parity bit stripping Revision 1.5 1991/12/15 03:42:33 ian Added tprocess_chat_cmd for all chat commands, and added CMDTABTYPE_PREFIX Revision 1.4 1991/11/13 20:38:00 ian Added TCP port type for connections over TCP Revision 1.3 1991/11/12 19:47:04 ian Add called-chat set of commands to run a chat script on an incoming call Revision 1.2 1991/11/11 23:47:24 ian Added chat-program to run a program to do a chat script Revision 1.1 1991/09/10 19:40:31 ian Initial revision */ #include "uucp.h" #if USE_RCS_ID char sysinf_rcsid[] = "$Id: sysinf.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; #endif #include #include #include "port.h" #include "system.h" #include "uutime.h" /* Whether we accept calls from unknown systems. */ boolean fUnknown_ok = FALSE; /* Information we hold for an unknown system. */ struct ssysteminfo sUnknown; /* Information we hold for the local system. */ struct ssysteminfo sLocalsys; #if HAVE_TAYLOR_CONFIG /* I don't find the system file format used by either V2 or BNU UUCP very intuitive, so I've developed my own format. This replaces the files L.sys and USERFILE for V2, and Systems and Permissions for BNU. The complete format is described in a separate document. */ /* Some local functions. */ static void uiset_clear P((boolean falternate)); /* Set when we read the first line of a new file, to indicate that a new set of defaults should be gathered. */ static boolean fIfirst; /* This structure holds system information as it is gathered. */ static struct ssysteminfo sIhold; /* Default information. */ static struct ssysteminfo sIdefault; /* The name of the next system. */ static char *zInext_system; /* The list of alternates to draw from. */ static struct ssysteminfo *qIalternates; /* Whether to use default alternates. */ static boolean fIdefault_alternates; /* There are several commands for which we want to take the default if provided, but we don't want to add to the default. For example, each system can have multiple time specifications (using different grades), which is implemented by adding each new time specification to the previous ones. We want to allow default time specifications. However, if a system has time specifications, we don't want to add its time specifications to the default ones. We handle this with a set of boolean variables which we set to tell the adding functions to ignore any existing entry; when an entry has been added, the boolean variable is cleared. */ static boolean fIclear_alias; static boolean fIclear_alternate; static boolean fIclear_time; static boolean fIclear_calltimegrade; static boolean fIclear_call_local_size; static boolean fIclear_call_remote_size; static boolean fIclear_called_local_size; static boolean fIclear_called_remote_size; static boolean fIclear_port; static boolean fIclear_chat_fail; static boolean fIclear_proto_param; static boolean fIclear_called_chat_fail; /* Local functions needed to parse the system information file. */ #define CMDTABFN(z) \ static enum tcmdtabret z P((int, char **, pointer, const char *)) CMDTABFN (ticlear); CMDTABFN (tisystem); CMDTABFN (tialias); CMDTABFN (tialternate); CMDTABFN (titime); CMDTABFN (titimegrade); CMDTABFN (ticall_local_size); CMDTABFN (ticall_remote_size); CMDTABFN (ticalled_local_size); CMDTABFN (ticalled_remote_size); CMDTABFN (titimetable); CMDTABFN (tiport); CMDTABFN (tichat); CMDTABFN (ticalled_login); CMDTABFN (tiproto_param); CMDTABFN (tirequest); CMDTABFN (titransfer); #undef CMDTABFN /* The commands accepted from the system information file. */ static const struct scmdtab asIcmds[] = { { "#", CMDTABTYPE_FN | 1, NULL, ticlear }, { "system", CMDTABTYPE_FN | 2, NULL, tisystem }, { "alias", CMDTABTYPE_FN | 2, NULL, tialias }, { "alternate", CMDTABTYPE_FN | 0, NULL, tialternate }, { "default-alternates", CMDTABTYPE_BOOLEAN, (pointer) &fIdefault_alternates, NULL }, { "time", CMDTABTYPE_FN | 0, NULL, titime }, { "timegrade", CMDTABTYPE_FN | 0, (pointer) &sIhold.ztime, titimegrade }, { "call-timegrade", CMDTABTYPE_FN | 3, (pointer) &sIhold.zcalltimegrade, titimegrade }, { "call-local-size", CMDTABTYPE_FN | 3, NULL, ticall_local_size }, { "call-remote-size", CMDTABTYPE_FN | 3, NULL, ticall_remote_size }, { "called-local-size", CMDTABTYPE_FN | 3, NULL, ticalled_local_size }, { "called-remote-size", CMDTABTYPE_FN | 3, NULL, ticalled_remote_size }, { "timetable", CMDTABTYPE_FN | 3, NULL, titimetable }, { "baud", CMDTABTYPE_LONG, (pointer) &sIhold.ibaud, NULL }, { "speed", CMDTABTYPE_LONG, (pointer) &sIhold.ibaud, NULL }, { "port", CMDTABTYPE_FN | 0, NULL, tiport }, { "phone", CMDTABTYPE_STRING, (pointer) &sIhold.zphone, NULL }, { "address", CMDTABTYPE_STRING, (pointer) &sIhold.zphone, NULL }, { "chat", CMDTABTYPE_PREFIX | 0, (pointer) &sIhold.schat, tichat }, { "call-login", CMDTABTYPE_STRING, (pointer) &sIhold.zcall_login, NULL }, { "call-password", CMDTABTYPE_STRING, (pointer) &sIhold.zcall_password, NULL }, { "called-login", CMDTABTYPE_FN | 0, NULL, ticalled_login }, { "callback", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fcallback, NULL }, { "sequence", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fsequence, NULL }, { "protocol", CMDTABTYPE_STRING, (pointer) &sIhold.zprotocols, NULL }, { "protocol-parameter", CMDTABTYPE_FN | 0, NULL, tiproto_param }, { "called-chat", CMDTABTYPE_PREFIX | 0, (pointer) &sIhold.scalled_chat, tichat }, #if DEBUG > 1 { "debug", CMDTABTYPE_FN | 0, (pointer) &sIhold.idebug, tidebug_parse }, { "max-remote-debug", CMDTABTYPE_FN | 0, (pointer) &sIhold.imax_remote_debug, tidebug_parse }, #endif { "call-request", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fcall_request, NULL }, { "called-request", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fcalled_request, NULL }, { "request", CMDTABTYPE_FN | 2, NULL, tirequest }, { "call-transfer", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fcall_transfer, NULL }, { "called-transfer", CMDTABTYPE_BOOLEAN, (pointer) &sIhold.fcalled_transfer, NULL }, { "transfer", CMDTABTYPE_FN | 2, NULL, titransfer }, { "local-send", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zlocal_send, NULL }, { "remote-send", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zremote_send, NULL }, { "local-receive", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zlocal_receive, NULL }, { "remote-receive", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zremote_receive, NULL }, { "command-path", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zpath, NULL }, { "commands", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zcmds, NULL }, { "free-space", CMDTABTYPE_LONG, (pointer) &sIhold.cfree_space, NULL }, { "forwardto", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zforwardto, NULL }, { "forward-to", CMDTABTYPE_FULLSTRING, (pointer) &sIhold.zforwardto, NULL }, { "pubdir", CMDTABTYPE_STRING, (pointer) &sIhold.zpubdir, NULL }, { "myname", CMDTABTYPE_STRING, (pointer) &sIhold.zlocalname, NULL }, { NULL, 0, NULL, NULL } }; /* This is called for the first line of each file. It clears the defaults, so that each file has a different set. */ /*ARGSUSED*/ static enum tcmdtabret ticlear (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { fIfirst = TRUE; return CMDTABRET_FREE_AND_EXIT; } /* Process the system command. We store away the system name and exit out of processing commands to let the main loop handle it. */ /*ARGSUSED*/ static enum tcmdtabret tisystem (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { const char *z; /* System names may only contain alphanumeric characters, underscores, dashes and dots, and they may not begin with a dot, at least for now. */ for (z = argv[1]; *z != '\0'; z++) { if (! isalnum (BUCHAR (*z)) && *z != '_' && *z != '-' && (*z != '.' || z == argv[1])) { ulog (LOG_ERROR, "%s: %s %s: Illegal character in system name", zerr, argv[0], argv[1]); return CMDTABRET_FREE; } } if (strlen (argv[1]) > cSysdep_max_name_len) { ulog (LOG_ERROR, "System name \"%s\" too long (max %d)", argv[1], cSysdep_max_name_len); return CMDTABRET_FREE; } zInext_system = argv[1]; DEBUG_MESSAGE1 (DEBUG_CONFIG, "tisystem: Reading system %s", zInext_system); return CMDTABRET_EXIT; } /* Process the alias command. */ /*ARGSUSED*/ static enum tcmdtabret tialias (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (fIclear_alias) { sIhold.zalias = NULL; fIclear_alias = FALSE; } uadd_string (&sIhold.zalias, argv[1], ' '); return CMDTABRET_FREE; } /* Process the alternate command. The current information is in sIhold. We link this onto a chain of alternates starting at sIalternate. We then set up sIhold with the defaults for the next alternate. qIalternates holds the list of alternates for the file-wide defaults, and we use to set up sIhold. We also call uiset_clear to set all the fIclear_* variables so that commands like ``time'' know that they should ignore any existing entry. */ static struct ssysteminfo sIalternate; /*ARGSUSED*/ static enum tcmdtabret tialternate (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (fIclear_alternate) { sIalternate = sIhold; fIclear_alternate = FALSE; } else { struct ssysteminfo *qnew; struct ssysteminfo **pq; qnew = (struct ssysteminfo *) xmalloc (sizeof (struct ssysteminfo)); *qnew = sIhold; for (pq = &sIalternate.qalternate; *pq != NULL; pq = &(*pq)->qalternate) ; *pq = qnew; sIhold = sIalternate; sIhold.qalternate = NULL; } /* Clear the name of the next alternate. */ sIhold.zalternate = NULL; /* Now, if there is a default alternate to base this on, we must override everything not changed before the first ``alternate'' command to the default alternate. */ if (fIdefault_alternates && qIalternates != NULL) { if (qIalternates->zalternate != NULL) sIhold.zalternate = qIalternates->zalternate; #define TEST(x) \ if (sIhold.x == sIdefault.x) \ sIhold.x = qIalternates->x; TEST (ztime); TEST (zcalltimegrade); TEST (zcall_local_size); TEST (zcall_remote_size); TEST (zcalled_local_size); TEST (zcalled_remote_size); TEST (ibaud); TEST (zport); TEST (qport); TEST (zphone); TEST (schat.zchat); TEST (schat.zprogram); TEST (schat.ctimeout); TEST (schat.zfail); TEST (zcall_login); TEST (zcalled_login); TEST (fcallback); TEST (zprotocols); TEST (cproto_params); TEST (qproto_params); TEST (scalled_chat.zchat); TEST (scalled_chat.zprogram); TEST (scalled_chat.ctimeout); TEST (scalled_chat.zfail); #if DEBUG > 1 TEST (idebug); TEST (imax_remote_debug); #endif TEST (fcall_request); TEST (fcalled_request); TEST (fcall_transfer); TEST (fcalled_transfer); TEST (zlocal_send); TEST (zcalled_local_send); TEST (zremote_send); TEST (zcalled_remote_send); TEST (zlocal_receive); TEST (zcalled_local_receive); TEST (zremote_receive); TEST (zcalled_remote_receive); TEST (zpath); TEST (zcmds); TEST (cfree_space); TEST (zforwardto); TEST (zpubdir); TEST (zlocalname); #undef TEST qIalternates = qIalternates->qalternate; } /* If there is a name for this alternate, put it in. */ if (argc > 1) sIhold.zalternate = xstrdup (argv[1]); uiset_clear (TRUE); return CMDTABRET_FREE; } /* Process the time command. */ /*ARGSUSED*/ static enum tcmdtabret titime (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { char *pznew[4]; char ab[2]; pznew[0] = argv[0]; ab[0] = 'z'; ab[1] = '\0'; pznew[1] = ab; pznew[2] = argv[1]; if (argc > 2) pznew[3] = argv[2]; return titimegrade (argc + 1, pznew, (pointer) &sIhold.ztime, zerr); } /* Process the timegrade and the call-timegrade commands. */ static enum tcmdtabret titimegrade (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { char **pztime = (char **) pvar; int clen; char *z; if (argc < 3 || argc > 4) { ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", zerr, argv[0]); return CMDTABRET_FREE; } if (argv[1][1] != '\0' || ! FGRADE_LEGAL (argv[1][0])) { ulog (LOG_ERROR, "%s: %s: Illegal grade '%s'", zerr, argv[0], argv[1]); return CMDTABRET_FREE; } /* We should probably check whether the time string is legal. A timegrade string is a single character grade, then a time string, then an optional semicolon and a retry time. */ clen = strlen (argv[2]) + 2; if (argc > 3) clen += strlen (argv[3]) + 1; z = (char *) alloca (clen); *z = argv[1][0]; strcpy (z + 1, argv[2]); if (argc > 3) { strcat (z, ";"); strcat (z, argv[3]); } if (pztime == &sIhold.ztime) { if (fIclear_time) { *pztime = NULL; fIclear_time = FALSE; } } else { if (fIclear_calltimegrade) { *pztime = NULL; fIclear_calltimegrade = FALSE; } } uadd_string (pztime, z, ' '); return CMDTABRET_FREE; } /* Add a size command. */ static enum tcmdtabret tiadd_size P((int argc, char **argv, const char *zerr, boolean *pf, char **pz)); static enum tcmdtabret tiadd_size (argc, argv, zerr, pf, pz) int argc; char **argv; const char *zerr; boolean *pf; char **pz; { long cbytes; char *zend; char *zarg; cbytes = strtol (argv[1], &zend, 10); if (*zend != '\0') { ulog (LOG_ERROR, "%s: %s: Bad number", zerr, argv[0]); return CMDTABRET_FREE; } /* We should check the legality of the time string here. */ if (*pf) { *pz = NULL; *pf = FALSE; } zarg = (char *) alloca (strlen (argv[2]) + 20); sprintf (zarg, "%ld %s", cbytes, argv[2]); uadd_string (pz, zarg, ' '); return CMDTABRET_FREE; } /* Process the call-local-size command. */ /*ARGSUSED*/ static enum tcmdtabret ticall_local_size (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { return tiadd_size (argc, argv, zerr, &fIclear_call_local_size, &sIhold.zcall_local_size); } /* Process the call-remote-size command. */ /*ARGSUSED*/ static enum tcmdtabret ticall_remote_size (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { return tiadd_size (argc, argv, zerr, &fIclear_call_remote_size, &sIhold.zcall_remote_size); } /* Process the called-local-size command. */ /*ARGSUSED*/ static enum tcmdtabret ticalled_local_size (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { return tiadd_size (argc, argv, zerr, &fIclear_called_local_size, &sIhold.zcalled_local_size); } /* Process the called-remote-size command. */ /*ARGSUSED*/ static enum tcmdtabret ticalled_remote_size (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { return tiadd_size (argc, argv, zerr, &fIclear_called_remote_size, &sIhold.zcalled_remote_size); } /* Process the timetable command. */ /*ARGSUSED*/ static enum tcmdtabret titimetable (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { uaddtimetable (argv[1], argv[2]); return CMDTABRET_CONTINUE; } /* Process the port command. */ /*ARGSUSED*/ static enum tcmdtabret tiport (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (argc < 2) { ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", zerr, argv[0]); return CMDTABRET_FREE; } if (fIclear_port) { sIhold.zport = NULL; sIhold.qport = NULL; fIclear_port = FALSE; } if (argc > 2) { enum tcmdtabret tret; if (sIhold.zport != NULL) { ulog (LOG_ERROR, "%s: %s: Ignoring port specification following port name", zerr, argv[0]); return CMDTABRET_FREE; } tret = tprocess_port_cmd (argc - 1, argv + 1, (pointer) &sIhold.qport, zerr); if (sIhold.qport != NULL && sIhold.qport->zname == NULL) { char *zname; if (sIhold.zname == NULL) sIhold.qport->zname = "default system file port"; else { zname = (char *) alloca (strlen (sIhold.zname) + sizeof "system port"); sprintf (zname, "system %s port", sIhold.zname); sIhold.qport->zname = xstrdup (zname); } } #if DEBUG > 1 if (sIhold.qport != NULL) DEBUG_MESSAGE2 (DEBUG_CONFIG, "tiport: Command %s to port %s", argv[1], sIhold.qport->zname); #endif return tret; } else { if (sIhold.qport != NULL) { ulog (LOG_ERROR, "%s: %s: Ignoring port name following port specification", zerr, argv[0]); return CMDTABRET_FREE; } sIhold.zport = argv[1]; return CMDTABRET_CONTINUE; } } /* Process one of the chat commands. We have a special version for systems just so that we clear out the chat failure strings. It would be nice if there were a cleaner way to do this. */ static enum tcmdtabret tichat (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (strcmp (argv[0], "chat-fail") == 0) { if (fIclear_chat_fail) { sIhold.schat.zfail = NULL; fIclear_chat_fail = FALSE; } } else if (strcmp (argv[0], "called-chat-fail") == 0) { if (fIclear_called_chat_fail) { sIhold.scalled_chat.zfail = NULL; fIclear_called_chat_fail = FALSE; } } return tprocess_chat_cmd (argc, argv, pvar, zerr); } /* Process the called-login command. */ /*ARGSUSED*/ static enum tcmdtabret ticalled_login (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (argc < 2) { ulog (LOG_ERROR, "%s: %s: Wrong number of arguments", zerr, argv[0]); return CMDTABRET_FREE; } sIhold.zcalled_login = argv[1]; if (argc > 2) uadd_validate (argv[1], argc - 2, (const char **) argv + 2); return CMDTABRET_CONTINUE; } /* Process the protocol parameter command. */ /*ARGSUSED*/ static enum tcmdtabret tiproto_param (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { if (fIclear_proto_param) { sIhold.cproto_params = 0; sIhold.qproto_params = NULL; fIclear_proto_param = FALSE; } return tadd_proto_param (&sIhold.cproto_params, &sIhold.qproto_params, zerr, argc - 1, argv + 1); } /* Process the request command. */ /*ARGSUSED*/ static enum tcmdtabret tirequest (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { char b; boolean fset; b = argv[1][0]; if (b == 'y' || b == 'Y' || b == 't' || b == 'T') fset = TRUE; else if (b == 'n' || b == 'N' || b == 'f' || b == 'F') fset = FALSE; else { ulog (LOG_ERROR, "%s: %s: %s: Bad boolean", zerr, argv[0], argv[1]); return CMDTABRET_FREE; } sIhold.fcall_request = fset; sIhold.fcalled_request = fset; return CMDTABRET_FREE; } /* Process the transfer command. */ /*ARGSUSED*/ static enum tcmdtabret titransfer (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { char b; boolean fset; b = argv[1][0]; if (b == 'y' || b == 'Y' || b == 't' || b == 'T') fset = TRUE; else if (b == 'n' || b == 'N' || b == 'f' || b == 'F') fset = FALSE; else { ulog (LOG_ERROR, "%s: %s: %s: Bad boolean", zerr, argv[0], argv[1]); return CMDTABRET_FREE; } sIhold.fcall_transfer = fset; sIhold.fcalled_transfer = fset; return CMDTABRET_FREE; } /* Mark all the contents of sIhold to be cleared before they are set. If the falternate argument is TRUE, then only prepare to clear those contents that should be cleared for an alternate. */ static void uiset_clear (falternate) boolean falternate; { if (! falternate) { fIclear_alias = TRUE; fIclear_alternate = TRUE; } fIclear_time = TRUE; fIclear_calltimegrade = TRUE; fIclear_call_local_size = TRUE; fIclear_call_remote_size = TRUE; fIclear_called_local_size = TRUE; fIclear_called_remote_size = TRUE; fIclear_port = TRUE; fIclear_chat_fail = TRUE; fIclear_proto_param = TRUE; fIclear_called_chat_fail = TRUE; } #endif /* HAVE_TAYLOR_CONFIG */ /* Set up the default values advertised in the documentation. */ void uset_system_defaults (qsys) struct ssysteminfo *qsys; { qsys->zname = NULL; qsys->zalias = NULL; qsys->qalternate = NULL; qsys->zalternate = NULL; qsys->ztime = xmalloc (1 + sizeof "Never"); sprintf (qsys->ztime, "%cNever", BGRADE_LOW); qsys->zcalltimegrade = NULL; qsys->zcall_local_size = NULL; qsys->zcall_remote_size = NULL; qsys->zcalled_local_size = NULL; qsys->zcalled_remote_size = NULL; qsys->ibaud = 0L; qsys->ihighbaud = 0L; qsys->zport = NULL; qsys->zphone = NULL; qsys->qport = NULL; INIT_CHAT (&qsys->schat); qsys->schat.zchat = (char *) "\"\" \\r\\c ogin:-BREAK-ogin:-BREAK-ogin: \\L word: \\P"; qsys->schat.ctimeout = 10; qsys->zcall_login = NULL; qsys->zcall_password = NULL; qsys->zcalled_login = NULL; qsys->fcallback = FALSE; qsys->fsequence = FALSE; qsys->zprotocols = NULL; qsys->cproto_params = 0; qsys->qproto_params = NULL; INIT_CHAT (&qsys->scalled_chat); #if DEBUG > 1 qsys->idebug = 0; qsys->imax_remote_debug = DEBUG_ABNORMAL | DEBUG_CHAT | DEBUG_HANDSHAKE; #endif qsys->fcall_request = TRUE; qsys->fcalled_request = TRUE; qsys->fcall_transfer = TRUE; qsys->fcalled_transfer = TRUE; qsys->zlocal_send = ZROOTDIR; qsys->zcalled_local_send = NULL; qsys->zremote_send = "~"; qsys->zcalled_remote_send = NULL; qsys->zlocal_receive = "~"; qsys->zcalled_local_receive = NULL; qsys->zremote_receive = "~"; qsys->zcalled_remote_receive = NULL; qsys->zpath = CMDPATH; qsys->zcmds = "rnews rmail"; qsys->cfree_space = DEFAULT_FREE_SPACE; qsys->zforwardto = NULL; qsys->zpubdir = NULL; qsys->zlocalname = NULL; } /* Variables to store the loaded system information. */ static boolean fIhave_systems; static int cIsystems; static struct ssysteminfo *pasIsystems; /* Read information about all systems. */ static void uiread_systems P((void)); static void uiread_systems () { if (fIhave_systems) return; fIhave_systems = TRUE; #if HAVE_TAYLOR_CONFIG if (zSysfile == NULL) { boolean fmore; /* Only warn about a missing file if we aren't going to read the V2 or BNU files. */ fmore = FALSE; #if HAVE_V2_CONFIG if (fV2) fmore = TRUE; #endif #if HAVE_BNU_CONFIG if (fBnu) fmore = TRUE; #endif if (! fmore) { ulog (LOG_ERROR, "%s%s: file not found", NEWCONFIGLIB, SYSFILE); return; } } else { struct smulti_file *qmulti; int calc; boolean fdefaults, fsystem; struct ssysteminfo *qalternates; qmulti = qmulti_open (zSysfile); if (qmulti != NULL) { calc = 0; fdefaults = FALSE; fsystem = FALSE; qalternates = NULL; fIfirst = FALSE; zInext_system = NULL; while (TRUE) { qIalternates = qalternates; fIdefault_alternates = TRUE; /* Read commands. This will exit when it encounters the start of a file (which it will when we first start reading) and when it reads a ``system'' command. */ uprocesscmds ((FILE *) NULL, qmulti, asIcmds, (const char *) NULL, CMDFLAG_WARNUNRECOG | CMDFLAG_BACKSLASH); /* The handling of alternates can get complex. Before we start reading a system, fIclear_alternate is set to TRUE (this is done in uiset_clear). After we have finished reading a system, then if fIclear_alternate is still TRUE then no ``alternate'' command was used and the system information is in sIhold. Otherwise, if fIclear_alternate is FALSE, an ``alternate'' command was used and we must call tialternate one more time; after this final call to tialternate, the system information will be in sIalternate. The final call to tialternate is needed because each occurrence of the ``alternate'' command links the previous alternate into sIalternate and sets up sIhold with the defaults for the next alternate. The final call will link the last alternate into sIalternate. */ if (fdefaults) { /* We were reading default information. Save it. */ if (fIclear_alternate) sIdefault = sIhold; else { (void) tialternate (0, (char **) NULL, (pointer) NULL, "alternate"); sIdefault = sIalternate; } qalternates = sIdefault.qalternate; sIdefault.qalternate = NULL; fdefaults = FALSE; } else if (fsystem) { /* We just either finished a file or encountered a ``system'' command after we had started reading a system. Finish up the information for the system we were reading. */ if (cIsystems >= calc) { calc += 10; pasIsystems = ((struct ssysteminfo *) xrealloc ((pointer) pasIsystems, calc * sizeof (struct ssysteminfo))); } /* We must now attach any remaining default alternates. */ if (fIdefault_alternates) { while (qIalternates != NULL) (void) tialternate (0, (char **) NULL, (pointer) NULL, "alternate"); } if (fIclear_alternate) pasIsystems[cIsystems] = sIhold; else { (void) tialternate (0, (char **) NULL, (pointer) NULL, "alternate"); pasIsystems[cIsystems] = sIalternate; } ++cIsystems; fsystem = FALSE; } if (fIfirst) { /* We just started reading a new file. Reset the default information. The next time around the loop we will read the default information. */ uset_system_defaults (&sIhold); uiset_clear (FALSE); qalternates = NULL; fdefaults = TRUE; fIfirst = FALSE; } else if (zInext_system != NULL) { /* We just encountered a ``system'' command. Save the name, reset the system information to the defaults, and go on to read the system information. */ sIhold = sIdefault; sIhold.zname = zInext_system; uiset_clear (FALSE); fsystem = TRUE; zInext_system = NULL; } else { /* We have reached the end of the files to read. */ break; } } (void) fmulti_close (qmulti); } } #endif /* HAVE_TAYLOR_CONFIG */ #if HAVE_V2_CONFIG if (fV2) { int cv2; struct ssysteminfo *pasv2; uv2_read_systems (&cv2, &pasv2); if (cv2 > 0) { pasIsystems = ((struct ssysteminfo *) xrealloc ((pointer) pasIsystems, (cIsystems + cv2) * sizeof (struct ssysteminfo))); memcpy (pasIsystems + cIsystems, pasv2, cv2 * sizeof (struct ssysteminfo)); cIsystems += cv2; } } #endif /* HAVE_V2_CONFIG */ #if HAVE_BNU_CONFIG if (fBnu) { int cbnu; struct ssysteminfo *pasbnu; ubnu_read_systems (&cbnu, &pasbnu); if (cbnu > 0) { pasIsystems = ((struct ssysteminfo *) xrealloc ((pointer) pasIsystems, (cIsystems + cbnu) * sizeof (struct ssysteminfo))); memcpy (pasIsystems + cIsystems, pasbnu, cbnu * sizeof (struct ssysteminfo)); cIsystems += cbnu; } } #endif /* HAVE_BNU_CONFIG */ } /* Get information about all systems. */ void uread_all_system_info (pc, ppas) int *pc; struct ssysteminfo **ppas; { if (! fIhave_systems) uiread_systems (); *pc = cIsystems; *ppas = pasIsystems; } /* Get information about a specific system. */ boolean fread_system_info (zsystem, qsys) const char *zsystem; struct ssysteminfo *qsys; { int i; DEBUG_MESSAGE1 (DEBUG_CONFIG, "fread_system_info: Reading information for system %s", zsystem); if (! fIhave_systems) uiread_systems (); for (i = 0; i < cIsystems; i++) { char *z; if (strcmp (zsystem, pasIsystems[i].zname) == 0) { *qsys = pasIsystems[i]; DEBUG_MESSAGE1 (DEBUG_CONFIG, "fread_system_info: Got information for system %s", qsys->zname); return TRUE; } z = pasIsystems[i].zalias; if (z == NULL) continue; while (TRUE) { char *znext; znext = z + strcspn (z, " "); if (strncmp (zsystem, z, znext - z) == 0) { *qsys = pasIsystems[i]; DEBUG_MESSAGE1 (DEBUG_CONFIG, "fread_system_info: Got system %s", qsys->zname); return TRUE; } z = znext; if (*z == ' ') ++z; else break; } } DEBUG_MESSAGE1 (DEBUG_CONFIG, "fread_system_info: Could not find system %s", zsystem); return FALSE; } /* Prepare to read commands defining unknown systems. */ void uiunknown_start () { #if HAVE_TAYLOR_CONFIG uset_system_defaults (&sIhold); uiset_clear (FALSE); #else /* ! HAVE_TAYLOR_CONFIG */ uset_system_defaults (&sUnknown); #endif /* ! HAVE_TAYLOR_CONFIG */ } #if HAVE_TAYLOR_CONFIG /* Process a command defining unknown systems. This is actually called from the main configuration file, not the system file. */ enum tcmdtabret tiunknown (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { fUnknown_ok = TRUE; return tprocess_one_cmd (argc - 1, argv + 1, asIcmds, zerr, CMDFLAG_WARNUNRECOG); } #endif /* HAVE_TAYLOR_CONFIG */ /* Finish up after all commands defining unknwon systems. */ void uiunknown_end () { #if HAVE_TAYLOR_CONFIG if (fUnknown_ok) { /* Add the final alternate. */ if (fIclear_alternate) sUnknown = sIhold; else { (void) tialternate (0, (char **) NULL, (pointer) NULL, "alternate"); sUnknown = sIalternate; } } #endif /* HAVE_TAYLOR_CONFIG */ } /* Initialize the local system information. Perhaps it would be desirable to allow the user to customize this as well. This is called after the configuration file has been read in and the system name has been determined. Only a few elements of this structure are ever actually used, probably just zname and zremote_receive. */ void uisetup_localsys () { uset_system_defaults (&sLocalsys); sLocalsys.zname = zLocalname; } /* Translate a system name into something we can use locally. This should be more intelligent than it is. Right now we just truncate the name; if this matches the name of another system, we reject the call. */ const char * ztranslate_system (zsystem) const char *zsystem; { char *z; struct ssysteminfo s; if (strlen (zsystem) <= cSysdep_max_name_len) return zsystem; z = (char *) xmalloc (cSysdep_max_name_len + 1); strncpy (z, zsystem, cSysdep_max_name_len); z[cSysdep_max_name_len] = '\0'; if (fread_system_info (z, &s)) { xfree ((pointer) z); return NULL; } return z; } #if HAVE_TAYLOR_CONFIG /* Get the login name and password for a system out of the call file. The call file is simple a sequence of lines. The first word on each line is the system name, the second word is the login name, and the third word is the password. We read it using uprocesscmds, since it's easy. */ struct silogpass { char **pzlog; char **pzpass; }; static enum tcmdtabret tilog_pass P((int argc, char **argv, pointer pvar, const char *zerr)); /*ARGSUSED*/ static enum tcmdtabret tilog_pass (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { struct silogpass *q = (struct silogpass *) pvar; *q->pzlog = xstrdup (argv[1]); *q->pzpass = xstrdup (argv[2]); return CMDTABRET_FREE_AND_EXIT; } #endif /* HAVE_TAYLOR_CONFIG */ /* Get the login name and password to use for a system. */ boolean fcallout_login (qsys, pzlog, pzpass) const struct ssysteminfo *qsys; char **pzlog; char **pzpass; { *pzlog = NULL; *pzpass = NULL; #if HAVE_TAYLOR_CONFIG { struct smulti_file *qmulti; struct scmdtab as[2]; struct silogpass s; qmulti = qmulti_open (zCallfile); if (qmulti == NULL) return FALSE; s.pzlog = pzlog; s.pzpass = pzpass; as[0].zcmd = qsys->zname; as[0].itype = CMDTABTYPE_FN | 3; as[0].pvar = (pointer)&s; as[0].ptfn = tilog_pass; as[1].zcmd = NULL; uprocesscmds ((FILE *) NULL, qmulti, as, (const char *) NULL, CMDFLAG_BACKSLASH); (void) fmulti_close (qmulti); } #endif /* HAVE_TAYLOR_CONFIG */ if (*pzlog == NULL) { ulog (LOG_ERROR, "No call out login for system %s", qsys->zname); return FALSE; } return TRUE; } #if HAVE_TAYLOR_CONFIG /* Check whether a login name and password gathered by the UUCP program itself are correct. */ static enum tcmdtabret ticheck_login P((int argc, char **argv, pointer pvar, const char *zerr)); /*ARGSUSED*/ static enum tcmdtabret ticheck_login (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { char **pz = (char **) pvar; *pz = xstrdup (argv[1]); return CMDTABRET_FREE_AND_EXIT; } #endif /* HAVE_TAYLOR_CONFIG */ boolean fcheck_login (zuser, zpass) const char *zuser; const char *zpass; { #if HAVE_TAYLOR_CONFIG struct smulti_file *qmulti; struct scmdtab as[2]; char *zfilepass; boolean fok; if (zPwdfile == NULL) { ulog (LOG_ERROR, "%s%s: file not found", NEWCONFIGLIB, PASSWDFILE); return FALSE; } qmulti = qmulti_open (zPwdfile); if (qmulti == NULL) return FALSE; zfilepass = NULL; as[0].zcmd = zuser; as[0].itype = CMDTABTYPE_FN | 2; as[0].pvar = (pointer)&zfilepass; as[0].ptfn = ticheck_login; as[1].zcmd = NULL; uprocesscmds ((FILE *) NULL, qmulti, as, (const char *) NULL, CMDFLAG_CASESIGNIFICANT | CMDFLAG_BACKSLASH); (void) fmulti_close (qmulti); fok = zfilepass != NULL && strcmp (zfilepass, zpass) == 0; if (zfilepass != NULL) { bzero (zfilepass, strlen (zfilepass)); xfree ((pointer) zfilepass); } if (! fok) ulog (LOG_ERROR, "Bad login"); return fok; #else /* HAVE_TAYLOR_CONFIG */ ulog (LOG_ERROR, "Not compiled to accept logins"); return FALSE; #endif /* HAVE_TAYLOR_CONFIG */ } #if HAVE_TAYLOR_CONFIG /* Process a chat command. These are done as prefix commands. We set it up such that argv[0] will contain the string "chat" and we must look after that point to see what command to execute. */ static struct schat_info sChat; static enum tcmdtabret tcchat_fail P((int argc, char **argv, pointer pvar, const char *zerr)); static const struct scmdtab asChatcmds[] = { { "chat", CMDTABTYPE_FULLSTRING, (pointer) &sChat.zchat, NULL }, { "chat-program", CMDTABTYPE_FULLSTRING, (pointer) &sChat.zprogram, NULL }, { "chat-timeout", CMDTABTYPE_INT, (pointer) &sChat.ctimeout, NULL }, { "chat-fail", CMDTABTYPE_FN | 2, NULL, tcchat_fail }, { "chat-seven-bit", CMDTABTYPE_BOOLEAN, (pointer) &sChat.fstrip, NULL }, { NULL, 0, NULL, NULL } }; enum tcmdtabret tprocess_chat_cmd (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { struct schat_info *qchat = (struct schat_info *) pvar; char *zchat; enum tcmdtabret t; zchat = strstr (argv[0], "chat"); #if DEBUG > 0 if (zchat == NULL) ulog (LOG_FATAL, "tprocess_chat_cmd: Can't happen"); #endif argv[0] = zchat; sChat = *qchat; t = tprocess_one_cmd (argc, argv, asChatcmds, zerr, CMDFLAG_WARNUNRECOG); *qchat = sChat; return t; } /* Add a new chat failure string. */ static enum tcmdtabret tcchat_fail (argc, argv, pvar, zerr) int argc; char **argv; pointer pvar; const char *zerr; { uadd_string (&sChat.zfail, argv[1], ' '); return CMDTABRET_FREE; } #endif /* HAVE_TAYLOR_CONFIG */ #if HAVE_TAYLOR_CONFIG /* Add a protocol parameter entry. The pc parameter points to the number of protocol parameter entries, and the pq parameter points to the array of protocol parameters. */ enum tcmdtabret tadd_proto_param (pc, pq, zerr, cargs, azargs) int *pc; struct sproto_param **pq; const char *zerr; int cargs; char **azargs; { int i; struct sproto_param *q; int ientry; int icopy; if (cargs < 1) { ulog (LOG_ERROR, "%s: protocol-parameter: Not enough arguments", zerr); return CMDTABRET_FREE; } if (azargs[0][1] != '\0') { ulog (LOG_ERROR, "%s: protocol-parameter: Protocol names are single characters", zerr); return CMDTABRET_FREE; } q = NULL; ientry = 0; for (i = 0; i < *pc; i++) { if ((*pq)[i].bproto == azargs[0][0]) { q = &(*pq)[i]; ientry = q->centries; ++q->centries; q->qentries = ((struct sproto_param_entry *) xrealloc ((pointer) q->qentries, (q->centries * sizeof (struct sproto_param_entry)))); break; } } if (i >= *pc) { ++(*pc); *pq = ((struct sproto_param *) xrealloc ((pointer) *pq, (*pc * sizeof (struct sproto_param)))); q = &(*pq)[*pc - 1]; q->bproto = azargs[0][0]; q->centries = 1; q->qentries = ((struct sproto_param_entry *) xmalloc (sizeof (struct sproto_param_entry))); ientry = 0; } q->qentries[ientry].cargs = cargs - 1; q->qentries[ientry].azargs = (char **) xmalloc ((cargs - 1) * sizeof (char *)); for (icopy = 0; icopy < cargs - 1; icopy++) q->qentries[ientry].azargs[icopy] = azargs[icopy + 1]; return CMDTABRET_CONTINUE; } #endif /* HAVE_TAYLOR_CONFIG */ /* Apply some protocol parameters, given the current protocol. */ void uapply_proto_params (bproto, qcmds, c, pas) int bproto; struct scmdtab *qcmds; int c; struct sproto_param *pas; { int i; struct sproto_param *q; for (i = 0, q = pas; i < c; i++, q++) { if (q->bproto == (char) bproto) { char ab[sizeof "g protocol parameters"]; struct sproto_param_entry *qentry; int ientry; sprintf (ab, "%c protocol parameters", bproto); q = &pas[i]; for (ientry = 0, qentry = &q->qentries[0]; ientry < q->centries; ientry++, qentry++) (void) tprocess_one_cmd (qentry->cargs, qentry->azargs, qcmds, ab, CMDFLAG_WARNUNRECOG); return; } } } /* Maintain a list of systems which are permitted to log in using a particular login name. This is the VALIDATE entry from the BNU Permissions file. */ static struct svalidate { struct svalidate *qnext; const char *zlogname; int cmachines; const char *azmachines[1]; } *qIvalidate; /* Add an entry to the validation list. This assumes that it does not have to copy the login name or the machine names. It does copy the array of machines names. */ void uadd_validate (zlogname, cmachines, pazmachines) const char *zlogname; int cmachines; const char **pazmachines; { struct svalidate **pq, *q; for (pq = &qIvalidate; *pq != NULL; pq = &(*pq)->qnext) { if (strcmp ((*pq)->zlogname, zlogname) == 0) { *pq = ((struct svalidate *) xrealloc ((pointer) *pq, sizeof (struct svalidate) + (((*pq)->cmachines + cmachines - 1) * sizeof (const char *)))); memcpy ((*pq)->azmachines + (*pq)->cmachines, pazmachines, cmachines * sizeof (const char *)); (*pq)->cmachines += cmachines; return; } } q = (struct svalidate *) xmalloc (sizeof (struct svalidate) + ((cmachines - 1) * sizeof (const char *))); q->qnext = qIvalidate; q->zlogname = zlogname; memcpy (q->azmachines, pazmachines, cmachines * sizeof (const char *)); q->cmachines = cmachines; qIvalidate = q; } /* Check whether a particular login name/machine name is valid. */ boolean fcheck_validate (zlogname, zmachine) const char *zlogname; const char *zmachine; { struct svalidate *q; for (q = qIvalidate; q != NULL; q = q->qnext) { if (strcmp (q->zlogname, zlogname) == 0) { int i; for (i = 0; i < q->cmachines; i++) if (strcmp (q->azmachines[i], zmachine) == 0) return TRUE; return FALSE; } } return TRUE; } /* The variables which hold the array of timetables. */ int cTtable; struct stimetable *pasTtable; /* Initialize the table of timetables as advertised in the documentation. */ void uinittimetables () { pasTtable = (struct stimetable *) xmalloc (3 * sizeof (struct stimetable)); pasTtable[0].zname = "Evening"; pasTtable[0].ztime = "Wk1705-0755,Sa,Su"; pasTtable[1].zname = "Night"; pasTtable[1].ztime = "Wk2305-0755,Sa,Su2305-1655"; pasTtable[2].zname = "NonPeak"; pasTtable[2].ztime = "Wk1805-0655,Sa,Su"; cTtable = 3; } /* Add a new timetable entry. This assumes it can take control of the strings it is passed, so they must not be on the stack and if they have been allocated they must not be freed. */ void uaddtimetable (zname, ztime) const char *zname; const char *ztime; { if (pasTtable == NULL) uinittimetables (); pasTtable = ((struct stimetable *) xrealloc ((pointer) pasTtable, (cTtable + 1) * sizeof (struct stimetable))); pasTtable[cTtable].zname = zname; pasTtable[cTtable].ztime = ztime; ++cTtable; }