3497 lines
90 KiB
C
3497 lines
90 KiB
C
/* uucico.c
|
||
This is the main UUCP communication program.
|
||
|
||
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: uucico.c,v $
|
||
Revision 1.1.1.1 1993/03/21 09:45:37 cgd
|
||
initial import of 386bsd-0.1 sources
|
||
|
||
Revision 1.92 1992/04/06 21:10:14 ian
|
||
Marc Boucher: set *pqsys to NULL in faccept_call
|
||
|
||
Revision 1.91 1992/03/30 04:07:13 ian
|
||
Dirk Musstopf: remove temporary file if receive fails
|
||
|
||
Revision 1.90 1992/03/28 22:06:38 ian
|
||
Michael I Bushnell: renamed enum tstatus to avoid header file conflict
|
||
|
||
Revision 1.89 1992/03/28 20:52:11 ian
|
||
Petri Helenius: must dump controlling terminal when going to next alternate
|
||
|
||
Revision 1.88 1992/03/28 20:31:55 ian
|
||
Franc,ois Pinard: allow a name to be given to an alternate
|
||
|
||
Revision 1.87 1992/03/28 19:40:26 ian
|
||
Close log and statistics file at each master/slave role switch
|
||
|
||
Revision 1.86 1992/03/28 04:45:31 ian
|
||
Mark E. Mallett: minor cleanup
|
||
|
||
Revision 1.85 1992/03/17 18:42:21 ian
|
||
T. William Wells: set current time in status file when call completes
|
||
|
||
Revision 1.84 1992/03/17 01:03:03 ian
|
||
Miscellaneous cleanup
|
||
|
||
Revision 1.83 1992/03/16 05:16:03 ian
|
||
Recognize SVR4 -U flag
|
||
|
||
Revision 1.82 1992/03/16 04:38:00 ian
|
||
Turn off DEBUG_PORT for handshake debugging
|
||
|
||
Revision 1.81 1992/03/15 04:51:17 ian
|
||
Keep an array of signals we've received rather than a single variable
|
||
|
||
Revision 1.80 1992/03/12 19:54:43 ian
|
||
Debugging based on types rather than number
|
||
|
||
Revision 1.79 1992/03/11 19:53:55 ian
|
||
Improved chat script debugging
|
||
|
||
Revision 1.78 1992/03/11 00:18:50 ian
|
||
Save temporary file if file send fails
|
||
|
||
Revision 1.77 1992/03/10 23:01:20 ian
|
||
Don't run uuxqt if we got a SIGTERM
|
||
|
||
Revision 1.76 1992/03/10 21:47:39 ian
|
||
Added protocol command for ports
|
||
|
||
Revision 1.75 1992/03/09 20:14:37 ian
|
||
Ted Lindgreen: added max-remote-debug command
|
||
|
||
Revision 1.74 1992/03/09 19:52:50 ian
|
||
Ted Lindgreen: strip parity from initial handshake strings
|
||
|
||
Revision 1.73 1992/03/09 19:42:43 ian
|
||
Ted Lindgreen: don't send mail for nonexistent file
|
||
|
||
Revision 1.72 1992/03/09 05:37:10 ian
|
||
Only look for hangup string in debugging mode
|
||
|
||
Revision 1.71 1992/03/09 05:29:20 ian
|
||
Ted Lindgreen: report requested grade on an incoming call
|
||
|
||
Revision 1.70 1992/03/09 05:08:16 ian
|
||
Added status for wrong time to call, not used if system can't be called
|
||
|
||
Revision 1.69 1992/03/08 17:45:41 ian
|
||
Ted Lindgreen: start uuxqt for only one system if appropriate
|
||
|
||
Revision 1.68 1992/03/08 17:08:20 ian
|
||
Ted Lindgreen: ignore -u option
|
||
|
||
Revision 1.67 1992/03/08 16:42:41 ian
|
||
Ted Lindgreen: report port and login name in log file
|
||
|
||
Revision 1.66 1992/03/07 02:56:30 ian
|
||
Rewrote time routines
|
||
|
||
Revision 1.65 1992/03/04 15:05:51 ian
|
||
Michael Haberler: some systems send \n after Shere
|
||
|
||
Revision 1.64 1992/03/04 00:36:44 ian
|
||
Michael Richardson: better chat script debugging
|
||
|
||
Revision 1.63 1992/03/03 21:01:20 ian
|
||
Use strict timeout in fsserial_read, eliminate all race conditions
|
||
|
||
Revision 1.62 1992/03/03 06:06:48 ian
|
||
T. William Wells: don't complain about missing configuration files
|
||
|
||
Revision 1.61 1992/02/29 04:07:08 ian
|
||
Added -j option to uucp and uux
|
||
|
||
Revision 1.60 1992/02/29 01:06:59 ian
|
||
Chip Salzenberg: recheck file permissions before sending
|
||
|
||
Revision 1.59 1992/02/27 05:40:54 ian
|
||
T. William Wells: detach from controlling terminal, handle signals safely
|
||
|
||
Revision 1.58 1992/02/24 22:38:45 ian
|
||
Don't treat an extra argument as a port
|
||
|
||
Revision 1.57 1992/02/24 04:58:47 ian
|
||
Only permit files to be received into directories that are world-writeable
|
||
|
||
Revision 1.56 1992/02/23 19:50:50 ian
|
||
Handle READ and WRITE in Permissions correctly
|
||
|
||
Revision 1.55 1992/02/23 03:26:51 ian
|
||
Overhaul to use automatic configure shell script
|
||
|
||
Revision 1.54 1992/02/20 22:57:19 ian
|
||
Chip Salzenberg: some systems truncate the Shere= machine name
|
||
|
||
Revision 1.53 1992/02/19 19:36:07 ian
|
||
Rearranged time functions
|
||
|
||
Revision 1.52 1992/02/14 21:32:50 ian
|
||
Niels Baggesen: under HAVE_BNU_LOGGING, don't lost system name when dieing
|
||
|
||
Revision 1.51 1992/02/09 05:21:55 ian
|
||
Bob Denny: call fmail_transfer before fsysdep_did_work
|
||
|
||
Revision 1.50 1992/02/08 22:33:32 ian
|
||
Only get the current working directory if it's going to be needed
|
||
|
||
Revision 1.49 1992/02/08 20:33:57 ian
|
||
Handle all possible signals raised by abort
|
||
|
||
Revision 1.48 1992/02/08 03:54:18 ian
|
||
Include <string.h> only in <uucp.h>, added 1992 copyright
|
||
|
||
Revision 1.47 1992/02/07 17:08:15 ian
|
||
Bob Denny: retry time not reached is not an error
|
||
|
||
Revision 1.46 1992/01/29 18:37:27 ian
|
||
Patrick Smith: only wait a short time for the hangup message
|
||
|
||
Revision 1.45 1992/01/28 04:34:10 ian
|
||
Marty Shannon: -f uucp flag not handled correctly
|
||
|
||
Revision 1.44 1992/01/28 03:50:42 ian
|
||
Chip Salzenberg: set .Status correctly if wrong time to call
|
||
|
||
Revision 1.43 1992/01/21 19:39:12 ian
|
||
Chip Salzenberg: uucp and uux start uucico for right system, not any
|
||
|
||
Revision 1.42 1992/01/20 16:44:54 ian
|
||
Marty Shannon: update .Status file if it's the wrong time to call
|
||
|
||
Revision 1.41 1992/01/19 02:27:00 ian
|
||
Marty Shannon: update .Status file on incoming calls
|
||
|
||
Revision 1.40 1992/01/18 22:48:53 ian
|
||
Reworked sending of mail and general handling of failed transfers
|
||
|
||
Revision 1.39 1992/01/15 07:06:29 ian
|
||
Set configuration directory in Makefile rather than sysdep.h
|
||
|
||
Revision 1.38 1992/01/14 04:38:43 ian
|
||
Chip Salzenberg: only declare sportinfo if it will be used
|
||
|
||
Revision 1.37 1992/01/12 19:53:05 ian
|
||
John Antypas: pass in sportinfo structure for fdo_call to use
|
||
|
||
Revision 1.36 1992/01/05 03:09:17 ian
|
||
Changed abProgram and abVersion to non const to avoid compiler bug
|
||
|
||
Revision 1.35 1992/01/04 21:53:36 ian
|
||
Start up uuxqt even if a call fails
|
||
|
||
Revision 1.34 1991/12/31 19:43:13 ian
|
||
Added 'e' protocol
|
||
|
||
Revision 1.33 1991/12/28 04:33:09 ian
|
||
Set fmasterdone correctly in slave mode
|
||
|
||
Revision 1.32 1991/12/23 05:15:54 ian
|
||
David Nugent: set debugging level for a specific system
|
||
|
||
Revision 1.31 1991/12/21 23:10:43 ian
|
||
Terry Gardner: record failed file transfers in statistics file
|
||
|
||
Revision 1.30 1991/12/21 22:17:20 ian
|
||
Change protocol ordering to 't', 'g', 'f'
|
||
|
||
Revision 1.29 1991/12/21 22:07:47 ian
|
||
John Theus: don't warn if port file does not exist
|
||
|
||
Revision 1.28 1991/12/20 04:30:24 ian
|
||
Terry Gardner: record conversation time in log file
|
||
|
||
Revision 1.27 1991/12/20 00:42:24 ian
|
||
Clear user name from error message given by getting next command
|
||
|
||
Revision 1.26 1991/12/18 05:12:00 ian
|
||
Added -l option to uucico to prompt for login name once and then exit
|
||
|
||
Revision 1.25 1991/12/18 03:54:14 ian
|
||
Made error messages to terminal appear more normal
|
||
|
||
Revision 1.24 1991/12/17 04:55:01 ian
|
||
David Nugent: ignore SIGHUP in uucico and uuxqt
|
||
|
||
Revision 1.23 1991/12/15 03:42:33 ian
|
||
Added tprocess_chat_cmd for all chat commands, and added CMDTABTYPE_PREFIX
|
||
|
||
Revision 1.22 1991/12/11 03:59:19 ian
|
||
Create directories when necessary; don't just assume they exist
|
||
|
||
Revision 1.21 1991/11/21 22:17:06 ian
|
||
Add version string, print version when printing usage
|
||
|
||
Revision 1.20 1991/11/16 00:33:28 ian
|
||
Remove ?: operator between string literal and variable
|
||
|
||
Revision 1.19 1991/11/14 03:40:10 ian
|
||
Try to figure out whether stdin is a TCP port
|
||
|
||
Revision 1.18 1991/11/14 03:20:13 ian
|
||
Added seven-bit and reliable commands to help when selecting protocols
|
||
|
||
Revision 1.17 1991/11/13 23:08:40 ian
|
||
Expand remote pathnames in uucp and uux; fix up uux special cases
|
||
|
||
Revision 1.16 1991/11/12 19:47:04 ian
|
||
Add called-chat set of commands to run a chat script on an incoming call
|
||
|
||
Revision 1.15 1991/11/12 18:25:33 ian
|
||
Added 't' protocol
|
||
|
||
Revision 1.14 1991/11/11 23:47:24 ian
|
||
Added chat-program to run a program to do a chat script
|
||
|
||
Revision 1.13 1991/11/11 19:32:03 ian
|
||
Added breceive_char to read characters through protocol buffering
|
||
|
||
Revision 1.12 1991/11/11 18:55:52 ian
|
||
Get protocol parameters from port and dialer for incoming calls
|
||
|
||
Revision 1.11 1991/11/11 16:59:05 ian
|
||
Eliminate fread_port_info, allow NULL pflock arg to ffind_port
|
||
|
||
Revision 1.10 1991/11/11 04:21:16 ian
|
||
Added 'f' protocol
|
||
|
||
Revision 1.9 1991/11/10 19:24:22 ian
|
||
Added pffile protocol entry point for file level control
|
||
|
||
Revision 1.8 1991/11/09 18:53:07 ian
|
||
Reworked protocol interface
|
||
|
||
Revision 1.7 1991/11/07 18:15:38 ian
|
||
Chip Salzenberg: move CMAXRETRIES to conf.h for easy configuration
|
||
|
||
Revision 1.6 1991/09/19 03:06:04 ian
|
||
Chip Salzenberg: put BNU temporary files in system's directory
|
||
|
||
Revision 1.5 1991/09/19 02:30:37 ian
|
||
From Chip Salzenberg: check whether signal is ignored differently
|
||
|
||
Revision 1.4 1991/09/19 02:22:44 ian
|
||
Chip Salzenberg's patch to allow ";retrytime" at the end of a time string
|
||
|
||
Revision 1.3 1991/09/12 05:04:26 ian
|
||
Changed sense of \0 return from btime_low_grade on calltimegrade
|
||
|
||
Revision 1.2 1991/09/11 02:33:14 ian
|
||
Added ffork argument to fsysdep_run
|
||
|
||
Revision 1.1 1991/09/10 19:40:31 ian
|
||
Initial revision
|
||
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
char uucico_rcsid[] = "$Id: uucico.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $";
|
||
#endif
|
||
|
||
#include <ctype.h>
|
||
|
||
#include "getopt.h"
|
||
|
||
#include "port.h"
|
||
#include "prot.h"
|
||
#include "system.h"
|
||
#include "uutime.h"
|
||
|
||
/* The program name. */
|
||
char abProgram[] = "uucico";
|
||
|
||
/* Define the known protocols.
|
||
bname, ffullduplex, qcmds, pfstart, pfshutdown, pfsendcmd, pzgetspace,
|
||
pfsenddata, pfprocess, pfwait, pffile */
|
||
|
||
static struct sprotocol asProtocols[] =
|
||
{
|
||
{ 't', FALSE, RELIABLE_ENDTOEND | RELIABLE_RELIABLE | RELIABLE_EIGHT,
|
||
asTproto_params, ftstart, ftshutdown, ftsendcmd, ztgetspace,
|
||
ftsenddata, ftprocess, ftwait, ftfile },
|
||
{ 'e', FALSE, RELIABLE_ENDTOEND | RELIABLE_RELIABLE | RELIABLE_EIGHT,
|
||
asEproto_params, festart, feshutdown, fesendcmd, zegetspace,
|
||
fesenddata, feprocess, fewait, fefile },
|
||
{ 'g', FALSE, RELIABLE_EIGHT,
|
||
asGproto_params, fgstart, fgshutdown, fgsendcmd, zggetspace,
|
||
fgsenddata, fgprocess, fgwait, NULL },
|
||
{ 'f', FALSE, RELIABLE_RELIABLE,
|
||
asFproto_params, ffstart, ffshutdown, ffsendcmd, zfgetspace,
|
||
ffsenddata, ffprocess, ffwait, fffile },
|
||
};
|
||
|
||
#define CPROTOCOLS (sizeof asProtocols / sizeof asProtocols[0])
|
||
|
||
/* Locked system. */
|
||
|
||
static boolean fLocked_system;
|
||
static struct ssysteminfo sLocked_system;
|
||
|
||
/* Local functions. */
|
||
|
||
static void uusage P((void));
|
||
static void uabort P((void));
|
||
static boolean fcall P((const struct ssysteminfo *qsys,
|
||
struct sport *qport,
|
||
boolean fforce, int bgrade,
|
||
boolean fnodetach));
|
||
static boolean fdo_call P((const struct ssysteminfo *qsys,
|
||
struct sport *qport,
|
||
struct sstatus *qstat, int cretry,
|
||
boolean *pfcalled, struct sport *quse));
|
||
static boolean fcall_failed P((const struct ssysteminfo *qsys,
|
||
enum tstatus_type twhy,
|
||
struct sstatus *qstat, int cretry));
|
||
static boolean flogin_prompt P((struct sport *qport));
|
||
static boolean faccept_call P((const char *zlogin, struct sport *qport,
|
||
const struct ssysteminfo **pqsys));
|
||
static boolean fuucp P((boolean fmaster, const struct ssysteminfo *qsys,
|
||
int bgrade, boolean fnew, long cmax_receive));
|
||
static boolean fdo_xcmd P((const struct ssysteminfo *qsys,
|
||
boolean fcaller,
|
||
const struct scmd *qcmd));
|
||
static boolean fok_to_send P((const char *zfrom, boolean flocal,
|
||
boolean fcaller, boolean fspool,
|
||
const struct ssysteminfo *qsys,
|
||
const char *zuser));
|
||
static boolean fok_to_receive P((const char *zto, boolean flocal,
|
||
boolean fcaller,
|
||
const struct ssysteminfo *qsys,
|
||
const char *zuser));
|
||
static boolean frequest_ok P((boolean flocal, boolean fcaller,
|
||
const struct ssysteminfo *qsys,
|
||
const char *zuser));
|
||
static boolean fsend_uucp_cmd P((const char *z));
|
||
static const char *zget_uucp_cmd P((boolean frequired));
|
||
static const char *zget_typed_line P((void));
|
||
|
||
/* Long getopt options. */
|
||
|
||
static const struct option asLongopts[] = { { NULL, 0, NULL, 0 } };
|
||
|
||
const struct option *_getopt_long_options = asLongopts;
|
||
|
||
int
|
||
main (argc, argv)
|
||
int argc;
|
||
char **argv;
|
||
{
|
||
/* getopt return value */
|
||
int iopt;
|
||
/* Don't detach from controlling terminal. */
|
||
boolean fnodetach = FALSE;
|
||
/* Configuration file name */
|
||
const char *zconfig = NULL;
|
||
/* System to call */
|
||
const char *zsystem = NULL;
|
||
/* Port to use; in master mode, call out on this port. In slave mode,
|
||
accept logins on this port. If port not specified, then in master
|
||
mode figure it out for each system, and in slave mode use stdin and
|
||
stdout. */
|
||
const char *zport = NULL;
|
||
/* Port information for the port name in zport. */
|
||
struct sport sportinfo;
|
||
/* Pointer to port to use, or NULL if unknown. */
|
||
struct sport *qport;
|
||
/* Whether to start uuxqt when done. */
|
||
boolean fuuxqt = TRUE;
|
||
/* Whether to force a call despite status of previous call */
|
||
boolean fforce = FALSE;
|
||
/* Whether we are the master */
|
||
boolean fmaster = FALSE;
|
||
/* Whether to give a single login prompt. */
|
||
boolean flogin = FALSE;
|
||
/* Whether to do an endless loop of accepting calls */
|
||
boolean floop = FALSE;
|
||
/* Whether to wait for an inbound call after doing an outbound call */
|
||
boolean fwait = FALSE;
|
||
boolean fret = TRUE;
|
||
#if DEBUG > 1
|
||
int iholddebug;
|
||
#endif
|
||
|
||
while ((iopt = getopt (argc, argv,
|
||
"DefI:lp:qr:s:S:u:x:X:w")) != EOF)
|
||
{
|
||
switch (iopt)
|
||
{
|
||
case 'D':
|
||
/* Don't detach from controlling terminal. */
|
||
fnodetach = TRUE;
|
||
break;
|
||
|
||
case 'e':
|
||
/* Do an endless loop of accepting calls. */
|
||
floop = TRUE;
|
||
break;
|
||
|
||
case 'f':
|
||
/* Force a call even if it hasn't been long enough since the last
|
||
failed call. */
|
||
fforce = TRUE;
|
||
break;
|
||
|
||
case 'I':
|
||
/* Set configuration file name (default is in sysdep.h). */
|
||
zconfig = optarg;
|
||
break;
|
||
|
||
case 'l':
|
||
/* Prompt for login name and password. */
|
||
flogin = TRUE;
|
||
break;
|
||
|
||
case 'p':
|
||
/* Port to use */
|
||
zport = optarg;
|
||
break;
|
||
|
||
case 'q':
|
||
/* Don't start uuxqt. */
|
||
fuuxqt = FALSE;
|
||
break;
|
||
|
||
case 'r':
|
||
/* Set mode: -r1 for master, -r0 for slave (default) */
|
||
if (optarg[0] == '1' && optarg[1] == '\0')
|
||
fmaster = TRUE;
|
||
else if (optarg[0] == '0' && optarg[1] == '\0')
|
||
fmaster = FALSE;
|
||
else
|
||
uusage ();
|
||
break;
|
||
|
||
case 's':
|
||
/* Set system name */
|
||
zsystem = optarg;
|
||
fmaster = TRUE;
|
||
break;
|
||
|
||
case 'S':
|
||
/* Set system name and force call like -f */
|
||
zsystem = optarg;
|
||
fforce = TRUE;
|
||
fmaster = TRUE;
|
||
break;
|
||
|
||
case 'u':
|
||
/* Some versions of uucpd invoke uucico with a -u argument
|
||
specifying the login name. I'm told it is safe to ignore
|
||
this value, although perhaps we should use it rather than
|
||
zsysdep_login_name (). */
|
||
break;
|
||
|
||
case 'x':
|
||
case 'X':
|
||
#if DEBUG > 1
|
||
/* Set debugging level */
|
||
iDebug |= idebug_parse (optarg);
|
||
#endif
|
||
break;
|
||
|
||
case 'w':
|
||
/* Call out and then wait for a call in */
|
||
fwait = TRUE;
|
||
break;
|
||
|
||
case 0:
|
||
/* Long option found, and flag value set. */
|
||
break;
|
||
|
||
default:
|
||
uusage ();
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (optind != argc)
|
||
uusage ();
|
||
|
||
if (fwait && zport == NULL)
|
||
{
|
||
fprintf (stderr, "%s: -w requires -e\n", abProgram);
|
||
uusage ();
|
||
}
|
||
|
||
uread_config (zconfig);
|
||
|
||
#ifdef SIGINT
|
||
usysdep_signal (SIGINT);
|
||
#endif
|
||
#ifdef SIGHUP
|
||
usysdep_signal (SIGHUP);
|
||
#endif
|
||
#ifdef SIGQUIT
|
||
usysdep_signal (SIGQUIT);
|
||
#endif
|
||
#ifdef SIGTERM
|
||
usysdep_signal (SIGTERM);
|
||
#endif
|
||
#ifdef SIGPIPE
|
||
usysdep_signal (SIGPIPE);
|
||
#endif
|
||
|
||
usysdep_initialize (TRUE, FALSE);
|
||
|
||
ulog_to_file (TRUE);
|
||
ulog_fatal_fn (uabort);
|
||
|
||
/* If a port was named, get its information. */
|
||
if (zport == NULL)
|
||
qport = NULL;
|
||
else
|
||
{
|
||
if (! ffind_port (zport, (long) 0, (long) 0, &sportinfo,
|
||
(boolean (*) P((struct sport *, boolean))) NULL,
|
||
FALSE))
|
||
{
|
||
ulog (LOG_ERROR, "%s: No such port", zport);
|
||
ulog_close ();
|
||
usysdep_exit (FALSE);
|
||
}
|
||
qport = &sportinfo;
|
||
}
|
||
|
||
if (fmaster)
|
||
{
|
||
/* If a system was named, call it up. Otherwise check all the
|
||
known systems, and call all the ones which have work to do. */
|
||
if (zsystem != NULL)
|
||
{
|
||
if (! fread_system_info (zsystem, &sLocked_system))
|
||
ulog (LOG_FATAL, "Unknown system %s", zsystem);
|
||
|
||
/* Detach from the controlling terminal for the call. This
|
||
probably makes sense only on Unix. We want the modem
|
||
line to become the controlling terminal. */
|
||
if (! fnodetach &&
|
||
(qport == NULL || qport->ttype != PORTTYPE_STDIN))
|
||
usysdep_detach ();
|
||
|
||
ulog_system (sLocked_system.zname);
|
||
|
||
#if DEBUG > 1
|
||
iholddebug = iDebug;
|
||
iDebug |= sLocked_system.idebug;
|
||
#endif
|
||
|
||
if (! fsysdep_lock_system (&sLocked_system))
|
||
{
|
||
ulog (LOG_ERROR, "System already locked");
|
||
fret = FALSE;
|
||
}
|
||
else
|
||
{
|
||
fLocked_system = TRUE;
|
||
fret = fcall (&sLocked_system, qport, fforce, BGRADE_HIGH,
|
||
fnodetach);
|
||
(void) fsysdep_unlock_system (&sLocked_system);
|
||
fLocked_system = FALSE;
|
||
}
|
||
|
||
#if DEBUG > 1
|
||
iDebug = iholddebug;
|
||
#endif
|
||
|
||
ulog_system ((const char *) NULL);
|
||
}
|
||
else
|
||
{
|
||
int csystems;
|
||
struct ssysteminfo *pas;
|
||
int i;
|
||
char bgrade;
|
||
boolean fdidone;
|
||
|
||
fret = TRUE;
|
||
fdidone = FALSE;
|
||
uread_all_system_info (&csystems, &pas);
|
||
for (i = 0; i < csystems && ! FGOT_SIGNAL (); i++)
|
||
{
|
||
if (fsysdep_has_work (&pas[i], &bgrade))
|
||
{
|
||
fdidone = TRUE;
|
||
|
||
/* Detach from the controlling terminal. On Unix
|
||
this means that we will wind up forking a new
|
||
process for each system we call. */
|
||
if (! fnodetach
|
||
&& (qport == NULL
|
||
|| qport->ttype != PORTTYPE_STDIN))
|
||
usysdep_detach ();
|
||
|
||
ulog_system (pas[i].zname);
|
||
|
||
#if DEBUG > 1
|
||
iholddebug = iDebug;
|
||
iDebug |= pas[i].idebug;
|
||
#endif
|
||
|
||
if (! fsysdep_lock_system (&pas[i]))
|
||
{
|
||
ulog (LOG_ERROR, "System already locked");
|
||
fret = FALSE;
|
||
}
|
||
else
|
||
{
|
||
sLocked_system = pas[i];
|
||
fLocked_system = TRUE;
|
||
if (! fcall (&pas[i], qport, fforce, bgrade,
|
||
fnodetach))
|
||
fret = FALSE;
|
||
|
||
/* Now ignore any SIGHUP that we got. */
|
||
afSignal[INDEXSIG_SIGHUP] = FALSE;
|
||
|
||
(void) fsysdep_unlock_system (&pas[i]);
|
||
fLocked_system = FALSE;
|
||
}
|
||
|
||
#if DEBUG > 1
|
||
iDebug = iholddebug;
|
||
#endif
|
||
|
||
ulog_system ((const char *) NULL);
|
||
}
|
||
}
|
||
|
||
if (! fdidone)
|
||
ulog (LOG_NORMAL, "No work");
|
||
}
|
||
|
||
/* If requested, wait for calls after dialing out. */
|
||
if (fwait)
|
||
{
|
||
floop = TRUE;
|
||
fmaster = FALSE;
|
||
}
|
||
}
|
||
|
||
if (! fmaster)
|
||
{
|
||
/* If a port was specified by name, we go into endless loop
|
||
mode. In this mode, we wait for calls and prompt them with
|
||
"login:" and "Password:", so that they think we are a regular
|
||
UNIX system. If we aren't in endless loop mode, we have been
|
||
called by some other system. If flogin is TRUE, we prompt
|
||
with "login:" and "Password:" a single time. */
|
||
|
||
fret = TRUE;
|
||
zsystem = NULL;
|
||
|
||
if (qport != NULL)
|
||
{
|
||
/* Detach from the controlling terminal, so that the port we
|
||
are about to use becomes our controlling terminal. */
|
||
if (! fnodetach && qport->ttype != PORTTYPE_STDIN)
|
||
usysdep_detach ();
|
||
|
||
floop = TRUE;
|
||
if (! fport_lock (qport, TRUE))
|
||
{
|
||
ulog (LOG_ERROR, "Port %s is locked", qport->zname);
|
||
fret = FALSE;
|
||
}
|
||
}
|
||
|
||
if (fret)
|
||
{
|
||
if (! fport_open (qport, (long) 0, (long) 0, TRUE))
|
||
fret = FALSE;
|
||
}
|
||
|
||
if (fret)
|
||
{
|
||
if (floop)
|
||
{
|
||
while (! FGOT_SIGNAL () && flogin_prompt (qport))
|
||
{
|
||
/* Now ignore any SIGHUP that we got. */
|
||
afSignal[INDEXSIG_SIGHUP] = FALSE;
|
||
|
||
if (fLocked_system)
|
||
{
|
||
(void) fsysdep_unlock_system (&sLocked_system);
|
||
fLocked_system = FALSE;
|
||
}
|
||
if (! fport_reset ())
|
||
break;
|
||
}
|
||
fret = FALSE;
|
||
}
|
||
else
|
||
{
|
||
if (flogin)
|
||
fret = flogin_prompt (qport);
|
||
else
|
||
{
|
||
const struct ssysteminfo *qsys;
|
||
|
||
#if DEBUG > 1
|
||
iholddebug = iDebug;
|
||
#endif
|
||
fret = faccept_call (zsysdep_login_name (), qport,
|
||
&qsys);
|
||
if (qsys != NULL)
|
||
zsystem = qsys->zname;
|
||
#if DEBUG > 1
|
||
iDebug = iholddebug;
|
||
#endif
|
||
}
|
||
}
|
||
|
||
(void) fport_close (fret);
|
||
|
||
if (fLocked_system)
|
||
{
|
||
(void) fsysdep_unlock_system (&sLocked_system);
|
||
fLocked_system = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
ulog_close ();
|
||
ustats_close ();
|
||
|
||
/* If we got a SIGTERM, perhaps because the system is going down,
|
||
don't run uuxqt. We go ahead and run it for any other signal,
|
||
since I think they indicate more temporary conditions. */
|
||
if (afSignal[INDEXSIG_SIGTERM])
|
||
fuuxqt = FALSE;
|
||
|
||
if (fuuxqt)
|
||
{
|
||
/* Detach from the controlling terminal before starting up uuxqt,
|
||
so that it runs as a true daemon. */
|
||
if (! fnodetach)
|
||
usysdep_detach ();
|
||
if (zsystem == NULL)
|
||
fret = fsysdep_run (FALSE, "uuxqt", (const char *) NULL,
|
||
(const char *) NULL);
|
||
else
|
||
fret = fsysdep_run (FALSE, "uuxqt", "-s", zsystem);
|
||
}
|
||
|
||
usysdep_exit (fret);
|
||
|
||
/* Avoid complaints about not returning. */
|
||
return 0;
|
||
}
|
||
|
||
/* Print out a usage message. */
|
||
|
||
static void
|
||
uusage ()
|
||
{
|
||
fprintf (stderr,
|
||
"Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
|
||
abVersion);
|
||
fprintf (stderr,
|
||
"Usage: uucico [options]\n");
|
||
fprintf (stderr,
|
||
" -s,-S system: Call system (-S implies -f)\n");
|
||
fprintf (stderr,
|
||
" -f: Force call despite system status\n");
|
||
fprintf (stderr,
|
||
" -r state: 1 for master, 0 for slave (default)\n");
|
||
fprintf (stderr,
|
||
" -p port: Specify port (implies -e)\n");
|
||
fprintf (stderr,
|
||
" -l: prompt for login name and password\n");
|
||
fprintf (stderr,
|
||
" -e: Endless loop of login prompts and daemon execution\n");
|
||
fprintf (stderr,
|
||
" -w: After calling out, wait for incoming calls\n");
|
||
fprintf (stderr,
|
||
" -q: Don't start uuxqt when done\n");
|
||
fprintf (stderr,
|
||
" -x,-X debug: Set debugging level\n");
|
||
#if HAVE_TAYLOR_CONFIG
|
||
fprintf (stderr,
|
||
" -I file: Set configuration file to use (default %s%s)\n",
|
||
NEWCONFIGLIB, CONFIGFILE);
|
||
#endif /* HAVE_TAYLOR_CONFIG */
|
||
|
||
exit (EXIT_FAILURE);
|
||
}
|
||
|
||
/* This function is called when a LOG_FATAL error occurs. */
|
||
|
||
static void
|
||
uabort ()
|
||
{
|
||
ustats_failed ();
|
||
|
||
#if ! HAVE_BNU_LOGGING
|
||
/* When using BNU logging, it's a pain to have no system name. */
|
||
ulog_system ((const char *) NULL);
|
||
#endif
|
||
|
||
ulog_user ((const char *) NULL);
|
||
|
||
if (qPort != NULL)
|
||
(void) fport_close (FALSE);
|
||
|
||
if (fLocked_system)
|
||
{
|
||
(void) fsysdep_unlock_system (&sLocked_system);
|
||
fLocked_system = FALSE;
|
||
}
|
||
|
||
ulog_close ();
|
||
ustats_close ();
|
||
|
||
usysdep_exit (FALSE);
|
||
}
|
||
|
||
/* Call another system, trying all the possible sets of calling
|
||
instructions. The fprepare_call function should already have been
|
||
called. The qsys argument is the system to call. The qport
|
||
argument is the port to use, and may be NULL. If the fforce
|
||
argument is TRUE, a call is forced even if not enough time has
|
||
passed since the last failed call. The bgrade argument is the
|
||
highest grade of work to be done for the system. The qstat
|
||
argument holds the status of the system. */
|
||
|
||
static boolean fcall (qsys, qport, fforce, bgrade, fnodetach)
|
||
const struct ssysteminfo *qsys;
|
||
struct sport *qport;
|
||
boolean fforce;
|
||
int bgrade;
|
||
boolean fnodetach;
|
||
{
|
||
boolean fbadtime, fnevertime;
|
||
const struct ssysteminfo *qorigsys;
|
||
struct sstatus sstat;
|
||
|
||
qorigsys = qsys;
|
||
if (! fsysdep_get_status (qorigsys, &sstat))
|
||
return FALSE;
|
||
|
||
/* Make sure it's been long enough since the last failed call. */
|
||
if (! fforce)
|
||
{
|
||
#ifdef CMAXRETRIES
|
||
#if CMAXRETRIES > 0
|
||
if (sstat.cretries >= CMAXRETRIES)
|
||
{
|
||
ulog (LOG_ERROR, "Too many retries");
|
||
return FALSE;
|
||
}
|
||
#endif /* CMAXRETRIES > 0 */
|
||
#endif /* defined (CMAXRETRIES) */
|
||
|
||
if (sstat.ttype != STATUS_COMPLETE
|
||
&& sstat.ilast + sstat.cwait > isysdep_time ((long *) NULL))
|
||
{
|
||
ulog (LOG_NORMAL, "Retry time not reached");
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
fbadtime = TRUE;
|
||
fnevertime = TRUE;
|
||
|
||
do
|
||
{
|
||
struct sspan *qtime;
|
||
const struct ssysteminfo *qnext;
|
||
|
||
qtime = qtimegrade_parse (qsys->ztime);
|
||
if (qtime != NULL)
|
||
{
|
||
long ival;
|
||
int cretry;
|
||
boolean fmatch;
|
||
|
||
fnevertime = FALSE;
|
||
|
||
/* The value returned in ival by fspan_match is the lowest
|
||
grade which may be done at this time. */
|
||
|
||
fmatch = (ftimespan_match (qtime, &ival, &cretry)
|
||
&& igradecmp (bgrade, (int) ival) <= 0);
|
||
|
||
utimespan_free (qtime);
|
||
|
||
if (fmatch)
|
||
{
|
||
boolean fret, fcalled;
|
||
struct sport sportinfo;
|
||
|
||
if (FGOT_SIGNAL ())
|
||
return FALSE;
|
||
|
||
fbadtime = FALSE;
|
||
|
||
fret = fdo_call (qsys, qport, &sstat, cretry, &fcalled,
|
||
&sportinfo);
|
||
(void) fport_close (fret);
|
||
|
||
if (fret)
|
||
return TRUE;
|
||
if (fcalled)
|
||
return FALSE;
|
||
|
||
/* Now we have to dump that port so that we can aquire a
|
||
new one. */
|
||
if (! fnodetach)
|
||
usysdep_detach ();
|
||
}
|
||
}
|
||
|
||
/* Look for the next alternate with different calling
|
||
instructions. */
|
||
qnext = qsys;
|
||
do
|
||
{
|
||
qnext = qnext->qalternate;
|
||
}
|
||
while (qnext != NULL
|
||
&& qsys->ztime == qnext->ztime
|
||
&& qsys->zport == qnext->zport
|
||
&& qsys->qport == qnext->qport
|
||
&& qsys->ibaud == qnext->ibaud
|
||
&& qsys->zphone == qnext->zphone
|
||
&& qsys->schat.zprogram == qnext->schat.zprogram
|
||
&& qsys->schat.zchat == qnext->schat.zchat);
|
||
|
||
qsys = qnext;
|
||
}
|
||
while (qsys != NULL);
|
||
|
||
if (fbadtime)
|
||
{
|
||
ulog (LOG_ERROR, "Wrong time to call");
|
||
|
||
/* Update the status, unless the system can never be called. If
|
||
the system can never be called, there is little point to
|
||
putting in a ``wrong time to call'' message. We don't change
|
||
the number of retries, although we do set the wait until the
|
||
next retry to 0. */
|
||
if (! fnevertime)
|
||
{
|
||
sstat.ttype = STATUS_WRONG_TIME;
|
||
sstat.ilast = isysdep_time ((long *) NULL);
|
||
sstat.cwait = 0;
|
||
(void) fsysdep_set_status (qorigsys, &sstat);
|
||
}
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
/* Do the actual work of calling another system, such as dialing and
|
||
logging in. The qsys argument is the system to call, the qport
|
||
argument is the port to use, and the qstat argument holds the
|
||
current status of the ssystem. If we log in successfully, set
|
||
*pfcalled to TRUE; this is used to distinguish a failed dial from a
|
||
failure during the call. The quse argument is passed in because
|
||
this function does not call fport_close, so if it reads in a port
|
||
structure to open it must not keep it on the stack. */
|
||
|
||
static boolean fdo_call (qsys, qport, qstat, cretry, pfcalled, quse)
|
||
const struct ssysteminfo *qsys;
|
||
struct sport *qport;
|
||
struct sstatus *qstat;
|
||
int cretry;
|
||
boolean *pfcalled;
|
||
struct sport *quse;
|
||
{
|
||
const char *zstr;
|
||
boolean fnew;
|
||
int cdial_proto_params;
|
||
struct sproto_param *qdial_proto_params;
|
||
int idial_reliable;
|
||
long istart_time;
|
||
|
||
*pfcalled = FALSE;
|
||
|
||
/* If no port was specified on the command line, use any port
|
||
defined for the system. To select the system port: 1) see if
|
||
port information was specified directly; 2) see if a port was
|
||
named; 3) get an available port given the baud rate. We don't
|
||
change the system status if a port is unavailable; i.e. we don't
|
||
force the system to wait for the retry time. */
|
||
|
||
if (qport == NULL)
|
||
qport = qsys->qport;
|
||
if (qport != NULL)
|
||
{
|
||
if (! fport_lock (qport, FALSE))
|
||
{
|
||
ulog (LOG_ERROR, "Port \"%s\" already locked", qport->zname);
|
||
return FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (! ffind_port (qsys->zport, qsys->ibaud, qsys->ihighbaud,
|
||
quse, fport_lock, TRUE))
|
||
return FALSE;
|
||
qport = quse;
|
||
/* The port is locked by ffind_port. */
|
||
}
|
||
|
||
/* Now try to call the system. */
|
||
|
||
if (! fport_open (qport, qsys->ibaud, qsys->ihighbaud, FALSE))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_PORT_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
if (qsys->zalternate == NULL)
|
||
ulog (LOG_NORMAL, "Calling system %s (port %s)", qsys->zname,
|
||
zLdevice == NULL ? "unknown" : zLdevice);
|
||
else
|
||
ulog (LOG_NORMAL, "Calling system %s (alternate %s, port %s)",
|
||
qsys->zname, qsys->zalternate,
|
||
zLdevice == NULL ? "unknown" : zLdevice);
|
||
|
||
cdial_proto_params = 0;
|
||
qdial_proto_params = NULL;
|
||
if (! fport_dial (qsys, &cdial_proto_params, &qdial_proto_params,
|
||
&idial_reliable))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_DIAL_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
if (! fchat (&qsys->schat, qsys, (const struct sdialer *) NULL,
|
||
(const char *) NULL, FALSE, qPort->zname, iport_baud ()))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_LOGIN_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
qstat->ttype = STATUS_TALKING;
|
||
qstat->ilast = isysdep_time ((long *) NULL);
|
||
qstat->cretries = 0;
|
||
qstat->cwait = 0;
|
||
if (! fsysdep_set_status (qsys, qstat))
|
||
return FALSE;
|
||
|
||
ulog (LOG_NORMAL, "Login successful");
|
||
|
||
*pfcalled = TRUE;
|
||
istart_time = isysdep_time ((long *) NULL);
|
||
|
||
/* We should now see "Shere" from the other system. Apparently
|
||
some systems send "Shere=foo" where foo is the remote name. */
|
||
|
||
zstr = zget_uucp_cmd (TRUE);
|
||
if (zstr == NULL)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
if (strncmp (zstr, "Shere", 5) != 0)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
ulog (LOG_ERROR, "Bad initialization string");
|
||
return FALSE;
|
||
}
|
||
|
||
if (zstr[5] == '=')
|
||
{
|
||
const char *zheresys;
|
||
int icmp;
|
||
|
||
/* Some UUCP packages only provide seven characters in the Shere
|
||
machine name. */
|
||
zheresys = zstr + 6;
|
||
if (strlen (zheresys) == 7)
|
||
icmp = strncmp (zheresys, qsys->zname, 7);
|
||
else
|
||
icmp = strcmp (zheresys, qsys->zname);
|
||
if (icmp != 0)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat,
|
||
cretry);
|
||
ulog (LOG_ERROR, "Called wrong system (%s)", zheresys);
|
||
return FALSE;
|
||
}
|
||
}
|
||
#if DEBUG > 1
|
||
else if (zstr[5] != '\0')
|
||
DEBUG_MESSAGE1 (DEBUG_HANDSHAKE,
|
||
"fdo_call: Strange Shere: %s", zstr);
|
||
#endif
|
||
|
||
/* We now send "S" name switches, where name is our UUCP name. If
|
||
we are using sequence numbers with this system, we send a -Q
|
||
argument with the sequence number. If the call-timegrade command
|
||
was used, we send a -p argument and a -vgrade= argument with the
|
||
grade to send us (we send both argument to make it more likely
|
||
that one is recognized). We always send a -N (for new) switch to
|
||
indicate that we are prepared to accept file sizes. */
|
||
{
|
||
char bgrade;
|
||
const char *zuse_local;
|
||
char *zsend;
|
||
|
||
/* Determine the grade we should request of the other system. A
|
||
'\0' means that no restrictions have been made. */
|
||
bgrade = btimegrade (qsys->zcalltimegrade);
|
||
|
||
if (qsys->zlocalname != NULL)
|
||
zuse_local = qsys->zlocalname;
|
||
else
|
||
zuse_local = zLocalname;
|
||
|
||
zsend = (char *) alloca (strlen (zuse_local) + 70);
|
||
if (! qsys->fsequence)
|
||
{
|
||
if (bgrade == '\0')
|
||
sprintf (zsend, "S%s -N", zuse_local);
|
||
else
|
||
sprintf (zsend, "S%s -p%c -vgrade=%c -N", zuse_local, bgrade,
|
||
bgrade);
|
||
}
|
||
else
|
||
{
|
||
long iseq;
|
||
|
||
iseq = isysdep_get_sequence (qsys);
|
||
if (iseq < 0)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat,
|
||
cretry);
|
||
return FALSE;
|
||
}
|
||
if (bgrade == '\0')
|
||
sprintf (zsend, "S%s -Q%ld -N", zuse_local, iseq);
|
||
else
|
||
sprintf (zsend, "S%s -Q%ld -p%c -vgrade=%c -N", zuse_local, iseq,
|
||
bgrade, bgrade);
|
||
}
|
||
|
||
if (! fsend_uucp_cmd (zsend))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat,
|
||
cretry);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Now we should see ROK or Rreason where reason gives a cryptic
|
||
reason for failure. If we are talking to a counterpart, we will
|
||
get back ROKN. */
|
||
zstr = zget_uucp_cmd (TRUE);
|
||
if (zstr == NULL)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zstr[0] != 'R')
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
ulog (LOG_ERROR, "Bad reponse to handshake string (%s)",
|
||
zstr);
|
||
return FALSE;
|
||
}
|
||
|
||
if (strcmp (zstr + 1, "OKN") == 0)
|
||
fnew = TRUE;
|
||
else if (strcmp (zstr + 1, "OK") == 0)
|
||
fnew = FALSE;
|
||
else if (strcmp (zstr + 1, "CB") == 0)
|
||
{
|
||
ulog (LOG_NORMAL, "Remote system will call back");
|
||
qstat->ttype = STATUS_COMPLETE;
|
||
(void) fsysdep_set_status (qsys, qstat);
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
ulog (LOG_ERROR, "Handshake failed (%s)", zstr + 1);
|
||
return FALSE;
|
||
}
|
||
|
||
/* The slave should now send \020Pprotos\0 where protos is a list of
|
||
supported protocols. Each protocol is a single character. */
|
||
|
||
zstr = zget_uucp_cmd (TRUE);
|
||
if (zstr == NULL)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zstr[0] != 'P')
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
ulog (LOG_ERROR, "Bad protocol handshake (%s)", zstr);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Now decide which protocol to use. The system and the port may
|
||
have their own list of protocols. */
|
||
{
|
||
int i;
|
||
char ab[5];
|
||
|
||
i = CPROTOCOLS;
|
||
if (qsys->zprotocols != NULL || qPort->zprotocols != NULL)
|
||
{
|
||
const char *zproto;
|
||
|
||
if (qsys->zprotocols != NULL)
|
||
zproto = qsys->zprotocols;
|
||
else
|
||
zproto = qPort->zprotocols;
|
||
for (; *zproto != '\0'; zproto++)
|
||
{
|
||
if (strchr (zstr + 1, *zproto) != NULL)
|
||
{
|
||
for (i = 0; i < CPROTOCOLS; i++)
|
||
if (asProtocols[i].bname == *zproto)
|
||
break;
|
||
if (i < CPROTOCOLS)
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int ir;
|
||
|
||
/* If neither the system nor the port specified a list of
|
||
protocols, we want only protocols that match the known
|
||
reliability of the dialer and the port. If we have no
|
||
reliability information, we default to a reliable eight bit
|
||
connection. */
|
||
|
||
ir = 0;
|
||
if ((qPort->ireliable & RELIABLE_SPECIFIED) != 0)
|
||
ir = qPort->ireliable;
|
||
if ((idial_reliable & RELIABLE_SPECIFIED) != 0)
|
||
{
|
||
if (ir != 0)
|
||
ir &= idial_reliable;
|
||
else
|
||
ir = idial_reliable;
|
||
}
|
||
if (ir == 0)
|
||
ir = RELIABLE_RELIABLE | RELIABLE_EIGHT | RELIABLE_SPECIFIED;
|
||
|
||
for (i = 0; i < CPROTOCOLS; i++)
|
||
{
|
||
int ipr;
|
||
|
||
ipr = asProtocols[i].ireliable;
|
||
if ((ipr & ir) != ipr)
|
||
continue;
|
||
if (strchr (zstr + 1, asProtocols[i].bname) != NULL)
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (i >= CPROTOCOLS)
|
||
{
|
||
(void) fsend_uucp_cmd ("UN");
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
ulog (LOG_ERROR, "No mutually supported protocols");
|
||
return FALSE;
|
||
}
|
||
|
||
qProto = &asProtocols[i];
|
||
|
||
sprintf (ab, "U%c", qProto->bname);
|
||
if (! fsend_uucp_cmd (ab))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Run any protocol parameter commands. */
|
||
|
||
if (qProto->qcmds != NULL)
|
||
{
|
||
if (qsys->cproto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
qsys->cproto_params, qsys->qproto_params);
|
||
if (qPort->cproto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
qPort->cproto_params, qPort->qproto_params);
|
||
if (cdial_proto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
cdial_proto_params, qdial_proto_params);
|
||
}
|
||
|
||
/* Turn on the selected protocol. */
|
||
|
||
if (! (*qProto->pfstart) (TRUE))
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_HANDSHAKE_FAILED, qstat,
|
||
cretry);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Now we have succesfully logged in as the master. */
|
||
|
||
ulog (LOG_NORMAL, "Handshake successful");
|
||
|
||
{
|
||
boolean fret;
|
||
long iend_time;
|
||
|
||
fret = fuucp (TRUE, qsys, '\0', fnew, (long) -1);
|
||
ulog_user ((const char *) NULL);
|
||
usysdep_get_work_free (qsys);
|
||
|
||
/* If we jumped out due to an error, shutdown the protocol. */
|
||
if (! fret)
|
||
{
|
||
(void) (*qProto->pfshutdown) ();
|
||
ustats_failed ();
|
||
}
|
||
|
||
/* Now send the hangup message. As the caller, we send six O's
|
||
and expect to receive seven O's. We send the six O's twice
|
||
to help the other side. We don't worry about errors here. */
|
||
if (fsend_uucp_cmd ("OOOOOO")
|
||
&& fsend_uucp_cmd ("OOOOOO"))
|
||
{
|
||
/* We don't even look for the hangup string from the other
|
||
side unless we're in debugging mode. */
|
||
#if DEBUG > 1
|
||
if (fret && FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
zstr = zget_uucp_cmd (FALSE);
|
||
if (zstr != NULL)
|
||
{
|
||
/* The Ultrix UUCP only sends six O's, although I
|
||
think it should send seven. Because of this, we
|
||
only check for six. */
|
||
if (strstr (zstr, "OOOOOO") == NULL)
|
||
ulog (LOG_DEBUG, "No hangup from remote");
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
iend_time = isysdep_time ((long *) NULL);
|
||
|
||
ulog (LOG_NORMAL, "Call complete (%ld seconds)",
|
||
iend_time - istart_time);
|
||
|
||
if (! fret)
|
||
{
|
||
(void) fcall_failed (qsys, STATUS_FAILED, qstat, cretry);
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
qstat->ttype = STATUS_COMPLETE;
|
||
qstat->ilast = iend_time;
|
||
(void) fsysdep_set_status (qsys, qstat);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* A small helper routine to write out the system status when something
|
||
goes wrong. */
|
||
|
||
static boolean
|
||
fcall_failed (qsys, twhy, qstat, cretry)
|
||
const struct ssysteminfo *qsys;
|
||
enum tstatus_type twhy;
|
||
struct sstatus *qstat;
|
||
int cretry;
|
||
{
|
||
DEBUG_MESSAGE2 (DEBUG_HANDSHAKE,
|
||
"fcall_failed: Cause %d (%s)", (int) twhy,
|
||
azStatus[(int) twhy]);
|
||
|
||
qstat->ttype = twhy;
|
||
qstat->cretries++;
|
||
qstat->ilast = isysdep_time ((long *) NULL);
|
||
if (cretry == 0)
|
||
qstat->cwait = CRETRY_WAIT (qstat->cretries);
|
||
else
|
||
qstat->cwait = cretry * 60;
|
||
return fsysdep_set_status (qsys, qstat);
|
||
}
|
||
|
||
/* Prompt for a login name and a password, and run as the slave. */
|
||
|
||
static boolean flogin_prompt (qport)
|
||
struct sport *qport;
|
||
{
|
||
const char *zuser, *zpass;
|
||
|
||
DEBUG_MESSAGE0 (DEBUG_HANDSHAKE, "flogin_prompt: Waiting for login");
|
||
|
||
do
|
||
{
|
||
if (! fport_write ("login: ", sizeof "login: " - 1))
|
||
return FALSE;
|
||
zuser = zget_typed_line ();
|
||
}
|
||
while (zuser != NULL && *zuser == '\0');
|
||
|
||
if (zuser != NULL)
|
||
{
|
||
char *zhold;
|
||
|
||
zhold = (char *) alloca (strlen (zuser) + 1);
|
||
strcpy (zhold, zuser);
|
||
|
||
if (! fport_write ("Password:", sizeof "Password:" - 1))
|
||
return FALSE;
|
||
|
||
zpass = zget_typed_line ();
|
||
if (zpass != NULL)
|
||
{
|
||
if (fcheck_login (zhold, zpass))
|
||
{
|
||
#if DEBUG > 1
|
||
int iholddebug;
|
||
#endif
|
||
|
||
/* We ignore the return value of faccept_call because we
|
||
really don't care whether the call succeeded or not.
|
||
We are going to reset the port anyhow. */
|
||
#if DEBUG > 1
|
||
iholddebug = iDebug;
|
||
#endif
|
||
(void) faccept_call (zhold, qport,
|
||
(const struct ssysteminfo **) NULL);
|
||
#if DEBUG > 1
|
||
iDebug = iholddebug;
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* Accept a call from a remote system. If pqsys is not NULL, *pqsys
|
||
will be set to the system that called in if known. */
|
||
|
||
static boolean
|
||
faccept_call (zlogin, qport, pqsys)
|
||
const char *zlogin;
|
||
struct sport *qport;
|
||
const struct ssysteminfo **pqsys;
|
||
{
|
||
long istart_time;
|
||
int cdial_proto_params;
|
||
struct sproto_param *qdial_proto_params;
|
||
int idial_reliable;
|
||
boolean ftcp_port;
|
||
const char *zport;
|
||
char *zsend, *zspace;
|
||
const char *zstr;
|
||
struct ssysteminfo ssys;
|
||
const struct ssysteminfo *qsys;
|
||
boolean fnew;
|
||
char bgrade;
|
||
const char *zuse_local;
|
||
struct sstatus sstat;
|
||
long cmax_receive;
|
||
boolean frestart;
|
||
#if HAVE_TAYLOR_CONFIG
|
||
struct sport sportinfo;
|
||
#endif
|
||
|
||
if (pqsys != NULL)
|
||
*pqsys = NULL;
|
||
|
||
ulog (LOG_NORMAL, "Incoming call (login %s port %s)",
|
||
zlogin == NULL ? "unknown" : zlogin,
|
||
zLdevice == NULL ? "unknown" : zLdevice);
|
||
|
||
istart_time = isysdep_time ((long *) NULL);
|
||
|
||
/* Figure out protocol parameters determined by the port. If no
|
||
port was specified we're reading standard input, so try to get
|
||
the port name and read information from the port file. We only
|
||
use the port information to get protocol parameters; we don't
|
||
want to start treating the port as though it were a modem, for
|
||
example. */
|
||
|
||
if (qport != NULL)
|
||
{
|
||
zport = qport->zname;
|
||
ftcp_port = FALSE;
|
||
}
|
||
else
|
||
{
|
||
zport = zsysdep_port_name (&ftcp_port);
|
||
|
||
/* We want to get the protocol parameters for the port. If we
|
||
aren't using HAVE_TAYLOR_CONFIG, that information isn't
|
||
stored anyhow, so we don't bother to look it up. */
|
||
|
||
#if HAVE_TAYLOR_CONFIG
|
||
|
||
if (zport != NULL && zPortfile != NULL)
|
||
{
|
||
if (ffind_port (zport, (long) 0, (long) 0, &sportinfo,
|
||
(boolean (*) P((struct sport *, boolean))) NULL,
|
||
FALSE))
|
||
qport = &sportinfo;
|
||
}
|
||
|
||
#endif /* HAVE_TAYLOR_CONFIG */
|
||
|
||
if (zport == NULL)
|
||
zport = "unknown";
|
||
}
|
||
|
||
/* If we've managed to figure out that this is a modem port, now try
|
||
to get protocol parameters from the dialer. */
|
||
|
||
cdial_proto_params = 0;
|
||
qdial_proto_params = NULL;
|
||
idial_reliable = 0;
|
||
if (qport != NULL)
|
||
{
|
||
if (qport->ttype == PORTTYPE_MODEM)
|
||
{
|
||
if (qport->u.smodem.zdialer != NULL)
|
||
{
|
||
char *zcopy;
|
||
char *zdial;
|
||
struct sdialer sdialerinfo;
|
||
|
||
/* We use the first dialer in the sequence. */
|
||
zcopy = (char *) alloca (strlen (qport->u.smodem.zdialer)
|
||
+ 1);
|
||
strcpy (zcopy, qport->u.smodem.zdialer);
|
||
|
||
zdial = strtok (zcopy, " \t");
|
||
if (fread_dialer_info (zdial, &sdialerinfo))
|
||
{
|
||
cdial_proto_params = sdialerinfo.cproto_params;
|
||
qdial_proto_params = sdialerinfo.qproto_params;
|
||
idial_reliable = sdialerinfo.ireliable;
|
||
}
|
||
}
|
||
else if (qport->u.smodem.qdialer != NULL)
|
||
{
|
||
cdial_proto_params = qport->u.smodem.qdialer->cproto_params;
|
||
qdial_proto_params = qport->u.smodem.qdialer->qproto_params;
|
||
idial_reliable = qport->u.smodem.qdialer->ireliable;
|
||
}
|
||
}
|
||
#if HAVE_TCP
|
||
else if (qport->ttype == PORTTYPE_TCP)
|
||
ftcp_port = TRUE;
|
||
#endif
|
||
}
|
||
|
||
/* If it's a TCP port, it's fully reliable. Even if HAVE_TCP is not
|
||
supported, zsysdep_port_name may be able to figure this out (not
|
||
on Unix, though). */
|
||
if (ftcp_port)
|
||
idial_reliable = (RELIABLE_SPECIFIED | RELIABLE_ENDTOEND
|
||
| RELIABLE_RELIABLE | RELIABLE_EIGHT);
|
||
|
||
/* We have to check to see whether some system uses this login name
|
||
to indicate a different local name. Obviously, this means that
|
||
any system which uses this login name must expect the alternate
|
||
system name. */
|
||
zuse_local = NULL;
|
||
if (fUnknown_ok)
|
||
{
|
||
for (qsys = &sUnknown; qsys != NULL; qsys = qsys->qalternate)
|
||
{
|
||
if (qsys->zlocalname != NULL
|
||
&& qsys->zcalled_login != NULL
|
||
&& strcmp (qsys->zcalled_login, zlogin) == 0)
|
||
{
|
||
zuse_local = qsys->zlocalname;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
if (zuse_local == NULL)
|
||
{
|
||
struct ssysteminfo *pas;
|
||
int isys, csystems;
|
||
|
||
zuse_local = zLocalname;
|
||
|
||
uread_all_system_info (&csystems, &pas);
|
||
for (isys = 0; isys < csystems; isys++)
|
||
{
|
||
for (qsys = &pas[isys]; qsys != NULL; qsys = qsys->qalternate)
|
||
{
|
||
if (qsys->zlocalname != NULL
|
||
&& qsys->zcalled_login != NULL
|
||
&& strcmp (qsys->zcalled_login, zlogin) == 0)
|
||
{
|
||
zuse_local = qsys->zlocalname;
|
||
break;
|
||
}
|
||
}
|
||
if (qsys != NULL)
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Tell the remote system who we are. */
|
||
zsend = (char *) alloca (strlen (zuse_local) + 10);
|
||
sprintf (zsend, "Shere=%s", zuse_local);
|
||
if (! fsend_uucp_cmd (zsend))
|
||
return FALSE;
|
||
|
||
zstr = zget_uucp_cmd (TRUE);
|
||
if (zstr == NULL)
|
||
return FALSE;
|
||
|
||
if (zstr[0] != 'S')
|
||
{
|
||
ulog (LOG_ERROR, "Bad introduction string");
|
||
return FALSE;
|
||
}
|
||
++zstr;
|
||
|
||
zspace = strchr (zstr, ' ');
|
||
if (zspace != NULL)
|
||
*zspace = '\0';
|
||
if (fread_system_info (zstr, &ssys))
|
||
qsys = &ssys;
|
||
else
|
||
{
|
||
/* We have no information on this system. */
|
||
if (! fUnknown_ok)
|
||
{
|
||
(void) fsend_uucp_cmd ("RYou are unknown to me");
|
||
ulog (LOG_ERROR, "Call from unknown system %s", zstr);
|
||
return FALSE;
|
||
}
|
||
|
||
/* We have to translate the name to a canonical form for the
|
||
benefit of systems which only allow short system names. */
|
||
sUnknown.zname = ztranslate_system (zstr);
|
||
if (sUnknown.zname == NULL)
|
||
{
|
||
(void) fsend_uucp_cmd ("RYou are unknown to me");
|
||
return FALSE;
|
||
}
|
||
|
||
qsys = &sUnknown;
|
||
}
|
||
|
||
if (pqsys != NULL)
|
||
*pqsys = qsys;
|
||
|
||
if (! fcheck_validate (zlogin, qsys->zname))
|
||
{
|
||
(void) fsend_uucp_cmd ("RLOGIN");
|
||
ulog (LOG_ERROR, "System %s used wrong login name %s",
|
||
zstr, zlogin);
|
||
return FALSE;
|
||
}
|
||
|
||
if (qsys->zcalled_login != NULL)
|
||
{
|
||
const struct ssysteminfo *qany;
|
||
|
||
/* Choose an alternate system definition based on the
|
||
login name. */
|
||
qany = NULL;
|
||
for (; qsys != NULL; qsys = qsys->qalternate)
|
||
{
|
||
if (qsys->zcalled_login != NULL)
|
||
{
|
||
if (qany == NULL
|
||
&& strcmp (qsys->zcalled_login, "ANY") == 0)
|
||
qany = qsys;
|
||
else if (strcmp (qsys->zcalled_login, zlogin) == 0)
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (qsys == NULL)
|
||
{
|
||
if (qany == NULL)
|
||
{
|
||
(void) fsend_uucp_cmd ("RLOGIN");
|
||
ulog (LOG_ERROR, "System %s used wrong login name %s",
|
||
zstr, zlogin);
|
||
return FALSE;
|
||
}
|
||
qsys = qany;
|
||
}
|
||
}
|
||
|
||
ulog_system (qsys->zname);
|
||
|
||
#if DEBUG > 1
|
||
iDebug |= qsys->idebug;
|
||
#endif
|
||
|
||
/* See if we are supposed to call the system back. This will queue
|
||
up an empty command. It would be better to actually call back
|
||
directly at this point as well. */
|
||
if (qsys->fcallback)
|
||
{
|
||
(void) fsend_uucp_cmd ("RCB");
|
||
ulog (LOG_NORMAL, "Will call back");
|
||
(void) zsysdep_spool_commands (qsys, BGRADE_HIGH, 0,
|
||
(const struct scmd *) NULL);
|
||
return TRUE;
|
||
}
|
||
|
||
/* We only permit one call at a time from a remote system. Lock it. */
|
||
if (! fsysdep_lock_system (qsys))
|
||
{
|
||
(void) fsend_uucp_cmd ("RLCK");
|
||
ulog (LOG_ERROR, "System already locked");
|
||
return FALSE;
|
||
}
|
||
sLocked_system = *qsys;
|
||
fLocked_system = TRUE;
|
||
|
||
/* Set the system status. We don't really care if we can't get the
|
||
earlier status. We also don't want to kill the conversation just
|
||
because we can't output the .Status file, so we ignore any
|
||
errors. */
|
||
if (! fsysdep_get_status (qsys, &sstat))
|
||
{
|
||
sstat.cretries = 0;
|
||
sstat.cwait = 0;
|
||
}
|
||
|
||
sstat.ttype = STATUS_TALKING;
|
||
sstat.ilast = isysdep_time ((long *) NULL);
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
|
||
/* Check the arguments of the remote system. We accept -x# to set
|
||
our debugging level and -Q# for a sequence number. We may insist
|
||
on a sequence number. The -p and -vgrade= arguments are taken to
|
||
specify the lowest job grade that we should transfer; I think
|
||
this is the traditional meaning, but I don't know. The -N switch
|
||
means that we are talking to another instance of ourselves. The
|
||
-U switch specifies the ulimit of the remote system, which we
|
||
treat as the maximum file size that may be sent. The -R switch
|
||
means that the remote system supports file restart; we don't. */
|
||
|
||
fnew = FALSE;
|
||
bgrade = BGRADE_LOW;
|
||
cmax_receive = (long) -1;
|
||
frestart = FALSE;
|
||
|
||
if (zspace == NULL)
|
||
{
|
||
if (qsys->fsequence)
|
||
{
|
||
(void) fsend_uucp_cmd ("RBADSEQ");
|
||
ulog (LOG_ERROR, "No sequence number (call rejected)");
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
++zspace;
|
||
while (isspace (BUCHAR (*zspace)))
|
||
++zspace;
|
||
|
||
while (*zspace != '\0')
|
||
{
|
||
boolean frecognized;
|
||
char *znext;
|
||
|
||
frecognized = FALSE;
|
||
if (*zspace == '-')
|
||
{
|
||
switch (zspace[1])
|
||
{
|
||
case 'x':
|
||
frecognized = TRUE;
|
||
#if DEBUG > 1
|
||
{
|
||
int iwant;
|
||
|
||
iwant = atoi (zspace + 2);
|
||
if (! fnew)
|
||
iwant = (1 << iwant) - 1;
|
||
iwant &= qsys->imax_remote_debug;
|
||
if ((iDebug | iwant) != iDebug)
|
||
{
|
||
iDebug |= iwant;
|
||
ulog (LOG_NORMAL, "Setting debugging mode to 0%o",
|
||
iDebug);
|
||
}
|
||
}
|
||
#endif
|
||
break;
|
||
case 'Q':
|
||
frecognized = TRUE;
|
||
{
|
||
long iseq;
|
||
|
||
if (! qsys->fsequence)
|
||
break;
|
||
iseq = atol (zspace + 2);
|
||
if (iseq != isysdep_get_sequence (qsys))
|
||
{
|
||
(void) fsend_uucp_cmd ("RBADSEQ");
|
||
ulog (LOG_ERROR, "Out of sequence call rejected");
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
}
|
||
break;
|
||
case 'p':
|
||
/* We don't accept a space between the -p and the
|
||
grade, although we should. */
|
||
frecognized = TRUE;
|
||
if (FGRADE_LEGAL (zspace[2]))
|
||
bgrade = zspace[2];
|
||
break;
|
||
case 'v':
|
||
if (strncmp (zspace + 1, "vgrade=",
|
||
sizeof "vgrade=" - 1) == 0)
|
||
{
|
||
frecognized = TRUE;
|
||
if (FGRADE_LEGAL (zspace[sizeof "vgrade="]))
|
||
bgrade = zspace[sizeof "vgrade="];
|
||
}
|
||
break;
|
||
case 'N':
|
||
frecognized = TRUE;
|
||
fnew = TRUE;
|
||
break;
|
||
case 'U':
|
||
frecognized = TRUE;
|
||
{
|
||
long c;
|
||
|
||
c = strtol (zspace + 2, (char **) NULL, 0);
|
||
if (c > 0)
|
||
cmax_receive = c * (long) 512;
|
||
}
|
||
break;
|
||
case 'R':
|
||
frecognized = TRUE;
|
||
frestart = TRUE;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
znext = zspace;
|
||
while (*znext != '\0' && ! isspace (BUCHAR (*znext)))
|
||
++znext;
|
||
|
||
if (! frecognized)
|
||
{
|
||
int clen;
|
||
char *zcopy;
|
||
|
||
/* We could just use %.*s for this, but it's probably
|
||
not portable. */
|
||
clen = znext - zspace;
|
||
zcopy = (char *) alloca (clen + 1);
|
||
strncpy (zcopy, zspace, clen);
|
||
zcopy[clen] = '\0';
|
||
ulog (LOG_NORMAL, "Unrecognized argument %s", zcopy);
|
||
}
|
||
|
||
zspace = znext;
|
||
while (isspace (BUCHAR (*zspace)))
|
||
++zspace;
|
||
}
|
||
}
|
||
|
||
/* We recognized the system, and the sequence number (if any) was
|
||
OK. Send an ROK, and send a list of protocols. If we got the -N
|
||
switch, send ROKN to confirm it. */
|
||
|
||
if (! fsend_uucp_cmd (fnew ? "ROKN" : "ROK"))
|
||
{
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
{
|
||
int i;
|
||
|
||
if (qsys->zprotocols != NULL ||
|
||
(qport != NULL && qport->zprotocols != NULL))
|
||
{
|
||
const char *zprotos;
|
||
|
||
if (qsys->zprotocols != NULL)
|
||
zprotos = qsys->zprotocols;
|
||
else
|
||
zprotos = qport->zprotocols;
|
||
zsend = (char *) alloca (strlen (zprotos) + 2);
|
||
sprintf (zsend, "P%s", zprotos);
|
||
}
|
||
else
|
||
{
|
||
char *zset;
|
||
int ir;
|
||
|
||
zsend = (char *) alloca (CPROTOCOLS + 2);
|
||
zset = zsend;
|
||
*zset++ = 'P';
|
||
|
||
/* If the system did not specify a list of protocols, we want
|
||
only protocols that match the known reliability of the
|
||
dialer and the port. If we have no information, we default
|
||
to a reliable eight bit connection. */
|
||
|
||
ir = 0;
|
||
if (qport != NULL
|
||
&& (qport->ireliable & RELIABLE_SPECIFIED) != 0)
|
||
ir = qport->ireliable;
|
||
if ((idial_reliable & RELIABLE_SPECIFIED) != 0)
|
||
{
|
||
if (ir != 0)
|
||
ir &= idial_reliable;
|
||
else
|
||
ir = idial_reliable;
|
||
}
|
||
if (ir == 0)
|
||
ir = RELIABLE_RELIABLE | RELIABLE_EIGHT | RELIABLE_SPECIFIED;
|
||
|
||
for (i = 0; i < CPROTOCOLS; i++)
|
||
{
|
||
int ipr;
|
||
|
||
ipr = asProtocols[i].ireliable;
|
||
if ((ipr & ir) != ipr)
|
||
continue;
|
||
*zset++ = asProtocols[i].bname;
|
||
}
|
||
*zset = '\0';
|
||
}
|
||
|
||
if (! fsend_uucp_cmd (zsend))
|
||
{
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
/* The master will now send back the selected protocol. */
|
||
zstr = zget_uucp_cmd (TRUE);
|
||
if (zstr == NULL)
|
||
{
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zstr[0] != 'U' || zstr[2] != '\0')
|
||
{
|
||
ulog (LOG_ERROR, "Bad protocol response string");
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
if (zstr[1] == 'N')
|
||
{
|
||
ulog (LOG_ERROR, "No supported protocol");
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
for (i = 0; i < CPROTOCOLS; i++)
|
||
if (asProtocols[i].bname == zstr[1])
|
||
break;
|
||
|
||
if (i >= CPROTOCOLS)
|
||
{
|
||
ulog (LOG_ERROR, "No supported protocol");
|
||
sstat.ttype = STATUS_FAILED;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
qProto = &asProtocols[i];
|
||
}
|
||
|
||
/* Run the chat script for when a call is received. */
|
||
|
||
if (! fchat (&qsys->scalled_chat, qsys, (const struct sdialer *) NULL,
|
||
(const char *) NULL, FALSE, zport, iport_baud ()))
|
||
{
|
||
sstat.ttype = STATUS_FAILED;
|
||
sstat.ilast = isysdep_time ((long *) NULL);
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Run any protocol parameter commands. There should be a way to
|
||
read the dialer information if there is any to permit modem
|
||
specific protocol parameters, but for now there isn't. */
|
||
|
||
if (qProto->qcmds != NULL)
|
||
{
|
||
if (qsys->cproto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
qsys->cproto_params, qsys->qproto_params);
|
||
if (qport != NULL
|
||
&& qport->cproto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
qport->cproto_params, qport->qproto_params);
|
||
if (cdial_proto_params != 0)
|
||
uapply_proto_params (qProto->bname, qProto->qcmds,
|
||
cdial_proto_params, qdial_proto_params);
|
||
}
|
||
|
||
/* Turn on the selected protocol. */
|
||
|
||
if (! (*qProto->pfstart)(FALSE))
|
||
{
|
||
sstat.ttype = STATUS_FAILED;
|
||
sstat.ilast = isysdep_time ((long *) NULL);
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return FALSE;
|
||
}
|
||
|
||
/* If we using HAVE_BNU_LOGGING, then the previous ``incoming call''
|
||
message went to the general log, since we didn't know the system
|
||
name at that point. In that case, we repeat the port and login
|
||
names. */
|
||
#if HAVE_BNU_LOGGING
|
||
if (bgrade == BGRADE_LOW)
|
||
ulog (LOG_NORMAL, "Handshake successful (login %s port %s)",
|
||
zlogin == NULL ? "unknown" : zlogin,
|
||
zLdevice == NULL ? "unknown" : zLdevice);
|
||
else
|
||
ulog (LOG_NORMAL, "Handshake successful (login %s port %s grade %c)",
|
||
zlogin == NULL ? "unknown" : zlogin,
|
||
zLdevice == NULL ? "unknown" : zLdevice,
|
||
bgrade);
|
||
#else /* ! HAVE_BNU_LOGGING */
|
||
if (bgrade == BGRADE_LOW)
|
||
ulog (LOG_NORMAL, "Handshake successful");
|
||
else
|
||
ulog (LOG_NORMAL, "Handshake successful (grade %c)", bgrade);
|
||
#endif /* ! HAVE_BNU_LOGGING */
|
||
|
||
{
|
||
boolean fret;
|
||
long iend_time;
|
||
|
||
fret = fuucp (FALSE, qsys, bgrade, fnew, cmax_receive);
|
||
ulog_user ((const char *) NULL);
|
||
usysdep_get_work_free (qsys);
|
||
|
||
/* If we bombed out due to an error, shut down the protocol. */
|
||
if (! fret)
|
||
{
|
||
(void) (*qProto->pfshutdown) ();
|
||
ustats_failed ();
|
||
}
|
||
|
||
/* Hangup. As the answerer, we send seven O's and expect to see
|
||
six. */
|
||
if (fsend_uucp_cmd ("OOOOOOO")
|
||
&& fsend_uucp_cmd ("OOOOOOO"))
|
||
{
|
||
/* We don't even look for the hangup string from the other
|
||
side unless we're in debugging mode. */
|
||
#if DEBUG > 1
|
||
if (fret && FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
zstr = zget_uucp_cmd (FALSE);
|
||
if (zstr != NULL)
|
||
{
|
||
if (strstr (zstr, "OOOOOO") == NULL)
|
||
ulog (LOG_DEBUG, "No hangup from remote");
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
|
||
iend_time = isysdep_time ((long *) NULL);
|
||
|
||
ulog (LOG_NORMAL, "Call complete (%ld seconds)",
|
||
iend_time - istart_time);
|
||
if (fret)
|
||
sstat.ttype = STATUS_COMPLETE;
|
||
else
|
||
sstat.ttype = STATUS_FAILED;
|
||
sstat.ilast = iend_time;
|
||
(void) fsysdep_set_status (qsys, &sstat);
|
||
return fret;
|
||
}
|
||
}
|
||
|
||
/* This function runs the main UUCP protocol. It is called when the
|
||
two systems have succesfully connected. It transfers files back
|
||
and forth until neither system has any more work to do. The
|
||
traditional UUCP protocol has a master which sends files to the
|
||
slave or requests files from the slave (a single file is requested
|
||
with the R command; a wildcarded file name is requested with the X
|
||
command). The slave simply obeys the commands of the master. When
|
||
the master has done all its work, it requests a hangup. If the
|
||
slave has work to do it refuses the hangup and becomes the new
|
||
master.
|
||
|
||
This is essentially a half-duplex connection, in that files are
|
||
only transferred in one direction at a time. This is not
|
||
unreasonable, since generally one site is receiving a lot of news
|
||
from the other site, and I believe that Telebit modems are
|
||
basically half-duplex in that it takes a comparatively long time to
|
||
turn the line around. However, it is possible to design a
|
||
full-duplex protocol which would be useful in some situtations when
|
||
using V.32 (or a network) and this function attempts to support
|
||
this possibility.
|
||
|
||
Traditionally the work to be done is kept in a set of files whose
|
||
names begin with C.[system][grade][pid], where system is the remote
|
||
system name, grade is the grade of transfer, and pid makes the file
|
||
name unique. Each line in these files is a command, and each line
|
||
can be treated independently. We let the system dependent layer
|
||
handle all of this. This will let us use some other scheme on
|
||
systems in which the fourteen character filename length limit
|
||
restricts the name of the remote system to seven characters (the
|
||
usual restriction cited is six characters; I do not yet know where
|
||
this comes from).
|
||
|
||
Here are the types of commands, along with the definitions of the
|
||
variables they use in the fuucp function.
|
||
|
||
'S' -- Send a file from master to slave.
|
||
zfrom -- master file name
|
||
zto -- slave file name
|
||
zuser -- user who requested the transfer
|
||
zoptions -- list of options
|
||
ztemp -- temporary file name on master (used unless option c)
|
||
imode -- mode to give file
|
||
znotify -- user to notify (if option n)
|
||
|
||
The options are:
|
||
C -- file copied to spool (use ztemp rather than zfrom)
|
||
c -- file not copied to spool (use zfrom rather than ztemp)
|
||
d -- create directories if necessary
|
||
f -- do not create directories
|
||
m -- notify originator (in zuser) when complete
|
||
n -- notify recipient (in znotify) when complete
|
||
|
||
I assume that the n option is implemented by the remote system.
|
||
|
||
'R' -- Retrieve a file from slave to master.
|
||
zfrom -- slave file name
|
||
zto -- master file name
|
||
zuser -- user who requested the transfer
|
||
zoptions -- list of options
|
||
|
||
The options are the same as in case 'S', except that option n is
|
||
not supported. If zto is a directory, we must create a file in
|
||
that directory using the last component of zfrom.
|
||
|
||
'X' -- Execute wildcard transfer from slave to master.
|
||
zfrom -- wildcard file name
|
||
zto -- local file (hopefully a directory)
|
||
zuser -- user who requested the transfer
|
||
zoptions -- list of options
|
||
|
||
The options are presumably the same as in case 'R'. It may be
|
||
permissible to have no zuser or zoptions. The zto name will have
|
||
local! prepended to it already (where local is the local system
|
||
name).
|
||
|
||
This command is merely sent over to the remote system, where it
|
||
is executed. When the remote system becomes the master, it sends
|
||
the files back.
|
||
|
||
'H' -- Hangup
|
||
This is used by the master to indicate a transfer of control. If
|
||
slave has nothing to do, it responds with HY and the conversation
|
||
is finished. Otherwise, the slave becomes the master, and
|
||
vice-versa. */
|
||
|
||
static boolean
|
||
fuucp (fmaster, qsys, bgrade, fnew, cmax_receive)
|
||
boolean fmaster;
|
||
const struct ssysteminfo *qsys;
|
||
int bgrade;
|
||
boolean fnew;
|
||
long cmax_receive;
|
||
{
|
||
boolean fcaller, fmasterdone, fnowork;
|
||
const char *zlocal_size, *zremote_size;
|
||
long clocal_size, cremote_size, cmax_ever;
|
||
|
||
fcaller = fmaster;
|
||
|
||
fmasterdone = FALSE;
|
||
if (! qProto->ffullduplex && ! fmaster)
|
||
fmasterdone = TRUE;
|
||
|
||
/* Make sure we have a spool directory for this system. */
|
||
if (! fsysdep_make_spool_dir (qsys))
|
||
return FALSE;
|
||
|
||
/* If we are not the caller, the grade will be passed in as an
|
||
argument. If we are the caller, we compute the grade in this
|
||
function so that we can recompute if time has passed. */
|
||
|
||
if (fcaller)
|
||
bgrade = btimegrade (qsys->ztime);
|
||
|
||
if (bgrade == '\0')
|
||
fnowork = TRUE;
|
||
else
|
||
{
|
||
if (! fsysdep_get_work_init (qsys, bgrade))
|
||
return FALSE;
|
||
fnowork = FALSE;
|
||
}
|
||
|
||
/* Determine the maximum sizes we can send and receive. */
|
||
|
||
if (fcaller)
|
||
{
|
||
zlocal_size = qsys->zcall_local_size;
|
||
zremote_size = qsys->zcall_remote_size;
|
||
}
|
||
else
|
||
{
|
||
zlocal_size = qsys->zcalled_local_size;
|
||
zremote_size = qsys->zcalled_remote_size;
|
||
}
|
||
|
||
clocal_size = cmax_size_now (zlocal_size);
|
||
cremote_size = cmax_size_now (zremote_size);
|
||
cmax_ever = (long) -2;
|
||
|
||
/* Loop while we have local commands to execute and while we receive
|
||
remote commands. */
|
||
|
||
while (TRUE)
|
||
{
|
||
#if ! HAVE_ALLOCA
|
||
/* This only works if we know that no caller of this function is
|
||
holding an alloca'ed pointer. */
|
||
(void) alloca (0);
|
||
#endif
|
||
|
||
#if DEBUG > 1
|
||
/* If we're doing any debugging, close the log and debugging
|
||
files regularly. This will let people copy them off and
|
||
remove them while the conversation is in progresss. */
|
||
if (iDebug != 0)
|
||
{
|
||
ulog_close ();
|
||
ustats_close ();
|
||
}
|
||
#endif
|
||
|
||
/* We send a command to the remote system if
|
||
we are the master or
|
||
this is full duplex protocol which is ready for a command and
|
||
we haven't finished executing commands. */
|
||
if (fmaster ||
|
||
(qProto->ffullduplex && ! fmasterdone))
|
||
{
|
||
struct scmd s;
|
||
const char *zmail, *zuse;
|
||
boolean fspool, fnever;
|
||
openfile_t e = EFILECLOSED;
|
||
boolean fgone;
|
||
|
||
/* Get the next work line for this system. All the arguments
|
||
are left pointing into a static buffer, so they must be
|
||
copied out before the next call. */
|
||
ulog_user ((const char *) NULL);
|
||
if (fnowork)
|
||
s.bcmd = 'H';
|
||
else
|
||
{
|
||
s.zuser = NULL;
|
||
if (! fsysdep_get_work (qsys, bgrade, &s))
|
||
return FALSE;
|
||
ulog_user (s.zuser);
|
||
}
|
||
|
||
switch (s.bcmd)
|
||
{
|
||
case 'S':
|
||
/* Send a file. */
|
||
|
||
fspool = fspool_file (s.zfrom);
|
||
|
||
if (! fspool)
|
||
{
|
||
zuse = zsysdep_real_file_name (qsys, s.zfrom,
|
||
(const char *) NULL);
|
||
if (zuse == NULL)
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot form file name",
|
||
s.zfrom, zLocalname,
|
||
s.zto, qsys->zname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
/* The 'C' option means that the file has been
|
||
copied to the spool directory. */
|
||
if (strchr (s.zoptions, 'C') != NULL)
|
||
fspool = TRUE;
|
||
|
||
if (! fok_to_send (zuse, TRUE, fcaller, fspool, qsys,
|
||
s.zuser))
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to send %s", zuse);
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"not permitted to send",
|
||
s.zfrom, zLocalname,
|
||
s.zto, qsys->zname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
/* If we're copying the real file, use its mode
|
||
directly rather than the mode copied into the
|
||
command file. */
|
||
if (! fspool)
|
||
e = esysdep_open_send (qsys, zuse, TRUE, s.zuser,
|
||
&s.imode, &s.cbytes, &fgone);
|
||
}
|
||
|
||
if (fspool)
|
||
{
|
||
unsigned int idummy;
|
||
|
||
zuse = zsysdep_spool_file_name (qsys, s.ztemp);
|
||
if (zuse == NULL)
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot form file name",
|
||
s.zfrom, zLocalname,
|
||
s.zto, qsys->zname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
e = esysdep_open_send (qsys, zuse, FALSE,
|
||
(const char *) NULL, &idummy,
|
||
&s.cbytes, &fgone);
|
||
}
|
||
|
||
if (! ffileisopen (e))
|
||
{
|
||
/* If the file does not exist, fgone will be set to
|
||
TRUE. In this case we might have sent the file
|
||
the last time we talked to the remote system,
|
||
because we might have been interrupted in the
|
||
middle of a command file. To avoid confusion, we
|
||
don't send a mail message. */
|
||
if (! fgone)
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot open file",
|
||
s.zfrom, zLocalname,
|
||
s.zto, qsys->zname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
if (s.cbytes != -1)
|
||
{
|
||
boolean fsmall;
|
||
const char *zerr;
|
||
|
||
fsmall = FALSE;
|
||
fnever = FALSE;
|
||
zerr = NULL;
|
||
|
||
if (cmax_receive != -1 && cmax_receive < s.cbytes)
|
||
{
|
||
fsmall = TRUE;
|
||
fnever = TRUE;
|
||
zerr = "too large for receiver";
|
||
}
|
||
else if (clocal_size != -1 && clocal_size < s.cbytes)
|
||
{
|
||
fsmall = TRUE;
|
||
|
||
if (cmax_ever == -2)
|
||
{
|
||
long c1, c2;
|
||
|
||
c1 = cmax_size_ever (qsys->zcall_local_size);
|
||
c2 = cmax_size_ever (qsys->zcalled_local_size);
|
||
if (c1 > c2)
|
||
cmax_ever = c1;
|
||
else
|
||
cmax_ever = c2;
|
||
}
|
||
|
||
if (cmax_ever == -1 || cmax_ever >= s.cbytes)
|
||
zerr = "too large to send now";
|
||
else
|
||
{
|
||
fnever = TRUE;
|
||
zerr = "too large to send";
|
||
}
|
||
}
|
||
|
||
if (fsmall)
|
||
{
|
||
ulog (LOG_ERROR, "File %s is %s", s.zfrom, zerr);
|
||
|
||
if (fnever)
|
||
{
|
||
const char *zsaved;
|
||
|
||
zsaved = zsysdep_save_temp_file (s.pseq);
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
zerr,
|
||
s.zfrom, zLocalname,
|
||
s.zto, qsys->zname,
|
||
zsaved);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
}
|
||
|
||
(void) ffileclose (e);
|
||
break;
|
||
}
|
||
}
|
||
|
||
ulog (LOG_NORMAL, "Sending %s", s.zfrom);
|
||
|
||
/* The send file function is responsible for notifying
|
||
the user upon success (if option m) or failure, and
|
||
for closing the file. This allows it to not complete
|
||
immediately. */
|
||
if (strchr (s.zoptions, 'm') == NULL)
|
||
zmail = NULL;
|
||
else
|
||
zmail = s.zuser;
|
||
|
||
if (! fsend_file (TRUE, e, &s, zmail, qsys->zname, fnew))
|
||
return FALSE;
|
||
|
||
break;
|
||
|
||
case 'R':
|
||
/* Receive a file. */
|
||
|
||
if (fspool_file (s.zto))
|
||
{
|
||
/* Normal users are not allowed to receive files in
|
||
the spool directory, and to make it particularly
|
||
difficult we require a special option '9'. This
|
||
is used only by uux when a file must be requested
|
||
from one system and then sent to another. */
|
||
if (s.zto[0] != 'D'
|
||
|| strchr (s.zoptions, '9') == NULL)
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to receive %s",
|
||
s.zto);
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"not permitted to receive",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
zuse = zsysdep_spool_file_name (qsys, s.zto);
|
||
if (zuse == NULL)
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot form file name",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
zuse = zsysdep_real_file_name (qsys, s.zto, s.zfrom);
|
||
if (zuse == NULL)
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot form file name",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
/* Check permissions. */
|
||
if (! fok_to_receive (zuse, TRUE, fcaller, qsys, s.zuser))
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to receive %s",
|
||
s.zto);
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"not permitted to receive",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
/* The 'f' option means that directories should not
|
||
be created if they do not already exist. */
|
||
if (strchr (s.zoptions, 'f') == NULL)
|
||
{
|
||
if (! fsysdep_make_dirs (zuse, TRUE))
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot create directories",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
e = esysdep_open_receive (qsys, zuse, &s.ztemp, &s.cbytes);
|
||
if (! ffileisopen (e))
|
||
{
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"cannot open file",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
}
|
||
|
||
/* Here s.cbytes represents the amount of free space we
|
||
have. We want to adjust it by the amount of free
|
||
space permitted for this system. If there is a
|
||
maximum transfer size, we may want to use that as an
|
||
amount of free space. */
|
||
if (s.cbytes != -1)
|
||
{
|
||
s.cbytes -= qsys->cfree_space;
|
||
if (s.cbytes < 0)
|
||
s.cbytes = 0;
|
||
}
|
||
|
||
if (clocal_size != -1
|
||
&& (s.cbytes == -1 || clocal_size < s.cbytes))
|
||
s.cbytes = clocal_size;
|
||
|
||
ulog (LOG_NORMAL, "Receiving %s", zuse);
|
||
s.zto = zuse;
|
||
|
||
/* As with the send routine, this function is
|
||
responsible for mailing a message to the user on
|
||
failure or on success if the m option is set, and is
|
||
also responsible for closing the file. */
|
||
if (strchr (s.zoptions, 'm') == NULL)
|
||
zmail = NULL;
|
||
else
|
||
zmail = s.zuser;
|
||
|
||
/* The imode argument (passed as 0666) will be corrected
|
||
with information from the remote system. */
|
||
s.imode = 0666;
|
||
|
||
if (! freceive_file (TRUE, e, &s, zmail, qsys->zname, fnew))
|
||
return FALSE;
|
||
|
||
break;
|
||
|
||
case 'X':
|
||
/* Request a file copy. This is used to request a file
|
||
to be sent to another machine, as well as to get a
|
||
wildcarded filespec. */
|
||
ulog (LOG_NORMAL, "Requesting work: %s to %s", s.zfrom,
|
||
s.zto);
|
||
|
||
if (! fxcmd (&s, &fnever))
|
||
return FALSE;
|
||
|
||
if (fnever)
|
||
(void) fmail_transfer (FALSE, s.zuser,
|
||
(const char *) NULL,
|
||
"wildcard request denied",
|
||
s.zfrom, qsys->zname,
|
||
s.zto, zLocalname,
|
||
(const char *) NULL);
|
||
(void) fsysdep_did_work (s.pseq);
|
||
break;
|
||
|
||
case 'H':
|
||
/* There is nothing left to do; hang up. If we are not the
|
||
master, take no action (this allows for two-way
|
||
protocols. */
|
||
|
||
fmasterdone = TRUE;
|
||
if (fmaster)
|
||
{
|
||
if (! fhangup_request ())
|
||
{
|
||
ulog (LOG_ERROR, "Hangup failed");
|
||
return FALSE;
|
||
}
|
||
|
||
fmaster = FALSE;
|
||
|
||
/* Close the log file at every master/slave switch.
|
||
This will cut down on the amount of time we have
|
||
an old log file open. */
|
||
ulog_close ();
|
||
ustats_close ();
|
||
}
|
||
break;
|
||
|
||
default:
|
||
ulog (LOG_ERROR, "Unknown command '%c'", s.bcmd);
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* We look for a command from the other system if we are the
|
||
slave or this is a full-duplex protocol and the slave still
|
||
has work to do. */
|
||
if (! fmaster || qProto->ffullduplex)
|
||
{
|
||
struct scmd s;
|
||
const char *zuse, *zmail;
|
||
openfile_t e;
|
||
char bhave_grade;
|
||
long cbytes;
|
||
|
||
/* We are the slave. Get the next command from the other
|
||
system. */
|
||
ulog_user ((const char *) NULL);
|
||
if (! fgetcmd (fmaster, &s))
|
||
return FALSE;
|
||
|
||
if (s.bcmd != 'H' && s.bcmd != 'Y')
|
||
ulog_user (s.zuser);
|
||
|
||
switch (s.bcmd)
|
||
{
|
||
case 'S':
|
||
/* The master wants to send a file to us. */
|
||
|
||
if (fspool_file (s.zto))
|
||
{
|
||
zuse = zsysdep_spool_file_name (qsys, s.zto);
|
||
/* We don't accept remote command files. */
|
||
if (zuse == NULL || s.zto[0] == 'C')
|
||
{
|
||
if (! ftransfer_fail ('S', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
zuse = zsysdep_real_file_name (qsys, s.zto, s.zfrom);
|
||
if (zuse == NULL)
|
||
{
|
||
if (! ftransfer_fail ('S', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
/* Check permissions. */
|
||
if (! fok_to_receive (zuse, FALSE, fcaller, qsys,
|
||
s.zuser))
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to receive %s", zuse);
|
||
if (! ftransfer_fail ('S', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
if (strchr (s.zoptions, 'f') == NULL)
|
||
{
|
||
if (! fsysdep_make_dirs (zuse, TRUE))
|
||
{
|
||
if (! ftransfer_fail ('S', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
e = esysdep_open_receive (qsys, zuse, &s.ztemp, &cbytes);
|
||
if (! ffileisopen (e))
|
||
{
|
||
if (! ftransfer_fail ('S', FAILURE_OPEN))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
/* Adjust the number of bytes we are prepared to receive
|
||
according to the amount of free space we are supposed
|
||
to leave available and the maximum file size we are
|
||
permitted to transfer. */
|
||
if (cbytes != -1)
|
||
{
|
||
cbytes -= qsys->cfree_space;
|
||
if (cbytes < 0)
|
||
cbytes = 0;
|
||
}
|
||
|
||
if (cremote_size != -1
|
||
&& (cbytes == -1 || cremote_size < cbytes))
|
||
cbytes = cremote_size;
|
||
|
||
/* If the number of bytes we are prepared to receive
|
||
is less than the file size, we must fail. */
|
||
|
||
if (s.cbytes != -1
|
||
&& cbytes != -1
|
||
&& cbytes < s.cbytes)
|
||
{
|
||
ulog (LOG_ERROR, "%s is too big to receive", zuse);
|
||
(void) ffileclose (e);
|
||
(void) remove (s.ztemp);
|
||
if (! ftransfer_fail ('S', FAILURE_SIZE))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
ulog (LOG_NORMAL, "Receiving %s", zuse);
|
||
s.zto = zuse;
|
||
|
||
if (strchr (s.zoptions, 'n') == NULL)
|
||
zmail = NULL;
|
||
else
|
||
zmail = s.znotify;
|
||
s.pseq = NULL;
|
||
if (! freceive_file (FALSE, e, &s, zmail, qsys->zname, fnew))
|
||
return FALSE;
|
||
|
||
break;
|
||
|
||
case 'R':
|
||
/* The master wants to get a file from us. */
|
||
|
||
if (fspool_file (s.zfrom))
|
||
{
|
||
ulog (LOG_ERROR, "No permission to send %s", s.zfrom);
|
||
if (! ftransfer_fail ('R', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
zuse = zsysdep_real_file_name (qsys, s.zfrom,
|
||
(const char *) NULL);
|
||
if (zuse == NULL)
|
||
{
|
||
if (! ftransfer_fail ('R', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
if (! fok_to_send (zuse, FALSE, fcaller, FALSE, qsys,
|
||
s.zuser))
|
||
{
|
||
ulog (LOG_ERROR, "No permission to send %s", zuse);
|
||
if (! ftransfer_fail ('R', FAILURE_PERM))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
e = esysdep_open_send (qsys, zuse, TRUE, (const char *) NULL,
|
||
&s.imode, &cbytes, (boolean *) NULL);
|
||
if (! ffileisopen (e))
|
||
{
|
||
if (! ftransfer_fail ('R', FAILURE_OPEN))
|
||
return FALSE;
|
||
break;
|
||
}
|
||
|
||
/* If the file is larger than the amount of space
|
||
the other side reported, we can't send it. */
|
||
if (cbytes != -1
|
||
&& ((s.cbytes != -1 && s.cbytes < cbytes)
|
||
|| (cremote_size != -1 && cremote_size < cbytes)
|
||
|| (cmax_receive != -1 && cmax_receive < cbytes)))
|
||
{
|
||
ulog (LOG_ERROR, "%s is too large to send", zuse);
|
||
if (! ftransfer_fail ('R', FAILURE_SIZE))
|
||
return FALSE;
|
||
(void) ffileclose (e);
|
||
break;
|
||
}
|
||
|
||
ulog (LOG_NORMAL, "Sending %s", zuse);
|
||
|
||
/* Pass in the real size of the file. */
|
||
s.cbytes = cbytes;
|
||
|
||
if (! fsend_file (FALSE, e, &s, (const char *) NULL,
|
||
qsys->zname, fnew))
|
||
return FALSE;
|
||
|
||
break;
|
||
|
||
case 'X':
|
||
/* This is an execution request. We are being asked to
|
||
send one or more files to a destination on either the
|
||
local or a remote system. We do this by spooling up
|
||
commands for the destination system. */
|
||
ulog (LOG_NORMAL, "Work requested: %s to %s", s.zfrom,
|
||
s.zto);
|
||
|
||
if (fdo_xcmd (qsys, fcaller, &s))
|
||
{
|
||
if (! fxcmd_confirm ())
|
||
return FALSE;
|
||
}
|
||
else
|
||
{
|
||
if (! ftransfer_fail ('X', FAILURE_PERM))
|
||
return FALSE;
|
||
}
|
||
|
||
break;
|
||
|
||
case 'H':
|
||
/* The master wants to hang up. If we have something to
|
||
do, become the master. Otherwise, agree to hang up.
|
||
We recheck the grades allowed at this time, since a
|
||
lot of time may have passed. */
|
||
if (fcaller)
|
||
bgrade = btimegrade (qsys->ztime);
|
||
if (bgrade != '\0'
|
||
&& fsysdep_has_work (qsys, &bhave_grade)
|
||
&& igradecmp (bgrade, bhave_grade) >= 0)
|
||
{
|
||
if (fmasterdone)
|
||
{
|
||
if (! fsysdep_get_work_init (qsys, bgrade))
|
||
return FALSE;
|
||
fnowork = FALSE;
|
||
}
|
||
|
||
fmasterdone = FALSE;
|
||
|
||
if (! fhangup_reply (FALSE))
|
||
return FALSE;
|
||
fmaster = TRUE;
|
||
|
||
/* Recalculate the maximum sizes we can send, since
|
||
the time might have changed significantly. */
|
||
clocal_size = cmax_size_now (zlocal_size);
|
||
cremote_size = cmax_size_now (zremote_size);
|
||
|
||
/* Close the log file at every switch of master and
|
||
slave. */
|
||
ulog_close ();
|
||
ustats_close ();
|
||
}
|
||
else
|
||
{
|
||
/* The hangup_reply function will shut down the
|
||
protocol. */
|
||
return fhangup_reply (TRUE);
|
||
}
|
||
break;
|
||
|
||
case 'Y':
|
||
/* This is returned when a hangup has been confirmed and
|
||
the protocol has been shut down. */
|
||
return TRUE;
|
||
|
||
default:
|
||
ulog (LOG_ERROR, "Unknown command %c", s.bcmd);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Do an 'X' request for another system. The other system has
|
||
basically requested us to execute a uucp command for them. */
|
||
|
||
static boolean
|
||
fdo_xcmd (qsys, fcaller, q)
|
||
const struct ssysteminfo *qsys;
|
||
boolean fcaller;
|
||
const struct scmd *q;
|
||
{
|
||
const char *zexclam;
|
||
const char *zdestfile;
|
||
char *zcopy;
|
||
struct ssysteminfo sdestsys;
|
||
const struct ssysteminfo *qdestsys;
|
||
char *zuser = NULL;
|
||
char aboptions[5];
|
||
char *zoptions = NULL;
|
||
boolean fmkdirs;
|
||
const char *zfile;
|
||
|
||
zexclam = strchr (q->zto, '!');
|
||
if (zexclam == NULL
|
||
|| zexclam == q->zto
|
||
|| strncmp (zLocalname, q->zto, zexclam - q->zto) == 0)
|
||
{
|
||
/* The files are supposed to be copied to the
|
||
local system. */
|
||
qdestsys = NULL;
|
||
if (zexclam == NULL)
|
||
zdestfile = q->zto;
|
||
else
|
||
zdestfile = zexclam + 1;
|
||
}
|
||
else
|
||
{
|
||
int clen;
|
||
|
||
clen = zexclam - q->zto;
|
||
zcopy = (char *) alloca (clen + 1);
|
||
strncpy (zcopy, q->zto, clen);
|
||
zcopy[clen] = '\0';
|
||
|
||
if (! fread_system_info (zcopy, &sdestsys))
|
||
{
|
||
if (! fUnknown_ok)
|
||
{
|
||
ulog (LOG_ERROR, "Destination system %s unknown",
|
||
zcopy);
|
||
return FALSE;
|
||
}
|
||
sdestsys = sUnknown;
|
||
sdestsys.zname = zcopy;
|
||
}
|
||
qdestsys = &sdestsys;
|
||
zdestfile = zexclam + 1;
|
||
}
|
||
|
||
if (qdestsys != NULL)
|
||
{
|
||
zuser = (char *) alloca (strlen (qdestsys->zname)
|
||
+ strlen (q->zuser) + sizeof "!");
|
||
sprintf (zuser, "%s!%s", qdestsys->zname,
|
||
q->zuser);
|
||
zoptions = aboptions;
|
||
*zoptions++ = 'C';
|
||
if (strchr (q->zoptions, 'd') != NULL)
|
||
*zoptions++ = 'd';
|
||
if (strchr (q->zoptions, 'm') != NULL)
|
||
*zoptions++ = 'm';
|
||
*zoptions = '\0';
|
||
fmkdirs = TRUE;
|
||
}
|
||
else
|
||
fmkdirs = strchr (q->zoptions, 'f') != NULL;
|
||
|
||
/* Now we have to process each source file. The
|
||
source specification may or may use wildcards. */
|
||
if (! fsysdep_wildcard_start (qsys, q->zfrom))
|
||
return FALSE;
|
||
|
||
while ((zfile = zsysdep_wildcard (qsys, q->zfrom)) != NULL)
|
||
{
|
||
const char *zsend;
|
||
const char *zto;
|
||
char abtname[CFILE_NAME_LEN];
|
||
|
||
zcopy = (char *) alloca (strlen (zfile) + 1);
|
||
strcpy (zcopy, zfile);
|
||
zfile = zcopy;
|
||
|
||
/* Make sure the remote system is permitted to read the
|
||
specified file. */
|
||
zsend = qsys->zremote_send;
|
||
if (! fcaller && qsys->zcalled_remote_send != NULL)
|
||
zsend = qsys->zcalled_remote_send;
|
||
|
||
if (! fin_directory_list (qsys, zfile, zsend, TRUE, TRUE,
|
||
(const char *) NULL))
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to send %s", zfile);
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
|
||
if (qdestsys != NULL)
|
||
{
|
||
/* We really should get the original grade here. */
|
||
zto = zsysdep_data_file_name (qdestsys, BDEFAULT_UUCP_GRADE,
|
||
abtname, (char *) NULL,
|
||
(char *) NULL);
|
||
}
|
||
else
|
||
{
|
||
const char *zrec;
|
||
|
||
zto = zsysdep_real_file_name (qsys, zexclam + 1, zfile);
|
||
if (zto == NULL)
|
||
{
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
/* We only accept a local destination if the remote system
|
||
has the right to create files there. */
|
||
zrec = qsys->zremote_receive;
|
||
if (! fcaller && qsys->zcalled_remote_receive != NULL)
|
||
zrec = qsys->zcalled_remote_receive;
|
||
|
||
if (! fin_directory_list (qsys, zto, zrec, TRUE, FALSE,
|
||
(const char *) NULL))
|
||
{
|
||
ulog (LOG_ERROR, "Not permitted to receive %s", zto);
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
/* Copy the file either to the final destination or to the
|
||
spool directory. */
|
||
if (! fcopy_file (zfile, zto, qdestsys == NULL, fmkdirs))
|
||
{
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
|
||
/* If there is a destination system, queue it up. */
|
||
if (qdestsys != NULL)
|
||
{
|
||
struct scmd ssend;
|
||
|
||
ssend.bcmd = 'S';
|
||
ssend.pseq = NULL;
|
||
ssend.zfrom = zfile;
|
||
ssend.zto = zdestfile;
|
||
ssend.zuser = zuser;
|
||
ssend.zoptions = aboptions;
|
||
ssend.ztemp = abtname;
|
||
ssend.imode = isysdep_file_mode (zfile);
|
||
if (ssend.imode == 0)
|
||
{
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
ssend.znotify = "";
|
||
ssend.cbytes = -1;
|
||
|
||
if (zsysdep_spool_commands (qdestsys, BDEFAULT_UUCP_GRADE,
|
||
1, &ssend) == NULL)
|
||
{
|
||
(void) fsysdep_wildcard_end ();
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (! fsysdep_wildcard_end ())
|
||
return FALSE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
/* See whether it's OK to send a file to another system, according to
|
||
the permissions recorded for that system. If the file is not in
|
||
the spool directory, this also makes sure that the user has
|
||
permission to access the file and all its containing directories.
|
||
|
||
zfile -- file to send
|
||
flocal -- TRUE if the send was requested locally
|
||
fcaller -- TRUE if the local system called the other system
|
||
fspool -- TRUE if file was copied to spool directory
|
||
qsys -- remote system information
|
||
zuser -- user who requested the action */
|
||
|
||
static boolean
|
||
fok_to_send (zfile, flocal, fcaller, fspool, qsys, zuser)
|
||
const char *zfile;
|
||
boolean flocal;
|
||
boolean fcaller;
|
||
boolean fspool;
|
||
const struct ssysteminfo *qsys;
|
||
const char *zuser;
|
||
{
|
||
const char *z;
|
||
|
||
if (! frequest_ok (flocal, fcaller, qsys, zuser))
|
||
return FALSE;
|
||
|
||
if (flocal)
|
||
{
|
||
z = qsys->zlocal_send;
|
||
if (! fcaller && qsys->zcalled_local_send != NULL)
|
||
z = qsys->zcalled_local_send;
|
||
}
|
||
else
|
||
{
|
||
z = qsys->zremote_send;
|
||
if (! fcaller && qsys->zcalled_remote_send != NULL)
|
||
z = qsys->zcalled_remote_send;
|
||
}
|
||
|
||
/* If fspool is TRUE, we don't want to check file accessibility. If
|
||
this was not a local request, we pass a NULL down as the user
|
||
name, since zuser has no meaning on this system. */
|
||
return fin_directory_list (qsys, zfile, z, ! fspool, TRUE,
|
||
flocal ? zuser : (const char *) NULL);
|
||
}
|
||
|
||
/* See whether it's OK to receive a file from another system. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
fok_to_receive (zto, flocal, fcaller, qsys, zuser)
|
||
const char *zto;
|
||
boolean flocal;
|
||
boolean fcaller;
|
||
const struct ssysteminfo *qsys;
|
||
const char *zuser;
|
||
{
|
||
const char *z;
|
||
|
||
if (! frequest_ok (flocal, fcaller, qsys, zuser))
|
||
return FALSE;
|
||
|
||
if (flocal)
|
||
{
|
||
z = qsys->zlocal_receive;
|
||
if (! fcaller && qsys->zcalled_local_receive != NULL)
|
||
z = qsys->zcalled_local_receive;
|
||
}
|
||
else
|
||
{
|
||
z = qsys->zremote_receive;
|
||
if (! fcaller && qsys->zcalled_remote_receive != NULL)
|
||
z = qsys->zcalled_remote_receive;
|
||
}
|
||
|
||
return fin_directory_list (qsys, zto, z, TRUE, FALSE,
|
||
flocal ? zuser : (const char *) NULL);
|
||
}
|
||
|
||
/* See whether a request is OK. This depends on which system placed
|
||
the call and which system made the request. */
|
||
|
||
/*ARGSUSED*/
|
||
static boolean
|
||
frequest_ok (flocal, fcaller, qsys, zuser)
|
||
boolean flocal;
|
||
boolean fcaller;
|
||
const struct ssysteminfo *qsys;
|
||
const char *zuser;
|
||
{
|
||
if (flocal)
|
||
{
|
||
if (fcaller)
|
||
return qsys->fcall_transfer;
|
||
else
|
||
return qsys->fcalled_transfer;
|
||
}
|
||
else
|
||
{
|
||
if (fcaller)
|
||
return qsys->fcall_request;
|
||
else
|
||
return qsys->fcalled_request;
|
||
}
|
||
}
|
||
|
||
/* Send a string to the other system beginning with a DLE
|
||
character and terminated with a null byte. This is only
|
||
used when no protocol is in force. */
|
||
|
||
static boolean
|
||
fsend_uucp_cmd (z)
|
||
const char *z;
|
||
{
|
||
char *zalc;
|
||
int cwrite;
|
||
|
||
cwrite = strlen (z) + 2;
|
||
|
||
zalc = (char *) alloca (cwrite);
|
||
sprintf (zalc, "\020%s", z);
|
||
|
||
return fport_write (zalc, cwrite);
|
||
}
|
||
|
||
/* Get a UUCP command beginning with a DLE character and ending with a
|
||
null byte. This is only used when no protocol is in force. This
|
||
implementation has the potential of being seriously slow. It also
|
||
doesn't have any real error recovery. The frequired argument is
|
||
passed as TRUE if we need the string; we don't care that much if
|
||
we're closing down the connection anyhow. */
|
||
|
||
#define CTIMEOUT (120)
|
||
#define CSHORTTIMEOUT (10)
|
||
#define CINCREMENT (10)
|
||
|
||
static const char *
|
||
zget_uucp_cmd (frequired)
|
||
boolean frequired;
|
||
{
|
||
static char *zalc;
|
||
static int calc;
|
||
int cgot;
|
||
long iendtime;
|
||
int ctimeout;
|
||
#if DEBUG > 1
|
||
int cchars;
|
||
int iolddebug;
|
||
#endif
|
||
|
||
iendtime = isysdep_time ((long *) NULL);
|
||
if (frequired)
|
||
iendtime += CTIMEOUT;
|
||
else
|
||
iendtime += CSHORTTIMEOUT;
|
||
|
||
#if DEBUG > 1
|
||
cchars = 0;
|
||
iolddebug = iDebug;
|
||
if (FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \"");
|
||
iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
|
||
}
|
||
#endif
|
||
|
||
cgot = -1;
|
||
while ((ctimeout = (int) (iendtime - isysdep_time ((long *) NULL))) > 0)
|
||
{
|
||
int b;
|
||
|
||
b = breceive_char (ctimeout, frequired);
|
||
/* Now b == -1 on timeout, -2 on error. */
|
||
if (b < 0)
|
||
{
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
ulog (LOG_DEBUG_END, "\" (%s)",
|
||
b == -1 ? "timeout" : "error");
|
||
iDebug = iolddebug;
|
||
}
|
||
#endif
|
||
if (b == -1 && frequired)
|
||
ulog (LOG_ERROR, "Timeout");
|
||
return NULL;
|
||
}
|
||
|
||
/* Apparently some systems use parity on these strings, so we
|
||
strip the parity bit. This may need to be configurable at
|
||
some point, although only if system names can have eight bit
|
||
characters. */
|
||
if (! isprint (BUCHAR (b)))
|
||
b &= 0x7f;
|
||
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
char ab[5];
|
||
|
||
++cchars;
|
||
if (cchars > 60)
|
||
{
|
||
ulog (LOG_DEBUG_END, "\"");
|
||
ulog (LOG_DEBUG_START, "zget_uucp_cmd: Got \"");
|
||
cchars = 0;
|
||
}
|
||
(void) cdebug_char (ab, b);
|
||
ulog (LOG_DEBUG_CONTINUE, "%s", ab);
|
||
}
|
||
#endif
|
||
|
||
if (cgot < 0)
|
||
{
|
||
if (b != '\020')
|
||
continue;
|
||
cgot = 0;
|
||
continue;
|
||
}
|
||
|
||
/* If we see another DLE, something has gone wrong; continue
|
||
as though this were the first one we saw. */
|
||
if (b == '\020')
|
||
{
|
||
cgot = 0;
|
||
continue;
|
||
}
|
||
|
||
/* Some systems send a trailing \n on the Shere line. As far as
|
||
I can tell this line can never contain a \n, so this
|
||
modification should be safe enough. */
|
||
if (b == '\r' || b == '\n')
|
||
b = '\0';
|
||
|
||
if (cgot >= calc)
|
||
{
|
||
calc += CINCREMENT;
|
||
zalc = (char *) xrealloc ((pointer) zalc, calc);
|
||
}
|
||
|
||
zalc[cgot] = (char) b;
|
||
++cgot;
|
||
|
||
if (b == '\0')
|
||
{
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
ulog (LOG_DEBUG_END, "\"");
|
||
iDebug = iolddebug;
|
||
}
|
||
#endif
|
||
return zalc;
|
||
}
|
||
}
|
||
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_HANDSHAKE))
|
||
{
|
||
ulog (LOG_DEBUG_END, "\" (timeout)");
|
||
iDebug = iolddebug;
|
||
}
|
||
#endif
|
||
|
||
if (frequired)
|
||
ulog (LOG_ERROR, "Timeout");
|
||
return NULL;
|
||
}
|
||
|
||
/* Read a sequence of characters up to a newline or carriage return, and
|
||
return the line without the line terminating character. */
|
||
|
||
static const char *
|
||
zget_typed_line ()
|
||
{
|
||
static char *zalc;
|
||
static int calc;
|
||
int cgot;
|
||
|
||
#if DEBUG > 1
|
||
int cchars;
|
||
int iolddebug;
|
||
|
||
cchars = 0;
|
||
iolddebug = iDebug;
|
||
if (FDEBUGGING (DEBUG_CHAT))
|
||
{
|
||
ulog (LOG_DEBUG_START, "zget_typed_line: Got \"");
|
||
iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
|
||
}
|
||
#endif
|
||
|
||
cgot = 0;
|
||
while (TRUE)
|
||
{
|
||
int b;
|
||
|
||
b = breceive_char (CTIMEOUT, FALSE);
|
||
|
||
/* Now b == -1 on timeout, -2 on error. */
|
||
|
||
if (b == -2 || FGOT_SIGNAL ())
|
||
{
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_CHAT))
|
||
{
|
||
ulog (LOG_DEBUG_END, "\" (error)");
|
||
iDebug = iolddebug;
|
||
}
|
||
#endif
|
||
return NULL;
|
||
}
|
||
|
||
if (b == -1)
|
||
continue;
|
||
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_CHAT))
|
||
{
|
||
char ab[5];
|
||
|
||
++cchars;
|
||
if (cchars > 60)
|
||
{
|
||
ulog (LOG_DEBUG_END, "\"");
|
||
ulog (LOG_DEBUG_START, "zget_typed_line: Got \"");
|
||
cchars = 0;
|
||
}
|
||
(void) cdebug_char (ab, b);
|
||
ulog (LOG_DEBUG_CONTINUE, "%s", ab);
|
||
}
|
||
#endif
|
||
|
||
if (cgot >= calc)
|
||
{
|
||
calc += CINCREMENT;
|
||
zalc = (char *) xrealloc ((pointer) zalc, calc);
|
||
}
|
||
|
||
if (b == '\r' || b == '\n')
|
||
b = '\0';
|
||
|
||
zalc[cgot] = (char) b;
|
||
++cgot;
|
||
|
||
if (b == '\0')
|
||
{
|
||
#if DEBUG > 1
|
||
if (FDEBUGGING (DEBUG_CHAT))
|
||
{
|
||
ulog (LOG_DEBUG_END, "\"");
|
||
iDebug = iolddebug;
|
||
}
|
||
#endif
|
||
return zalc;
|
||
}
|
||
}
|
||
}
|