#ifndef STANDALONE
#include "global.h"
#include "ctype.h"
#ifdef MSDOS
#include <dir.h>
#include <dos.h>
#else
#include <time.h>
#endif
#ifndef _lint
#define SKIP_HACK 1		/* a quick hack to get around a compiler warning */
#endif
#include "commands.h"
#include "files.h"
#include "mailbox.h"
#include "usock.h"
#include "pktdrvr.h"
#include "color.h"
#include "x.h"
#ifdef SQL
#include "sql.h"
#endif
#ifndef MSDOS
#include "ftp.h"
#include "session.h"
#endif
#endif


#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: tutor.c,v 1.25 1997/08/19 01:19:22 root Exp $";
#endif

struct world {
	int mode;
	char color;
	char colorblock;
	char last[2];
	char inescape;
	char ipconnect;
	char *subdir;
	char const *call;
	time_t starttime;
	int ischild;
#ifndef STANDALONE
	struct mbx *m;
#endif
	FILE *fp;
	FILE *out;
	char *Tutors[100];
	int maxselect;
	char *variables[10];
	long indexes[10];
#ifdef old
	int socket[10];
#else
	int *socket;
#endif
	int user;
	int whichsocket;
	int background;
	int goback;
	int lostconnection;
	char Error[80];
};



#ifndef STANDALONE
int Tutored = 0;
static int inMbScriptHook = 0;
extern int Stutor, Sinfo, Snews, Tutorlogins[];
extern char Tcall[AXALEN], Icall[AXALEN], Ncall[AXALEN];
#endif

extern int sockblock (int s, int value);

#ifdef TUTOR
static void mycolorchange (struct world *here, const char *input, char *last);
static void mytprintf (struct world *here, const char *str);

#ifndef STANDALONE
static void mygets (char *buf, int size, struct world *here);
#else
void mygets (char *buf, int size, struct world *here);
#endif

#ifdef SQL
static void expand_buf (char *buf, struct world *here);
#endif

static char *nextarg (char *cp, long *i);
static char *nextnum (char *cp, int defval, struct world *here, long *i);
static long getnum (char *cp, int defval, struct world *here);
static int scriptconn (int argc, char *argv[], void *p);
static char *getnext (char *cp, char **put, struct world *here);
static void process (struct world *here);
static void buildmenu (struct world *here);

#ifndef STANDALONE
void tutorserv (const char *call, struct mbx *m, int mode, int color, int ip);
#else
void tutorserv (const char *call, int mode, int color, int ip);
#endif

static int scriptcmd (struct world *inherited, FILE * fp, const char *name);

#ifndef STANDALONE
int dombscript (int argc, char *argv[], void *p);
int proxy (FILE * fp, const char *from);
#endif
#endif /* TUTOR */


#ifdef TUTOR

static void
mycolorchange (struct world *here, const char *input, char *last)
{
	if (here->color)
		(void) colorchange (input, last);
}



static void
mytprintf (struct world *here, const char *str)
{
	if (here->background && !here->whichsocket)
		return;
	if (!here->colorblock)
		tprintf (str);
	else if (colorprintf (&here->inescape, here->color, (const unsigned char *) str))
		here->last[0] = 0;
}
#endif



char *
skipwhite (char IFLINT(const) *cp)
{
	while (*cp && (*cp == ' ' || *cp == '\t' || *cp == '\n'))
		cp++;
	return (cp);		/*lint !e605 */
}



char *
skipnonwhite (char IFLINT(const) *cp)
{
	while (*cp && *cp != ' ' && *cp != '\t' && *cp != '\n')
		cp++;
	return (cp);		/*lint !e605 */
}



void
trimright (char *cp)
{
	while (cp[strlen (cp) - 1] == ' ')
		cp[strlen (cp) - 1] = 0;
}



void
trimrightCR (char *cp)
{
	rip (cp);
	trimright (cp);
	strcat (cp, "\n");
}



#ifdef TUTOR
#ifndef STANDALONE
static int tutor_recvline (int s, char *buf, unsigned size);


static int
tutor_recvline (int s, char *buf, unsigned size)
{
char *origbuf = buf;
int c, cnt = 0, opt, cl;

	usflush (Curproc->output);

	for ( ; ; ) {
		if (nullsocket (s)) {
			c = EOF;
			cnt = 0;
			break;
		}
		c = recvchar (s);
		if (c == EOF) {
			if (errno == EWOULDBLOCK)
				continue;
			break;
		}
		kwait (NULL);
		if (c == IAC) {	/* Telnet command escape */
			if ((c = recvchar (s)) == EOF)
				break;
			if (c >= 250 && c < 255 && (opt = recvchar (s)) != EOF) {
				switch (c) {
					case SB:
						opt = recvchar (s);
						if (opt == EOF)
							break;
						cl = opt;
						c = recvchar (s);
						while ((c != EOF) && !(cl == IAC && c == SE)) {
							cl = c;
							c = recvchar (s);
						}
						break;
					case WILL:
						if (opt == TN_LINEMODE) {
							tprintf ("%c%c%c", IAC, DO, opt);
							tprintf ("%c%c%c%c%c%c%c", IAC, SB, TN_LINEMODE, 1, 1, IAC, SE);
						} else
							tprintf ("%c%c%c", IAC, DONT, opt);
						break;
					case WONT:
						tprintf ("%c%c%c", IAC, DONT, opt);
						break;
					case DO:
						tprintf ("%c%c%c", IAC, WONT, opt);
						break;
					case DONT:
						tprintf ("%c%c%c", IAC, WONT, opt);
						break;
					default:
						break;
				}
				usflush (Curproc->output);
				kwait (NULL);
				continue;
			}
			if (c != IAC && (c = recvchar (s)) == EOF)
				break;
		}
		*buf++ = (char) c;
		++cnt;
		if (c == '\n')
			break;
		if ((unsigned) cnt == (size - 1)) {
			cnt = -2;
			break;
		}
		/* the following is a special case HACK for telnet logins */
		if (!strncmp (origbuf, "login: ", 7) || !strncmp (origbuf, "Password: ", 10))
			break;
	}
	if (c == EOF && cnt == 0)
		return -1;
	*buf = '\0';
	return cnt;
}



static void
mygets (char *buf, int size, struct world *here)
{
	if ((here->background && !here->whichsocket) || here->user < 0) {
		buf[0] = 0;
		return;
	}
	usflush (Curproc->output);
#ifdef MBXTDISC
	/* Restart the inactivity timer */
	if (here->m != NULLMBX)
		start_timer (&here->m->tdisc);
#endif


	if ((size = tutor_recvline (here->user ? here->user : Curproc->input, buf, (unsigned) size)) < 0) {
		if (here->user > 0) {
			here->socket[here->whichsocket] = here->user = -1;
			Curproc->output = here->socket[0];
		}
		buf[0] = 0;
		here->lostconnection = -1;
	}
	rip (buf);
}
#endif /* STANDALONE */



static char *
nextarg (char *cp, long *i)
{
	cp = skipnonwhite (cp);
	cp = skipwhite (cp);
	*i = atol (cp);
	return (cp);
}



static char *
nextnum (char *cp, int defval, struct world *here, long *i)
{
	cp = skipnonwhite (cp);
	cp = skipwhite (cp);
	*i = getnum (cp, defval, here);
	return (cp);
}



static long
getnum (char *cp, int defval, struct world *here)
{
long l = defval;

	switch (*cp) {
		case '~':
			if (tolower (cp[1]) == 'i') {
				l = here->indexes[cp[2] - '0'];
				break;
			}
			if (cp[1] >= '0' && cp[1] <= '9') {
				l = atol (here->variables[cp[1] - '0']);
				break;
			}
			break;
		case 0:
			break;
		default:
			l = atol (cp);
			break;
	}
	return l;
}



static char SCONFail[] = "SCRIPT connect failed: ";
static char SCONFail2[] = "SCRIPT disconnect failed: ";


/* open a network connection based upon information in the cc line.
 * m->user is set to the socket number.
 */
static int
scriptconn (int argc, char *argv[], void *p)
{
struct world *here;
char sock[MAXSOCKSIZE];
union sp sp;
char ctype;
struct iface *ifp;
char digis[MAXDIGIS][AXALEN];
char target[AXALEN];
int ndigis, i;

	here = (struct world *) p;
	sp.p = sock;
	here->Error[0] = 0;	/* null error string */
	ctype = (char) tolower (*argv[0]);
	if (argc < 2) {
		sprintf (here->Error, "%sSyntax Error", SCONFail);
		return -1;
	}
	switch (ctype) {
#ifdef AX25
		case 'a':
		case 'c':	/* allow 'c' for 'connect' as well */
			if (((ifp = if_lookup (argv[1])) == NULLIF) || (ifp->flags & HIDE_PORT)) {
				sprintf (here->Error, "%sUnknown port %s", SCONFail, argv[1]);
				return -1;
			}
			if (ifp->type != CL_AX25) {
				sprintf (here->Error, "%sPort %s not usable for AX.25 connects", SCONFail, argv[1]);
				return -1;
			}
			if (setcall (target, argv[2]) == -1) {
				sprintf (here->Error, "%sBad call %s", SCONFail, argv[2]);
				return -1;
			}
			/* If digipeaters are given, put them in the routing table */
			if (argc > 3) {
				ndigis = argc - 3;
				if (ndigis > MAXDIGIS) {
					sprintf (here->Error, "%sToo many digipeaters", SCONFail);
					return -1;
				}
				for (i = 0; i < ndigis; i++) {
					if (setcall (digis[i], argv[i + 3]) == -1) {
						sprintf (here->Error, "%sBad digipeater call %s", SCONFail, argv[i + 3]);
						return -1;
					}
				}
				if (ax_add (target, AX_AUTO, digis, ndigis, ifp) == NULLAXR) {
					sprintf (here->Error, "%sAX25 route add failed", SCONFail);
					return -1;
				}
			}
			sp.ax->sax_family = AF_AX25;
			strncpy (sp.ax->iface, argv[1], ILEN - 1);	/* the interface name */
			(void) setcall (sp.ax->ax25_addr, argv[2]);	/* the remote callsign */
			if ((here->user = socket (AF_AX25, SOCK_STREAM, 0)) == -1) {
				sprintf (here->Error, "%sError allocating socket", SCONFail);
				return -1;
			}
			break;
#endif
		default:
			sp.in->sin_family = AF_INET;
			if ((sp.in->sin_addr.s_addr = resolve (argv[1])) == 0) {
				sprintf (here->Error, "%sHost Unknown", SCONFail);
				return -1;
			}
			/* get the optional port number */
			if (argc > 2)
				sp.in->sin_port = (int16) atoip (argv[2]);
			else
				sp.in->sin_port = IPPORT_TELNET;
			if ((here->user = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
				sprintf (here->Error, "%sError allocating socket", SCONFail);
				return -1;
			}
	}

	(void) sockmode (here->user, SOCK_ASCII);

	if (connect (here->user, sp.p, SOCKSIZE) == -1) {
		sprintf (here->Error, "SCRIPT connect failed: %s errno %d",
			 sockerr (here->user), errno);
		close_s (here->user);
		return -1;
	}
	return here->user;
}


static struct cmds sconcmds[] =
{
	{ "tcp",	scriptconn, 0, 0, NULLCHAR },
	{ "telnet",	scriptconn, 0, 0, NULLCHAR },
#ifdef AX25
	{ "ax25",	scriptconn, 0, 0, NULLCHAR },
	{ "connect",	scriptconn, 0, 0, NULLCHAR },
#endif
	{ NULLCHAR,	NULL,	    0, 0, NULLCHAR }
};



static char *
getnext (char *cp, char **put, struct world *here)
{
char *retval = cp, buf[4], c;
time_t tm;

	buf[1] = 0;
	if (*cp != '~') {
		buf[0] = *cp;
		*put = strdup (buf);
	} else {
		cp++;
		retval++;
		c = (char) tolower (*cp);
		(void) time (&tm);
		cp = ctime (&tm);
		rip (cp);
		cp[10] = cp[19] = 0;
		switch (c) {
			case 'l':	/* elapsed time of script */
				*put = (char *) mallocw (20);
				sprintf (*put, "%-ld", (tm - here->starttime));
				break;
			case 'p':	/* current data file position */
				*put = (char *) mallocw (20);
				if (here->out)
					sprintf (*put, "%-ld", ftell (here->out));
				else
					strcpy (*put, "-1");
				break;
			case 'c':
				*put = strdup (here->call);
				break;
			case 'e':
				*put = strdup (here->Error);
				break;
			case '~':
			case 'b':
				buf[0] = (c == 'b') ? '\007' : *retval;
				*put = strdup (buf);
				break;
			case 'h':
				*put = strdup (Hostname);
				break;
			case 'd':
				strcat (cp, ", ");
				strcat (cp, &cp[20]);
				*put = strdup (cp);
				break;
			case 't':
				*put = strdup (&cp[11]);
				break;
			case 'n':
				buf[0] = '\n';
				*put = strdup (buf);
				break;
			case 'u':	/* un-terminate this line */
				rip (retval);
				buf[0] = *(++retval);
				*put = strdup (buf);
				break;
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (here->variables[c - '0'])
					*put = strdup (here->variables[c - '0']);
				else
					*put = strdup ("");
				break;
			case 'i':
				if (retval[1] >= '0' && retval[1] <= '9') {
					*put = (char *) mallocw (20);
					sprintf (*put, "%-ld", here->indexes[*(++retval) - '0']);
					break;
				}
				/* else fall through */
			default:
				sprintf (buf, "~%c", *retval);
				*put = strdup (buf);
				break;
		}
	}
	return (retval);
}



#ifdef SQL
static void
expand_buf (char *buf, struct world *here)
{
char *cp, *put;
char newbuf[256];

	cp = buf;
	newbuf[0] = 0;
	while (*cp) {
		cp = getnext (cp, &put, here);
		strcat (newbuf, put);
		free (put);
		cp++;
	}
	strcpy (buf, newbuf);
}
#endif



static void
process (struct world *here)
{
FILE *fp;
int done = 0, skipping = 0, k, disconexit = 1;
long j, i, l;
char buf[256], c, *cp, lastresponse = 'y', *label = 0;
char *put, *replyto;
long pos;

	(void) time (&here->starttime);
	if (!here->ischild) {
#if 0
		for (k = 0; k < 10; k++) {
			here->variables[k] = 0;
			here->indexes[k] = 1;
			here->socket[k] = 0;
		}
#endif
	} else {
		here->user = 0;
		disconexit = 0;
	}

	here->socket[0] = Curproc->output;
#if 0
	if (here->user) {	/* inherited a socket from a parent script! */
		Curproc->output = here->socket[1] = here->user;
		here->whichsocket = 1;

		Curproc->output = here->socket[here->whichsocket];
		disconexit = 0;
	}
#endif

	while (!done && fgets (buf, 256, here->fp)) {
#ifdef MBXTDISC
		/* Restart the inactivity timer */
		if (here->m != NULLMBX)
			start_timer (&here->m->tdisc);
#endif
		kwait (NULL);
		if (buf[0] != '~' || ((buf[1] == '~') || isdigit (buf[1]))) {
			if (!skipping) {
				cp = buf;
				while (*cp) {
					cp = getnext (cp, &put, here);
					mytprintf (here, put);
					free (put);
					cp++;
				}
			}
			continue;
		}
		c = (char) tolower (buf[1]);
		cp = skipwhite (&buf[2]);
		rip (cp);
		if (skipping && c != 'l')
			continue;
		k = *cp - '0';
		if (k > 9)
			k = 0;
		switch (c) {
			case 'i':	/* modify an index counter
					   index array in k, already */
				cp++;
				rip (cp);
				cp = skipwhite (cp);
				c = *cp++;
				cp = skipwhite (cp);
				if (!strnicmp (cp, "~p", 2)) {
					if (here->out)
						l = ftell (here->out);
					else
						l = -1;
				} else
					l = getnum (cp, 1, here);
				switch (c) {
					case '=':	/* assign */
						here->indexes[k] = l;
						break;
					case '+':	/* add */
						here->indexes[k] += l;
						break;
					case '-':	/* subtract */
						here->indexes[k] -= l;
						break;
					case '?':	/* compare */
						c = lastresponse = (here->indexes[k] == l) ? 'y' : 'n';
						cp = skipnonwhite (cp);
						cp = skipwhite (cp);
						if (*cp && c == 'y')
							goto another;
						break;
					default:
						break;
				}
				break;
			another: case 'y':	/* on yes, goto label *//*lint !e616 */
			case 'n':	/* on no, goto label */
				if (lastresponse != c)
					break;
			case 'g':	/* goto label *//*lint !e616 */
				skipping = 1;
				free (label);
				label = strdup (cp);
				rewind (here->fp);
				break;
			case 'l':	/* label line */
				if (skipping && !stricmp (cp, label)) {
					free (label);
					label = 0;
					skipping = 0;
				}
				break;
			case 'b':	/* output 'n' blank lines */
				k = (int) getnum (cp, 0, here);
				if (!k)
					k = 1;
				while (k--)
					mytprintf (here, "\n");
				break;
			case 'm':	/* more prompt */
				mytprintf (here, "\n---MORE (*y/n)---\n");
				mygets (buf, 256, here);
				if (here->lostconnection)
					goto lost;
				kwait (NULL);
				if (tolower (buf[0]) != 'n')
					break;
			case 'x':	/* exit at this point *//*lint !e616 */
				/*					done = 1;	*/
				skipping = 1;
				free (label);
				label = strdup ("exit");
				break;
			case 'q':	/* query with string for y/n answer */
				mytprintf (here, cp);
				mytprintf (here, " (*y/n)\n");
				mygets (buf, 256, here);
				if (here->lostconnection)
					goto lost;
				kwait (NULL);
				lastresponse = (tolower (buf[0]) == 'n') ? 'n' : 'y';
				break;
			case 'j':	/* just compare first characters */
			case 'c':	/* compare entire strings
					   first string value in k, already */
				cp = nextarg (cp, &l);
				if (!here->variables[l] || !here->variables[k])
					lastresponse = 'n';
				else {
					if (c == 'c')
						c = lastresponse = (stricmp (here->variables[l], here->variables[k])) ? 'n' : 'y';
					else {
						cp = nextnum (cp, 0, here, &j);
						c = lastresponse = (strnicmp (here->variables[l], here->variables[k], (unsigned) j)) ? 'n' : 'y';
					}
					cp = skipnonwhite (cp);
					cp = skipwhite (cp);
					if (*cp && c == 'y')
						goto another;
				}
				break;
			case 'h':	/* string has substring?
					   first string value in k, already */
				cp = nextarg (cp, &l);
				if (!here->variables[l] || !here->variables[k])
					lastresponse = 'n';
				else {
					i = (int) strstr (here->variables[k], here->variables[l]);
					c = lastresponse = (!i) ? 'n' : 'y';
					if (i)
						i -= (int) here->variables[k];
					cp = nextarg (cp, &j);
					here->indexes[j] = i;
					cp = skipnonwhite (cp);
					cp = skipwhite (cp);
					if (*cp && c == 'y')
						goto another;
				}
				break;
			case 'p':	/* pick out a sub-string
					   to variable already in k */
				cp = nextarg (cp, &j);	/* from variable */
				cp = nextnum (cp, 0, here, &i);	/* start index */
				cp = nextnum (cp, 0, here, &l);	/* length */
				if ((int) (strlen (here->variables[j]) - (unsigned long) i) < l)
					l = (long) (strlen (here->variables[j]) - (unsigned long) i);
				l++;	/* allow room for zero byte */
				free (here->variables[k]);
				here->variables[k] = (char *) mallocw ((unsigned) l);
				strncpy (here->variables[k], &here->variables[j][i], (unsigned) l);
				here->variables[k][l - 1] = 0;
				break;
			case 't':	/* truncate a variable string */
				cp = nextnum (cp, 0, here, &j);
				if ((int) strlen (here->variables[k]) > j)
					here->variables[k][j] = 0;
				break;
			case 'z':	/* get length of variable and place in index */
				cp = nextarg (cp, &j);
				here->indexes[j] = (long) ((here->variables[k]) ? strlen (here->variables[k]) : 0);
				break;
			case 'a':	/* assign a string to a variable */
				/* or append if 'ap str1 str2' */
				if ((cp == &buf[2]) && (tolower (*cp) == 'p')) {
					cp = skipwhite (++cp);
					if (*cp) {
						k = *cp - '0';
						cp = skipwhite (++cp);
						if (*cp) {
							l = *cp - '0';
							cp = (char *) mallocw (strlen (here->variables[k]) +
									      strlen (here->variables[l]) + 1);
							strcpy (cp, here->variables[k]);
							strcat (cp, here->variables[l]);
							free (here->variables[k]);
							here->variables[k] = cp;
						}
					}
					break;
				}	/* else fall through */
			case 'v':	/* prompt and place result in variable */
				free (here->variables[k]);
				cp++;
				cp = skipwhite (cp);
				if (c == 'a') {
					if (*cp == '~')
						(void) getnext (cp, &here->variables[k], here);
					else
						here->variables[k] = strdup (cp);
				} else {
					if (*cp) {
						mytprintf (here, cp);
						mytprintf (here, "\n");
					}
					mygets (buf, 256, here);
					if (here->lostconnection)
						goto lost;
					kwait (NULL);
					here->variables[k] = strdup (buf);
				}
				break;
			case 'd':	/* deliver a mail file */
				/* first check to see if this is a 'dr' command */
				replyto = NULLCHAR;
				if ((cp == &buf[2]) && (tolower (buf[2]) == 'r')) {
					/* this contains a reply-to string */
					cp = skipwhite (++cp);
					(void) getnext (cp, &replyto, here);	/* allow ~c and ~# subs */
					if (replyto == NULLCHAR || strlen (replyto) == 1) {
						char *cp2;

						free (replyto);
						replyto = strdup (cp);
						cp2 = strchr (replyto, ' ');
						if (cp2)
							*cp2++ = 0;
						cp = cp2;
					} else	/* point to 'to' user's parameter */
						cp = strchr (cp, ' ');
					cp = skipwhite (cp);
				}
				(void) getnext (cp, &put, here);	/* allow ~c and ~# subs */
				if (strlen (put) == 1) {
					char *cp2;

					free (put);
					put = strdup (cp);
					cp2 = strchr (put, ' ');
					if (cp2)
						*cp2++ = 0;
					cp = cp2;
				} else
					cp = strchr (cp, ' ');
				cp = skipwhite (cp);
				lastresponse = 'n';	/* default to fails */
				/* get filename and open it */
				if (cp) {	/* if filename given */
					FILE *out2;

					out2 = NULLFILE;
					if (stricmp (cp, "null"))
						out2 = fopen (cp, READ_TEXT);
					/* save current pos, and get tutor description */
					pos = ftell (here->fp);
					rewind (here->fp);
					(void) fgets (buf, 256, here->fp);
					fseek (here->fp, pos, SEEK_SET);
					cp = skipwhite (buf);
					rip (cp);
					/* now mail the file */
					(void) rdaemon (out2, replyto, NULLCHAR, put, cp, 'P', 0);
					lastresponse = 'y';
					if (out2 != NULLFILE)
						(void) fclose (out2);
				}
				free (put);
				break;

			case 'e':	/* is io file at end of file */
				if (here->out != NULLFILE) {
					c = lastresponse = (feof (here->out)) ? 'y' : 'n';
					if (*cp && c == 'y')
						goto another;
				}
				break;
			case 's':	/* seek to start of io file or sql query */
#ifdef SQL
				if (tolower (*cp) == 'q') {
					/* sql query */
					cp = skipnonwhite (cp);
					cp = skipwhite (cp);
					expand_buf (cp, here);
					usflush (Curproc->output);
					(void) sql_query (cp);
				} else
#endif
				if (here->out != NULLFILE) {
					switch (tolower (*cp)) {
						case 'e':
							fseek (here->out, 0L, 2);
							break;
						case 'p':
							cp = skipwhite (++cp);
							if (*cp) {
								k = *cp - '0';
								fseek (here->out, here->indexes[k], 0);
								break;
							}	/* else, fall through */
						default:
							rewind (here->out);
					}
				}
				break;
			case 'u':	/* upload a text file */
				if ((fp = fopen (cp, READ_TEXT)) != NULLFILE) {
#ifndef STANDALONE
					(void) sendfile (fp, Curproc->output, ASCII_TYPE, 0);
#else
					(void) sendfile (fp);
#endif
					(void) fclose (fp);
				}
				break;
			case 'o':	/* open an old io file */
			case 'f':	/* create a new io file */
				if (here->out != NULLFILE)
					(void) fclose (here->out);
				here->out = NULLFILE;
				if (*cp == '~' && isdigit (cp[1]))
					cp = here->variables[cp[1] - '0'];
				here->out = fopen (cp, (c == 'f') ? CREATE_TEXT : UPDATE_TEXT);
				lastresponse = (here->out == NULLFILE) ? 'n' : 'y';
				break;
			case 'w':	/* write a textline to the io file */
				if (here->out != NULLFILE)
					while (*cp) {
						cp = getnext (cp, &put, here);
						fprintf (here->out, put);
						free (put);
						cp++;
					}
				break;
			case 'r':	/* read a line from io file in var */
				if (here->out != NULLFILE) {
					free (here->variables[k]);
					(void) fgets (buf, 256, here->out);
					rip (buf);
					here->variables[k] = strdup (buf);
				}
				break;
			case 'k':	/* kill a file */
				unlink (cp);
				lastresponse = (access (cp, 0)) ? 'y' : 'n';
				break;
			case '?':
				switch (tolower (*cp)) {
					case 'c':
						lastresponse = (!here->color) ? 'n' : 'y';
						break;
					case 'i':
						lastresponse = (here->ipconnect) ? 'y' : 'n';
						break;
					default:
						break;
				}
				break;
			case '!':
				switch (tolower (*cp)) {
					case 'c':
						here->color = 1;
						break;
					default:
						break;
				}
				break;
			case '*':
				switch (tolower (*cp)) {
					case 'b':
						here->colorblock = 1;
						break;
					case 'e':
						here->colorblock = 0;
						break;
					default:
						break;
				}
				break;
			case '%':
				lastresponse = (!here->color) ? 'n' : 'y';
				if (here->color)
					colorfile (cp, here->last);
				break;
			case '@':
				mycolorchange (here, cp, here->last);
				break;
			case '$':{
					char *args[2];
					int lastback;

					lastback = here->goback;
					here->goback = 0;
					if (*cp == '$') {
						here->goback = 1;
						cp = skipwhite (++cp);
					}
					args[1] = cp;
					if (access (cp, 4))
						lastresponse = 'n';
					else {
						Curproc->output = here->socket[0];
						(void) doscript (0, args, here);
						lastresponse = 'y';
						Curproc->output = here->socket[0];
					}
					here->goback = lastback;
				}
				break;
			case ')':
				lastresponse = 'n';
				if (!k)
					sprintf (here->Error, "%scan't close stream 0", SCONFail2);
				else if (here->socket[k] <= 0)
					sprintf (here->Error, "%sstream %d not connected", SCONFail2, k);
				else {
					lastresponse = 'y';
					here->Error[0] = 0;
					if (socklen (here->socket[k], 0))	/* discard any remaining input */
						(void) recv_mbuf (here->socket[k], NULL, 0, NULLCHAR, 0);
					(void) shutdown (here->socket[k], 1);
					close_s (here->socket[k]);
					if (Curproc->output == here->socket[k]) {
						Curproc->output = here->socket[0];
						here->user = here->whichsocket = 0;
					}
					here->socket[k] = 0;
				}
				break;
			case '#':
				lastresponse = 'n';
				if (k && here->socket[k] < 0)
					sprintf (here->Error, "%sstream %d not connected", SCONFail, k);
				else {
					lastresponse = 'y';
					Curproc->output = here->socket[k];
					here->user = (k) ? here->socket[k] : 0;
					here->whichsocket = k;
				}
				break;
			case '(':
				lastresponse = 'n';
				if (here->socket[k])
					sprintf (here->Error, "%sstream %d already connected", SCONFail2, k);
				else {
					cp = skipnonwhite (cp);
					cp = skipwhite (cp);
					if ((here->socket[k] = cmdparse (sconcmds, cp, (void *) here)) != -1)
						lastresponse = 'y';
					else
						here->socket[k] = 0;
				}
				break;
			case '=':
				lastresponse = (here->socket[k] > 0) ? 'y' : 'n';
				break;
			case '^':	/* convert string variable to upper/lower case */
				cp++;
				cp = skipwhite (cp);
				if (*cp == 'l')
					(void) strlwr (here->variables[k]);
				else if (*cp == 'u')
					(void) strupr (here->variables[k]);
				break;
			default:
				break;
		}
	}
lost:
	if (here->out != NULLFILE) {
		(void) fclose (here->out);
		here->out = NULLFILE;
	}
	free (label);
	/*	Curproc->output = here->socket[0]; */
	for (k = 0; k < 10; k++) {
		if (here->variables[k])
			if (!here->ischild || k < 5)
				free (here->variables[k]);
#if 1
		if (disconexit && k && here->socket[k] > 0) {	/* free connected streams */
			if (socklen (here->socket[k], 0))	/* discard any remaining input */
				(void) recv_mbuf (here->socket[k], NULL, 0, NULLCHAR, 0);
			(void) shutdown (here->socket[k], 1);
			close_s (here->socket[k]);
			here->socket[k] = 0;
		}
#endif
	}
	here->user = 0;
	here->whichsocket = 0;
}



static const char *tutorialName[] = { "Learning Center", "Information Center", "News Center" };
static const char banner[] = "For %s, connect to '%s' using the same path...\n";



static void
buildmenu (struct world *here)
{
FILE *fp;
char buf[80], buf2[256], *cp, *cp2;
struct ffblk ff;

	here->maxselect = 0;
	if (here->subdir)
		tprintf ("Current Sub-directory: %s\n\n", here->subdir);
	mycolorchange (here, "0F", here->last);
	tprintf ("\nWelcome to the ");
	mycolorchange (here, "0C", here->last);
	tprintf ("TNOS %s ", tutorialName[here->mode]);
	mycolorchange (here, "0F", here->last);
	tprintf ("at ");
	mycolorchange (here, "05", here->last);
	tprintf ("%s\n\n     %c", Hostname, here->subdir ? 'E' : '0');
	mycolorchange (here, "07", here->last);
	tprintf (" -  Exit %s\n", tutorialName[here->mode]);
	mycolorchange (here, "05", here->last);
	tprintf ("     %c", (here->subdir) ? '0' : 'C');
	mycolorchange (here, "07", here->last);
	if (here->subdir)
		tputs (" -  Return to Previous Menu\n");
	else
		tprintf (" -  %sable ANSI Color Graphics\n", (here->color) ? "Dis" : "En");
	sprintf (buf, "%s/%s*.tut", (here->mode == 1) ? Info : (here->mode) ? News : Tutor, (here->subdir) ? here->subdir : "");
	if (findfirst (buf, &ff, 0) == 0) {
		do {
			kwait (NULL);	/* Let others run */
			cp2 = strrchr (ff.ff_name, '.');
			if (cp2)
				*cp2 = '\0';
			sprintf (buf2, "%s/%s%s.tut", (here->mode == 1) ? Info : (here->mode) ? News : Tutor, (here->subdir) ? here->subdir : "", ff.ff_name);
			if ((fp = fopen (buf2, READ_TEXT)) != NULLFILE) {
				do {
					(void) fgets (buf2, 256, fp);
					cp = skipwhite (buf2);
				} while (*cp == '\n');
				(void) fclose (fp);
				if (*cp == '~')	/* subdir directing file */
					cp = skipwhite (skipnonwhite (skipwhite (++cp)));
				here->Tutors[here->maxselect++] = strdup (ff.ff_name);
				mycolorchange (here, "05", here->last);
				tprintf ("    %2d", here->maxselect);
				mycolorchange (here, "07", here->last);
				tprintf (" -  %s", cp);
			}
			if (here->maxselect == 100)
				break;
		} while (findnext (&ff) == 0);
	}
}



#ifdef MSDOS
#define SUBDIRSIZE 8
#else
#define SUBDIRSIZE 256
#endif



void
#ifndef STANDALONE
tutorserv (const char *call, struct mbx *m, int mode, int color, int ip)
#else
tutorserv (char *call, int mode, int color, int ip)
#endif
{
char selection[SUBDIRSIZE + 2], filename[80], *cp;
int k, sel, inited = 0;
struct world here;
#ifndef STANDALONE
char tmp[AXBUF];

	Tutored++;
#ifdef XSERVER
	xnotify (X_TUT);
#endif
#ifdef STATS_USE
	STATS_adduse (1);
#endif
#endif
	here.mode = mode;
#ifndef STANDALONE
	Tutorlogins[here.mode]++;
#endif
	here.maxselect = 0;
	here.call = call;
#ifndef STANDALONE
	here.m = m;
#endif
	here.subdir = NULLCHAR;
#ifndef STANDALONE
	(void) sockblock (Curproc->output, SOCK_NOTXBLOCK);	/* prevent backlogs ! */
#endif
	here.color = (char) color;
	here.ipconnect = (char) ip;
	here.inescape = 0;
	here.ischild = 0;
	here.lostconnection = 0;
	here.Error[0] = 0;
	here.out = NULLFILE;
	here.whichsocket = here.background = 0;
	here.socket = callocw (10, sizeof (int));

	here.user = here.socket[0] = Curproc->output;
	filename[0] = 0;
	if (m && color)
		memcpy (here.last, m->colorset, 2);
	else
		here.last[0] = 0;
	for ( ; ; ) {
		if (inited)
			tprintf ("\n\n\n\n");
		else
			inited = 1;
		buildmenu (&here);
		do {
			mycolorchange (&here, "0F", here.last);
			tprintf ("\nEnter Selection:\n");
			mygets (selection, 10, &here);
			if (here.lostconnection)
				goto lost;
			kwait (NULL);
			if (toupper (*selection) == 'E') {
				free (here.subdir);
				here.subdir = NULLCHAR;
				sel = 0;
				break;
			}
			if (toupper (*selection) == 'C') {
				sel = -1;
				here.color ^= 1;
				break;
			}
			sel = atoi (selection);
			if (!sel)
				break;
			if (sel > here.maxselect)
				tprintf ("Invalid number... select 0 - %-d!\007\n", here.maxselect);
		} while (sel > here.maxselect);
		if (sel == -1)
			continue;
		if (sel)
			sprintf (filename, "%s/%s%s.tut", (here.mode == 1) ? Info : (here.mode) ? News : Tutor, (here.subdir) ? here.subdir : "", here.Tutors[sel - 1]);
		for (k = 0; k < here.maxselect; k++)
			free (here.Tutors[k]);
		if (!sel) {
			if (!here.subdir)	/* not in a sub-directory! */
				break;
			here.subdir[strlen (here.subdir) - 1] = 0;	/* take off last "/" */
			cp = strrchr (here.subdir, '/');
			if (!cp) {
				free (here.subdir);
				here.subdir = NULLCHAR;
			} else
				*(++cp) = 0;
			continue;
		}
		if (!*filename || (here.fp = fopen (filename, READ_TEXT)) == NULLFILE) {
			tprintf ("Sorry, but something seems to be wrong with that tutorial!\n");
			continue;
		}
		/* Let's check first line to see if this file describes a sub-directory */
		(void) fgets (filename, 80, here.fp);
		cp = filename;
		if (*cp == '~') {	/* Yep! Sub-directory time! */
			cp = skipwhite (++cp);
			for (k = 0; k < SUBDIRSIZE && *cp && cp[k] != '\t' && cp[k] != ' '; k++)
				selection[k] = cp[k];
			selection[k++] = '/';
			selection[k] = 0;
			k += (int) ((here.subdir) ? strlen (here.subdir) : 0);
			cp = (char *) mallocw ((unsigned) ++k);
			*cp = 0;
			if (here.subdir)
				strcpy (cp, here.subdir);
			strcat (cp, selection);
			free (here.subdir);
			here.subdir = cp;
		} else {
			rewind (here.fp);
			for (k = 0; k < 10; k++) {
				here.variables[k] = NULLCHAR;
				here.indexes[k] = 0;
				here.socket[k] = 0;
			}
			process (&here);
		}
		(void) fclose (here.fp);
	}
	mycolorchange (&here, "09", here.last);
	tprintf ("\n\nThanks for using the ");
	mycolorchange (&here, "0C", here.last);
	tprintf ("TNOS %s ", tutorialName[here.mode]);
	mycolorchange (&here, "09", here.last);
	tprintf ("at ");
	mycolorchange (&here, "0E", here.last);
	tprintf ("%s\n\n", Hostname);
	mycolorchange (&here, "0F", here.last);
	if (m)
		mycolorchange (&here, m->colorset, here.last);
#ifndef STANDALONE
	(void) pax25 (tmp, Tcall);
	if (*tmp && mode && Stutor != -1)	/* if tutor server active, but not 'us */
		tprintf (banner, "tutorial assistance", tmp);
	(void) pax25 (tmp, Icall);
	if (*tmp && mode != 1 && Sinfo != -1)	/* if info server active, but not 'us' */
		tprintf (banner, "local/area information", tmp);
	(void) pax25 (tmp, Ncall);
	if (*tmp && mode != 2 && Snews != -1)	/* if news server active, but not 'us' */
		tprintf (banner, "Ham related NEWS", tmp);
	tputc ('\n');
	usflush (Curproc->output);
	(void) sockblock (Curproc->output, SOCK_BLOCK);
	free (here.socket);
#endif
      lost:
#ifndef STANDALONE
	Tutored--;
#ifdef XSERVER
	xnotify (X_TUT);
#endif
#endif
}



extern char NoRead[];
static short scriptmode = 0;
static struct mbx *MbxCalling = NULLMBX;
static short numCalling = 0;
static char const **argsCalling;
static short proxyServer = 0;



static int
scriptcmd (
struct world *inherited,	/* non-zero, inherited world */
FILE *fp,
const char *name
) {
struct session *sp = NULLSESSION;
int usesession = 0, k;
struct world here;

	/* Use a session if this comes from console - WG7J*/
	if (!inMbScriptHook && !scriptmode && Curproc->input == Command->input) {
		usesession = 1;
		if ((sp = newsession (name, SCRIPT, 0)) == NULLSESSION) {
			return 1;
		}
	}
	inMbScriptHook = 0;
	here.background = scriptmode;
	if (!inherited || inherited == (struct world *) -1) {
		here.ischild = 0;
		here.user = 0;
		here.whichsocket = 0;
		here.ipconnect = 1;
		here.goback = 0;
		here.socket = callocw (10, sizeof (int));

#ifndef STANDALONE
		if (inherited == (struct world *) -1) {
			here.m = MbxCalling;
			if (here.m)
				here.call = here.m->name;
			else
				here.call = "noname";
			MbxCalling = NULLMBX;
		} else {
			here.m = NULLMBX;
#endif
			here.call = "noname";
#ifndef STANDALONE
		}
#endif
		for (k = 0; k < 10; k++) {
			here.indexes[k] = 0;
			here.variables[k] = NULLCHAR;
		}
		if (inherited == (struct world *) -1) {
			for (k = 0; k < numCalling; k++)
				here.variables[k] = strdup (argsCalling[k]);
			here.indexes[0] = numCalling;
			inherited = 0;
			if (proxyServer)
				here.goback = proxyServer;
			proxyServer = 0;
		}
	} else {
		here.ischild = 1;
		here.user = inherited->user;
		here.whichsocket = inherited->whichsocket;
		here.ipconnect = inherited->ipconnect;
		here.goback = inherited->goback;
		here.socket = inherited->socket;
		here.call = inherited->call;
		if (inherited->background)
			here.background = inherited->background;
#ifndef STANDALONE
		here.m = inherited->m;
#endif
		for (k = 0; k < 10; k++) {
			here.indexes[k] = inherited->indexes[k];
			if (inherited->variables[k])
				here.variables[k] = strdup (inherited->variables[k]);
			else
				here.variables[k] = NULLCHAR;
		}
	}
	here.fp = fp;
	here.mode = 0;
	here.maxselect = 0;
	here.subdir = NULLCHAR;
	here.color = 0;
	here.lostconnection = 0;
	here.out = NULLFILE;
	here.inescape = 0;
	here.last[0] = 0;
	here.Error[0] = 0;
	process (&here);
	(void) fclose (here.fp);
	if (inherited) {
		for (k = 5; k < 10; k++) {
			if (!here.goback) {
				if (inherited->variables[k])
					free (inherited->variables[k]);
				inherited->variables[k] = NULLCHAR;
				if (here.variables[k])
					inherited->variables[k] = here.variables[k];
				inherited->indexes[k] = here.indexes[k];
			} else
				free (here.variables[k]);
		}
		if (!here.goback)
			inherited->whichsocket = here.whichsocket;
	} else
		free (here.socket);
	if (usesession) {
		(void) keywait (NULLCHAR, 1);
		freesession (sp);
	}
	return 0;
}



int
doscript (int argc, char *argv[], void *p)
{
FILE *fp;
int oldin, oldout;
int thisscriptmode;

	{
	char fname[256];

		strncpy (fname, make_fname (Command->curdirs->dir, argv[1]), 256);
		if ((fp = fopen (fname, READ_TEXT)) == NULLFILE) {
			tprintf (NoRead, fname, SYS_ERRLIST(errno));
			return 1;
		}
	}

	scriptmode = (argc > 2 && !strnicmp (argv[2], "back", 4)) ? 1 :
		(!argc && ((struct world *) p)->goback) ? 1 : 0;
	oldin = Curproc->input;
	oldout = Curproc->output;
	if (scriptmode)
		Curproc->input = Curproc->output = -1;
	thisscriptmode = scriptmode;
	if (!inMbScriptHook && (scriptmode || Curproc->input == Command->input))
		(void) newproc ("script", 2048, (void (*)(int, void *, void *)) scriptcmd, (argc > 1) ? 0 : (int) p, (void *) fp, (void *) strdup (argv[1]), 2);
	else
		(void) scriptcmd ((argc > 1) ? 0 : (struct world *) p, fp, argv[1]);

	if (thisscriptmode) {
		Curproc->input = oldin;
		Curproc->output = oldout;
	}
	return 0;
}




#ifndef STANDALONE
int
dombscript (int argc, char *argv[], void *p)
{
struct mbx *m;
char *args[2], buffer[128];

	m = (struct mbx *) p;
	if (argc == 1) {
		/* listing of commands available */
		(void) DisplayFile (UCmdsHelp, m->user);
		return 0;
	}
	sprintf (buffer, "%s/%s.cmd", UserCmds, argv[1]);
	if (!access (buffer, 4)) {
		args[1] = buffer;
		numCalling = (short int) (argc - 2);
		if (numCalling > 10)
			numCalling = 10;
		argsCalling = (char const **) &argv[2];
		MbxCalling = m;
		(void) doscript (1, args, (void *) -1);
	} else {
		tprintf ("Unknown CMD: '%s'\n", argv[1]);
		(void) DisplayFile (UCmdsHelp, m->user);
	}
	return 0;
}



void
mbscripthook (struct mbx *m, const char *hookfile)
{
char *args[2], buffer[128];
char *args2[2];

	sprintf (buffer, "%s/%s", UserCmds, hookfile);
	if (!access (buffer, 4)) {
		args[1] = buffer;
		numCalling = 1;
		args2[0] = m->line;
		argsCalling = (char const **) &args2[0];	/*lint !e789 */
		MbxCalling = m;
		inMbScriptHook = 1;
		(void) doscript (1, args, (void *) -1);
	}
}



int
proxy (FILE *fp, const char *from OPTIONAL)
{
char buf[512], subject[256], realfrom[128], *cp;
long startat;
FILE *out;
char const *args[2];
int oldin, oldout;
int skiptoblank = 0;

	parseheader (fp, realfrom, subject, NULLCHAR, NULLCHAR, buf, &startat);
	cp = strchr (realfrom, '@');
	if (cp)
		*cp = 0;
	sprintf (buf, "%s/proxy.tmp", Spool);
	out = fopen (buf, "w");
	if (out == NULLFILE)
		return 1;
	fprintf (out, "[%s]\n", realfrom);
	fseek (fp, startat, SEEK_SET);
	while (fgets (buf, 512, fp) != NULLCHAR) {
		if (!strncmp ("Message-Id: ", buf, 12))
			skiptoblank = 1;
		else {
			if (skiptoblank && buf[0] == '\n')
				skiptoblank = 0;
			else if (!skiptoblank)
				fputs (buf, out);
		}
	}
	(void) fclose (out);
	numCalling = 1;
	argsCalling = args;	/*lint !e789 */
	args[0] = "import";

	sprintf (buf, "%s/proxy.cmd", UserCmds);
	if ((out = fopen (buf, READ_TEXT)) == NULLFILE)
		return 1;

	oldin = Curproc->input;
	oldout = Curproc->output;
	Curproc->input = Curproc->output = -1;
	scriptmode = 1;
	proxyServer = 1;

#if 0
	newproc ("script", 2048, (void (*)(int, void *, void *)) scriptcmd, (int) -1, (void *) out, (void *) strdup ("Proxy"), 2);
#else
	(void) scriptcmd ((struct world *) -1, out, "Proxy");
#endif
	Curproc->input = oldin;
	Curproc->output = oldout;
	return 1;
}
#endif


#endif
