/* EMACS_MODES: !lnumb !fill */

/* to do */
/* send feedback when file not found */

#include <stdio.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>
#include <dos.h>
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
#include <dir.h>

#define NAME "fremote"
#define VERSION "V2.2"
#define ANNOY "UNREGISTERED!"
#define ANNOYKEY "mibPEmgWPbPw7B0jok0jok"

#define AUTHOR "Andrew Gaunt"
#define AUTHORKEY "vvugec.ExvvM00ok00ok"

#define PAUSEWARN 20L /* Warn user if paused more than PAUSEWARN secs */

#define CONFIGFLE "fmenu.cfg" /* Config file for this program, list of Nodes */
#define MAXDIRS  64  /* maximum number of file areas */

#define MAXLINE 128

#define OK    0
#define ERROR   1

#define BREAKON 0
#define NOBREAK 1

#define MAXELM  256          /* Maximum number of elements for temp. array */
#define MAXNODE 128          /* Maximum number of nodes */
#define MAXNAME 81           /* Max number of chars for a BBS's name */
#define NODEFLAG "[NODES]"
#define DIRFLAG  "[DIRS]"
#define KEYFLAG  "[KEYS]"
#define ENDLIST  "[END]"
#define INT       6    /* number of chars needed to represent and integer */
#define MAXREGNO MAXNAME+INT+INT+INT

#define nl() printf("\n");

/* store file area paths in dirlist */
char dirlist[MAXDIRS][MAXPATH];
/* int secure; */
char array[MAXELM][MAXLINE];   /* temp array */
int nodelist[MAXNODE];     /* store nodes here */

char work_dir[MAXPATH];
char temp_dir[MAXPATH];
char this_node[INT];
char bbs_name[MAXNAME];         /* BBS's name */
char reg_key[MAXREGNO];         /* reg. key - calc. from name and node */
char file_minor[INT];
char remote_minor[INT];
static char author[]=AUTHOR;
static char annoy[]=ANNOY;

/* Name of WWIV chain.txt file */
char chainfile[MAXPATH];
char configfile[MAXPATH];

/* From skeleton.c */
#define v407


int    usernum,             /* user number for the user */
       age,                 /* age of the user */
       screenchars,         /* chars/line user has specified */
       screenlines,         /* lines/screen user has specified */
       sl,                  /* sec lev for user (0-255) */
       so,                  /* non-zero if user is sysop (sl=255) */
       cs,                  /* non-zero if user is co-sysop */
       okansi,              /* non-zero if user can support ANSI */
       incom,               /* non-zero if user is calling remotely */
       comport;             /* com port user is on */
char   name[81],            /* name/alias of user */
       realname[81],        /* real name of user */
       callsign[10],        /* amateur radio callsign of user */
       sex,                 /* sex of user, M or F */
       laston[81],          /* date user was last on */
       gfiles[81],          /* directory for text files, ends in \ */
       data[81],            /* directory for non-text files, ends in \ */
       sysoplog[81],        /* full path & filename for sysop log */
       curspeed[81];        /* speed user is on at, "KB" if local */
double gold,                /* gold user has */
       timeallowed,         /* number of seconds before user logged off */
       version;             /* version of WWIV running under */


#ifdef v407

/* function pointers for BBS functions.  ONLY used for 4.07 or later. */

void far interrupt (*inli)(char *, char *, int, int);
void far interrupt (*checka)(int *, int *);
void far interrupt (*pla)(char *, int *);
void far interrupt (*outchr)(char);
void far interrupt (*outstr)(char *);
void far interrupt (*nl)();
void far interrupt (*pl)(char *);
int  far interrupt (*empty)();
char far interrupt (*inkey)();
unsigned char far interrupt (*getkey)();
void far interrupt (*input)(char *, int);
void far interrupt (*inputl)(char *, int);
int  far interrupt (*yn)();
int  far interrupt (*ny)();
void far interrupt (*ansic)(int);
char far interrupt (*onek)(char *);
void far interrupt (*prt)(int, char *);
void far interrupt (*mpl)(int);

void far **funcs;

#endif

int read_in_data(char *fn)
{
  char buf[1024];
  char *ptr[50],*ss,s[81];
  int i,f,len,i1;
  float fl;

  f=open(fn,O_RDONLY | O_BINARY);
  if (f<0) {
    return(-1);
  }
  i1=1;
  ptr[0]=buf;
  len=read(f,(void *)buf,1024);
  close(f);
  for (i=0; i<len; i++)
    if (buf[i]==13) {
      buf[i]=0;
      ptr[i1++]=&buf[i+2];
    }
  while (*ptr[6]==32)
    ++(ptr[6]);
  while (*ptr[15]==32)
    ++(ptr[15]);
  usernum=atoi(ptr[0]);
  strcpy(name,ptr[1]);
  strcpy(realname,ptr[2]);
  strcpy(callsign,ptr[3]);
  age=atoi(ptr[4]);
  sex=*ptr[5];
  sscanf(ptr[6],"%f",&fl);
  gold=(double)fl;
  strcpy(laston,ptr[7]);
  screenchars=atoi(ptr[8]);
  screenlines=atoi(ptr[9]);
  sl=atoi(ptr[10]);
  so=atoi(ptr[11]);
  cs=atoi(ptr[12]);
  okansi=atoi(ptr[13]);
  incom=atoi(ptr[14]);
  sscanf(ptr[15],"%f",&fl);
  timeallowed=(double)fl;
  strcpy(gfiles,ptr[16]);
  strcpy(data,ptr[17]);
  strcpy(sysoplog,gfiles);
  strcat(sysoplog,ptr[18]);
  strcpy(curspeed,ptr[19]);
  comport=atoi(ptr[20]);

  version=-1.0;

  funcs=(void far *)getvect(0x6a);
  inli=funcs[0];
  checka=funcs[1];
  pla=funcs[2];
  outchr=funcs[3];
  outstr=funcs[4];
  nl=funcs[5];
  pl=funcs[8];
  empty=funcs[9];
  inkey=funcs[10];
  getkey=funcs[11];
  input=funcs[12];
  inputl=funcs[13];
  yn=funcs[14];
  ny=funcs[15];
  ansic=funcs[16];
  onek=funcs[17];
  prt=funcs[18];
  mpl=funcs[19];
  return(0);
}

/* My stuff */

struct time tm;


void main(int argc, char *argv[])
{
  char s[MAXLINE];
  char outstr[128];
  FILE *fi;
  int i;
  int num_read,nodes;

  colour(3);
  printf("Compile date and time %s %s",__DATE__,__TIME__);
  colour(0);
  nl();

  if (argc<2){
     printf("usage: fmenu chain_file [config_file]\n");
     exit(ERROR);
  }
  else {
     strcpy(chainfile,argv[1]);
     strcpy(configfile,CONFIGFLE);
  }
  if (argc==3){
     strcpy(configfile,argv[2]);
  }

  if (read_in_data(chainfile)==-1) {
    fprintf(stderr,"%s: %s not found\n",NAME,chainfile);
    exit(ERROR);
  }

  nl();
  colour(4);
  printf(" %s %s By Quantum (c) Copyright 1991,1992 %s. ",NAME,VERSION,author);
  colour(0);
  nl();
  nl();

  /* Open configuration file, current directory */
  if ( (fi=fopen(configfile,"r"))==NULL){
     colour(6);
     printf("Can't read config file %s.\n",configfile);
     exit(ERROR);
     colour(0);
  }

  /* Read config file into temp array */
  if ( (num_read=readin_array(fi,MAXELM)) < 11){
    colour(6);
    printf("Config file %s too small, %d items read.",configfile,num_read);
    colour(0);
    nl();
  }
  fclose(fi);

  /* print_array(); /* DEBUG */
  strcpy(work_dir,array[0]);
  strcpy(temp_dir,array[1]);
  strcpy(this_node,array[2]);
  strcpy(bbs_name,array[3]);
  strcpy(reg_key,array[4]);
  strcpy(file_minor,array[5]);
  strcpy(remote_minor,array[6]);
/*   secure=atoi(array[5]); */
  nodes=make_nodelist();
  make_dirlist();

  if(check_regkey(annoy,"",ANNOYKEY)==ERROR){ 
    colour(6);
    printf("ERROR: Invalid executable!");
    colour(0);
    nl();
    exit(ERROR);
  }


  if(check_regkey(author,"",AUTHORKEY)==ERROR){
    colour(6);
    printf("ERROR: Invalid executable!");
    colour(0);
    nl();
    exit(ERROR);
  }

  colour(3);
  if(strcmp(reg_key,"-")!=NULL){
    if(check_regkey(bbs_name,this_node,reg_key)!=OK){
      colour(6);
      printf("Check BBS name, BBS node and registration key in fmenu.cfg.");
      sprintf(s,"Invalid Registration Key\n");
      logtosysop(s);        
      colour(0);
      nl();
      exit(ERROR);
    }
    else{
      printf("Registered to: \"%s\", @%s",bbs_name,this_node);
    }
  }
  else{
    printf("%s: \"%s\", @%s",ANNOY,bbs_name,this_node);
  }
  colour(0);
  nl();
  nl();

/*  print_nodelist(); /* DEBUG */


  /* Check for remote request data files from other nodes */ 
  sprintf(s,"f_recv %s %sexternal.net %sexternal.net %s",remote_minor,data,temp_dir,work_dir);
  system(s);

  for(i=0;i<nodes;i++){
    sprintf(s,"%s%d.req",work_dir,nodelist[i]);
    printf("Node %d, ",nodelist[i]);
    if (readable(s)!=0){
      printf("none\n");
      sprintf(s,"No requests from %d.\n",nodelist[i]);
      logtosysop(s);
      continue;  
    }
    printf("processing\n");


    /* Open request file in work area */
    sprintf(s,"%s%d.req",work_dir,nodelist[i]);
    if ( (fi=fopen(s,"r"))==NULL){
       colour(6);
       printf("Can't read %s\n",s);
       exit(ERROR);
       colour(0);
    }

    /* Use the temp array to store data from remote system's request file */
    if ( (num_read=readin_array(fi,MAXELM)) < 1){
      colour(6);
      printf("Nothing to process @%d, %d items read.",nodelist[i],num_read);
      colour(0);
      nl();
    }
    /* Send the requested files to the remote system */
    remote(num_read);

    /* Remove remote request data file */
    sprintf(s,"%s%d.req",work_dir,nodelist[i]);
    unlink(s);
  }
  colour(4);
  printf(" [%s:%s] Complete ",NAME,VERSION);
  colour(0);
  nl();
  exit(OK);
}

remote(num_read)
int num_read;
{
  char outstr[80];
  char node[10];
  char filename[MAXFILE+MAXEXT];
  char ext[MAXEXT];
  char batfile[MAXPATH];
  char s[MAXPATH];
  int i,j;
  int found;
  FILE *f;

  colour(3);
  printf("Searching directories");
  colour(0);
  nl();
  for(i=0;i<num_read;i+=2){
     strcpy(node,array[i]);
     if(check_filename(array[i+1])==ERROR){
       sprintf(s,"Bad filename \"%s\" from @%s.\n",array[i+1],node);
       logtosysop(s);
       continue;
     }
     strcpy(filename,array[i+1]);

     if ( check_node(atoi(node))==ERROR){
        colour(6);
        printf("Node @%s not in list.",node);
        colour(0);
        nl();
        sprintf(s,"Invalid node: \"%s\" to @%s.\n",filename,node);
        logtosysop(s);
        continue;
     }

     found=0;
     for(j=0;j<=MAXDIRS;j++){
       if(dirlist[j][0]=='\0' || mykbhit()){
          break;
       }
       sprintf(s,"%s%s",dirlist[j],filename);
       if(readable(s)==0){
          found=1;
          printf("Found %s%s\n",dirlist[j],filename);
          sprintf(s,"f_send %s %s %s %s%s %s 1",node,this_node,file_minor,dirlist[j],filename,data);
          system(s);
          sprintf(s,"Sent \"%s\" to @%s.\n",filename,node);
          logtosysop(s);
          continue;
       }
    }
    if(found==0){
      colour(6);
      printf("File \"%s\" not found.",filename);
      colour(0);
      nl();
      sprintf(s,"File \"%s\" not found for @%s\n",filename,node);
      logtosysop(s);
    }
  }
  return(0);
}


/* if new is [0-7] then change colour */
colour(code)
short code;
{
  if(!okansi){
    return(0);
  }
  if (code<=7 && code>=0 ){
    printf("%c%d",'\003',code);

  }
  return(OK);
}

int readable(f)
char f[];
{
  FILE *fp;
  if ( (fp=fopen(f,"r")) != NULL){
    fclose(fp);
    return(0);
  }
  fclose(fp);
  return(1);
}

int fgetline(s,max,fp)
char s[];
int max;
FILE *fp;
{
  int i,c;
  i=0;
  while ( (c=fgetc(fp)) != EOF){
    if (c=='\n' || i>=max || c=='\t' ){
      break;
    }
    /* Comments begin with # and end with newline */
    if (c=='#'){
      while( (c=fgetc(fp))!='\n' ){
      }
      continue;
    }
    s[i]=c;
    i++;
  }
  s[i]='\0';
  return(i);
}

logtosysop(s)
char s[];
{
  FILE *fo;
  char f[MAXPATH];

  /* Don't log if Sysop and not-remote */
  if (incom==0 && sl==255){
    return(0);
  }

  sprintf(f,"%s",sysoplog);
  if ( (fo=fopen(f,"a")) != NULL){
    fprintf(fo,"   %s:%s",NAME,s);
  }
  else{
    colour(6);
    printf("Can't append to \"%s%s\"\n",gfiles,sysoplog);
    colour(0);
  }
  fclose(fo);
  return(OK);
}

int init_array()
{
	int i;
	i=0;
	for (i=1;i<MAXELM;i++){
		strcpy(array[i],"");
		i++;
	}
	return(OK);
}



/* Read from fi up to max number of items into array */
/* and return number of items read */
int readin_array(fi,max)
FILE *fi;
int max;
{
	int i;
	char s[MAXLINE];


	init_array();
	i=0;
	while ( i<max ){
		if ( fgetline(array[i],MAXLINE,fi) == NULL){
			break;
		}

/*   		printf("\t%d:[%s]\n",i,array[i]); /* FOR DEBUG */

 		if ( (strcmp(array[i],"")) == NULL){
			break;
		}

		i++;
		if ( i >= MAXELM ){
			colour(6);
			printf(s,"Config file too large, array full: %d\n",MAXELM);
			colour(0);
			nl();
			return(i);
		}
	}
	return(i);
}

int make_dirlist()
{
	char s[MAXLINE];
	int i,j,f;

	f=0,j=0;
	for(i=0;i<MAXELM;i++){
		if (strcmp(array[i],DIRFLAG) == NULL){
			f=1;
			continue;
		}
		if (strcmp(array[i],KEYFLAG) == NULL){
			strcpy(dirlist[j],"");
			break;
		}
		if(f==1){
			if(j>=MAXDIRS){
				colour(6);
				printf("Too many directories: %d.\n",j);
				colour(0);
				strcpy(dirlist[j],"");
				return(-1);
			}
			strcpy(dirlist[j],array[i]);
/*  			printf("dir %d %s\n",j,dirlist[j]); /* DEBUG */
			j++;
		}
	}
	return(j);
}

int make_nodelist()
{
	char s[MAXLINE];
	int i,j,f;

	f=0,j=0;
	for(i=0;i<MAXELM;i++){
		if (strcmp(array[i],NODEFLAG) == NULL){
			f=1;
			continue;
		}
		if (strcmp(array[i],DIRFLAG) == NULL){
			nodelist[j]=0;
			break;
		}
		if(f==1){
			if(j>=MAXNODE){
				colour(6);
				printf("Too many nodes: %d.\n",j);
				colour(0);
				nodelist[j]=0;
				return(-1);
			}
			nodelist[j]=atoi(array[i]);
/* 			printf("node %d %d\n",j,nodelist[j]); /* DEBUG */
			j++;
		}
	}
	return(j);
}

check_node(n)
int n;
{
	int i;
	for(i=0;nodelist[i]>0;i++){
		if(n==nodelist[i]){
			return(OK);
		}
	}
	return(ERROR);
}

print_nodelist()
{
  int i;
  colour(3);
  printf("<< Nodelist >>");
  colour(1);
  nl();
  for (i=0;i<MAXELM;i++){
     if(nodelist[i]==0 ||mykbhit()){
         break;
     }
     printf("%03d. @%-04d\n",i,nodelist[i]);
  }
  colour(3);
  printf("<< END >>");
  colour(0);
  nl();
  return(OK);
}

print_array()
{
  int i;
  nl();
  for (i=0;i<MAXELM;i++){
     if(mykbhit()){
         break;
     }
     printf("%03d. %s\n",i,array[i]);
  }
  nl();
  return(OK);
}

mykbhit()
{
  long i,j;
  int c;
  static short pause;

  if(kbhit()){
    c=getch();
    switch (tolower(c)) {
      case 'p':
        if (pause==0){
          pause=1;
          time(&i);
          while (!kbhit()){
            time(&j);
            /* Warn user if paused more than 10 seconds */
            if (j>i+PAUSEWARN){
              printf("\007[PAUSED]\b\b\b\b\b\b\b\b");
              time(&i);
            }
          }
        }
        else{
          pause=0;
        }
        return(0);

      case '\n':
      case ' ':
        if (pause==0)
          return(1);
      default:
        return(1);
    }
  }
  return(OK);
}

check_filename(f)
char f[];
{
	int i;
	int bad;

	bad=0;
	if(strncmp(f,"LPT",3)==NULL)
		bad=1;
	if(strncmp(f,"COM",3)==NULL)
		bad=1;
	if(strncmp(f,"PRN",3)==NULL)
		bad=1;
	if(strncmp(f,"AUX",3)==NULL)
		bad=1;
	if(strncmp(f,"CON",3)==NULL)
		bad=1;
	if(bad!=0){
		colour(6);
		printf("Bad filename\t");
		colour(0);
		return(ERROR);
	}

	bad=0;
	for(i=0;i<strlen(f);i++){
		switch(f[i]) {
			case '.':
				bad++;
				break;
			case ':':
			case '\\':
			case '/':				
				bad=2;
		}
		
	}
/*
 	if (i>(MAXFILE)){
		colour(6);
		printf("Filename too long\t");
		colour(0);
		return(ERROR);
	}
*/
	if(bad>1){
		colour(6);
		printf("Bad Filename\n");
		colour(0);
		return(ERROR);
	}
	return(OK);
}

check_regkey(name,node,inum)
char name[];
char node[];
char inum[]; /* Input key - to be checked */
{
  char s[81];
  char cnum[MAXREGNO]; /* calculate key - from BBS name & node */
  int chk; /* check sum */
  int i,j,k;

  static char tr[2][255] ={
    {
      'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
      'p','q','r','s','t','u','v','w','x','y','z',
      'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
      'P','Q','R','S','T','U','V','W','X','Y','Z',
      '!','@','#','$','%','^','&','*','(',')','-','+','-','=',
      '{','}','[',']',':','"',';','\'','<','>','?',',','.','/','\\',
      '~','`','|','0','1','2','3','4','5','6','7','8','9','\007'
    },
    {
      'x','k','h','u','e','c','4','5','v','0','q','l','L','v','R',
      'q','f','g','d','M','v','7','c','3','c','k',
      'v','F','L','w','P','e','E','N','m','z','u','B','H','i','F',
      'J','j','b','g','W','m','o','8','5','B','3',
      '7','n','4','T','U','G','J','K','e','U','K','j','k','f',
      'k','y','h','n','y','6','j','j','6','r','9','h','v','7','h',
      'S','7','s','i','0','j','g','l','o','t','m','k','1','B'
    },
  };

  strcpy(cnum,"");
  chk=0;
  k=0;

  /* encode the name and calulate checksum */
  for(i=0;name[i]!='\0';i++,k++){
    cnum[k]='.';
    for(j=0;j<255;j++){
      if(name[i]==tr[0][j]){
        cnum[k]=tr[1][j];
        chk+=cnum[k];
      }
    }
  }


  sprintf(s,"%d",chk);
/*  printf("Check=%s\n",s); /* DEBUG */
  /* add encoded version checksum so far */
  for(i=0;s[i]!='\0';i++,k++){
    cnum[k]='.';
    for(j=0;j<255;j++){
      if(s[i]==tr[0][j]){
        cnum[k]=tr[1][j];
      }
    }
  }

  /* encode node number and keep adding to check sum */
  for(i=0;node[i]!='\0';i++,k++){
    cnum[k]='.';
    for(j=0;j<255;j++){
      if(node[i]==tr[0][j]){
        cnum[k]=tr[1][j];
        chk+=cnum[k];
      }
    }
  }

  /* add in encoded final value of checksum */

  sprintf(s,"%d",chk);
/*   printf("Check=%s\n",s); /* DEBUG */
  for(i=0;s[i]!='\0';i++,k++){
    cnum[k]='.';
/*     printf("%c",s[i]); /* DEBUG */
    for(j=0;j<255;j++){
      if(s[i]==tr[0][j]){
/*        printf("s[%d]=%c\ttr[1][%d]=%c\n",i,s[i],tr[1][j],j); /* DEBUG */
        cnum[k]=tr[1][j];
/*        printf("cnum[%d]=%c\n",k,cnum[k]); /* DEBUG */
      }
    }
  }
  cnum[k]='\0';

/*  printf("\ncnum=\t\"%s\"\ninum=\t\"%s\"\n",cnum,inum); /* DEBUG */
  if (strcmp(cnum,inum)){
    return(ERROR);
  }
  else{
    return(OK);
  }
}
