text: /* ========================================================================
   * Copyright 2008-2010 Mark Crispin
   * ========================================================================
   */
  
  /*
   * Program:	UNIX environment routines
   *
   * Author:	Mark Crispin
   *
   * Date:	1 August 1988
   * Last Edited:	15 November 2010
   *
   * Previous versions of this file were
   *
   * Copyright 1988-2008 University of Washington
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   */
  
  #include <grp.h>
  #include <signal.h>
  #include <sys/wait.h>
  
  
  /* in case stat.h is ancient */
  
  #ifndef S_IRUSR
  #define S_IRUSR S_IREAD
  #endif
  #ifndef S_IWUSR
  #define S_IWUSR S_IWRITE
  #endif
  #ifndef S_IXUSR
  #define S_IXUSR S_IEXEC
  #endif
  #ifndef S_IRGRP
  #define S_IRGRP (S_IREAD >> 3)
  #endif
  #ifndef S_IWGRP
  #define S_IWGRP (S_IWRITE >> 3)
  #endif
  #ifndef S_IXGRP
  #define S_IXGRP (S_IEXEC >> 3)
  #endif
  #ifndef S_IROTH
  #define S_IROTH (S_IREAD >> 6)
  #endif
  #ifndef S_IWOTH
  #define S_IWOTH (S_IWRITE >> 6)
  #endif
  #ifndef S_IXOTH
  #define S_IXOTH (S_IEXEC >> 6)
  #endif
  
  /* c-client environment parameters */
  
  static char *myUserName = NIL;	/* user name */
  static char *myHomeDir = NIL;	/* home directory name */
  static char *myServerName = NIL;/* server name */
  static char *myLocalHost = NIL;	/* local host name */
  static char *myNewsrc = NIL;	/* newsrc file name */
  static char *mailsubdir = NIL;	/* mailbox subdirectory name */
  static char *sysInbox = NIL;	/* system inbox name */
  static char *newsActive = NIL;	/* news active file */
  static char *newsSpool = NIL;	/* news spool */
  static char *blackBoxDir = NIL;	/* black box directory name */
  				/* black box default home directory */
  static char *blackBoxDefaultHome = NIL;
  static char *sslCApath = NIL;	/* non-standard CA path */
  static short anonymous = NIL;	/* is anonymous */
  static short blackBox = NIL;	/* is a black box */
  static short closedBox = NIL;	/* is a closed box (uses chroot() jail) */
  static long restrictBox = NIL;	/* is a restricted box */
  static short has_no_life = NIL;	/* is a cretin with no life */
  				/* block environment init */
  static short block_env_init = NIL;
  static short hideDotFiles = NIL;/* hide files whose names start with . */
  				/* advertise filesystem root */
  static short advertisetheworld = NIL;
  				/* only advertise own mailboxes and #shared */
  static short limitedadvertise = NIL;
  				/* disable automatic shared namespaces */
  static short noautomaticsharedns = NIL;
  static short no822tztext = NIL;	/* disable RFC [2]822 timezone text */
  				/* client principals include service name */
  static short kerb_cp_svr_name = NIL;
  static long locktimeout = 5;	/* default lock timeout in minutes */
  				/* default prototypes */
  static MAILSTREAM *createProto = NIL;
  static MAILSTREAM *appendProto = NIL;
  				/* default user flags */
  static char *userFlags[NUSERFLAGS] = {NIL};
  static NAMESPACE *nslist[3];	/* namespace list */
  static int logtry = 3;		/* number of server login tries */
  				/* block notification */
  static blocknotify_t mailblocknotify = mm_blocknotify;
  				/* logout function */
  static logouthook_t maillogouthook = NIL;
  				/* logout data */
  static void *maillogoutdata = NIL;
  				/* allow user config files */
  static short allowuserconfig = NIL;
  				/* 1 = disable plaintext, 2 = if not SSL */
  static long disablePlaintext = NIL;
  static long list_max_level = 20;/* maximum level of list recursion */
  				/* facility for syslog */
  static int syslog_facility = LOG_MAIL;
  
  /* Path of the privileged system lock program (mlock).  Normally set by
   * logic test.
   */
  
  static char *lockpgm = LOCKPGM;
  
  /* Directory used for shared locks.  MUST be the same for all users of the
   * system, and MUST be protected 1777.  /var/tmp may be preferable on some
   * systems.
   */
  
  static const char *tmpdir = "/tmp";
  
  /* Do not change shlock_mode.  Doing so can cause mailbox corruption and
   * denial of service.  It also defeats the entire purpose of the shared
   * lock mechanism.  The right way to avoid shared locks is to set up a
   * closed box (see the closedBox setting).
   */
  
  				/* shared lock mode */
  static const int shlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
  
  
  /* It is STRONGLY recommended that you do not change dotlock_mode.  Doing so
   * can cause denial of service with old dot-lock files left lying around.
   * However, since dot-locks are only used with traditional UNIX and MMDF
   * formats which are not normally shared, it is much less harmful to tamper
   * with this than with shlock_mode.
   */
  
  				/* dot-lock mode */
  static long dotlock_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
  
  /* File/directory access and protection policies */
  
  /* Unlike shlock_mode, the ????_protection modes are intended to be fully
   * customizable according to site policy.  The values here are recommended
   * settings, based upon the documented purposes of the namespaces.
   */
  
  	/* user space - only owner can read/write */
  static char *myMailboxDir = NIL;/* user space directory name */
  				/* default file protection */
  static long mbx_protection = S_IRUSR|S_IWUSR;
  				/* default directory protection */
  static long dir_protection = S_IRUSR|S_IWUSR|S_IXUSR;
  
  	/* user space for user "anonymous" */
  				/* anonymous home directory */
  static char *anonymousHome = NIL;
  
  	/* #ftp - everybody can read, only owner can write */
  static char *ftpHome = NIL;	/* ftp export home directory */
  				/* default ftp file protection */
  static long ftp_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
  static long ftp_dir_protection =/* default ftp directory protection */
    S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
  
  	/* #public - everybody can read/write */
  static char *publicHome = NIL;	/* public home directory */
  static long public_protection =	/* default public file protection */
    S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
  				/* default public directory protection */
  static long public_dir_protection =
    S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH;
  
  	/* #shared/ - owner and group members can read/write */
  static char *sharedHome = NIL;	/* shared home directory */
  				/* default shared file protection */
  static long shared_protection = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
  				/* default shared directory protection */
  static long shared_dir_protection =
    S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP;
  
  /* OS bug workarounds - should be avoided at all cost */
  
  
  /* Don't set fcntlhangbug unless you really have to, since it risks mailbox
   * corruption.  The flocksim.c mechanism is designed to detect NFS access
   * and no-op in that cases only, so this flag should be unnecessary.
   */
  
  static short fcntlhangbug = NIL;/* flock() emulator using fcntl() is a no-op */
  
  
  /* Don't set netfsstatbug unless you really have to, since it dramatically
   * slows down traditional UNIX and MMDF mailbox performance.
   */
  
  static short netfsstatbug = NIL;/* compensate for broken stat() on network
  				 * filesystems (AFS and old NFS)
  				 */
  
  
  /* Note: setting disableLockWarning means that you assert that the
   * so-modified copy of this software will NEVER be used:
   *  1) in conjunction with any software which expects .lock files
   *  2) to access NFS-mounted files and directories
   *
   * Unless both of these conditions apply, then do not set this flag.
   * Instead, read the FAQ (item 7.10) and either use 1777 protection
   * on the mail spool, or install mlock.
   *
   * In addition, by setting this flag you also agree that you are fully
   * legally and morally responsible when (not if) mail files are damaged
   * as the result of your choice.
   *
   * The mlock tool exists for a reason.  Use it.
   */
  				/* disable warning if can't make .lock file */
  static short disableLockWarning = NIL;
  
  /* UNIX Namespaces */
  
  				/* personal mh namespace */
  static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
  static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
  				/* home namespace */
  static NAMESPACE nshome = {"",'/',NIL,&nsmh};
  				/* UNIX other user namespace */
  static NAMESPACE nsunixother = {"~",'/',NIL,NIL};
  				/* black box other user namespace */
  static NAMESPACE nsblackother = {"/",'/',NIL,NIL};
  				/* public (anonymous OK) namespace */
  static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
  				/* netnews namespace */
  static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
  				/* FTP export namespace */
  static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
  				/* shared (no anonymous) namespace */
  static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
  				/* world namespace */
  static NAMESPACE nsworld = {"/",'/',NIL,&nsshared};
  				/* only shared and public namespaces */
  static NAMESPACE nslimited = {"#shared/",'/',NIL,&nspublic};
  
  
  
  #include "write.c"		/* include safe writing routines */
  #include "crexcl.c"		/* include exclusive create */
  #include "pmatch.c"		/* include wildcard pattern matcher */
  
  /* Get all authenticators */
  
  #include "auths.c"
  
  /* Environment manipulate parameters
   * Accepts: function code
   *	    function-dependent value
   * Returns: function-dependent return value
   */
  
  void *env_parameters (long function,void *value)
  {
    void *ret = NIL;
    switch ((int) function) {
    case GET_NAMESPACE:
      ret = (void *) nslist;
      break;
    case SET_USERNAME:
      if (myUserName) fs_give ((void **) &myUserName);
      myUserName = cpystr ((char *) value);
    case GET_USERNAME:
      ret = (void *) myUserName;
      break;
    case SET_HOMEDIR:
      if (myHomeDir) fs_give ((void **) &myHomeDir);
      myHomeDir = cpystr ((char *) value);
    case GET_HOMEDIR:
      ret = (void *) myHomeDir;
      break;
    case SET_LOCALHOST:
      if (myLocalHost) fs_give ((void **) &myLocalHost);
      myLocalHost = cpystr ((char *) value);
    case GET_LOCALHOST:
      ret = (void *) myLocalHost;
      break;
    case SET_NEWSRC:
      if (myNewsrc) fs_give ((void **) &myNewsrc);
      myNewsrc = cpystr ((char *) value);
    case GET_NEWSRC:
      ret = (void *) myNewsrc;
      break;
    case SET_NEWSACTIVE:
      if (newsActive) fs_give ((void **) &newsActive);
      newsActive = cpystr ((char *) value);
    case GET_NEWSACTIVE:
      ret = (void *) newsActive;
      break;
    case SET_NEWSSPOOL:
      if (newsSpool) fs_give ((void **) &newsSpool);
      newsSpool = cpystr ((char *) value);
    case GET_NEWSSPOOL:
      ret = (void *) newsSpool;
      break;
  
    case SET_ANONYMOUSHOME:
      if (anonymousHome) fs_give ((void **) &anonymousHome);
      anonymousHome = cpystr ((char *) value);
    case GET_ANONYMOUSHOME:
      if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
      ret = (void *) anonymousHome;
      break;
    case SET_FTPHOME:
      if (ftpHome) fs_give ((void **) &ftpHome);
      ftpHome = cpystr ((char *) value);
    case GET_FTPHOME:
      ret = (void *) ftpHome;
      break;
    case SET_PUBLICHOME:
      if (publicHome) fs_give ((void **) &publicHome);
      publicHome = cpystr ((char *) value);
    case GET_PUBLICHOME:
      ret = (void *) publicHome;
      break;
    case SET_SHAREDHOME:
      if (sharedHome) fs_give ((void **) &sharedHome);
      sharedHome = cpystr ((char *) value);
    case GET_SHAREDHOME:
      ret = (void *) sharedHome;
      break;
    case SET_SYSINBOX:
      if (sysInbox) fs_give ((void **) &sysInbox);
      sysInbox = cpystr ((char *) value);
    case GET_SYSINBOX:
      ret = (void *) sysInbox;
      break;
    case SET_SSLCAPATH:		/* this can be set null */
      if (sslCApath) fs_give ((void **) &sslCApath);
      sslCApath = value ? cpystr ((char *) value) : value;
      break;
    case GET_SSLCAPATH:
      ret = (void *) sslCApath;
      break;
    case SET_LISTMAXLEVEL:
      list_max_level = (long) value;
    case GET_LISTMAXLEVEL:
      ret = (void *) list_max_level;
      break;
  
    case SET_MBXPROTECTION:
      mbx_protection = (long) value;
    case GET_MBXPROTECTION:
      ret = (void *) mbx_protection;
      break;
    case SET_DIRPROTECTION:
      dir_protection = (long) value;
    case GET_DIRPROTECTION:
      ret = (void *) dir_protection;
      break;
    case SET_LOCKPROTECTION:
      dotlock_mode = (long) value;
    case GET_LOCKPROTECTION:
      ret = (void *) dotlock_mode;
      break;
    case SET_FTPPROTECTION:
      ftp_protection = (long) value;
    case GET_FTPPROTECTION:
      ret = (void *) ftp_protection;
      break;
    case SET_PUBLICPROTECTION:
      public_protection = (long) value;
    case GET_PUBLICPROTECTION:
      ret = (void *) public_protection;
      break;
    case SET_SHAREDPROTECTION:
      shared_protection = (long) value;
    case GET_SHAREDPROTECTION:
      ret = (void *) shared_protection;
      break;
    case SET_FTPDIRPROTECTION:
      ftp_dir_protection = (long) value;
    case GET_FTPDIRPROTECTION:
      ret = (void *) ftp_dir_protection;
      break;
    case SET_PUBLICDIRPROTECTION:
      public_dir_protection = (long) value;
    case GET_PUBLICDIRPROTECTION:
      ret = (void *) public_dir_protection;
      break;
    case SET_SHAREDDIRPROTECTION:
      shared_dir_protection = (long) value;
    case GET_SHAREDDIRPROTECTION:
      ret = (void *) shared_dir_protection;
      break;
    case SET_RESTRICTIONS:
      restrictBox = (long) value;
    case GET_RESTRICTIONS:
      ret = (void *) restrictBox;
  
    case SET_LOCKTIMEOUT:
      locktimeout = (long) value;
    case GET_LOCKTIMEOUT:
      ret = (void *) locktimeout;
      break;
    case SET_DISABLEFCNTLLOCK:
      fcntlhangbug = value ? T : NIL;
    case GET_DISABLEFCNTLLOCK:
      ret = (void *) (fcntlhangbug ? VOIDT : NIL);
      break;
    case SET_LOCKEACCESERROR:
      disableLockWarning = value ? NIL : T;
    case GET_LOCKEACCESERROR:
      ret = (void *) (disableLockWarning ? NIL : VOIDT);
      break;
    case SET_HIDEDOTFILES:
      hideDotFiles = value ? T : NIL;
    case GET_HIDEDOTFILES:
      ret = (void *) (hideDotFiles ? VOIDT : NIL);
      break;
    case SET_DISABLEPLAINTEXT:
      disablePlaintext = (long) value;
    case GET_DISABLEPLAINTEXT:
      ret = (void *) disablePlaintext;
      break;
    case SET_CHROOTSERVER:
      closedBox = value ? T : NIL;
    case GET_CHROOTSERVER:
      ret = (void *) (closedBox ? VOIDT : NIL);
      break;
    case SET_ADVERTISETHEWORLD:
      advertisetheworld = value ? T : NIL;
    case GET_ADVERTISETHEWORLD:
      ret = (void *) (advertisetheworld ? VOIDT : NIL);
      break;
    case SET_LIMITEDADVERTISE:
      limitedadvertise = value ? T : NIL;
    case GET_LIMITEDADVERTISE:
      ret = (void *) (limitedadvertise ? VOIDT : NIL);
      break;
    case SET_DISABLEAUTOSHAREDNS:
      noautomaticsharedns = value ? T : NIL;
    case GET_DISABLEAUTOSHAREDNS:
      ret = (void *) (noautomaticsharedns ? VOIDT : NIL);
      break;
    case SET_DISABLE822TZTEXT:
      no822tztext = value ? T : NIL;
    case GET_DISABLE822TZTEXT:
      ret = (void *) (no822tztext ? VOIDT : NIL);
      break;
  
    case SET_USERHASNOLIFE:
      has_no_life = value ? T : NIL;
    case GET_USERHASNOLIFE:
      ret = (void *) (has_no_life ? VOIDT : NIL);
      break;
    case SET_KERBEROS_CP_SVR_NAME:
      kerb_cp_svr_name = value ? T : NIL;
    case GET_KERBEROS_CP_SVR_NAME:
      ret = (void *) (kerb_cp_svr_name ? VOIDT : NIL);
      break;
    case SET_NETFSSTATBUG:
      netfsstatbug = value ? T : NIL;
    case GET_NETFSSTATBUG:
      ret = (void *) (netfsstatbug ? VOIDT : NIL);
      break;
    case SET_BLOCKENVINIT:
      block_env_init = value ? T : NIL;
    case GET_BLOCKENVINIT:
      ret = (void *) (block_env_init ? VOIDT : NIL);
      break;
    case SET_BLOCKNOTIFY:
      mailblocknotify = (blocknotify_t) value;
    case GET_BLOCKNOTIFY:
      ret = (void *) mailblocknotify;
      break;
    case SET_LOGOUTHOOK:
      maillogouthook = (logouthook_t) value;
    case GET_LOGOUTHOOK:
      ret = maillogouthook;
      break;
    case SET_LOGOUTDATA:
      maillogoutdata = (void *) value;
    case GET_LOGOUTDATA:
      ret = maillogoutdata;
    }
    return ret;
  }
  
  /* Write current time
   * Accepts: destination string
   *	    optional format of day-of-week prefix
   *	    format of date and time
   *	    flag whether to append symbolic timezone
   */
  
  static void do_date (char *date,char *prefix,char *fmt,int suffix)
  {
    time_t tn = time (0);
    struct tm *t = gmtime (&tn);
    int zone = t->tm_hour * 60 + t->tm_min;
    int julian = t->tm_yday;
    t = localtime (&tn);		/* get local time now */
  				/* minus UTC minutes since midnight */
    zone = t->tm_hour * 60 + t->tm_min - zone;
    /* julian can be one of:
     *  36x  local time is December 31, UTC is January 1, offset -24 hours
     *    1  local time is 1 day ahead of UTC, offset +24 hours
     *    0  local time is same day as UTC, no offset
     *   -1  local time is 1 day behind UTC, offset -24 hours
     * -36x  local time is January 1, UTC is December 31, offset +24 hours
     */
    if (julian = t->tm_yday -julian)
      zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
    if (prefix) {			/* want day of week? */
      sprintf (date,prefix,days[t->tm_wday]);
      date += strlen (date);	/* make next sprintf append */
    }
  				/* output the date */
    sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  	   t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
  				/* append timezone suffix if desired */
    if (suffix) rfc822_timezone (date,(void *) t);
  }
  
  /* Write current time in RFC 822 format
   * Accepts: destination string
   */
  
  void rfc822_date (char *date)
  {
    do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",
  	   no822tztext ? NIL : T);
  }
  
  
  /* Write current time in fixed-width RFC 822 format
   * Accepts: destination string
   */
  
  void rfc822_fixed_date (char *date)
  {
    do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
  }
  
  
  /* Write current time in internal format
   * Accepts: destination string
   */
  
  void internal_date (char *date)
  {
    do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
  }
  
  /* Initialize server
   * Accepts: server name for syslog or NIL
   *	    /etc/services service name or NIL
   *	    alternate /etc/services service name or NIL
   *	    clock interrupt handler
   *	    kiss-of-death interrupt handler
   *	    hangup interrupt handler
   *	    termination interrupt handler
   */
  
  void server_init (char *server,char *service,char *sslservice,
  		  void *clkint,void *kodint,void *hupint,void *trmint,
  		  void *staint)
  {
    int onceonly = server && service && sslservice;
    if (onceonly) {		/* set server name in syslog */
      int mask;
      openlog (myServerName = cpystr (server),LOG_PID,syslog_facility);
      fclose (stderr);		/* possibly save a process ID */
      dorc (NIL,NIL);		/* do systemwide configuration */
      switch (mask = umask (022)){/* check old umask */
      case 0:			/* definitely unreasonable */
      case 022:			/* don't need to change it */
        break;
      default:			/* already was a reasonable value */
        umask (mask);		/* so change it back */
      }
    }
    arm_signal (SIGALRM,clkint);	/* prepare for clock interrupt */
    arm_signal (SIGUSR2,kodint);	/* prepare for Kiss Of Death */
    arm_signal (SIGHUP,hupint);	/* prepare for hangup */
    arm_signal (SIGPIPE,hupint);	/* alternative hangup */
    arm_signal (SIGTERM,trmint);	/* prepare for termination */
  				/* status dump */
    if (staint) arm_signal (SIGUSR1,staint);
    if (onceonly) {		/* set up network and maybe SSL */
      long port;
      struct servent *sv;
      /* Use SSL if SSL service, or if server starts with "s" and not service */
      if (((port = tcp_serverport ()) >= 0)) {
        if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
  	syslog (LOG_DEBUG,"%s service init from %s",service,tcp_clientaddr ());
        else if ((sv = getservbyname (sslservice,"tcp")) &&
  	       (port == ntohs (sv->s_port))) {
  	syslog (LOG_DEBUG,"%s SSL service init from %s",sslservice,
  		tcp_clientaddr ());
  	ssl_server_init (server);
        }
        else {			/* not service or SSL service port */
  	syslog (LOG_DEBUG,"port %ld service init from %s",port,
  		tcp_clientaddr ());
  	if (*server == 's') ssl_server_init (server);
        }
      }
    }
  }
  
  /* Wait for stdin input
   * Accepts: timeout in seconds
   * Returns: T if have input on stdin, else NIL
   */
  
  long server_input_wait (long seconds)
  {
    int err;
    fd_set rfd,efd;
    struct timeval tmo;
    do {
      FD_ZERO (&rfd);
      FD_ZERO (&efd);
      FD_SET (0,&rfd);
      FD_SET (0,&efd);
      tmo.tv_sec = seconds; tmo.tv_usec = 0;
    } while (((err = select (1,&rfd,0,&efd,&tmo)) < 0) && (errno = EINTR));
    return err ? LONGT : NIL;
  }
  
  /* Return UNIX password entry for user name
   * Accepts: user name string
   * Returns: password entry
   *
   * Tries all-lowercase form of user name if given user name fails
   */
  
  static struct passwd *pwuser (unsigned char *user)
  {
    unsigned char *s;
    struct passwd *pw = getpwnam (user);
    if (!pw) {			/* failed, see if any uppercase characters */
      for (s = user; *s && ((*s < 'A') || (*s > 'Z')); s++);
      if (*s) {			/* yes, try all lowercase form */
        pw = getpwnam (s = lcase (cpystr (user)));
        fs_give ((void **) &s);
      }
    }
    return pw;
  }
  
  
  /* Validate password for user name
   * Accepts: user name string
   *	    password string
   *	    argument count
   *	    argument vector
   * Returns: password entry if validated
   *
   * Tries password+1 if password fails and starts with space
   */
  
  static struct passwd *valpwd (char *user,char *pwd,int argc,char *argv[])
  {
    char *s;
    struct passwd *pw;
    struct passwd *ret = NIL;
    if (auth_md5.server) {	/* using CRAM-MD5 authentication? */
      if (s = auth_md5_pwd (user)) {
        if (!strcmp (s,pwd) || ((*pwd == ' ') && pwd[1] && !strcmp (s,pwd+1)))
  	ret = pwuser (user);	/* validated, get passwd entry for user */
        memset (s,0,strlen (s));	/* erase sensitive information */
        fs_give ((void **) &s);
      }
    }
    else if (pw = pwuser (user)) {/* can get user? */
      s = cpystr (pw->pw_name);	/* copy returned name in case we need it */
      if (*pwd && !(ret = checkpw (pw,pwd,argc,argv)) &&
  	(*pwd == ' ') && pwd[1] && (ret = pwuser (s)))
        ret = checkpw (pw,pwd+1,argc,argv);
      fs_give ((void **) &s);	/* don't need copy of name any more */
    }
    return ret;
  }
  
  /* Server log in
   * Accepts: user name string
   *	    password string
   *	    authenticating user name string
   *	    argument count
   *	    argument vector
   * Returns: T if password validated, NIL otherwise
   */
  
  long server_login (char *user,char *pwd,char *authuser,int argc,char *argv[])
  {
    struct passwd *pw = NIL;
    int level = LOG_NOTICE;
    char *err = "failed";
  				/* cretins still haven't given up */
    if ((strlen (user) >= NETMAXUSER) ||
        (authuser && (strlen (authuser) >= NETMAXUSER))) {
      level = LOG_ALERT;		/* escalate this alert */
      err = "SYSTEM BREAK-IN ATTEMPT";
      logtry = 0;			/* render this session useless */
    }
    else if (logtry-- <= 0) err = "excessive login failures";
    else if (disablePlaintext) err = "disabled";
    else if (!(authuser && *authuser)) pw = valpwd (user,pwd,argc,argv);
    else if (valpwd (authuser,pwd,argc,argv)) pw = pwuser (user);
    if (pw && pw_login (pw,authuser,pw->pw_name,NIL,argc,argv)) return T;
    syslog (level|LOG_AUTH,"Login %s user=%.64s auth=%.64s host=%.80s",err,
  	  user,(authuser && *authuser) ? authuser : user,tcp_clienthost ());
    sleep (3);			/* slow down possible cracker */
    return NIL;
  }
  
  /* Authenticated server log in
   * Accepts: user name string
   *	    authenticating user name string
   *	    argument count
   *	    argument vector
   * Returns: T if password validated, NIL otherwise
   */
  
  long authserver_login (char *user,char *authuser,int argc,char *argv[])
  {
    return pw_login (pwuser (user),authuser,user,NIL,argc,argv);
  }
  
  
  /* Log in as anonymous daemon
   * Accepts: argument count
   *	    argument vector
   * Returns: T if successful, NIL if error
   */
  
  long anonymous_login (int argc,char *argv[])
  {
  				/* log in Mr. A. N. Onymous */
    return pw_login (getpwnam (ANONYMOUSUSER),NIL,NIL,
  		   (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL),
  		   argc,argv);
  }
  
  /* Finish log in and environment initialization
   * Accepts: passwd struct for loginpw()
   *	    optional authentication user name
   *	    user name (NIL for anonymous)
   *	    home directory (NIL to use directory from passwd struct)
   *	    argument count
   *	    argument vector
   * Returns: T if successful, NIL if error
   */
  
  long pw_login (struct passwd *pw,char *auser,char *user,char *home,int argc,
  	       char *argv[])
  {
    struct group *gr;
    char **t;
    long ret = NIL;
    if (pw && pw->pw_uid) {	/* must have passwd struct for non-UID 0 */
  				/* make safe copies of user and home */
      if (user) user = cpystr (pw->pw_name);
      home = cpystr (home ? home : pw->pw_dir);
  				/* authorization ID .NE. authentication ID? */
      if (user && auser && *auser && compare_cstring (auser,user)) {
  				/* scan list of mail administrators */
        if ((gr = getgrnam (ADMINGROUP)) && (t = gr->gr_mem)) while (*t && !ret)
  	if (!compare_cstring (auser,*t++))
  	  ret = pw_login (pw,NIL,user,home,argc,argv);
        syslog (LOG_NOTICE|LOG_AUTH,"%s %.80s override of user=%.80s host=%.80s",
  	      ret ? "Admin" : "Failed",auser,user,tcp_clienthost ());
      }
      else if (closedBox) {	/* paranoid site, lock out other directories */
        if (chdir (home) || chroot (home))
  	syslog (LOG_NOTICE|LOG_AUTH,
  		"Login %s failed: unable to set chroot=%.80s host=%.80s",
  		pw->pw_name,home,tcp_clienthost ());
        else if (loginpw (pw,argc,argv)) ret = env_init (user,NIL);
        else fatal ("Login failed after chroot");
      }
  				/* normal login */
      else if (((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
  	     (ret = env_init (user,home))) chdir (myhomedir ());
      fs_give ((void **) &home);	/* clean up */
      if (user) fs_give ((void **) &user);
    }
    endpwent ();			/* in case shadow passwords in pw data */
    return ret;			/* return status */
  }
  
  /* Initialize environment
   * Accepts: user name (NIL for anonymous)
   *	    home directory name
   * Returns: T, always
   */
  
  long env_init (char *user,char *home)
  {
    extern MAILSTREAM CREATEPROTO;
    extern MAILSTREAM EMPTYPROTO;
    struct passwd *pw;
    struct stat sbuf;
    char tmp[MAILTMPLEN];
  				/* don't init if blocked */
    if (block_env_init) return LONGT;
    if (myUserName) fatal ("env_init called twice!");
  				/* initially nothing in namespace list */
    nslist[0] = nslist[1] = nslist[2] = NIL;
  				/* myUserName must be set before dorc() call */
    myUserName = cpystr (user ? user : ANONYMOUSUSER);
  				/* force default prototypes to be set */
    if (!createProto) createProto = &CREATEPROTO;
    if (!appendProto) appendProto = &EMPTYPROTO;
    dorc (NIL,NIL);		/* do systemwide configuration */
    if (!home) {			/* closed box server */
  				/* standard user can only reference home */
      if (user) nslist[0] = &nshome;
      else {			/* anonymous user */
        nslist[0] = &nsblackother; /* set root */
        anonymous = T;		/* flag as anonymous */
      }
      myHomeDir = cpystr ("");	/* home directory is root */
      sysInbox = cpystr ("INBOX");/* make system INBOX */
    }
    else {			/* open or black box */
      closedBox = NIL;		/* definitely not a closed box */
      if (user) {			/* remember user name and home directory */
        if (blackBoxDir) {	/* build black box directory name */
  	sprintf (tmp,"%s/%s",blackBoxDir,myUserName);
  				/* must exist */
  	if (!((!stat (home = tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) ||
  	      (blackBoxDefaultHome &&
  	       !stat (home = blackBoxDefaultHome,&sbuf) &&
  	       (sbuf.st_mode & S_IFDIR)))) fatal ("no home");
  	sysInbox = (char *) fs_get (strlen (home) + 7);
  				/* set system INBOX */
  	sprintf (sysInbox,"%s/INBOX",home);
  	blackBox = T;		/* mark that it's a black box */
  				/* mbox meaningless if black box */
  	mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox");
        }
        nslist[0] = &nshome;	/* home namespace */
  				/* limited advertise namespaces */
        if (limitedadvertise) nslist[2] = &nslimited;
        else if (blackBox) {	/* black box namespaces */
  	nslist[1] = &nsblackother;
  	nslist[2] = &nsshared;
        }
        else {			/* open box namespaces */
  	nslist[1] = &nsunixother;
  	nslist[2] = advertisetheworld ? &nsworld : &nsshared;
        }
      }
      else {
        nslist[2] = &nsftp;	/* anonymous user */
        sprintf (tmp,"%s/INBOX",
  	       home = (char *) mail_parameters (NIL,GET_ANONYMOUSHOME,NIL));
        sysInbox = cpystr (tmp);	/* make system INBOX */
        anonymous = T;		/* flag as anonymous */
      }
      myHomeDir = cpystr (home);	/* set home directory */
    }
  
    if (allowuserconfig) {	/* allow user config files */
      dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T);
      dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL);
    }
    if (!closedBox && !noautomaticsharedns) {
  				/* #ftp namespace */
      if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
  				/* #public namespace */
      if (!publicHome && (pw = getpwnam ("imappublic")))
        publicHome = cpystr (pw->pw_dir);
  				/* #shared namespace */
      if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
        sharedHome = cpystr (pw->pw_dir);
    }
    if (!myLocalHost) mylocalhost ();
    if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
    if (!newsActive) newsActive = cpystr (ACTIVEFILE);
    if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
  				/* re-do open action to get flags */
    (*createProto->dtb->open) (NIL);
    endpwent ();			/* close pw database */
    return T;
  }
   
  /* Return my user name
   * Accepts: pointer to optional flags
   * Returns: my user name
   */
  
  char *myusername_full (unsigned long *flags)
  {
    struct passwd *pw;
    struct stat sbuf;
    char *s;
    unsigned long euid;
    char *ret = UNLOGGEDUSER;
  				/* no user name yet and not root? */
    if (!myUserName && (euid = geteuid ())) {
  				/* yes, look up getlogin() user name or EUID */
      if (((s = (char *) getlogin ()) && *s && (strlen (s) < NETMAXUSER) &&
  	 (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
  	(pw = getpwuid (euid))) {
        if (block_env_init) {	/* don't env_init if blocked */
  	if (flags) *flags = MU_LOGGEDIN;
  	return pw->pw_name;
        }
        env_init (pw->pw_name,
  		((s = getenv ("HOME")) && *s && (strlen (s) < NETMAXMBX) &&
  		 !stat (s,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) ?
  		s : pw->pw_dir);
      }
      else fatal ("Unable to look up user name");
    }
    if (myUserName) {		/* logged in? */
      if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
      ret = myUserName;		/* return user name */
    }
    else if (flags) *flags = MU_NOTLOGGEDIN;
    return ret;
  }
  
  
  /* Return my local host name
   * Returns: my local host name
   */
  
  char *mylocalhost ()
  {
    if (!myLocalHost) {
      char *s,tmp[MAILTMPLEN];
      tmp[0] = tmp[MAILTMPLEN-1] = '\0';
      if (!gethostname (tmp,MAILTMPLEN-1) && tmp[0]) {
  				/* sanity check of name */
        for (s = tmp; (*s > 0x20) && (*s < 0x7f); ++s);
        if (!*s) myLocalHost = tcp_canonical (tmp);
      }
    }
    return myLocalHost ? myLocalHost : "unknown";
  }
  
  /* Return my home directory name
   * Returns: my home directory name
   */
  
  char *myhomedir ()
  {
    if (!myHomeDir) myusername ();/* initialize if first time */
    return myHomeDir ? myHomeDir : "";
  }
  
  
  /* Return my home mailbox name
   * Returns: my home directory name
   */
  
  static char *mymailboxdir ()
  {
    char *home = myhomedir ();
  				/* initialize if first time */
    if (!myMailboxDir && myHomeDir) {
      if (mailsubdir) {
        char tmp[MAILTMPLEN];
        sprintf (tmp,"%s/%s",home,mailsubdir);
        myMailboxDir = cpystr (tmp);/* use pre-defined subdirectory of home */
      }
      else myMailboxDir = cpystr (home);
    }
    return myMailboxDir ? myMailboxDir : "";
  }
  
  
  /* Return system standard INBOX
   * Accepts: buffer string
   */
  
  char *sysinbox ()
  {
    char tmp[MAILTMPLEN];
    if (!sysInbox) {		/* initialize if first time */
      sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
      sysInbox = cpystr (tmp);	/* system inbox is from mail spool */
    }
    return sysInbox;
  }
  
  /* Return mailbox directory name
   * Accepts: destination buffer
   *	    directory prefix
   *	    name in directory
   * Returns: file name or NIL if error
   */
  
  char *mailboxdir (char *dst,char *dir,char *name)
  {
    char tmp[MAILTMPLEN];
    if (dir || name) {		/* if either argument provided */
      if (dir) {
        if (strlen (dir) > NETMAXMBX) return NIL;
        strcpy (tmp,dir);		/* write directory prefix */
      }
      else tmp[0] = '\0';		/* otherwise null string */
      if (name) {
        if (strlen (name) > NETMAXMBX) return NIL;
        strcat (tmp,name);	/* write name in directory */
      }
  				/* validate name, return its name */
      if (!mailboxfile (dst,tmp)) return NIL;
    }
  				/* no arguments, wants mailbox directory */
    else strcpy (dst,mymailboxdir ());
    return dst;			/* return the name */
  }
  
  /* Return mailbox file name
   * Accepts: destination buffer
   *	    mailbox name
   * Returns: file name or empty string for driver-selected INBOX or NIL if error
   */
  
  char *mailboxfile (char *dst,char *name)
  {
    struct passwd *pw;
    char *s;
    if (!name || !*name || (*name == '{') || (strlen (name) > NETMAXMBX) ||
        ((anonymous || blackBox || restrictBox || (*name == '#')) &&
         (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))))
      dst = NIL;			/* invalid name */
    else switch (*name) {		/* determine mailbox type based upon name */
    case '#':			/* namespace name */
  				/* #ftp/ namespace */
      if (((name[1] == 'f') || (name[1] == 'F')) &&
  	((name[2] == 't') || (name[2] == 'T')) &&
  	((name[3] == 'p') || (name[3] == 'P')) &&
  	(name[4] == '/') && ftpHome) sprintf (dst,"%s/%s",ftpHome,name+5);
  				/* #public/ and #shared/ namespaces */
      else if ((((name[1] == 'p') || (name[1] == 'P')) &&
  	      ((name[2] == 'u') || (name[2] == 'U')) &&
  	      ((name[3] == 'b') || (name[3] == 'B')) &&
  	      ((name[4] == 'l') || (name[4] == 'L')) &&
  	      ((name[5] == 'i') || (name[5] == 'I')) &&
  	      ((name[6] == 'c') || (name[6] == 'C')) &&
  	      (name[7] == '/') && (s = publicHome)) ||
  	     (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
  	      ((name[2] == 'h') || (name[2] == 'H')) &&
  	      ((name[3] == 'a') || (name[3] == 'A')) &&
  	      ((name[4] == 'r') || (name[4] == 'R')) &&
  	      ((name[5] == 'e') || (name[5] == 'E')) &&
  	      ((name[6] == 'd') || (name[6] == 'D')) &&
  	      (name[7] == '/') && (s = sharedHome)))
        sprintf (dst,"%s/%s",s,compare_cstring (name+8,"INBOX") ?
  	       name+8 : "INBOX");
      else dst = NIL;		/* unknown namespace */
      break;
  
    case '/':			/* root access */
      if (anonymous) dst = NIL;	/* anonymous forbidden to do this */
      else if (blackBox) {	/* other user access if blackbox */
        if (restrictBox & RESTRICTOTHERUSER) dst = NIL;
  				/* see if other user INBOX */
        else if ((s = strchr (name+1,'/')) && !compare_cstring (s+1,"INBOX")) {
  	*s = '\0';		/* temporarily tie off string */
  	sprintf (dst,"%s/%s/INBOX",blackBoxDir,name+1);
  	*s = '/';		/* in case caller cares */
        }
        else sprintf (dst,"%s/%s",blackBoxDir,name+1);
      }
      else {
        size_t i = strlen (mymailboxdir ());
        if ((restrictBox & RESTRICTROOT) && strcmp (name,sysinbox ()) &&
  	  (!i || strncmp (name,mymailboxdir (),i) || (name[i] !='/')))
  	dst = NIL;		/* restricted and not sysinbox or home */
        else strcpy (dst,name);	/* unrestricted, copy root name */
      }
      break;
    case '~':			/* other user access */
  				/* bad syntax or anonymous can't win */
      if (!*++name || anonymous) dst = NIL;
  				/* ~/ equivalent to ordinary name */
      else if (*name == '/') sprintf (dst,"%s/%s",mymailboxdir (),name+1);
  				/* other user forbidden if closed/restricted */
      else if (closedBox || (restrictBox & RESTRICTOTHERUSER)) dst = NIL;
      else if (blackBox) {	/* black box form of other user */
  				/* see if other user INBOX */
        if ((s = strchr (name,'/')) && compare_cstring (s+1,"INBOX")) {
  	*s = '\0';		/* temporarily tie off string */
  	sprintf (dst,"%s/%s/INBOX",blackBoxDir,name);
  	*s = '/';		/* in case caller cares */
        }
        else sprintf (dst,"%s/%s",blackBoxDir,name);
      }
      else {			/* clear box other user */
  				/* copy user name */
        for (s = dst; *name && (*name != '/'); *s++ = *name++);
        *s++ = '\0';		/* tie off user name, look up in passwd file */
        if ((pw = getpwnam (dst)) && pw->pw_dir) {
  	if (*name) name++;	/* skip past the slash */
  				/* canonicalize case of INBOX */
  	if (!compare_cstring (name,"INBOX")) name = "INBOX";
  				/* remove trailing / from directory */
  	if ((s = strrchr (pw->pw_dir,'/')) && !s[1]) *s = '\0';
  				/* don't allow ~root/ if restricted root */
  	if ((restrictBox & RESTRICTROOT) && !*pw->pw_dir) dst = NIL;
  				/* build final name w/ subdir if needed */
  	else if (mailsubdir) sprintf (dst,"%s/%s/%s",pw->pw_dir,mailsubdir,name);
  	else sprintf (dst,"%s/%s",pw->pw_dir,name);
        }
        else dst = NIL;		/* no such user */
      }
      break;
  
    case 'I': case 'i':		/* possible INBOX */
      if (!compare_cstring (name+1,"NBOX")) {
  				/* if restricted, use INBOX in mailbox dir */
        if (anonymous || blackBox || closedBox)
  	sprintf (dst,"%s/INBOX",mymailboxdir ());
        else *dst = '\0';		/* otherwise driver selects the name */
        break;
      }
  				/* drop into to ordinary name case */
    default:			/* ordinary name is easy */
      sprintf (dst,"%s/%s",mymailboxdir (),name);
      break;
    }
    return dst;			/* return final name */
  }
  
  /* Dot-lock file locker
   * Accepts: file name to lock
   *	    destination buffer for lock file name
   *	    open file description on file name to lock
   * Returns: T if success, NIL if failure
   */
  
  long dotlock_lock (char *file,DOTLOCK *base,int fd)
  {
    int i = locktimeout * 60;
    int j,mask,retry,pi[2],po[2];
    char *s,tmp[MAILTMPLEN];
    struct stat sb;
  				/* flush absurd file name */
    if (strlen (file) > 512) return NIL;
  				/* build lock filename */
    sprintf (base->lock,"%s.lock",file);
  				/* assume no pipe */
    base->pipei = base->pipeo = -1;
    do {				/* make sure not symlink */
      if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
  				/* time out if file older than 5 minutes */
      if ((j > 0) && ((time (0)) >= (sb.st_ctime + locktimeout * 60))) i = 0;
  				/* try to create the lock */
      switch (retry = crexcl (base->lock)) {
      case -1:			/* OK to retry */
        if (!(i%15)) {		/* time to notify? */
  	sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
  		 file,i);
  	MM_LOG (tmp,WARN);
        }
        sleep (1);		/* wait 1 second before next try */
        break;
      case NIL:			/* failure, can't retry */
        i = 0;
        break;
      case T:			/* success, make sure others can break lock */
        chmod (base->lock,(int) dotlock_mode);
        return LONGT;
      }
    } while (i--);		/* until out of retries */
    if (retry < 0) {		/* still returning retry after locktimeout? */
      if (!(j = chk_notsymlink (base->lock,&sb))) return NIL;
      if ((j > 0) && ((time (0)) < (sb.st_ctime + locktimeout * 60))) {
        sprintf (tmp,"Mailbox vulnerable - seizing %ld second old lock",
  	       (long) (time (0) - sb.st_ctime));
        MM_LOG (tmp,WARN);
      }
      mask = umask (0);		/* want our lock protection */
      unlink (base->lock);	/* try to remove the old file */
  				/* seize the lock */
      if ((i = open (base->lock,O_WRONLY|O_CREAT,(int) dotlock_mode)) >= 0) {
        close (i);		/* don't need descriptor any more */
        sprintf (tmp,"Mailbox %.80s lock overridden",file);
        MM_LOG (tmp,NIL);
        chmod (base->lock,(int) dotlock_mode);
        umask (mask);		/* restore old umask */
        return LONGT;
      }
      umask (mask);		/* restore old umask */
    }
  
    if (fd >= 0) switch (errno) {
    case EACCES:			/* protection failure? */
      MM_CRITICAL (NIL);		/* go critical */
      if (closedBox || !lockpgm);	/* can't do on closed box or disabled */
      else if ((*lockpgm && stat (lockpgm,&sb)) ||
  	     (!*lockpgm && stat (lockpgm = LOCKPGM1,&sb) &&
  	      stat (lockpgm = LOCKPGM2,&sb) && stat (lockpgm = LOCKPGM3,&sb) &&
  	      stat (lockpgm = LOCKPGM4,&sb)))
        lockpgm = NIL;		/* disable if can't find lockpgm */
      else if (pipe (pi) >= 0) {	/* make command pipes */
        long cf;
        char *argv[4],arg[20];
  				/* if input pipes usable create output pipes */
        if ((pi[0] < FD_SETSIZE) && (pi[1] < FD_SETSIZE) && (pipe (po) >= 0)) {
  				/* make sure output pipes are usable */
  	if ((po[0] >= FD_SETSIZE) || (po[1] >= FD_SETSIZE));
  				/* all is good, make inferior process */
  	else if (!(j = fork ())) {
  	  if (!fork ()) {	/* make grandchild so it's inherited by init */
  				/* prepare argument vector */
  	    sprintf (arg,"%d",fd);
  	    argv[0] = lockpgm; argv[1] = arg;
  	    argv[2] = file; argv[3] = NIL;
  				/* set parent's I/O to my O/I */
  	    dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
  				/* close all unnecessary descriptors */
  	    for (cf = max (20,max (max (pi[0],pi[1]),max(po[0],po[1])));
  		 cf >= 3; --cf) if (cf != fd) close (cf);
  				/* be our own process group */
  	    setpgrp (0,getpid ());
  				/* now run it */
  	    _exit (execv (argv[0],argv));
  	  }
  	  _exit (1);		/* child is done */
  	}
  	else if (j > 0) {	/* parent process */
  	  fd_set rfd;
  	  struct timeval tmo;
  	  FD_ZERO (&rfd);
  	  FD_SET (pi[0],&rfd);
  	  tmo.tv_sec = locktimeout * 60;
  	  grim_pid_reap (j,NIL);/* reap child; grandchild now owned by init */
  				/* read response from locking program */
  	  if (select (pi[0]+1,&rfd,0,0,&tmo) &&
  	      (read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
  				/* success, record pipes */
  	    base->pipei = pi[0]; base->pipeo = po[1];
  				/* close child's side of the pipes */
  	    close (pi[1]); close (po[0]);
  	    MM_NOCRITICAL (NIL);/* no longer critical */
  	    return LONGT;
  	  }
  	}
  	close (po[0]); close (po[1]);
        }
        close (pi[0]); close (pi[1]);
      }
  
      MM_NOCRITICAL (NIL);	/* no longer critical */
  				/* find directory/file delimiter */
      if (s = strrchr (base->lock,'/')) {
        *s = '\0';		/* tie off at directory */
        sprintf(tmp,		/* generate default message */
  	      "Mailbox vulnerable - directory %.80s must have 1777 protection",
  	      base->lock);
  				/* definitely not 1777 if can't stat */
        mask = stat (base->lock,&sb) ? 0 : (sb.st_mode & 1777);
        *s = '/';			/* restore lock name */
        if (mask != 1777) {	/* default warning if not 1777 */
  	if (!disableLockWarning) MM_LOG (tmp,WARN);
  	break;
        }
      }
    default:
      sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
  	     base->lock,strerror (errno));
      if (!disableLockWarning) MM_LOG (tmp,WARN);
      break;
    }
    base->lock[0] = '\0';		/* don't use lock files */
    return NIL;
  }
  
  /* Dot-lock file unlocker
   * Accepts: lock file name
   * Returns: T if success, NIL if failure
   */
  
  long dotlock_unlock (DOTLOCK *base)
  {
    long ret = LONGT;
    if (base && base->lock[0]) {
      if (base->pipei >= 0) {	/* if running through a pipe unlocker */
        ret = (write (base->pipeo,"+",1) == 1);
  				/* nuke the pipes */
        close (base->pipei); close (base->pipeo);
      }
      else ret = !unlink (base->lock);
    }
    return ret;
  }
  
  /* Lock file name
   * Accepts: scratch buffer
   *	    file name
   *	    type of locking operation (LOCK_SH or LOCK_EX)
   *	    pointer to return PID of locker
   * Returns: file descriptor of lock or negative if error
   */
  
  int lockname (char *lock,char *fname,int op,long *pid)
  {
    struct stat sbuf;
    *pid = 0;			/* no locker PID */
    return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
  }
  
  
  /* Lock file descriptor
   * Accepts: file descriptor
   *	    lock file name buffer
   *	    type of locking operation (LOCK_SH or LOCK_EX)
   * Returns: file descriptor of lock or negative if error
   */
  
  int lockfd (int fd,char *lock,int op)
  {
    struct stat sbuf;
    return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
  }
  
  /* Lock file name worker
   * Accepts: lock file name
   *	    pointer to stat() buffer
   *	    type of locking operation (LOCK_SH or LOCK_EX)
   *	    pointer to return PID of locker
   * Returns: file descriptor of lock or negative if error
   */
  
  int lock_work (char *lock,void *sb,int op,long *pid)
  {
    struct stat lsb,fsb;
    struct stat *sbuf = (struct stat *) sb;
    char tmp[MAILTMPLEN];
    long i;
    int fd;
    int mask = umask (0);
    if (pid) *pid = 0;		/* initialize return PID */
  				/* make temporary lock file name */
    sprintf (lock,"%s/.%lx.%lx",closedBox ? "" : tmpdir,
  	   (unsigned long) sbuf->st_dev,(unsigned long) sbuf->st_ino);
    while (T) {			/* until get a good lock */
      do switch ((int) chk_notsymlink (lock,&lsb)) {
      case 1:			/* exists just once */
        if (((fd = open (lock,O_RDWR,shlock_mode)) >= 0) ||
  	  (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
      case -1:			/* name doesn't exist */
        fd = open (lock,O_RDWR|O_CREAT|O_EXCL,shlock_mode);
        break;
      default:			/* multiple hard links */
        MM_LOG ("hard link to lock name",ERROR);
        syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
      case 0:			/* symlink (already did syslog) */
        umask (mask);		/* restore old mask */
        return -1;		/* fail: no lock file */
      } while ((fd < 0) && (errno == EEXIST));
      if (fd < 0) {		/* failed to get file descriptor */
        syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
  	      strerror (errno));
        if (!closedBox) {		/* more explicit snarl for bad configuration */
  	if (stat (tmpdir,&lsb))
  	  syslog (LOG_CRIT,"SYSTEM ERROR: no %s: %s",tmpdir,strerror (errno));
  	else if ((lsb.st_mode & 01777) != 01777) {
  	  sprintf (tmp,"Can't lock for write: %.80s must have 1777 protection",
  		   tmpdir);
  	  MM_LOG (tmp,WARN);
  	}
        }
        umask (mask);		/* restore old mask */
        return -1;		/* fail: can't open lock file */
      }
  
  				/* non-blocking form */
      if (op & LOCK_NB) i = flock (fd,op);
      else {			/* blocking form */
        (*mailblocknotify) (BLOCK_FILELOCK,NIL);
        i = flock (fd,op);
        (*mailblocknotify) (BLOCK_NONE,NIL);
      }
      if (i) {			/* failed, get other process' PID */
        if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
  	  (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
  	*pid = i;
        close (fd);		/* failed, give up on lock */
        umask (mask);		/* restore old mask */
        return -1;		/* fail: can't lock */
      }
  				/* make sure this lock is good for us */
      if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
  	!fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
  	(lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
      close (fd);			/* lock not right, drop fd and try again */
    }
    chmod (lock,shlock_mode);	/* make sure mode OK (don't use fchmod()) */
    umask (mask);			/* restore old mask */
    return fd;			/* success */
  }
  
  /* Check to make sure not a symlink
   * Accepts: file name
   *	    stat buffer
   * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
   */
  
  long chk_notsymlink (char *name,void *sb)
  {
    struct stat *sbuf = (struct stat *) sb;
  				/* name exists? */
    if (lstat (name,sbuf)) return -1;
  				/* forbid symbolic link */
    if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
      MM_LOG ("symbolic link on lock name",ERROR);
      syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
  	    name);
      return NIL;
    }
    return (long) sbuf->st_nlink;	/* return number of hard links */
  }
  
  
  /* Unlock file descriptor
   * Accepts: file descriptor
   *	    lock file name from lockfd()
   */
  
  void unlockfd (int fd,char *lock)
  {
  				/* delete the file if no sharers */
    if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
    flock (fd,LOCK_UN);		/* unlock it */
    close (fd);			/* close it */
  }
  
  /* Set proper file protection for mailbox
   * Accepts: mailbox name
   *	    actual file path name
   * Returns: T, always
   */
  
  long set_mbx_protections (char *mailbox,char *path)
  {
    struct stat sbuf;
    int mode = (int) mbx_protection;
    if (*mailbox == '#') {	/* possible namespace? */
        if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
  	  ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
  	  ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
  	  (mailbox[4] == '/')) mode = (int) ftp_protection;
        else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
  	       ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
  	       ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
  	       ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
  	       ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
  	       ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
  	       (mailbox[7] == '/')) mode = (int) public_protection;
        else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
  	       ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
  	       ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
  	       ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
  	       ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
  	       ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
  	       (mailbox[7] == '/')) mode = (int) shared_protection;
    }
  				/* if a directory */
    if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
  				/* set owner search if allow read or write */
      if (mode & 0600) mode |= 0100;
      if (mode & 060) mode |= 010;/* set group search if allow read or write */
      if (mode & 06) mode |= 01;	/* set world search if allow read or write */
  				/* preserve directory SGID bit */
      if (sbuf.st_mode & S_ISGID) mode |= S_ISGID;
    }
    chmod (path,mode);		/* set the new protection, ignore failure */
    return LONGT;
  }
  
  /* Get proper directory protection
   * Accepts: mailbox name
   * Returns: directory mode, always
   */
  
  long get_dir_protection (char *mailbox)
  {
    if (*mailbox == '#') {	/* possible namespace? */
        if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
  	  ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
  	  ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
  	  (mailbox[4] == '/')) return ftp_dir_protection;
        else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
  	       ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
  	       ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
  	       ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
  	       ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
  	       ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
  	       (mailbox[7] == '/')) return public_dir_protection;
        else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
  	       ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
  	       ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
  	       ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
  	       ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
  	       ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
  	       (mailbox[7] == '/')) return shared_dir_protection;
    }
    return dir_protection;
  }
  
  /* Determine default prototype stream to user
   * Accepts: type (NIL for create, T for append)
   * Returns: default prototype stream
   */
  
  MAILSTREAM *default_proto (long type)
  {
    myusername ();		/* make sure initialized */
  				/* return default driver's prototype */
    return type ? appendProto : createProto;
  }
  
  
  /* Set up user flags for stream
   * Accepts: MAIL stream
   * Returns: MAIL stream with user flags set up
   */
  
  MAILSTREAM *user_flags (MAILSTREAM *stream)
  {
    int i;
    myusername ();		/* make sure initialized */
    for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
      if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
    return stream;
  }
  
  
  /* Return nth user flag
   * Accepts: user flag number
   * Returns: flag
   */
  
  char *default_user_flag (unsigned long i)
  {
    myusername ();		/* make sure initialized */
    return userFlags[i];
  }
  
  /* Process rc file
   * Accepts: file name
   *	    .mminit flag
   * Don't use this feature.
   */
  
  void dorc (char *file,long flag)
  {
    int i;
    char *s,*t,*k,*r,tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
    extern MAILSTREAM CREATEPROTO;
    extern MAILSTREAM EMPTYPROTO;
    DRIVER *d;
    FILE *f;
    if ((f = fopen (file ? file : SYSCONFIG,"r")) &&
        (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n'))) do {
      *t++ = '\0';		/* tie off line, find second space */
      if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
        *k++ = '\0';		/* tie off two words */
        if (!compare_cstring (s,"set keywords") && !userFlags[0]) {
  				/* yes, get first keyword */
  	k = strtok_r (k,", ",&r);
  				/* copy keyword list */
  	for (i = 0; k && i < NUSERFLAGS; ++i) if (strlen (k) <= MAXUSERFLAG) {
  	  if (userFlags[i]) fs_give ((void **) &userFlags[i]);
  	  userFlags[i] = cpystr (k);
  	  k = strtok_r (NIL,", ",&r);
  	}
  	if (flag) break;	/* found "set keywords" in .mminit */
        }
  
        else if (!flag) {		/* none of these valid in .mminit */
  	if (myUserName) {	/* only valid if logged in */
  	  if (!compare_cstring (s,"set new-mailbox-format") ||
  	      !compare_cstring (s,"set new-folder-format")) {
  	    if (!compare_cstring (k,"same-as-inbox")) {
  	      if (d = mail_valid (NIL,"INBOX",NIL)) {
  		if (!compare_cstring (d->name,"mbox"))
  		  d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,
  						  (void *) "unix");
  		else if (!compare_cstring (d->name,"dummy")) d = NIL;
  	      }
  	      createProto = d ? ((*d->open) (NIL)) : &CREATEPROTO;
  	    }
  	    else if (!compare_cstring (k,"system-standard"))
  	      createProto = &CREATEPROTO;
  	    else {		/* canonicalize mbox to unix */
  	      if (!compare_cstring (k,"mbox")) k = "unix";
  				/* see if a driver name */
  	      if (d = (DRIVER *) mail_parameters (NIL,GET_DRIVER,(void *) k))
  		createProto = (*d->open) (NIL);
  	      else {		/* duh... */
  		sprintf (tmpx,"Unknown new mailbox format in %s: %s",
  			 file ? file : SYSCONFIG,k);
  		MM_LOG (tmpx,WARN);
  	      }
  	    }
  	  }
  	  if (!compare_cstring (s,"set empty-mailbox-format") ||
  	      !compare_cstring (s,"set empty-folder-format")) {
  	    if (!compare_cstring (k,"invalid")) appendProto = NIL;
  	    else if (!compare_cstring (k,"same-as-inbox"))
  	      appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
  			     compare_cstring (d->name,"dummy")) ?
  			       ((*d->open) (NIL)) : &EMPTYPROTO;
  	    else if (!compare_cstring (k,"system-standard"))
  	      appendProto = &EMPTYPROTO;
  	    else {		/* see if a driver name */
  	      for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
  		   d && compare_cstring (d->name,k); d = d->next);
  	      if (d) appendProto = (*d->open) (NIL);
  	      else {		/* duh... */
  		sprintf (tmpx,"Unknown empty mailbox format in %s: %s",
  			 file ? file : SYSCONFIG,k);
  		MM_LOG (tmpx,WARN);
  	      }
  	    }
  	  }
  	}
  
  	if (!compare_cstring (s,"set local-host")) {
  	  fs_give ((void **) &myLocalHost);
  	  myLocalHost = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set news-active-file")) {
  	  fs_give ((void **) &newsActive);
  	  newsActive = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set news-spool-directory")) {
  	  fs_give ((void **) &newsSpool);
  	  newsSpool = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set mh-path"))
  	  mail_parameters (NIL,SET_MHPATH,(void *) k);
  	else if (!compare_cstring (s,"set mh-allow-inbox"))
  	  mail_parameters (NIL,SET_MHALLOWINBOX,(void *) atol (k));
  	else if (!compare_cstring (s,"set news-state-file")) {
  	  fs_give ((void **) &myNewsrc);
  	  myNewsrc = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set ftp-export-directory")) {
  	  fs_give ((void **) &ftpHome);
  	  ftpHome = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set public-home-directory")) {
  	  fs_give ((void **) &publicHome);
  	  publicHome = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set shared-home-directory")) {
  	  fs_give ((void **) &sharedHome);
  	  sharedHome = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set system-inbox")) {
  	  fs_give ((void **) &sysInbox);
  	  sysInbox = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set mail-subdirectory")) {
  	  fs_give ((void **) &mailsubdir);
  	  mailsubdir = cpystr (k);
  	}
  	else if (!compare_cstring (s,"set from-widget"))
  	  mail_parameters (NIL,SET_FROMWIDGET,
  			   compare_cstring (k,"header-only") ?
  			   VOIDT : NIL);
  
  	else if (!compare_cstring (s,"set rsh-command"))
  	  mail_parameters (NIL,SET_RSHCOMMAND,(void *) k);
  	else if (!compare_cstring (s,"set rsh-path"))
  	  mail_parameters (NIL,SET_RSHPATH,(void *) k);
  	else if (!compare_cstring (s,"set ssh-command"))
  	  mail_parameters (NIL,SET_SSHCOMMAND,(void *) k);
  	else if (!compare_cstring (s,"set ssh-path"))
  	  mail_parameters (NIL,SET_SSHPATH,(void *) k);
  	else if (!compare_cstring (s,"set tcp-open-timeout"))
  	  mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k));
  	else if (!compare_cstring (s,"set tcp-read-timeout"))
  	  mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k));
  	else if (!compare_cstring (s,"set tcp-write-timeout"))
  	  mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k));
  	else if (!compare_cstring (s,"set rsh-timeout"))
  	  mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k));
  	else if (!compare_cstring (s,"set ssh-timeout"))
  	  mail_parameters (NIL,SET_SSHTIMEOUT,(void *) atol (k));
  	else if (!compare_cstring (s,"set maximum-login-trials"))
  	  mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k));
  	else if (!compare_cstring (s,"set lookahead"))
  	  mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k));
  	else if (!compare_cstring (s,"set prefetch"))
  	  mail_parameters (NIL,SET_PREFETCH,(void *) atol (k));
  	else if (!compare_cstring (s,"set close-on-error"))
  	  mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k));
  	else if (!compare_cstring (s,"set imap-port"))
  	  mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k));
  	else if (!compare_cstring (s,"set pop3-port"))
  	  mail_parameters (NIL,SET_POP3PORT,(void *) atol (k));
  	else if (!compare_cstring (s,"set uid-lookahead"))
  	  mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k));
  	else if (!compare_cstring (s,"set try-ssl-first"))
  	  mail_parameters (NIL,SET_TRYSSLFIRST,(void *) atol (k));
  
  	else if (!compare_cstring (s,"set mailbox-protection"))
  	  mbx_protection = atol (k);
  	else if (!compare_cstring (s,"set directory-protection"))
  	  dir_protection = atol (k);
  	else if (!compare_cstring (s,"set lock-protection"))
  	  dotlock_mode = atol (k);
  	else if (!compare_cstring (s,"set ftp-protection"))
  	  ftp_protection = atol (k);
  	else if (!compare_cstring (s,"set public-protection"))
  	  public_protection = atol (k);
  	else if (!compare_cstring (s,"set shared-protection"))
  	  shared_protection = atol (k);
  	else if (!compare_cstring (s,"set ftp-directory-protection"))
  	  ftp_dir_protection = atol (k);
  	else if (!compare_cstring (s,"set public-directory-protection"))
  	  public_dir_protection = atol (k);
  	else if (!compare_cstring (s,"set shared-directory-protection"))
  	  shared_dir_protection = atol (k);
  	else if (!compare_cstring (s,"set dot-lock-file-timeout"))
  	  locktimeout = atoi (k);
  	else if (!compare_cstring (s,"set disable-fcntl-locking"))
  	  fcntlhangbug = atoi (k);
  	else if (!compare_cstring (s,"set disable-lock-warning"))
  	  disableLockWarning = atoi (k);
  	else if (!compare_cstring (s,"set disable-unix-UIDs-and-keywords"))
  	  has_no_life = atoi (k);
  	else if (!compare_cstring (s,"set hide-dot-files"))
  	  hideDotFiles = atoi (k);
  	else if (!compare_cstring (s,"set list-maximum-level"))
  	  list_max_level = atol (k);
  	else if (!compare_cstring (s,"set trust-dns"))
  	  mail_parameters (NIL,SET_TRUSTDNS,(void *) atol (k));
  	else if (!compare_cstring (s,"set sasl-uses-ptr-name"))
  	  mail_parameters (NIL,SET_SASLUSESPTRNAME,(void *) atol (k));
  	else if (!compare_cstring (s,"set network-filesystem-stat-bug"))
  	  netfsstatbug = atoi (k);
  	else if (!compare_cstring (s,"set nntp-range"))
  	  mail_parameters (NIL,SET_NNTPRANGE,(void *) atol (k));
  
  	else if (!file) {	/* only allowed in system init */
  	  if (!compare_cstring (s,"set black-box-directory") &&
  	      !blackBoxDir) blackBoxDir = cpystr (k);
  	  else if (!compare_cstring(s,"set black-box-default-home-directory")&&
  		   blackBoxDir && !blackBoxDefaultHome)
  	    blackBoxDefaultHome = cpystr (k);
  	  else if (!compare_cstring (s,"set anonymous-home-directory") &&
  		   !anonymousHome) anonymousHome = cpystr (k);
  				/* It's tempting to allow setting the CA path
  				 * in a user init.  However, that opens up a
  				 * vector of attack big enough to drive a
  				 * truck through...  Resist the temptation.
  				 */
  	  else if (!compare_cstring (s,"set CA-certificate-path"))
  	    sslCApath = cpystr (k);
  	  else if (!compare_cstring (s,"set disable-plaintext"))
  	    disablePlaintext = atoi (k);
  	  else if (!compare_cstring (s,"set allowed-login-attempts"))
  	    logtry = atoi (k);
  	  else if (!compare_cstring (s,"set chroot-server"))
  	    closedBox = atoi (k);
  	  else if (!compare_cstring (s,"set restrict-mailbox-access"))
  	    for (k = strtok_r (k,", ",&r); k; k = strtok_r (NIL,", ",&r)) {
  	      if (!compare_cstring (k,"root")) restrictBox |= RESTRICTROOT;
  	      else if (!compare_cstring (k,"otherusers"))
  		restrictBox |= RESTRICTOTHERUSER;
  	      else if (!compare_cstring (k,"all")) restrictBox = -1;
  	    }
  	  else if (!compare_cstring (s,"set advertise-the-world"))
  	    advertisetheworld = atoi (k);
  	  else if (!compare_cstring (s,"set limited-advertise"))
  	    limitedadvertise = atoi (k);
  	  else if (!compare_cstring
  		   (s,"set disable-automatic-shared-namespaces"))
  	    noautomaticsharedns = atoi (k);
  	  else if (!compare_cstring (s,"set allow-user-config"))
  	    allowuserconfig = atoi (k);
  	  else if (!compare_cstring (s,"set allow-reverse-dns"))
  	    mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k));
  	  else if (!compare_cstring (s,"set k5-cp-uses-service-name"))
  	    kerb_cp_svr_name = atoi (k);
  				/* must appear in file after any
  				 * "set disable-plaintext" command! */
  	  else if (!compare_cstring (s,"set plaintext-allowed-clients")) {
  	    for (k = strtok_r (k,", ",&r); k && !tcp_isclienthost (k);
  		 k = strtok_r (NIL,", ",&r));
  	    if (k) disablePlaintext = 0;
  	  }
  	}
        }
      }
    } while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'\n')));
    if (f) fclose (f);		/* flush the file */
  }
  
  /* INBOX create function for tmail/dmail use only
   * Accepts: mail stream
   *	    path name buffer, preloaded with driver-dependent path
   * Returns: T on success, NIL on failure
   *
   * This routine is evil and a truly incredible kludge.  It is private for
   * tmail/dmail and is not supported for any other application.
   */
  
  long path_create (MAILSTREAM *stream,char *path)
  {
    long ret;
    short rsave = restrictBox;
    restrictBox = NIL;		/* can't restrict */
    if (blackBox) {		/* if black box */
  				/* toss out driver dependent names */
      sprintf (path,"%s/INBOX",mymailboxdir ());
      blackBox = NIL;		/* well that's evil - evil is going on */
      ret = mail_create (stream,path);
      blackBox = T;		/* restore the box */
    }
  				/* easy thing otherwise */
    else ret = mail_create (stream,path);
    restrictBox = rsave;		/* restore restrictions */
    return ret;
  }
  
  /* Default block notify routine
   * Accepts: reason for calling
   *	    data
   * Returns: data
   */
  
  void *mm_blocknotify (int reason,void *data)
  {
    void *ret = data;
    switch (reason) {
    case BLOCK_SENSITIVE:		/* entering sensitive code */
      ret = (void *) (unsigned long) alarm (0);
      break;
    case BLOCK_NONSENSITIVE:	/* exiting sensitive code */
      if ((unsigned long) data) alarm ((unsigned long) data);
      break;
    default:			/* ignore all other reasons */
      break;
    }
    return ret;
  }
  