xref: /386bsd/usr/src/libexec/uucp/chat.c (revision a2142627)
1 /* chat.c
2    Chat routine for the UUCP package.
3 
4    Copyright (C) 1991, 1992 Ian Lance Taylor
5 
6    This file is part of the Taylor UUCP package.
7 
8    This program is free software; you can redistribute it and/or
9    modify it under the terms of the GNU General Public License as
10    published by the Free Software Foundation; either version 2 of the
11    License, or (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22    The author of the program may be contacted at ian@airs.com or
23    c/o AIRS, P.O. Box 520, Waltham, MA 02254.
24 
25    $Log: chat.c,v $
26    Revision 1.26  1992/04/03  05:37:11  ian
27    Minor cleanups for gcc 2.1
28 
29    Revision 1.25  1992/03/28  21:47:55  ian
30    David J. MacKenzie: allow backslash to quote newline in config files
31 
32    Revision 1.24  1992/03/28  21:28:00  ian
33    David J. MacKenzie: handle empty subexpect strings correctly
34 
35    Revision 1.23  1992/03/28  19:57:22  ian
36    David J. MacKenzie: send port device for /Y rather than port name
37 
38    Revision 1.22  1992/03/17  01:03:03  ian
39    Miscellaneous cleanup
40 
41    Revision 1.21  1992/03/16  21:21:59  ian
42    Scott Ballantyne: go ahead and send an illegal send script character
43 
44    Revision 1.20  1992/03/16  00:47:15  ian
45    Turn off DEBUG_PORT for chat script debugging
46 
47    Revision 1.19  1992/03/12  19:56:10  ian
48    Debugging based on types rather than number
49 
50    Revision 1.18  1992/03/11  19:53:55  ian
51    Improved chat script debugging
52 
53    Revision 1.17  1992/03/04  00:36:44  ian
54    Michael Richardson: better chat script debugging
55 
56    Revision 1.16  1992/03/03  06:06:48  ian
57    T. William Wells: don't complain about missing configuration files
58 
59    Revision 1.15  1992/02/19  19:36:07  ian
60    Rearranged time functions
61 
62    Revision 1.14  1992/02/19  05:24:07  ian
63    Bob Denny: if no trailing send string, don't send a carriage return
64 
65    Revision 1.13  1992/02/08  03:54:18  ian
66    Include <string.h> only in <uucp.h>, added 1992 copyright
67 
68    Revision 1.12  1992/02/02  06:38:22  ian
69    Michael Nolan: chat script strings might be separated by more than just ' '
70 
71    Revision 1.11  1992/01/05  03:11:06  ian
72    Made fcsend static
73 
74    Revision 1.10  1991/12/28  03:49:23  ian
75    Added HAVE_MEMFNS and HAVE_BFNS; changed uses of memset to bzero
76 
77    Revision 1.9  1991/12/17  23:14:08  ian
78    T. William Wells: allow dialer complete and abort to be chat scripts
79 
80    Revision 1.8  1991/12/15  04:17:11  ian
81    Added chat-seven-bit command to control parity bit stripping
82 
83    Revision 1.7  1991/12/15  03:42:33  ian
84    Added tprocess_chat_cmd for all chat commands, and added CMDTABTYPE_PREFIX
85 
86    Revision 1.6  1991/12/13  04:27:33  ian
87    Franc,ois Pinard: add some chat script debugging messages
88 
89    Revision 1.5  1991/12/07  17:58:38  ian
90    Handle a chat script with nothing but a send string
91 
92    Revision 1.4  1991/12/06  21:10:27  ian
93    Franc,ois Pinard: ccescape should never return a negative number
94 
95    Revision 1.3  1991/11/11  23:47:24  ian
96    Added chat-program to run a program to do a chat script
97 
98    Revision 1.2  1991/11/11  19:32:03  ian
99    Added breceive_char to read characters through protocol buffering
100 
101    Revision 1.1  1991/09/10  19:38:16  ian
102    Initial revision
103 
104    */
105 
106 #include "uucp.h"
107 
108 #if USE_RCS_ID
109 char chat_rcsid[] = "$Id: chat.c,v 1.26 1992/04/03 05:37:11 ian Rel $";
110 #endif
111 
112 #include <ctype.h>
113 #include <errno.h>
114 
115 #include "port.h"
116 #include "system.h"
117 
118 /* Local functions.  */
119 
120 static int ccescape P((char *zbuf));
121 static int icexpect P((int cstrings, char **azstrings, int *aclens,
122 		       int ctimeout, boolean fstrip));
123 static boolean fcsend P((const char *zsend,
124 			 const struct ssysteminfo *qsys,
125 			 const struct sdialer *qdial,
126 			 const char *zphone,
127 			 boolean ftranslate));
128 static boolean fcecho_send P((const char *z, int clen));
129 static boolean fcphone P((const struct sdialer *qdial, const char *zphone,
130 			  boolean (*pfwrite) P((const char *zwrite,
131 						int cwrite)),
132 			  boolean ftranslate, boolean *pfquote));
133 static boolean fctranslate P((const char *zphone, const char **pzprefix,
134 			      const char **pzsuffix));
135 static boolean fcprogram P((const char *zprogram,
136 			    const struct ssysteminfo *qsys,
137 			    const struct sdialer *qdial,
138 			    const char *zphone, const char *zport,
139 			    long ibaud));
140 
141 /* Run a chat script with the other system.  The chat script is a
142    series of expect send pairs.  We wait for the expect string to show
143    up, and then we send the send string.  The chat string for a system
144    holds the expect and send strings separated by a single space.  */
145 
146 boolean
fchat(qchat,qsys,qdial,zphone,ftranslate,zport,ibaud)147 fchat (qchat, qsys, qdial, zphone, ftranslate, zport, ibaud)
148      const struct schat_info *qchat;
149      const struct ssysteminfo *qsys;
150      const struct sdialer *qdial;
151      const char *zphone;
152      boolean ftranslate;
153      const char *zport;
154      long ibaud;
155 {
156   const char *zchat;
157   int cstrings;
158   char **azstrings;
159   int *aclens;
160   char *zbuf;
161 
162   /* First run the program, if any.  */
163   if (qchat->zprogram != NULL)
164     {
165       if (! fcprogram (qchat->zprogram, qsys, qdial, zphone, zport, ibaud))
166 	return FALSE;
167     }
168 
169   /* If there's no chat script, we're done.  */
170   if (qchat->zchat == NULL)
171     return TRUE;
172 
173   zchat = qchat->zchat;
174 
175   if (qchat->zfail == NULL)
176     {
177       cstrings = 1;
178       azstrings = (char **) alloca (sizeof (char *));
179       aclens = (int *) alloca (sizeof (int));
180     }
181   else
182     {
183       const char *zlook;
184       char *zcopy, *z;
185 
186       /* We leave string number 0 for the chat script; after that
187 	 we want 1 more than the number of spaces in the chat_fail
188 	 string (the fencepost problem).  */
189       cstrings = 2;
190       for (zlook = qchat->zfail; *zlook != '\0'; zlook++)
191 	if (*zlook == ' ')
192 	  ++cstrings;
193 
194       azstrings = (char **) alloca (cstrings * sizeof (char *));
195       aclens = (int *) alloca (cstrings * sizeof (int));
196 
197       zcopy = (char *) alloca (strlen (qchat->zfail) + 1);
198       strcpy (zcopy, qchat->zfail);
199 
200       /* Get the strings into the array, and handle all the escape
201 	 characters.  */
202       cstrings = 1;
203       azstrings[1] = zcopy;
204       for (z = zcopy; *z != '\0'; z++)
205 	{
206 	  if (*z == ' ')
207 	    {
208 	      *z++ = '\0';
209 	      aclens[cstrings] = ccescape (azstrings[cstrings]);
210 	      ++cstrings;
211 	      azstrings[cstrings] = z;
212 	    }
213 	}
214       aclens[cstrings] = ccescape (azstrings[cstrings]);
215       ++cstrings;
216     }
217 
218   zbuf = (char *) alloca (strlen (zchat) + 1);
219 
220   while (*zchat != '\0')
221     {
222       int cchatlen;
223       char *znext;
224 
225       /* Get this expect string into zbuf.  */
226       cchatlen = strcspn (zchat, " \t");
227       strncpy (zbuf, zchat, cchatlen);
228       zbuf[cchatlen] = '\0';
229       zchat += cchatlen;
230       zchat += strspn (zchat, " \t");
231 
232       /* Separate out the first subexpect string.  */
233       azstrings[0] = zbuf;
234       znext = strchr (zbuf, '-');
235       if (znext != NULL)
236 	*znext = '\0';
237       aclens[0] = ccescape (azstrings[0]);
238 
239       /* Loop over subexpects and subsends.  */
240       while (TRUE)
241 	{
242 	  char *zsub;
243 
244 	  if (aclens[0] == 0
245 	      || (aclens[0] == 2
246 		  && strcmp (azstrings[0], "\"\"") == 0))
247 	    {
248 	      /* There is no subexpect sequence.  If there is a
249 		 subsend sequence we move on to it.  Otherwise we let
250 		 this expect succeed.  This is somewhat inconsistent,
251 		 but it seems to be the traditional approach.  */
252 	      if (znext == NULL)
253 		break;
254 	    }
255 	  else
256 	    {
257 	      int istr;
258 
259 	      istr = icexpect (cstrings, azstrings, aclens,
260 			       qchat->ctimeout, qchat->fstrip);
261 
262 	      /* If we found the string, break out of the
263 		 subexpect/subsend loop.  */
264 	      if (istr == 0)
265 		break;
266 
267 	      /* If we got an error, return FALSE.  */
268 	      if (istr < -1)
269 		return FALSE;
270 
271 	      /* If we found a failure string, log it and get out.  */
272 	      if (istr > 0)
273 		{
274 		  const char *zfail;
275 		  int clen;
276 		  char *zcopy;
277 
278 		  zfail = qchat->zfail;
279 		  for (--istr; istr > 0; --istr)
280 		    zfail = strchr (zfail, ' ') + 1;
281 		  clen = strcspn (zfail, " ");
282 		  zcopy = (char *) alloca (clen + 1);
283 		  strncpy (zcopy, zfail, clen);
284 		  zcopy[clen] = '\0';
285 		  ulog (LOG_ERROR, "Chat script failed: Got \"%s\"",
286 			zcopy);
287 		  return FALSE;
288 		}
289 
290 	      /* We timed out; look for a send subsequence.  If none,
291 		 the chat script has failed.  */
292 	      if (znext == NULL)
293 		{
294 		  ulog (LOG_ERROR, "Timed out in chat script");
295 		  return FALSE;
296 		}
297 	    }
298 
299 	  /* Send the send subsequence.  A \"\" will send nothing.  An
300 	     empty string will send a carriage return.  */
301 	  ++znext;
302 	  zsub = znext;
303 	  znext = strchr (zsub, '-');
304 	  if (znext != NULL)
305 	    *znext = '\0';
306 	  if (! fcsend (zsub, qsys, qdial, zphone, ftranslate))
307 	    return FALSE;
308 
309 	  /* If there is no expect subsequence, we are done.  */
310 	  if (znext == NULL)
311 	    break;
312 
313 	  /* Move on to next expect subsequence.  */
314 	  ++znext;
315 	  azstrings[0] = znext;
316 	  znext = strchr (azstrings[0], '-');
317 	  if (znext != NULL)
318 	    *znext = '\0';
319 	  aclens[0] = ccescape (azstrings[0]);
320 	}
321 
322       /* We matched the expect string.  */
323       if (*zchat == '\0')
324 	return TRUE;
325 
326       /* Copy the send string into zbuf.  */
327       cchatlen = strcspn (zchat, " \t");
328       strncpy (zbuf, zchat, cchatlen);
329       zbuf[cchatlen] = '\0';
330       zchat += cchatlen;
331       zchat += strspn (zchat, " \t");
332 
333       if (*zbuf != '\0')
334 	{
335 	  if (! fcsend (zbuf, qsys, qdial, zphone, ftranslate))
336 	    return FALSE;
337 	}
338     }
339 
340   /* The chat sequence has been completed.  */
341   return TRUE;
342 }
343 
344 /* Translate escape sequences within an expect string.  */
345 
346 static int
ccescape(z)347 ccescape (z)
348      char *z;
349 {
350   char *zto, *zfrom;
351 
352   zto = z;
353   zfrom = z;
354   while (*zfrom != '\0')
355     {
356       if (*zfrom != '\\')
357 	{
358 	  *zto++ = *zfrom++;
359 	  continue;
360 	}
361       ++zfrom;
362       switch (*zfrom)
363 	{
364 	case 'b':
365 	  *zto++ = '\b';
366 	  break;
367 	case 'n':
368 	  *zto++ = '\n';
369 	  break;
370 	case 'N':
371 	  *zto++ = '\0';
372 	  break;
373 	case 'r':
374 	  *zto++ = '\r';
375 	  break;
376 	case 's':
377 	  *zto++ = ' ';
378 	  break;
379 	case 't':
380 	  *zto++ = '\t';
381 	  break;
382 	case '\0':
383 	  --zfrom;
384 	  /* Fall through.  */
385 	case '\\':
386 	  *zto++ = '\\';
387 	  break;
388 	case '0': case '1': case '2': case '3': case '4':
389 	case '5': case '6': case '7': case '8': case '9':
390 	  {
391 	    int i;
392 
393 	    i = *zfrom - '0';
394 	    if (zfrom[1] >= '0' && zfrom[1] <= '7')
395 	      i = 8 * i + *++zfrom - '0';
396 	    if (zfrom[1] >= '0' && zfrom[1] <= '7')
397 	      i = 8 * i + *++zfrom - '0';
398 	    *zto++ = (char) i;
399 	  }
400 	  break;
401 	case 'x':
402 	  {
403 	    int i;
404 
405 	    i = 0;
406 	    while (isxdigit (BUCHAR (zfrom[1])))
407 	      {
408 		if (isdigit (BUCHAR (zfrom[1])))
409 		  i = 16 * i + *++zfrom - '0';
410 		else if (isupper (BUCHAR (zfrom[1])))
411 		  i = 16 * i + *++zfrom - 'A';
412 		else
413 		  i = 16 * i + *++zfrom - 'a';
414 	      }
415 	    *zto++ = (char) i;
416 	  }
417 	  break;
418 	default:
419 	  ulog (LOG_ERROR,
420 		"Unrecognized escape sequence \\%c in expect string",
421 		*zfrom);
422 	  *zto++ = *zfrom;
423 	  break;
424 	}
425 
426       ++zfrom;
427     }
428 
429   *zto = '\0';
430 
431   return zto - z;
432 }
433 
434 /* Read characters and wait for one of a set of memory strings to come
435    in.  This returns the index into the array of the string that
436    arrives, or -1 on timeout, or -2 on error.  */
437 
438 static int
icexpect(cstrings,azstrings,aclens,ctimeout,fstrip)439 icexpect (cstrings, azstrings, aclens, ctimeout, fstrip)
440      int cstrings;
441      char **azstrings;
442      int *aclens;
443      int ctimeout;
444      boolean fstrip;
445 {
446   int i;
447   int cmin, cmax;
448   char *zhave;
449   int chave;
450   long iendtime;
451 #if DEBUG > 1
452   int cchars;
453   int iolddebug;
454 #endif
455 
456   cmax = cmin = aclens[0];
457   for (i = 1; i < cstrings; i++)
458     {
459       if (cmax < aclens[i])
460 	cmax = aclens[i];
461       if (cmin > aclens[i])
462 	cmin = aclens[i];
463     }
464 
465   zhave = (char *) alloca (cmax);
466   chave = 0;
467 
468   iendtime = isysdep_time ((long *) NULL) + ctimeout;
469 
470 #if DEBUG > 1
471   cchars = 0;
472   iolddebug = iDebug;
473   if (FDEBUGGING (DEBUG_CHAT))
474     {
475       udebug_buffer ("icexpect: Looking for", azstrings[0],
476 		     aclens[0]);
477       ulog (LOG_DEBUG_START, "icexpect: Got \"");
478       iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
479     }
480 #endif
481 
482   while (TRUE)
483     {
484       int bchar;
485 
486       /* If we have no more time, get out.  */
487       if (ctimeout <= 0)
488 	{
489 #if DEBUG > 1
490 	  if (FDEBUGGING (DEBUG_CHAT))
491 	    {
492 	      ulog (LOG_DEBUG_END, "\" (timed out)");
493 	      iDebug = iolddebug;
494 	    }
495 #endif
496 	  return -1;
497 	}
498 
499       /* Read one character at a time.  We could use a more complex
500 	 algorithm to read in larger batches, but it's probably not
501 	 worth it.  If the buffer is full, shift it left; we already
502 	 know that no string matches, and the buffer holds the largest
503 	 string, so this can't lose a match.  */
504       if (chave >= cmax)
505 	{
506 	  xmemmove (zhave, zhave + 1, cmax - 1);
507 	  --chave;
508 	}
509 
510       /* The timeout/error return values from breceive_char are the
511 	 same as for this function.  */
512       bchar = breceive_char (ctimeout, TRUE);
513       if (bchar < 0)
514 	{
515 #if DEBUG > 1
516 	  if (FDEBUGGING (DEBUG_CHAT))
517 	    {
518 	      /* If there was an error, it will probably be logged in
519 		 the middle of our string, but this is only debugging
520 		 so it's not a big deal.  */
521 	      ulog (LOG_DEBUG_END, "\" (%s)",
522 		    bchar == -1 ? "timed out" : "error");
523 	      iDebug = iolddebug;
524 	    }
525 #endif
526 	  return bchar;
527 	}
528 
529       /* Strip the parity bit if desired.  */
530       if (fstrip)
531 	bchar &= 0x7f;
532 
533       zhave[chave] = (char) bchar;
534       ++chave;
535 
536 #if DEBUG > 1
537       if (FDEBUGGING (DEBUG_CHAT))
538 	{
539 	  char ab[5];
540 
541 	  ++cchars;
542 	  if (cchars > 60)
543 	    {
544 	      ulog (LOG_DEBUG_END, "\"");
545 	      ulog (LOG_DEBUG_START, "icexpect: Got \"");
546 	      cchars = 0;
547 	    }
548 	  (void) cdebug_char (ab, bchar);
549 	  ulog (LOG_DEBUG_CONTINUE, "%s", ab);
550 	}
551 #endif
552 
553       /* See if any of the strings can be found in the buffer.  Since
554 	 we read one character at a time, the string can only be found
555 	 at the end of the buffer.  */
556       for (i = 0; i < cstrings; i++)
557 	{
558 	  if (aclens[i] <= chave
559 	      && memcmp (zhave + chave - aclens[i], azstrings[i],
560 			 aclens[i]) == 0)
561 	    {
562 #if DEBUG > 1
563 	      if (FDEBUGGING (DEBUG_CHAT))
564 		{
565 		  if (i == 0)
566 		    ulog (LOG_DEBUG_END, "\" (found it)");
567 		  else
568 		    {
569 		      ulog (LOG_DEBUG_END, "\"");
570 		      udebug_buffer ("icexpect: Found", azstrings[i],
571 				     aclens[i]);
572 		    }
573 		  iDebug = iolddebug;
574 		}
575 #endif
576 	      return i;
577 	    }
578 	}
579 
580       ctimeout = (int) (iendtime - isysdep_time ((long *) NULL));
581     }
582 }
583 
584 #if DEBUG > 1
585 
586 /* Debugging function for fcsend.  This takes the fquote variable, the
587    length of the string (0 if this an informational string which can
588    be printed directly) and the string itself.  It returns the new
589    value for fquote.  The fquote variable is TRUE if the debugging
590    output is in the middle of a quoted string.  */
591 
592 static int cCsend_chars;
593 static int iColddebug;
594 
595 static boolean fcsend_debug P((boolean, int, const char *));
596 
597 static boolean
fcsend_debug(fquote,clen,zbuf)598 fcsend_debug (fquote, clen, zbuf)
599      boolean fquote;
600      int clen;
601      const char *zbuf;
602 {
603   int cwas;
604 
605   if (! FDEBUGGING (DEBUG_CHAT))
606     return TRUE;
607 
608   cwas = cCsend_chars;
609   if (clen > 0)
610     cCsend_chars += clen;
611   else
612     cCsend_chars += strlen (zbuf);
613   if (cCsend_chars > 60 && cwas > 10)
614     {
615       ulog (LOG_DEBUG_END, "%s", fquote ? "\"" : "");
616       fquote = FALSE;
617       ulog (LOG_DEBUG_START, "fcsend: Writing");
618       cCsend_chars = 0;
619     }
620 
621   if (clen == 0)
622     {
623       ulog (LOG_DEBUG_CONTINUE, "%s %s", fquote ? "\"" : "", zbuf);
624       return FALSE;
625     }
626   else
627     {
628       int i;
629 
630       if (! fquote)
631 	ulog (LOG_DEBUG_CONTINUE, " \"");
632       for (i = 0; i < clen; i++)
633 	{
634 	  char ab[5];
635 
636 	  (void) cdebug_char (ab, zbuf[i]);
637 	  ulog (LOG_DEBUG_CONTINUE, "%s", ab);
638 	}
639 
640       return TRUE;
641     }
642 }
643 
644 /* Finish up the debugging information for fcsend.  */
645 
646 static void ucsend_debug_end P((boolean, boolean));
647 
648 static void
ucsend_debug_end(fquote,ferr)649 ucsend_debug_end (fquote, ferr)
650      boolean fquote;
651      boolean ferr;
652 {
653   if (! FDEBUGGING (DEBUG_CHAT))
654     return;
655 
656   if (fquote)
657     ulog (LOG_DEBUG_CONTINUE, "\"");
658 
659   if (ferr)
660     ulog (LOG_DEBUG_CONTINUE, " (error)");
661 
662   ulog (LOG_DEBUG_END, "%s", "");
663 
664   iDebug = iColddebug;
665 }
666 
667 #else /* DEBUG <= 1 */
668 
669 /* Use macro definitions to make fcsend look neater.  */
670 
671 #define fcsend_debug(fquote, clen, zbuf) TRUE
672 
673 #define ucsend_debug_end(fquote, ferror)
674 
675 #endif /* DEBUG <= 1 */
676 
677 /* Send a string out.  This has to parse escape sequences as it goes.
678    Note that it handles the dialer escape sequences (\e, \E, \D, \T)
679    although they make no sense for chatting with a system.  */
680 
681 static boolean
fcsend(z,qsys,qdial,zphone,ftranslate)682 fcsend (z, qsys, qdial, zphone, ftranslate)
683      const char *z;
684      const struct ssysteminfo *qsys;
685      const struct sdialer *qdial;
686      const char *zphone;
687      boolean ftranslate;
688 {
689   boolean fnocr;
690   boolean (*pfwrite) P((const char *, int));
691   char *zcallout_login;
692   char *zcallout_pass;
693   boolean fquote;
694 
695   if (strcmp (z, "\"\"") == 0)
696     return TRUE;
697 
698   fnocr = FALSE;
699   pfwrite = fport_write;
700   zcallout_login = NULL;
701   zcallout_pass = NULL;
702 
703 #if DEBUG > 1
704   if (FDEBUGGING (DEBUG_CHAT))
705     {
706       ulog (LOG_DEBUG_START, "fcsend: Writing");
707       fquote = FALSE;
708       cCsend_chars = 0;
709       iColddebug = iDebug;
710       iDebug &=~ (DEBUG_OUTGOING | DEBUG_PORT);
711     }
712 #endif
713 
714   while (*z != '\0')
715     {
716       const char *zlook;
717       boolean fsend;
718       char bsend;
719 
720       zlook = z + strcspn (z, "\\BE");
721 
722       if (zlook > z)
723 	{
724 	  fquote = fcsend_debug (fquote, zlook - z, z);
725 	  if (! (*pfwrite) (z, zlook - z))
726 	    {
727 	      ucsend_debug_end (fquote, TRUE);
728 	      return FALSE;
729 	    }
730 	}
731 
732       if (*zlook == '\0')
733 	break;
734 
735       z = zlook;
736 
737       fsend = FALSE;
738       switch (*z)
739 	{
740 	case 'B':
741 	  if (strncmp (z, "BREAK", 5) == 0)
742 	    {
743 	      fquote = fcsend_debug (fquote, 0, "break");
744 	      if (! fport_break ())
745 		{
746 		  ucsend_debug_end (fquote, TRUE);
747 		  return FALSE;
748 		}
749 	      z += 5;
750 	    }
751 	  else
752 	    {
753 	      fsend = TRUE;
754 	      bsend = 'B';
755 	      ++z;
756 	    }
757 	  break;
758 	case 'E':
759 	  if (strncmp (z, "EOT", 3) == 0)
760 	    {
761 	      fsend = TRUE;
762 	      bsend = '\004';
763 	    }
764 	  else
765 	    {
766 	      fsend = TRUE;
767 	      bsend = 'E';
768 	      ++z;
769 	    }
770 	  break;
771 	case '\\':
772 	  ++z;
773 	  switch (*z)
774 	    {
775 	    case 'b':
776 	      fsend = TRUE;
777 	      bsend = '\b';
778 	      break;
779 	    case 'c':
780 	      fnocr = TRUE;
781 	      break;
782 	    case 'd':
783 	      fquote = fcsend_debug (fquote, 0, "sleep");
784 	      usysdep_sleep (1);
785 	      break;
786 	    case 'e':
787 	      fquote = fcsend_debug (fquote, 0, "echo-check-off");
788 	      pfwrite = fport_write;
789 	      break;
790 	    case 'E':
791 	      fquote = fcsend_debug (fquote, 0, "echo-check-on");
792 	      pfwrite = fcecho_send;
793 	      break;
794 	    case 'K':
795 	      fquote = fcsend_debug (fquote, 0, "break");
796 	      if (! fport_break ())
797 		{
798 		  ucsend_debug_end (fquote, TRUE);
799 		  return FALSE;
800 		}
801 	      break;
802 	    case 'n':
803 	      fsend = TRUE;
804 	      bsend = '\n';
805 	      break;
806 	    case 'N':
807 	      fsend = TRUE;
808 	      bsend = '\0';
809 	      break;
810 	    case 'p':
811 	      fquote = fcsend_debug (fquote, 0, "pause");
812 	      usysdep_pause ();
813 	      break;
814 	    case 'r':
815 	      fsend = TRUE;
816 	      bsend = '\r';
817 	      break;
818 	    case 's':
819 	      fsend = TRUE;
820 	      bsend = ' ';
821 	      break;
822 	    case 't':
823 	      fsend = TRUE;
824 	      bsend = '\t';
825 	      break;
826 	    case '\0':
827 	      --z;
828 	      /* Fall through.  */
829 	    case '\\':
830 	      fsend = TRUE;
831 	      bsend = '\\';
832 	      break;
833 	    case '0': case '1': case '2': case '3': case '4':
834 	    case '5': case '6': case '7': case '8': case '9':
835 	      fsend = TRUE;
836 	      bsend = *z - '0';
837 	      if (z[1] >= '0' && z[1] <= '7')
838 		bsend = (char) (8 * bsend + *++z - '0');
839 	      if (z[1] >= '0' && z[1] <= '7')
840 		bsend = (char) (8 * bsend + *++z - '0');
841 	      break;
842 	    case 'x':
843 	      fsend = TRUE;
844 	      bsend = 0;
845 	      while (isxdigit (BUCHAR (z[1])))
846 		{
847 		  if (isdigit (BUCHAR (z[1])))
848 		    bsend = (char) (16 * bsend + *++z - '0');
849 		  else if (isupper (BUCHAR (z[1])))
850 		    bsend = (char) (16 * bsend + *++z - 'A');
851 		  else
852 		    bsend = (char) (16 * bsend + *++z - 'a');
853 		}
854 	      break;
855 	    case 'L':
856 	      {
857 		const char *zlog;
858 
859 		if (qsys == NULL)
860 		  {
861 		    ucsend_debug_end (fquote, TRUE);
862 		    ulog (LOG_ERROR, "Illegal use of \\L");
863 		    return FALSE;
864 		  }
865 		zlog = qsys->zcall_login;
866 		if (zlog == NULL)
867 		  {
868 		    ucsend_debug_end (fquote, TRUE);
869 		    ulog (LOG_ERROR, "No login defined");
870 		    return FALSE;
871 		  }
872 		if (zlog[0] == '*' && zlog[1] == '\0')
873 		  {
874 		    if (zcallout_login == NULL
875 			&& ! fcallout_login (qsys, &zcallout_login,
876 					     &zcallout_pass))
877 		      {
878 			ucsend_debug_end (fquote, TRUE);
879 			return FALSE;
880 		      }
881 		    zlog = zcallout_login;
882 		  }
883 		fquote = fcsend_debug (fquote, 0, "login");
884 		fquote = fcsend_debug (fquote, strlen (zlog), zlog);
885 		if (! (*pfwrite) (zlog, strlen (zlog)))
886 		  {
887 		    ucsend_debug_end (fquote, TRUE);
888 		    return FALSE;
889 		  }
890 	      }
891 	      break;
892 	    case 'P':
893 	      {
894 		const char *zpass;
895 
896 		if (qsys == NULL)
897 		  {
898 		    ucsend_debug_end (fquote, TRUE);
899 		    ulog (LOG_ERROR, "Illegal use of \\P");
900 		    return FALSE;
901 		  }
902 		zpass = qsys->zcall_password;
903 		if (zpass == NULL)
904 		  {
905 		    ucsend_debug_end (fquote, TRUE);
906 		    ulog (LOG_ERROR, "No password defined");
907 		    return FALSE;
908 		  }
909 		if (zpass[0] == '*' && zpass[1] == '\0')
910 		  {
911 		    if (zcallout_pass == NULL
912 			&& ! fcallout_login (qsys, &zcallout_login,
913 					     &zcallout_pass))
914 		      {
915 			ucsend_debug_end (fquote, TRUE);
916 			return FALSE;
917 		      }
918 		    zpass = zcallout_pass;
919 		  }
920 		fquote = fcsend_debug (fquote, 0, "password");
921 		fquote = fcsend_debug (fquote, strlen (zpass), zpass);
922 		if (! (*pfwrite) (zpass, strlen (zpass)))
923 		  {
924 		    ucsend_debug_end (fquote, TRUE);
925 		    return FALSE;
926 		  }
927 	      }
928 	      break;
929 	    case 'D':
930 	      if (qdial == NULL || zphone == NULL)
931 		{
932 		  ucsend_debug_end (fquote, TRUE);
933 		  ulog (LOG_ERROR, "Illegal use of \\D");
934 		  return FALSE;
935 		}
936 	      fquote = fcsend_debug (fquote, 0, "\\D");
937 	      if (! fcphone (qdial, zphone, pfwrite, ftranslate, &fquote))
938 		{
939 		  ucsend_debug_end (fquote, TRUE);
940 		  return FALSE;
941 		}
942 	      break;
943 	    case 'T':
944 	      if (qdial == NULL || zphone == NULL)
945 		{
946 		  ucsend_debug_end (fquote, TRUE);
947 		  ulog (LOG_ERROR, "Illegal use of \\T");
948 		  return FALSE;
949 		}
950 	      fquote = fcsend_debug (fquote, 0, "\\T");
951 	      if (! fcphone (qdial, zphone, pfwrite, TRUE, &fquote))
952 		{
953 		  ucsend_debug_end (fquote, TRUE);
954 		  return FALSE;
955 		}
956 	      break;
957 	    case 'M':
958 	      if (qdial == NULL)
959 		{
960 		  ucsend_debug_end (fquote, TRUE);
961 		  ulog (LOG_ERROR, "Illegal use of \\M");
962 		  return FALSE;
963 		}
964 	      fquote = fcsend_debug (fquote, 0, "ignore-carrier");
965 	      if (! fport_no_carrier ())
966 		{
967 		  ucsend_debug_end (fquote, TRUE);
968 		  return FALSE;
969 		}
970 	      break;
971 	    case 'm':
972 	      if (qdial == NULL)
973 		{
974 		  ucsend_debug_end (fquote, TRUE);
975 		  ulog (LOG_ERROR, "Illegal use of \\m");
976 		  return FALSE;
977 		}
978 	      fquote = fcsend_debug (fquote, 0, "need-carrier");
979 	      if (! fport_need_carrier ())
980 		{
981 		  ucsend_debug_end (fquote, TRUE);
982 		  return FALSE;
983 		}
984 	      break;
985 	    default:
986 	      /* This error message will screw up any debugging
987 		 information, but it's easily avoidable.  */
988 	      ulog (LOG_ERROR,
989 		    "Unrecognized escape sequence \\%c in send string",
990 		    *z);
991 	      fsend = TRUE;
992 	      bsend = *z;
993 	      break;
994 	    }
995 	  ++z;
996 	  break;
997 #if DEBUG > 0
998 	default:
999 	  ulog (LOG_FATAL, "fcsend: Can't happen");
1000 	  break;
1001 #endif
1002 	}
1003 
1004       if (fsend)
1005 	{
1006 	  fquote = fcsend_debug (fquote, 1, &bsend);
1007 	  if (! (*pfwrite) (&bsend, 1))
1008 	    {
1009 	      ucsend_debug_end (fquote, TRUE);
1010 	      return FALSE;
1011 	    }
1012 	}
1013     }
1014 
1015   /* Clobber and free the login and password names that came from
1016      the call out file.  We probably shouldn't even keep them around
1017      this long.  */
1018 
1019   if (zcallout_login != NULL)
1020     {
1021       bzero (zcallout_login, strlen (zcallout_login));
1022       xfree ((pointer) zcallout_login);
1023     }
1024   if (zcallout_pass != NULL)
1025     {
1026       bzero (zcallout_pass, strlen (zcallout_pass));
1027       xfree ((pointer) zcallout_pass);
1028     }
1029 
1030   /* Output a final carriage return, unless there was a \c.  Don't
1031      bother to check for an echo.  */
1032   if (! fnocr)
1033     {
1034       char b;
1035 
1036       b = '\r';
1037       fquote = fcsend_debug (fquote, 1, &b);
1038       if (! fport_write (&b, 1))
1039 	{
1040 	  ucsend_debug_end (fquote, TRUE);
1041 	  return FALSE;
1042 	}
1043     }
1044 
1045   ucsend_debug_end (fquote, FALSE);
1046 
1047   return TRUE;
1048 }
1049 
1050 /* Write out a phone number with optional dialcode translation.  The
1051    pfquote argument is only used for debugging.  */
1052 
1053 static boolean
fcphone(qdial,zphone,pfwrite,ftranslate,pfquote)1054 fcphone (qdial, zphone, pfwrite, ftranslate, pfquote)
1055      const struct sdialer *qdial;
1056      const char *zphone;
1057      boolean (*pfwrite) P((const char *zwrite, int cwrite));
1058      boolean ftranslate;
1059      boolean *pfquote;
1060 {
1061   const char *zprefix, *zsuffix;
1062 
1063   if (ftranslate)
1064     {
1065       if (! fctranslate (zphone, &zprefix, &zsuffix))
1066 	return FALSE;
1067     }
1068   else
1069     {
1070       zprefix = zphone;
1071       zsuffix = NULL;
1072     }
1073 
1074   while (zprefix != NULL)
1075     {
1076       while (TRUE)
1077 	{
1078 	  const char *z;
1079 	  const char *zstr;
1080 
1081 	  z = zprefix + strcspn (zprefix, "=-");
1082 	  if (z > zprefix)
1083 	    {
1084 	      *pfquote = fcsend_debug (*pfquote, z - zprefix, zprefix);
1085 	      if (! (*pfwrite) (zprefix, z - zprefix))
1086 		return FALSE;
1087 	    }
1088 
1089 	  if (*z == '=')
1090 	    zstr = qdial->zdialtone;
1091 	  else if (*z == '-')
1092 	    zstr = qdial->zpause;
1093 	  else			/* *z == '\0' */
1094 	    break;
1095 
1096 	  if (zstr != NULL)
1097 	    {
1098 	      *pfquote = fcsend_debug (*pfquote, strlen (zstr), zstr);
1099 	      if (! (*pfwrite) (zstr, strlen (zstr)))
1100 		return FALSE;
1101 	    }
1102 
1103 	  zprefix = z + 1;
1104 	}
1105 
1106       zprefix = zsuffix;
1107       zsuffix = NULL;
1108     }
1109 
1110   return TRUE;
1111 }
1112 
1113 /* Given a phone number, run it through dial code translation
1114    returning two strings.  */
1115 
1116 static boolean
fctranslate(zphone,pzprefix,pzsuffix)1117 fctranslate (zphone, pzprefix, pzsuffix)
1118      const char *zphone;
1119      const char **pzprefix;
1120      const char **pzsuffix;
1121 {
1122   char *zdialcode, *zto;
1123   const char *zfrom;
1124 
1125   *pzprefix = zphone;
1126   *pzsuffix = NULL;
1127 
1128   if (zDialcodefile == NULL)
1129     return TRUE;
1130 
1131   zdialcode = (char *) alloca (strlen (zphone) + 1);
1132   zfrom = zphone;
1133   zto = zdialcode;
1134   while (*zfrom != '\0' && isalpha (BUCHAR (*zfrom)))
1135     *zto++ = *zfrom++;
1136   *zto = '\0';
1137 
1138   if (*zdialcode != '\0')
1139     {
1140       struct smulti_file *qmulti;
1141       struct scmdtab as[2];
1142       char *zpre;
1143 
1144       qmulti = qmulti_open (zDialcodefile);
1145       if (qmulti == NULL)
1146 	return FALSE;
1147 
1148       as[0].zcmd = zdialcode;
1149       as[0].itype = CMDTABTYPE_STRING;
1150       as[0].pvar = (pointer) &zpre;
1151       as[0].ptfn = NULL;
1152       as[1].zcmd = NULL;
1153 
1154       zpre = NULL;
1155 
1156       uprocesscmds ((FILE *) NULL, qmulti, as, (const char *) NULL,
1157 		    CMDFLAG_BACKSLASH);
1158 
1159       (void) fmulti_close (qmulti);
1160 
1161       if (zpre == NULL)
1162 	ulog (LOG_ERROR, "Unknown dial code %s", zdialcode);
1163       else
1164 	{
1165 	  *pzprefix = zpre;
1166 	  *pzsuffix = zfrom;
1167 	}
1168     }
1169 
1170   return TRUE;
1171 }
1172 
1173 /* Write out a string making sure the each character is echoed back.  */
1174 
1175 static boolean
fcecho_send(zwrite,cwrite)1176 fcecho_send (zwrite, cwrite)
1177      const char *zwrite;
1178      int cwrite;
1179 {
1180   const char *zend;
1181 
1182   zend = zwrite + cwrite;
1183 
1184   for (; zwrite < zend; zwrite++)
1185     {
1186       int b;
1187 
1188       if (! fport_write (zwrite, 1))
1189 	return FALSE;
1190       do
1191 	{
1192 	  /* We arbitrarily wait five seconds for the echo.  */
1193 	  b = breceive_char (5, TRUE);
1194 	  /* Now b == -1 on timeout, -2 on error.  */
1195 	  if (b < 0)
1196 	    {
1197 	      if (b == -1)
1198 		ulog (LOG_ERROR, "Character not echoed");
1199 	      return FALSE;
1200 	    }
1201 	}
1202       while (b != BUCHAR (*zwrite));
1203     }
1204 
1205   return TRUE;
1206 }
1207 
1208 /* Run a chat program.  Expand any escape sequences and call a system
1209    dependent program to run it.  */
1210 
1211 static boolean
fcprogram(zprogram,qsys,qdial,zphone,zport,ibaud)1212 fcprogram (zprogram, qsys, qdial, zphone, zport, ibaud)
1213      const char *zprogram;
1214      const struct ssysteminfo *qsys;
1215      const struct sdialer *qdial;
1216      const char *zphone;
1217      const char *zport;
1218      long ibaud;
1219 {
1220   char *zbuf;
1221   int calc, clen;
1222   char *zto;
1223   const char *zfrom;
1224   char *zcallout_login;
1225   char *zcallout_pass;
1226   boolean fret;
1227 
1228   zcallout_login = NULL;
1229   zcallout_pass = NULL;
1230 
1231   /* Copy the string into memory expanding escape sequences.  */
1232 
1233   zbuf = (char *) xmalloc (1);
1234   calc = 1;
1235   clen = 0;
1236   zto = zbuf;
1237   for (zfrom = zprogram; *zfrom != '\0'; zfrom++)
1238     {
1239       const char *zadd;
1240       int cadd;
1241 
1242       if (*zfrom != '\\')
1243 	{
1244 	  if (clen + 2 > calc)
1245 	    {
1246 	      calc = clen + 80;
1247 	      zbuf = (char *) xrealloc ((pointer) zbuf, calc);
1248 	      zto = zbuf + clen;
1249 	    }
1250 	  *zto++ = *zfrom;
1251 	  ++clen;
1252 	  continue;
1253 	}
1254 
1255       ++zfrom;
1256       switch (*zfrom)
1257 	{
1258 	case '\0':
1259 	  --zfrom;
1260 	  /* Fall through.  */
1261 	case '\\':
1262 	  zadd = "\\";
1263 	  break;
1264 	case 'L':
1265 	  {
1266 	    const char *zlog;
1267 
1268 	    if (qsys == NULL)
1269 	      {
1270 		ulog (LOG_ERROR, "chat-program: Illegal use of \\L");
1271 		return FALSE;
1272 	      }
1273 	    zlog = qsys->zcall_login;
1274 	    if (zlog == NULL)
1275 	      {
1276 		ulog (LOG_ERROR, "chat-program: No login defined");
1277 		return FALSE;
1278 	      }
1279 	    if (zlog[0] == '*' && zlog[1] == '\0')
1280 	      {
1281 		if (zcallout_login == NULL
1282 		    && ! fcallout_login (qsys, &zcallout_login,
1283 					 &zcallout_pass))
1284 		  return FALSE;
1285 		zlog = zcallout_login;
1286 	      }
1287 	    zadd = zlog;
1288 	  }
1289 	  break;
1290 	case 'P':
1291 	  {
1292 	    const char *zpass;
1293 
1294 	    if (qsys == NULL)
1295 	      {
1296 		ulog (LOG_ERROR, "chat-program: Illegal use of \\P");
1297 		return FALSE;
1298 	      }
1299 	    zpass = qsys->zcall_password;
1300 	    if (zpass == NULL)
1301 	      {
1302 		ulog (LOG_ERROR, "chat-program: No password defined");
1303 		return FALSE;
1304 	      }
1305 	    if (zpass[0] == '*' && zpass[1] == '\0')
1306 	      {
1307 		if (zcallout_pass == NULL
1308 		    && ! fcallout_login (qsys, &zcallout_login,
1309 					 &zcallout_pass))
1310 		  return FALSE;
1311 		zpass = zcallout_pass;
1312 	      }
1313 	    zadd = zpass;
1314 	  }
1315 	  break;
1316 	case 'D':
1317 	  if (qdial == NULL || zphone == NULL)
1318 	    {
1319 	      ulog (LOG_ERROR, "chat-program: Illegal use of \\D");
1320 	      return FALSE;
1321 	    }
1322 	  zadd = zphone;
1323 	  break;
1324 	case 'T':
1325 	  {
1326 	    const char *zprefix, *zsuffix;
1327 
1328 	    if (qdial == NULL || zphone == NULL)
1329 	      {
1330 		ulog (LOG_ERROR, "chat-program: Illegal use of \\T");
1331 		return FALSE;
1332 	      }
1333 
1334 	    if (! fctranslate (zphone, &zprefix, &zsuffix))
1335 	      return FALSE;
1336 
1337 	    if (zsuffix == NULL)
1338 	      zadd = zprefix;
1339 	    else
1340 	      {
1341 		int cprefix;
1342 
1343 		cprefix = strlen (zprefix);
1344 		if (clen + cprefix + 1 > calc)
1345 		  {
1346 		    calc = clen + cprefix + 20;
1347 		    zbuf = (char *) xrealloc ((pointer) zbuf, calc);
1348 		    zto = zbuf + clen;
1349 		  }
1350 		strcpy (zto, zprefix);
1351 		zto += cprefix;
1352 		clen += cprefix;
1353 		zadd = zsuffix;
1354 	      }
1355 	  }
1356 	  break;
1357 	case 'Y':
1358 	  if (zLdevice == NULL && zport == NULL)
1359 	    {
1360 	      ulog (LOG_ERROR, "chat-program: Illegal use of \\Y");
1361 	      return FALSE;
1362 	    }
1363 	  /* zLdevice will generally make more sense than zport, but
1364 	     it might not be set yet.  */
1365 	  zadd = zLdevice;
1366 	  if (zadd == NULL)
1367 	    zadd = zport;
1368 	  break;
1369 	case 'Z':
1370 	  if (qsys == NULL)
1371 	    {
1372 	      ulog (LOG_ERROR, "chat-program: Illegal use of \\Z");
1373 	      return FALSE;
1374 	    }
1375 	  zadd = qsys->zname;
1376 	  break;
1377 	case 'S':
1378 	  {
1379 	    char *zalc;
1380 
1381 	    if (ibaud == 0)
1382 	      {
1383 		ulog (LOG_ERROR, "chat-program: Illegal use of \\S");
1384 		return FALSE;
1385 	      }
1386 	    zalc = (char *) alloca (15);
1387 	    sprintf (zalc, "%ld", ibaud);
1388 	    zadd = zalc;
1389 	  }
1390 	  break;
1391 	default:
1392 	  {
1393 	    char *zset;
1394 
1395 	    ulog (LOG_ERROR,
1396 		  "chat-program: Unrecognized escape sequence \\%c",
1397 		  *zfrom);
1398 	    zset = (char *) alloca (2);
1399 	    zset[0] = *zfrom;
1400 	    zset[1] = '\0';
1401 	    zadd = zset;
1402 	  }
1403 	  break;
1404 	}
1405 
1406       cadd = strlen (zadd);
1407       if (clen + cadd + 1 > calc)
1408 	{
1409 	  calc = clen + cadd + 20;
1410 	  zbuf = (char *) xrealloc ((pointer) zbuf, calc);
1411 	  zto = zbuf + clen;
1412 	}
1413       strcpy (zto, zadd);
1414       zto += cadd;
1415       clen += cadd;
1416     }
1417 
1418   *zto = '\0';
1419 
1420   /* Invoke the program.  */
1421 
1422   fret = fport_run_chat (zbuf);
1423 
1424   xfree ((pointer) zbuf);
1425 
1426   return fret;
1427 }
1428