Update to version 2.0-beta
Allow for NULL fd_sets in descriptor_UpdateSet() Reimplement the entire chat module, creating `struct chat' - a `type' of struct descriptor. Remove CARRIER logging. CONNECT logging now only logs "CONNECT" lines. CHAT logging masks it with an entire log of the conversation. Modem dialing is now asynchronous, including pauses and timeouts :-) The hooks in DoLoop() in main.c are *very* messy ! I'll have to rewrite DoLoop fairly soon, so I don't care too much for the moment. This code is pretty raw.
This commit is contained in:
parent
6b5fb63d1e
commit
b6dec9f07f
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/cvs2svn/branches/MP/; revision=33305
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: auth.c,v 1.27.2.6 1998/02/07 20:49:18 brian Exp $
|
||||
* $Id: auth.c,v 1.27.2.7 1998/02/09 19:20:32 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
* o Implement check against with registered IP addresses.
|
||||
@ -42,7 +42,6 @@
|
||||
#include "loadalias.h"
|
||||
#include "vars.h"
|
||||
#include "auth.h"
|
||||
#include "chat.h"
|
||||
#include "systems.h"
|
||||
#include "lcp.h"
|
||||
#include "hdlc.h"
|
||||
@ -50,6 +49,7 @@
|
||||
#include "link.h"
|
||||
#include "descriptor.h"
|
||||
#include "physical.h"
|
||||
#include "chat.h"
|
||||
#include "lcpproto.h"
|
||||
|
||||
const char *
|
||||
@ -145,7 +145,7 @@ AuthValidate(struct bundle *bundle, const char *fname, const char *system,
|
||||
if (n < 2)
|
||||
continue;
|
||||
if (strcmp(vector[0], system) == 0) {
|
||||
ExpandString(vector[1], passwd, sizeof passwd, 0);
|
||||
chat_ExpandString(NULL, vector[1], passwd, sizeof passwd, 0);
|
||||
if (strcmp(passwd, key) == 0) {
|
||||
CloseSecret(fp);
|
||||
if (n > 2 && !UseHisaddr(bundle, vector[2], 1))
|
||||
@ -184,7 +184,7 @@ AuthGetSecret(struct bundle *bundle, const char *fname, const char *system,
|
||||
if (n < 2)
|
||||
continue;
|
||||
if (strlen(vector[0]) == len && strncmp(vector[0], system, len) == 0) {
|
||||
ExpandString(vector[1], passwd, sizeof passwd, 0);
|
||||
chat_ExpandString(NULL, vector[1], passwd, sizeof passwd, 0);
|
||||
if (setaddr)
|
||||
memset(&IpcpInfo.DefHisAddress, '\0', sizeof IpcpInfo.DefHisAddress);
|
||||
if (n > 2 && setaddr)
|
||||
|
@ -1,30 +1,31 @@
|
||||
/*
|
||||
* Written by Toshiharu OHNO (tony-o@iij.ad.jp)
|
||||
/*-
|
||||
* Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* Chat -- a program for automatic session establishment (i.e. dial
|
||||
* the phone and log in).
|
||||
*
|
||||
* This software is in the public domain.
|
||||
*
|
||||
* Please send all bug reports, requests for information, etc. to:
|
||||
*
|
||||
* Karl Fox <karl@MorningStar.Com>
|
||||
* Morning Star Technologies, Inc.
|
||||
* 1760 Zollinger Road
|
||||
* Columbus, OH 43221
|
||||
* (614)451-1883
|
||||
*
|
||||
* $Id: chat.c,v 1.44.2.5 1998/02/09 19:20:36 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
* o Support more UUCP compatible control sequences.
|
||||
* o Dialing shoud not block monitor process.
|
||||
* o Reading modem by select should be unified into main.c
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
@ -60,6 +61,10 @@
|
||||
#include "chat.h"
|
||||
#include "prompt.h"
|
||||
|
||||
#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
|
||||
|
||||
struct chat chat;
|
||||
|
||||
#ifndef isblank
|
||||
#define isblank(c) ((c) == '\t' || (c) == ' ')
|
||||
#endif
|
||||
@ -78,6 +83,425 @@ static jmp_buf ChatEnv;
|
||||
#define NOMATCH 0
|
||||
#define ABORT -1
|
||||
|
||||
static void ExecStr(struct physical *, char *, char *, int);
|
||||
|
||||
static void
|
||||
chat_PauseTimer(void *v)
|
||||
{
|
||||
struct chat *c = (struct chat *)v;
|
||||
StopTimer(&c->pause);
|
||||
c->pause.state = TIMER_STOPPED;
|
||||
c->pause.load = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
chat_Pause(struct chat *c, u_long load)
|
||||
{
|
||||
StopTimer(&c->pause);
|
||||
c->pause.state = TIMER_STOPPED;
|
||||
c->pause.load += load;
|
||||
c->pause.func = chat_PauseTimer;
|
||||
c->pause.arg = c;
|
||||
StartTimer(&c->pause);
|
||||
}
|
||||
|
||||
static void
|
||||
chat_TimeoutTimer(void *v)
|
||||
{
|
||||
struct chat *c = (struct chat *)v;
|
||||
StopTimer(&c->timeout);
|
||||
c->TimedOut = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
chat_SetTimeout(struct chat *c)
|
||||
{
|
||||
StopTimer(&c->timeout);
|
||||
c->timeout.state = TIMER_STOPPED;
|
||||
if (c->TimeoutSec > 0) {
|
||||
c->timeout.load = SECTICKS * c->TimeoutSec;
|
||||
c->timeout.func = chat_TimeoutTimer;
|
||||
c->timeout.arg = c;
|
||||
StartTimer(&c->timeout);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
chat_NextChar(char *ptr, char ch)
|
||||
{
|
||||
for (; *ptr; ptr++)
|
||||
if (*ptr == ch)
|
||||
return ptr;
|
||||
else if (*ptr == '\\')
|
||||
if (*++ptr == '\0')
|
||||
return NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
chat_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
|
||||
{
|
||||
struct chat *c = descriptor2chat(d);
|
||||
int special, gotabort, gottimeout, needcr;
|
||||
|
||||
if (c->pause.state == TIMER_RUNNING)
|
||||
return 0;
|
||||
|
||||
if (c->TimedOut) {
|
||||
LogPrintf(LogCHAT, "Expect timeout\n");
|
||||
if ( c->nargptr == NULL)
|
||||
c->state = CHAT_FAILED;
|
||||
else {
|
||||
c->state = CHAT_EXPECT;
|
||||
c->argptr = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (c->state != CHAT_EXPECT && c->state != CHAT_SEND)
|
||||
return 0;
|
||||
|
||||
gottimeout = gotabort = 0;
|
||||
|
||||
if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) {
|
||||
/* Go get the next string */
|
||||
if (c->arg < 0 || c->state == CHAT_SEND)
|
||||
c->state = CHAT_EXPECT;
|
||||
else
|
||||
c->state = CHAT_SEND;
|
||||
|
||||
special = 1;
|
||||
while (special && (c->nargptr || c->arg < c->argc - 1)) {
|
||||
if (c->arg < 0 || !c->TimedOut)
|
||||
c->nargptr = NULL;
|
||||
|
||||
if (c->nargptr != NULL) {
|
||||
/* We're doing expect-send-expect.... */
|
||||
c->argptr = c->nargptr;
|
||||
/* Put the '-' back in case we ever want to rerun our script */
|
||||
c->nargptr[-1] = '-';
|
||||
c->nargptr = chat_NextChar(c->nargptr, '-');
|
||||
if (c->nargptr != NULL)
|
||||
*c->nargptr++ = '\0';
|
||||
} else {
|
||||
int minus;
|
||||
|
||||
c->argptr = c->argv[++c->arg];
|
||||
|
||||
if (c->state == CHAT_EXPECT) {
|
||||
/* Look for expect-send-expect sequence */
|
||||
c->nargptr = c->argptr;
|
||||
minus = 0;
|
||||
while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) {
|
||||
c->nargptr++;
|
||||
minus++;
|
||||
}
|
||||
|
||||
if (minus % 2)
|
||||
LogPrintf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of"
|
||||
" '-' chars, all ignored\n", c->argptr);
|
||||
else if (minus) {
|
||||
c->nargptr = chat_NextChar(c->argptr, '-');
|
||||
*c->nargptr++ = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* c->argptr now temporarily points into c->script (via c->argv)
|
||||
* If it's an expect-send-expect sequence, we've just got the correct
|
||||
* portion of that sequence.
|
||||
*/
|
||||
|
||||
needcr = c->state == CHAT_SEND && *c->argptr != '!';
|
||||
|
||||
/* We leave room for a potential HDLC header in the target string */
|
||||
chat_ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr);
|
||||
|
||||
if (gotabort) {
|
||||
if (c->numaborts < sizeof c->AbortStrings / sizeof c->AbortStrings[0])
|
||||
c->AbortStrings[c->numaborts++] = strdup(c->exp+2);
|
||||
else
|
||||
LogPrintf(LogERROR, "chat_UpdateSet: AbortStrings overflow\n");
|
||||
gotabort = 0;
|
||||
} else if (gottimeout) {
|
||||
c->TimeoutSec = atoi(c->exp + 2);
|
||||
if (c->TimeoutSec <= 0)
|
||||
c->TimeoutSec = 30;
|
||||
gottimeout = 0;
|
||||
} else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT"))
|
||||
gotabort = 1;
|
||||
else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT"))
|
||||
gottimeout = 1;
|
||||
else {
|
||||
if (c->exp[2] == '!')
|
||||
ExecStr(c->physical, c->exp + 3, c->exp + 2, sizeof c->exp - 2);
|
||||
|
||||
if (c->exp[2] == '\0')
|
||||
/* Empty string, reparse (this may be better as a `goto start') */
|
||||
return chat_UpdateSet(d, r, w, e, n);
|
||||
|
||||
special = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (special) {
|
||||
if (gottimeout)
|
||||
LogPrintf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n");
|
||||
else if (gotabort)
|
||||
LogPrintf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n");
|
||||
|
||||
/* End of script - all ok */
|
||||
c->state = CHAT_DONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* set c->argptr to point in the right place */
|
||||
c->argptr = c->exp + 2;
|
||||
c->arglen = strlen(c->argptr);
|
||||
|
||||
if (c->state == CHAT_EXPECT) {
|
||||
/* We must check to see if the string's already been found ! */
|
||||
char *begin, *end;
|
||||
|
||||
end = c->bufend - c->arglen + 1;
|
||||
if (end < c->bufstart)
|
||||
end = c->bufstart;
|
||||
for (begin = c->bufstart; begin < end; begin++)
|
||||
if (!strncmp(begin, c->argptr, c->arglen)) {
|
||||
c->bufstart = begin + c->arglen;
|
||||
c->argptr += c->arglen;
|
||||
c->arglen = 0;
|
||||
/* Continue - we've already read our expect string */
|
||||
return chat_UpdateSet(d, r, w, e, n);
|
||||
}
|
||||
|
||||
c->TimedOut = 0;
|
||||
LogPrintf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr);
|
||||
chat_SetTimeout(c);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We now have c->argptr pointing at what we want to expect/send and
|
||||
* c->state saying what we want to do... we now know what to put in
|
||||
* the fd_set :-)
|
||||
*/
|
||||
|
||||
if (c->state == CHAT_EXPECT)
|
||||
return Physical_UpdateSet(&c->physical->desc, r, NULL, e, n, 1);
|
||||
else
|
||||
return Physical_UpdateSet(&c->physical->desc, NULL, w, e, n, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
chat_IsSet(struct descriptor *d, fd_set *fdset)
|
||||
{
|
||||
struct chat *c = descriptor2chat(d);
|
||||
return Physical_IsSet(&c->physical->desc, fdset);
|
||||
}
|
||||
|
||||
static void
|
||||
chat_UpdateLog(struct chat *c, int in)
|
||||
{
|
||||
if (LogIsKept(LogCHAT) || LogIsKept(LogCONNECT)) {
|
||||
/*
|
||||
* If a linefeed appears in the last `in' characters of `c's input
|
||||
* buffer, output from there, all the way back to the last linefeed.
|
||||
* This is called for every read of `in' bytes.
|
||||
*/
|
||||
char *ptr, *end, *stop;
|
||||
int level;
|
||||
|
||||
level = LogIsKept(LogCHAT) ? LogCHAT : LogCONNECT;
|
||||
ptr = c->bufend - in;
|
||||
|
||||
for (end = c->bufend - 1; end >= ptr; end--)
|
||||
if (*end == '\n')
|
||||
break;
|
||||
|
||||
if (end >= ptr) {
|
||||
for (ptr = c->bufend - in - 1; ptr >= c->bufstart; ptr--)
|
||||
if (*ptr == '\n')
|
||||
break;
|
||||
ptr++;
|
||||
stop = NULL;
|
||||
while (stop != end && (stop = strchr(ptr, '\n'))) {
|
||||
*stop = '\0';
|
||||
if (level == LogCHAT || strstr(ptr, "CONNECT"))
|
||||
LogPrintf(level, "Received: %s\n", ptr);
|
||||
*stop = '\n';
|
||||
ptr = stop + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chat_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
|
||||
{
|
||||
struct chat *c = descriptor2chat(d);
|
||||
|
||||
if (c->state == CHAT_EXPECT) {
|
||||
ssize_t in;
|
||||
char *begin, *end;
|
||||
|
||||
/*
|
||||
* XXX - should this read only 1 byte to guarantee that we don't
|
||||
* swallow any ppp talk from the peer ?
|
||||
*/
|
||||
in = BUFLEFT(c);
|
||||
if (in > sizeof c->buf / 2)
|
||||
in = sizeof c->buf / 2;
|
||||
|
||||
in = Physical_Read(c->physical, c->bufend, in);
|
||||
if (in <= 0)
|
||||
return;
|
||||
|
||||
/* `begin' and `end' delimit where we're going to strncmp() from */
|
||||
begin = c->bufend - c->arglen + 1;
|
||||
end = begin + in;
|
||||
if (begin < c->bufstart)
|
||||
begin = c->bufstart;
|
||||
c->bufend += in;
|
||||
|
||||
chat_UpdateLog(c, in);
|
||||
|
||||
if (c->bufend > c->buf + sizeof c->buf / 2) {
|
||||
/* Shuffle our receive buffer back a bit */
|
||||
int chop;
|
||||
|
||||
for (chop = begin - c->buf; chop; chop--)
|
||||
if (c->buf[chop] == '\n')
|
||||
/* found some already-logged garbage to remove :-) */
|
||||
break;
|
||||
if (!chop) {
|
||||
chop = begin - c->buf;
|
||||
}
|
||||
if (chop) {
|
||||
char *from, *to;
|
||||
|
||||
to = c->buf;
|
||||
from = to + chop;
|
||||
while (from < c->bufend)
|
||||
*to++ = *from++;
|
||||
c->bufstart -= chop;
|
||||
c->bufend -= chop;
|
||||
begin -= chop;
|
||||
end -= chop;
|
||||
}
|
||||
}
|
||||
|
||||
for (; begin < end; begin++)
|
||||
if (!strncmp(begin, c->argptr, c->arglen)) {
|
||||
/* Got it ! */
|
||||
if (begin[c->arglen - 1] != '\n') {
|
||||
/* Now coerce chat_UpdateLog() into logging it.... */
|
||||
char ch;
|
||||
|
||||
end = c->bufend;
|
||||
c->bufend = begin + c->arglen;
|
||||
ch = *c->bufend;
|
||||
*c->bufend++ = '\n';
|
||||
chat_UpdateLog(c, 1);
|
||||
*--c->bufend = ch;
|
||||
c->bufend = end;
|
||||
}
|
||||
c->bufstart = begin + c->arglen;
|
||||
c->argptr += c->arglen;
|
||||
c->arglen = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
chat_Write(struct descriptor *d, const fd_set *fdset)
|
||||
{
|
||||
struct chat *c = descriptor2chat(d);
|
||||
|
||||
if (c->state == CHAT_SEND) {
|
||||
int wrote;
|
||||
|
||||
if (strstr(c->argv[c->arg], "\\P")) /* Don't log the password */
|
||||
LogPrintf(LogCHAT, "Send: %s\n", c->argv[c->arg]);
|
||||
else {
|
||||
int sz;
|
||||
|
||||
sz = c->arglen - 1;
|
||||
while (sz >= 0 && c->argptr[sz] == '\n')
|
||||
sz--;
|
||||
LogPrintf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr);
|
||||
}
|
||||
|
||||
if (Physical_IsSync(c->physical)) {
|
||||
/* There's always room for the HDLC header */
|
||||
c->argptr -= 2;
|
||||
c->arglen += 2;
|
||||
memcpy(c->argptr, "\377\003", 2); /* Prepend HDLC header */
|
||||
}
|
||||
|
||||
wrote = Physical_Write(c->physical, c->argptr, c->arglen);
|
||||
if (wrote == -1) {
|
||||
if (errno != EINTR)
|
||||
LogPrintf(LogERROR, "chat_Write: %s\n", strerror(errno));
|
||||
if (Physical_IsSync(c->physical)) {
|
||||
c->argptr += 2;
|
||||
c->arglen -= 2;
|
||||
}
|
||||
} else if (wrote < 2 && Physical_IsSync(c->physical)) {
|
||||
/* Oops - didn't even write our HDLC header ! */
|
||||
c->argptr += 2;
|
||||
c->arglen -= 2;
|
||||
} else {
|
||||
c->argptr += wrote;
|
||||
c->arglen -= wrote;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chat_Init(struct chat *c, struct physical *p, const char *data, int emptybuf)
|
||||
{
|
||||
c->desc.type = CHAT_DESCRIPTOR;
|
||||
c->desc.next = NULL;
|
||||
c->desc.UpdateSet = chat_UpdateSet;
|
||||
c->desc.IsSet = chat_IsSet;
|
||||
c->desc.Read = chat_Read;
|
||||
c->desc.Write = chat_Write;
|
||||
c->physical = p;
|
||||
|
||||
c->state = CHAT_EXPECT;
|
||||
|
||||
strncpy(c->script, data, sizeof c->script - 1);
|
||||
c->script[sizeof c->script - 1] = '\0';
|
||||
c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv));
|
||||
|
||||
c->arg = -1;
|
||||
c->argptr = NULL;
|
||||
c->nargptr = NULL;
|
||||
|
||||
if (emptybuf)
|
||||
c->bufstart = c->bufend = c->buf;
|
||||
|
||||
c->TimeoutSec = 30;
|
||||
c->TimedOut = 0;
|
||||
c->numaborts = 0;
|
||||
|
||||
StopTimer(&c->pause);
|
||||
c->pause.state = TIMER_STOPPED;
|
||||
|
||||
StopTimer(&c->timeout);
|
||||
c->timeout.state = TIMER_STOPPED;
|
||||
}
|
||||
|
||||
void
|
||||
chat_Destroy(struct chat *c)
|
||||
{
|
||||
while (c->numaborts)
|
||||
free(c->AbortStrings[--c->numaborts]);
|
||||
}
|
||||
|
||||
static char *
|
||||
findblank(char *p, int instring)
|
||||
{
|
||||
@ -146,7 +570,8 @@ MakeArgs(char *script, char **pvect, int maxargs)
|
||||
* \U Auth User
|
||||
*/
|
||||
char *
|
||||
ExpandString(const char *str, char *result, int reslen, int sendmode)
|
||||
chat_ExpandString(struct chat *c, const char *str, char *result, int reslen,
|
||||
int sendmode)
|
||||
{
|
||||
int addcr = 0;
|
||||
char *phone;
|
||||
@ -164,10 +589,12 @@ ExpandString(const char *str, char *result, int reslen, int sendmode)
|
||||
addcr = 0;
|
||||
break;
|
||||
case 'd': /* Delay 2 seconds */
|
||||
nointr_sleep(2);
|
||||
if (c != NULL)
|
||||
chat_Pause(c, 2 * SECTICKS);
|
||||
break;
|
||||
case 'p':
|
||||
nointr_usleep(250000);
|
||||
if (c != NULL)
|
||||
chat_Pause(c, SECTICKS / 4);
|
||||
break; /* Pause 0.25 sec */
|
||||
case 'n':
|
||||
*result++ = '\n';
|
||||
@ -241,46 +668,6 @@ ExpandString(const char *str, char *result, int reslen, int sendmode)
|
||||
return (result);
|
||||
}
|
||||
|
||||
#define MAXLOGBUFF LINE_LEN
|
||||
static char logbuff[MAXLOGBUFF];
|
||||
static int loglen = 0;
|
||||
|
||||
static void
|
||||
clear_log(void)
|
||||
{
|
||||
memset(logbuff, 0, MAXLOGBUFF);
|
||||
loglen = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
flush_log(void)
|
||||
{
|
||||
if (LogIsKept(LogCONNECT))
|
||||
LogPrintf(LogCONNECT, "%s\n", logbuff);
|
||||
else if (LogIsKept(LogCARRIER) && strstr(logbuff, "CARRIER"))
|
||||
LogPrintf(LogCARRIER, "%s\n", logbuff);
|
||||
|
||||
clear_log();
|
||||
}
|
||||
|
||||
static void
|
||||
connect_log(const char *str, int single_p)
|
||||
{
|
||||
int space = MAXLOGBUFF - loglen - 1;
|
||||
|
||||
while (space--) {
|
||||
if (*str == '\n') {
|
||||
flush_log();
|
||||
} else {
|
||||
logbuff[loglen++] = *str;
|
||||
}
|
||||
if (single_p || !*++str)
|
||||
break;
|
||||
}
|
||||
if (!space)
|
||||
flush_log();
|
||||
}
|
||||
|
||||
static void
|
||||
ExecStr(struct physical *physical, char *command, char *out, int olen)
|
||||
{
|
||||
@ -364,325 +751,3 @@ ExecStr(struct physical *physical, char *command, char *out, int olen)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
WaitforString(struct physical *physical, const char *estr)
|
||||
{
|
||||
struct timeval timeout;
|
||||
char *s, *str, ch;
|
||||
char *inp;
|
||||
fd_set rfds;
|
||||
int i, nfds, nb;
|
||||
char buff[IBSIZE];
|
||||
#ifdef SIGALRM
|
||||
int omask;
|
||||
|
||||
omask = sigblock(sigmask(SIGALRM));
|
||||
#endif
|
||||
clear_log();
|
||||
if (*estr == '!') {
|
||||
ExpandString(estr + 1, buff, sizeof buff, 0);
|
||||
ExecStr(physical, buff, buff, sizeof buff);
|
||||
} else {
|
||||
ExpandString(estr, buff, sizeof buff, 0);
|
||||
}
|
||||
if (LogIsKept(LogCHAT)) {
|
||||
s = buff + strlen(buff) - 1;
|
||||
while (s >= buff && *s == '\n')
|
||||
s--;
|
||||
if (!strcmp(estr, buff))
|
||||
LogPrintf(LogCHAT, "Wait for (%d): %.*s\n",
|
||||
TimeoutSec, s - buff + 1, buff);
|
||||
else
|
||||
LogPrintf(LogCHAT, "Wait for (%d): %s --> %.*s\n",
|
||||
TimeoutSec, estr, s - buff + 1, buff);
|
||||
}
|
||||
|
||||
if (buff[0] == '\0')
|
||||
return (MATCH);
|
||||
|
||||
str = buff;
|
||||
inp = inbuff;
|
||||
|
||||
if (strlen(str) >= IBSIZE) {
|
||||
str[IBSIZE - 1] = 0;
|
||||
LogPrintf(LogCHAT, "Truncating String to %d character: %s\n", IBSIZE, str);
|
||||
}
|
||||
/* XXX-ML - this look REALLY fishy. */
|
||||
nfds = Physical_GetFD(physical) + 1;
|
||||
s = str;
|
||||
for (;;) {
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(Physical_GetFD(physical), &rfds);
|
||||
|
||||
/*
|
||||
* Because it is not clear whether select() modifies timeout value, it is
|
||||
* better to initialize timeout values everytime.
|
||||
*/
|
||||
timeout.tv_sec = TimeoutSec;
|
||||
timeout.tv_usec = 0;
|
||||
i = select(nfds, &rfds, NULL, NULL, &timeout);
|
||||
#ifdef notdef
|
||||
TimerService();
|
||||
#endif
|
||||
if (i < 0) {
|
||||
#ifdef SIGALRM
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
LogPrintf(LogERROR, "WaitForString: select(): %s\n", strerror(errno));
|
||||
*inp = 0;
|
||||
return (NOMATCH);
|
||||
} else if (i == 0) { /* Timeout reached! */
|
||||
*inp = 0;
|
||||
if (inp != inbuff)
|
||||
LogPrintf(LogCHAT, "Got: %s\n", inbuff);
|
||||
LogPrintf(LogCHAT, "Can't get (%d).\n", timeout.tv_sec);
|
||||
#ifdef SIGALRM
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
return (NOMATCH);
|
||||
}
|
||||
if (Physical_FD_ISSET(physical, &rfds)) { /* got something */
|
||||
if (Physical_IsSync(physical)) {
|
||||
int length;
|
||||
|
||||
if ((length = strlen(inbuff)) > IBSIZE) {
|
||||
/* shuffle down next part */
|
||||
memcpy(inbuff, &(inbuff[IBSIZE]), IBSIZE + 1);
|
||||
length = strlen(inbuff);
|
||||
}
|
||||
if (length + IBSIZE > sizeof(inbuff))
|
||||
abort(); /* Bug & security problem */
|
||||
nb = Physical_Read(physical, &(inbuff[length]), IBSIZE);
|
||||
inbuff[nb + length] = 0;
|
||||
connect_log(inbuff, 0);
|
||||
if (strstr(inbuff, str)) {
|
||||
#ifdef SIGALRM
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
flush_log();
|
||||
return (MATCH);
|
||||
}
|
||||
for (i = 0; i < numaborts; i++) {
|
||||
if (strstr(inbuff, AbortStrings[i])) {
|
||||
LogPrintf(LogCHAT, "Abort: %s\n", AbortStrings[i]);
|
||||
#ifdef SIGALRM
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
flush_log();
|
||||
return (ABORT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (Physical_Read(physical, &ch, 1) < 0) {
|
||||
LogPrintf(LogERROR, "read error: %s\n", strerror(errno));
|
||||
*inp = '\0';
|
||||
return (NOMATCH);
|
||||
}
|
||||
connect_log(&ch, 1);
|
||||
*inp++ = ch;
|
||||
if (ch == *s) {
|
||||
s++;
|
||||
if (*s == '\0') {
|
||||
#ifdef SIGALRM
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
*inp = 0;
|
||||
flush_log();
|
||||
return (MATCH);
|
||||
}
|
||||
} else
|
||||
s = str;
|
||||
if (inp == inbuff + IBSIZE) {
|
||||
memcpy(inbuff, inp - 100, 100);
|
||||
inp = inbuff + 100;
|
||||
}
|
||||
if (s == str) {
|
||||
for (i = 0; i < numaborts; i++) { /* Look for Abort strings */
|
||||
int len;
|
||||
char *s1;
|
||||
|
||||
s1 = AbortStrings[i];
|
||||
len = strlen(s1);
|
||||
if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
|
||||
LogPrintf(LogCHAT, "Abort: %s\n", s1);
|
||||
*inp = 0;
|
||||
#ifdef SIGALRM
|
||||
sigsetmask(omask);
|
||||
#endif
|
||||
flush_log();
|
||||
return (ABORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SendString(struct physical *physical, const char *str)
|
||||
{
|
||||
char *cp;
|
||||
int on;
|
||||
char buff[LINE_LEN];
|
||||
|
||||
if (abort_next) {
|
||||
abort_next = 0;
|
||||
ExpandString(str, buff, sizeof buff, 0);
|
||||
AbortStrings[numaborts++] = strdup(buff);
|
||||
} else if (timeout_next) {
|
||||
timeout_next = 0;
|
||||
TimeoutSec = atoi(str);
|
||||
if (TimeoutSec <= 0)
|
||||
TimeoutSec = 30;
|
||||
} else {
|
||||
if (*str == '!') {
|
||||
ExpandString(str + 1, buff + 2, sizeof buff - 2, 0);
|
||||
ExecStr(physical, buff + 2, buff + 2, sizeof buff - 2);
|
||||
} else {
|
||||
ExpandString(str, buff + 2, sizeof buff - 2, 1);
|
||||
}
|
||||
if (strstr(str, "\\P")) /* Do not log the password itself. */
|
||||
LogPrintf(LogCHAT, "Sending: %s", str);
|
||||
else {
|
||||
cp = buff + strlen(buff + 2) + 1;
|
||||
while (cp >= buff + 2 && *cp == '\n')
|
||||
cp--;
|
||||
LogPrintf(LogCHAT, "Sending: %.*s\n", cp - buff - 1, buff + 2);
|
||||
}
|
||||
cp = buff;
|
||||
if (Physical_IsSync(physical))
|
||||
memcpy(buff, "\377\003", 2); /* Prepend HDLC header */
|
||||
else
|
||||
cp += 2;
|
||||
on = strlen(cp);
|
||||
/* XXX - missing return value check */
|
||||
Physical_Write(physical, cp, on);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ExpectString(struct physical *physical, char *str)
|
||||
{
|
||||
char *minus;
|
||||
int state;
|
||||
|
||||
if (strcmp(str, "ABORT") == 0) {
|
||||
++abort_next;
|
||||
return (MATCH);
|
||||
}
|
||||
if (strcmp(str, "TIMEOUT") == 0) {
|
||||
++timeout_next;
|
||||
return (MATCH);
|
||||
}
|
||||
LogPrintf(LogCHAT, "Expecting: %s\n", str);
|
||||
while (*str) {
|
||||
/*
|
||||
* Check whether if string contains sub-send-expect.
|
||||
*/
|
||||
for (minus = str; *minus; minus++) {
|
||||
if (*minus == '-') {
|
||||
if (minus == str || minus[-1] != '\\')
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*minus == '-') { /* We have sub-send-expect. */
|
||||
*minus = '\0'; /* XXX: Cheat with the const string */
|
||||
state = WaitforString(physical, str);
|
||||
*minus = '-'; /* XXX: Cheat with the const string */
|
||||
minus++;
|
||||
if (state != NOMATCH)
|
||||
return (state);
|
||||
|
||||
/*
|
||||
* Can't get expect string. Sendout send part.
|
||||
*/
|
||||
str = minus;
|
||||
for (minus = str; *minus; minus++) {
|
||||
if (*minus == '-') {
|
||||
if (minus == str || minus[-1] != '\\')
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (*minus == '-') {
|
||||
*minus = '\0'; /* XXX: Cheat with the const string */
|
||||
SendString(physical, str);
|
||||
*minus = '-'; /* XXX: Cheat with the const string */
|
||||
str = ++minus;
|
||||
} else {
|
||||
SendString(physical, str);
|
||||
return (MATCH);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Simple case. Wait for string.
|
||||
*/
|
||||
return (WaitforString(physical, str));
|
||||
}
|
||||
}
|
||||
return (MATCH);
|
||||
}
|
||||
|
||||
static void (*oint) (int);
|
||||
|
||||
static void
|
||||
StopDial(int sig)
|
||||
{
|
||||
LogPrintf(LogPHASE, "DoChat: Caught signal %d, abort connect\n", sig);
|
||||
longjmp(ChatEnv, 1);
|
||||
}
|
||||
|
||||
int
|
||||
DoChat(struct physical *physical, char *script)
|
||||
{
|
||||
char *vector[MAXARGS];
|
||||
char *const *argv;
|
||||
int argc, n, state, err;
|
||||
|
||||
if (!script || !*script)
|
||||
return MATCH;
|
||||
|
||||
if ((err = setjmp(ChatEnv))) {
|
||||
signal(SIGINT, oint);
|
||||
if (err == 1)
|
||||
/* Caught a SIGINT during chat */
|
||||
return (-1);
|
||||
return (NOMATCH);
|
||||
}
|
||||
oint = signal(SIGINT, StopDial);
|
||||
|
||||
timeout_next = abort_next = 0;
|
||||
for (n = 0; AbortStrings[n]; n++) {
|
||||
free(AbortStrings[n]);
|
||||
AbortStrings[n] = NULL;
|
||||
}
|
||||
numaborts = 0;
|
||||
|
||||
memset(vector, '\0', sizeof vector);
|
||||
argc = MakeArgs(script, vector, VECSIZE(vector));
|
||||
argv = vector;
|
||||
TimeoutSec = 30;
|
||||
while (*argv) {
|
||||
if (strcmp(*argv, "P_ZERO") == 0 ||
|
||||
strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
|
||||
modem_SetParity(physical, *argv++);
|
||||
continue;
|
||||
}
|
||||
state = ExpectString(physical, *argv++);
|
||||
switch (state) {
|
||||
case MATCH:
|
||||
if (*argv)
|
||||
SendString(physical, *argv++);
|
||||
break;
|
||||
case ABORT:
|
||||
case NOMATCH:
|
||||
signal(SIGINT, oint);
|
||||
return (NOMATCH);
|
||||
}
|
||||
}
|
||||
signal(SIGINT, oint);
|
||||
return (MATCH);
|
||||
}
|
||||
|
@ -1,29 +1,75 @@
|
||||
/*
|
||||
* Written by Toshiharu OHNO (tony-o@iij.ad.jp)
|
||||
/*-
|
||||
* Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
|
||||
*
|
||||
* Chat -- a program for automatic session establishment (i.e. dial
|
||||
* the phone and log in).
|
||||
*
|
||||
* This software is in the public domain.
|
||||
*
|
||||
* Please send all bug reports, requests for information, etc. to:
|
||||
*
|
||||
* Karl Fox <karl@MorningStar.Com>
|
||||
* Morning Star Technologies, Inc.
|
||||
* 1760 Zollinger Road
|
||||
* Columbus, OH 43221
|
||||
* (614)451-1883
|
||||
*
|
||||
* $Id: chat.h,v 1.9 1997/11/22 03:37:27 brian Exp $
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#define CHAT_EXPECT 0
|
||||
#define CHAT_SEND 1
|
||||
#define CHAT_DONE 2
|
||||
#define CHAT_FAILED 3
|
||||
|
||||
struct chat {
|
||||
struct descriptor desc;
|
||||
struct physical *physical;
|
||||
|
||||
int state; /* Our CHAT_* status */
|
||||
|
||||
char script[LINE_LEN]; /* Our arg buffer */
|
||||
char *argv[MAXARGS]; /* Our arguments (pointing to script) */
|
||||
int argc; /* Number of argv's */
|
||||
|
||||
int arg; /* Our current arg number */
|
||||
char exp[LINE_LEN]; /* Our translated current argument */
|
||||
char *argptr; /* Our current arg pointer */
|
||||
int arglen; /* The length of argptr */
|
||||
char *nargptr; /* Our next for expect-send-expect */
|
||||
|
||||
char buf[LINE_LEN*2]; /* Our input */
|
||||
char *bufstart; /* start of relevent data */
|
||||
char *bufend; /* end of relevent data */
|
||||
|
||||
int TimeoutSec; /* Expect timeout value */
|
||||
int TimedOut; /* We timed out */
|
||||
char *AbortStrings[50]; /* Abort the dial if we get one */
|
||||
int numaborts; /* How many AbortStrings */
|
||||
|
||||
struct pppTimer pause; /* Inactivity timer */
|
||||
struct pppTimer timeout; /* TimeoutSec timer */
|
||||
};
|
||||
|
||||
#define chat2descriptor(c) (&(c)->desc)
|
||||
#define descriptor2chat(d) \
|
||||
((d)->type == CHAT_DESCRIPTOR ? (struct chat *)(d) : NULL)
|
||||
|
||||
void chat_Init(struct chat *, struct physical *, const char *, int);
|
||||
void chat_Destroy(struct chat *);
|
||||
|
||||
extern struct chat chat;
|
||||
|
||||
#define VECSIZE(v) (sizeof(v) / sizeof(v[0]))
|
||||
|
||||
extern char *ExpandString(const char *, char *, int, int);
|
||||
extern char *chat_ExpandString(struct chat *, const char *, char *, int, int);
|
||||
extern int MakeArgs(char *, char **, int); /* Mangles the first arg ! */
|
||||
extern int DoChat(struct physical *, char *); /* passes arg to MakeArgs() */
|
||||
|
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: command.c,v 1.131.2.14 1998/02/10 03:21:39 brian Exp $
|
||||
* $Id: command.c,v 1.131.2.15 1998/02/10 03:23:09 brian Exp $
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
@ -64,7 +64,6 @@
|
||||
#include "loadalias.h"
|
||||
#include "vars.h"
|
||||
#include "systems.h"
|
||||
#include "chat.h"
|
||||
#include "bundle.h"
|
||||
#include "main.h"
|
||||
#include "route.h"
|
||||
@ -78,6 +77,7 @@
|
||||
#include "physical.h"
|
||||
#include "server.h"
|
||||
#include "prompt.h"
|
||||
#include "chat.h"
|
||||
|
||||
struct in_addr ifnetmask;
|
||||
static const char *HIDDEN = "********";
|
||||
@ -169,6 +169,11 @@ DialCommand(struct cmdargs const *arg)
|
||||
int tries;
|
||||
int res;
|
||||
|
||||
if (dialing) {
|
||||
prompt_Printf(&prompt, "A dial is already in progress\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LcpInfo.fsm.state > ST_CLOSED) {
|
||||
prompt_Printf(&prompt, "LCP state is [%s]\n",
|
||||
StateNames[LcpInfo.fsm.state]);
|
||||
@ -184,24 +189,7 @@ DialCommand(struct cmdargs const *arg)
|
||||
if (arg->argc > 0 && (res = LoadCommand(arg)) != 0)
|
||||
return res;
|
||||
|
||||
tries = 0;
|
||||
do {
|
||||
if (tries) {
|
||||
LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n",
|
||||
VarRedialNextTimeout);
|
||||
nointr_sleep(VarRedialNextTimeout);
|
||||
}
|
||||
prompt_Printf(&prompt, "Dial attempt %u of %d\n", ++tries, VarDialTries);
|
||||
if (modem_Open(arg->bundle->physical, arg->bundle) < 0) {
|
||||
prompt_Printf(&prompt, "Failed to open modem.\n");
|
||||
break;
|
||||
}
|
||||
if ((res = modem_Dial(arg->bundle->physical, arg->bundle)) == EX_DONE) {
|
||||
PacketMode(arg->bundle, VarOpenMode);
|
||||
break;
|
||||
} else if (res == EX_SIG)
|
||||
return 1;
|
||||
} while (VarDialTries == 0 || tries < VarDialTries);
|
||||
dial_up = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -23,12 +23,13 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: descriptor.h,v 1.1.2.3 1998/02/10 03:21:55 brian Exp $
|
||||
* $Id: descriptor.h,v 1.1.2.4 1998/02/10 03:23:17 brian Exp $
|
||||
*/
|
||||
|
||||
#define PHYSICAL_DESCRIPTOR (1)
|
||||
#define SERVER_DESCRIPTOR (2)
|
||||
#define PROMPT_DESCRIPTOR (3)
|
||||
#define CHAT_DESCRIPTOR (4)
|
||||
|
||||
struct descriptor {
|
||||
int type;
|
||||
|
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: main.c,v 1.121.2.19 1998/02/10 03:23:28 brian Exp $
|
||||
* $Id: main.c,v 1.121.2.20 1998/02/10 22:28:51 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
* o Add commands for traffic summary, version display, etc.
|
||||
@ -79,6 +79,7 @@
|
||||
#include "physical.h"
|
||||
#include "server.h"
|
||||
#include "prompt.h"
|
||||
#include "chat.h"
|
||||
|
||||
#ifndef O_NONBLOCK
|
||||
#ifdef O_NDELAY
|
||||
@ -88,7 +89,8 @@
|
||||
|
||||
static pid_t BGPid = 0;
|
||||
static char pid_filename[MAXPATHLEN];
|
||||
static int dial_up;
|
||||
int dial_up;
|
||||
int dialing;
|
||||
|
||||
static void DoLoop(struct bundle *);
|
||||
static void TerminalStop(int);
|
||||
@ -603,7 +605,7 @@ DoLoop(struct bundle *bundle)
|
||||
* If we lost carrier and want to re-establish the connection due to the
|
||||
* "set reconnect" value, we'd better bring the line back up.
|
||||
*/
|
||||
if (LcpInfo.fsm.state <= ST_CLOSED) {
|
||||
if (!dialing && LcpInfo.fsm.state <= ST_CLOSED) {
|
||||
if (!dial_up && reconnectState == RECON_TRUE) {
|
||||
if (++reconnectCount <= VarReconnectTries) {
|
||||
LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n",
|
||||
@ -626,7 +628,7 @@ DoLoop(struct bundle *bundle)
|
||||
/*
|
||||
* If Ip packet for output is enqueued and require dial up, Just do it!
|
||||
*/
|
||||
if (dial_up && RedialTimer.state != TIMER_RUNNING) {
|
||||
if (dial_up && !dialing && RedialTimer.state != TIMER_RUNNING) {
|
||||
LogPrintf(LogDEBUG, "going to dial: modem = %d\n",
|
||||
Physical_GetFD(bundle->physical));
|
||||
if (modem_Open(bundle->physical, bundle) < 0) {
|
||||
@ -654,33 +656,9 @@ DoLoop(struct bundle *bundle)
|
||||
else
|
||||
LogPrintf(LogCHAT, "Dial attempt %u\n", tries);
|
||||
|
||||
if ((res = modem_Dial(bundle->physical, bundle)) == EX_DONE) {
|
||||
PacketMode(bundle, VarOpenMode);
|
||||
dial_up = 0;
|
||||
reconnectState = RECON_UNKNOWN;
|
||||
tries = 0;
|
||||
} else {
|
||||
if (mode & MODE_BACKGROUND) {
|
||||
if (VarNextPhone == NULL || res == EX_SIG)
|
||||
Cleanup(EX_DIAL); /* Tried all numbers - no luck */
|
||||
else
|
||||
/* Try all numbers in background mode */
|
||||
StartRedialTimer(VarRedialNextTimeout);
|
||||
} else if (!(mode & MODE_DDIAL) &&
|
||||
((VarDialTries && tries >= VarDialTries) ||
|
||||
res == EX_SIG)) {
|
||||
/* I give up ! Can't get through :( */
|
||||
StartRedialTimer(VarRedialTimeout);
|
||||
dial_up = 0;
|
||||
reconnectState = RECON_UNKNOWN;
|
||||
reconnectCount = 0;
|
||||
tries = 0;
|
||||
} else if (VarNextPhone == NULL)
|
||||
/* Dial failed. Keep quite during redial wait period. */
|
||||
StartRedialTimer(VarRedialTimeout);
|
||||
else
|
||||
StartRedialTimer(VarRedialNextTimeout);
|
||||
}
|
||||
chat_Init(&chat, bundle->physical, VarDialScript, 1);
|
||||
dialing = 1;
|
||||
dial_up = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -692,7 +670,54 @@ DoLoop(struct bundle *bundle)
|
||||
|
||||
handle_signals();
|
||||
|
||||
descriptor_UpdateSet(&bundle->physical->desc, &rfds, &wfds, &efds, &nfds);
|
||||
if (dialing) {
|
||||
descriptor_UpdateSet(&chat.desc, &rfds, &wfds, &efds, &nfds);
|
||||
if (dialing == -1) {
|
||||
if (chat.state == CHAT_DONE || chat.state == CHAT_FAILED) {
|
||||
dialing = 0;
|
||||
modem_Close(bundle->physical);
|
||||
if (mode & MODE_BACKGROUND) {
|
||||
if (VarNextPhone == NULL || res == EX_SIG)
|
||||
Cleanup(EX_DIAL); /* Tried all numbers - no luck */
|
||||
else
|
||||
/* Try all numbers in background mode */
|
||||
StartRedialTimer(VarRedialNextTimeout);
|
||||
} else if (!(mode & MODE_DDIAL) &&
|
||||
((VarDialTries && tries >= VarDialTries) ||
|
||||
res == EX_SIG)) {
|
||||
/* I give up ! Can't get through :( */
|
||||
StartRedialTimer(VarRedialTimeout);
|
||||
dial_up = 0;
|
||||
reconnectState = RECON_UNKNOWN;
|
||||
reconnectCount = 0;
|
||||
tries = 0;
|
||||
} else if (VarNextPhone == NULL)
|
||||
/* Dial failed. Keep quite during redial wait period. */
|
||||
StartRedialTimer(VarRedialTimeout);
|
||||
else
|
||||
StartRedialTimer(VarRedialNextTimeout);
|
||||
continue;
|
||||
}
|
||||
} else if (chat.state == CHAT_DONE) {
|
||||
if (dialing == 1) {
|
||||
chat_Init(&chat, bundle->physical, VarLoginScript, 0);
|
||||
dialing++;
|
||||
continue;
|
||||
} else {
|
||||
PacketMode(bundle, VarOpenMode);
|
||||
reconnectState = RECON_UNKNOWN;
|
||||
tries = 0;
|
||||
dialing = 0;
|
||||
}
|
||||
} else if (chat.state == CHAT_FAILED) {
|
||||
chat_Init(&chat, bundle->physical, VarHangupScript, 0);
|
||||
dialing = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dialing)
|
||||
descriptor_UpdateSet(&bundle->physical->desc, &rfds, &wfds, &efds, &nfds);
|
||||
descriptor_UpdateSet(&server.desc, &rfds, &wfds, &efds, &nfds);
|
||||
|
||||
#ifndef SIGALRM
|
||||
@ -761,15 +786,22 @@ DoLoop(struct bundle *bundle)
|
||||
if (descriptor_IsSet(&prompt.desc, &rfds))
|
||||
descriptor_Read(&prompt.desc, bundle, &rfds);
|
||||
|
||||
if (descriptor_IsSet(&bundle->physical->desc, &wfds)) {
|
||||
/* ready to write into modem */
|
||||
descriptor_Write(&bundle->physical->desc, &wfds);
|
||||
if (!link_IsActive(physical2link(bundle->physical)))
|
||||
dial_up = 1;
|
||||
}
|
||||
if (dialing) {
|
||||
if (descriptor_IsSet(&chat.desc, &wfds))
|
||||
descriptor_Write(&chat.desc, &wfds);
|
||||
if (descriptor_IsSet(&chat.desc, &rfds))
|
||||
descriptor_Read(&chat.desc, bundle, &rfds);
|
||||
} else {
|
||||
if (descriptor_IsSet(&bundle->physical->desc, &wfds)) {
|
||||
/* ready to write into modem */
|
||||
descriptor_Write(&bundle->physical->desc, &wfds);
|
||||
if (!link_IsActive(physical2link(bundle->physical)))
|
||||
dial_up = 1;
|
||||
}
|
||||
|
||||
if (descriptor_IsSet(&bundle->physical->desc, &rfds))
|
||||
descriptor_Read(&bundle->physical->desc, bundle, &rfds);
|
||||
if (descriptor_IsSet(&bundle->physical->desc, &rfds))
|
||||
descriptor_Read(&bundle->physical->desc, bundle, &rfds);
|
||||
}
|
||||
|
||||
if (bundle->tun_fd >= 0 && FD_ISSET(bundle->tun_fd, &rfds)) {
|
||||
/* something to read from tun */
|
||||
|
@ -17,11 +17,13 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: main.h,v 1.9.2.3 1998/02/08 11:07:32 brian Exp $
|
||||
* $Id: main.h,v 1.9.2.4 1998/02/10 03:23:31 brian Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
extern int CleaningUp;
|
||||
extern int dial_up;
|
||||
extern int dialing;
|
||||
|
||||
extern void Cleanup(int);
|
||||
extern void AbortProgram(int);
|
||||
|
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: modem.c,v 1.77.2.13 1998/02/10 03:22:00 brian Exp $
|
||||
* $Id: modem.c,v 1.77.2.14 1998/02/10 03:23:35 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
*/
|
||||
@ -57,7 +57,6 @@
|
||||
#include "loadalias.h"
|
||||
#include "vars.h"
|
||||
#include "main.h"
|
||||
#include "chat.h"
|
||||
#include "throughput.h"
|
||||
#include "async.h"
|
||||
#include "bundle.h"
|
||||
@ -65,6 +64,7 @@
|
||||
#include "descriptor.h"
|
||||
#include "physical.h"
|
||||
#include "prompt.h"
|
||||
#include "chat.h"
|
||||
|
||||
|
||||
#ifndef O_NONBLOCK
|
||||
@ -79,6 +79,8 @@ static void modem_Hangup(struct link *, int);
|
||||
static void modem_Destroy(struct link *);
|
||||
static void modem_DescriptorRead(struct descriptor *, struct bundle *,
|
||||
const fd_set *);
|
||||
static int modem_UpdateSet(struct descriptor *, fd_set *, fd_set *, fd_set *,
|
||||
int *);
|
||||
|
||||
struct physical *
|
||||
modem_Create(const char *name)
|
||||
@ -101,7 +103,7 @@ modem_Create(const char *name)
|
||||
p->speed = MODEM_SPEED;
|
||||
p->parity = CS8;
|
||||
p->desc.type = PHYSICAL_DESCRIPTOR;
|
||||
p->desc.UpdateSet = Physical_UpdateSet;
|
||||
p->desc.UpdateSet = modem_UpdateSet;
|
||||
p->desc.IsSet = Physical_IsSet;
|
||||
p->desc.Read = modem_DescriptorRead;
|
||||
p->desc.Write = Physical_DescriptorWrite;
|
||||
@ -727,24 +729,38 @@ modem_PhysicalClose(struct physical *modem)
|
||||
throughput_log(&modem->link.throughput, LogPHASE, "Modem");
|
||||
}
|
||||
|
||||
static int force_hack;
|
||||
|
||||
static void
|
||||
modem_Hangup(struct link *l, int dedicated_force)
|
||||
{
|
||||
struct termios tio;
|
||||
struct physical *modem = (struct physical *)l;
|
||||
|
||||
force_hack = dedicated_force;
|
||||
if (modem->fd >= 0 && dialing != -1) {
|
||||
StopTimer(&modem->link.Timer);
|
||||
throughput_stop(&modem->link.throughput);
|
||||
|
||||
if (prompt_IsTermMode(&prompt))
|
||||
prompt_TtyCommandMode(&prompt);
|
||||
|
||||
dialing = -1;
|
||||
dial_up = 0;
|
||||
chat_Init(&chat, modem, VarHangupScript, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
modem_Close(struct physical *modem)
|
||||
{
|
||||
struct termios tio;
|
||||
|
||||
LogPrintf(LogDEBUG, "Hangup modem (%s)\n",
|
||||
modem->fd >= 0 ? "open" : "closed");
|
||||
|
||||
if (modem->fd < 0)
|
||||
return;
|
||||
|
||||
StopTimer(&modem->link.Timer);
|
||||
throughput_stop(&modem->link.throughput);
|
||||
|
||||
if (prompt_IsTermMode(&prompt))
|
||||
prompt_TtyCommandMode(&prompt);
|
||||
|
||||
if (!isatty(modem->fd)) {
|
||||
modem->mbits &= ~TIOCM_DTR;
|
||||
modem_PhysicalClose(modem);
|
||||
@ -762,19 +778,13 @@ modem_Hangup(struct link *l, int dedicated_force)
|
||||
}
|
||||
|
||||
if (modem->fd >= 0) {
|
||||
char ScriptBuffer[SCRIPT_LEN];
|
||||
|
||||
strncpy(ScriptBuffer, VarHangupScript, sizeof ScriptBuffer - 1);
|
||||
ScriptBuffer[sizeof ScriptBuffer - 1] = '\0';
|
||||
LogPrintf(LogDEBUG, "modem_Hangup: Script: %s\n", ScriptBuffer);
|
||||
if (dedicated_force ||
|
||||
if (force_hack ||
|
||||
#ifdef notyet
|
||||
!modem->is_dedicated
|
||||
#else
|
||||
!(mode & MODE_DEDICATED)
|
||||
#endif
|
||||
) {
|
||||
DoChat(modem, ScriptBuffer);
|
||||
tcflush(modem->fd, TCIOFLUSH);
|
||||
modem_Unraw(modem);
|
||||
modem_LogicalClose(modem);
|
||||
@ -785,7 +795,6 @@ modem_Hangup(struct link *l, int dedicated_force)
|
||||
*/
|
||||
modem->mbits |= TIOCM_DTR;
|
||||
ioctl(modem->fd, TIOCMSET, &modem->mbits);
|
||||
DoChat(modem, ScriptBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -867,43 +876,6 @@ modem_IsActive(struct link *l)
|
||||
return ((struct physical *)l)->fd >= 0;
|
||||
}
|
||||
|
||||
int
|
||||
modem_Dial(struct physical *modem, struct bundle *bundle)
|
||||
{
|
||||
char ScriptBuffer[SCRIPT_LEN];
|
||||
int excode;
|
||||
|
||||
strncpy(ScriptBuffer, VarDialScript, sizeof ScriptBuffer - 1);
|
||||
ScriptBuffer[sizeof ScriptBuffer - 1] = '\0';
|
||||
if ((excode = DoChat(modem, ScriptBuffer)) > 0) {
|
||||
prompt_Printf(&prompt, "dial OK!\n");
|
||||
strncpy(ScriptBuffer, VarLoginScript, sizeof ScriptBuffer - 1);
|
||||
if ((excode = DoChat(modem, ScriptBuffer)) > 0) {
|
||||
struct timeoutArg to;
|
||||
|
||||
VarAltPhone = NULL;
|
||||
prompt_Printf(&prompt, "login OK!\n");
|
||||
to.modem = modem;
|
||||
to.bundle = bundle;
|
||||
modem_Timeout(&to);
|
||||
return EX_DONE;
|
||||
} else if (excode == -1)
|
||||
excode = EX_SIG;
|
||||
else {
|
||||
LogPrintf(LogWARN, "modem_Dial: login failed.\n");
|
||||
excode = EX_NOLOGIN;
|
||||
}
|
||||
modem_Timeout(modem); /* Dummy call to check modem status */
|
||||
} else if (excode == -1)
|
||||
excode = EX_SIG;
|
||||
else {
|
||||
LogPrintf(LogWARN, "modem_Dial: dial failed.\n");
|
||||
excode = EX_NODIAL;
|
||||
}
|
||||
modem_Hangup(&modem->link, 0);
|
||||
return (excode);
|
||||
}
|
||||
|
||||
int
|
||||
modem_ShowStatus(struct cmdargs const *arg)
|
||||
{
|
||||
@ -1015,3 +987,9 @@ modem_DescriptorRead(struct descriptor *d, struct bundle *bundle,
|
||||
} else if (n > 0)
|
||||
async_Input(bundle, rbuff, n, p);
|
||||
}
|
||||
|
||||
static int
|
||||
modem_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
|
||||
{
|
||||
return Physical_UpdateSet(d, r, w, e, n, 0);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: modem.h,v 1.16.2.4 1998/02/06 02:22:23 brian Exp $
|
||||
* $Id: modem.h,v 1.16.2.5 1998/02/06 02:22:51 brian Exp $
|
||||
*
|
||||
* TODO:
|
||||
*/
|
||||
@ -26,7 +26,7 @@ extern int modem_Raw(struct physical *);
|
||||
extern struct physical *modem_Create(const char *);
|
||||
extern int modem_Open(struct physical *, struct bundle *);
|
||||
extern int modem_Speed(struct physical *);
|
||||
extern int modem_Dial(struct physical *, struct bundle *);
|
||||
extern speed_t IntToSpeed(int);
|
||||
extern int modem_SetParity(struct physical *, const char *);
|
||||
extern int modem_ShowStatus(struct cmdargs const *);
|
||||
extern void modem_Close(struct physical *);
|
||||
|
@ -16,7 +16,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: physical.c,v 1.1.2.6 1998/02/09 19:21:07 brian Exp $
|
||||
* $Id: physical.c,v 1.1.2.7 1998/02/10 03:22:02 brian Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -179,25 +179,30 @@ Physical_ReportProtocolStatus(struct cmdargs const *arg)
|
||||
|
||||
int
|
||||
Physical_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e,
|
||||
int *n)
|
||||
int *n, int force)
|
||||
{
|
||||
struct physical *p = descriptor2physical(d);
|
||||
int sets;
|
||||
|
||||
LogPrintf(LogDEBUG, "descriptor2physical; %p -> %p\n", d, p);
|
||||
|
||||
sets = 0;
|
||||
if (p->fd >= 0) {
|
||||
if (*n < p->fd + 1)
|
||||
*n = p->fd + 1;
|
||||
FD_SET(p->fd, r);
|
||||
FD_SET(p->fd, e);
|
||||
if (link_QueueLen(&p->link)) {
|
||||
if (r) {
|
||||
FD_SET(p->fd, r);
|
||||
sets++;
|
||||
}
|
||||
if (e) {
|
||||
FD_SET(p->fd, e);
|
||||
sets++;
|
||||
}
|
||||
if (w && (force || link_QueueLen(&p->link))) {
|
||||
FD_SET(p->fd, w);
|
||||
sets = 3;
|
||||
} else
|
||||
sets = 2;
|
||||
} else
|
||||
sets = 0;
|
||||
sets++;
|
||||
}
|
||||
if (sets && *n < p->fd + 1)
|
||||
*n = p->fd + 1;
|
||||
}
|
||||
|
||||
return sets;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: physical.h,v 1.1.2.6 1998/02/09 19:21:08 brian Exp $
|
||||
* $Id: physical.h,v 1.1.2.7 1998/02/10 03:22:03 brian Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -86,6 +86,7 @@ void Physical_DupAndClose(struct physical *);
|
||||
ssize_t Physical_Read(struct physical *phys, void *buf, size_t nbytes);
|
||||
ssize_t Physical_Write(struct physical *phys, const void *buf, size_t nbytes);
|
||||
int Physical_ReportProtocolStatus(struct cmdargs const *);
|
||||
int Physical_UpdateSet(struct descriptor *, fd_set *, fd_set *, fd_set *, int *);
|
||||
int Physical_UpdateSet(struct descriptor *, fd_set *, fd_set *, fd_set *,
|
||||
int *, int);
|
||||
int Physical_IsSet(struct descriptor *, fd_set *);
|
||||
void Physical_DescriptorWrite(struct descriptor *, const fd_set *);
|
||||
|
@ -23,7 +23,7 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id$
|
||||
* $Id: prompt.c,v 1.1.2.1 1998/02/10 03:23:38 brian Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -59,18 +59,25 @@ static int
|
||||
prompt_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
|
||||
{
|
||||
struct prompt *p = descriptor2prompt(d);
|
||||
int sets;
|
||||
|
||||
LogPrintf(LogDEBUG, "descriptor2prompt; %p -> %p\n", d, p);
|
||||
|
||||
sets = 0;
|
||||
if (p->fd_in >= 0) {
|
||||
if (*n < p->fd_in + 1)
|
||||
if (r) {
|
||||
FD_SET(p->fd_in, r);
|
||||
sets++;
|
||||
}
|
||||
if (e) {
|
||||
FD_SET(p->fd_in, e);
|
||||
sets++;
|
||||
}
|
||||
if (sets && *n < p->fd_in + 1)
|
||||
*n = p->fd_in + 1;
|
||||
FD_SET(p->fd_in, r);
|
||||
FD_SET(p->fd_in, e);
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return sets;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -23,7 +23,7 @@
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $Id: server.c,v 1.16.2.3 1998/02/10 03:22:05 brian Exp $
|
||||
* $Id: server.c,v 1.16.2.4 1998/02/10 03:23:41 brian Exp $
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -57,7 +57,7 @@ server_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
|
||||
struct server *s = descriptor2server(d);
|
||||
|
||||
LogPrintf(LogDEBUG, "descriptor2server; %p -> %p\n", d, s);
|
||||
if (s->fd >= 0) {
|
||||
if (r && s->fd >= 0) {
|
||||
if (*n < s->fd + 1)
|
||||
*n = s->fd + 1;
|
||||
FD_SET(s->fd, r);
|
||||
|
@ -17,7 +17,7 @@
|
||||
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* $Id: vars.c,v 1.45.2.5 1998/02/09 19:21:11 brian Exp $
|
||||
* $Id: vars.c,v 1.45.2.6 1998/02/10 03:23:49 brian Exp $
|
||||
*
|
||||
*/
|
||||
#include <sys/param.h>
|
||||
@ -45,8 +45,8 @@
|
||||
#include "physical.h"
|
||||
#include "prompt.h"
|
||||
|
||||
char VarVersion[] = "PPP Version 1.90";
|
||||
char VarLocalVersion[] = "$Date: 1998/02/09 19:21:11 $";
|
||||
char VarVersion[] = "PPP Version 2.0-beta";
|
||||
char VarLocalVersion[] = "$Date: 1998/02/10 03:23:49 $";
|
||||
int Utmp = 0;
|
||||
int ipKeepAlive = 0;
|
||||
int reconnectState = RECON_UNKNOWN;
|
||||
|
Loading…
Reference in New Issue
Block a user