chat.c revision 15738
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.9 1996/04/06 02:00:17 ache 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*2+1];
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(s) (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  char *phone;
127
128  if (sendmode)
129    addcr = 1;
130  while (*str) {
131    switch (*str) {
132    case '\\':
133      str++;
134      switch (*str) {
135      case 'c':
136	if (sendmode)
137	  addcr = 0;
138	break;
139      case 'd':		/* Delay 2 seconds */
140        sleep(2); break;
141      case 'p':
142        usleep(250000); break;	/* Pause 0.25 sec */
143      case 'n':
144	*result++ = '\n'; break;
145      case 'r':
146	*result++ = '\r'; break;
147      case 's':
148	*result++ = ' '; break;
149      case 't':
150	*result++ = '\t'; break;
151      case 'P':
152	bcopy(VarAuthKey, result, strlen(VarAuthKey));
153	result += strlen(VarAuthKey);
154	break;
155      case 'T':
156	if (VarNextPhone == NULL) {
157	  strcpy(VarPhoneCopy, VarPhoneList);
158	  VarNextPhone = VarPhoneCopy;
159	}
160	phone = strsep(&VarNextPhone, ":");
161	bcopy(phone, result, strlen(phone));
162	result += strlen(phone);
163	if ((mode & (MODE_INTER|MODE_AUTO)) == MODE_INTER)
164	  fprintf(stderr, "Phone: %s\n", phone);
165	LogPrintf(LOG_PHASE_BIT, "Phone: %s\n", phone);
166	break;
167      case 'U':
168	bcopy(VarAuthName, result, strlen(VarAuthName));
169	result += strlen(VarAuthName);
170	break;
171      default:
172	*result++ = *str; break;
173      }
174      if (*str) str++;
175      break;
176    case '^':
177      str++;
178      if (*str)
179	*result++ = *str++ & 0x1f;
180      break;
181    default:
182      *result++ = *str++;
183      break;
184    }
185  }
186  if (addcr)
187    *result++ = '\r';
188  *result++ = '\0';
189  return(result);
190}
191
192#define MAXLOGBUFF 200
193static char logbuff[MAXLOGBUFF];
194static int loglen = 0;
195
196static void clear_log() {
197  memset(logbuff,0,MAXLOGBUFF);
198  loglen = 0;
199}
200
201static void flush_log() {
202  if ((loglevel & LOG_CONNECT_BIT)
203      || ((loglevel & LOG_CARRIER_BIT)
204	  && strstr(logbuff,"CARRIER"))) {
205    LogPrintf(LOG_CONNECT_BIT|LOG_CARRIER_BIT,"Chat: %s\n",logbuff);
206  }
207  clear_log();
208}
209
210static void connect_log(char *str, int single_p) {
211  int space = MAXLOGBUFF - loglen - 1;
212
213  while (space--) {
214    if (*str == '\n') {
215      flush_log();
216    } else {
217      logbuff[loglen++] = *str;
218    }
219    if (single_p || !*++str) break;
220  }
221  if (!space) flush_log();
222}
223
224
225
226int
227WaitforString(estr)
228char *estr;
229{
230  struct timeval timeout;
231  char *s, *str, ch;
232  char *inp;
233  fd_set rfds;
234  int i, nfds, nb, msg;
235  char buff[200];
236
237
238#ifdef SIGALRM
239  int omask;
240  omask = sigblock(sigmask(SIGALRM));
241#endif
242  clear_log();
243  (void) ExpandString(estr, buff, 0);
244  LogPrintf(LOG_CHAT_BIT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
245  str = buff;
246  inp = inbuff;
247
248  if (strlen(str)>=IBSIZE){
249    str[IBSIZE]=0;
250    LogPrintf(LOG_CHAT_BIT, "Truncating String to %d character: %s\n", IBSIZE, str);
251  }
252
253  nfds = modem + 1;
254  s = str;
255  msg = FALSE;
256  for (;;) {
257    FD_ZERO(&rfds);
258    FD_SET(modem, &rfds);
259    /*
260     *  Because it is not clear whether select() modifies timeout value,
261     *  it is better to initialize timeout values everytime.
262     */
263    timeout.tv_sec = TimeoutSec;
264    timeout.tv_usec = 0;
265    i = select(nfds, &rfds, NULL, NULL, &timeout);
266#ifdef notdef
267    TimerService();
268#endif
269    if (i < 0) {
270#ifdef SIGALRM
271      if (errno == EINTR)
272	continue;
273      sigsetmask(omask);
274#endif
275      perror("select");
276      *inp = 0;
277      return(NOMATCH);
278    } else if (i == 0) { 	/* Timeout reached! */
279      *inp = 0;
280      if (inp != inbuff)
281      LogPrintf(LOG_CHAT_BIT, "got: %s\n", inbuff);
282      LogPrintf(LOG_CHAT_BIT, "can't get (%d).\n", timeout.tv_sec);
283#ifdef SIGALRM
284      sigsetmask(omask);
285#endif
286      return(NOMATCH);
287    }
288    if (FD_ISSET(modem, &rfds)) {	/* got something */
289      if (DEV_IS_SYNC) {
290	int length;
291	if ((length=strlen(inbuff))>IBSIZE){
292	  bcopy(&(inbuff[IBSIZE]),inbuff,IBSIZE+1); /* shuffle down next part*/
293	  length=strlen(inbuff);
294	}
295	nb = read(modem, &(inbuff[length]), IBSIZE);
296	inbuff[nb + length] = 0;
297	connect_log(inbuff,0);
298	if (strstr(inbuff, str)) {
299#ifdef SIGALRM
300          sigsetmask(omask);
301#endif
302	  flush_log();
303	  return(MATCH);
304	}
305	for (i = 0; i < numaborts; i++) {
306	  if (strstr(inbuff, AbortStrings[i])) {
307	    LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", AbortStrings[i]);
308#ifdef SIGALRM
309            sigsetmask(omask);
310#endif
311	    flush_log();
312	    return(ABORT);
313	  }
314	}
315      } else {
316        read(modem, &ch, 1);
317	connect_log(&ch,1);
318        *inp++ = ch;
319        if (ch == *s) {
320	  s++;
321	  if (*s == '\0') {
322#ifdef SIGALRM
323            sigsetmask(omask);
324#endif
325	    *inp = 0;
326	    flush_log();
327	    return(MATCH);
328	  }
329        } else {
330	  s = str;
331	  if (inp == inbuff+ IBSIZE) {
332	    bcopy(inp - 100, inbuff, 100);
333	    inp = inbuff + 100;
334	  }
335	  for (i = 0; i < numaborts; i++) {	/* Look for Abort strings */
336	    int len;
337	    char *s1;
338
339	    s1 = AbortStrings[i];
340	    len = strlen(s1);
341	    if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
342	      LogPrintf(LOG_CHAT_BIT, "Abort: %s\n", s1);
343	      *inp = 0;
344#ifdef SIGALRM
345      	      sigsetmask(omask);
346#endif
347	      flush_log();
348	      return(ABORT);
349	    }
350	  }
351        }
352      }
353    }
354  }
355#ifdef SIGALRM
356  sigsetmask(omask);
357#endif
358}
359
360void
361ExecStr(command, out)
362char *command, *out;
363{
364  int pid;
365  int fids[2];
366  char *vector[20];
367  int stat, nb;
368  char *cp;
369  char tmp[300];
370  extern int errno;
371
372  cp = inbuff + strlen(inbuff) - 1;
373  while (cp > inbuff) {
374    if (*cp < ' ' && *cp != '\t') {
375      cp++;
376      break;
377    }
378    cp--;
379  }
380  sprintf(tmp, "%s %s", command, cp);
381  (void) MakeArgs(tmp, &vector);
382
383  pipe(fids);
384  pid = fork();
385  if (pid == 0) {
386    signal(SIGINT, SIG_DFL);
387    signal(SIGQUIT, SIG_DFL);
388    signal(SIGTERM, SIG_DFL);
389    signal(SIGHUP, SIG_DFL);
390    close(fids[0]);
391    dup2(fids[1], 1);
392    close(fids[1]);
393    nb = open("/dev/tty", O_RDWR);
394    dup2(nb, 0);
395LogPrintf(LOG_CHAT_BIT, "exec: %s\n", command);
396    pid = execvp(command, vector);
397    LogPrintf(LOG_CHAT_BIT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
398    exit(127);
399  } else {
400    close(fids[1]);
401    for (;;) {
402      nb = read(fids[0], out, 1);
403      if (nb <= 0)
404	break;
405      out++;
406    }
407    *out = '\0';
408    close(fids[0]);
409    close(fids[1]);
410    waitpid(pid, &stat, WNOHANG);
411  }
412}
413
414void
415SendString(str)
416char *str;
417{
418  char *cp;
419  int nb, on;
420  char buff[200];
421
422  if (abort_next) {
423    abort_next = 0;
424    ExpandString(str, buff, 0);
425    AbortStrings[numaborts++] = strdup(buff);
426  } else if (timeout_next) {
427    timeout_next = 0;
428    TimeoutSec = atoi(str);
429    if (TimeoutSec <= 0)
430      TimeoutSec = 30;
431  } else {
432    if (*str == '!') {
433      (void) ExpandString(str+1, buff+2, 0);
434      ExecStr(buff + 2, buff + 2);
435    } else {
436      (void) ExpandString(str, buff+2, 1);
437    }
438    if (strstr(str, "\\P")) { /* Do not log the password itself. */
439      LogPrintf(LOG_CHAT_BIT, "sending: %s\n", str);
440    } else {
441      LogPrintf(LOG_CHAT_BIT, "sending: %s\n", buff+2);
442    }
443    cp = buff;
444    if (DEV_IS_SYNC)
445      bcopy("\377\003", buff, 2);	/* Prepend HDLC header */
446    else
447      cp += 2;
448    on = strlen(cp);
449    nb = write(modem, cp, on);
450  }
451}
452
453int
454ExpectString(str)
455char *str;
456{
457  char *minus;
458  int state;
459
460  if (strcmp(str, "ABORT") == 0) {
461    ++abort_next;
462    return(MATCH);
463  }
464  if (strcmp(str, "TIMEOUT") == 0) {
465    ++timeout_next;
466    return(MATCH);
467  }
468  LogPrintf(LOG_CHAT_BIT, "Expecting %s\n", str);
469  while (*str) {
470    /*
471     *  Check whether if string contains sub-send-expect.
472     */
473    for (minus = str; *minus; minus++) {
474      if (*minus == '-') {
475	if (minus == str || minus[-1] != '\\')
476	  break;
477      }
478    }
479    if (*minus == '-') {      /* We have sub-send-expect. */
480      *minus++ = '\0';
481      state = WaitforString(str);
482      if (state != NOMATCH)
483	return(state);
484      /*
485       * Can't get expect string. Sendout send part.
486       */
487      str = minus;
488      for (minus = str; *minus; minus++) {
489	if (*minus == '-') {
490	  if (minus == str || minus[-1] != '\\')
491	    break;
492	}
493      }
494      if (*minus == '-') {
495	*minus++ = '\0';
496	SendString(str);
497	str = minus;
498      } else {
499	SendString(str);
500	return(MATCH);
501      }
502    } else {
503      /*
504       *  Simple case. Wait for string.
505       */
506      return(WaitforString(str));
507    }
508  }
509  return(MATCH);
510}
511
512int
513DoChat(script)
514char *script;
515{
516  char *vector[20];
517  char **argv;
518  int argc, n, state;
519#ifdef DEBUG
520  int i;
521#endif
522
523  timeout_next = abort_next = 0;
524  for (n = 0; AbortStrings[n]; n++) {
525    free(AbortStrings[n]);
526    AbortStrings[n] = NULL;
527  }
528  numaborts = 0;
529
530  bzero(vector, sizeof(vector));
531  n = MakeArgs(script, &vector);
532#ifdef DEBUG
533  logprintf("n = %d\n", n);
534  for (i = 0; i < n; i++)
535    logprintf("  %s\n", vector[i]);
536#endif
537  argc = n;
538  argv = vector;
539  TimeoutSec = 30;
540  while (*argv) {
541    if (strcmp(*argv, "P_ZERO") == 0 ||
542	strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
543      ChangeParity(*argv++);
544      continue;
545    }
546    state = ExpectString(*argv++);
547    switch (state) {
548    case MATCH:
549      if (*argv)
550	SendString(*argv++);
551      break;
552    case ABORT:
553#ifdef notdef
554      HangupModem();
555#endif
556    case NOMATCH:
557      return(NOMATCH);
558    }
559  }
560  return(MATCH);
561}
562