#include "bbs.h"

int checkfileperms(char *path, const UID_T euid, const GID_T egid)
  /* Prueft das Objekt path, ob es ein lesbares File ist
     Reueckgabewerte:
       -EACCES: Objekt ist nicht ueberpruefbar (Dir im Pfad ohne x-Bit)
       -ENOENT: Objekt existiert nicht
        EFAULT: Objekt ist kein File
        EACCES: File ist nicht lesbar
	     0: Files ist lesbar
  */
{
  struct stat stats;
  
  if (stat(path,&stats) < 0) {
    return(-errno); /* EACCES, ENOENT */
  }
  else if (! S_ISREG(stats.st_mode)) {
    return(EFAULT);
  }
  else {
    if (getperms(&stats,euid,egid) & PERM_R) return(0);
  }
  return(EACCES);
}


int getperms(const struct stat *stats, const UID_T euid, const GID_T egid)
  /* Prueft die Zugriffsrechte einer Datei/Directory
  */
{
  int perm=0;
  
  if (euid==0) return PERM_R|PERM_W|PERM_X;
  if (euid==stats->st_uid) {
    if (S_IRUSR & stats->st_mode) perm = PERM_R;
    if (S_IWUSR & stats->st_mode) perm |= PERM_W;
    if (S_IXUSR & stats->st_mode) perm |= PERM_X;
  }
  else if (egid==stats->st_gid) {
    if (S_IRGRP & stats->st_mode) perm = PERM_R;
    if (S_IWGRP & stats->st_mode) perm |= PERM_W;
    if (S_IXGRP & stats->st_mode) perm |= PERM_X;
  }
  else {
    if (S_IROTH & stats->st_mode) perm = PERM_R;
    if (S_IWOTH & stats->st_mode) perm |= PERM_W;
    if (S_IXOTH & stats->st_mode) perm |= PERM_X;
  }
  return(perm);
}


char *chrootpath(char path[], const char *chrootcwd, const char *chrootdir)
/*
  Wandelt einen auf chrootcwd bezogenen beliebigen Pfad in einen bezueglich
  chrootcwd (bei chrootdir==NULL) (bzw. "/" bei chrootdir!=NULL) absoluten
  Pfad um.
  "path" wird dabei durch das Ergebnis ueberschrieben.
*/
{
  int k;
  char str[2*PATH_MAX+2], *sp1, *sp2;

  /* absoluten Pfad erzeugen */
  if (path[0]!='/') {
    strcpy(str,path);
    strcpy(path,chrootcwd);
    k = strlen(chrootcwd);
    k--;
    if (k>=0) {
      if (chrootcwd[k]!='/') {
        strcat(path,"/");
      }
    }
    strcat(path,str);
  }

  /* "/../"-Sequenzen kuerzen */
  while ((sp1=strstr(path,"/../"))!=(char *)NULL) {
    sp2 = sp1;
    if (sp1!=path) {
      sp1--;
      while (*sp1!='/') sp1--;
    }
    sp2 += 3*sizeof(char);
    do {
      *(sp1++) = *(sp2++);
    } while (*sp2!='\0');
    *sp1 = '\0';
  }
  
  /* "/./"-Sequenzen kuerzen */
  while ((sp1=strstr(path,"/./"))!=(char *)NULL) {
    sp2 = sp1 + 2*sizeof(char);
    do {
      *(sp1++) = *(sp2++);
    } while (*sp2!='\0');
    *sp1 = '\0';
  }
  
  /* Pfadende anpassen */
  k = strlen(path);
  if (k>=3) {
    if (path[k-3]=='/' && path[k-2]=='.' && path[k-1]=='.') {
    /* "/.." am Pfadende */
      if (k==3) {
        path[k-3] = '\0';
      }
      else {
        sp1 = &(path[k-4]);
	while (*sp1!='/') {
	  sp1--;
	}
	*sp1 = '\0';
      }
    }
  }
  if (k>=2) {
    if (path[k-2]=='/' && path[k-1]=='.') {
    /* "/." am Pfadende */
      path[k-2] = '\0';
    }
  }
  if (k>=1) {
    if (path[k-1]=='/') {
    /* "/" am Pfadende */
      path[k-1] = '\0';
    }
  }
  
  /* Pfad wieder absolut bezueglich "/" machen */
  if (chrootdir!=(char *)NULL) {
    strprepend(path,chrootdir);
  }

  /* leerer Pfad entspricht dem Wurzelverzeichnis */
  if (path[0]=='\0') {
    strcpy(path,"/");
  }

  return path;
}


char *getchrootcwd(char chrootcwd[], const char *chrootdir)
  /*
  Gibt cwd, bezogen auf chrootdir, zurueck.
  Ist das nicht moeglich, weil chrootdir nicht den Anfang vom Pfad bildet,
  so wird NULL zurueckgegeben.
  */ 
{
  getcwd(chrootcwd,PATH_MAX);
  return getchrootpath(chrootcwd,chrootdir);
}


char *getchrootpath(char chrootpath[], const char *chrootdir)
  /*
  Kuerzt den in chrootpath stehenden Pfad um chrootdir.
  Ist das nicht moeglich, weil chrootdir nicht den Anfang vom Pfad bildet,
  so wird NULL zurueckgegeben.
  */ 
{
  int k, p;
  
  if (strstr(chrootpath,chrootdir)==chrootpath) {
    for (k=strlen(chrootdir),p=0;k<=strlen(chrootpath);k++) {
      chrootpath[p++] = chrootpath[k];
    }
    if (chrootpath[0]=='\0') {
      strcpy(chrootpath,"/");
    }
    return chrootpath;
  }
  else {
    return (char *)NULL;
  }
}


char *getrealdir(char dir[], confrecordtyp *confrecord)
{
  char olddir[PATH_MAX+1];
  
  getcwd(olddir,PATH_MAX);
  if (chdir(dir) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","getrealdir","chdir %s: %m",
             dir);
    return(NULL);
  }
  getcwd(dir,PATH_MAX);
  chdir(olddir);
  
  return(dir);
}


int readconffile(confrecordtyp *confrecord, const char *conffile)
{
  int znr, n;
  char zeile[PATH_MAX+S_STRLEN+1], str[PATH_MAX+1], *key, *arg;
  FILE *fp;

  /* Default-Werte */
  confrecord->idletimeout = IDLETIMEOUT;
  confrecord->logintimeout = LOGINTIMEOUT;
  confrecord->maxversuche = MAXVERSUCHE;
  strcpy(confrecord->bbsdpath,BBSDPATH);
  confrecord->bbsdwatchtime = BBSDWATCHTIME;
#ifdef BBSDUID
  strcpy(confrecord->bbsduid,BBSDUID);
#else
  strcpy(confrecord->bbsduid,SYSOP);
#endif
  strcpy(confrecord->usersfile,USERRECFILE);
  strcpy(confrecord->logfile,LOGFILENAME);
  strcpy(confrecord->vardir,VARDIR);
  strcpy(confrecord->helpdir,HELPDIR);
  strcpy(confrecord->scratchdir,SCRATCHDIR);
  strcpy(confrecord->sysop,SYSOP);
  confrecord->lang = DEFLANGUAGE;
  strcpy(confrecord->authkey,BBS_AUTHKEY_FIX);
  strcpy(confrecord->rootdir,ROOT);

  /* Config-File lesen */
  if ((fp=fopen(conffile,"r")) == NULL) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,NULL,"readconffile",
             "fopen %s: %m",conffile);
    return(-1);
  }
  znr = 0;
  while((n=fgetnln(zeile,PATH_MAX+S_STRLEN,fp)) >= 0) {
    znr++;
    if (n > PATH_MAX+S_STRLEN) {
      errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,NULL,"readconffile",
               "line %i too long",znr);
      return(-1);
    }
    split2key_arg(zeile,&key,&arg);
    if (*key=='\0' || *key=='#') {
      /* */
    }
    else if (strncmp("idletimeout",key,6)==0) {
      confrecord->idletimeout = atoi(arg);
    }
    else if (strncmp("logintimeout",key,6)==0) {
      confrecord->logintimeout = atoi(arg);
    }
    else if (strncmp("maxversuche",key,6)==0) {
      confrecord->maxversuche = atoi(arg);
    }
    else if (strncmp("bbsdpath",key,6)==0) {
      strcpy(confrecord->bbsdpath,arg);
    }
    else if (strncmp("bbsdwatchtime",key,6)==0) {
      confrecord->bbsdwatchtime = atoi(arg);
    }
    else if (strncmp("bbsduid",key,6)==0) {
      strcpy(confrecord->bbsduid,arg);
    }
    else if (strncmp("usersfile",key,6)==0) {
      strcpy(confrecord->usersfile,arg);
    }
    else if (strncmp("logfile",key,6)==0) {
      strcpy(confrecord->logfile,arg);
    }
    else if (strncmp("vardir",key,6)==0) {
      strcpy(confrecord->vardir,arg);
    }
    else if (strncmp("helpdir",key,6)==0) {
      strcpy(confrecord->helpdir,arg);
    }
    else if (strncmp("scratchdir",key,6)==0) {
      strcpy(confrecord->scratchdir,arg);
    }
    else if (strncmp("sysop",key,5)==0) {
      strcpy(confrecord->sysop,arg);
    }
    else if (strncmp("lang",key,4)==0) {
      confrecord->lang = atoi(arg);
    }
    else if (strncmp("rootdir",key,6)==0) {
      strcpy(confrecord->rootdir,arg);
    }
    else if (strncmp("authkey",key,6)==0) {
      strcat(confrecord->authkey,arg);
    }
    else {
      errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,NULL,"readconffile",
               "wrong line %i (key %s)",znr,key);
      return(-1);
    }
  }
  
  /* '/' sollte Helpdir nicht abschliessen */
  n = strlen(confrecord->helpdir) - 1;
  if (confrecord->helpdir[n] == '/')  confrecord->helpdir[n] = '\0';
  /* '/' sollte Scratchdir nicht abschliessen */
  n = strlen(confrecord->scratchdir) - 1;
  if (confrecord->scratchdir[n] == '/')  confrecord->scratchdir[n] = '\0';
  
  /* '/' sollte Root nicht abschliessen */
  n = strlen(confrecord->rootdir) - 1;
  if (confrecord->rootdir[n] == '/')  confrecord->rootdir[n] = '\0';
  /* Root sollte keine Symlinks enthalten */
  if (getrealdir(confrecord->rootdir,confrecord) == NULL) {
    errormsg(E_SYSLOG|E_USER|E_CONSOLE,confrecord,NULL,"readconffile",
             "cannot get real rootdirectory");
    return(-1);
  }
    
  
  /* einige Pfade aus vardir ableiten */
  n = strlen(confrecord->vardir) - 1;
  if (confrecord->vardir[n] == '/')  confrecord->vardir[n] = '\0';
  strcpy(str,confrecord->vardir);
  strcat(str,"/");
  strcpy(confrecord->sockpath,str);
  strcat(confrecord->sockpath,"bbs-socket");
  strcpy(confrecord->lockpath,str);
  strcat(confrecord->lockpath,"bbsd-lock");
  strcpy(confrecord->bbsdpidpath,str);
  strcat(confrecord->bbsdpidpath,"bbsdpid");
  strcpy(confrecord->bbsdkpidpath,str);
  strcat(confrecord->bbsdkpidpath,"bbsdkpid");
  strcpy(confrecord->kermrcpath,str);
  strcat(confrecord->kermrcpath,"kermrc");

  return 0;
}


#if 0

#ifndef NO_REGCOMP
#define NO_RE_COMP
#endif
int expandfilenames(const char *inpath, char outpath[],
                    const int maxoutpathlen, confrecordtyp *confrecord)
  /*
  Expandiert eine Argumentezeile zu allen Files
  
  Rueckgabewerte:
    Anzahl der Files (>=0), wenn ok
    sonst < 0:
      -1: Bereichsueberlauf, Meldung in outpath
      -2: fehlerhaftes Pattern, Meldung in outpath
      -3: Fehler im RE-Vergleich
  */
{
  int nr, k, opos, p;
  SIZE_T membuflen;
  char inbuf[PATH_MAX+1], basedir[PATH_MAX+1], pattern[PATH_MAX+1],
       str[PATH_MAX+1], *memary[MAXARGS], *mbuf, *nbuf, *ipp, c;
  DIR *dp;
  struct dirent *dirp;
  struct stat stats;
#ifndef NO_REGCOMP
  regex_t preg;
  regmatch_t pmatch[1];
#else
#ifndef NO_RE_COMP
  char *sp;
#endif
#endif

  opos = 0;
  nr = 0;
  membuflen = 1024;
  if ((mbuf=(char *)malloc(membuflen)) == NULL) {
    strcpy(outpath,"malloc failed");
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames","malloc: %m");
    return(-1);
  }
  memary[0] = mbuf;
  
  /* Argumentezeile in Buffer kopieren und mittels ipp lesen */
  if (strlen(inpath) > PATH_MAX) {
    strcpy(outpath,"inpath too long");
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
             msg("expandfilenames",0,confrecord->lang));
    free(mbuf);
    return(-1);
  }
  strcpy(inbuf,inpath);
  ipp = inbuf;
  while (*ipp!='\0' && nr>=0) {
    /* a=argumentweise lesen, die Argumente sind durch Leerzeichen getrennt */
    while (*ipp==' ' && *ipp!='\0') ipp++;    /* zum Anfang des Argumentes */
    if (*ipp != '/') {                        /* absoluten Pfad erzeugen */
      getcwd(basedir,PATH_MAX);
      k = strlen(basedir);
      basedir[k++] = '/';
    }
    else {
      k = 0;
    }
    /* Argument nach basedir kopieren, dabei wird der Pfad absolut */
    while (*ipp!=' ' && *ipp!='\0' && k<PATH_MAX) {
      basedir[k++] = *ipp++;
    }
    basedir[k] = '\0';
    /* /a/b.../e/f aufspalten in /a/b.../e/ (basedir) und f (str) */
    while (basedir[--k] != '/') ;
    k++;
    strcpy(str,&(basedir[k]));
    basedir[k] = '\0';
    /* Shell-Metazeichen umwandeln in RE */
    p = 0;
    pattern[p++] = '^';
    k = 0;
    while ((c=str[k++]) != '\0') {
      switch (c) {
      case '.':
      pattern[p++] = '\\';
	pattern[p++] = '.';
	break;
      case '*':
	pattern[p++] = '.';
	pattern[p++] = '*';
	break;
      case '?':
	pattern[p++] = '.';
	pattern[p++] = '?';
	break;
      case ']':
	pattern[p++] = ']';
	pattern[p++] = '?';
	break;
      default:
	pattern[p++] = c;
      }
    }
    pattern[p++] = '$';
    pattern[p] = '\0';
    /* RE compilieren */
#ifndef NO_REGCOMP
    if ((k=regcomp(&preg,pattern,REG_EXTENDED|REG_NOSUB)) != 0) {
      regerror(k,&preg,outpath,PATH_MAX);
#else
#ifndef NO_RE_COMP
    if ((sp=re_comp(pattern)) != (char *)0) {
      strcpy(outpath,sp);
#endif
#endif
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
               msg("expandfilenames",1,confrecord->lang),outpath);
      free(mbuf);
      return(-2);
    }

    /* pattern mit allen Files in basedir vergleichen */
    if ((dp=opendir(basedir)) != NULL) {
      while ((dirp=readdir(dp)) != NULL) {
        strcpy(str,basedir);
        strcat(str,dirp->d_name);
	/* stat-Struct von allen Eintraegen in basedir holen */
        if (stat(str,&stats) == 0) {
          if (S_ISREG(stats.st_mode)) {
#ifndef NO_REGCOMP
       	    if ((k=regexec(&preg,dirp->d_name,0,pmatch,0)) == 0) {
#else
#ifndef NO_RE_COMP
            if ((k=re_exec(dirp->d_name)) == 1) {
#endif
#endif
	      /* Pattern passt */
	      /* Test, ob Filename schon vorhanden ist */
	      for (k=0; k<nr; k++) {
	        if ((p=strcmp(str,memary[k])) == 0) continue;
	      }
	      if (p != 0) {
	        /* Filename ist neu */
		/* Filename merken und diverse Ueberlauftests */
		k = (strlen(str)+1)*sizeof(char);
		if (k + memary[nr] - mbuf > membuflen) {
		  membuflen += 1024;
		  if ((nbuf=(char *)realloc((void *)mbuf,membuflen)) == NULL) {
		    strcpy(outpath,"realloc failed");
                    errormsg(E_LOGFILE|E_USER,confrecord,"bbs",
		             "expandfilenames","realloc: %m");
#ifndef NO_REGCOMP
  	  	    regfree(&preg);
#endif
                    free(mbuf);
		    return(-1);
		  }
		  if (nbuf != mbuf) {
		    for (p=0; p<=nr; p++)  memary[p] += nbuf - mbuf;
		    mbuf = nbuf;
		  }
		}
                strcpy(memary[nr],str);
	        memary[nr+1] = memary[nr] + k;
	        nr++;
	        if (nr >= MAXARGS) {
		  strcpy(outpath,"nr zu gross");
                  errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
		           msg("expandfilenames",2,confrecord->lang));
#ifndef NO_REGCOMP
	          regfree(&preg);
#endif
                  free(mbuf);
	          return(-1);
	        }
		/* Filename an outpath anhaengen */
	        for (k=0; str[k]!='\0' && opos<maxoutpathlen-2; opos++,k++) {
	          outpath[opos] = str[k];
                }
   	        outpath[opos++] = ' ';
	        outpath[opos] = '\0';
  	        if (opos > maxoutpathlen-2) {
		  strcpy(outpath,"outpath zu klein");
                  errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
			   msg("expandfilenames",3,confrecord->lang));
#ifndef NO_REGCOMP
	          regfree(&preg);
#endif
                  free(mbuf);
	          return(-1);
	        }	      
	      }			/* if (p...) */
	    }			/* if (S_ISREG...) */
#ifndef NO_REGCOMP
  	    else if (k != REG_NOMATCH) {
	      regerror(k,&preg,outpath,PATH_MAX);
              errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
		       "regexec: %s",outpath);
	      regfree(&preg);
#else
#ifndef NO_RE_COMP
  	    else if (k < 0) {
              errormsg(E_LOGFILE|E_USER,confrecord,"bbs","expandfilenames",
		       "re_exec: internal error");
#endif
#endif
              free(mbuf);
	      return(-3);
	    }
	  }
	}
      }				/* while ((dirp=...) */
    }				/* if ((dp=...) */
#ifndef NO_REGCOMP
    regfree(&preg);
#endif
  }				/* while(*ipp...) */
  free(mbuf);
  return nr;
}

#endif /* if 0 */
