chat.c revision 7001
1/*
2 *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
3 *
4 *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
5 *
6 *  Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
7 *
8 *	Chat -- a program for automatic session establishment (i.e. dial
9 *		the phone and log in).
10 *
11 *	This software is in the public domain.
12 *
13 *	Please send all bug reports, requests for information, etc. to:
14 *
15 *		Karl Fox <karl@MorningStar.Com>
16 *		Morning Star Technologies, Inc.
17 *		1760 Zollinger Road
18 *		Columbus, OH  43221
19 *		(614)451-1883
20 *
21 * $Id: chat.c,v 1.2 1995/02/26 12:17:20 amurai Exp $
22 *
23 *  TODO:
24 *	o Support more UUCP compatible control sequences.
25 *	o Dialing shoud not block monitor process.
26 *	o Reading modem by select should be unified into main.c
27 */
28#include "defs.h"
29#include <ctype.h>
30#include <sys/uio.h>
31#ifndef isblank
32#define	isblank(c)	((c) == '\t' || (c) == ' ')
33#endif
34#include <sys/time.h>
35#include <fcntl.h>
36#include <errno.h>
37#include <signal.h>
38#include <sys/wait.h>
39#include "timeout.h"
40#include "vars.h"
41
42#define	IBSIZE 200
43
44static int TimeoutSec;
45static int abort_next, timeout_next;
46static int numaborts;
47char *AbortStrings[50];
48char inbuff[IBSIZE];
49
50extern int ChangeParity(char *);
51
52#define	MATCH	1
53#define	NOMATCH	0
54#define	ABORT	-1
55
56static char *
57findblank(p, instring)
58char *p;
59int instring;
60{
61  if (instring) {
62    while (*p) {
63      if (*p == '\\') {
64	strcpy(p, p + 1);
65	if (!*p)
66	  break;
67      } else if (*p == '"')
68	return(p);
69      p++;
70    }
71  } else {
72    while (*p) {
73      if (isblank(*p))
74	return(p);
75      p++;
76    }
77  }
78  return p;
79}
80
81int
82MakeArgs(script, pvect)
83char *script;
84char **pvect;
85{
86  int nargs, nb;
87  int instring;
88
89  nargs = 0;
90  while (*script) {
91    nb = strspn(script, " \t");
92    script += nb;
93    if (*script) {
94      if (*script == '"') {
95	instring = 1;
96	script++;
97	if (*script == '\0')
98	  return(nargs);
99      } else
100	instring = 0;
101      *pvect++ = script;
102      nargs++;
103      script = findblank(script, instring);
104      if (*script)
105	*script++ = '\0';
106    }
107  }
108  *pvect = NULL;
109  return nargs;
110}
111
112/*
113 *  \r	Carrige return character
114 *  \s  Space character
115 *  \n  Line feed character
116 *  \T  Telephone number (defined via `set phone'
117 *  \t  Tab character
118 */
119char *
120ExpandString(str, result, sendmode)
121char *str;
122char *result;
123int sendmode;
124{
125  int addcr = 0;
126
127  if (sendmode)
128    addcr = 1;
129  while (*str) {
130    switch (*str) {
131    case '\\':
132      str++;
133      switch (*str) {
134      case 'c':
135	if (sendmode)
136	  addcr = 0;
137	break;
138      case 'd':		/* Delay 2 seconds */
139        sleep(2); break;
140      case 'p':
141        usleep(250000); break;	/* Pause 0.25 sec */
142      case 'n':
143	*result++ = '\n'; break;
144      case 'r':
145	*result++ = '\r'; break;
146      case 's':
147	*result++ = ' '; break;
148      case 't':
149	*result++ = '\t'; break;
150      case 'P':
151	bcopy(VarAuthKey, result, strlen(VarAuthKey));
152	result += strlen(VarAuthKey);
153	break;
154      case 'T':
155	bcopy(VarPhone, result, strlen(VarPhone));
156	result += strlen(VarPhone);
157	break;
158      case 'U':
159	bcopy(VarAuthName, result, strlen(VarAuthName));
160	result += strlen(VarAuthName);
161	break;
162      default:
163	*result++ = *str; break;
164      }
165      if (*str) str++;
166      break;
167    case '^':
168      str++;
169      if (*str)
170	*result++ = *str++ & 0x1f;
171      break;
172    default:
173      *result++ = *str++;
174      break;
175    }
176  }
177  if (addcr)
178    *result++ = '\r';
179  *result++ = '\0';
180  return(result);
181}
182
183int
184WaitforString(estr)
185char *estr;
186{
187  struct timeval timeout;
188  char *s, *str, ch;
189  char *inp;
190  fd_set rfds;
191  int i, nfds, nb;
192  char buff[200];
193
194#ifdef SIGALRM
195  int omask;
196  omask = sigblock(sigmask(SIGALRM));
197#endif
198  (void) ExpandString(estr, buff, 0);
199  LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
200  str = buff;
201  inp = inbuff;
202
203  nfds = modem + 1;
204  s = str;
205  for (;;) {
206    FD_ZERO(&rfds);
207    FD_SET(modem, &rfds);
208    /*
209     *  Because it is not clear whether select() modifies timeout value,
210     *  it is better to initialize timeout values everytime.
211     */
212    timeout.tv_sec = TimeoutSec;
213    timeout.tv_usec = 0;
214    i = select(nfds, &rfds, NULL, NULL, &timeout);
215#ifdef notdef
216    TimerService();
217#endif
218    if (i < 0) {
219#ifdef SIGALRM
220      if (errno == EINTR)
221	continue;
222      sigsetmask(omask);
223#endif
224      perror("select");
225      *inp = 0;
226      return(NOMATCH);
227    } else if (i == 0) { 	/* Timeout reached! */
228      *inp = 0;
229      if (inp != inbuff)
230      LogPrintf(LOG_CHAT, "got: %s\n", inbuff);
231      LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec);
232#ifdef SIGALRM
233      sigsetmask(omask);
234#endif
235      return(NOMATCH);
236    }
237    if (FD_ISSET(modem, &rfds)) {	/* got something */
238      if (DEV_IS_SYNC) {
239        nb = read(modem, inbuff, IBSIZE-1);
240	inbuff[nb] = 0;
241	if (strstr(inbuff, str)) {
242#ifdef SIGALRM
243          sigsetmask(omask);
244#endif
245	  return(MATCH);
246	}
247	for (i = 0; i < numaborts; i++) {
248	  if (strstr(inbuff, AbortStrings[i])) {
249	    LogPrintf(LOG_CHAT, "Abort: %s\n", AbortStrings[i]);
250#ifdef SIGALRM
251            sigsetmask(omask);
252#endif
253	    return(ABORT);
254	  }
255	}
256      } else {
257        read(modem, &ch, 1);
258        *inp++ = ch;
259        if (ch == *s) {
260	  s++;
261	  if (*s == '\0') {
262#ifdef SIGALRM
263            sigsetmask(omask);
264#endif
265	    *inp = 0;
266	    return(MATCH);
267	  }
268        } else {
269	  s = str;
270	  if (inp == inbuff+ IBSIZE) {
271	    bcopy(inp - 100, inbuff, 100);
272	    inp = inbuff + 100;
273	  }
274	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
275	    int len;
276	    char *s1;
277
278	    s1 = AbortStrings[i];
279	    len = strlen(s1);
280	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
281	      LogPrintf(LOG_CHAT, "Abort: %s\n", s1);
282	      *inp = 0;
283#ifdef SIGALRM
284      	      sigsetmask(omask);
285#endif
286	      return(ABORT);
287	    }
288	  }
289        }
290      }
291    }
292  }
293#ifdef SIGALRM
294  sigsetmask(omask);
295#endif
296}
297
298void
299ExecStr(command, out)
300char *command, *out;
301{
302  int pid;
303  int fids[2];
304  char *vector[20];
305  int stat, nb;
306  char *cp;
307  char tmp[300];
308  extern int errno;
309
310  cp = inbuff + strlen(inbuff) - 1;
311  while (cp > inbuff) {
312    if (*cp < ' ' && *cp != '\t') {
313      cp++;
314      break;
315    }
316    cp--;
317  }
318  sprintf(tmp, "%s %s", command, cp);
319  (void) MakeArgs(tmp, &vector);
320
321  pipe(fids);
322  pid = fork();
323  if (pid == 0) {
324    signal(SIGINT, SIG_DFL);
325    signal(SIGQUIT, SIG_DFL);
326    signal(SIGTERM, SIG_DFL);
327    signal(SIGHUP, SIG_DFL);
328    close(fids[0]);
329    dup2(fids[1], 1);
330    close(fids[1]);
331    nb = open("/dev/tty", O_RDWR);
332    dup2(nb, 0);
333LogPrintf(LOG_CHAT, "exec: %s\n", command);
334    pid = execvp(command, vector);
335    LogPrintf(LOG_CHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
336    exit(127);
337  } else {
338    close(fids[1]);
339    for (;;) {
340      nb = read(fids[0], out, 1);
341      if (nb <= 0)
342	break;
343      out++;
344    }
345    *out = '\0';
346    close(fids[0]);
347    close(fids[1]);
348    waitpid(pid, &stat, WNOHANG);
349  }
350}
351
352void
353SendString(str)
354char *str;
355{
356  char *cp;
357  int nb, on;
358  char buff[200];
359
360  if (abort_next) {
361    abort_next = 0;
362    ExpandString(str, buff, 0);
363    AbortStrings[numaborts++] = strdup(buff);
364  } else if (timeout_next) {
365    timeout_next = 0;
366    TimeoutSec = atoi(str);
367    if (TimeoutSec <= 0)
368      TimeoutSec = 30;
369  } else {
370    if (*str == '!') {
371      (void) ExpandString(str+1, buff+2, 0);
372      ExecStr(buff + 2, buff + 2);
373    } else {
374      (void) ExpandString(str, buff+2, 1);
375    }
376    LogPrintf(LOG_CHAT, "sending: %s\n", buff+2);
377    cp = buff;
378    if (DEV_IS_SYNC)
379      bcopy("\377\003", buff, 2);	/* Prepend HDLC header */
380    else
381      cp += 2;
382    on = strlen(cp);
383    nb = write(modem, cp, on);
384  }
385}
386
387int
388ExpectString(str)
389char *str;
390{
391  char *minus;
392  int state;
393
394  if (strcmp(str, "ABORT") == 0) {
395    ++abort_next;
396    return(MATCH);
397  }
398  if (strcmp(str, "TIMEOUT") == 0) {
399    ++timeout_next;
400    return(MATCH);
401  }
402  LogPrintf(LOG_CHAT, "Expecting %s\n", str);
403  while (*str) {
404    /*
405     *  Check whether if string contains sub-send-expect.
406     */
407    for (minus = str; *minus; minus++) {
408      if (*minus == '-') {
409	if (minus == str || minus[-1] != '\\')
410	  break;
411      }
412    }
413    if (*minus == '-') {      /* We have sub-send-expect. */
414      *minus++ = '\0';
415      state = WaitforString(str);
416      if (state != NOMATCH)
417	return(state);
418      /*
419       * Can't get expect string. Sendout send part.
420       */
421      str = minus;
422      for (minus = str; *minus; minus++) {
423	if (*minus == '-') {
424	  if (minus == str || minus[-1] != '\\')
425	    break;
426	}
427      }
428      if (*minus == '-') {
429	*minus++ = '\0';
430	SendString(str);
431	str = minus;
432      } else {
433	SendString(str);
434	return(MATCH);
435      }
436    } else {
437      /*
438       *  Simple case. Wait for string.
439       */
440      return(WaitforString(str));
441    }
442  }
443  return(MATCH);
444}
445
446int
447DoChat(script)
448char *script;
449{
450  char *vector[20];
451  char **argv;
452  int argc, n, state;
453#ifdef DEBUG
454  int i;
455#endif
456
457  timeout_next = abort_next = 0;
458  for (n = 0; AbortStrings[n]; n++) {
459    free(AbortStrings[n]);
460    AbortStrings[n] = NULL;
461  }
462  numaborts = 0;
463
464  bzero(vector, sizeof(vector));
465  n = MakeArgs(script, &vector);
466#ifdef DEBUG
467  logprintf("n = %d\n", n);
468  for (i = 0; i < n; i++)
469    logprintf("  %s\n", vector[i]);
470#endif
471  argc = n;
472  argv = vector;
473  TimeoutSec = 30;
474  while (*argv) {
475    if (strcmp(*argv, "P_ZERO") == 0 ||
476	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
477      ChangeParity(*argv++);
478      continue;
479    }
480    state = ExpectString(*argv++);
481    switch (state) {
482    case MATCH:
483      if (*argv)
484	SendString(*argv++);
485      break;
486    case ABORT:
487#ifdef notdef
488      HangupModem();
489#endif
490    case NOMATCH:
491      return(NOMATCH);
492    }
493  }
494  return(MATCH);
495}
496