/* * *********** * * XCHAT.C * * *********** * * Extended chat processor for Taylor UUCP. See accompanying documentation. * * Written by: * Bob Denny (denny@alisa.com) * Based on code in DECUS UUCP (for VAX/VMS) * * Small modification by: * Daniel Hagerty (hag@eddie.mit.edu) * * History: * Version 1.0 shipped with Taylor 1.03. No configuration info inside. * * Bob Denny - Sun Aug 30 18:41:30 1992 * V1.1 - long overdue changes for other systems. Rip out interval * timer code, use timer code from Taylor UUCP, use select() * for timed reads. Use Taylor UUCP "conf.h" file to set * configuration for this program. Add defaulting of script * and log file paths. * * Daniel Hagerty - Mon Nov 22 18:17:38 1993 * V1.2 - Added a new opcode to xchat. "expectstr" is a cross between * sendstr and expect, looking for a parameter supplied string. * Useful where a prompt could change for different dial in * lines and such. * * Bugs: * Does not support BSD terminal I/O. Anyone care to add it? */ #include #include #include #include #include #include #include #include #include "xc-conf.h" /* * Pick a timing routine to use, as done in Taylor UUCP. */ #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL #define USE_SELECT_TIMER 0 #else #define USE_SELECT_TIMER HAVE_SELECT #if USE_SELECT_TIMER #include #endif #endif #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS #undef HAVE_POLL #define HAVE_POLL 0 #endif #if HAVE_USLEEP || HAVE_NAP #undef HAVE_NAPMS #define HAVE_NAPMS 0 #endif #if HAVE_USLEEP #undef HAVE_NAP #define HAVE_NAP 0 #endif static int ttblind(); static int ttcd(); /* script entry -- "compiled" form of dial, hangup, or login script */ struct script { struct script *next; /* pointer to next entry, or null */ int opcode; /* numeric opcode */ char *strprm; /* pointer to string param */ long intprm; /* integer parameter */ char *newstate; /* new state name */ }; /* opcode definition array element -- one for each possible opcode */ struct script_opdef { char *opname; int opcode; /* numeric opcode -- same as array index */ int prmtype; /* one of SC_NONE, SC_STR, SC_XSTR, SC_INT */ int newstate; /* one of SC_NONE, SC_NWST */ }; /* values for opcode */ #define SC_LABEL 0 /* "label" (state name) */ #define SC_CDLY 1 /* set char output delay in msec */ #define SC_PCHR 2 /* pause char for dial string (from P in input) */ #define SC_PTIM 3 /* seconds to allow for pause char */ #define SC_WCHR 4 /* wait char for dial string (from W in input) */ #define SC_WTIM 5 /* seconds to allow for wait char */ #define SC_ZERO 6 /* zero counter */ #define SC_INCR 7 /* increment counter */ #define SC_IFGT 8 /* change state if counter > int param */ #define SC_WAIT 9 /* wait for int param seconds */ #define SC_GOTO 10 /* unconditional change to new state */ #define SC_SEND 11 /* send strparam (after sprintf substitutions) */ #define SC_BRK 12 /* send a break */ #define SC_HANG 13 /* drop DTR */ #define SC_DIAL 14 /* send telno string (after subst PCHR & WCHR) */ #define SC_DTIM 15 /* time in msec per digit (for timeout calculations) */ /* default = 100 (one tenth second) */ #define SC_CTIM 16 /* additional time (in seconds) to wait for carrier */ /* default = 45 seconds */ #define SC_EXIT 17 /* script done, success */ #define SC_FAIL 18 /* script done, failure */ #define SC_LOG 19 /* write strparam to uucp.log */ #define SC_LOGE 20 /* write strparam to uucp.log w/error ind */ #define SC_DBG 21 /* write strparam to debug log if debug lvl = LGI */ #define SC_DBGE 22 /* write strparam to debug log if debug lvl = LGIE */ #define SC_DBST 23 /* 'or' intparam into debug mask */ #define SC_DBCL 24 /* 'bicl' intparam into debug mask */ #define SC_TIMO 25 /* newstate if no match in intparam secs */ /* (uses calculated dial time if intparam is 0) */ #define SC_XPCT 26 /* wait for strparam, goto _newstate if found */ #define SC_CARR 27 /* goto _newstate if carrier detected */ #define SC_FLSH 28 /* flush typeahead buffer */ #define SC_IFBL 29 /* change state if controller is blind w/o CD */ #define SC_IFBG 30 /* chg state if ctlr is blind and counter > intprm */ #define SC_SNDP 31 /* send parameter n */ #define SC_IF1P 32 /* if parameter n present */ #define SC_IF0P 33 /* if parameter n absent */ #define SC_DBOF 34 /* open debugging file */ #define SC_TELN 35 /* Set telno from parameter n */ #define SC_7BIT 36 /* Set port to 7-bit stripping */ #define SC_8BIT 37 /* Set port for 8-bit characters */ #define SC_PNON 38 /* Set port for 8-bit, no parity */ #define SC_PEVN 39 /* Set port for 7-bit, even parity */ #define SC_PODD 40 /* Set port for 7-bit, odd parity */ #define SC_HUPS 41 /* Change state on HUP signal */ #define SC_XPST 42 /* Expect a param string */ #define SC_END 43 /* end of array */ /* values for prmtype, prm2type */ #define SC_NONE 0 /* no parameter */ #define SC_STR 1 /* simple string */ #define SC_INT 2 /* integer */ #define SC_NWST 3 /* new state name */ #define SC_XSTR 4 /* translated string */ /* opcode definition table for dial/login/hangup scripts */ static struct script_opdef sc_opdef[] = { {"label", SC_LABEL, SC_NONE, SC_NONE}, {"chrdly", SC_CDLY, SC_INT, SC_NONE}, {"pchar", SC_PCHR, SC_STR, SC_NONE}, {"ptime", SC_PTIM, SC_INT, SC_NONE}, {"wchar", SC_WCHR, SC_STR, SC_NONE}, {"wtime", SC_WTIM, SC_INT, SC_NONE}, {"zero", SC_ZERO, SC_NONE, SC_NONE}, {"count", SC_INCR, SC_NONE, SC_NONE}, {"ifgtr", SC_IFGT, SC_INT, SC_NWST}, {"sleep", SC_WAIT, SC_INT, SC_NONE}, {"goto", SC_GOTO, SC_NONE, SC_NWST}, {"send", SC_SEND, SC_XSTR, SC_NONE}, {"break", SC_BRK, SC_NONE, SC_NONE}, {"hangup", SC_HANG, SC_NONE, SC_NONE}, {"7bit", SC_7BIT, SC_NONE, SC_NONE}, {"8bit", SC_8BIT, SC_NONE, SC_NONE}, {"nopar", SC_PNON, SC_NONE, SC_NONE}, {"evenpar", SC_PEVN, SC_NONE, SC_NONE}, {"oddpar", SC_PODD, SC_NONE, SC_NONE}, {"telno", SC_TELN, SC_INT, SC_NONE}, {"dial", SC_DIAL, SC_NONE, SC_NONE}, {"dgttime", SC_DTIM, SC_INT, SC_NONE}, {"ctime", SC_CTIM, SC_INT, SC_NONE}, {"success", SC_EXIT, SC_NONE, SC_NONE}, {"failed", SC_FAIL, SC_NONE, SC_NONE}, {"log", SC_LOG, SC_XSTR, SC_NONE}, {"logerr", SC_LOGE, SC_XSTR, SC_NONE}, {"debug", SC_DBG, SC_XSTR, SC_NONE}, {"debuge", SC_DBGE, SC_XSTR, SC_NONE}, {"dbgset", SC_DBST, SC_INT, SC_NONE}, {"dbgclr", SC_DBCL, SC_INT, SC_NONE}, {"dbgfile", SC_DBOF, SC_XSTR, SC_NONE}, {"timeout", SC_TIMO, SC_INT, SC_NWST}, {"expect", SC_XPCT, SC_XSTR, SC_NWST}, {"ifcarr", SC_CARR, SC_NONE, SC_NWST}, {"ifhang", SC_HUPS, SC_NONE, SC_NWST}, {"flush", SC_FLSH, SC_NONE, SC_NONE}, {"ifblind", SC_IFBL, SC_NONE, SC_NWST}, {"ifblgtr", SC_IFBG, SC_INT, SC_NWST}, {"sendstr", SC_SNDP, SC_INT, SC_NONE}, {"ifstr", SC_IF1P, SC_INT, SC_NWST}, {"ifnstr", SC_IF0P, SC_INT, SC_NWST}, {"expectstr", SC_XPST, SC_INT, SC_NWST}, {"table end", SC_END, SC_NONE, SC_NONE} }; #define SUCCESS 0 #define FAIL 1 #define ERROR -1 #define MAX_SCLINE 255 /* max length of a line in a script file */ #define MAX_EXPCT 127 /* max length of an expect string */ #define CTL_DELIM " \t\n\r" /* Delimiters for tokens */ #define SAME 0 /* if (strcmp(a,b) == SAME) ... */ #define SLOP 10 /* Slop space on arrays */ #define MAX_STRING 200 /* Max length string to send/expect */ #define DEBUG_LEVEL(level) \ (Debug & (1 << level)) #define DB_LOG 0 /* error messages and a copy of the LOGFILE output */ #define DB_LGIE 1 /* dial,login,init trace -- errors only */ #define DB_LGI 2 /* dial,login,init trace -- nonerrors (incl chr I/O) */ #define DB_LGII 3 /* script processing internals */ #define TRUE 1 #define FALSE 0 #define NONE 0 #define EVEN 1 #define ODD 2 #define logit(m, p1) fprintf(stderr, "%s %s\n", m, p1) static char **paramv; /* Parameter vector */ static int paramc; /* Parameter count */ static char telno[64]; /* Telephone number w/meta-chars */ static int Debug; static int fShangup = FALSE; /* TRUE if HUP signal received */ static FILE *dbf = NULL; static struct termio old, new; extern int usignal(); extern int uhup(); static struct siglist { int signal; int (*o_catcher) (); int (*n_catcher) (); } sigtbl[] = { { SIGHUP, NULL, uhup }, { SIGINT, NULL, usignal }, { SIGIOT, NULL, usignal }, { SIGQUIT, NULL, usignal }, { SIGTERM, NULL, usignal }, { SIGALRM, NULL, usignal }, { 0, NULL, NULL } /* Table end */ }; extern struct script *read_script(); extern void msleep(); extern char xgetc(); extern void charlog(); extern void setup_tty(); extern void restore_tty(); extern void ttoslow(); extern void ttflui(); extern void tthang(); extern void ttbreak(); extern void tt7bit(); extern void ttpar(); extern void DEBUG(); extern void *malloc(); /* * ********************************** * * BEGIN EXECUTION - MAIN PROGRAM * * ********************************** * * This program is called by Taylor UUCP with a list of * arguments in argc/argv, and stdin/stdout mapped to the * tty device, and stderr mapped to the Taylor logfile, where * anything written to stdout will be logged as an error. * */ int main(argc, argv) int argc; char *argv[]; { int i, stat; FILE *sf; char sfname[256]; struct script *script; struct siglist *sigs; /* * The following is needed because my cpp does not have the * #error directive... */ #if ! HAVE_SELECT no_select_sorry(); /* Sad way to fail make */ #endif paramv = &argv[2]; /* Parameters start at 2nd arg */ paramc = argc - 2; /* Number of live parameters */ telno[0] = '\0'; if (argc < 2) { fprintf(stderr, "%s: no script file supplied\n", argv[0]); exit(FAIL); } /* * If the script file argument begins with '/', then we assume * it is an absolute pathname, otherwise, we prepend the * SCRIPT_DIR path. */ *sfname = '\0'; /* Empty name string */ if(argv[1][0] != '/') /* If relative path */ strcat(sfname, SCRIPT_DIR); /* Prepend the default dir. */ strcat(sfname, argv[1]); /* Add the script file name */ /* * Now open the script file. */ if ((sf = fopen(sfname, "r")) == NULL) { fprintf(stderr, "%s: Failed to open script %s\n", argv[0], sfname); perror(" "); exit(FAIL); } /* * COMPILE SCRIPT */ if ((script = read_script(sf)) == NULL) { fprintf(stderr, "%s: script error in \"%s\"\n", argv[0], argv[1]); exit(FAIL); } /* * Set up a signal catcher so the line can be returned to * it's current state if something nasty happens. */ sigs = &sigtbl[0]; while(sigs->signal) { sigs->o_catcher = (int (*) ())signal(sigs->signal, sigs->n_catcher); sigs += 1; } /* * Save current tty settings, then set up raw, single * character input processing, with 7-bit stripping. */ setup_tty(); /* * EXECUTE SCRIPT */ if ((stat = do_script(script)) != SUCCESS) fprintf(stderr, "%s: script %s failed.\n", argv[0], argv[1]); /* * Clean up and exit. */ restore_tty(); #ifdef FIXSIGS sigs = &sigtbl[0]; while(sigs->signal) if(sigs->o_catcher != -1) signal(sigs->signal, sigs->o_catcher); #endif exit(stat); } /* * deal_script - deallocate a script and all strings it points to */ int deal_script(loc) struct script *loc; { /* * If pointer is null, just exit */ if (loc == (struct script *)NULL) return SUCCESS; /* * Deallocate the rest of the script */ deal_script(loc->next); /* * Deallocate the string parameter, if any */ if (loc->strprm != (char *)NULL) free(loc->strprm); /* * Deallocate the new state name parameter, if any */ if (loc->newstate != (char *)NULL) free(loc->newstate); /* * Deallocate this entry */ free(loc); return SUCCESS; } /* * read_script * * Read & compile a script, return pointer to first entry, or null if bad */ struct script *read_script(fd) FILE *fd; { struct script *this = NULL; struct script *prev = NULL; struct script *first = NULL; long len, i; char inpline[MAX_SCLINE]; char inpcopy[MAX_SCLINE]; char *c, *cln, *opc, *cp; /* * MAIN COMPILATION LOOP */ while ((c = fgets(inpline, (sizeof inpline - 1), fd)) != (char *)NULL) { /* * Skip comments and blank lines */ if (*c == '#' || *c == '\n') continue; /* * Get rid of the trailing newline, and copy the string */ inpline[strlen(inpline)-1] = '\0'; strcpy(inpcopy, inpline); /* * Look for text starting in the first col (a label) */ if ((!isspace(inpline[0])) && (cln = strchr (inpline, ':')) != (char *)NULL) { this = (struct script *)malloc (sizeof (struct script)); if (prev != (struct script *)NULL) prev->next = this; prev = this; if (first == (struct script *)NULL) first = this; this->next = (struct script *)NULL; this->opcode = SC_LABEL; len = cln - c; this->strprm = (char *)malloc(len+1); strncpy(this->strprm, c, len); (this->strprm)[len] = '\0'; this->intprm = 0; this->newstate = (char *)NULL; c = cln + 1; } /* * Now handle the opcode. Fold it to lower case. */ opc = strtok(c, CTL_DELIM); if (opc == (char *)NULL) /* If no opcode... */ continue; /* ...read the next line */ cp = opc; while(*cp) tolower(*cp++); /* * If we have an opcode but we haven't seen anything * else (like a label) yet, i.e., this is the first * entry, and there was no label. We need to * cobble up a label so that read_script is happy */ if (first == (struct script *)NULL) { this = (struct script *)malloc (sizeof (struct script)); prev = this; first = this; this->next = (struct script *)NULL; this->opcode = SC_LABEL; this->strprm = (char *)malloc(2); strcpy(this->strprm, ":"); this->intprm = 0; this->newstate = (char *)NULL; } /* * Find opcode - ndex through the opcode definition table */ for (i=1; sc_opdef[i].opcode != SC_END; i++) if (strcmp(opc, sc_opdef[i].opname) == SAME) break; if ((sc_opdef[i].opcode) == SC_END) { logit ("Bad opcode in script", opc); deal_script(first); return (struct script *)NULL; } /* * Found opcode. Allocate a new command node and initialize */ this = (struct script *)malloc(sizeof (struct script)); prev->next = this; prev = this; this->next = (struct script *)NULL; this->opcode = sc_opdef[i].opcode; this->strprm = (char *)NULL; this->intprm = 0; this->newstate = (char *)NULL; /* * Pick up new state parameter, if any */ if (sc_opdef[i].newstate == SC_NWST) { c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing new state", opc); deal_script(first); return (struct script *)NULL; } else { this->newstate = (char *)malloc(strlen(c)+1); strcpy(this->newstate, c); } } /* * Pick up the string or integer parameter. Handle missing * parameter gracefully. */ switch (sc_opdef[i].prmtype) { /* * INT parameter - convert and store in node */ case SC_INT: c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing script param", opc); deal_script(first); return (struct script *)NULL; } /* * If this is the parameter to DBST or DBCL, force * base-10 conversion, else convert per parameter. */ if (sc_opdef[i].opcode == SC_DBST || sc_opdef[i].opcode == SC_DBCL) this->intprm = strtol(c, (char **)NULL, 0); else this->intprm = strtol(c, (char **)NULL, 10); break; /* * STR/XSTR strings. */ case SC_STR: case SC_XSTR: c = strtok((char *)NULL, CTL_DELIM); if (c == (char *)NULL) { logit("Missing script param", opc); deal_script(first); return (struct script *)NULL; } /* * For XSTR opcode, use c to find out where * the string param begins in the copy of the * input line, and pick up all that's left of * the line (to allow imbedded blanks, etc.). */ if (sc_opdef[i].prmtype == SC_XSTR) c = &inpcopy[0] + (c - &inpline[0]); /* * Allocate a buffer for the string parameter */ this->strprm = (char *)malloc(strlen(c)+1); /* * For XSTR, Translate the string and store its * length. Note that, after escape sequences are * compressed, the resulting string may well be a * few bytes shorter than the input string (whose * length was the basis for the malloc above), * but it will never be longer. */ if (sc_opdef[i].prmtype == SC_XSTR) { this->intprm = xlat_str(this->strprm, c); this->strprm[this->intprm] = '\0'; } else strcpy(this->strprm, c); break; } } /* * EOF */ return first; } /* * xlat_str * * Translate embedded escape characters in a "send" or "expect" string. * * Called by read_script(), above. * * Returns the actual length of the resulting string. Note that imbedded * nulls (specified by \000 in the input) ARE allowed in the result. */ xlat_str(out, in) char *out, *in; { register int i = 0, j = 0; int byte, k; while (in[i]) { if (in[i] != '\\') { out[j++] = in[i++]; } else { switch (in[++i]) { case 'd': /* EOT */ out[j++] = 0x04; break; case 'N': /* null */ out[j++] = 0x00; break; case 'n': /* line feed */ out[j++] = 0x0a; break; case 'r': /* carriage return */ out[j++] = 0x0d; break; case 's': /* space */ out[j++] = ' '; break; case 't': /* tab */ out[j++] = '\t'; break; case '-': /* hyphen */ out[j++] = '-'; break; case '\\': /* back slash */ out[j++] = '\\'; break; case '0': /* '\nnn' format */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': byte = in[i] - '0'; k = 0; while (3 > ++k) if ((in[i+1] < '0') || (in[i+1] > '7')) break; else { byte = (byte<<3) + in[i+1] - '0'; ++i; } out[j++] = byte; break; default: /* don't know so skip it */ break; } ++i; } } return j; } /* find a state within a script */ struct script * find_state(begin, newstate) struct script *begin; char *newstate; { struct script *here; for (here=begin; here != (struct script *)NULL; here=here->next) { if (here->opcode == SC_LABEL && strcmp(here->strprm, newstate) == SAME) return here; } return (struct script *)NULL; } /* * do_script() - execute a script */ int do_script(begin) struct script *begin; { struct script *curstate, *newstate, *curscr; int dbgsave; char tempstr[MAX_SCLINE]; char dfname[256]; char *c, chr; int prmlen; int dbfd; time_t sc_carrtime = 45000; /* time to wf carr after dial */ time_t sc_chrdly = 100; /* delay time for ttoslow */ time_t sc_ptime = 2000; /* time to allow for pause char */ time_t sc_wtime = 10000; /* time to allow for wait char */ time_t sc_dtime = 100; /* time to allow for each digit */ time_t sc_dtmo; /* total time to dial number */ int sc_counter; /* random counter */ char sc_pchar = ','; /* modem pause character */ char sc_wchar = 'W'; /* modem wait-for-dialtone character */ time_t sc_begwait; /* time at beg of wait */ time_t sc_secs; /* timeout period */ int expcnt; int expin; static char expbuf[MAX_EXPCT]; dbgsave = Debug; curstate = begin; if (curstate == (struct script *)NULL) return SUCCESS; _newstate: /* * do all of curstate's actions. Enter with curstate pointing * to a label entry */ expin = 0; for (curscr = curstate->next; /* point to 1st scr after label */ (curscr != (struct script *)NULL) && /* do until end of scr */ (curscr->opcode != SC_LABEL); /* or next label */ curscr = curscr->next) { expcnt = 0; switch (curscr->opcode) { case SC_LABEL: logit("Script proc err", curstate->strprm); return FAIL; case SC_FLSH: DEBUG(DB_LGII, "Flushing typeahead buffer\n", 0); ttflui(); break; case SC_CDLY: sc_chrdly = curscr->intprm; DEBUG(DB_LGII, "Set chrdly to %d\n", sc_chrdly); break; case SC_PCHR: sc_pchar = *(curscr->strprm); DEBUG(DB_LGII, "Set pause char to %c\n", sc_pchar); break; case SC_PTIM: sc_ptime = curscr->intprm; DEBUG(DB_LGII, "Set pause time to %d\n", sc_ptime); break; case SC_WCHR: sc_wchar = *(curscr->strprm); DEBUG(DB_LGII, "Set wait char to %c\n", sc_wchar); break; case SC_WTIM: sc_wtime = curscr->intprm; DEBUG(DB_LGII, "Set wait time to %d\n", sc_wtime); break; case SC_ZERO: sc_counter = 0; DEBUG(DB_LGII, "Set counter to %d\n", sc_counter); break; case SC_INCR: sc_counter++; DEBUG(DB_LGII, "Incr counter to %d\n", sc_counter); break; case SC_WAIT: DEBUG(DB_LGII, "Sleeping %d tenth-secs\n", curscr->intprm); msleep(curscr->intprm); break; case SC_DTIM: sc_dtime = curscr->intprm; DEBUG(DB_LGII, "Digit time is %d\n", sc_dtime); break; case SC_CTIM: sc_carrtime = curscr->intprm; DEBUG(DB_LGII, "Carrier time is %d\n", sc_carrtime); break; case SC_EXIT: Debug = dbgsave; DEBUG(DB_LGI, "Script ended successfully\n", 0); return SUCCESS; case SC_FAIL: Debug = dbgsave; if (DEBUG_LEVEL(DB_LGI) && dbf != NULL) fprintf(dbf, "Script failed\n"); else if (expin) charlog(expbuf, expin, DB_LOG, "Script failed. Last received data"); return FAIL; case SC_LOG: logit(curscr->strprm, ""); break; case SC_LOGE: logit("ERROR: ", curscr->strprm); break; case SC_DBOF: /* * If the debug file name does not begin with "/", then * we prepend the LOG_DIR to the string. Then CREATE the * file. This WIPES OUT previous logs. */ *dfname = '\0'; /* Zero name string */ if(curscr->strprm[0] != '/') strcat(dfname, LOG_DIR); /* Prepend default directory */ strcat(dfname, curscr->strprm); /* Add given string */ DEBUG(DB_LGII, "Open debug file %s\n", dfname); if ((dbfd = creat (dfname, 0600)) <= 0) { logit("Failed to create debug log %s", dfname); perror(""); return FAIL; } if ((dbf = fdopen(dbfd, "w")) == NULL) { logit("Failed to open debug log fildes.", ""); perror(""); return FAIL; } break; case SC_DBG: DEBUG(DB_LGI, "<%s>\n", curscr->strprm); break; case SC_DBGE: DEBUG(DB_LGIE, "ERROR: <%s>\n", curscr->strprm); break; case SC_DBST: Debug |= curscr->intprm; DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); break; case SC_DBCL: Debug &= ~(curscr->intprm); DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug); break; case SC_BRK: DEBUG(DB_LGI, "Sending break\n", 0); ttbreak(); break; case SC_HANG: DEBUG(DB_LGI, "Dropping DTR\n", 0); tthang(); break; case SC_7BIT: DEBUG(DB_LGI, "Enabling 7-bit stripping\n", 0); tt7bit(TRUE); break; case SC_8BIT: DEBUG(DB_LGI, "Disabling 7-bit stripping\n", 0); tt7bit(FALSE); break; case SC_PNON: DEBUG(DB_LGI, "Setting 8-bit, no parity\n", 0); ttpar(NONE); break; case SC_PEVN: DEBUG(DB_LGI, "Setting 7-bit, even parity\n", 0); ttpar(EVEN); break; case SC_PODD: DEBUG(DB_LGI, "Setting 7-bit, odd parity\n", 0); ttpar(ODD); break; case SC_IFBL: if (ttblind()) { DEBUG(DB_LGI, "Blind mux,\n", 0); goto _chgstate; } break; case SC_IFBG: if (ttblind() && sc_counter > curscr->intprm) { DEBUG(DB_LGI, "Blind mux & ctr > %d\n", curscr->intprm); goto _chgstate; } break; case SC_IFGT: if (sc_counter > curscr->intprm) { DEBUG(DB_LGI, "Counter > %d\n", curscr->intprm); goto _chgstate; } break; case SC_GOTO: _chgstate: DEBUG(DB_LGI, "Changing to state %s\n", curscr->newstate); curstate = find_state(begin, curscr->newstate); if (curstate == NULL) { logit("New state not found", curscr->newstate); return FAIL; } goto _newstate; case SC_SEND: ttoslow(curscr->strprm, curscr->intprm, sc_chrdly); break; case SC_TELN: if (curscr->intprm > paramc - 1) { sprintf(tempstr, "telno - param #%d", curscr->intprm); logit(tempstr, " not present"); return FAIL; } strcpy(telno, paramv[curscr->intprm]); DEBUG(DB_LGII, "telno set to %s\n", telno); break; case SC_SNDP: if (curscr->intprm > paramc - 1) { sprintf(tempstr, "sendstr - param #%d", curscr->intprm); logit(tempstr, " not present"); return FAIL; } prmlen = xlat_str(tempstr, paramv[curscr->intprm]); ttoslow(tempstr, prmlen, sc_chrdly); break; case SC_IF1P: if (curscr->intprm < paramc) goto _chgstate; break; case SC_IF0P: if (curscr->intprm >= paramc) goto _chgstate; break; case SC_DIAL: if(telno[0] == '\0') { logit("telno not set", ""); return(FAIL); } /* * Compute and set a default timeout for the 'timeout' * command. Some parameters in this computation may be * changed by the script. See the man page xchat(8) for * details. */ sc_dtmo = (sc_dtime+sc_chrdly)*strlen(telno) + sc_carrtime; c=strcpy(tempstr, telno); for (; *c!='\0'; c++) { if (*c == 'W') { *c = sc_wchar; sc_dtmo += sc_wtime; } else if (*c == 'P') { *c = sc_pchar; sc_dtmo += sc_ptime; } } DEBUG(DB_LGI, "Dialing, default timeout is %d millisecs\n", sc_dtmo); ttoslow(tempstr, 0, sc_chrdly); break; case SC_TIMO: /* these are "expects", don't bother */ case SC_XPCT: /* with them yet, other than noting that */ case SC_CARR: /* they exist */ case SC_XPST: expcnt++; break; } } /* we've done the current state's actions, now do its expects, if any */ if (expcnt == 0) { if (curscr != (struct script *)NULL && (curscr->opcode == SC_LABEL)) { curstate = curscr; DEBUG(DB_LGI, "Fell through to state %s\n", curstate->strprm); goto _newstate; } else { logit("No way out of state", curstate->strprm); return FAIL; } } time(&sc_begwait); /* log time at beg of expect */ DEBUG(DB_LGI, "Doing expects for state %s\n", curstate->strprm); charlog((char *)NULL, 0, DB_LGI, "Received"); while (1) { chr = xgetc(1); /* Returns upon char input or 1 sec. tmo */ charlog(&chr, 1, DB_LGI, (char *)NULL); if (chr != EOF) { if (expin < MAX_EXPCT) { expbuf[expin++] = chr & 0x7f; } else { strncpy(expbuf, &expbuf[1], MAX_EXPCT-1); expbuf[MAX_EXPCT-1] = chr & 0x7f; } } /* for each entry in the current state... */ for (curscr = curstate->next; (curscr != (struct script *)NULL) && (curscr->opcode != SC_LABEL); curscr = curscr->next) { switch (curscr->opcode) { case SC_TIMO: sc_secs = curscr->intprm; if (sc_secs == 0) sc_secs = sc_dtmo; sc_secs /= 1000; if (time(NULL)-sc_begwait > sc_secs) { DEBUG(DB_LGI, "\nTimed out (%d secs)\n", sc_secs); goto _chgstate; } break; case SC_CARR: if (ttcd()) { DEBUG(DB_LGI, "\nGot carrier\n", 0); goto _chgstate; } break; case SC_HUPS: if (fShangup) { DEBUG(DB_LGI, "\nGot data set hangup\n", 0); goto _chgstate; } break; case SC_XPCT: if ((expin >= curscr->intprm) && (strncmp(curscr->strprm, &expbuf[expin - curscr->intprm], curscr->intprm) == SAME)) { charlog(curscr->strprm, curscr->intprm, DB_LGI, "Matched"); goto _chgstate; } break; } } } } /* New opcode added by hag@eddie.mit.edu for expecting a parameter supplied string */ case SC_XPST: if(curscr->intprm >paramc-1) { sprintf(tempstr,"expectstr - param#%d",curscr->intprm); logit(tempstr, " not present"); return(FAIL); } prmlen=xlat_str(tempstr,paramv[curscr->intprm]); if((expin >= prmlen) && (strncmp(tempstr,&expbuf[expin-prmlen], prmlen) == SAME)) { charlog(tempstr,prmlen,DB_LGI, "Matched"); goto _chgstate; } break; /* * SIGNAL HANDLERS */ /* * usignal - generic signal catcher */ static int usignal(isig) int isig; { DEBUG(DB_LOG, "Caught signal %d. Exiting...\n", isig); restore_tty(); exit(FAIL); } /* * uhup - HUP catcher */ static int uhup(isig) int isig; { DEBUG(DB_LOG, "Data set hangup.\n"); fShangup = TRUE; } /* * TERMINAL I/O ROUTINES */ /* * xgetc - get a character with timeout * * Assumes that stdin is opened on a terminal or TCP socket * with O_NONBLOCK. */ static char xgetc(tmo) int tmo; /* Timeout, seconds */ { char c; struct timeval s; int f = 1; /* Select on stdin */ int result; if(read(0, &c, 1) <= 0) /* If no data available */ { s.tv_sec = (long)tmo; s.tv_usec = 0L; if(select (1, &f, (int *) NULL, &f, &s) == 1) read(0, &c, 1); else c = '\377'; } return(c); } /* * Pause for an interval in milliseconds */ void msleep(msec) long msec; { #if HAVE_USLEEP if(msec == 0) /* Skip all of this if delay = 0 */ return; usleep (msec * (long)1000); #endif /* HAVE_USLEEP */ #if HAVE_NAPMS if(msec == 0) /* Skip all of this if delay = 0 */ return; napms (msec); #endif /* HAVE_NAPMS */ #if HAVE_NAP if(msec == 0) /* Skip all of this if delay = 0 */ return; nap (msec); #endif /* HAVE_NAP */ #if HAVE_POLL struct pollfd sdummy; if(msec == 0) return; /* * We need to pass an unused pollfd structure because poll checks * the address before checking the number of elements. */ poll (&sdummy, 0, msec); #endif /* HAVE_POLL */ #if USE_SELECT_TIMER struct timeval s; if(msec == 0) return; s.tv_sec = msec / 1000L; s.tv_usec = (msec % 1000L) * 1000L; select (0, (int *) NULL, (int *) NULL, (int *) NULL, &s); #endif /* USE_SELECT_TIMER */ #if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP && \ ! HAVE_POLL && ! USE_SELECT_TIMER if(msec == 0) return; sleep (1); /* Sleep for a whole second (UGH!) */ #endif /* HAVE_ and USE_ nothing */ } /* * Debugging output */ static void DEBUG(level, msg1, msg2) int level; char *msg1, *msg2; { if ((dbf != NULL) && DEBUG_LEVEL(level)) fprintf(dbf, msg1, msg2); } /* * charlog - log a string of characters * * SPECIAL CASE: msg=NULL, len=1 and msg[0]='\377' gets logged * when read does its 1 sec. timeout. Log "<1 sec.>" * so user can see elapsed time */ static void charlog(buf, len, mask, msg) char *buf; int len, mask; char *msg; { char tbuf[256]; if (DEBUG_LEVEL(mask) && dbf != NULL) { if(msg == (char *)NULL) msg = ""; strncpy(tbuf, buf, len); tbuf[len] = '\0'; if(len == 1 && tbuf[0] == '\377') strcpy(tbuf, "<1 sec.>"); fprintf(dbf, "%s %s\n", msg, tbuf); } } /* * setup_tty() * * Save current tty settings, then set up raw, single * character input processing, with 7-bit stripping. */ static void setup_tty() { register int i; ioctl(0, TCGETA, &old); new = old; for(i = 0; i < 7; i++) new.c_cc[i] = '\0'; new.c_cc[VMIN] = 0; /* MIN = 0, use requested count */ new.c_cc[VTIME] = 10; /* TIME = 1 sec. */ new.c_iflag = ISTRIP; /* Raw mode, 7-bit stripping */ new.c_lflag = 0; /* No special line discipline */ ioctl(0, TCSETA, &new); } /* * restore_tty() - restore signal handlers and tty modes on exit. */ static void restore_tty(sig) int sig; { ioctl(0, TCSETA, &old); return; } /* * ttoslow() - Send characters with pacing delays */ static void ttoslow(s, len, delay) char *s; int len; time_t delay; { int i; if (len == 0) len = strlen(s); charlog (s, len, DB_LGI, "Sending slowly"); for (i = 0; i < len; i++, s++) { write(1, s, 1); msleep(delay); } } /* * ttflui - flush input buffer */ static void ttflui() { if(isatty(0)) (void) ioctl ( 0, TCFLSH, 0); } /* * ttcd - Test if carrier is present * * NOT IMPLEMENTED. I don't know how!!! */ static int ttcd() { return TRUE; } /* * tthang - Force DTR low for 1-2 sec. */ static void tthang() { if(!isatty()) return; #ifdef TCCLRDTR (void) ioctl (1, TCCLRDTR, 0); sleep (2); (void) ioctl (1, TCSETDTR, 0); #endif return; } /* * ttbreak - Send a "break" on the line */ static void ttbreak() { (void) ioctl (1, TCSBRK, 0); } /* * ttblind - return TRUE if tty is "blind" * * NOT IMPLEMENTED - Don't know how!!! */ static int ttblind() { return FALSE; } /* * tt7bit - enable/disable 7-bit stripping on line */ static void tt7bit(enable) int enable; { if(enable) new.c_iflag |= ISTRIP; else new.c_iflag &= ~ISTRIP; ioctl(0, TCSETA, &new); } /* * ttpar - Set parity mode on line. Ignore parity errors on input. */ static void ttpar(mode) int mode; { switch(mode) { case NONE: new.c_iflag &= ~(INPCK | IGNPAR); new.c_cflag &= ~(CSIZE | PARENB | PARODD); new.c_cflag |= CS8; break; case EVEN: new.c_iflag |= (INPCK | IGNPAR); new.c_cflag &= ~(CSIZE | PARODD); new.c_cflag |= (CS7 | PARENB); break; case ODD: new.c_iflag |= (INPCK | IGNPAR); new.c_cflag &= ~(CSIZE); new.c_cflag |= (CS7 | PARENB | PARODD); break; } ioctl(0, TCSETA, &new); }