Filewatcher File Search
FTP Search
  
Directory 
  
Content Search 
   
pkg://Eznet-1.11.1-1.src.rpm:28948/Eznet-1.11.1.tar.gz  info  downloads

Eznet-1.11.1/ 40755      0      0           0  6706444444  10656 5ustar  rootrootEznet-1.11.1/eznet.c100644      0      0      171473  6706436236  12321 0ustar  rootroot/*
** Copyright (c) 1998 D. Richard Hipp
**
** 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., 59 Temple Place - Suite 330,
** Boston, MA  02111-1307, USA.
**
** Author contact information:
**   drh@acm.org
**   http://www.hwaci.com/drh/
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

/*
** Names of files and executables and other hard-coded
** strings.
*/
#define DB_DIR     "/etc/ppp/eznet"
#define DB_FILE    DB_DIR "/eznet.conf"
#define PPP_DIR    "/etc/ppp"
#define PPP_EXE    "/usr/sbin/pppd"
#define SELF       "/usr/bin/eznet"
#define DIALD_EXE  "/usr/sbin/diald"
#define SESSIONLOG DB_DIR "/session.log"

/*
** The following define determines what version of PPP
** this program expects by default.  You can override this
** value using the pppversion= parameter.
*/
#define DFLT_PPP_VERSION "2.3"

/*
** Each entry in the database is represented in memory by an instance
** of the following structure.
*/
typedef struct ConfEntry ConfEntry;
struct ConfEntry {
  char *zLabel;       /* A label for this entry */
  ConfEntry *next;    /* Next entry in the same record */
  char *zValue;       /* Value for this entry */
};

/*
** These variables record the whole database.  There are nRecord
** records, and each record consists of a linked list of ConfEntry
** structures where aRecord[N] points to the head of the list for
** record number N.
*/
static int nRecord = 0;           /* Number of records */
static ConfEntry **aRecord = 0;   /* List of ConfEntries for each record */

/*
** A transcript of the CHAT session is written to this file, if
** it is open.
*/
static FILE *transcript = 0;

/*
** If this is a set-uid or set-gid process, then release all
** privileges.
*/
static void Unprivileged(void){
  int uid = getuid();
  int gid = getgid();
  setregid(gid,gid);
  setreuid(uid,uid);
}

/*
** Read the entire contents of a file into memory.  Space to
** hold the file is obtained from malloc.  NULL is returned if
** we can't read the file for any reason.
*/
static char *ReadFile(const char *zFilename){
  int fd;
  FILE *in;
  int toread;
  int read;
  int n;
  char *zBuf;
  struct stat sbuf;
 
  in = fopen(zFilename,"r");
  if( in==0 ) return 0;
  fd = fileno(in);
  if( fstat(fd, &sbuf)!=0 ){
    fclose(in);
    return 0;
  }
  toread = sbuf.st_size;
  read = 0;
  zBuf = malloc( toread+1 );
  if( zBuf==0 ){
    fclose(in);
    return 0;
  }
  while( toread && (n=fread(&zBuf[read],1,toread,in))>0 ){
    toread -= n;
    read += n;
  }
  zBuf[read] = 0;
  fclose(in);
  return zBuf;
}

/* Change the status for a record.  The status is recorded in
** a file named
**
**       /var/eznet/status.NNN
**
** where NNN is the record number.
*/
static void WriteStatus(int iRec, char *zStatus, ...){
  FILE *out;
  va_list ap;
  char zFilename[sizeof(DB_DIR)+20];

  sprintf(zFilename,"%s/status.%d",DB_DIR,iRec);
  out = fopen(zFilename,"w");
  if( out ){
    time_t now;
    struct tm *p;
    char zBuf[200];
    fchown(fileno(out),0,0);
    fchmod(fileno(out),0644);
    va_start(ap, zStatus);
    vfprintf(out,zStatus,ap);
    va_end(ap);
    time(&now);
    p = localtime(&now);
    strftime(zBuf,sizeof(zBuf)-1," %I:%M%p %a %b %d, %Y\n", p);
    fprintf(out,zBuf);
    fclose(out);
  }
}

/* Write a status report from Chat.  This is written to a file
** named
**
**     /var/eznet/chatstat.NNN
**
** where NNN is the record number.
*/
static void WriteChatStat(int iRec, char *zStatus, ...){
  FILE *out;
  va_list ap;
  char zFilename[sizeof(DB_DIR)+20];

  sprintf(zFilename,"%s/chatstat.%d",DB_DIR,iRec);
  out = fopen(zFilename,"w");
  if( out ){
    va_start(ap, zStatus);
    vfprintf(out,zStatus,ap);
    va_end(ap);
    fclose(out);
  }
}

/*
** Write text into a file.  zFile is the name of the file into
** which the text is written.  zFormat is a printf-like format
** string that defines the text.  The owner of the file is converted
** to root and the permissions are set to "mode".
*/
static void WriteToFile(char *zFile, int mode, char *zFormat, ...){
  FILE *out;
  va_list ap;

  out = fopen(zFile,"w");
  if( out==0 ) return;
  fchmod(fileno(out),mode);
  fchown(fileno(out),0,0);
  va_start(ap,zFormat);
  vfprintf(out,zFormat,ap);
  va_end(ap);
  fclose(out);
}

/* Make sure /etc/ppp/ip-up and /etc/ppp/ip-down invoke eznet
** with either the "ipup" or "ipdown" argument (as approprite.)
** This is needed so that we can adjust the status when the link
** goes up or down.
*/
static void SetIpUpDown(char *zUpDown){
  char *zText;
  int i;
  int lineLen = 0;
  int patLen;
  char zPat[100];
  char zFile[sizeof(PPP_DIR)+20];

  sprintf(zFile,"%s/ip-%s",PPP_DIR,zUpDown);
  zText = ReadFile(zFile);
  if( zText==0 ){
    WriteToFile(zFile, 0744, 
      "#!/bin/sh\n" SELF " ip%s $* &\n", zUpDown);
    return;
  }
  for(i=0; zText[i] && zText[i]!='\n'; i++){}
  if( zText[i]=='\n' ){
    lineLen = i;
  }
  sprintf(zPat,SELF " ip%s $* &",zUpDown);
  patLen = strlen(zPat);
  while( zText[i] && (zText[i]!='/' || strncmp(&zText[i], zPat, patLen)) ){
    i++;
  }
  if( zText[i]==0 ){
    WriteToFile(zFile, 0744, "#!/bin/sh\n%s\n%s",
      zPat, &zText[lineLen]);
  }
}

/*
** Parse a single line of the pap-secrets or chap-secrets file whose
** text begins as *pzText.  The elements of the line are place in
** azArgs[0] through azArgs[3].
**
** After parsing, *pzText is left pointing to the first character
** after then end of the parsed text.  At end of file, *pzText points
** to 0.
**
** All of azArgs[0] through azArgs[3] are always filled in.  Empty
** strings are inserted if necessary.  A comment line is placed in
** azArgs[0] and the others are empty strings.
**
** Quotes and escapes are removed from azArgs[0] through azArgs[2],
** except for comment lines which are passed through unaltered.
*/
static void GetSecret(char **pzText, char **azArgs){
  int i;
  char *z = *pzText;
  while( isspace(*z) ) z++;
  azArgs[0] = azArgs[1] = azArgs[2] = azArgs[3] = "";
  if( *z=='#' ){
     azArgs[0] = z;
     while( *z && *z!='\n' ) z++;
     if( *z=='\n' ){
       *z = 0;
       z++;
     }
     *pzText = z;
     return;
  }
  for(i=0; i<=2; i++){
    int hasEscape = 0;
    azArgs[i] = z;
    if( *z=='"' ){
      azArgs[i]++;
      z++;
      while( *z && *z!='"' ){ 
        if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
        z++;
      }
      if( *z=='"' ){
        *z = 0;
        z++;
      }
    }else{
      while( *z && !isspace(*z) ){
        if( *z=='\\' && z[1] ){ z++; hasEscape = 1; }
        z++;
      }
      if( *z ){
        *z = 0;
        z++;
      }
    }
    while( isspace(*z) && *z!='\n' ){ z++; }
    if( hasEscape ){
      int j = 0, k = 0;
      char *zStr = azArgs[i];
      while( zStr[j] ){
        if( zStr[j]=='\\' && zStr[j+1] ){ j++; }
        zStr[k++] = zStr[j++];
      }
    }
  }
  azArgs[i] = z;
  while( *z && *z!='\n' ){
    if( *z=='\\' && z[1] ){ z++; }
    z++;
  }
  if( *z=='\n' ){
    *z = 0;
    z++;
  }
  *pzText = z;
}

/*
** The elements azArgs[0] through azArgs[3] contain the text of
** a secret.  This secret is written to *pzBuf.  *pzBuf is updated
** to point to the null terminator at the end of the written text.
**
** Quotes and escapes are inserted on azArgs[0] through azArgs[2]
** if needed.  Except if the first character of azArgs[0] is "#"
** then no quotes or escapes are inserted.
*/
static void AppendSecret(char **pzBuf, char **azArgs){
  char *z = *pzBuf;
  char *zSrc;
  int i;
  if( azArgs[0][0]=='#' ){
    zSrc = azArgs[0];
    while( *zSrc ){ *(z++) = *(zSrc++); }
    *(z++) = '\n';
    *z = 0;
    *pzBuf = z;
    return;
  }
  for(i=0; i<=2; i++){
    zSrc = azArgs[i];
    if( *zSrc==0 ) continue;
    while( *zSrc ){
      switch( *zSrc ){
        case ' ':
        case '\t':
        case '\n':
        case '"':
        case '#':
        case '\'':
        case '\\':
          *(z++) = '\\';
          /* Fall thru */
        default:
          *(z++) = *(zSrc++);
          break;
      }
    }
    *(z++) = ' ';
  }
  while( *zSrc ){ *(z++) = *(zSrc++); }
  *(z++) = '\n';
  *z = 0;
  *pzBuf = z;
}

/* Make sure the correct password for the service and user is found in the
** secrets file.  Add it if is not.  Preserve any commands or
** unrelated secrets that are previously found in the file.
**
** Needless to say, we have to have root privileges to run
** this function successfully.  The secrets files contain information
** which should not be publicly viewable.
*/
static void SetSecrets(
  char *zBase,         /* Either "pap-secrets" or "chap-secrets" */
  char *zService,      /* Name of the service provider we are calling */
  char *zUser,         /* Our login name */
  char *zPassword      /* Our password */
){
  char *zOld, *zOldOrig;
  char *z;
  char *zNew;
  int extra;
  int seenServiceUser = 0;
  int seenUserService = 0;
  char *azArgs[4];
  char zFile[sizeof(PPP_DIR)+20];

  if( zPassword==0 || zUser==0 || zService==0 || zFile==0 ) return;
  sprintf(zFile,"%s/%.15s",PPP_DIR,zBase);
  zOldOrig = zOld = ReadFile(zFile);
  extra = (strlen(zService) + strlen(zPassword) + strlen(zUser) + 10)*4;
  if( zOld==0 ){
    WriteToFile(zFile, 0600, "#\n%s %s %s\n%s %s %s\n",
       zService, zUser, zPassword,
       zUser, zService, zPassword
    );
    return;
  }
  z = zNew = malloc( strlen(zOld) + extra + 100 );
  if( z==0 ) return;
  while( zOld && *zOld ){
    GetSecret(&zOld, azArgs);
    if( azArgs[0][0]!='#' ){
      if( strcmp(azArgs[0],zService)==0 && strcmp(azArgs[1],zUser)==0 ){
        seenServiceUser = 1;
        azArgs[2] = zPassword;
        azArgs[3] = "";
      }else if( strcmp(azArgs[0],zUser)==0 && strcmp(azArgs[1],zService)==0 ){
        seenUserService = 1;
        azArgs[2] = zPassword;
        azArgs[3] = "";
      }
    }
    AppendSecret(&z, azArgs);
  }
  if( !seenServiceUser ){
    azArgs[0] = zService;
    azArgs[1] = zUser;
    azArgs[2] = zPassword;
    azArgs[3] = "";
    AppendSecret(&z, azArgs);
  }
  if( !seenUserService ){
    azArgs[0] = zUser;
    azArgs[1] = zService;
    azArgs[2] = zPassword;
    azArgs[3] = "";
    AppendSecret(&z, azArgs);
  }
  WriteToFile(zFile, 0600, "%s", zNew);
  free(zOldOrig);
  free(zNew);
}

/* zFile[*pI] is the first character in a line of text.  Return
** a pointer to the first non-whitespace character in this line.
** Also convert the \n into a \0 and make *pI be the index of
** the first character on the next line.
**
** If zFile[*pI] is 0, then we've reached the end of input.
** Return NULL.
*/
static char *GetLine(char *zFile, int *pI){
  int i = *pI;
  int iStart;
  if( zFile[i]==0 ) return 0;
  while( isspace(zFile[i]) ){ i++; }
  iStart = i;
  while( zFile[i] && zFile[i]!='\n' ){ i++; }
  if( zFile[i] ){
    zFile[i] = 0;
    *pI = i+1;
  }else{
    *pI = i;
  }
  return &zFile[iStart];
}

/* This array maps hexadecimal digit values into their numeric
** value.
*/
static int hexval[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,
  0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

/* Return a pointer to the first space-separated token past
** the character zLine[*pI].  Return NULL if there are no more
** tokens.  *pI is left pointing to the first character after
** the token.
**
** The HTTP-style encoding is removed from the token and the
** token is null-terminated.
*/
static char *GetToken(char *zLine, int *pI){
  int i = *pI;
  int iStart;
  int j;
  while( isspace(zLine[i]) ){ i++; }
  if( zLine[i]==0 ) return 0;
  iStart = i;
  while( zLine[i] && !isspace(zLine[i]) ){ i++; }
  if( zLine[i] ){
    *pI = i + 1;
    zLine[i] = 0;
  }else{
    *pI = i;
  }
  for(i=j=iStart; zLine[i]; i++){
    switch( zLine[i] ){
      case '+':
       zLine[j++] = ' ';
       break;
      case '%':
       if( zLine[i+1] && zLine[i+2] ){
         zLine[j++] = hexval[0x7f & zLine[i+1]]*16 + hexval[0x7f & zLine[i+2]];
         i += 2;
       }else{
         zLine[j++] = '%';
       }
       break;
      default:
       zLine[j++] = zLine[i];
       break;
    }
  }
  zLine[j] = 0;
  return &zLine[iStart];
}

/*
** This function inserts a new entry into the database (or replaces
** the value of an existing entry).  iRec is the record number.
** New records are created as necessary.  zField is the entry label,
** and zValue is its value.  Both are plain text.
**
** If zValue==0 or *zValue==0, then the entry is deleted.
**
** Memory to hold the new entry is obtained from malloc.
*/
static void InsertEntry(int iRec, char *zLabel, char *zValue){
  ConfEntry *p;
  if( zValue==0 || *zValue==0 ){
    if( iRec>=0 && iRec<nRecord ){
      ConfEntry **pp = &aRecord[iRec];
      while( (p=*pp)!=0 ){
        if( strcmp(p->zLabel,zLabel)==0 ) break;
        pp = &p->next;
      }
      if( p ){
        *pp = p->next;
        free( p->zValue );
        free( p );
      }
    }
    return;
  }
  if( iRec>=nRecord ){
    if( nRecord==0 ){
      aRecord = malloc( sizeof(ConfEntry*)*(iRec+1) );
    }else{
      aRecord = realloc( aRecord, sizeof(ConfEntry*)*(iRec+1) );
    }
    if( aRecord==0 ){ nRecord = 0; return; }
    while( nRecord<=iRec ){
      aRecord[nRecord++] = 0;
    }
  }
  for(p=aRecord[iRec]; p; p=p->next){
    if( strcmp(p->zLabel,zLabel)==0 ) break;
  }
  if( p==0 ){
    p = malloc( sizeof(ConfEntry) + strlen(zLabel) + 1 );
    if( p==0 ) return;
    p->zLabel = (char*)&p[1];
    strcpy(p->zLabel, zLabel);
    p->next = aRecord[iRec];
    p->zValue = 0;
    aRecord[iRec] = p;
  }
  if( p ){
    if( p->zValue ) free(p->zValue);
    p->zValue = malloc( strlen(zValue) + 1 );
    if( p->zValue ) strcpy(p->zValue, zValue);
  }
}

/*
** The set of characters that need to be excaped are coded as "1".
** Safe characters are 0.
*/
static unsigned char NeedEsc[] = {
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};

/* Do an HTTP-style encoding of zToken and write it on the
** output file given.
*/
static void WriteToken(FILE *out, char *zToken){
  int i = 0;
  while( zToken[i] ){
    if( !NeedEsc[(unsigned)zToken[i]&0xff] ){
      i++;
    }else{
      if( i>0 ){
        fwrite(zToken,1,i,out);
      }
      if( zToken[i]==' ' ){
        fwrite("+",1,1,out);
      }else{
        char zBuf[4];
        zBuf[0] = '%';
        zBuf[1] = "0123456789ABCDEF"[(zToken[i]>>4)&0xf];
        zBuf[2] = "0123456789ABCDEF"[zToken[i]&0xf];
        zBuf[3] = 0;
        fwrite(zBuf,1,3,out);
      }
      zToken += i + 1;
      i = 0;
    }
  }
  if( i>0 ){
    fwrite(zToken,1,i,out);
  }
}

/*
** The set of characters that need to be excaped like "\012" are
** are coded as 1.  Safe characters are 0.  Characters that need
** a backslash in front are coded with 2.  Another code generates
** a symbolic escape by putting a backslash in front of the value.
*/
static unsigned char NeedTclEsc[] = {
  1,  1,  1,  1,  1,  1,  1, 'a',  'b','t','n', 1, 'f','r', 1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  2,  0,  2,  1,  2,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  2,  2,  2,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  1,  0,  1,  0,  1,    
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
};

/* Write a string as a single TCL token.
*/
static void WriteTcl(FILE *out, char *z){
  register c;
  while( (c=*(z++))!=0 ){
    if( NeedTclEsc[c] ){
      int e = NeedTclEsc[c];
      if( e==1 ){
        fprintf(out,"\\%03o",0xff&c);
      }else if( e==2 ){
        fprintf(out,"\\%c",c);
      }else{
        fprintf(out,"\\%c",e);
      }
    }else{
      putc(c,out);
    }
  }
}

/*
** The set of characters that need to be excaped like "\012" are
** are coded as 1.  Safe characters are 0.  Characters that need
** a backslash in front are coded with 2.  Another code generates
** a symbolic escape by putting a backslash in front of the value.
*/
static unsigned char NeedHumanEsc[] = {
  1,  1,  1,  1,  1,  1,  1, 'a',  'b','t','n', 1, 'f','r', 1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  0,  0,  2,  0,  2,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  2,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  0,    
  0,  0,  0,  0,  0,  0,  0,  0,    0,  0,  0,  0,  0,  0,  0,  1,    
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
  1,  1,  1,  1,  1,  1,  1,  1,    1,  1,  1,  1,  1,  1,  1,  1,
};

/* Write a string as a single TCL token.
*/
static void WriteHumanReadable(FILE *out, char *z){
  register c;
  while( (c=*(z++))!=0 ){
    if( NeedHumanEsc[c] ){
      int e = NeedHumanEsc[c];
      if( e==1 ){
        fprintf(out,"\\%03o",0xff&c);
      }else if( e==2 ){
        fprintf(out,"\\%c",c);
      }else{
        fprintf(out,"\\%c",e);
      }
    }else{
      putc(c,out);
    }
  }
}

/*
** Convert a non-negative number from its ASCII form into a binary
** form.  Return -1 if the input string is not a non-negative number.
*/
static int GetInt(char *z){
  int v = 0;
  while( *z ){
    if( *z<'0' || *z>'9' ) return -1;
    v = v*10 + *(z++) - '0';
  }
  return v;
}

/*
** Read the whole database into memory.
*/
static void ReadDb(void){
  char *zDb;
  int i,j;
  char *zLine;
  char *zToken;
  int iRec;
  char *zField;
  char *zValue;

  zDb = ReadFile(DB_FILE);
  if( zDb==0 ) return;
  i = 0;
  while( (zLine=GetLine(zDb,&i))!=0 ){
    if( *zLine=='#' ) continue;
    j = 0;
    zToken = GetToken(zLine,&j);
    if( zToken==0 ) continue;
    iRec = GetInt(zToken);
    if( iRec<0 ) continue;
    zField = GetToken(zLine,&j);
    zValue = GetToken(zLine,&j);
    if( zValue==0 ) continue;
    InsertEntry(iRec,zField,zValue);
  }
  free( zDb );
}

/* Given two lists of ConfEntry structures both of which are
** sorted and either or both of which can be empty, combine the
** two lists into a single sorted list.  Return a pointer to 
** the head of the new list.
**
** This is part of the merge-sort algorithm.
*/
static ConfEntry *merge(ConfEntry *left, ConfEntry *right){
  ConfEntry *head = 0, *tail = 0;
  while( left && right ){
    int c = strcmp(left->zLabel, right->zLabel);
    if( c<0 ){
      if( tail ){
        tail->next = left;
        tail = tail->next;
      }else{
        head = tail = left;
      }
      left = left->next;
      tail->next = 0;
    }else{
      if( tail ){
        tail->next = right;
        tail = tail->next;
      }else{
        head = tail = right;
      }
      right = right->next;
      tail->next = 0;
    }
  }
  if( left ){
    if( tail ){
      tail->next = left;
    }else{
      head = tail = left;
    }
  }else if( right ){
    if( tail ){
      tail->next = right;
    }else{
      head = tail = right;
    }
  }
  return head;
}

/* Sort a list of ConfEntry structures.  Return a pointer to
** the sorted list.
**
** The algorithm is merge-sort.  
*/
static ConfEntry *sort(ConfEntry *p){
  int i;
  ConfEntry *pNext;
  ConfEntry *a[32];

  for(i=0; i<sizeof(a)/sizeof(a[0]); i++){ a[i] = 0; }
  while( p ){
    pNext = p->next;
    p->next = 0;
    for(i=0; a[i] && i<sizeof(a)/sizeof(a[0])-1; i++){
      p = merge(a[i],p);
      a[i] = 0;
    }
    a[i] = p;
    p = pNext;
  }
  for(i=0; i<sizeof(a)/sizeof(a[0]); i++){
    p = merge(p,a[i]);
  }
  return p;
}

/*
** Write the entire database back to the disk.  Make sure the
** database file is owned by root and has 0600 permissions so
** that unprivileged users can't see the passwords.
*/
static void WriteDb(void){
  FILE *out;
  int i;
  ConfEntry *p;
  int fd;
  char zFilename[sizeof(DB_FILE)+100];

  sprintf(zFilename,"%s.%d",DB_FILE,getpid());
  out = fopen(zFilename,"w");
  if( out==0 ) return;
  fd = fileno(out);
  fchmod(fd,0600);
  fchown(fd,0,0);
  for(i=0; i<nRecord; i++){
    aRecord[i] = p = sort(aRecord[i]);
    for(p=aRecord[i]; p; p=p->next){
      fprintf(out,"%d %s ",i,p->zLabel);
      WriteToken(out,p->zValue);
      fprintf(out,"\n");
    }
  } 
  fclose(out);
  unlink(DB_FILE);
  link(zFilename,DB_FILE);
  unlink(zFilename);
}

/*
** Given a sequence of strings of the form
**
**              LABEL=VALUE
**
** parse the strings up into LABEL and VALUE and add entries
** for each string to the database as record iRec.
*/
static void AddEntries(int iRec, int argc, char **argv){
  int i, j;
  char *zLabel;
  char *zValue;
  for(i=0; i<argc; i++){
    zLabel = argv[i];
    for(j=1; zLabel[j] && zLabel[j]!='='; j++){}
    if( zLabel[j]!='=' ) continue;
    zLabel[j] = 0;
    zValue = &zLabel[j+1];
    InsertEntry(iRec, zLabel, zValue);
  }
}

/*
** Rearrange the order of records so that the Mth record
** becomes the Nth record.
*/
static void MoveRecord(int M, int N){
  int i;
  ConfEntry *pTemp;

  if( nRecord<0 ) return;
  if( M>=nRecord ) M = nRecord-1;
  if( M<0 ) M = 0;
  if( N>=nRecord ) N = nRecord-1;
  if( N<0 ) N = 0;
  if( M==N ) return;
  if( M<N ){
    pTemp = aRecord[M];
    for(i=M; i<N; i++){
      aRecord[i] = aRecord[i+1];
    }
    aRecord[N] = pTemp;
  }else{
    pTemp = aRecord[M];
    for(i=M; i>N; i--){
      aRecord[i] = aRecord[i-1];
    }
    aRecord[N] = pTemp;
  }
}

/*
** Return TRUE if the given string is an IP address composed of
** 4 numbers between 0 and 255 separated by dots.  If the string
** is an IP address, return the value in *pIp.
*/
static int IsIpAddr(char *z,  int *pIp){
  int a1, a2, a3, a4, rc;
  if( sscanf(z,"%d.%d.%d.%d",&a1,&a2,&a3,&a4)==4
    && a1>=0 && a1<=255
    && a2>=0 && a2<=255
    && a3>=0 && a3<=255
    && a4>=0 && a4<=255
  ){
    *pIp = (a1<<24) | (a2<<16) | (a3<<8) | a4;
    rc = 1;
  }else{
    rc = 0;
  }
  return rc;
}

/*
** Look up a specific field in a specific record and return its
** value.  Return the given default value if it is not found.
*/
static char *Lookup(int iRec, char *zLabel, char *zDflt){
  ConfEntry *p;
  if( iRec<0 || iRec>=nRecord ) return zDflt;
  for(p=aRecord[iRec]; p; p=p->next){
    if( strcmp(zLabel,p->zLabel)==0 ) return p->zValue;
  }
  return zDflt;
}

/*
** Given a command-line parameter that might be a record number,
** or an IP address, or a modem tty name, or the keyword "all",
** return a record number.  The special code FIND_ERROR is returned
** if we can't find a match.  FIND_ALL is returned if the keyword
** "all" appears or for a NULL string.
*/
#define FIND_ALL    -1
#define FIND_ERROR  -2
static int FindRecord(char *z){
  int i;
  int ip;

  if( z==0 ){
    if( nRecord==1 ) return 0;
    return FIND_ALL;
  }
  if( strcmp(z,"all")==0 ){
    return FIND_ALL;
  }
  i = GetInt(z);
  if( i>=0 ){
    if( i>=nRecord ) i = nRecord-1;
    if( i<0 ) i = 0;
    return i;
  }
  if( IsIpAddr(z,&ip) ){
    int t, mask, iRec;
    char *zVal;
    for(iRec=0; iRec<nRecord; iRec++){
      zVal = Lookup(iRec,"ip","0.0.0.0");
      if( !IsIpAddr(zVal,&t) ) continue;
      zVal = Lookup(iRec,"netmask","0.0.0.0");
      if( !IsIpAddr(zVal,&mask) ) continue;
      if( (t&mask)==(ip&mask) ) return iRec;
    }
  }
  if( z[0]=='/' ){
    int iRec;
    char *zVal;
    for(iRec=0; iRec<nRecord; iRec++){
      zVal = Lookup(iRec,"tty","/dev/modem");
      if( strcmp(zVal,z)==0 ) return iRec;
    }
  }
  for(i=0; i<nRecord; i++){
    char *zVal = Lookup(i,"service",0);
    if( zVal && strcmp(zVal,z)==0 ) return i;
  }
  return FIND_ERROR;
}

/*
** Write a timestamp on the transcript.
*/
static void WriteTimestamp(void){
  struct timeval stv;
  struct timezone stz;
  if( transcript==0 ) return;
  gettimeofday(&stv,&stz);
  fprintf(transcript,"%02ld:%02ld.%03ld", (long)(stv.tv_sec/60)%60,
    (long)stv.tv_sec % 60, (long)stv.tv_usec/1000);
}

/*
** Write to a file descriptor, and also to the transcript.
*/
static void ChatWrite(char *z, int shroud){
  int n, w;
  if( transcript ){
    if( shroud>0 ){
      int i = strlen(z);
      while( i>0 ){
        fprintf(transcript,"%.*s",i,"**********" "**********" "***********");
        i -= 30;
      }
    }else{
      WriteHumanReadable(transcript,z);
    }
  }
  n = strlen(z);
  while( n ){
    w = write(1, z, n);
    if( w>0 ){
      n -= w;
      z += w;
    }
  }
}

/*
** Send output to the modem. 
**
** Delay for "delay" microseconds before actually doing the write.
** This is for modems that get fouled up by input that follows to
** closely after their own output.
**
** The transmitted text is normally written to the transcript file.
** But if "shroud" is one, then "*" characters are subsitituted.
** This is used when writting the password, to keep it from view.
*/
static void ChatSend(int delay, int shroud, char *z, ...){
  va_list ap;
  if( z==0 ) return;
  if( delay>0 ){
    usleep(delay);
  }
  if( transcript ){
    WriteTimestamp();
    fprintf(transcript," Send:  \"");
  }
  va_start(ap, z);
  ChatWrite(z, shroud);
  while( (z = va_arg(ap,char*))!=0 ){
    ChatWrite(z, shroud);
    shroud--;
  }
  if( transcript ){
    fprintf(transcript,"\"\n");
    fflush(transcript);
  }
}

/*
** Read text from the input and put it into zBuf.  Reading continues
** until:
**
**   *  No characters are received after waiting for "timeout" 
**      seconds, or
**
**   *  Some characters have been received but no characters have
**      appeared for 500 milliseconds or more.
**
** Only characters since the last new-line are returned in zBuf.
** But all received characters are written to the transcript. 
**
** Characters in zBuf[] when this routine is called are not erased
** or overwritten.  Newly read characters are appended.  Unless
** a new-line or carriage routine is seen, when all characters are
** deleted.
**
** The function returns the number of characters that were received
** during the current call to this function.  This number will be
** zero if a timeout occurs.
*/
static int ChatRecv(char *zBuf, int nBuf, int timeout){
  int n;            /* Number of bytes returned from a single read() */
  int i;            /* Loop counter */
  int idle = 0;     /* Number of 1/10ths of second since last read() */
  int got;          /* How many characters previously read */
  int nChar = 0;    /* Total number of characters read */
  
  time_t start;
  time_t now;
  
  /* Remember when we began looking... */
  time(&start);

  /* Figure out how much text is already in zBuf[].  New
  ** text will be appended.
  */
  for(got=0; zBuf[got]; got++){}
  if( got>0 && (zBuf[got-1]=='\n' || zBuf[got-1]=='\r') ){ got = 0; }
  zBuf[got] = 0;

  /* Start reading input...
  */
  while( got<nBuf-1 && (nChar==0 || idle<5) ){
    time(&now);
    if( start+timeout < now ) break;
    n = read(0, &zBuf[got], nBuf-1-got);
    if( n<0 ) n = 0;
    nChar += n;
    if( n==0 ){
      usleep(100000);
      idle++;
      continue;
    }
    zBuf[got+n] = 0;
    idle = 0;
    got += n;

    /* Remove any characters that are followed by a '\n'.  Except,
    ** don't remove characters if the '\n' is followed by nothing
    ** unless '\n' is on a line by itself or preceded by a single
    ** '\r'.
    */
    for(i=0; i<got; i++){
      if( zBuf[i]=='\n' && (zBuf[i+1]!=0 || i==0 || (i==1 && zBuf[0]=='\r')) ){
        if( transcript ){
          int c;
          WriteTimestamp();
          fprintf(transcript," Skip:  \"");
          c = zBuf[i+1];
          zBuf[i+1] = 0;
          WriteHumanReadable(transcript,zBuf);
          zBuf[i+1] = c;
          fprintf(transcript,"\"\n");
          fflush(transcript);
        }
        strcpy(zBuf, &zBuf[i+1]);
        got -= i+1;
        i = -1;
      }
    }
  }
  if( transcript && zBuf[0] ){
    WriteTimestamp();
    fprintf(transcript," Recv:  \"");
    WriteHumanReadable(transcript,zBuf);
    fprintf(transcript,"\"\n");
    fflush(transcript);
  }
  return nChar;
}

/*
** Return true if zPattern occurs anywhere in zBuf
*/
static int Match(char *zBuf, char *zPattern){
  int len = strlen(zPattern);
  while( *zBuf ){
    if( *zBuf==*zPattern && strncmp(zBuf,zPattern,len)==0 ){
      if( transcript ){
        WriteTimestamp();
        fprintf(transcript," Match: \"");
        WriteHumanReadable(transcript,zPattern);
        fprintf(transcript,"\"\n");
        fflush(transcript);
      }
      return 1;
    }
    zBuf++;
  }
  return 0;
}

/*
** The following array maps 8-bit characters into 7-bit characters
** and upper-case into lower-case.
*/
static unsigned char SevenBit[] = {
   0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
  96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
 128,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
  96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
};

/*
** Compare two strings.  In the first string, use only the lower
** 7 bits and convert all upper case to lower case letters.
*/
static int StrNCmp7(char *zBuf, char *zPattern, int n){
  while( n-- ){
    int a = SevenBit[(int)*((unsigned char*)zBuf++)];
    int b = SevenBit[(int)*((unsigned char*)zPattern++)];
    if( a==0 || b==0 || a!=b ){ return a - b; }
  }
  return 0;
}

/*
** Like Match(), but only uses the lower seven 7 bits of zBuf
** and treats all characters in zBuf as lower-case.
*/
static int Match7(char *zBuf, char *zPattern){
  int len = strlen(zPattern);
  while( *zBuf ){
    if( SevenBit[*(unsigned char*)zBuf]==SevenBit[*(unsigned char*)zPattern]
    && StrNCmp7(zBuf,zPattern,len)==0 ){
      if( transcript ){
        WriteTimestamp();
        fprintf(transcript," Match: \"");
        WriteHumanReadable(transcript,zPattern);
        fprintf(transcript,"\"\n");
        fflush(transcript);
      }
      return 1;
    }
    zBuf++;
  }
  return 0;
}

/*
** Return TRUE if the input line looks like a prompt of some kind.
** A "prompt" is a seqence of words followed by a colon, or perhaps
** something like "->".
*/
static int IsPrompt(char *zLine){
  int i;
  for(i=0; zLine[i]!=0 && zLine[i]!='\n' && zLine[i]!='\r'; i++){}
  while( i>0 && isspace(zLine[i-1]) ){ i--; }
  i--;
  if( i<=0 ) return 0;
  if( zLine[i]==':' ) return 1;
  if( zLine[i]=='?' ) return 1;
  if( zLine[i]=='>' ) return 1;
  if( zLine[i]=='$' ) return 1;
  return 0;
}

#define MAX_TIME      70   /* Maximum time to connect */
#define DIAL_TIMEOUT "60"  /* How long to wait for dialing to complete */
#define CHAT_TIMEOUT  "3"  /* How long to wait for responses after dialing */

/* This routine is pretty much the whole point.  Assuming a
** modem is connected on file descriptors 0 and 1, initialize
** the modem, dial the phone number, supply user ID and password
** (if requested).  Return successfully (0) if we see the start of
** PPP communication and return an error (1) if anything goes
** wrong.
**
** This routine runs as root so that it will have permission
** to open the output file associated with WriteChatStat().
*/
static int Chat(int iRec){
  struct termios tio_0_saved;
  struct termios tio_1_saved;
  struct termios tio_new;
  int flags;
  int i;
  char *zVal;
  int timeout;
  int dial_timeout;
  int nTimeout = 0;
  time_t start, now;
  int rc = 4;
  int chatDelay = 0;
  char *zChat;
  char *zPhone;
  char *zExpect[10];
  char *zReply[10];
  char zLine[1000];

  tcgetattr(0,&tio_0_saved);
  tcgetattr(1,&tio_1_saved);
  tio_new = tio_0_saved;
  cfmakeraw(&tio_new);
  tcsetattr(0,TCSANOW,&tio_new);
  tio_new = tio_1_saved;
  cfmakeraw(&tio_new);
  tcsetattr(1,TCSANOW,&tio_new);
  flags = fcntl(0, F_GETFL);
  fcntl(0, F_SETFL, O_NONBLOCK);

  sprintf(zLine,"%s/transcript.%d",DB_DIR,iRec);
  transcript = fopen(zLine,"a");
  if( transcript ){
    fchmod(fileno(transcript),0644);
    fchown(fileno(transcript),0,0);
  }

  zChat = Lookup(iRec,"chat","yes");
  chatDelay = atoi(Lookup(iRec,"delay","0"))*1000;
  for(i=0; i<=9; i++){
    static char *dfltInit[10] = { "atz", "at&d3", };
    sprintf(zLine,"init%d",i);
    zVal = Lookup(iRec,zLine,dfltInit[i]);
    if( zVal ){
      ChatSend(chatDelay,0,zVal,"\r",0);
      zLine[0] = 0;
      ChatRecv(zLine,sizeof(zLine),1000);
      if( !Match(zLine,"OK") ){
        ChatRecv(zLine,sizeof(zLine),1000);
      }
    }
    sprintf(zLine,"expect%d",i);
    zExpect[i] = Lookup(iRec,zLine,0);
    sprintf(zLine,"reply%d",i);
    zReply[i] = Lookup(iRec,zLine,0);
  }
  zPhone = Lookup(iRec,"phone",0);
  if( zPhone==0 ){
    WriteChatStat(iRec,"No phone number specified");
    return 1;
  }
  ChatSend(chatDelay,0,"atd", zPhone, "\r", 0);
  timeout = dial_timeout = atoi(Lookup(iRec,"dialtimeout",DIAL_TIMEOUT));
  time(&start);
  zLine[0] = 0;
  nTimeout = 0;
  while( 1 ){
    int nChar = ChatRecv(zLine,sizeof(zLine),timeout);
    time(&now);
    if( now-start > MAX_TIME ){
      WriteChatStat(iRec,"Login timeout at");
      break;
    }
    if( zLine[0]=='a' && strncmp(zLine,"atd",3)==0
    && zLine[3]==zPhone[0] && strncmp(&zLine[3],zPhone,strlen(zPhone))==0 ){
      /* Skip the echo of the dial command to the modem */
      zLine[0] = 0;
      continue;
    }
    if( nChar==0 ){
      nTimeout++;
      if( timeout==dial_timeout ){
        WriteChatStat(iRec,"Modem could not connect at");
        break;
      }else if( (nTimeout>2 && *Lookup(iRec,"autostart","yes")!='n')
                || zChat[0]=='n' ){
        rc = 0;
        break;
      }else{
        ChatSend(chatDelay,0,"\r",0);
        continue;
      }
    }
    timeout = atoi(Lookup(iRec,"chattimeout",CHAT_TIMEOUT));
    nTimeout = 0;
    for(i=0; i<=9; i++){
      if( zExpect[i]==0 || zReply[i]==0 ) continue;
      if( Match7(zLine,zExpect[i]) ){
        zLine[0] = 0;
        if( strcmp(zReply[i],"ACCEPT")==0 ){
          rc = 0;
          goto chat_done;
        }else if( strcmp(zReply[i],"FAIL")==0 ){
          goto chat_done;
        }else{
          ChatSend(chatDelay,0,zReply[i],"\r",0);
          nTimeout = 0;
          timeout = 3;
          break;
        }
      }
    }
    if( Match(zLine,"BUSY") ){
      WriteChatStat(iRec,"Busy signal at");
      break;
    }else if( Match(zLine,"NO CARRIER") ){
      WriteChatStat(iRec,"No carrier at");
      break;
    }else if( Match(zLine,"NO DIALTONE") ){
      WriteChatStat(iRec,"No dialtone at");
      break;
    }else if( Match(zLine,"NO ANSWER") ){
      WriteChatStat(iRec,"Ringing but no answer at");
      break;
    }else if( Match7(zLine,"ogin:") || Match7(zLine,"rname:") 
              || Match7(zLine,"-on:") || Match7(zLine,"serid:") ){
      zVal = Lookup(iRec,"user","anonymous");
      ChatSend(chatDelay,0,zVal,"\r",0);
      zLine[0] = 0;
    }else if( Match7(zLine,"ssword:") ){
      zVal = Lookup(iRec,"password","");
      ChatSend(chatDelay,1,zVal,"\r",0);
      zLine[0] = 0;
    }else if( Match(zLine,"~\377}#") || Match(zLine,"\300!}!") ){
      rc = 0;
      break;
    }else if( Match(zLine,"CONNECT") && zChat[0]=='n' ){
      rc = 0;
      break;
#if 0
    }else if( Match7(zLine,"destination:") || Match7(zLine,"response:") ){
      ChatSend(chatDelay,0,"ppp\r",0);
      zLine[0] = 0;
#endif
    }else if( IsPrompt(zLine) ){
      ChatSend(chatDelay,0,"ppp\r",0);
      zLine[0] = 0;
    }else{
      /* Ignore it */
    }
  }

chat_done:
  if( transcript ){
    WriteTimestamp();
    fprintf(transcript, rc ? " Fail\n" : " Accept\n");
    fclose(transcript);
  }
  fcntl(0,F_SETFL,flags);
  tcsetattr(0,TCSANOW,&tio_0_saved);
  tcsetattr(1,TCSANOW,&tio_1_saved);
  return rc;
}

/* Print status information for a given service.
*/
static void PrintStatus(int iRec){
  char *zService;
  char *zStatus;
  char *zDiald;
  int pidDiald;
  char zBuf[30];
  char zFile[sizeof(DB_DIR)+100];

  if( iRec<0 || iRec>=nRecord ) return;
  sprintf(zFile,"%s/diald.%d",DB_DIR,iRec);
  zDiald = ReadFile(zFile);
  if( zDiald==0 || (pidDiald=atoi(zDiald))<=0 
     || kill(pidDiald,0)!=0 ){
    zDiald = "";
  }else{
    zDiald = "(diald running) ";
  }
  sprintf(zBuf,"service #%d",iRec);
  zService = Lookup(iRec,"service",zBuf);
  sprintf(zFile,"%s/status.%d",DB_DIR,iRec);
  zStatus = ReadFile(zFile);
  if( zStatus==0 ){
    printf("%s: %sStatus Unknown\n",zService, zDiald);
  }else{
    printf("%s: %s%s",zService, zDiald, zStatus);
    free( zStatus );
  }
}

/*
** Return true if the given string contains a space or $ character
** and therefore needs to be quoted for the shell.
*/
static int NeedQuote(char *z){
  while( *z ){
    if( *z==' ' || *z=='$' || *z=='"' ) return 1;
    z++;
  }
  return 0;
}

/*
** Open the transcript for record N and write the given argv[] string
** to it.
*/
static void WriteArgvToTranscript(int iRec, char **argv){
  int size;
  int i;
  char *zStr, *z;
  char zFile[sizeof(DB_DIR)+20];

  size = 0;
  for(i=0; argv[i]; i++){
    size += strlen(argv[i]) + 3;
  }
  zStr = z = malloc( size );
  if( zStr ){
    for(i=0; argv[i]; i++){
      if( NeedQuote(argv[i]) ){
        *z++ = '\'';
        strcpy(z,argv[i]);
        z += strlen(z);
        *z++ = '\'';
      }else{
        strcpy(z,argv[i]);
        z += strlen(z);
      }
      if( argv[i+1] ){
        *z++ = ' ';
      }
    }
    *z = 0;
    sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
    WriteToFile(zFile, 0600, "%s\n", zStr);
  }
}

/* Check to see if diald is already running.  Return a process ID
** for diald if it is running.  Or return 0 if there is no diald
** running.
*/
static int DialdPid(int iRec){
  char *zDiald;
  int pidDiald;
  char zDialdFile[sizeof(DB_DIR)+100];

  sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
  zDiald = ReadFile(zDialdFile);
  if( zDiald==0 ) return 0;
  pidDiald = atoi(zDiald);
  free(zDiald);
  if( pidDiald==0 ){
    unlink(zDialdFile);
    return 0;
  }
  if( kill(pidDiald,0) ){
    unlink(zDialdFile);
    return 0;
  }
  return pidDiald;
}

/*
** Shutdown the diald process
*/
static int StopDiald(int iRec){
  int pidDiald;

  if( (pidDiald = DialdPid(iRec))!=0 ){
    kill(pidDiald,SIGTERM);
  }
  return 0;
}

/* Attempt to hang up the given port.
*/
static void Hangup(int iRec){
  char *zDev;
  char *zPpp;
  int pidPpp = -1;
  int pidDiald = -1;
  int fd;
  int tries;
  struct termios ios;
  char zFile[sizeof(DB_DIR)+20];

  WriteStatus(iRec,"hangup at");

  /* Send a SIGINT to any diald process that is running.
  ** This is suppose to make diald hangup the line.
  */
  pidDiald = DialdPid(iRec);
  if( pidDiald ){
    kill(pidDiald,SIGINT);
    return;
  }

  /* We only reach this point if diald is not running.
  ** Send a SIGTERM to any running ppp process.  This is suppose
  ** to make ppp hang up the line and exit
  */
  sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
  zPpp = ReadFile(zFile);
  if( zPpp && (pidPpp=atoi(zPpp))>0 ){
    kill(pidPpp,SIGTERM);
    sleep(1);
  }

  /* Try to open the TTY.  Try as many as three times waiting for
  ** one second between each attempt.  After the second failed attempt,
  ** send a SIGKILL to any diald or ppp process out there.
  */
  zDev = Lookup(iRec,"tty","/dev/modem");
  tries = 0;
  while( (fd=open(zDev,O_RDWR|O_NONBLOCK))<0 && tries<2 ){
    if( tries==1 ){
      if( pidPpp>0 ){
        kill(pidPpp, SIGKILL);
      }else if( pidDiald>0 ){
        kill(pidDiald, SIGKILL);
      }
    }
    tries++;
    sleep(1);
  }

  /* Change the buad rate of the TTY to 0.  This is suppose to make
  ** the DTR line go low, which should make the modem hangup.
  */ 
  if( fd>=0 ){
    tcgetattr(fd, &ios);
    cfsetospeed(&ios, B0);
    tcsetattr(fd, TCSANOW, &ios);
    close(fd);
  }
}

/*
** Write into the given buffer the name of a file that will contain
** the PID of the PPP process that is calling on serial device zDev.
** Assume the size of the buffer into which we write is at least
** sizeof(DB_DIR)+100.
**
** The zDev probably contains '/' characters.  These must be converted
** to something different.
*/
static void MakeDeviceFile(char *zFile, char *zDev){
  int i;
  sprintf(zFile,"%s/device%.80s", DB_DIR, zDev);
  i = strlen(DB_DIR) + 5;
  while( zFile[i] ){
    if( zFile[i]=='/' ){ zFile[i] = '.'; }
    i++;
  }
}

/*
** Given a modem device name, find the service number.
*/
static int DeviceToRec(char *zDev){
  char *z;
  int iRec;
  char zFile[sizeof(DB_DIR)+100];

  MakeDeviceFile(zFile, zDev);
  z = ReadFile(zFile);
  if( z==0 || (iRec=atoi(z))<0 || iRec>=nRecord ){
    for(iRec=0; iRec<nRecord; iRec++){
      z = Lookup(iRec,"tty","/dev/modem");
      if( strcmp(z,zDev)==0 ) break;
    }
    if( iRec>nRecord ) iRec = FIND_ERROR;
  }
  return iRec;
}

/* This routine is called when "pppd" executes "ip-up".
*/
static void IpUp(char **argv){
  int iRec;
  int i;
  char zFile[sizeof(DB_DIR)+20];

  iRec = DeviceToRec(argv[1]);
  if( iRec<0 ) return;
  WriteStatus(iRec,"%s %s to %s\nUp since",argv[0],argv[3],argv[4]);
  sprintf(zFile,"%s/pid.%d", DB_DIR, iRec);
  unlink(zFile);
  for(i=0; i<=9; i++){
    char *zNet;
    char zRoute[1000];
    char zLabel[20];
    sprintf(zLabel,"route%d",i);
    zNet = Lookup(iRec,zLabel,0);
    if( zNet==0 ) continue;
    sprintf(zRoute,"/sbin/route add -net %.100s %.100s", zNet, argv[0]);
    system(zRoute);
  }
}

/* This routine is called when "pppd" executes "ip-down".
*/
static void IpDown(char **argv){
  int iRec;
  char zFile[sizeof(DB_DIR)+80];

  iRec = DeviceToRec(argv[1]);
  if( iRec<0 ) return;
  WriteStatus(iRec,"down since");
  MakeDeviceFile(zFile, argv[1]);
  unlink(zFile);
  sprintf(zFile,"%s/ppp.%d", DB_DIR, iRec);
  unlink(zFile);
}

/*
** This routine creates a session log for the most recent login
** attempt.  The session log consists of two data sources:
**
**    1.  The modem dialing and logon transcript.
**
**    2.  All messages written to the /var/log/messages file by pppd.
**
** The session log is overwritten after every logon attempt.
*/
static void MakeSessionLog(time_t startTime, int pid, int iRec){
  FILE *out;
  char *z;
  struct tm *pTm;
  char zFile[sizeof(DB_DIR)+100];
  char zCmd[sizeof(DB_DIR)+100];

  out = fopen(SESSIONLOG,"w");
  if( out==0 ) return;
  fchmod(fileno(out),0644);
  fprintf(out,"This is a diagnostic log of the most recent login\n"
     "attempt by eznet.\n\n");
  z = Lookup(iRec,"service",0);
  if( z ){
    fprintf(out, "  ISP Name: \"%s\"\n", z);
  }
  pTm = localtime(&startTime);
  fprintf(out,"  Call Initiated: %s\n", asctime(pTm));
  sprintf(zFile,"%s/transcript.%d", DB_DIR, iRec);
  z = ReadFile(zFile);
  if( z ){
    int i;
    for(i=0; z[i] && z[i]!='\n'; i++){}
    fprintf(out,
      "The UNIX command used to initiate the connection attempt was: \n\n"
      "%.*s\n\n",
      i, z);
    if( z[i] ){ i++; }
    fprintf(out,"The interaction with the modem was as follows:\n\n"
      "%s\n", &z[i]);
    free(z);
  }else{
    fprintf(out,"The modem transcript could not be located!\n\n");
  }
  fprintf(out,
    "The following messages were written to the system log\n"
    "(in /var/log/messages) by pppd as it attempted to negotiate\n"
    "a PPP connection.");
  z = Lookup(iRec,"debug","n");
  if( z && *z=='y' ){
    fprintf(out,"  Details may be omitted by clearing the\n"
       "\"debug=%s\" property.\n\n", z);
  }else{
    fprintf(out,"  To see more detail, set the \"debug=yes\"\n"
       "property and reattempt the logon.\n\n");
  }
  fclose(out);
  sprintf(zCmd,"tail -200 /var/log/messages | grep 'pppd.%d.' >>%s",
    pid, SESSIONLOG);
  system(zCmd);
}

/* This routine is called in order to attempt to establish
** a connection.  Return 0 if successful and non-zero if we fail.
**
** The process ID of the PPP process is written to *piChild if piChild!=0.
*/
static int Dial(int iRec, int *piChild){
  char *zPw;
  char *zDevice;
  char *zUser;
  char *zPpp;
  char *zService;
  char *zIdle;
  int pidPpp;
  int pidDiald;
  int rc;
  int isup = 1;
  time_t startTime;
  char zDeviceFile[sizeof(DB_DIR)+100];
  char zChatStat[sizeof(DB_DIR)+100];
  char zPidFile[sizeof(DB_DIR)+100];
  char zConnect[200];
  char zServiceBuf[50];

  /* Check to see if we are already running directly.
  */
  startTime = time(0);
  sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
  zPpp = ReadFile(zPidFile);
  if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
    return 0; 
  }

  /* Check to see if diald is running.  If it is, then
  ** just send it a SIGUSR1 to bring the link up.
  */
  if( (pidDiald = DialdPid(iRec))>0 ){
    kill(pidDiald,SIGUSR1);
    return 0;
  }

  /* Initialize the ipup, ipdown, chap-secrets and pap-secrets files.
  */
  SetIpUpDown("up");
  SetIpUpDown("down");
  sprintf(zServiceBuf,"x%d",iRec);
  zService = Lookup(iRec,"service",zServiceBuf);
  zUser = Lookup(iRec,"user","anonymous");
  zPw = Lookup(iRec, "password", "*");
  if( zPw ){
    SetSecrets("pap-secrets",zService,zUser,zPw);
    SetSecrets("chap-secrets",zService,zUser,zPw);
  }
  zDevice = Lookup(iRec, "tty", "/dev/modem");

  /* Set the device file to the current ordinal number.  This is needed
  ** by the ipup and ipdown scripts.
  */
  MakeDeviceFile(zDeviceFile, zDevice);
  WriteToFile(zDeviceFile, 0644, "%d\n", iRec);
  WriteToFile(zPidFile, 0644, "%d\n", getpid());

  /* Set the status to dialing.
  */
  WriteStatus(iRec, "dialing since");

  /* The child does the work.  The parent waits for it to finish.
  */
  rc = fork();
  if( rc<0 ){
    WriteStatus(iRec,"fork failed");
    return 1;
  }
  if( rc==0 ){
    /* The child process */
    int argc = 0;
    char *z;
    int i;
    char zLabel[100];
    char *argv[200];

    argv[argc++] = Lookup(iRec, "pppd", PPP_EXE);
    argv[argc++] = zDevice;
    argv[argc++] = Lookup(iRec,"baud","115200");
    argv[argc++] = "connect";
    sprintf(zConnect,SELF " chat %d",iRec);
    argv[argc++] = zConnect;
    z = Lookup(iRec, "defaultroute", "y");
    if( *z=='y' ){
      argv[argc++] = "defaultroute";
    }
    argv[argc++] = "lock";
    argv[argc++] = "modem";
    argv[argc++] = "asyncmap";
    argv[argc++] = Lookup(iRec,"asyncmap","0");
    z = Lookup(iRec,"debug",0);
    if( z && *z=='y' ){
      argv[argc++] = "debug";
    }
    argv[argc++] = "crtscts";
    argv[argc++] = "-detach";
    z = Lookup(iRec,"persist","no");
    if( *z=='y' ){
      argv[argc++] = "persist";
    }else{
      zIdle = Lookup(iRec,"idle","600");
      if( atoi(zIdle)>0 ){
        z = Lookup(iRec,"pppversion",DFLT_PPP_VERSION);
        argv[argc++] = strcmp(z,"2.3")<0 ? "idle-disconnect" : "idle";
        argv[argc++] = zIdle;
      }
    }
    argv[argc++] = "user";
    argv[argc++] = zUser;
    argv[argc++] = "remotename";
    argv[argc++] = zService;
    for(i=0; i<10; i++){
      sprintf(zLabel,"pppopt%d",i);
      z = Lookup(iRec,zLabel,0);
      if( z==0 ) break;
      argv[argc++] = z;
    }
    argv[argc] = 0;
    WriteArgvToTranscript(iRec,argv);
    execv(argv[0],argv);
    exit(0);
  }else{ 
    /* The parent process */
    char zPppFile[sizeof(DB_DIR)+20];

    if( piChild ) *piChild = rc;
    sprintf(zPppFile,"%s/ppp.%d", DB_DIR, iRec);
    WriteToFile(zPppFile,0644,"%d\n",rc);
    while( access(zPidFile,0)==0 ){
      int status;
      if( waitpid(rc,&status,WNOHANG)>0 ){
        isup = 0;
        break;
      }
      sleep(1);
    }
    sprintf(zChatStat,"%s/chatstat.%d", DB_DIR, iRec);
    if( !isup ){
      char *z = ReadFile(zChatStat);
      if( z ){
        WriteStatus(iRec,z);
      }else{
        WriteStatus(iRec,"pppd failure at");
      }
      unlink(zDeviceFile);
      unlink(zPppFile);
    }
    unlink(zPidFile);
    unlink(zChatStat);
    MakeSessionLog(startTime, rc,iRec);
    PrintStatus(iRec);
  }
  return !isup;
}


/* This routine is called in order to active the diald daemon.
*/
static int StartDiald(int iRec){
  char *zPpp;
  char *zDiald;
  char *zPw;
  char *zUser;
  char *zService;
  char *z;
  int pidPpp;
  int pidDiald;
  int rc;
  int i;
  FILE *out;
  char zConfFile[sizeof(DB_DIR)+100];
  char zPidFile[sizeof(DB_DIR)+20];
  char zDialdFile[sizeof(DB_DIR)+100];
  char zServiceBuf[50];

  /* Check to see if diald is already running.
  */
  sprintf(zDialdFile, "%s/diald.%d", DB_DIR, iRec);
  zDiald = ReadFile(zDialdFile);
  if( zDiald && (pidDiald=atoi(zDiald))>0 && kill(pidDiald,0)==0 ){
    return 0;
  }

  /* Check to see if we are already running directly.  If so, then
  ** do a hangup before continuing.
  */
  sprintf(zPidFile, "%s/pid.%d", DB_DIR, iRec);
  zPpp = ReadFile(zPidFile);
  if( zPpp && (pidPpp=atoi(zPpp))>0 && kill(pidPpp,0)==0 ){
    Hangup(iRec);
  }

  /* Initialize the chap-secrets and pap-secrets files.
  */
  sprintf(zServiceBuf,"x%d",iRec);
  zService = Lookup(iRec,"service",zServiceBuf);
  zUser = Lookup(iRec,"user","anonymous");
  zPw = Lookup(iRec, "password", "*");
  if( zPw ){
    SetSecrets("pap-secrets",zService,zUser,zPw);
    SetSecrets("chap-secrets",zService,zUser,zPw);
  }

  /* Create the diald configuration script.
  */
  sprintf(zConfFile, "%s/diald.conf.%d", DB_DIR, iRec);
  out = fopen(zConfFile,"w");
  if( out==0 ){
    WriteStatus(iRec, "configuration failed");
    return 1;
  }
  fprintf(out,"mode ppp\n");
  fprintf(out,"device %s\n", Lookup(iRec,"tty","/dev/modem"));
  fprintf(out,"connect '" SELF " chat %d'\n", iRec);
  fprintf(out,"speed %s\n", Lookup(iRec,"baud","115200"));
  z = Lookup(iRec,"defaultroute","y");
  if( *z=='y' ){
    fprintf(out,"defaultroute\n");
  }
  fprintf(out,"local %s\n", Lookup(iRec,"local","127.0.0.100"));
  fprintf(out,"remote %s\n", Lookup(iRec,"remote","127.0.0.101"));
  fprintf(out,
    "lock\n"
    "modem\n"
    "crtscts\n"
    "-reroute\n"
    "dynamic\n"
    "-daemon\n"
  );
  fclose(out);

  /* Fire up the diald process
  */
  rc = fork();
  if( rc<0 ){
    WriteStatus(iRec,"fork failed");
    return 1;
  }
  if( rc==0 ){
    /* The child process */
    int argc = 0;
    char *argv[30];

    argv[argc++] = Lookup(iRec, "diald", DIALD_EXE);
    argv[argc++] = "-f";
    argv[argc++] = zConfFile;
    argv[argc++] = "--";
    argv[argc++] = "remotename";
    argv[argc++] = zService;
    argv[argc++] = "user";
    argv[argc++] = zUser;
    for(i=0; i<10; i++){
      char zLabel[100];
      sprintf(zLabel,"pppopt%d",i);
      z = Lookup(iRec,zLabel,0);
      if( z==0 ) break;
      argv[argc++] = z;
    }
    argv[argc] = 0;
    WriteArgvToTranscript(iRec,argv);
    close(0);
    close(1);
    close(2);
    open("/dev/null",O_RDWR);
    open("/dev/null",O_RDWR);
    open("/dev/null",O_RDWR);
    execv(argv[0],argv);
    exit(0);
  }
  /* The parent process */
  WriteToFile(zDialdFile,0644,"%d\n",rc);
  sleep(1);
  if( kill(rc,0) ){
    WriteStatus(iRec,"diald startup failed at");
    rc = 1;
  }else{
    WriteStatus(iRec,"diald started at");
    rc = 0;
  }
  return rc;
}

/*
** Given a configuration file entry, return a "safe" value for
** this entry.  If the entry is the password and the real user
** is anything other than root, return "*********".
*/
static char *SafeValue(ConfEntry *p){
  char *zVal;
  if( strcmp(p->zLabel,"password")==0 && getuid()!=0 ){
    zVal = "**********";
  }else{
    zVal = p->zValue;
  }
  return zVal;
}

/* Print a usage comment and die.  A call to this
** routine never returns.
*/
static void usage(char *argv0){
  fprintf(stderr,
    "User commands:\n"
    "  %s add ORDINAL FIELD=VALUE...\n"
    "  %s change ORDINAL|SERVICE ?FIELD=VALUE?...\n"
    "  %s delete ORDINAL|SERVICE|IP\n"
    "  %s dialdoff ORDINAL|IP|DEVICE\n"
    "  %s dialdon ORDINAL|IP|DEVICE\n"
    "  %s down ORDINAL|IP|DEVICE|all\n"
    "  %s list ?ORDINAL|SERVICE?\n"
    "  %s log\n"
    "  %s move ORDINAL ORDINAL\n"
    "  %s names\n"
    "  %s status\n"
    "  %s up SERVICE|IP|ORDINAL\n"
    "  %s wait SERVICE|IP|ORDINAL\n"
    "\n"
    "Commands for internal use only:\n"
    "  %s chat ORDINAL\n"
    "  %s ipdown INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
    "  %s ipup INTERFACE DEVICE SPEED LOCAL-IP REMOTE-IP\n"
    "\n"
    "Field names:\n"
    "  phone = phone number for modem to dial\n"
    "  user = user login name\n"
    "  password = login password\n"
    "  tty = serial port for the modem\n"
    "  baud = baud rate\n"
    "  service = name of ISP\n"
    "  chat = \"no\" to ignore \"login:\" prompts, etc.\n"
    "  autostart = \"no\" to prevent starting pppd if no \"login:\" seen\n"
    "  pppversion = what version of pppd is being used\n"
    "  debug = \"yes\" to turn on pppd debugging\n"
    "  expectN = Nth extra expect string (N between 1 and 9)\n"
    "  replyN = Reply to issue when expectN is seen\n"
    "  initN = Nth modem initialization string  (N between 1 and 9)\n"
    "  diald = name of diald executable\n"
    "  pppd = name of pppd executable\n"
    "  local = local IP address for diald slip link\n"
    "  remote = remote IP address for diald slip link\n"
    "  idle = number of seconds for idle timeout\n"
    "  persist = \"yes\" for a permanent connection\n"
    "  defaultroute = \"no\" to prevent creating a default route\n"
    "  pppoptN = Nth extra parameter to pppd\n"
    "  routeN = Add a route to the specified network when pppd comes up\n"
    "  ip = network that this service gives access to\n"
    "  netmask = netmask for the ip= network\n"
    "  dialtimeout = time allowed for the modem to connect\n"
    "  chattimeout = time allowed for chat responses\n"
    "  errordelay = \"wait\" command waits this long after an error\n",

    argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0,
    argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0
  );
  exit(1);
}


int main(int argc, char **argv){
  int iRec;
  int rc = 0;

  if( argc<2 ) usage(argv[0]);
  if( access(DB_DIR,0) ){
    mkdir(DB_DIR,0755);
    chown(DB_DIR,0,0);
    if( access(DB_DIR,0) ){
      fprintf(stderr,"%s: missing directory \"%s\"\n", argv[0], DB_DIR);
      exit(1);
    }
  }
  ReadDb();
  if( (argc==2 || argc==3) && strcmp(argv[1],"list")==0 ){
    int iRec = FindRecord(argv[2]);
    Unprivileged();
    if( iRec<0 || iRec>=nRecord ){
      int i;
      for(i=0; i<nRecord; i++){
        ConfEntry *p = aRecord[i] = sort(aRecord[i]);
        if( p==0 ) continue;
        printf("Record %d:\n",i);
        for(; p; p=p->next){
          printf("  %s = %s\n",p->zLabel, SafeValue(p));
        }
      }
    }else{
      ConfEntry *p = aRecord[iRec] = sort(aRecord[iRec]);
      for(; p; p=p->next){
        printf("  %s = %s\n",p->zLabel, SafeValue(p));
      }
    }    
  }else if( argc==2 && strcmp(argv[1],"log")==0 ){
    char *z;
    Unprivileged();
    z = ReadFile(SESSIONLOG);
    if( z ){
      printf("%s",z);
    }
  }else if( (argc==2 || argc==3 ) && strcmp(argv[1],"tcllist")==0 ){
    int i;
    Unprivileged();
    for(i=0; i<nRecord; i++){
      ConfEntry *p = aRecord[i] = sort(aRecord[i]);
      if( p==0 ) continue;
      for(; p; p=p->next){
        printf("set eznet(%d:%s) ",i,p->zLabel);
        WriteTcl(stdout,SafeValue(p));
        printf("\n");
      }
    }
  }else if( argc==2 && strcmp(argv[1],"names")==0 ){
    int i;
    Unprivileged();
    for(i=0; i<nRecord; i++){
      char *z = Lookup(i,"service",0);
      if( z ){ 
        printf("%s\n",z);
      }else{
        printf("%d\n",i);
      }
    }
  }else if( argc>=3 && strcmp(argv[1],"add")==0 ){
    int iRec;
    Unprivileged();
    iRec = FindRecord(argv[2]);
    if( iRec==FIND_ERROR ){
      AddEntries(nRecord, argc-2, argv+2);
    }else if( iRec>=nRecord ){
      AddEntries(nRecord, argc-3, argv+3);
    }else{
      AddEntries(nRecord, argc-3, argv+3);
      MoveRecord(nRecord, iRec);
    }
    WriteDb();
  }else if( argc==3 && strcmp(argv[1],"delete")==0 ){
    int iRec = FindRecord(argv[2]);
    Unprivileged();
    if( iRec==FIND_ALL ){
      nRecord = 0;
    }else if( iRec==FIND_ERROR ){
      usage(argv[0]);
    }else{
      MoveRecord(iRec,nRecord-1);
      aRecord[nRecord-1] = 0;
    }
    WriteDb();
  }else if( argc>=4 && strcmp(argv[1],"change")==0 ){
    int iRec;
    Unprivileged();
    iRec = FindRecord(argv[2]);
    if( iRec==FIND_ALL ){
      usage(argv[0]);
    }else{
      if( iRec==FIND_ERROR ){
        iRec = nRecord==1 ? 0 : nRecord;
      }
      AddEntries(iRec, argc-3, argv+3);
    }
    WriteDb();
  }else if( argc==4 && strcmp(argv[1],"move")==0 ){
    int from, to;
    Unprivileged();
    if( argc!=4 ) usage(argv[0]);
    from = GetInt(argv[2]);
    to = GetInt(argv[3]);
    if( from>=0 && to>=0 ) MoveRecord(from,to);
    WriteDb();
  }else if( argc==3 && strcmp(argv[1],"chat")==0 ){
    iRec = FindRecord(argv[2]);
    if( iRec<0 ) usage(argv[0]);
    rc = Chat(iRec);
  }else if( (argc==2 || argc==3) && strcmp(argv[1],"status")==0 ){
    iRec = FindRecord(argv[2]);
    Unprivileged();
    if( iRec<0 ){
      for(iRec=0; iRec<nRecord; iRec++){
        PrintStatus(iRec);
      }
    }else{
      PrintStatus(iRec);
    }
  }else if( (argc==2 || argc==3) && strcmp(argv[1],"down")==0 ){
    iRec = FindRecord(argv[2]);
    /*  Unprivileged();  --- Yeah.  Let anybody do a hangup... */
    if( iRec==FIND_ALL ){
      system("kill -TERM `pidof pppd` 2>/dev/null");
      system("kill -TERM `pidof diald` 2>/dev/null");
      for(iRec=0; iRec<nRecord; iRec++){
        Hangup(iRec);
      }
    }else{
      Hangup(iRec);
    }
  }else if( argc==7 && strcmp(argv[1],"ipup")==0 ){
    Unprivileged();
    IpUp(&argv[2]);
  }else if( argc==7 && strcmp(argv[1],"ipdown")==0 ){
    Unprivileged();
    IpDown(&argv[2]);
  }else if( (argc==2 || argc==3) && strcmp(argv[1],"up")==0 ){
    if( access(SELF,X_OK)!=0 ){
      fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
      exit(1);
    }
    iRec = FindRecord(argv[2]);
    if( iRec<0 ){ usage(argv[0]); }
    rc = Dial(iRec, 0);
  }else if( (argc==2 || argc==3) && strcmp(argv[1],"wait")==0 ){
    int child;
    if( access(SELF,X_OK)!=0 ){
      fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
      exit(1);
    }
    iRec = FindRecord(argv[2]);
    if( iRec<0 ){ usage(argv[0]); }
    rc = Dial(iRec, &child);
    if( rc==0 ){
      int status;
      waitpid(child, &status, 0);
    }else{
      int delay = atoi(Lookup(iRec,"errordelay","0"));
      if( delay>0 ) sleep(delay);
    }
  }else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdon")==0 ){
    if( access(SELF,X_OK)!=0 ){
      fprintf(stderr,"eznet must be installed as \"" SELF "\"\n");
      exit(1);
    }
    iRec = FindRecord(argv[2]);
    if( iRec<0 ){ usage(argv[0]); }
    rc = StartDiald(iRec);
  }else if( (argc==3 || argc==2) && strcmp(argv[1],"dialdoff")==0 ){
    iRec = FindRecord(argv[2]);
    if( iRec<0 ){ usage(argv[0]); }
    rc = StopDiald(iRec);
  }else if( argc==3 && strcmp(argv[1],"delete")==0 ){
    Unprivileged();
    iRec = FindRecord(argv[2]);
    if( iRec>=0 && iRec<nRecord ){
      MoveRecord(iRec, nRecord-1);
      nRecord--;
      WriteDb();
    }
  }else{
    Unprivileged();
    usage(argv[0]);
  }
  return rc;
}
Eznet-1.11.1/ezsetup100755      0      0       10037  6706436236  12421 0ustar  rootroot#!/bin/sh

MSG() {
cat <<EOF

# ezsetup
#
# Setup PPP connection with eznet: (2-24-98) Kent Robotti
#
# This script will ask you a few questions to setup a PPP connection
# to your service provider using 'eznet'.
#
# ezsetup -s   <Setup eznet>
# ezsetup -h   <This message>

EOF
}

if [ "$1" = "" -o "$1" = "-h" ]; then
MSG
exit
fi

if [ ! "$UID" = "0" ]; then
echo "You need to be 'root' to run this script."
exit
fi

if [ "$1" = "-s" ]; then

if [ ! -x /usr/bin/eznet ]; then
echo "You don't have '/usr/bin/eznet' installed, i can't continue."
exit
fi

if [ -s /etc/ppp/eznet/eznet.conf ]; then
echo
echo "You have a /etc/ppp/eznet/eznet.conf file, do you"
echo "want to remove it or add a new entry to it?"
echo -n "Should i remove it? (N/y) : "
read ans
if [ "$ans" = "y" -o "$ans" = "Y" ]; then
rm -f /etc/ppp/eznet/* 2>/dev/null
else
ADD="YES"
fi
fi

echo
echo "Example: 1234567"
echo -n "What is your service providers Phone Number? : "
read PN

if [ "$PN" = "" ]; then
echo "Cancelled."
exit
fi

echo
echo "Examples: something.com something.net something.edu"
echo -n "What is your service providers Domain Name? : "
read DN

if [ "$DN" = "" ]; then
echo "Cancelled."
exit
fi

echo
echo "What is the IP address of your service providers nameserver?"
echo "If you have a IP address in /etc/resolv.conf press [Enter] and skip this."
echo "Example: 205.252.116.61"
echo -n "IP address? : "
read IP

echo
echo "Example: jerry"
echo -n "What is your Username? : "
read UN

if [ "$UN" = "" ]; then
echo "Cancelled."
exit
fi

echo
echo "Example: Xpo4Gkh9"
echo -n "What is your Password? : "
read PW

if [ "$PW" = "" ]; then
echo "Cancelled."
exit
fi

echo
echo "Examples: ttyS0 = com1 ttyS1 = com2 ttyS2 = com3 ttyS3 = com4"
echo "Don't include the /dev/ part in your answer."
echo -n "Where is your modem?  ttyS? : "
read MOD

if [ "$MOD" = "" ]; then
echo "Cancelled."
exit
fi

echo
echo "You probably have PPP 2.2.something or the more recent 2.3.something." 
echo "If not sure or NO press [Enter] otherwise put: y"
echo -n "Do you have version 2.2 of PPP? (N/y) : "
read VER

if [ "$VER" = "y" -o "$VER" = "Y" ]; then
ver=2.2
fi

if [ ! -d /etc/ppp ]; then
mkdir -p /etc/ppp
fi

if [ -f /etc/ppp/pap-secrets -o -f /etc/ppp/chap-secrets ]; then
rm -f /etc/ppp/pap-secrets
rm -f /etc/ppp/chap-secrets
fi

if [ -f /etc/ppp/ip-up -o -f /etc/ppp/ip-down ]; then
rm -f /etc/ppp/ip-up
rm -f /etc/ppp/ip-down
fi

if [ -s /etc/ppp/options ]; then
mv /etc/ppp/options /etc/ppp/options.off
fi

if [ ! "$IP" = "" ]; then
echo "nameserver $IP" > /etc/resolv.conf
fi

if [ -c "/dev/$MOD" ]; then
ln -sf /dev/$MOD /dev/modem 2>/dev/null
else
DEV="NO"
fi

if ! type -all pidof >/dev/null 2>&1 ; then
if [ -x /sbin/killall5 ]; then
ln -sf /sbin/killall5 /sbin/pidof 2>/dev/null
elif [ -x /bin/killall5 ]; then
ln -sf /bin/killall5 /bin/pidof 2>/dev/null
elif [ -x /usr/sbin/killall5 ]; then
ln -sf /usr/sbin/killall5 /usr/sbin/pidof 2>/dev/null
elif [ -x /usr/bin/killall5 ]; then
ln -sf /usr/bin/killall5 /usr/bin/pidof 2>/dev/null
elif [ -x /sbin/killall ]; then
ln -sf /sbin/killall /sbin/pidof 2>/dev/null
elif [ -x /bin/killall ]; then
ln -sf /bin/killall /bin/pidof 2>/dev/null
elif [ -x /usr/sbin/killall ]; then
ln -sf /usr/sbin/killall /usr/sbin/pidof 2>/dev/null
elif [ -x /usr/bin/killall ]; then
ln -sf /usr/bin/killall /usr/bin/pidof 2>/dev/null
fi
fi

chown root.root /usr/bin/eznet 2>/dev/null
chmod 4755 /usr/bin/eznet 2>/dev/null

/usr/bin/eznet add service=$DN phone=$PN user=$UN password=$PW 

if [ "$ver" = "2.2" ]; then
/usr/bin/eznet change $DN pppversion=2.2
fi

if [ "$ADD" = "YES" ]; then
N="`tail -n 1 /etc/ppp/eznet/eznet.conf | cut -b-1 2>/dev/null`" || N="number?"
OR="or  eznet up $DN"
fi

echo
echo "Done ..."
echo "To make the PPP connection do this."
echo "eznet up $N  $OR"
echo "To end the PPP connection do this."
echo "eznet down"
echo "Your config file is in /etc/ppp/eznet/eznet.conf."
if [ "$DEV" = "NO" ]; then
echo
echo "WARNING: The modem device you chose '/dev/$MOD' doesn't exist."
echo "Create '/dev/MAKEDEV $MOD' and link 'ln -sf /dev/$MOD /dev/modem'."
echo
fi
echo "Done ..."
else
MSG
fi

Eznet-1.11.1/eznet.txt100600      0      0       41651  6706436236  12660 0ustar  rootroot============================================================================

There's a compiled libc6 eznet in the bin directory.

To compile eznet do this.

eznet-1.11.1# gcc -O2 -o eznet eznet.c
              chown root.root /usr/bin/eznet
              chmod 04755 /usr/bin/eznet

Put eznet and ezsetup in the /usr/bin directory.

You can use the shell script 'ezsetup' to setup 'eznet' to make the PPP
connection, it will ask you a few questions i.e. username, password,
and phone number.

Made these changes to eznet.c 1.11

Changed DB_DIR to /etc/ppp/eznet
Added 'asyncmap 0' to pppd options
Changed sesion.html to sesion.log
Changed idle 300 to 600
Removed mtu and mru 552
Added new line for Up since
Changed /bin/kill -TERM `/sbin/pidof pppd` to kill -TERM `pidof pppd`
Changed /bin/kill -TERM `/sbin/pidof diald` to kill -TERM `pidof diald`

2-24-98 Kent Robotti

The text below is by the author of eznet taken from his home page.

============================================================================

   Notice: This page is incomplete. I've tried to provide enough data to
   get started, but I don't have the time right now to write a complete
   tutorial, or even a decent man-page. I hope that this will not prevent
   you from trying out eznet and sending me your comments. If it does,
   please check back in a few weeks to see what improvements have been
   made. Thanks...
   
                        Eznet: A Simpler Way To Do PPP
                                       
   Let's face it: setting up PPP under Linux can be intimidating,
   especially for newbies. Between chat scripts and a zillion options to
   pppd, setting up PPP can be a hassle for experienced users. It's a
   wonder beginners are able to do it at all.
   
   ``eznet'' is a new program designed to make it much easier to get PPP
   running under Linux. Basically all you have to do is supply a name for
   your ISP, a login name, password and phone number and eznet will do
   the rest. There are not configuration files to edit, no chat scripts
   to design and no need to figure out what a "chap-secret" is. Assuming
   you have pppd and eznet correctly installed (which should happen
   automatically from your CD-ROM, right?) the following command is all
   it takes to set up PPP:
   
   eznet add service=NAME user=ABC password=XYZ phone=5551234

   The setup command has to be entered as root. But after the setup is
   done, any user can bring up the PPP connection by simply typing:
   
   eznet up NAME

   If everything works right, you should have a PPP connection in 30
   seconds or so. The ``eznet up'' command will not return until either
   the connection has been established, or the connection attempt fails.
   The command will return a non-zero result code if the connection
   attempt fails, making is suitable for use within shell-scripts.
   ``eznet up'' prints a status message just before it returns so that
   interactive users can also see what happened.
   
   The connection established by eznet will automatically go down after 5
   minutes of inactivity. Or you can bring it down manually by typing:
   
   eznet down NAME

   To see the current status of a connection, type:
   
   eznet status

   Eznet also supports demand dialing. If you have Eric Schenk's
   ``Diald'' package installed, you can start it up by typing:
   
   eznet dialdon NAME

   And turn diald back off using:
   
   eznet dialdoff NAME

   If you prefer to use the request-route feature of kerneld, then just
   write the following script into the file ``/sbin/request-route'':
   
   #!/bin/sh
   eznet up NAME

   I prefer to use diald, since it seems to work better and because
   kerneld will not be supported in the 2.2.X kernels. But either method
   works.
   
Viewing And Changing The Configuration

   Eznet has a configuration file (in /var/eznet/eznet.conf) but you
   never need to read or edit it. Instead, use eznet itself to keep the
   configuration up-to-date. To see the current configuration type:
   
   eznet list

   This command can be run as an unprivileged user, but if it is, the
   passwords will not be displayed. Be careful when using the list
   command as root because the passwords will appear in plain text on
   your screen -- make sure nobody is looking over your shoulder!
   
   To change the configuration, use a command like the following:
   
   eznet change NAME ARG1=VALUE1 ARG2=VALUE2...

   Only root is allowed to change configuration values. The four basic
   configuration values are shown in the sample command above: "service"
   "user", "password" and "phone". But there are many others. A complete
   list is shown below. Hopefully, most users will never need to deal
   with more than the basic four.
   
Multiple Service Providers

   Eznet is able to manage connections to multiple service providers.
   (The only purpose of the "service" option is to give a symbolic name
   to each service provider.) Suppose, for example, that you have a
   personal dialup account with AT&T WorldNet and a separate dialup
   account where you work. You could setup two eznet configurations like
   this:
   
   eznet add service=work user=slave5 'password=yes sir!' phone=5551234
   eznet add service=worldnet user=123456@worldnet.att.net \
         password=xyzzy phone=5554321

   Now eznet knows about both services. To log into work, just type
   
   eznet up work

   And to log into your personal WorldNet account, type
   
   eznet up worldnet

   Assuming you have two modems, you can even log into both serices at
   the same time!
   
   Some eznet commands require that you specify a particular service. The
   "up" command is like this. Both other commands will apply to all
   services if no particular service name is given. For example, if you
   type
   
   eznet down

   then eznet immediately terminates all connections. The best why to
   know whether or not a particular eznet command will work globally is
   to try it and see. Note that if you only have a single service
   provider defined, then there is no need to mention its name with any
   command -- eznet will always know which service you mean.
   
Optimizing and Trouble Shooting

   In some situations, eznet may require some additional hints in order
   to work optimally, or to work at all. If you are having trouble
   getting eznet to work, or wonder if you can make it work better, this
   section is for you.
   
   If eznet is not working for you, the first step is to figure out why
   not. If eznet fails for any reason, run the command
   
   eznet log

   to get a diagnostic log of what it did on the most recent connection
   attempt. This diagnostic log will often immediately show what the
   trouble is. Then all you have to do is fix the problem. Usually adding
   a few additional configuration options to your eznet setup will do the
   trick.
   
   Eznet makes very few assumptions about your modem. Basically, it
   assumes that the modem supports the single Hayes modem command "atd"
   for dialing, and that lowering the DTR line on your serial port will
   cause the modem to hang up and reset. If your particular modem
   requires some additional setup, you can cause eznet to issue extra
   initialization commands to the modem using configuration parameters.
   For example, to dial one of my local ISPs (named "VNet") using a
   SupraFaxModem, I have to configure eznet as follows:
   
   eznet change vnet init0=atz 'init1=at&d3'

   The "initN" options (where N is between 0 and 9) each contain an
   additional modem initialization that eznet issues prior to dialing the
   modem. In this example, the first string resets the modem, and the
   second string tells the modem to hangup and reset whenever the DTR
   line goes low. Some folks like to turn the speaker on their modem off.
   The following command will do that:
   
   eznet change vnet init2=atm0

   There are some minor differences between version 2.2 and 2.3 of pppd
   that eznet needs to know about. Eznet assumes by default that you are
   using version 2.3 or later of pppd. If you get an error message like
   this:
   
   idle: unrecognized command
   pppd version 2.2 patch level 0

   then configure eznet to use version 2.2 of pppd as follows:
   
   eznet change vnet pppversion=2.2

   It is possible to recompile eznet to assume version 2.2 of pppd (by
   changing the DFLT_PPP_VERSION #define in the source code) but then it
   will not work right for versions 2.3 of pppd and later.
   
   Eznet does a good job of negotiating the login name and password of
   most ISPs on its on, but with some ISPs it may require some additional
   hints. If eznet fails to connect, you can view a transcript of the
   connection attempt in the file "/var/eznet/transcript.*". This
   transcript should help you figure out what is going wrong. Beginning
   with version 1.8 of eznet, this transcript is a part of the "eznet
   log" output.
   
   With some service providers it may be necessary to respond to unusual
   login prompts. Eznet allows you to code around this problem using
   "expectN" and "replyN" options. For example, suppose VNet prompted for
   the login name like this:
   
  Enter your name:

   instead of the more traditional "login:". By default, eznet would not
   recognize this string and would not know how to respond. But you can
   tell it would to do like this:
   
   eznet change vnet expect0=name: reply0=drh

   The "expect0" and "reply0" option pair tells eznet that it should
   respond with the value of reply0 whenever it sees the text contained
   in expect0. You can have up to 10 such expect and reply pairs numbered
   0 through 9. The order is not important.
   
   Two particular values of the replyN string have a special meaning. A
   reply of "FAIL" will cause eznet to immediately abandon the connection
   attempt when the corresponding expectN string is encountered.
   Similarly, a reply of "ACCEPT" will cause eznet to immediately attempt
   to start pppd.
   
     If you have to use expectN and replyN parameters to get eznet to
     work, please send e-mail to drh@acm.org. Include the output of
     "eznet log" if possible. Hopefully, the information you provide
     will enable me to upgrade eznet to work on your ISP without the
     need for additional expectN and replyN configuration options. 
     
A Summary of Eznet Configuration Options

   The following is a summary of all of the configuration options that
   eznet currently understands:
   
   service This is a symbolic name for the ISP to which you will connect.
   This name is used in the output of the status command. If you
   configure eznet with data for two or more ISPs, then you can use this
   name to select a particular ISP to dial.
   phone This is the phone number used to dial the modem. The text
   associated with this value will be appended to the string ``atd'' and
   sent to the modem verbatim.
   user The username or login for your ISP account. If your ISP is
   running some kind of Unix, then this is the name that is entered at
   the ``Login:'' prompt. If your ISP runs NT, then this is the name used
   for PAP or CHAP authentification.
   password This is the password for your ISP account. The password will
   be entered at the ``Password:'' prompt during login or used during PAP
   or CHAP authentification or both.
   baud The is the baud rate of the serial connection between your
   computer and the modem. The default value is 115200. If your modem or
   serial port can't handle that speed, you might need to lower this
   number.
   debug If the value of the option is ``y'' or ``yes'' then pppd will be
   launched in debugging mode. This will cause lots of extra information
   to be sent to syslog, and can sometime be helpful in fixing problems.
   tty By default, eznet attempt to setup the PPP connection on the
   device named /dev/modem. You can choose a different serial device with
   this option.
   defaultroute Eznet normally sets up a default route to connections it
   arranges. You can disable this feature by setting the defaultroute
   attribute to ``no'' or ``n''.
   idle This parameter records the number of seconds of inactivity that
   will cause the PPP connection to shutdown. The default value is 300 (5
   minutes). If you set it to 0, then no idle timeout will occur.
   chat The default value of this option is "yes". If you set it to "no",
   then eznet will try to set up a PPP link as soon as the modem
   connects, ignoring any "login:" or similar prompts from the remote
   machine.
   pppversion Specifies what version of pppd is being used. (There are
   changes in the command line syntax between 2.2 and 2.3 that eznet has
   to deal with.) The default value is determined by the #define
   "DFLT_PPP_VERSION" in the source code. If eznet causes pppd to fail
   with a long error message the first time you try to use it, you may
   want to set this value manually.
   pppoptN You can pass up to 10 additional options to pppd by setting
   the values of parameters pppopt0, pppopt1, ..., pppopt9 as
   appropriate. The values specified are placed at the end of the
   parameter list.
   ip & netmask These parameters can be used to select from two or more
   ISPs in eznet's database. If you type:
   
   eznet up 192.18.135.11

   for example, eznet will search through its list of ISPs looking for
   one whose ``ip'' value matches 192.18.135.11 for every bit that is set
   in the ``netmask''. It will then dial the first match it finds. This
   feature is useful when eznet is started from /sbin/request-route and
   you need to dial different ISPs depending on the IP number of the
   machine you want to talk to.
   initN You can have up to 10 extra modem initialization strings named
   init0, init1, ...,init9. A common option is:
   
    eznet change 0 init0=atz

   expectN & replyN For more complex login procedures, you may need to
   specify additional prompt strings and replies. You can enter up to 10
   additional prompt strings that will respond with the corresponding
   reply. For example:
   
    eznet change 0 expect0=SERVICE: reply0=PPP

   A carriage return is automatically added to every reply string.
   pppd If your pppd is installed someplace other than /usr/sbin/pppd
   then you can enter an alternative name here.
   diald If you have diald installed as something other than
   /usr/sbin/diald then enter its name using this parameter.
   local This option can be used to specify the local IP number required
   by diald. The default value is 127.0.0.100.
   remote This option can be used to specify the remote IP number
   required by diald. The default value is 127.0.0.101.
   mtu This option sets the maximum length of a transmitted packet over
   the PPP link. Longer packets are fragmented. The default is 552.
   mru This option sets the maximum length of a received packet over the
   PPP link. Longer packets are fragmented. The default is 552.
   autostart If this option is "yes" (its default) then PPP will
   automatically begin its protocol negotiation with the other end if
   there is no response over the serial link for 8 seconds. If this
   option is "no", then PPP will not start on the local end until PPP
   packets are seen coming from the remote end.
   routeN Where N is any digit between 0 and 9, this option causes a
   route to be setup to the specified network after PPPD connects.
   dialtimeout This is the number of seconds that eznet will wait for a
   CONNECT message from the modem after issuing the dialing commands. The
   default is now 60 seconds.
   chattimeout This is the number of seconds that eznet will wait for
   replies to commands while doing the login chat sequence. The default
   is 3 seconds.
   
Installation

   The eznet program is completely contained in a single C source file
   named ``eznet.c''. You can compile and install it as follows:
   
   gcc -o /usr/bin/eznet -O eznet.c
   chown root.root /usr/bin/eznet
   chmod 04755 /usr/bin/eznet

   The eznet executable must reside in the directory /usr/bin. It won't
   work if you install it someplace else. If you need to install eznet
   elsewhere, change the value of the SELF #define near the top of the
   source file and recompile.
   
   Eznet does not need to be suid. But if it isn't, then only root will
   be able to initiate a PPP connection. Eznet takes care to drop its
   root privileges as soon as possible so it should be fairly safe as a
   suid program. It is certainly easier to use that way.
   
Copyright And License

   Eznet 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.
   
   Eznet 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.
   
Author Contact Information

   Eznet is written and maintained by D. Richard Hipp. If you have
   questions or comments, send him e-mail at drh@acm.org. The eznet home
   page is http://www.hwaci.com/sw/eznet/.
Eznet-1.11.1/eznet-1.11.1.lsm100644      0      0        1676  6706436236  13344 0ustar  rootrootBegin3
Title:           eznet 
Version:         1.11.1         
Entered-date:    25FEB99 
Description:     EZNET is a easy-to-use, user-friendly way to set up your 
                 PPP daemon. Get it if you're having trouble creating a chat
		 script to connect to your service provider with pppd & chat.
		 Source and compiled libc6 included.
		 Added frontend 'ezsetup' script and made a few changes 
		 to eznet.c.
		 The changes to eznet.c 1.11 are listed in eznet.txt which
		 is included in this package.
		 This is eznet.c 1.11 with a few changes by me kent robotti.
Keywords:        ppp pppd setup isp internet dialup   
Author:          krobot@erols.com (Kent Robotti) 
Maintained-by:   
Primary-site:    http://www.hwaci.com/sw/eznet <Home site of original eznet>
                 http://www.hwaci.com/drh
Alternate-site:  ftp://sunsite.unc.edu/pub/Linux/system/network/serial/ppp
Platforms:       Unix system with pppd. 
Copying-policy:  GPL
End
Eznet-1.11.1/Makefile100664      0      0         765  6706436400  12376 0ustar  rootroot#
# woz's quick'n'dirty makefile... (hack at will!)
#

#
# install dir
#
INSTALL_BIN = /usr/bin

#
# program
#
EXEC = eznet
SRC = eznet.c

#
# options/commands
#
CC = gcc
CC_OPTS = -O2
CP = cp -f
CHOWN = chown
CHMOD = chmod
RM = rm -f

#
# make targets
#
all: build

build:
	$(CC) $(CC_OPTS) -o $(EXEC) $(SRC)

install:
	$(CP) $(EXEC) $(INSTALL_BIN)
	$(CHOWN) root.root $(INSTALL_BIN)/$(EXEC)
	$(CHMOD) 04755 $(INSTALL_BIN)/$(EXEC)

uninstall:
	$(RM) $(INSTALL_BIN)/$(EXEC)

clean:
	$(RM) $(EXEC) *.o
Results 1 - 1
Help - FTP Sites List - Software Dir.
Searching half a billion files worldwide
© 1997-2009 MARUHN Internet Solutions