1467 lines
32 KiB
C
1467 lines
32 KiB
C
/* chat.c
|
||
Chat routine for the UUCP package.
|
||
|
||
Copyright (C) 1991, 1992, 1993 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 Cygnus Support, Building 200, 1 Kendall Square, Cambridge, MA 02139.
|
||
*/
|
||
|
||
#include "uucp.h"
|
||
|
||
#if USE_RCS_ID
|
||
const char chat_rcsid[] = "$Id: chat.c,v 1.2 1994/10/24 22:17:03 jtc 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)
|
||
{
|
||
char *ztimeout;
|
||
int ctimeout;
|
||
|
||
/* 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];
|
||
|
||
/* \Wnum at the end of the string is a timeout. */
|
||
ctimeout = qchat->uuconf_ctimeout;
|
||
ztimeout = strrchr (azstrings[0], '\\');
|
||
if (ztimeout != NULL && ztimeout[1] == 'W')
|
||
{
|
||
char *zend;
|
||
int cval;
|
||
|
||
cval = (int) strtol (ztimeout + 2, &zend, 10);
|
||
if (zend != ztimeout + 2 && *zend == '\0')
|
||
{
|
||
ctimeout = cval;
|
||
*ztimeout = '\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,
|
||
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 (1);
|
||
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;
|
||
char *zcopy;
|
||
size_t clen;
|
||
|
||
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;
|
||
}
|
||
zcopy = zbufcpy (zlog);
|
||
clen = cescape (zcopy);
|
||
fquote = fcsend_debug (fquote, (size_t) 0, "login");
|
||
fquote = fcsend_debug (fquote, clen, zcopy);
|
||
if (! (*pfwrite) (qconn, zcopy, clen))
|
||
{
|
||
ubuffree (zcopy);
|
||
ucsend_debug_end (fquote, TRUE);
|
||
return FALSE;
|
||
}
|
||
ubuffree (zcopy);
|
||
}
|
||
break;
|
||
case 'P':
|
||
{
|
||
const char *zpass;
|
||
char *zcopy;
|
||
size_t clen;
|
||
|
||
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;
|
||
}
|
||
zcopy = zbufcpy (zpass);
|
||
clen = cescape (zcopy);
|
||
fquote = fcsend_debug (fquote, (size_t) 0, "password");
|
||
fquote = fcsend_debug (fquote, clen, zcopy);
|
||
if (! (*pfwrite) (qconn, zcopy, clen))
|
||
{
|
||
ubuffree (zcopy);
|
||
ucsend_debug_end (fquote, TRUE);
|
||
return FALSE;
|
||
}
|
||
ubuffree (zcopy);
|
||
}
|
||
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;
|
||
char *zfree = 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;
|
||
}
|
||
zfree = zbufcpy (zlog);
|
||
(void) cescape (zfree);
|
||
zadd = zfree;
|
||
}
|
||
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;
|
||
}
|
||
zfree = zbufcpy (zpass);
|
||
(void) cescape (zfree);
|
||
zadd = zfree;
|
||
}
|
||
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;
|
||
ubuffree (zfree);
|
||
}
|
||
|
||
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;
|
||
}
|