aboutsummaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/common_sources/chat.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/common_sources/chat.c')
-rw-r--r--gnu/libexec/uucp/common_sources/chat.c1429
1 files changed, 1429 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/common_sources/chat.c b/gnu/libexec/uucp/common_sources/chat.c
new file mode 100644
index 000000000000..8544ed2e2027
--- /dev/null
+++ b/gnu/libexec/uucp/common_sources/chat.c
@@ -0,0 +1,1429 @@
+/* chat.c
+ Chat routine for the UUCP package.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#if USE_RCS_ID
+const char chat_rcsid[] = "$Id: chat.c,v 1.1 1993/08/05 18:22:30 conklin Exp $";
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+
+#include "uudefs.h"
+#include "uuconf.h"
+#include "conn.h"
+#include "prot.h"
+#include "system.h"
+
+/* Local functions. */
+
+static int icexpect P((struct sconnection *qconn, int cstrings,
+ char **azstrings, size_t *aclens,
+ int ctimeout, boolean fstrip));
+static boolean fcsend P((struct sconnection *qconn, pointer puuconf,
+ const char *zsend,
+ const struct uuconf_system *qsys,
+ const struct uuconf_dialer *qdial,
+ const char *zphone,
+ boolean ftranslate, boolean fstrip));
+static boolean fcecho_send_strip P((struct sconnection *qconn,
+ const char *z, size_t clen));
+static boolean fcecho_send_nostrip P((struct sconnection *qconn,
+ const char *z, size_t clen));
+static boolean fcecho_send P((struct sconnection *qconn, const char *z,
+ size_t clen, boolean fstrip));
+static boolean fcphone P((struct sconnection *qconn,
+ pointer puuconf,
+ const struct uuconf_dialer *qdial,
+ const char *zphone,
+ boolean (*pfwrite) P((struct sconnection *qc,
+ const char *zwrite,
+ size_t cwrite)),
+ boolean ftranslate, boolean *pfquote));
+static boolean fctranslate P((pointer puuconf, const char *zphone,
+ const char **pzprefix,
+ const char **pzsuffix));
+static boolean fcprogram P((struct sconnection *qconn, pointer puuconf,
+ char **pzprogram,
+ const struct uuconf_system *qsys,
+ const struct uuconf_dialer *qdial,
+ const char *zphone, const char *zport,
+ long ibaud));
+
+/* Run a chat script with the other system. The chat script is a
+ series of expect send pairs. We wait for the expect string to show
+ up, and then we send the send string. The chat string for a system
+ holds the expect and send strings separated by a single space. */
+
+boolean
+fchat (qconn, puuconf, qchat, qsys, qdial, zphone, ftranslate, zport, ibaud)
+ struct sconnection *qconn;
+ pointer puuconf;
+ const struct uuconf_chat *qchat;
+ const struct uuconf_system *qsys;
+ const struct uuconf_dialer *qdial;
+ const char *zphone;
+ boolean ftranslate;
+ const char *zport;
+ long ibaud;
+{
+ int cstrings;
+ char **azstrings;
+ size_t *aclens;
+ char **pzchat;
+ char *zbuf;
+ size_t cbuflen;
+ boolean fret;
+ int i;
+
+ /* First run the program, if any. */
+ if (qchat->uuconf_pzprogram != NULL)
+ {
+ if (! fcprogram (qconn, puuconf, qchat->uuconf_pzprogram, qsys, qdial,
+ zphone, zport, ibaud))
+ return FALSE;
+ }
+
+ /* If there's no chat script, we're done. */
+ if (qchat->uuconf_pzchat == NULL)
+ return TRUE;
+
+ if (qchat->uuconf_pzfail == NULL)
+ {
+ cstrings = 1;
+ azstrings = (char **) xmalloc (sizeof (char *));
+ aclens = (size_t *) xmalloc (sizeof (size_t));
+ }
+ else
+ {
+ char **pz;
+
+ /* We leave string number 0 for the chat script. */
+ cstrings = 1;
+ for (pz = qchat->uuconf_pzfail; *pz != NULL; pz++)
+ ++cstrings;
+
+ azstrings = (char **) xmalloc (cstrings * sizeof (char *));
+ aclens = (size_t *) xmalloc (cstrings * sizeof (size_t));
+
+ /* Get the strings into the array, and handle all the escape
+ characters. */
+ for (cstrings = 1, pz = qchat->uuconf_pzfail;
+ *pz != NULL;
+ cstrings++, pz++)
+ {
+ azstrings[cstrings] = zbufcpy (*pz);
+ aclens[cstrings] = cescape (azstrings[cstrings]);
+ }
+ }
+
+ cbuflen = 0;
+ zbuf = NULL;
+ fret = TRUE;
+
+ pzchat = qchat->uuconf_pzchat;
+
+ while (*pzchat != NULL)
+ {
+ size_t clen;
+
+ /* Loop over subexpects and subsends. */
+ while (TRUE)
+ {
+ /* Copy the expect string into the buffer so that we can
+ modify it in cescape. */
+ clen = strlen (*pzchat);
+ if (clen >= cbuflen)
+ {
+ ubuffree (zbuf);
+ zbuf = zbufalc (clen + 1);
+ cbuflen = clen;
+ }
+ memcpy (zbuf, *pzchat, clen + 1);
+
+ azstrings[0] = zbuf;
+ if (azstrings[0][0] == '-')
+ ++azstrings[0];
+ aclens[0] = cescape (azstrings[0]);
+
+ if (aclens[0] == 0
+ || (aclens[0] == 2
+ && strcmp (azstrings[0], "\"\"") == 0))
+ {
+ /* There is no subexpect sequence. If there is a
+ subsend sequence we move on to it. Otherwise we let
+ this expect succeed. This is somewhat inconsistent,
+ but it seems to be the traditional approach. */
+ if (pzchat[1] == NULL || pzchat[1][0] != '-')
+ break;
+ }
+ else
+ {
+ int istr;
+
+ istr = icexpect (qconn, cstrings, azstrings, aclens,
+ qchat->uuconf_ctimeout,
+ qchat->uuconf_fstrip);
+
+ /* If we found the string, break out of the
+ subexpect/subsend loop. */
+ if (istr == 0)
+ break;
+
+ /* If we got an error, return FALSE. */
+ if (istr < -1)
+ {
+ fret = FALSE;
+ break;
+ }
+
+ /* If we found a failure string, log it and get out. */
+ if (istr > 0)
+ {
+ ulog (LOG_ERROR, "Chat script failed: Got \"%s\"",
+ qchat->uuconf_pzfail[istr - 1]);
+ fret = FALSE;
+ break;
+ }
+
+ /* We timed out; look for a send subsequence. If none,
+ the chat script has failed. */
+ if (pzchat[1] == NULL || pzchat[1][0] != '-')
+ {
+ ulog (LOG_ERROR, "Timed out in chat script");
+ fret = FALSE;
+ break;
+ }
+ }
+
+ /* Send the send subsequence without the leading '-'. A
+ \"\" will send nothing. An empty string will send a
+ carriage return. */
+ ++pzchat;
+ if (! fcsend (qconn, puuconf, *pzchat + 1, qsys, qdial, zphone,
+ ftranslate, qchat->uuconf_fstrip))
+ {
+ fret = FALSE;
+ break;
+ }
+
+ /* If there is no expect subsequence, we are done. */
+ if (pzchat[1] == NULL || pzchat[1][0] != '-')
+ break;
+
+ /* Move on to next expect subsequence. */
+ ++pzchat;
+ }
+
+ if (! fret)
+ break;
+
+ /* Move on to the send string. If there is none, we have
+ succeeded. */
+ do
+ {
+ ++pzchat;
+ }
+ while (*pzchat != NULL && (*pzchat)[0] == '-');
+
+ if (*pzchat == NULL)
+ break;
+
+ if (**pzchat != '\0')
+ {
+ if (! fcsend (qconn, puuconf, *pzchat, qsys, qdial, zphone,
+ ftranslate, qchat->uuconf_fstrip))
+ {
+ fret = FALSE;
+ break;
+ }
+ }
+
+ ++pzchat;
+ }
+
+ ubuffree (zbuf);
+ for (i = 1; i < cstrings; i++)
+ ubuffree (azstrings[i]);
+ xfree ((pointer) azstrings);
+ xfree ((pointer) aclens);
+
+ return fret;
+}
+
+/* Read characters and wait for one of a set of memory strings to come
+ in. This returns the index into the array of the string that
+ arrives, or -1 on timeout, or -2 on error. */
+
+static int
+icexpect (qconn, cstrings, azstrings, aclens, ctimeout, fstrip)
+ struct sconnection *qconn;
+ int cstrings;
+ char **azstrings;
+ size_t *aclens;
+ int ctimeout;
+ boolean fstrip;
+{
+ int i;
+ size_t cmax;
+ char *zhave;
+ size_t chave;
+ long iendtime;
+#if DEBUG > 1
+ int cchars;
+ int iolddebug;
+#endif
+
+ cmax = aclens[0];
+ for (i = 1; i < cstrings; i++)
+ if (cmax < aclens[i])
+ cmax = aclens[i];
+
+ zhave = zbufalc (cmax);
+ chave = 0;
+
+ iendtime = ixsysdep_time ((long *) NULL) + ctimeout;
+
+#if DEBUG > 1
+ cchars = 0;
+ iolddebug = iDebug;
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ udebug_buffer ("icexpect: Looking for", azstrings[0],
+ aclens[0]);
+ ulog (LOG_DEBUG_START, "icexpect: Got \"");
+ iDebug &=~ (DEBUG_INCOMING | DEBUG_PORT);
+ }
+#endif
+
+ while (TRUE)
+ {
+ int bchar;
+
+ /* If we have no more time, get out. */
+ if (ctimeout <= 0)
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ ulog (LOG_DEBUG_END, "\" (timed out)");
+ iDebug = iolddebug;
+ }
+#endif
+ ubuffree (zhave);
+ return -1;
+ }
+
+ /* Read one character at a time. We could use a more complex
+ algorithm to read in larger batches, but it's probably not
+ worth it. If the buffer is full, shift it left; we already
+ know that no string matches, and the buffer holds the largest
+ string, so this can't lose a match. */
+ if (chave >= cmax)
+ {
+ size_t imove;
+
+ for (imove = 0; imove < cmax - 1; imove++)
+ zhave[imove] = zhave[imove + 1];
+ --chave;
+ }
+
+ /* The timeout/error return values from breceive_char are the
+ same as for this function. */
+ bchar = breceive_char (qconn, ctimeout, TRUE);
+ if (bchar < 0)
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ /* If there was an error, it will probably be logged in
+ the middle of our string, but this is only debugging
+ so it's not a big deal. */
+ ulog (LOG_DEBUG_END, "\" (%s)",
+ bchar == -1 ? "timed out" : "error");
+ iDebug = iolddebug;
+ }
+#endif
+ ubuffree (zhave);
+ return bchar;
+ }
+
+ /* Strip the parity bit if desired. */
+ if (fstrip)
+ bchar &= 0x7f;
+
+ zhave[chave] = (char) bchar;
+ ++chave;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ char ab[5];
+
+ ++cchars;
+ if (cchars > 60)
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ ulog (LOG_DEBUG_START, "icexpect: Got \"");
+ cchars = 0;
+ }
+ (void) cdebug_char (ab, bchar);
+ ulog (LOG_DEBUG_CONTINUE, "%s", ab);
+ }
+#endif
+
+ /* See if any of the strings can be found in the buffer. Since
+ we read one character at a time, the string can only be found
+ at the end of the buffer. */
+ for (i = 0; i < cstrings; i++)
+ {
+ if (aclens[i] <= chave
+ && memcmp (zhave + chave - aclens[i], azstrings[i],
+ aclens[i]) == 0)
+ {
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ if (i == 0)
+ ulog (LOG_DEBUG_END, "\" (found it)");
+ else
+ {
+ ulog (LOG_DEBUG_END, "\"");
+ udebug_buffer ("icexpect: Found", azstrings[i],
+ aclens[i]);
+ }
+ iDebug = iolddebug;
+ }
+#endif
+ ubuffree (zhave);
+ return i;
+ }
+ }
+
+ ctimeout = (int) (iendtime - ixsysdep_time ((long *) NULL));
+ }
+}
+
+#if DEBUG > 1
+
+/* Debugging function for fcsend. This takes the fquote variable, the
+ length of the string (0 if this an informational string which can
+ be printed directly) and the string itself. It returns the new
+ value for fquote. The fquote variable is TRUE if the debugging
+ output is in the middle of a quoted string. */
+
+static size_t cCsend_chars;
+static int iColddebug;
+
+static boolean fcsend_debug P((boolean, size_t, const char *));
+
+static boolean
+fcsend_debug (fquote, clen, zbuf)
+ boolean fquote;
+ size_t clen;
+ const char *zbuf;
+{
+ size_t cwas;
+
+ if (! FDEBUGGING (DEBUG_CHAT))
+ return TRUE;
+
+ cwas = cCsend_chars;
+ if (clen > 0)
+ cCsend_chars += clen;
+ else
+ cCsend_chars += strlen (zbuf);
+ if (cCsend_chars > 60 && cwas > 10)
+ {
+ ulog (LOG_DEBUG_END, "%s", fquote ? "\"" : "");
+ fquote = FALSE;
+ ulog (LOG_DEBUG_START, "fcsend: Writing");
+ cCsend_chars = 0;
+ }
+
+ if (clen == 0)
+ {
+ ulog (LOG_DEBUG_CONTINUE, "%s %s", fquote ? "\"" : "", zbuf);
+ return FALSE;
+ }
+ else
+ {
+ int i;
+
+ if (! fquote)
+ ulog (LOG_DEBUG_CONTINUE, " \"");
+ for (i = 0; i < clen; i++)
+ {
+ char ab[5];
+
+ (void) cdebug_char (ab, zbuf[i]);
+ ulog (LOG_DEBUG_CONTINUE, "%s", ab);
+ }
+
+ return TRUE;
+ }
+}
+
+/* Finish up the debugging information for fcsend. */
+
+static void ucsend_debug_end P((boolean, boolean));
+
+static void
+ucsend_debug_end (fquote, ferr)
+ boolean fquote;
+ boolean ferr;
+{
+ if (! FDEBUGGING (DEBUG_CHAT))
+ return;
+
+ if (fquote)
+ ulog (LOG_DEBUG_CONTINUE, "\"");
+
+ if (ferr)
+ ulog (LOG_DEBUG_CONTINUE, " (error)");
+
+ ulog (LOG_DEBUG_END, "%s", "");
+
+ iDebug = iColddebug;
+}
+
+#else /* DEBUG <= 1 */
+
+/* Use macro definitions to make fcsend look neater. */
+
+#define fcsend_debug(fquote, clen, zbuf) TRUE
+
+#define ucsend_debug_end(fquote, ferror)
+
+#endif /* DEBUG <= 1 */
+
+/* Send a string out. This has to parse escape sequences as it goes.
+ Note that it handles the dialer escape sequences (\e, \E, \D, \T)
+ although they make no sense for chatting with a system. */
+
+static boolean
+fcsend (qconn, puuconf, z, qsys, qdial, zphone, ftranslate, fstrip)
+ struct sconnection *qconn;
+ pointer puuconf;
+ const char *z;
+ const struct uuconf_system *qsys;
+ const struct uuconf_dialer *qdial;
+ const char *zphone;
+ boolean ftranslate;
+ boolean fstrip;
+{
+ boolean fnocr;
+ boolean (*pfwrite) P((struct sconnection *, const char *, size_t));
+ char *zcallout_login;
+ char *zcallout_pass;
+ boolean fquote;
+
+ if (strcmp (z, "\"\"") == 0)
+ return TRUE;
+
+ fnocr = FALSE;
+ pfwrite = fconn_write;
+ zcallout_login = NULL;
+ zcallout_pass = NULL;
+
+#if DEBUG > 1
+ if (FDEBUGGING (DEBUG_CHAT))
+ {
+ ulog (LOG_DEBUG_START, "fcsend: Writing");
+ fquote = FALSE;
+ cCsend_chars = 0;
+ iColddebug = iDebug;
+ iDebug &=~ (DEBUG_OUTGOING | DEBUG_PORT);
+ }
+#endif
+
+ while (*z != '\0')
+ {
+ const char *zlook;
+ boolean fsend;
+ char bsend;
+
+ zlook = z + strcspn ((char *) z, "\\BE");
+
+ if (zlook > z)
+ {
+ size_t c;
+
+ c = zlook - z;
+ fquote = fcsend_debug (fquote, c, z);
+ if (! (*pfwrite) (qconn, z, c))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+
+ if (*zlook == '\0')
+ break;
+
+ z = zlook;
+
+ fsend = FALSE;
+ switch (*z)
+ {
+ case 'B':
+ if (strncmp (z, "BREAK", 5) == 0)
+ {
+ fquote = fcsend_debug (fquote, (size_t) 0, "break");
+ if (! fconn_break (qconn))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ fnocr = TRUE;
+ z += 5;
+ }
+ else
+ {
+ fsend = TRUE;
+ bsend = 'B';
+ ++z;
+ }
+ break;
+ case 'E':
+ if (strncmp (z, "EOT", 3) == 0)
+ {
+ fsend = TRUE;
+ bsend = '\004';
+ fnocr = TRUE;
+ z += 3;
+ }
+ else
+ {
+ fsend = TRUE;
+ bsend = 'E';
+ ++z;
+ }
+ break;
+ case '\\':
+ ++z;
+ switch (*z)
+ {
+ case '-':
+ fsend = TRUE;
+ bsend = '-';
+ break;
+ case 'b':
+ fsend = TRUE;
+ bsend = '\b';
+ break;
+ case 'c':
+ fnocr = TRUE;
+ break;
+ case 'd':
+ fquote = fcsend_debug (fquote, (size_t) 0, "sleep");
+ usysdep_sleep (2);
+ break;
+ case 'e':
+ fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-off");
+ pfwrite = fconn_write;
+ break;
+ case 'E':
+ fquote = fcsend_debug (fquote, (size_t) 0, "echo-check-on");
+ if (fstrip)
+ pfwrite = fcecho_send_strip;
+ else
+ pfwrite = fcecho_send_nostrip;
+ break;
+ case 'K':
+ fquote = fcsend_debug (fquote, (size_t) 0, "break");
+ if (! fconn_break (qconn))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ break;
+ case 'n':
+ fsend = TRUE;
+ bsend = '\n';
+ break;
+ case 'N':
+ fsend = TRUE;
+ bsend = '\0';
+ break;
+ case 'p':
+ fquote = fcsend_debug (fquote, (size_t) 0, "pause");
+ usysdep_pause ();
+ break;
+ case 'r':
+ fsend = TRUE;
+ bsend = '\r';
+ break;
+ case 's':
+ fsend = TRUE;
+ bsend = ' ';
+ break;
+ case 't':
+ fsend = TRUE;
+ bsend = '\t';
+ break;
+ case '\0':
+ --z;
+ /* Fall through. */
+ case '\\':
+ fsend = TRUE;
+ bsend = '\\';
+ break;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ fsend = TRUE;
+ bsend = *z - '0';
+ if (z[1] >= '0' && z[1] <= '7')
+ bsend = (char) (8 * bsend + *++z - '0');
+ if (z[1] >= '0' && z[1] <= '7')
+ bsend = (char) (8 * bsend + *++z - '0');
+ break;
+ case 'x':
+ fsend = TRUE;
+ bsend = 0;
+ while (isxdigit (BUCHAR (z[1])))
+ {
+ if (isdigit (BUCHAR (z[1])))
+ bsend = (char) (16 * bsend + *++z - '0');
+ else if (isupper (BUCHAR (z[1])))
+ bsend = (char) (16 * bsend + *++z - 'A');
+ else
+ bsend = (char) (16 * bsend + *++z - 'a');
+ }
+ break;
+ case 'L':
+ {
+ const char *zlog;
+
+ if (qsys == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\L");
+ return FALSE;
+ }
+ zlog = qsys->uuconf_zcall_login;
+ if (zlog == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "No login defined");
+ return FALSE;
+ }
+ if (zlog[0] == '*' && zlog[1] == '\0')
+ {
+ if (zcallout_login == NULL)
+ {
+ int iuuconf;
+
+ iuuconf = uuconf_callout (puuconf, qsys,
+ &zcallout_login,
+ &zcallout_pass);
+ if (iuuconf == UUCONF_NOT_FOUND
+ || zcallout_login == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "No login defined");
+ return FALSE;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ }
+ zlog = zcallout_login;
+ }
+ fquote = fcsend_debug (fquote, (size_t) 0, "login");
+ fquote = fcsend_debug (fquote, strlen (zlog), zlog);
+ if (! (*pfwrite) (qconn, zlog, strlen (zlog)))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+ break;
+ case 'P':
+ {
+ const char *zpass;
+
+ if (qsys == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\P");
+ return FALSE;
+ }
+ zpass = qsys->uuconf_zcall_password;
+ if (zpass == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "No password defined");
+ return FALSE;
+ }
+ if (zpass[0] == '*' && zpass[1] == '\0')
+ {
+ if (zcallout_pass == NULL)
+ {
+ int iuuconf;
+
+ iuuconf = uuconf_callout (puuconf, qsys,
+ &zcallout_login,
+ &zcallout_pass);
+ if (iuuconf == UUCONF_NOT_FOUND
+ || zcallout_pass == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "No password defined");
+ return FALSE;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ }
+ zpass = zcallout_pass;
+ }
+ fquote = fcsend_debug (fquote, (size_t) 0, "password");
+ fquote = fcsend_debug (fquote, strlen (zpass), zpass);
+ if (! (*pfwrite) (qconn, zpass, strlen (zpass)))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+ break;
+ case 'D':
+ if (qdial == NULL || zphone == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\D");
+ return FALSE;
+ }
+ fquote = fcsend_debug (fquote, (size_t) 0, "\\D");
+ if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite,
+ ftranslate, &fquote))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ break;
+ case 'T':
+ if (qdial == NULL || zphone == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\T");
+ return FALSE;
+ }
+ fquote = fcsend_debug (fquote, (size_t) 0, "\\T");
+ if (! fcphone (qconn, puuconf, qdial, zphone, pfwrite, TRUE,
+ &fquote))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ break;
+ case 'M':
+ if (qdial == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\M");
+ return FALSE;
+ }
+ fquote = fcsend_debug (fquote, (size_t) 0, "ignore-carrier");
+ if (! fconn_carrier (qconn, FALSE))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ break;
+ case 'm':
+ if (qdial == NULL)
+ {
+ ucsend_debug_end (fquote, TRUE);
+ ulog (LOG_ERROR, "Illegal use of \\m");
+ return FALSE;
+ }
+ if (qdial->uuconf_fcarrier)
+ {
+ fquote = fcsend_debug (fquote, (size_t) 0, "need-carrier");
+ if (! fconn_carrier (qconn, TRUE))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ /* This error message will screw up any debugging
+ information, but it's easily avoidable. */
+ ulog (LOG_ERROR,
+ "Unrecognized escape sequence \\%c in send string",
+ *z);
+ fsend = TRUE;
+ bsend = *z;
+ break;
+ }
+ ++z;
+ break;
+#if DEBUG > 0
+ default:
+ ulog (LOG_FATAL, "fcsend: Can't happen");
+ break;
+#endif
+ }
+
+ if (fsend)
+ {
+ fquote = fcsend_debug (fquote, (size_t) 1, &bsend);
+ if (! (*pfwrite) (qconn, &bsend, (size_t) 1))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+ }
+
+ xfree ((pointer) zcallout_login);
+ xfree ((pointer) zcallout_pass);
+
+ /* Output a final carriage return, unless there was a \c. Don't
+ bother to check for an echo. */
+ if (! fnocr)
+ {
+ char b;
+
+ b = '\r';
+ fquote = fcsend_debug (fquote, (size_t) 1, &b);
+ if (! fconn_write (qconn, &b, (size_t) 1))
+ {
+ ucsend_debug_end (fquote, TRUE);
+ return FALSE;
+ }
+ }
+
+ ucsend_debug_end (fquote, FALSE);
+
+ return TRUE;
+}
+
+/* Write out a phone number with optional dialcode translation. The
+ pfquote argument is only used for debugging. */
+
+static boolean
+fcphone (qconn, puuconf, qdial, zphone, pfwrite, ftranslate, pfquote)
+ struct sconnection *qconn;
+ pointer puuconf;
+ const struct uuconf_dialer *qdial;
+ const char *zphone;
+ boolean (*pfwrite) P((struct sconnection *qc, const char *zwrite,
+ size_t cwrite));
+ boolean ftranslate;
+ boolean *pfquote;
+{
+ const char *zprefix, *zsuffix;
+
+ if (ftranslate)
+ {
+ if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix))
+ return FALSE;
+ }
+ else
+ {
+ zprefix = zphone;
+ zsuffix = NULL;
+ }
+
+ while (zprefix != NULL)
+ {
+ while (TRUE)
+ {
+ const char *z;
+ const char *zstr;
+
+ z = zprefix + strcspn ((char *) zprefix, "=-");
+ if (z > zprefix)
+ {
+ size_t clen;
+
+ clen = z - zprefix;
+ *pfquote = fcsend_debug (*pfquote, clen, zprefix);
+ if (! (*pfwrite) (qconn, zprefix, clen))
+ return FALSE;
+ }
+
+ if (*z == '=')
+ zstr = qdial->uuconf_zdialtone;
+ else if (*z == '-')
+ zstr = qdial->uuconf_zpause;
+ else /* *z == '\0' */
+ break;
+
+ if (zstr != NULL)
+ {
+ *pfquote = fcsend_debug (*pfquote, strlen (zstr), zstr);
+ if (! (*pfwrite) (qconn, zstr, strlen (zstr)))
+ return FALSE;
+ }
+
+ zprefix = z + 1;
+ }
+
+ zprefix = zsuffix;
+ zsuffix = NULL;
+ }
+
+ return TRUE;
+}
+
+/* Given a phone number, run it through dial code translation
+ returning two strings. */
+
+static boolean
+fctranslate (puuconf, zphone, pzprefix, pzsuffix)
+ pointer puuconf;
+ const char *zphone;
+ const char **pzprefix;
+ const char **pzsuffix;
+{
+ int iuuconf;
+ char *zdialcode, *zto;
+ const char *zfrom;
+ char *ztrans;
+
+ *pzprefix = zphone;
+ *pzsuffix = NULL;
+
+ zdialcode = zbufalc (strlen (zphone) + 1);
+ zfrom = zphone;
+ zto = zdialcode;
+ while (*zfrom != '\0' && isalpha (BUCHAR (*zfrom)))
+ *zto++ = *zfrom++;
+ *zto = '\0';
+
+ if (*zdialcode == '\0')
+ {
+ ubuffree (zdialcode);
+ return TRUE;
+ }
+
+ iuuconf = uuconf_dialcode (puuconf, zdialcode, &ztrans);
+
+ ubuffree (zdialcode);
+
+ if (iuuconf == UUCONF_NOT_FOUND)
+ return TRUE;
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ return FALSE;
+ }
+ else
+ {
+ /* We really should figure out a way to free up ztrans here. */
+ *pzprefix = ztrans;
+ *pzsuffix = zfrom;
+ return TRUE;
+ }
+}
+
+/* Write out a string making sure the each character is echoed back.
+ There are two versions of this function, one which strips the
+ parity bit from the characters and one which does not. This is so
+ that I can use a single function pointer in fcsend, and to avoid
+ using any static variables so that I can put chat scripts in a
+ library some day. */
+
+static boolean
+fcecho_send_strip (qconn, zwrite, cwrite)
+ struct sconnection *qconn;
+ const char *zwrite;
+ size_t cwrite;
+{
+ return fcecho_send (qconn, zwrite, cwrite, TRUE);
+}
+
+static boolean
+fcecho_send_nostrip (qconn, zwrite, cwrite)
+ struct sconnection *qconn;
+ const char *zwrite;
+ size_t cwrite;
+{
+ return fcecho_send (qconn, zwrite, cwrite, FALSE);
+}
+
+static boolean
+fcecho_send (qconn, zwrite, cwrite, fstrip)
+ struct sconnection *qconn;
+ const char *zwrite;
+ size_t cwrite;
+ boolean fstrip;
+{
+ const char *zend;
+
+ zend = zwrite + cwrite;
+
+ for (; zwrite < zend; zwrite++)
+ {
+ int b;
+ char bwrite;
+
+ bwrite = *zwrite;
+ if (! fconn_write (qconn, &bwrite, (size_t) 1))
+ return FALSE;
+ if (fstrip)
+ bwrite &= 0x7f;
+ do
+ {
+ /* We arbitrarily wait five seconds for the echo. */
+ b = breceive_char (qconn, 5, TRUE);
+ /* Now b == -1 on timeout, -2 on error. */
+ if (b < 0)
+ {
+ if (b == -1)
+ ulog (LOG_ERROR, "Character not echoed");
+ return FALSE;
+ }
+ if (fstrip)
+ b &= 0x7f;
+ }
+ while (b != BUCHAR (bwrite));
+ }
+
+ return TRUE;
+}
+
+/* Run a chat program. Expand any escape sequences and call a system
+ dependent program to run it. */
+
+static boolean
+fcprogram (qconn, puuconf, pzprogram, qsys, qdial, zphone, zport, ibaud)
+ struct sconnection *qconn;
+ pointer puuconf;
+ char **pzprogram;
+ const struct uuconf_system *qsys;
+ const struct uuconf_dialer *qdial;
+ const char *zphone;
+ const char *zport;
+ long ibaud;
+{
+ size_t cargs;
+ char **pzpass, **pzarg;
+ char **pz;
+ char *zcallout_login;
+ char *zcallout_pass;
+ boolean fret;
+
+ cargs = 1;
+ for (pz = pzprogram; *pz != NULL; pz++)
+ ++cargs;
+
+ pzpass = (char **) xmalloc (cargs * sizeof (char *));
+
+ zcallout_login = NULL;
+ zcallout_pass = NULL;
+ fret = TRUE;
+
+ /* Copy the string into memory expanding escape sequences. */
+ for (pz = pzprogram, pzarg = pzpass; *pz != NULL; pz++, pzarg++)
+ {
+ const char *zfrom;
+ size_t calc, clen;
+ char *zto;
+
+ if (strchr (*pz, '\\') == NULL)
+ {
+ *pzarg = zbufcpy (*pz);
+ continue;
+ }
+
+ *pzarg = NULL;
+ zto = NULL;
+ calc = 0;
+ clen = 0;
+
+ for (zfrom = *pz; *zfrom != '\0'; zfrom++)
+ {
+ const char *zadd = NULL;
+ size_t cadd;
+ char abadd[15];
+
+ if (*zfrom != '\\')
+ {
+ if (clen + 2 > calc)
+ {
+ char *znew;
+
+ calc = clen + 50;
+ znew = zbufalc (calc);
+ memcpy (znew, *pzarg, clen);
+ ubuffree (*pzarg);
+ *pzarg = znew;
+ zto = znew + clen;
+ }
+ *zto++ = *zfrom;
+ ++clen;
+ continue;
+ }
+
+ ++zfrom;
+ switch (*zfrom)
+ {
+ case '\0':
+ --zfrom;
+ /* Fall through. */
+ case '\\':
+ zadd = "\\";
+ break;
+ case 'L':
+ {
+ const char *zlog;
+
+ if (qsys == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\L");
+ fret = FALSE;
+ break;
+ }
+ zlog = qsys->uuconf_zcall_login;
+ if (zlog == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: No login defined");
+ fret = FALSE;
+ break;
+ }
+ if (zlog[0] == '*' && zlog[1] == '\0')
+ {
+ if (zcallout_login == NULL)
+ {
+ int iuuconf;
+
+ iuuconf = uuconf_callout (puuconf, qsys,
+ &zcallout_login,
+ &zcallout_pass);
+ if (iuuconf == UUCONF_NOT_FOUND
+ || zcallout_login == NULL)
+ {
+ ulog (LOG_ERROR,
+ "chat-program: No login defined");
+ fret = FALSE;
+ break;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ fret = FALSE;
+ break;
+ }
+ }
+ zlog = zcallout_login;
+ }
+ zadd = zlog;
+ }
+ break;
+ case 'P':
+ {
+ const char *zpass;
+
+ if (qsys == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\P");
+ fret = FALSE;
+ break;
+ }
+ zpass = qsys->uuconf_zcall_password;
+ if (zpass == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: No password defined");
+ fret = FALSE;
+ break;
+ }
+ if (zpass[0] == '*' && zpass[1] == '\0')
+ {
+ if (zcallout_pass == NULL)
+ {
+ int iuuconf;
+
+ iuuconf = uuconf_callout (puuconf, qsys,
+ &zcallout_login,
+ &zcallout_pass);
+ if (iuuconf == UUCONF_NOT_FOUND
+ || zcallout_pass == NULL)
+ {
+ ulog (LOG_ERROR,
+ "chat-program: No password defined");
+ fret = FALSE;
+ break;
+ }
+ else if (iuuconf != UUCONF_SUCCESS)
+ {
+ ulog_uuconf (LOG_ERROR, puuconf, iuuconf);
+ fret = FALSE;
+ break;
+ }
+ }
+ zpass = zcallout_pass;
+ }
+ zadd = zpass;
+ }
+ break;
+ case 'D':
+ if (qdial == NULL || zphone == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\D");
+ fret = FALSE;
+ break;
+ }
+ zadd = zphone;
+ break;
+ case 'T':
+ {
+ const char *zprefix, *zsuffix;
+
+ if (qdial == NULL || zphone == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\T");
+ fret = FALSE;
+ break;
+ }
+
+ if (! fctranslate (puuconf, zphone, &zprefix, &zsuffix))
+ {
+ fret = FALSE;
+ break;
+ }
+
+ if (zsuffix == NULL)
+ zadd = zprefix;
+ else
+ {
+ size_t cprefix;
+
+ cprefix = strlen (zprefix);
+ if (clen + cprefix + 1 > calc)
+ {
+ char *znew;
+
+ calc = clen + cprefix + 20;
+ znew = zbufalc (calc);
+ memcpy (znew, *pzarg, clen);
+ ubuffree (*pzarg);
+ *pzarg = znew;
+ zto = znew + clen;
+ }
+ memcpy (zto, zprefix, cprefix);
+ zto += cprefix;
+ clen += cprefix;
+ zadd = zsuffix;
+ }
+ }
+ break;
+ case 'Y':
+ if (zLdevice == NULL && zport == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\Y");
+ fret = FALSE;
+ break;
+ }
+ /* zLdevice will generally make more sense than zport, but
+ it might not be set yet. */
+ zadd = zLdevice;
+ if (zadd == NULL)
+ zadd = zport;
+ break;
+ case 'Z':
+ if (qsys == NULL)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\Z");
+ fret = FALSE;
+ break;
+ }
+ zadd = qsys->uuconf_zname;
+ break;
+ case 'S':
+ {
+ if (ibaud == 0)
+ {
+ ulog (LOG_ERROR, "chat-program: Illegal use of \\S");
+ fret = FALSE;
+ break;
+ }
+ sprintf (abadd, "%ld", ibaud);
+ zadd = abadd;
+ }
+ break;
+ default:
+ {
+ ulog (LOG_ERROR,
+ "chat-program: Unrecognized escape sequence \\%c",
+ *zfrom);
+ abadd[0] = *zfrom;
+ abadd[1] = '\0';
+ zadd = abadd;
+ }
+ break;
+ }
+
+ if (! fret)
+ break;
+
+ cadd = strlen (zadd);
+ if (clen + cadd + 1 > calc)
+ {
+ char *znew;
+
+ calc = clen + cadd + 20;
+ znew = zbufalc (calc);
+ memcpy (znew, *pzarg, clen);
+ ubuffree (*pzarg);
+ *pzarg = znew;
+ zto = znew + clen;
+ }
+ memcpy (zto, zadd, cadd + 1);
+ zto += cadd;
+ clen += cadd;
+ }
+
+ if (! fret)
+ break;
+
+ *zto++ = '\0';
+ ++clen;
+ }
+
+ *pzarg = NULL;
+
+ if (fret)
+ fret = fconn_run_chat (qconn, pzpass);
+
+ for (pz = pzpass; *pz != NULL; pz++)
+ ubuffree (*pz);
+ xfree ((pointer) pzpass);
+ xfree ((pointer) zcallout_login);
+ xfree ((pointer) zcallout_pass);
+
+ return fret;
+}