wsh/
wsh/binsrc/
wsh/docs/help/
wsh/docs/old/
wsh/etc/
wsh/src/util/
/* See the file wizshell.c, wizshell.h or the files in docs/ for information */

#include "wizshell.h"

/* tokenlen
 * returns length of string to '/' or '\0'
 */
int tokenlen(token)
  char *token;
{
  int i=0;    /*   v--  is this needed? */
  while(token[i]!='/' && token[i]!='\0') i++;
  return i;
}

/* match_token
 * see if tok1 matches the tok2 from access tree
 */
int match_token(tok1,tok2)
  char *tok1,*tok2;
{
  if(!strcmp(tok1,tok2)) return 1;
  if(!strcmp(tok2,"*")) return 1;  /* add pattern matching later?? */
  return 0;
}

/* find_token
 * finds token in down level from acc or returns NULL
 */
acc_t *find_token(acc,token)
  acc_t *acc;
  char *token;
{
  acc=acc->down;
  while((acc!=NULL) && (!match_token(token,acc->token)))
    acc=acc->next;
  return acc;
}

/* add_token
 * finds or adds a token in down level from acc and returns it
 * replacements: $<var>->environment variable
 * $JUBAL.o will replace the variable $JUBAL, not $JUBAL.o <- not yet.. :)
 * (i.e. if $JUBAL=moo, then $JUBAL.o becomes moo.o)
 */
acc_t *add_token(acc,token)
  acc_t *acc;
  char *token;
{
  int len;
  acc_t *tmp;
  char *str;

/*  fprintf(stderr,"Adding token: %s\n",token); /* */
  tmp=find_token(acc,token);
/*  if(tmp) fprintf(stderr," -- found it.\n"); */
  if(tmp) return tmp;
  /* Deal with environment variables */
  if(token[0]=='$' && (str=getenv(token+1))) token=str;
  MK_ACC(tmp);
  tmp->next=acc->down;
  acc->down=tmp;
  tmp->down=NULL;
  len=strlen(token);
  MK_STR(tmp->token,len)
  strncpy(tmp->token,token,len+1);
/*  fprintf(stderr," -- added: %s\n",tmp->token); /* */
  return tmp;
}

/*  Prints out access tree (for debugging) (maybe as a catch later?)
 */
void print_acc(acc,spc)
  acc_t *acc;
  int spc;
{
  acc_t *tmp,*dis;
  int i;

  if(!spc) {
    if(acc==write_acc || acc==read_acc) {
      if(acc==write_acc) fprintf(stderr,"Write Access Tree:\n");
      else fprintf(stderr,"Read Access Tree:\n");
      print_acc(acc->down,2);
      if(acc->next->down) {
        fprintf(stderr,"excluding:\n");
        print_acc(acc->next->down,2);
      }
      return;
    }
    fprintf(stderr,"Access Tree\n");
  }
  tmp=acc;
  while(tmp) {
    for(i=0;i<spc;i++) fprintf(stderr," ");
    fprintf(stderr,"/%s\n",tmp->token);
    if(tmp->down) print_acc(tmp->down,spc+strlen(tmp->token)+1);
    tmp=tmp->next;
  }
}

/* Frees all tokens down and next of acc */
/* Recursive because we have a tree to traverse */
void free_acc(acc)
  acc_t *acc;
{
  if(!acc) return;
  if(acc->down) free_acc(acc->down);
  if(acc->next) free_acc(acc->next);
  free(acc);
}

/* adds path in down level of acc */
/* returns the last token added (useful for $var expansion) */
acc_t *add_path(acc,path)
  acc_t *acc;
  char *path;
{
  char buf[MAX_BUF];
  char *tmp;
  int i=0;
  
  if(path[0]=='/') path++;
  while(*path!='\0') {
    i=0;
    while(*path!='\0' && *path!='/') buf[i++]=*(path++);
    buf[i]='\0';
    if(*path=='/') path++;
    acc=add_token(acc,buf);
  }
  /* Sometimes (because of groups) we might add /path1/path2/path3 and then
   * /path1 - if we do that we want to get rid of the rest of the path
   * that's there, otherwise we only get access to /path1/path2/path3
   */
  if(acc->down) {
    free_acc(acc->down);
    acc->down=NULL;
  }
  return acc;
}

/* get next full path in access file, return 0 if another
 * access type entry is found.  If path is of the form $<str> then
 * the value of the environment variable <str> will be added
 * If the path is of the form +<str> then the access type <str> is added.
 */
int get_next_path(acc,fp,fname)
  acc_t *acc;
  FILE *fp;
  char *fname;
{
  char buf[MAX_BUF+1],*str;
  acc_t *tmp=acc;
  int i=0,nottoken=1,c;

  while(nottoken) {
    while((c=getc(fp))==' ' || c=='\t' || c=='\n');  /* eat white space */
    if(c=='#') while((c=getc(fp))!='\n' && c!=EOF);  /* eat comments */
    else nottoken=0;
  }

  while(c!=' ' && c!='\t' && c!='\n' && c!=EOF) {
    buf[i++]=c;
    c=getc(fp);
  }
  buf[i]='\0';
  if(i==0) return 0;
  if(buf[i-1]==':') return 0;
/* we shouldn't need this, but I couldn't get it to work properly in add_path */
  if(buf[0]=='$' && (str=getenv(buf+1))) add_path(acc,str); else
/* figure this out later..  :( */

  if(buf[0]=='+') get_acc(&acc,fname,buf+1);
  else if(buf[0]=='-') add_path(acc->next,buf+1);
  else add_path(acc,buf);

  if(c==EOF) return 0;
  return 1;
}


/* get_acc
 * reads access file <fname> and loads access of type <type> into <acc>
 * Creates two handles of type acc_t, the main handle, and the one ->next
 * from it, which are the disallowed directories.
 */
void get_acc(acch,fname,type)
  acc_t **acch;
  char *fname;
  char *type;
{
  FILE *fp;
  acc_t *acc;

  if(!(fp=fopen(fname,"r"))) {
    fprintf(stdout,"Could not open access file: %s",fname);
    exit(1);
  }
  rewind(fp);
  if (!find_entry(type,fp)) { /* use find_entry from login.c */
    fprintf(stderr,"Could not find access type: %s\n",type);
    fprintf(stderr,"In access file: %s",fname);
    exit(1);
  }

  if(!*acch) {
    MK_ACC(acc)
    MK_ACC((acc->next))  /* for disallowed directories */
    (*acch)=acc;
  }
  while(get_next_path(*acch,fp,fname));
/*  print_acc((*acch),0); */

  fclose(fp);
  return;
}

/* mk_path
 * things that are not implemented (yet):
 * str="."
 * wildcards in string?   shouldn't be a problem...
 * frees string and returns new one
 */
char *mk_path(str)
  char *str;
{
  char *arg;
  char *hmdir,*user,*tmp,c;

/*  to turn off mk_path for debugging (don't use!):
  MK_STR(arg,strlen(str))
  str_add(arg,str);
  return gen_abs_path(arg);
*/

  switch (str[0]) {
    case '-':
      return str;
      break;
    case '/':
      MK_STR(arg,ROOT_DIR_LEN+strlen(str))
      str_add(arg,ROOT_DIR);
      str_add(arg,str);
      break;
    case '~':
      if (str[1]=='/' || str[1]=='\0') {
        MK_STR(arg,ROOT_DIR_LEN+strlen(envir.home)+strlen(str+1))
        str_add(arg,ROOT_DIR);  str_add(arg,envir.home);
        str_add(arg,str+1);
      } else {
        user=str+1;
        tmp=user;
        while(*tmp!='/' && *tmp!='\0') tmp++;
        c=*tmp;
        *tmp='\0';   /* temporarily null terminate the user name */
        hmdir=get_home_dir(user);
        if(!hmdir) {
          fprintf(stderr,"Unknown user: %s.\n",user);
          *tmp=c;
          return 0;
        }
        *tmp=c;

        MK_STR(arg,ROOT_DIR_LEN+strlen(hmdir)+strlen(tmp)+5)
        str_add(arg,ROOT_DIR);  str_add(arg,"/");
        str_add(arg,hmdir);  str_add(arg,tmp);
/* there was a seg fault here..  should be okay now */
        FREE(hmdir);
      }
      break;
    default:
      MK_STR(arg,ROOT_DIR_LEN+strlen(WD)+1+strlen(str))
      str_add(arg,ROOT_DIR);
      if(*(WD+1)!='\0') str_add(arg,WD); /* not if WD->"/\0" */
      str_add(arg,"/");       str_add(arg,str);
      break;
  }
  FREE(str);
  arg=gen_abs_path(arg);
if(DEBUGON) if(arg) fprintf(stderr,"ABS_FILE: %s\n",arg);
  return arg;
}

/* gen_abs_path
 * take care of /../ and /./ and // and last / on string
 * States:
 * state=0 not in possible "/../" or "/./"
 * state=1 encountered "/"   (rewind back 1 if next char='/' or '\0')
 * state=2 encountered "/."  (rewind back 2 if next char='/' or '\0')
 * state=3 encountered "/.." (rewind back a pathstep if next='/' or '\0')
 * if RESOLVE_LINKS is defined, then the REAL absolute path will be found
 * allows that the last token of the pathname not exist (for creating files)
 */
#ifdef RESOLVE_LINKS
/* Resolve links - follow links to create REAL absolute path
 * if the ROOT_DIR is linked to somewhere else, and a path component
 * makes a root path which!= ROOT_DIR, then access will be denied
 */
char *gen_abs_path(arg)
  char *arg;
{
int DEBUG=0;
  char *tmp,*before,*after,*newarg;
  char link[MAX_PATH];
  int lasttoken=0;
  struct stat status;
  int result,len,loop=0;

  tmp=arg+ROOT_DIR_LEN-1;
  while(*tmp!='\0') {
    if(loop++>100) { fprintf(stderr,"too many loops in gen_abs()\n");return 0;}
if (DEBUG) fprintf(stderr,"while (arg:tmp): %s:%s\n",arg,tmp);
    if(*(tmp+1)=='.' && *(tmp+2)=='.' && (*(tmp+3)=='/' || *(tmp+3)=='\0')) {
      before=tmp;
      while(before>=arg && *(--before)!='/');
      if(before<arg+ROOT_DIR_LEN-1) {
        fprintf(stderr,"Can't .. before root directory\n");
        return 0;
      }
      before++;
      if(*(tmp+3)=='\0' || (*(tmp+3)=='/' && *(tmp+4)=='\0')) { 
        *before='\0';
        lasttoken=1;
      }
      else strcpy(before,tmp+4);
      tmp=before-1;
      if (DEBUG) fprintf(stderr,"REWOUND: %s\n",arg);
    } else if(*(tmp+1)=='.' && *(tmp+2)=='/') {
      strcpy(tmp+1,tmp+3);
    } else if(*(tmp+1)=='.' && *(tmp+2)=='\0') {
      *tmp='\0';
      lasttoken=1;
    } else if(*(tmp+1)=='/') {
      strcpy(tmp+1,tmp+2);
    } else if(*(tmp+1)=='\0') {
      *tmp='\0';
      lasttoken=1;
    } else {
      before=tmp+1;
      while(*(++tmp)!='/' && *tmp!='\0');  /* go to next '/' */
      if(*tmp=='\0') lasttoken=1;
      else lasttoken=0;
      *tmp='\0';

      if (DEBUG) fprintf(stderr,"Checking for link: %s\n",arg);
      if((result=lstat(arg,&status))==-1) {
       if(errno==ENOENT) {
         if(lasttoken) return arg;  /* allow last token to not exist */
         else fprintf(stderr,"%s: Path not found.\n",arg+ROOT_DIR_LEN);} else
       if(errno==ENOTDIR) fprintf(stderr,"Directory doesn't exist\n"); else
       if(errno==EACCES) fprintf(stderr,"%s unreadable\n",arg+ROOT_DIR_LEN);else
       if(errno==ELOOP) fprintf(stderr,"Too many symbolic links\n"); else
       if(errno==ENAMETOOLONG) fprintf(stderr,"Filename too long\n"); else
       fprintf(stderr,"Error in reading path: %s\n",arg+ROOT_DIR_LEN);
       FREE(arg);
       return 0;
      } else if((status.st_mode & S_IFMT)==S_IFLNK) {
        if (DEBUG) fprintf(stderr,"LINK!\n");
        result=readlink(arg,link,MAX_PATH);
        if(result==-1) {
          if(errno==ENOENT) fprintf(stderr,"file does not exist\n");
          else fprintf(stderr,"error in reading link: %s\n",arg+ROOT_DIR_LEN);
          FREE(arg);
          return 0;
        }
        if(link[result-1]!='/') link[result++]='/';
        link[result]='\0';
        if (DEBUG) fprintf(stderr,"LINK TO: %s\n",link);
        if(link[0]=='/') {   /* absolute link */
          if(strncmp(link,ROOT_DIR,ROOT_DIR_LEN-1)) {
            fprintf(stderr,"link to outside root directory\n");
            FREE(arg);
            return 0;
          }
          MK_STR(newarg,result+(1-lasttoken)*strlen(tmp+1)+2)
          str_add(newarg,link);  if(!lasttoken) str_add(newarg,tmp+1);
          FREE(arg);
          arg=newarg;
          tmp=arg+ROOT_DIR_LEN-1;
        } else {     /* not absolute link */
          *before='\0';
          len=before-arg;
          MK_STR(newarg,len+result+(1-lasttoken)*strlen(tmp+1)+3)
          str_add(newarg,arg); str_add(newarg,link);
          if(!lasttoken) str_add(newarg,tmp+1);
          FREE(arg);
          arg=newarg;
          tmp=arg+len-1;
        }
        if (DEBUG) fprintf(stderr,"Resolved link: %s\n",arg);
      }
      if(!lasttoken) *(tmp)='/';
    }
  } /* end while */
  if(*(tmp-1)=='/') *(tmp-1)='\0';
  return arg;
}

#else /* #ifdef RESOLVE_LINKS */
/* Don't resolve links, just clean up pathname string - NOT SECURE, FASTER */
char *gen_abs_path(arg)
  char *arg;
{
  int i,state=0;
  char *start,*a,*b;

  a=b=start=arg+ROOT_DIR_LEN-1;

  do {
    *b=*a;
    switch (state) {
      case 0:
        if(*a=='/') state=1;
        break;
      case 1:
        if(*a=='/' || *a=='\0') b-=1; /* "//" encountered, stay in state 1 */
        else if(*a=='.') state=2;
        else state=0;
        break;
      case 2:
        if(*a=='.') state=3;
        else if(*a=='/' || *a=='\0') {state=1; b-=2;} /* encountered "/./" */
        else state=0;
        break;
      case 3:
        if(*a=='/' || *a=='\0') {
          b-=4;
          while(b>=start&&*b!='/') b--;
          if(b<start) b=start;  /* rewound past start */
          state=1;
        } else
          state=0;
        break;
      default:
        FATAL("FATAL ERROR: impossible state in gen_abs_path")
        break;
    } /* end switch */

  } while(*(a++)!='\0' && *(b++)!=0);
  if(b==start) *(b++)='/';
  *b='\0';
  return arg;
}

#endif /* #ifdef RESOLVE_LINKS */

/* ref_from
 * Takes the absolute path from gen-abs_path and then
 * references it from either the WD or the ROOT or both
 * depending upon which of (FROM_WD, FROM_ROOT, HYBRID)
 * is defined
 * returns new string, does not free abs
 */
char *ref_from(abs)
  char *abs;
{
  char *tmp;

  if(*abs=='-') return abs;

#ifdef FROM_WD
  fprintf(stderr,"FROM_WD not available yet.\n"); 
#endif /* FROM_WD */

#ifdef HYBRID
  fprintf(stderr,"HYBRID not available yet.\n");
#endif /* HYBRID */

#ifdef FROM_ROOT
  MK_STR(tmp,strlen(abs+ROOT_DIR_LEN)+1)
  strcpy(tmp,abs+ROOT_DIR_LEN);
  return tmp;
#endif /* FROM_ROOT */
  fprintf(stderr,
       "You must choose to define FROM_WD or FROM_ROOT in config.c\n");
  return 0;
}

/* see if path is valid in acc tree */
int valid_path(acc,path)
  acc_t *acc;
  char *path;
{
  char buf[MAX_BUF];
  acc_t *tmp;
  int i;

  if(*path=='/' && *(path+1)=='\0') /* accessing "/" */
    if((acc=find_token(acc,"*")) && !acc->down) return 1;
    else return 0;

  do {
    i=0;
    while(((buf[i]=*path)!='\0') && (*path!='/')) { /*copy 1st token*/
      path++;  i++;
    } 
    buf[i]='\0';
    if(*path!='\0') path++;  /* point after '/' */
  } while (buf[0]!='\0' && acc->down && (acc=find_token(acc,buf)));

  if(acc && !acc->down) return 1;
  return 0;
}

/* ck_access  ("access" is already defined somewhere...)
 */
int ck_access(comm,file_args)
  char **comm;
  int *file_args;
{
  char *path,num[10];
  int i=1,num_file_args=0;
  acc_t *acc;

  while(comm[i]) {
    if(!file_args[i]) { i++; continue; }
    path=comm[i]+ROOT_DIR_LEN;
    if(*(path-1)=='\0') path="/"; /* need to null terminate? */
    sprintf(num,"%d",i);

if(DEBUGON)fprintf(stderr,"ck_access %d [%s]\n",i,comm[i]);
    if(!valid_path(write_acc,path) ||
           (write_acc->next->down && valid_path(write_acc->next,path))) {
      if(!(acc=find_token(can_read,comm[0])) ||
          (acc->down && !find_token(acc,num))) {
        ERROR(path,"permission denied [write_access]");
        return 0;
      }
      if(!valid_path(read_acc,path) || 
           (read_acc->next->down && valid_path(read_acc->next,path))) {
        ERROR(path,"permission denied [read_access]");
        return 0;
      }
    }
    i++;
  }

  return 1;
}