chat.c revision 6059
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:$
22 *
23 *  TODO:
24 *	o Support more UUCP compatible control sequences.
25 *	o Dialing shoud not block monitor process.
26 */
27#include "defs.h"
28#include <ctype.h>
29#include <sys/uio.h>
30#ifndef isblank
31#define	isblank(c)	((c) == '\t' || (c) == ' ')
32#endif
33#include <sys/time.h>
34#include <fcntl.h>
35#include "timeout.h"
36#include "vars.h"
37
38static int TimeoutSec;
39static int abort_next, timeout_next;
40static int numaborts;
41char *AbortStrings[50];
42
43extern int ChangeParity(char *);
44
45#define	MATCH	1
46#define	NOMATCH	0
47#define	ABORT	-1
48
49static char *
50findblank(p, instring)
51char *p;
52int instring;
53{
54  if (instring) {
55    while (*p) {
56      if (*p == '\\') {
57	strcpy(p, p + 1);
58	if (!*p)
59	  break;
60      } else if (*p == '"')
61	return(p);
62      p++;
63    }
64  } else {
65    while (*p) {
66      if (isblank(*p))
67	return(p);
68      p++;
69    }
70  }
71  return p;
72}
73
74int
75MakeArgs(script, pvect)
76char *script;
77char **pvect;
78{
79  int nargs, nb;
80  int instring;
81
82  nargs = 0;
83  while (*script) {
84    nb = strspn(script, " \t");
85    script += nb;
86    if (*script) {
87      if (*script == '"') {
88	instring = 1;
89	script++;
90	if (*script == '\0')
91	  return(nargs);
92      } else
93	instring = 0;
94      *pvect++ = script;
95      nargs++;
96      script = findblank(script, instring);
97      if (*script)
98	*script++ = '\0';
99    }
100  }
101  *pvect = NULL;
102  return nargs;
103}
104
105/*
106 *  \r	Carrige return character
107 *  \s  Space character
108 *  \n  Line feed character
109 *  \T  Telephone number (defined via `set phone'
110 *  \t  Tab character
111 */
112char *
113ExpandString(str, result, sendmode)
114char *str;
115char *result;
116int sendmode;
117{
118  int addcr = 0;
119
120  if (sendmode)
121    addcr = 1;
122  while (*str) {
123    switch (*str) {
124    case '\\':
125      str++;
126      switch (*str) {
127      case 'c':
128	if (sendmode)
129	  addcr = 0;
130	break;
131      case 'd':		/* Delay 2 seconds */
132        sleep(2); break;
133      case 'p':
134        usleep(250000); break;	/* Pause 0.25 sec */
135      case 'n':
136	*result++ = '\n'; break;
137      case 'r':
138	*result++ = '\r'; break;
139      case 's':
140	*result++ = ' '; break;
141      case 't':
142	*result++ = '\t'; break;
143      case 'P':
144	bcopy(VarAuthKey, result, strlen(VarAuthKey));
145	result += strlen(VarAuthKey);
146	break;
147      case 'T':
148	bcopy(VarPhone, result, strlen(VarPhone));
149	result += strlen(VarPhone);
150	break;
151      case 'U':
152	bcopy(VarAuthName, result, strlen(VarAuthName));
153	result += strlen(VarAuthName);
154	break;
155      default:
156	*result++ = *str; break;
157      }
158      if (*str) str++;
159      break;
160    case '^':
161      str++;
162      if (*str)
163	*result++ = *str++ & 0x1f;
164      break;
165    default:
166      *result++ = *str++;
167      break;
168    }
169  }
170  if (addcr)
171    *result++ = '\r';
172  *result++ = '\0';
173  return(result);
174}
175
176int
177WaitforString(estr)
178char *estr;
179{
180#define	IBSIZE 200
181  struct timeval timeout;
182  char *s, *str, ch;
183  char *inp;
184  fd_set rfds;
185  int i, nfds;
186  char buff[200];
187  char inbuff[IBSIZE];
188
189  (void) ExpandString(estr, buff, 0);
190  LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
191  str = buff;
192  inp = inbuff;
193
194  nfds = modem + 1;
195  s = str;
196  for (;;) {
197    FD_ZERO(&rfds);
198    FD_SET(modem, &rfds);
199    /*
200     *  Because it is not clear whether select() modifies timeout value,
201     *  it is better to initialize timeout values everytime.
202     */
203    timeout.tv_sec = TimeoutSec;
204    timeout.tv_usec = 0;
205
206    i = select(nfds, &rfds, NULL, NULL, &timeout);
207#ifdef notdef
208    TimerService();
209#endif
210    if (i < 0) {
211      perror("select");
212      return(NOMATCH);
213    } else if (i == 0) { 	/* Timeout reached! */
214      LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec);
215      return(NOMATCH);
216    }
217    if (FD_ISSET(modem, &rfds)) {	/* got something */
218      read(modem, &ch, 1);
219      *inp++ = ch;
220      if (ch == *s) {
221	s++;
222	if (*s == '\0') {
223	  return(MATCH);
224	}
225      } else {
226	s = str;
227	if (inp == inbuff+ IBSIZE) {
228	  bcopy(inp - 100, inbuff, 100);
229	  inp = inbuff + 100;
230	}
231	for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
232	  int len;
233	  char *s1;
234
235	  s1 = AbortStrings[i];
236	  len = strlen(s1);
237	  if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
238	    LogPrintf(LOG_CHAT, "Abort: %s\n", s1);
239	    return(ABORT);
240	  }
241	}
242      }
243    }
244  }
245}
246
247void
248SendString(str)
249char *str;
250{
251  char buff[200];
252
253  if (abort_next) {
254    abort_next = 0;
255    ExpandString(str, buff, 0);
256    AbortStrings[numaborts++] = strdup(buff);
257  } else if (timeout_next) {
258    timeout_next = 0;
259    TimeoutSec = atoi(str);
260    if (TimeoutSec <= 0)
261      TimeoutSec = 30;
262  } else {
263    (void) ExpandString(str, buff, 1);
264    LogPrintf(LOG_CHAT, "sending: %s\n", buff);
265    write(modem, buff, strlen(buff));
266  }
267}
268
269int
270ExpectString(str)
271char *str;
272{
273  char *minus;
274  int state;
275
276  if (strcmp(str, "ABORT") == 0) {
277    ++abort_next;
278    return(MATCH);
279  }
280  if (strcmp(str, "TIMEOUT") == 0) {
281    ++timeout_next;
282    return(MATCH);
283  }
284  LogPrintf(LOG_CHAT, "Expecting %s\n", str);
285  while (*str) {
286    /*
287     *  Check whether if string contains sub-send-expect.
288     */
289    for (minus = str; *minus; minus++) {
290      if (*minus == '-') {
291	if (minus == str || minus[-1] != '\\')
292	  break;
293      }
294    }
295    if (*minus == '-') {      /* We have sub-send-expect. */
296      *minus++ = '\0';
297      state = WaitforString(str);
298      if (state != NOMATCH)
299	return(state);
300      /*
301       * Can't get expect string. Sendout send part.
302       */
303      str = minus;
304      for (minus = str; *minus; minus++) {
305	if (*minus == '-') {
306	  if (minus == str || minus[-1] != '\\')
307	    break;
308	}
309      }
310      if (*minus == '-') {
311	*minus++ = '\0';
312	SendString(str);
313	str = minus;
314      } else {
315	SendString(str);
316	return(MATCH);
317      }
318    } else {
319      /*
320       *  Simple case. Wait for string.
321       */
322      return(WaitforString(str));
323    }
324  }
325  return(MATCH);
326}
327
328int
329DoChat(script)
330char *script;
331{
332  char *vector[20];
333  char **argv;
334  int argc, n, state;
335#ifdef DEBUG
336  int i;
337#endif
338
339  timeout_next = abort_next = 0;
340  for (n = 0; AbortStrings[n]; n++) {
341    free(AbortStrings[n]);
342    AbortStrings[n] = NULL;
343  }
344  numaborts = 0;
345
346  bzero(vector, sizeof(vector));
347  n = MakeArgs(script, &vector);
348#ifdef DEBUG
349  logprintf("n = %d\n", n);
350  for (i = 0; i < n; i++)
351    logprintf("  %s\n", vector[i]);
352#endif
353  argc = n;
354  argv = vector;
355  TimeoutSec = 30;
356  while (*argv) {
357    if (strcmp(*argv, "P_ZERO") == 0 ||
358	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
359      ChangeParity(*argv++);
360      continue;
361    }
362    state = ExpectString(*argv++);
363    switch (state) {
364    case MATCH:
365      if (*argv)
366	SendString(*argv++);
367      break;
368    case ABORT:
369#ifdef notdef
370      HangupModem();
371#endif
372    case NOMATCH:
373      return(NOMATCH);
374    }
375  }
376  return(MATCH);
377}
378