ftp.nice.ch/pub/next/unix/communication/ft_bbs.1.0.s.tar.gz#/ft_bbs-1.0/do.c

This is do.c in view mode; [Download] [Up]

/* $Id: do.c,v 1.2 1995/12/19 19:30:47 eilts Exp eilts $ */
#include "bbs.h"

#ifdef NNTPNEWS
int do_postart_nntp(const char *groups, const char *subject, const char *refs,
                    const char *letter, newsstattyp *newsrecord,
		    const confrecordtyp *confrecord)
{
  char astr[ARG_MAX+1], str[STRLEN+1];
  boolean deconnect_on_return = FALSE;
  newsstattyp newsrec, *newsrecp;
  
  newsrec.is_connected = FALSE;
  if (newsrecord != NULL) {
    newsrecp = newsrecord;
  }
  else {
    newsrecp = &newsrec;
  }
  if (checknewsperms(confrecord) < 1) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_nntp",
             "You are not allowed to post");
    return 0;
  }

  strcpy(str,confrecord->userrecord.name);
  if (strchr(str,'@') == NULL && strchr(str,'!') == NULL) {
    strcat(str,"@");
    strcat(str,confrecord->hostname);
  }
  if (*confrecord->userrecord.realname != '\0') {
    sprintf(astr,"From: %s <%s>\nNewsgroups: %s\nSubject: %s",
            confrecord->userrecord.realname,str,groups,subject);
  }
  else {
    sprintf(astr,"From: %s\nNewsgroups: %s\nSubject: %s",str,groups,subject);
  }
  if (refs != NULL && *refs != '\0') {
    strcat(astr,"\nReferences: ");
    strcat(astr,refs);
  }
  strcat(astr,"\n\n");
  if (! newsrecp->is_connected) {
    if (nntp_connect(newsrecp,confrecord) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_nntp",
  	       "cannot connect to Newsserver %s",confrecord->nntpserver);
      return -1;
    }
    deconnect_on_return = TRUE;
  }
  if (nntp_postarticle(astr,letter,newsrecp,confrecord) == OK_POSTED) {
    writetouser(confrecord,"article send");
  }
  else {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_nntp",
             "posting failed, got %s",newsrecp->lastretstr);
    writetouser(confrecord,"article NOT send");
    return -1;
  }
  if (deconnect_on_return)  nntp_close(newsrecp);
  return 0;
}

int do_postart_inews(const char *groups, const char *subject, const char *refs,
                     const char *letter, const confrecordtyp *confrecord)
{
  int status, pfds[2], fd, errfd, save_stderr;
  PID_T pid;
  SIZE_T len;
  SSIZE_T bytes;
  char astr[ARG_MAX+1], errfile[PATH_MAX+1];
  struct stat stats;
  
  if (checknewsperms(confrecord) < 1) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
             "You are not allowed to post");
    return 0;
  }
  sprintf(errfile,"%s/inews_err.%ld",confrecord->scratchdir,
          (long)getpid());
  if ((errfd=open(errfile,O_CREAT|O_TRUNC|O_RDWR,0600)) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postaricle",
             "open %s: %m", errfile);
    return -1;
  }
  unlink(errfile);
  if ((fd=open(letter,O_RDONLY)) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
             "open letter %s: %m", letter);
    return -1;
  }
  if (pipe(pfds) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews","pipe: %m");
    return -1;
  }      
  if ((pid = fork()) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews","fork: %m");
    return -1;
  }
  if (pid == 0) {
    save_stderr = dup(STDERR_FILENO);
    dup2(pfds[0],STDIN_FILENO);
    close(pfds[1]);
    dup2(errfd,STDOUT_FILENO);
    dup2(errfd,STDERR_FILENO);
    execl(INEWS,"inews: receiving from bbs","-h",(char *)0);
    close(errfd);
    dup2(STDERR_FILENO,save_stderr);
    perror("execv INEWS");
    errormsg(E_LOGFILE,confrecord,"bbs","do_postart_inews","execv INEWS: %m");
    exit(1);
  }

  close(pfds[0]);
  if (*confrecord->userrecord.realname != '\0') {
    sprintf(astr,"From: %s <%s>\nNewsgroups: %s\nSubject: %s",
            confrecord->userrecord.realname,confrecord->userrecord.name,
	    groups,subject);
  }
  else {
    sprintf(astr,"From: %s\nNewsgroups: %s\nSubject:%s",
            confrecord->userrecord.name,groups,subject);
  }
  if (refs != NULL && *refs != '\0') {
    strcat(astr,"\nReferences: ");
    strcat(astr,refs);
  }
  strcat(astr,"\n\n");
  len = strlen(astr);
  if (write(pfds[1],astr,len) != len) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
             "write (header): %m");
  }
  else {
    bytes = 0;
    while (bytes >= 0 &&
           (bytes=read(fd,(void *)astr,(SIZE_T)PATH_MAX)) > 0) {
      bytes = writen(pfds[1],astr,bytes);
    }
  }
  close(pfds[1]);
  close(fd);
  while (wait(&status) != pid);
  if (fstat(errfd,&stats) == 0 && stats.st_size > 0) {
    lseek(errfd,(OFF_T)0,SEEK_SET);
    bytes = read(errfd,astr,STRLEN);
    astr[bytes] = '\0';
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
             "inews reports: %s",astr);
    }
  close(errfd);
  if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
    writetouser(confrecord,"article send");
  }
  else {
    if (WIFEXITED(status)) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
               "inews exited with status %d",WEXITSTATUS(status));
    }
    if (WIFSIGNALED(status)) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
               "inews got signal %d",WTERMSIG(status));
      if (WCOREDUMP(status)) {
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_postart_inews",
                 "inews dumped core");
      }
    }
    writetouser(confrecord,"article NOT send");
  }
  return 0;
}


void do_shownewsheader(const char *header, const confrecordtyp *confrecord)
{
  int fd;
  char scratch[PATH_MAX+1];

  scratchfilename(scratch,"newsheader-",NULL,confrecord->scratchdir);
  if ((fd=open(scratch,O_CREAT|O_TRUNC|O_RDWR,0600)) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_shownewsheader",
             "open %s: %m",scratch);
    return;
  }
  write(fd,header,strlen(header));
  close(fd);
  do_showfile(scratch,NULL,NULL,NULL,confrecord);
  unlink(scratch);
  return;
}
#endif


void do_msgbrowser(const char params[], const confrecordtyp *confrecord)
  /*
  Format fuer params: -a Aktion -n Nummer [-i Message-Id]
    Aktion ist R (lesen), D (loeschen), A (beantworten), G (downladen)
  */
{
  int nr, lang;
  char action, option, msg_id[STRLEN+1], str[STRLEN+1], spath[PATH_MAX+1];
  chrootrecordtyp chrootrec;
  
  lang = confrecord->userrecord.lang;
  *msg_id = '\0';
  nr = -1;
  getarg(NULL,params);
  while ((option=getarg(str,params)) != '\0') {
    switch (option) {
    case 'a':
      action = *str;
      break;
    case 'n':
      nr = atoi(str);
      break;
    case 'i':
      strmaxcpy(msg_id,str,STRLEN);
      break;
    default:
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_msgbrowser",
               "%c: unknown option",option);
      return;
    }
  }
  if (nr < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_msgbrowser",
             "No message number given");
    return;
  }
  switch (action) {
  case 'R':
    if (getmessage(spath,nr,msg_id,confrecord) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_msgbrowser",
               "message not found");
      return;
    }
    do_showfile(spath,NULL,NULL,NULL,confrecord);
    unlink(spath);
    break;
  case 'D':
    if (removemessage(nr,msg_id,confrecord) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_msgbrowser",
               "message cannot be removed");
    }
    break;
  case 'A':
    /* wird in menue_c erledigt */
    break;
  case 'G':
    if (getmessage(spath,nr,msg_id,confrecord) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_msgbrowser",
               "message not found");
      return;
    }
    strcpy(chrootrec.root,"/");
    getcwd(chrootrec.cwd,PATH_MAX);
    do_get(spath,&chrootrec,protokollnamen,confrecord->userrecord.protokoll,
           confrecord);
    unlink(spath);
    break;
  }
  
  return;
}


void do_sendmessage(const char params[], const char *chrootcwd,
                    const char *chrootdir, const confrecordtyp *confrecord)
  /*
  Format fuer params: -l letter -s subject -a address
  */
{
  char letter[PATH_MAX+1], subject[STRLEN+1], address[ARG_MAX+1],
       sender[2*S_STRLEN+4], str[ARG_MAX+1], option;
  
  *letter = '\0';
  *subject = '\0';
  *address = '\0';
  getarg(NULL,params);
  while ((option=getarg(str,params)) != '\0') {
    switch (option) {
    case 'l':
      strcpy(letter,str);
      break;
    case 's':
      strcpy(subject,str);
      break;
    case 'a':
      strcpy(address,str);
      break;
    default:
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmessage",
               "%c: unknown option",option);
      return;
    }
  }
  if (*letter == '\0') {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmessage",
	     "%s: no letter given",params);
    return;
  }
  if (*address == '\0') {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmessage",
	     "%s: no address given",params);
    return;
  }
  chrootpath(letter,chrootcwd,confrecord->scratchdir);
  if (*subject == '\0') {
    strcpy(subject,"no subject given");
  }

  if (*(confrecord->userrecord.realname)) {
    sprintf(sender,"%s <%s>",confrecord->userrecord.realname,
            confrecord->userrecord.name);
  }
  else {
    sprintf(sender,"<%s>",confrecord->userrecord.name);
  }
  if (sendmessage(sender,address,subject,letter,confrecord) == 0) {
    writetouser(confrecord,"message send");
  }
  else {
    writetouser(confrecord,"message NOT send");
  }
  return;
}


int do_sendmail(const char params[], char *letter, const char *chrootcwd,
                 const char *chrootdir, const confrecordtyp *confrecord)
  /*
  Format fuer params: -l letter -a address [-s subject]
  letter und address duerfen keine Whitespaces beinhalten
  */
{
  int n, anz, status, pfds[2], fd, errfd, save_stderr, rw = -1;
  PID_T pid;
  SIZE_T len;
  SSIZE_T bytes;
  char *splits[MAXARGS+1], *arg[MAXARGS+1], str[PATH_MAX+2*STRLEN+20],
       astr[ARG_MAX+1], address[ARG_MAX+1], subject[STRLEN+1],
       errfile[PATH_MAX+1], option;
  boolean sendok = FALSE;
  struct stat stats;
  
  *letter = '\0';
  *address = '\0';
  strcpy(subject,"no subject given");
  getarg(NULL,params);
  while ((option=getarg(str,params)) != '\0') {
    switch (option) {
    case 'a':
      strcpy(address,str);
      break;
    case 's':
      strcpy(subject,str);
      break;
    case 'l':
      strcpy(letter,str);
      if (chrootcwd != NULL) {
        chrootpath(letter,chrootcwd,confrecord->scratchdir);
      }
      break;
    default:
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "%c: unknown option",option);
      return -1;
    }
  }

  if (strchr(address,'!') != NULL) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
             "no '!' in address allowed: %s",address);
    return -1;
  }
  if (*address == '-') {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
             "no leading '-' in address allowed: %s",address);
    return -1;
  }

  if (strcmp(address,confrecord->sysop) == 0) {
    sendok = TRUE;
  }
  else {
    if (checkmailperms(address,confrecord) != 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "You are not allowed to send to this destination");
      return -1;
    }
    else {
      sendok = TRUE;
    }
  }

  if (sendok) {
    sprintf(errfile,"%s/sendmail_err.%ld",confrecord->scratchdir,
            (long)getpid());
    if ((errfd=open(errfile,O_CREAT|O_TRUNC|O_RDWR,0600)) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "open %s: %m", errfile);
      return -1;
    }
    unlink(errfile);
    if ((fd=open(letter,O_RDONLY)) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "open letter %s: %m", letter);
      return -1;
    }
    strcpy(astr,address);
    anz = splitstring(splits,astr,',',MAXARGS);
    arg[0] = "sendmail: receiving from bbs";
    for (n=0; n < anz; n++) {
      if ((arg[n+1]=(char *)malloc(strlen(splits[n]+1))) == NULL) {
	errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail","malloc: %m");
	return -1;
      }
      parseaddrline(arg[n+1],str,splits[n]);
    }
    arg[n+1] = NULL;
    if (pipe(pfds) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail","pipe: %m");
      return -1;
    }      
    if ((pid = fork()) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail","fork: %m");
      return -1;
    }
    if (pid == 0) {
      save_stderr = dup(STDERR_FILENO);
      dup2(pfds[0],STDIN_FILENO);
      close(pfds[1]);
      dup2(errfd,STDOUT_FILENO);
      dup2(errfd,STDERR_FILENO);
      execv(SENDMAIL,arg);
      close(errfd);
      dup2(STDERR_FILENO,save_stderr);
      perror("execv INEWS");
      errormsg(E_LOGFILE,confrecord,"bbs","do_sendmail","execv SENDMAIL: %m");
      exit(1);
    }

    close(pfds[0]);
    if (*confrecord->userrecord.realname != '\0') {
      sprintf(str,"From: %s <%s>\nTo: %s\nSubject: %s\n\n",
              confrecord->userrecord.realname,
	      confrecord->userrecord.name,address,subject);
    }
    else {
      sprintf(str,"From: %s\nTo: %s\nSubject: %s\n\n",
              confrecord->userrecord.name,address,subject);
    }
    len = strlen(str);
    if (write(pfds[1],str,len) != len) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "write (header): %m");
    }
    else {
      bytes = 0;
      while (bytes >= 0 &&
             (bytes=read(fd,(void *)str,(SIZE_T)PATH_MAX)) > 0) {
        bytes = writen(pfds[1],str,bytes);
      }
    }
    close(pfds[1]);
    close(fd);
    while (wait(&status) != pid);
    for (n=0; n < anz; n++)  free(arg[n+1]);
    if (fstat(errfd,&stats) == 0 && stats.st_size > 0) {
      lseek(errfd,(OFF_T)0,SEEK_SET);
      bytes = read(errfd,str,STRLEN);
      str[bytes] = '\0';
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
               "sendmail reports: %s",str);
      sendok = FALSE;
    }
    close(errfd);
    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
      rw = 0;
      sendok = TRUE;
    }
    else {
      if (WIFEXITED(status)) {
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
                 "sendmail exited with status %d",WEXITSTATUS(status));
      }
      if (WIFSIGNALED(status)) {
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
                 "sendmail got signal %d",WTERMSIG(status));
	if (WCOREDUMP(status)) {
          errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
                   "sendmail dumped core");
	}
      }
      sendok = FALSE;
    }
  }
  if (sendok) {
    writetouser(confrecord,"mail send");
  }
  else {
    writetouser(confrecord,"mail NOT send");
  }
  return rw;
}


void do_talk(const char params[], const confrecordtyp *confrecord)
{
  int n;

  if (*params == '\0')  return;
  n = 0;
  n = atoi(params);
  if (n < 1) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_talk","wrong pid %s",
             params);
    return;
  }
  iniconnecttouser(n,confrecord);
}


void do_sprache(const char params[], const char *sprachennamen[],
                confrecordtyp *confrecord)
{
  int lang;
  char c;
  
  c = params[0];
  c = tolower(c);
  if (c=='d') {
    confrecord->userrecord.lang = GERMAN;
  }
  else if (c=='e') {
    confrecord->userrecord.lang = ENGLISH;
  }
  lang = confrecord->userrecord.lang;
  writetouser(confrecord,msg("do_sprache",0,lang),sprachennamen[lang]);
}


void do_autozmodem(const char params[], confrecordtyp *confrecord)
{
  char c;
  
  c = params[0];
  c = tolower(c);
  if (c=='y' || c=='j') {
    confrecord->userrecord.autozmodem = TRUE;
  }
  else if (c=='n') {
    confrecord->userrecord.autozmodem = FALSE;
  }
  
  if (confrecord->userrecord.autozmodem) {
    writetouser(confrecord,msg("do_autozmodem",0,confrecord->userrecord.lang));
  }
  else {
    writetouser(confrecord,msg("do_autozmodem",1,confrecord->userrecord.lang));
  }
}


void do_fullist(const char params[], confrecordtyp *confrecord)
{
  char c;
  
  c = params[0];
  c = tolower(c);
  if (c=='y' || c=='j') {
    confrecord->userrecord.fullist_on_cd = TRUE;
  }
  else if (c=='n') {
    confrecord->userrecord.fullist_on_cd = FALSE;
  }
  
  if (confrecord->userrecord.fullist_on_cd) {
    writetouser(confrecord,msg("do_fullist",0,confrecord->userrecord.lang));
  }
  else {
    writetouser(confrecord,msg("do_fullist",1,confrecord->userrecord.lang));
  }
}


void do_cd(const char params[], chrootrecordtyp *chrootrecord,
           const confrecordtyp *confrecord)
{
  int n;
  boolean rootchg = FALSE, cdok = TRUE;
  char *args[MAXARGS+1], *spary[MAXARGS+1], str[PATH_MAX+1], str2[PATH_MAX+1],
        realpath[PATH_MAX+1], ndir[PATH_MAX+1], oldchrootdir[PATH_MAX+1],
        oldrootname[PATH_MAX+1];

  strmaxcpy(str,params,PATH_MAX);
  splitparams(args,str);
  if (args[0]==(char *)NULL) {
    strcpy(str,confrecord->userrecord.home);
    args[0] = str;
    strmaxcpy(chrootrecord->root,chrootrecord->primaryroot,PATH_MAX);
    strcpy(chrootrecord->rootname,"/");
    chrootpath(args[0],chrootrecord->cwd,chrootrecord->root);
  }
  else if (strcmp(chrootrecord->root,chrootrecord->primaryroot)!=0 &&
           strcmp(chrootrecord->cwd,"/")==0 && strcmp(args[0],"..")==0) {
    strcpy(chrootrecord->root,chrootrecord->primaryroot);
    strcpy(chrootrecord->rootname,"/");
    rootchg = TRUE;
    strcpy(args[0],"/");
    chrootpath(args[0],chrootrecord->cwd,chrootrecord->root);
  }
  else {
    strmaxcpy(ndir,args[0],PATH_MAX);
    chrootpath(args[0],chrootrecord->cwd,chrootrecord->root);
    if (access(args[0],F_OK)==0 &&
        strcmp(chrootrecord->root,chrootrecord->primaryroot)==0) {
      if ((n=readlink(args[0],realpath,PATH_MAX)) > 0) {
        realpath[n] = '\0';
        if (realpath[0] != '/') {
	  getcwd(str2,PATH_MAX);
	  strcat(str2,"/");
	  strprepend(realpath,str2);
	}
	if (strstr(realpath,chrootrecord->root) != realpath) {
	  cdok = FALSE;
	  strmaxcpy(str2,confrecord->rootdir,PATH_MAX);
	  splitstring(spary,str2,':',MAXARGS);
	  for (n=0; spary[n]!=NULL; n++) {
	    if (strstr(realpath,spary[n]) == realpath) {
  	      cdok = TRUE;
	      break;
	    }
	  }
	  if (cdok) {
	    rootchg = TRUE;
	    strcpy(oldchrootdir,chrootrecord->root);
	    strcpy(oldrootname,chrootrecord->rootname);
	    strmaxcat(chrootrecord->rootname,"/",PATH_MAX);
	    strmaxcat(chrootrecord->rootname,ndir,PATH_MAX);
	    stripslashes(chrootrecord->rootname);
	    strmaxcpy(chrootrecord->root,realpath,PATH_MAX);
	    getrealdir(chrootrecord->root,confrecord);
	  }
	  else {
	    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_cd",
		      msg("do_cd",0,confrecord->userrecord.lang));
	  }
	}
      }
    }
  }
  if (cdok && chdir(args[0]) < 0) {
    if (rootchg) {
      strcpy(chrootrecord->root,oldchrootdir);
      strcpy(chrootrecord->rootname,oldrootname);
    }
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_cd","chdir %s: %m",
             getchrootpath(str,chrootrecord->root));
  }
  getchrootcwd(chrootrecord);
#ifdef AUTOLISTFILE
  if (checkfileperms(AUTOLISTFILE,geteuid(),getegid()) == 0) {
    do_showfile(AUTOLISTFILE,NULL,chrootrecord->cwd,chrootrecord->root,
                confrecord);
  }
#endif
}


void do_get(const char params[], const chrootrecordtyp *chrootrecord,
            const char *protokollnamen[], const int protokoll,
	    const confrecordtyp *confrecord)
{
  UID_T euid;
  GID_T egid;
  int k, p;
  char *args[MAXARGS+1], *inargs[MAXARGS+1], *files[MAXARGS+1],
       str[ARG_MAX+1], path[PATH_MAX+1], kermrcpath[PATH_MAX+1];

  strmaxcpy(str,params,ARG_MAX);
  euid = geteuid();
  egid = getegid();
  bbslogn(LOG_FILE,confrecord,"%s: %s",protokollnamen[protokoll],str);
  splitparams(inargs,str);
  p = 0;
  for (k=0;inargs[k]!=NULL;k++) {
    if (*inargs[k]!='-') {
      strmaxcpy(path,inargs[k],PATH_MAX);
      chrootpath(path,chrootrecord->cwd,chrootrecord->root);
      switch (checkfileperms(path,euid,egid)) {
      case -EACCES:
      case -ENOENT:
	errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_get",
	         msg("do_get",0,confrecord->userrecord.lang),
		     getchrootpath(path,chrootrecord->root));
	break;
      case EFAULT:
	errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_get",
	         msg("do_get",1,confrecord->userrecord.lang),
		     getchrootpath(path,chrootrecord->root));
	break;
      case EACCES:
	errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_get",
	         msg("do_get",2,confrecord->userrecord.lang),
		     getchrootpath(path,chrootrecord->root));
	break;
      default:
        files[p] = (char *)malloc((SIZE_T)PATH_MAX);
	strcpy(files[p],path);
        p++;
      }
    }
  }
  files[p] = NULL;
  if (p == 0)  return;
  
  strcpy(path,".");
  chrootpath(path,chrootrecord->cwd,chrootrecord->root);
  p = 0;
  switch (protokoll) {
    case KERMITPROTO:
      if ((errno=checkfileperms(confrecord->kermrcpath,euid,egid)) != 0) {
        if (errno < 0)  errno = -errno;
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_get",
	         "cannot access kermit configuration file\n%s:\n%m",
		 confrecord->kermrcpath);
	break;
      }
      args[p++] = "kermit";
      args[p++] = "-y";
      strcpy(kermrcpath,confrecord->kermrcpath);
      args[p++] = kermrcpath;
      args[p++] = "-s";
      for (k=0;files[k]!=NULL;k++) {
        getchrootpath(files[k],path);
  	args[p++] = &(files[k][1]);	/* wegen dem fuehrenden '/' */
      }
      args[p] = NULL;
      execute(KERMIT,args,confrecord->userrecord.loglevel,confrecord);
      break;
    case ZMODEMPROTO:
      args[p++] = "sz";
      args[p++] = "-b";
      for (k=0;files[k]!=NULL;k++) {
        getchrootpath(files[k],path);
	args[p++] = &(files[k][1]);	/* wegen dem fuehrenden '/' */
      }
      args[p] = NULL;
      execute(SZ,args,confrecord->userrecord.loglevel,confrecord);
      break;
  }
  for (k=0;files[k]!=NULL;k++) {
    free((void *)files[k]);
  }
}


void do_home(const char params[], const chrootrecordtyp *chrootrecord,
             userrecordtyp *userrecord, const confrecordtyp *confrecord)
{
  char *args[MAXARGS+1], path[PATH_MAX+1], fullpath[PATH_MAX+1],
       wdir[PATH_MAX+1];
  
  strmaxcpy(path,params,PATH_MAX);
  splitparams(args,path);
  if (args[0]!=(char *)NULL) {
    strcpy(fullpath,args[0]);
    chrootpath(fullpath,chrootrecord->cwd,chrootrecord->root);
    getcwd(wdir,PATH_MAX);
    if (chdir(fullpath)<0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_home",
               msg("do_home",0,confrecord->lang));
    }
    else if (userrecord->seclevel == HIGHSECURITY) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_home",
               msg("do_home",1,confrecord->lang));
    }
    else {
      mdefenv("HOME",fullpath);
      chrootpath(args[0],chrootrecord->cwd,(char *)NULL);
      strcpy(userrecord->home,args[0]);
    }
    chdir(wdir);
  }
  writetouser(confrecord,"Home: %s\n",userrecord->home);
}


void do_ls(const char params[], const chrootrecordtyp *chrootrecord,
           const confrecordtyp *confrecord)
{
  UID_T euid;
  GID_T egid;
  int n, anz;
  char *args[MAXARGS+1], dir[PATH_MAX+1], tstr[ASCIITIMESTRLEN],
       str[PATH_MAX+1], *sp, *sp2;
  DIR *dp;
  struct dirent *dirp;
  struct stat stats;
  tlistetyp *tliste;

  strmaxcpy(str,params,PATH_MAX);
  anz = splitparams(args,str);
  if (anz == 0) {
    strcpy(dir,".");
  }
  else {
    strcpy(dir,args[0]);
  }
  chrootpath(dir,chrootrecord->cwd,chrootrecord->root);
  
  if (stat(dir,&stats) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_ls","stat %s: %m",dir);
    return;
  }
  if (! S_ISDIR(stats.st_mode)) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_ls",
             "%s is not a directory",dir);
    return;
  }
  if ((dp=opendir(dir)) == NULL) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_ls","opendir %s: %m",dir);
    return;
  }

  euid = geteuid();
  egid = getegid();
  tliste = (tlistetyp *)0;
/*
  addstrtoliste(&tliste,TRUE,0,TL_BLOCKLEN,"",confrecord);
*/
  anz = 0;
  while ((dirp=readdir(dp)) != NULL) {
    if (strcmp(dirp->d_name,".") == 0) continue;
    if (strcmp(dirp->d_name,"..") == 0) continue;
    if (stat(dirp->d_name,&stats) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_ls","stat %s: %m",
               dirp->d_name);
      continue;
    }
    n = getperms(&stats,euid,egid);
    if (!(PERM_R & n)) continue;
    if (S_ISREG(stats.st_mode)) {
      strcpy(tstr,ctime(&(stats.st_mtime)));
      tstr[7] = '\0';
      tstr[10] = '\0';
      tstr[19] = '\0';
      tstr[24] = '\0';
      sprintf(str,"%s\t%s %s %s %8ld",dirp->d_name,
        &(tstr[8]),&(tstr[4]),&(tstr[20]),(long)stats.st_size);
      addstrtoliste(&tliste,TRUE,anz,TL_BLOCKLEN,str,confrecord);
      anz++;
    }
    else if (S_ISDIR(stats.st_mode) && (PERM_X & n)) {
      strcpy(str,dirp->d_name);
      strcat(str,"\t      (Directory)   ");
      addstrtoliste(&tliste,TRUE,anz,TL_BLOCKLEN,str,confrecord);
      anz++;
    }
    else {
      continue;
    }
  }
  sortliste(tliste,TL_BLOCKLEN,anz,confrecord);
  for (n=0; n<anz; n++) {
    readliste(&tliste,FALSE,n,TL_BLOCKLEN,&sp,confrecord);
    sp2 = sp;
    for (; *sp!='\t'; sp++) ;
    *sp++ = '\0';
    sprintf(str,"%s  %s",sp,sp2);
    addstrtoliste(&tliste,TRUE,n,TL_BLOCKLEN,str,confrecord);
  }
  tlpager(tliste,anz,"     Datum       Bytes    Name","dir",confrecord);
  removeliste(&tliste,TRUE,TL_BLOCKLEN);
  
  return;
}


void do_protokoll(const char params[], const char *protokollnamen[],
                  int *protokoll, confrecordtyp *confrecord)
{
  char c;
  
  c = params[0];
  c = tolower(c);
  if (c=='k') {
    if (confrecord->userrecord.kermitok) {
      *protokoll = KERMITPROTO;
      mputenv("PROTOKOLL=KERMIT");
    }
    else {
      writetouser(confrecord,msg("do_protokoll",0,
                  confrecord->userrecord.lang));
      return;
    }
  }
  else if (c=='z') {
    *protokoll = ZMODEMPROTO;
    mputenv("PROTOKOLL=ZMODEM");
  }
  confrecord->userrecord.protokoll = *protokoll;
  writetouser(confrecord,"Protokoll: %s\n",protokollnamen[*protokoll]);
}


void do_put(const char params[], const chrootrecordtyp *chrootrecord,
            const int protokoll, confrecordtyp *confrecord)
{
  int p, rw = -1;
  char *args[MAXARGS+1], *inargs[MAXARGS+1], str[PATH_MAX+1], path[PATH_MAX+1];
  
  getcwd(path,PATH_MAX);
  strmaxcpy(str,confrecord->userrecord.uploaddir,PATH_MAX);
  chrootpath(str,chrootrecord->cwd,chrootrecord->root);
  if (chdir(str) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_put","chdir %s: %m",
             confrecord->userrecord.uploaddir);
    return;
  }
  if (access(".",W_OK) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_put",
             msg("do_put",0,confrecord->lang), chrootrecord->cwd);
    if (chdir(path) < 0) {
      errormsg(E_LOGFILE,confrecord,"bbs","do_put","chdir (r) %s: %m",path);
    }
    return;
  }

  strcpy(str,params);
  splitparams(inargs,str);
  p = 0;
  switch (protokoll) {
    case KERMITPROTO:
      if ((errno=checkfileperms(confrecord->kermrcpath,geteuid(),getegid())) != 0) {
        if (errno < 0)  errno = -errno;
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_put",
                 "cannot access kermit configuration file\n%s:\n%m",
                 confrecord->kermrcpath);
        break;
      }
      args[p++] = "kermit";
      args[p++] = "-y";
      args[p++] = confrecord->kermrcpath;
      args[p++] = "-r";
      args[p] = NULL;
      rw = execute(KERMIT,args,confrecord->userrecord.loglevel,confrecord);
      break;
    case ZMODEMPROTO:
      args[p++] = "rz";
#ifndef NO_RZ_BFLAG
      args[p++] = "-b";
#endif
      args[p] = NULL;
      rw = execute(RZ,args,confrecord->userrecord.loglevel,confrecord);
      break;
  }
  if (chdir(path) < 0) {
    errormsg(E_LOGFILE,confrecord,"bbs","do_put","chdir (r) %s: %m",path);
  }
  if (rw == 0) {
    writetouser(confrecord,msg("do_put",1,confrecord->lang),
                confrecord->userrecord.uploaddir);
  }
  else {
    writetouser(confrecord,msg("do_put",2,confrecord->lang),
                confrecord->userrecord.uploaddir);
  }
  return;
}


void do_pwd(const char *chrootcwd, const confrecordtyp *confrecord)
{
  writetouser(confrecord,"Directory: %s",chrootcwd);
}


void do_showenv(const userrecordtyp *userrecord,
                const confrecordtyp *confrecord)
{
  char str[STRLEN+1], str1[STRLEN+1];

  sprintf(str1,"Username: %s\n", userrecord->name);
  strmaxcpy(str,str1,STRLEN);
  sprintf(str1,"Securitylevel: %d\n", userrecord->seclevel);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Protokoll: %s\n", protokollnamen[userrecord->protokoll]);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Auto-Z-Modem: %s\n", userrecord->autozmodem ? "on":"off");
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Fullist on cd: %s\n", userrecord->fullist_on_cd ? "yes":"no");
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Home: %s\n", userrecord->home);
  strmaxcat(str,str1,STRLEN);
  if (strcmp(userrecord->shell,NOSHELL)==0) {
    sprintf(str1,"No shell\n");
  }
  else if (strcmp(userrecord->shell,RESHELL)==0) {
    sprintf(str1,"'restricted' shell (%s)\n", RESHELL);
  }
  else {
    sprintf(str1,"Shell: %s\n", userrecord->shell);
  }
  strmaxcat(str,str1,STRLEN);
  if (userrecord->seclevel>=MEDIUMSECURITY) {
    sprintf(str1,"Path: %s\n", userrecord->path);
    strmaxcat(str,str1,STRLEN);
  }
  sprintf(str1,"Term: %s\n", userrecord->term);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Lines: %d\n", userrecord->lines);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Columns: %d\n", userrecord->columns);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Charset: %s\n", userrecord->charset);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Language: %s\n", sprachennamen[userrecord->lang]);
  strmaxcat(str,str1,STRLEN);
  sprintf(str1,"Permittions: %s\n", userrecord->permittions);
  strmaxcat(str,str1,STRLEN);
  writetouser(confrecord,str);
}


char do_showfile(const char *filepath, const char *header,
                 const char *chrootcwd, const char *chrootdir,
		 const confrecordtyp *confrecord)
{
  int fd, pfds[2], maxz, k, offset, zeilen, zstep, lang, cntrlchars, status;
  PID_T pid = 0;
  SIZE_T len, stsize;
  SSIZE_T bytes;
  char str[PATH_MAX+1], prompt[S_STRLEN+1], buf[BUFSIZE], *mmp, *cp, key,
       cascii;
  char cset[] = {SPACE_KEY,BACKSPACE_KEY,DELETE_KEY,QUIT_KEY,'\0'};
  char cset2[] = {'Y','J','N','\0'};
  struct stat stats;
  tlistetyp *tliste;
  
  lang = confrecord->userrecord.lang;
  strmaxcpy(str,filepath,PATH_MAX);
  if (chrootcwd != NULL) {
    chrootpath(str,chrootcwd,chrootdir);
  }
  switch (checkfileperms(str,geteuid(),getegid())) {
  case -EACCES:
  case -ENOENT:
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
             msg("do_showfile",0,lang),filepath);
    return '\0';
    break;
  case EFAULT:
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
             msg("do_showfile",1,lang),filepath);
    return '\0';
    break;
  case EACCES:
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
             msg("do_showfile",2,lang),filepath);
    return '\0';
    break;
  }
  
#ifdef GUNZIP
  len = strlen(str) - 1;
  if ((len>2 && str[len]=='z' && str[len-1]=='g' && str[len-2]=='.') ||
       (len>1 && str[len]=='Z' && str[len-1]=='.')) {
    pipe(pfds);
    if ((pid = fork()) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile","fork: %m");
      return '\0';
    }
    if (pid == 0) {
      close(pfds[0]);
      dup2(pfds[1],STDOUT_FILENO);
      execl(GUNZIP,"gunzip","-c",str,(char *)0);
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
               "execl GUNZIP -c %s: %m",str);
      exit(1);
    }
    close(pfds[1]);
    sprintf(str,"%s/gunzip.%ld",confrecord->scratchdir,(long)getpid());
    if ((fd=open(str,O_RDWR|O_CREAT|O_EXCL,0600)) < 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
               "open %s: %m",str);
      return '\0';
    }
    bytes = 0;
    while (bytes >= 0 && (bytes=read(pfds[0],(void *)buf,(SIZE_T)BUFSIZE)) > 0) {
      bytes = writen(fd,buf,bytes);
    }
    close(pfds[0]);
    close(fd);
    while (wait(&status) != pid);
    if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
		"gunzip exited with status %d",WEXITSTATUS(status));
    }
    else if (WIFSIGNALED(status)) {
      errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
		"gunzip got signal %d",WTERMSIG(status));
      if (WCOREDUMP(status)) {
	errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_sendmail",
		  "gunzip dumped core");
      }
    }
  }
#endif
  if ((fd=open(str,O_RDONLY)) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "open %s: %m",str);
    return '\0';
  }
  if (pid > 0)  unlink(str);
  if (fstat(fd,&stats) < 0) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "fstat %s: %m",str);
    return '\0';
  }
  stsize = stats.st_size;
#ifdef NO_MMAP
# ifdef NeXT
  mmp = (char *)0;
  if (map_fd(fd,(vm_offset_t)0,(vm_offset_t *)&mmp,TRUE,
      (vm_size_t)stsize) != KERN_SUCCESS) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "map_fd: failed");
    return '\0';
  } 
# else
  if ((mmp=(char *)malloc(stsize)) == NULL) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "malloc: %m");
    return '\0';
  }
  if (readn(fd,(void *)mmp,stsize) != stsize) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "read %s: %m",str);
    return '\0';
  }
# endif
#else
  if ((mmp=(char *)mmap(0,stsize,PROT_READ,MAP_FILE|MAP_SHARED,fd,0)) == (CADDR_T) -1) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "mmap %s: %m",str);
    return '\0';
  }
#endif

  key = 'N';
  for (k=0, cp=mmp, cntrlchars=0; k<stsize; k++) {
    if (!(isprint(*cp) || isspace(*cp)))  cntrlchars++;
    cp++;
  }
  if (cntrlchars*BINARYFACTOR > stsize) {
    key = getkeyonprompt(msg("do_showfile",7,lang),cset2,TRUE,FALSE,
                         "showfile",confrecord);
  }

  if (key == 'N') {
    cascii = 'U';
    if (cntrlchars > 0) {
      if (strcmp(confrecord->userrecord.charset,"?") == 0) {
        key = getkeyonprompt(msg("do_showfile",8,lang),cset2,TRUE,FALSE,
  	  		     "showfile",confrecord);
      }
      if (key == 'J' || key == 'Y' ||
               strcasecmp(confrecord->userrecord.charset,"ascii") == 0) {
        cascii = 'A';
      }
      else if (strcasecmp(confrecord->userrecord.charset,"next") == 0) {
        cascii = 'N';
      }
    }
    cp = mmp;
    tliste = (tlistetyp *)0;
    zeilen = 0;
    do {
      addstrtoliste(&tliste,FALSE,zeilen++,TL_BLOCKLEN,cp,confrecord);
      for (k=0; *cp!='\n' && k<COLS && (OFF_T)(cp-mmp)<stsize; cp++) {
        k += strlen(recode(str,*cp,cascii));
      }
      if (*cp=='\n' && (OFF_T)(cp-mmp)<stsize) {
        cp++;
      }
    } while ((OFF_T)(cp-mmp)<stsize);
    move(0,0);
    clear();
    if (header != NULL) {
      maxz = confrecord->userrecord.lines - 2;
      standout();
      addstr_withmargin(header);
      standend();
    }
    else {
      maxz = confrecord->userrecord.lines - 1;
    }
    zstep = maxz - 2;
    offset = 0;
    do {
      if (header != NULL) {
        move(1,0);
        clrtobot();
      }
      else {
        move(0,0);
	clear();
      }
      for (k=offset; k<offset+maxz && k<zeilen; k++) {
	readliste(&tliste,FALSE,k,TL_BLOCKLEN,&cp,confrecord);
	linerecodeout(cp,mmp,stsize,cascii,COLS);
      }
      if (zeilen > maxz) {
	if (offset+maxz < zeilen) {
	  if (offset == 0) {
	    strcpy(prompt,msg("do_showfile",4,lang));
	  }
	  else {
	    sprintf(prompt,msg("do_showfile",5,lang),offset);
	  }
	}
	else {
	  strcpy(prompt,msg("do_showfile",3,lang));
	}
      }
      else {
	strcpy(prompt,msg("do_showfile",6,lang));
      }
      key = getkeyonprompt(prompt,cset,TRUE,FALSE,"showfile",confrecord);
      if (pagerstep(key) < 0) {
	offset -= zstep;
      }
      else if (pagerstep(key) > 0) {
	offset += zstep;
      }
      if (offset > zeilen-maxz) offset = zeilen-maxz;
      if (offset < 0) offset = 0;
    } while (pagerquit(key) == 0);

    removeliste(&tliste,FALSE,TL_BLOCKLEN);
  }
  
#ifdef NO_MMAP
# ifdef NeXT
  if (vm_deallocate(task_self(),(vm_address_t)mmp,(vm_size_t)stsize) !=
      KERN_SUCCESS) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_showfile",
	      "vm_deallocate: failed");
  }
# else
  free(mmp);
# endif
#else
  munmap(mmp,stsize);
#endif
  close(fd);
  return key;
}


int do_term(const char params[], userrecordtyp *userrecord,
             confrecordtyp *confrecord)
{
  int n;
  boolean size_changed=FALSE;
  char *args[MAXARGS+1], str[PATH_MAX+1];
#ifdef TIOCGWINSZ
  char tgetentbuf[TGETENTBUF];
  struct winsize termsize;
#endif
  
  strmaxcpy(str,params,PATH_MAX);
  splitparams(args,str);
  if (args[0]!=(char *)NULL) {
    if (strcmp(args[0],"*")!=0) {
      if (! termexists(args[0])) {
        errormsg(E_LOGFILE|E_USER|E_CONSOLE,confrecord,NULL,"do_term",
                 "Terminaltype %s does not exist",args[0]);
        return(-1);
      }
      strmaxcpy(userrecord->term,args[0],S_STRLEN);
      mdefenv("TERM",args[0]);
    }
    if (args[1]!=(char *)NULL) {
      if (isdigit(args[1][0])) {
        n = atoi(args[1]);
        if (n>0 && n<1000) {
          userrecord->lines = n;
       	  mdefenv("LINES","%d",n);
	  size_changed = TRUE;
        }
      }
      if (args[2]!=(char *)NULL) {
        if (isdigit(args[2][0])) {
          n = atoi(args[2]);
          if (n>0 && n<1000) {
            userrecord->columns = n;
 	    mdefenv("COLUMNS","%d",n);
	    size_changed = TRUE;
          }
	}
      }
    }
  }
  if (confrecord->curses_on) {
#ifdef TIOCGWINSZ
    if (size_changed) {
      ioctl(STDIN_FILENO,TIOCGWINSZ, (char *)&termsize);
      termsize.ws_row = userrecord->lines;
      termsize.ws_col = userrecord->columns;
      ioctl(STDIN_FILENO,TIOCSWINSZ, (char *)&termsize);
      tgetent(tgetentbuf,userrecord->term);
      endwin();
      confrecord->curses_on = FALSE;
      window_on(&save_termios,&ttystate,confrecord);
    }
#endif
    writetouser(confrecord,"Term: %s\nLines: %d\nColumns: %d\nCharset: %s\n",
                userrecord->term,userrecord->lines, userrecord->columns,
                userrecord->charset);
  }
  return(0);
}


int do_newpasswd(const char params[], userrecordtyp *userrecord,
                 const confrecordtyp *confrecord)
{
  boolean passwdchanged=FALSE;
  char *args[MAXARGS+1], str[S_STRLEN+PASS_MAX+2], salt[9];
  userrecordtyp urec;
#ifndef NO_CRYPT
  void to64(char *, long, int);
#endif
  
  strmaxcpy(str,params,S_STRLEN+PASS_MAX+1);
  if (splitparams(args,str) != 2) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_newpasswd",
             "wrong number of args");
    return(-1);
  }
  if (strlen(args[1]) > PASS_MAX) {
    errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_newpasswd",
             "password too long");
    return(-1);
  }

  if (userrecord->seclevel != HIGHSECURITY) {
#ifndef NO_CRYPT
    SRANDOM(time((TIME_T)0));
# ifdef NEWSALT
    salt[0] = _PASSWORD_EFMT1;
    to64(&salt[1], (long)(29 * 25), 4);
    to64(&salt[5], RANDOM(), 4);
# else
    to64(&salt[0], RANDOM(), 2);
# endif
#endif
    if (strcmp(userrecord->name,args[0]) == 0) {
      strmaxcpy(userrecord->passwd,CRYPTFUNC(args[1],salt),PASSCRYPTED_LEN);
      saveuserrecord(userrecord,confrecord);
      passwdchanged = TRUE;
    }
    else if (strcmp(userrecord->name,confrecord->sysop) == 0) {
      if (! getuserrecord(&urec,args[0],confrecord)) {
        errormsg(E_LOGFILE|E_USER,confrecord,"bbs","do_newpasswd",
	         msg("do_newpasswd",0,userrecord->lang),args[0]);
        return(-1);
      }
      strmaxcpy(urec.passwd,CRYPTFUNC(args[1],salt),PASSCRYPTED_LEN);
      saveuserrecord(&urec,confrecord);
      passwdchanged = TRUE;
    }
  }
  
  if (passwdchanged) {
    bbslog(LOG_FILE,confrecord,msg("do_newpasswd",1,userrecord->lang),
           args[0],userrecord->name);
    writetouser(confrecord,msg("do_newpasswd",2,userrecord->lang));
  }
  else {
    writetouser(confrecord,msg("do_newpasswd",3,userrecord->lang));
  }
  return(0);
}

#ifndef NO_CRYPT
void to64(char *s, long v, int n)
{
  static U_CHAR itoa64[] =
  "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  while (--n >= 0) {
    *s++ = itoa64[v&0x3f];
    v >>= 6;
  }
}
#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.