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