env_unix.c
上传用户:ycwykj01
上传日期:2007-01-04
资源大小:1819k
文件大小:43k
源码类别:

网络编程

开发平台:

Unix_Linux

  1. /*
  2.  * Program: UNIX environment routines
  3.  *
  4.  * Author: Mark Crispin
  5.  * Networks and Distributed Computing
  6.  * Computing & Communications
  7.  * University of Washington
  8.  * Administration Building, AG-44
  9.  * Seattle, WA  98195
  10.  * Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date: 1 August 1988
  13.  * Last Edited: 17 October 1999
  14.  *
  15.  * Copyright 1999 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35. #include <signal.h>
  36. #include <sys/wait.h>
  37. #include "write.c" /* include safe writing routines */
  38. /* Get all authenticators */
  39. #include "auths.c"
  40. /* c-client environment parameters */
  41. static const char *anonymous_user = "nobody";
  42. static const char *unlogged_user = "root";
  43. static char *myUserName = NIL; /* user name */
  44. static char *myHomeDir = NIL; /* home directory name */
  45. static char *myLocalHost = NIL; /* local host name */
  46. static char *myNewsrc = NIL; /* newsrc file name */
  47. static char *sysInbox = NIL; /* system inbox name */
  48. static char *newsActive = NIL; /* news active file */
  49. static char *newsSpool = NIL; /* news spool */
  50. /* anonymous home directory */
  51. static char *anonymousHome = NIL;
  52. static char *ftpHome = NIL; /* ftp export home directory */
  53. static char *publicHome = NIL; /* public home directory */
  54. static char *sharedHome = NIL; /* shared home directory */
  55. static char *blackBoxDir = NIL; /* black box directory name */
  56. /* black box default home directory */
  57. static char *blackBoxDefaultHome = NIL;
  58. static short anonymous = NIL; /* is anonymous */
  59. static short blackBox = NIL; /* is a black box */
  60. static short has_no_life = NIL; /* is a cretin with no life */
  61. static long list_max_level = 20;/* maximum level of list recursion */
  62. /* default file protection */
  63. static long mbx_protection = 0600;
  64. /* default directory protection */
  65. static long dir_protection = 0700;
  66. /* default lock file protection */
  67. static long lock_protection = 0666;
  68. /* default ftp file protection */
  69. static long ftp_protection = 0644;
  70. /* default public file protection */
  71. static long public_protection = 0666;
  72. /* default shared file protection */
  73. static long shared_protection = 0660;
  74. static long locktimeout = 5; /* default lock timeout */
  75. static long disableFcntlLock = /* flock() emulator is a no-op */
  76. #ifdef SVR4_DISABLE_FLOCK
  77.   T
  78. #else
  79.   NIL
  80. #endif
  81.   ;
  82. static long lockEaccesError = /* warning on EACCES errors on .lock files */
  83. #ifdef IGNORE_LOCK_EACCES_ERRORS
  84.   NIL
  85. #else
  86.   T
  87. #endif
  88.   ;
  89. /* default prototypes */
  90. static MAILSTREAM *createProto = NIL;
  91. static MAILSTREAM *appendProto = NIL;
  92. /* default user flags */
  93. static char *userFlags[NUSERFLAGS] = {NIL};
  94. static NAMESPACE *nslist[3]; /* namespace list */
  95. static int logtry = 3; /* number of server login tries */
  96. /* block notification */
  97. static blocknotify_t mailblocknotify = mm_blocknotify;
  98. /* UNIX namespaces */
  99. /* personal mh namespace */
  100. static NAMESPACE nsmhf = {"#mh/",'/',NIL,NIL};
  101. static NAMESPACE nsmh = {"#mhinbox",NIL,NIL,&nsmhf};
  102. /* home namespace */
  103. static NAMESPACE nshome = {"",'/',NIL,&nsmh};
  104. /* UNIX other user namespace */
  105. static NAMESPACE nsunixother = {"~",'/',NIL,NIL};
  106. /* black box other user namespace */
  107. static NAMESPACE nsblackother = {"/",'/',NIL,NIL};
  108. /* public (anonymous OK) namespace */
  109. static NAMESPACE nspublic = {"#public/",'/',NIL,NIL};
  110. /* netnews namespace */
  111. static NAMESPACE nsnews = {"#news.",'.',NIL,&nspublic};
  112. /* FTP export namespace */
  113. static NAMESPACE nsftp = {"#ftp/",'/',NIL,&nsnews};
  114. /* shared (no anonymous) namespace */
  115. static NAMESPACE nsshared = {"#shared/",'/',NIL,&nsftp};
  116. /* Environment manipulate parameters
  117.  * Accepts: function code
  118.  *     function-dependent value
  119.  * Returns: function-dependent return value
  120.  */
  121. void *env_parameters (long function,void *value)
  122. {
  123.   switch ((int) function) {
  124.   case SET_NAMESPACE:
  125.     fatal ("SET_NAMESPACE not permitted");
  126.   case GET_NAMESPACE:
  127.     value = (void *) nslist;
  128.     break;
  129.   case SET_USERNAME:
  130.     if (myUserName) fs_give ((void **) &myUserName);
  131.     myUserName = cpystr ((char *) value);
  132.     break;
  133.   case GET_USERNAME:
  134.     value = (void *) myUserName;
  135.     break;
  136.   case SET_HOMEDIR:
  137.     if (myHomeDir) fs_give ((void **) &myHomeDir);
  138.     myHomeDir = cpystr ((char *) value);
  139.     break;
  140.   case GET_HOMEDIR:
  141.     value = (void *) myHomeDir;
  142.     break;
  143.   case SET_LOCALHOST:
  144.     if (myLocalHost) fs_give ((void **) &myLocalHost);
  145.     myLocalHost = cpystr ((char *) value);
  146.     break;
  147.   case GET_LOCALHOST:
  148.     value = (void *) myLocalHost;
  149.     break;
  150.   case SET_NEWSRC:
  151.     if (myNewsrc) fs_give ((void **) &myNewsrc);
  152.     myNewsrc = cpystr ((char *) value);
  153.     break;
  154.   case GET_NEWSRC:
  155.     value = (void *) myNewsrc;
  156.     break;
  157.   case SET_NEWSACTIVE:
  158.     if (newsActive) fs_give ((void **) &newsActive);
  159.     newsActive = cpystr ((char *) value);
  160.     break;
  161.   case GET_NEWSACTIVE:
  162.     value = (void *) newsActive;
  163.     break;
  164.   case SET_NEWSSPOOL:
  165.     if (newsSpool) fs_give ((void **) &newsSpool);
  166.     newsSpool = cpystr ((char *) value);
  167.     break;
  168.   case GET_NEWSSPOOL:
  169.     value = (void *) newsSpool;
  170.     break;
  171.   case SET_ANONYMOUSHOME:
  172.     if (anonymousHome) fs_give ((void **) &anonymousHome);
  173.     anonymousHome = cpystr ((char *) value);
  174.     break;
  175.   case GET_ANONYMOUSHOME:
  176.     value = (void *) anonymousHome;
  177.     break;
  178.   case SET_FTPHOME:
  179.     if (ftpHome) fs_give ((void **) &ftpHome);
  180.     ftpHome = cpystr ((char *) value);
  181.     break;
  182.   case GET_FTPHOME:
  183.     value = (void *) ftpHome;
  184.     break;
  185.   case SET_PUBLICHOME:
  186.     if (publicHome) fs_give ((void **) &publicHome);
  187.     publicHome = cpystr ((char *) value);
  188.     break;
  189.   case GET_PUBLICHOME:
  190.     value = (void *) publicHome;
  191.     break;
  192.   case SET_SHAREDHOME:
  193.     if (sharedHome) fs_give ((void **) &sharedHome);
  194.     sharedHome = cpystr ((char *) value);
  195.     break;
  196.   case GET_SHAREDHOME:
  197.     value = (void *) sharedHome;
  198.     break;
  199.   case SET_SYSINBOX:
  200.     if (sysInbox) fs_give ((void **) &sysInbox);
  201.     sysInbox = cpystr ((char *) value);
  202.     break;
  203.   case GET_SYSINBOX:
  204.     value = (void *) sysInbox;
  205.     break;
  206.   case SET_LISTMAXLEVEL:
  207.     list_max_level = (long) value;
  208.     break;
  209.   case GET_LISTMAXLEVEL:
  210.     value = (void *) list_max_level;
  211.     break;
  212.   case SET_MBXPROTECTION:
  213.     mbx_protection = (long) value;
  214.     break;
  215.   case GET_MBXPROTECTION:
  216.     value = (void *) mbx_protection;
  217.     break;
  218.   case SET_DIRPROTECTION:
  219.     dir_protection = (long) value;
  220.     break;
  221.   case GET_DIRPROTECTION:
  222.     value = (void *) dir_protection;
  223.     break;
  224.   case SET_LOCKPROTECTION:
  225.     lock_protection = (long) value;
  226.     break;
  227.   case GET_LOCKPROTECTION:
  228.     value = (void *) lock_protection;
  229.     break;
  230.   case SET_FTPPROTECTION:
  231.     ftp_protection = (long) value;
  232.     break;
  233.   case GET_FTPPROTECTION:
  234.     value = (void *) ftp_protection;
  235.     break;
  236.   case SET_PUBLICPROTECTION:
  237.     public_protection = (long) value;
  238.     break;
  239.   case GET_PUBLICPROTECTION:
  240.     value = (void *) public_protection;
  241.     break;
  242.   case SET_SHAREDPROTECTION:
  243.     shared_protection = (long) value;
  244.     break;
  245.   case GET_SHAREDPROTECTION:
  246.     value = (void *) shared_protection;
  247.     break;
  248.   case SET_LOCKTIMEOUT:
  249.     locktimeout = (long) value;
  250.     break;
  251.   case GET_LOCKTIMEOUT:
  252.     value = (void *) locktimeout;
  253.     break;
  254.   case SET_DISABLEFCNTLLOCK:
  255.     disableFcntlLock = (long) value;
  256.     break;
  257.   case GET_DISABLEFCNTLLOCK:
  258.     value = (void *) disableFcntlLock;
  259.     break;
  260.   case SET_LOCKEACCESERROR:
  261.     lockEaccesError = (long) value;
  262.     break;
  263.   case GET_LOCKEACCESERROR:
  264.     value = (void *) lockEaccesError;
  265.     break;
  266.   case SET_USERHASNOLIFE:
  267.     has_no_life = value ? T : NIL;
  268.     break;
  269.   case GET_USERHASNOLIFE:
  270.     value = (void *) (has_no_life ? T : NIL);
  271.     break;
  272.   case SET_BLOCKNOTIFY:
  273.     mailblocknotify = (blocknotify_t) value;
  274.   case GET_BLOCKNOTIFY:
  275.     value = (void *) mailblocknotify;
  276.     break;
  277.   default:
  278.     value = NIL; /* error case */
  279.     break;
  280.   }
  281.   return value;
  282. }
  283. /* Write current time
  284.  * Accepts: destination string
  285.  *     optional format of day-of-week prefix
  286.  *     format of date and time
  287.  *     flag whether to append symbolic timezone
  288.  */
  289. static void do_date (char *date,char *prefix,char *fmt,int suffix)
  290. {
  291.   time_t tn = time (0);
  292.   struct tm *t = gmtime (&tn);
  293.   int zone = t->tm_hour * 60 + t->tm_min;
  294.   int julian = t->tm_yday;
  295.   t = localtime (&tn); /* get local time now */
  296. /* minus UTC minutes since midnight */
  297.   zone = t->tm_hour * 60 + t->tm_min - zone;
  298.   /* julian can be one of:
  299.    *  36x  local time is December 31, UTC is January 1, offset -24 hours
  300.    *    1  local time is 1 day ahead of UTC, offset +24 hours
  301.    *    0  local time is same day as UTC, no offset
  302.    *   -1  local time is 1 day behind UTC, offset -24 hours
  303.    * -36x  local time is January 1, UTC is December 31, offset +24 hours
  304.    */
  305.   if (julian = t->tm_yday -julian)
  306.     zone += ((julian < 0) == (abs (julian) == 1)) ? -24*60 : 24*60;
  307.   if (prefix) { /* want day of week? */
  308.     sprintf (date,prefix,days[t->tm_wday]);
  309.     date += strlen (date); /* make next sprintf append */
  310.   }
  311. /* output the date */
  312.   sprintf (date,fmt,t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  313.    t->tm_hour,t->tm_min,t->tm_sec,zone/60,abs (zone) % 60);
  314. /* append timezone suffix if desired */
  315.   if (suffix) rfc822_timezone (date,(void *) t);
  316. }
  317. /* Write current time in RFC 822 format
  318.  * Accepts: destination string
  319.  */
  320. void rfc822_date (char *date)
  321. {
  322.   do_date (date,"%s, ","%d %s %d %02d:%02d:%02d %+03d%02d",T);
  323. }
  324. /* Write current time in fixed-width RFC 822 format
  325.  * Accepts: destination string
  326.  */
  327. void rfc822_fixed_date (char *date)
  328. {
  329.   do_date (date,NIL,"%02d %s %4d %02d:%02d:%02d %+03d%02d",NIL);
  330. }
  331. /* Write current time in internal format
  332.  * Accepts: destination string
  333.  */
  334. void internal_date (char *date)
  335. {
  336.   do_date (date,NIL,"%02d-%s-%d %02d:%02d:%02d %+03d%02d",NIL);
  337. }
  338. /* Initialize server
  339.  * Accepts: server name for syslog or NIL
  340.  *     /etc/services service name or NIL
  341.  *     alternate /etc/services service name or NIL
  342.  *     SASL service name or NIL
  343.  *     clock interrupt handler
  344.  *     kiss-of-death interrupt handler
  345.  *     hangup interrupt handler
  346.  *     termination interrupt handler
  347.  */
  348. void server_init (char *server,char *service,char *altservice,char *sasl,
  349.   void *clkint,void *kodint,void *hupint,void *trmint)
  350. {
  351.   long port;
  352.   struct servent *sv;
  353.   struct sockaddr_in sin;
  354.   int sinlen = sizeof (struct sockaddr_in);
  355.   /* Don't use tcp_clienthost() since reverse DNS problems may slow down the
  356.    * greeting message and cause the client to time out.
  357.    */
  358.   char *client = getpeername (0,(struct sockaddr *) &sin,(void *) &sinlen) ?
  359.     "UNKNOWN" : inet_ntoa (sin.sin_addr);
  360. /* set server name in syslog */
  361.   if (server) openlog (server,LOG_PID,LOG_MAIL);
  362.   if (service && altservice && ((port = tcp_serverport ()) >= 0)) {
  363.     if ((sv = getservbyname (service,"tcp")) && (port == ntohs (sv->s_port)))
  364.       syslog (LOG_DEBUG,"%s service init from %s",service,client);
  365.     else if ((sv = getservbyname (altservice,"tcp")) &&
  366.      (port == ntohs (sv->s_port)))
  367.       syslog (LOG_DEBUG,"%s alternative service init from %s",
  368.       altservice,client);
  369.     else syslog (LOG_DEBUG,"port %ld service init from %s",port,client);
  370.   }
  371. /* set SASL name */
  372.   if (sasl) mail_parameters (NIL,SET_SERVICENAME,(void *) sasl);
  373.   arm_signal (SIGALRM,clkint); /* prepare for clock interrupt */
  374.   arm_signal (SIGUSR2,kodint); /* prepare for Kiss Of Death */
  375.   arm_signal (SIGHUP,hupint); /* prepare for hangup */
  376.   arm_signal (SIGTERM,trmint); /* prepare for termination */
  377. }
  378. /* Wait for stdin input
  379.  * Accepts: timeout in seconds
  380.  * Returns: T if have input on stdin, else NIL
  381.  */
  382. long server_input_wait (long seconds)
  383. {
  384.   fd_set rfd,efd;
  385.   struct timeval tmo;
  386.   FD_ZERO (&rfd);
  387.   FD_ZERO (&efd);
  388.   FD_SET (0,&rfd);
  389.   FD_SET (0,&efd);
  390.   tmo.tv_sec = seconds; tmo.tv_usec = 0;
  391.   return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
  392. }
  393. /* Server log in
  394.  * Accepts: user name string
  395.  *     password string
  396.  *     argument count
  397.  *     argument vector
  398.  * Returns: T if password validated, NIL otherwise
  399.  */
  400. long server_login (char *user,char *pwd,int argc,char *argv[])
  401. {
  402.   char *s,usr[MAILTMPLEN];
  403.   struct passwd *pw;
  404. /* cretins still haven't given up */
  405.   if (strlen (user) >= MAILTMPLEN)
  406.     syslog (LOG_ALERT|LOG_AUTH,"System break-in attempt, host=%.80s",
  407.     tcp_clienthost ());
  408. /* validate with case-independence */
  409.   else if ((logtry > 0) && ((pw = getpwnam (strcpy (usr,user))) ||
  410.     (pw = getpwnam (lcase (usr))))) {
  411.     if (auth_md5.server) { /* if using CRAM-MD5 authentication */
  412.       char *p = auth_md5_pwd (pw->pw_name);
  413.       if (p) { /* verify password */
  414. if (strcmp (p,pwd) && strcmp (p,pwd+1)) pw = NIL;
  415. memset (p,0,strlen (p));/* erase sensitive information */
  416. fs_give ((void **) &p); /* flush erased password */
  417. if (pw) return pw_login (pw,pw->pw_name,pw->pw_dir,argc,argv);
  418.       }
  419.       else syslog (LOG_ERR|LOG_AUTH,
  420.    "Login failed: %s has no CRAM-MD5 password, host=%.80s",
  421.    pw->pw_name,tcp_clienthost ());
  422.     }
  423. /* ordinary password authentication */
  424.     else if ((pw = checkpw (pw,pwd,argc,argv)) ||
  425.      ((*pwd == ' ') && (pw = checkpw (getpwnam(usr),pwd+1,argc,argv))))
  426.       return pw_login (pw,pw->pw_name,pw->pw_dir,argc,argv);
  427.   }
  428.   s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts";
  429. /* note the failure in the syslog */
  430.   syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ());
  431.   sleep (3); /* slow down possible cracker */
  432.   return NIL;
  433. }
  434. /* Authenticated server log in
  435.  * Accepts: user name string
  436.  *     argument count
  437.  *     argument vector
  438.  * Returns: T if password validated, NIL otherwise
  439.  */
  440. long authserver_login (char *user,int argc,char *argv[])
  441. {
  442.   struct passwd *pw = getpwnam (user);
  443.   return pw ? pw_login (pw,pw->pw_name,pw->pw_dir,argc,argv) : NIL;
  444. }
  445. /* Log in as anonymous daemon
  446.  * Accepts: argument count
  447.  *     argument vector
  448.  * Returns: T if successful, NIL if error
  449.  */
  450. long anonymous_login (int argc,char *argv[])
  451. {
  452.   struct passwd *pw = getpwnam ((char *) anonymous_user);
  453. /* log in Mr. A. N. Onymous */
  454.   return pw ? pw_login (pw,NIL,NIL,argc,argv) : NIL;
  455. }
  456. /* Finish log in and environment initialization
  457.  * Accepts: passwd struct for loginpw()
  458.  *     user name (NIL for anonymous)
  459.  *     home directory (NIL for anonymous)
  460.  *     argument count
  461.  *     argument vector
  462.  * Returns: T if successful, NIL if error
  463.  */
  464. long pw_login (struct passwd *pw,char *user,char *home,int argc,char *argv[])
  465. {
  466.   long ret = NIL;
  467.   char *u = user ? cpystr (user) : NIL;
  468.   char *h = home ? cpystr (home) : NIL;
  469.   if (pw->pw_uid && ((pw->pw_uid == geteuid ()) || loginpw (pw,argc,argv)) &&
  470.       (ret = env_init (u,h))) chdir (myhomedir ());
  471.   if (h) fs_give ((void **) &h);/* flush temporary home directory */
  472.   if (u) fs_give ((void **) &u);/* flush temporary user name */
  473.   return ret; /* return status */
  474. }
  475. /* Initialize environment
  476.  * Accepts: user name
  477.  *     home directory name
  478.  * Returns: T, always
  479.  */
  480. long env_init (char *user,char *home)
  481. {
  482.   extern MAILSTREAM CREATEPROTO;
  483.   extern MAILSTREAM EMPTYPROTO;
  484.   struct passwd *pw;
  485.   struct stat sbuf;
  486.   char *s,tmp[MAILTMPLEN];
  487.   if (myUserName) fatal ("env_init called twice!");
  488. /* myUserName must be set before dorc() call */
  489.   myUserName = cpystr (user ? user : (char *) anonymous_user);
  490. /* do systemwide configuration */
  491.   dorc ("/etc/c-client.cf",NIL);
  492.   if (!anonymousHome) anonymousHome = cpystr (ANONYMOUSHOME);
  493.   if (user) { /* remember user name and home directory */
  494.     if (blackBoxDir) { /* build black box directory name */
  495.       sprintf (tmp,"%s/%s",blackBoxDir,myUserName);
  496. /* if black box if exists and directory */
  497.       if (s = (!stat (tmp,&sbuf) && (sbuf.st_mode & S_IFDIR)) ?
  498.   tmp : blackBoxDefaultHome) {
  499. sprintf (tmp,"%s/INBOX",myHomeDir = cpystr (s));
  500. sysInbox = cpystr (tmp);/* set black box values in their place */
  501. blackBox = T;
  502. /* mbox meaningless if black box */
  503. mail_parameters (NIL,DISABLE_DRIVER,(void *) "mbox");
  504.       }
  505.     }
  506.     if (blackBox) /* black box? */
  507.       nslist[0] = &nshome,nslist[1] = &nsblackother,nslist[2] = &nsshared;
  508.     else { /* not a black box */
  509.       nslist[0] = &nshome,nslist[1] = &nsunixother,nslist[2] = &nsshared;
  510.       myHomeDir = cpystr (home);/* use real home directory */
  511. /* make sure user rc files don't try this */
  512.       blackBoxDir = blackBoxDefaultHome = "";
  513.     }
  514.   }
  515.   else { /* anonymous user */
  516.     nslist[0] = nslist[1] = NIL,nslist[2] = &nsftp;
  517.     sprintf (tmp,"%s/INBOX",myHomeDir = cpystr (anonymousHome));
  518.     sysInbox = cpystr (tmp); /* make system INBOX */
  519.     anonymous = T; /* flag as anonymous */
  520. /* make sure an error message happens */
  521.     if (!blackBoxDir) blackBoxDir = blackBoxDefaultHome = anonymousHome;
  522.   }
  523.   dorc (strcat (strcpy (tmp,myHomeDir),"/.mminit"),T);
  524.   dorc (strcat (strcpy (tmp,myHomeDir),"/.imaprc"),NIL);
  525.   if (!myLocalHost) mylocalhost ();
  526.   if (!myNewsrc) myNewsrc = cpystr(strcat (strcpy (tmp,myHomeDir),"/.newsrc"));
  527.   if (!newsActive) newsActive = cpystr (ACTIVEFILE);
  528.   if (!newsSpool) newsSpool = cpystr (NEWSSPOOL);
  529.   if (!ftpHome && (pw = getpwnam ("ftp"))) ftpHome = cpystr (pw->pw_dir);
  530.   if (!publicHome && (pw = getpwnam ("imappublic")))
  531.     publicHome = cpystr (pw->pw_dir);
  532.   if (!anonymous && !sharedHome && (pw = getpwnam ("imapshared")))
  533.     sharedHome = cpystr (pw->pw_dir);
  534. /* force default prototype to be set */
  535.   if (!createProto) createProto = &CREATEPROTO;
  536.   if (!appendProto) appendProto = &EMPTYPROTO;
  537. /* re-do open action to get flags */
  538.   (*createProto->dtb->open) (NIL);
  539.   endpwent (); /* close pw database */
  540.   return T;
  541. }
  542.  
  543. /* Return my user name
  544.  * Accepts: pointer to optional flags
  545.  * Returns: my user name
  546.  */
  547. char *myusername_full (unsigned long *flags)
  548. {
  549.   char *ret = (char *) unlogged_user;
  550.   if (!myUserName) { /* get user name if don't have it yet */
  551.     struct passwd *pw;
  552.     unsigned long euid = geteuid ();
  553.     char *s = (char *) (euid ? getlogin () : NIL);
  554. /* look up getlogin() user name or EUID */
  555.     if (!((s && *s && (pw = getpwnam (s)) && (pw->pw_uid == euid)) ||
  556.   (pw = getpwuid (euid)))) fatal ("Unable to look up user name");
  557. /* init environment if not root */
  558.     if (euid) env_init (pw->pw_name,((s=getenv("HOME")) && *s) ? s:pw->pw_dir);
  559.     else ret = pw->pw_name; /* in case UID 0 user is other than root */
  560.   }
  561.   if (myUserName) { /* logged in? */
  562.     if (flags) *flags = anonymous ? MU_ANONYMOUS : MU_LOGGEDIN;
  563.     ret = myUserName; /* return user name */
  564.   }
  565.   else if (flags) *flags = MU_NOTLOGGEDIN;
  566.   return ret;
  567. }
  568. /* Return my local host name
  569.  * Returns: my local host name
  570.  */
  571. char *mylocalhost ()
  572. {
  573.   char tmp[MAILTMPLEN];
  574.   struct hostent *host_name;
  575.   if (!myLocalHost) {
  576.     gethostname(tmp,MAILTMPLEN);/* get local host name */
  577.     myLocalHost = cpystr ((host_name = gethostbyname (tmp)) ?
  578.   host_name->h_name : tmp);
  579.   }
  580.   return myLocalHost;
  581. }
  582. /* Return my home directory name
  583.  * Returns: my home directory name
  584.  */
  585. char *myhomedir ()
  586. {
  587.   if (!myHomeDir) myusername ();/* initialize if first time */
  588.   return myHomeDir ? myHomeDir : "";
  589. }
  590. /* Return system standard INBOX
  591.  * Accepts: buffer string
  592.  */
  593. char *sysinbox ()
  594. {
  595.   char tmp[MAILTMPLEN];
  596.   if (!sysInbox) { /* initialize if first time */
  597.     sprintf (tmp,"%s/%s",MAILSPOOL,myusername ());
  598.     sysInbox = cpystr (tmp); /* system inbox is from mail spool */
  599.   }
  600.   return sysInbox;
  601. }
  602. /* Return mailbox directory name
  603.  * Accepts: destination buffer
  604.  *     directory prefix
  605.  *     name in directory
  606.  * Returns: file name or NIL if error
  607.  */
  608. char *mailboxdir (char *dst,char *dir,char *name)
  609. {
  610.   char tmp[MAILTMPLEN];
  611.   if (dir || name) { /* if either argument provided */
  612.     if (dir) strcpy (tmp,dir); /* write directory prefix */
  613.     else tmp[0] = ''; /* otherwise null string */
  614.     if (name) strcat (tmp,name);/* write name in directory */
  615. /* validate name, return its name */
  616.     if (!mailboxfile (dst,tmp)) return NIL;
  617.   }
  618.   else strcpy (dst,myhomedir());/* no arguments, wants home directory */
  619.   return dst; /* return the name */
  620. }
  621. /* Return mailbox file name
  622.  * Accepts: destination buffer
  623.  *     mailbox name
  624.  * Returns: file name or empty string for driver-selected INBOX or NIL if error
  625.  */
  626. char *mailboxfile (char *dst,char *name)
  627. {
  628.   struct passwd *pw;
  629.   char *dir = myhomedir ();
  630.   *dst = ''; /* default to empty string */
  631. /* check invalid name */
  632.   if (!name || !*name || (*name == '{')) return NIL;
  633. /* check for INBOX */
  634.   if (((name[0] == 'I') || (name[0] == 'i')) &&
  635.       ((name[1] == 'N') || (name[1] == 'n')) &&
  636.       ((name[2] == 'B') || (name[2] == 'b')) &&
  637.       ((name[3] == 'O') || (name[3] == 'o')) &&
  638.       ((name[4] == 'X') || (name[4] == 'x')) && !name[5]) {
  639. /* if restricted, canonicalize name of INBOX */
  640.     if (anonymous || blackBox) name = "INBOX";
  641.     else return dst; /* else driver selects the INBOX name */
  642.   }
  643. /* restricted name? */
  644.   else if ((*name == '#') || anonymous || blackBox) {
  645.     if (strstr (name,"..") || strstr (name,"//") || strstr (name,"/~"))
  646.       return NIL; /* none of these allowed when restricted */
  647.     switch (*name) { /* what kind of restricted name? */
  648.     case '#': /* namespace name */
  649.       if (((name[1] == 'f') || (name[1] == 'F')) &&
  650.   ((name[2] == 't') || (name[2] == 'T')) &&
  651.   ((name[3] == 'p') || (name[3] == 'P')) &&
  652.   (name[4] == '/') && (dir = ftpHome)) name += 5;
  653.       else if (((name[1] == 'p') || (name[1] == 'P')) &&
  654.        ((name[2] == 'u') || (name[2] == 'U')) &&
  655.        ((name[3] == 'b') || (name[3] == 'B')) &&
  656.        ((name[4] == 'l') || (name[4] == 'L')) &&
  657.        ((name[5] == 'i') || (name[5] == 'I')) &&
  658.        ((name[6] == 'c') || (name[6] == 'C')) &&
  659.        (name[7] == '/') && (dir = publicHome)) name += 8;
  660.       else if (!anonymous && ((name[1] == 's') || (name[1] == 'S')) &&
  661.        ((name[2] == 'h') || (name[2] == 'H')) &&
  662.        ((name[3] == 'a') || (name[3] == 'A')) &&
  663.        ((name[4] == 'r') || (name[4] == 'R')) &&
  664.        ((name[5] == 'e') || (name[5] == 'E')) &&
  665.        ((name[6] == 'd') || (name[6] == 'D')) &&
  666.        (name[7] == '/') && (dir = sharedHome)) name += 8;
  667.       else return NIL; /* unknown namespace name */
  668.       break;
  669.     case '/': /* rooted restricted name */
  670.       if (anonymous) return NIL;/* anonymous can't do this */
  671.       dir = blackBoxDir; /* base is black box directory */
  672.       name++; /* skip past delimiter */
  673.       break;
  674.     }
  675.   }
  676. /* absolute path name? */
  677.   else if (*name == '/') return strcpy (dst,name);
  678. /* some home directory? */
  679.   else if ((*name == '~') && *++name) {
  680.     if (*name == '/') name++; /* yes, my home directory? */
  681.     else { /* no, copy user name */
  682.       for (dir = dst; *name && (*name != '/'); *dir++ = *name++);
  683.       *dir++ = ''; /* tie off user name, look up in passwd file */
  684.       if (!((pw = getpwnam (dst)) && (dir = pw->pw_dir))) return NIL;
  685.       if (*name) name++; /* skip past the slash */
  686.     }
  687.   }
  688. /* build resulting name */
  689.   sprintf (dst,"%s/%s",dir,name);
  690.   return dst; /* return it */
  691. }
  692. /* Dot-lock file locker
  693.  * Accepts: file name to lock
  694.  *     destination buffer for lock file name
  695.  *     open file description on file name to lock
  696.  * Returns: T if success, NIL if failure
  697.  */
  698. long dotlock_lock (char *file,DOTLOCK *base,int fd)
  699. {
  700.   int ld,j;
  701.   int i = locktimeout * 60 - 1;
  702.   char *s,hitch[MAILTMPLEN],tmp[MAILTMPLEN];
  703.   time_t t;
  704.   struct stat sb;
  705. /* flush absurd file name */
  706.   if (strlen (file) > 512) return NIL;
  707. /* build lock filename */
  708.   sprintf (base->lock,"%s.lock",file);
  709. /* assume no pipe */
  710.   base->pipei = base->pipeo = -1;
  711. /* until OK or out of tries */
  712.   if (j = chk_notsymlink (base->lock,&sb)) do {
  713.     t = time (0); /* get the time now */
  714. #ifdef NFSKLUDGE
  715.   /* SUN-OS had an NFS, As kludgy as an albatross;
  716.    * And everywhere that it was installed, It was a total loss.
  717.    *  -- MRC 9/25/91
  718.    */
  719. /* time out old locks */
  720.     if ((j > 0) && (t >= (sb.st_ctime + locktimeout * 60))) unlink(base->lock);
  721. /* build hitching post file name */
  722.     sprintf (hitch,"%s.%lu.%d.",base->lock,(unsigned long) time (0),getpid ());
  723.     j = strlen (hitch); /* append local host name */
  724.     gethostname (hitch + j,(MAILTMPLEN - j) - 1);
  725. /* try to get hitching-post file */
  726.     if ((ld = open (hitch,O_WRONLY|O_CREAT|O_EXCL,(int)lock_protection)) >= 0){
  727.       close (ld); /* close the hitching-post */
  728.       link (hitch,base->lock); /* tie hitching-post to lock, ignore failure */
  729. /* success if link count now 2 */
  730.       ld = (!stat (hitch,&sb) && (sb.st_nlink == 2)) ? 0 : -1;
  731.       unlink (hitch); /* flush hitching post */
  732.     }
  733. #else
  734. /* time out old locks */
  735.     if ((j > 0) && (t >= (sb.st_ctime + locktimeout * 60))) {
  736.       unlink (base->lock); /* flush extant old lock */
  737.       j = O_WRONLY | O_CREAT; /* try to grab it for ourselves */
  738.     }
  739.     else j = O_WRONLY | O_CREAT | O_EXCL;
  740. /* try to get the lock */
  741.     if ((ld = open (strcpy (hitch,base->lock),j,(int) lock_protection)) >= 0)
  742.       close (ld); /* close the lock file */
  743. #endif
  744.     else switch (errno) { /* what happened? */
  745.       case EACCES: /* protection fail */
  746. if (stat (hitch,&sb)) { /* file exists, fall into EEXIST case */
  747.   int pi[2],po[2];
  748. /* make command pipes */
  749.   if (!stat (LOCKPGM,&sb) && pipe (pi) >= 0) {
  750.     if (pipe (po) >= 0) {
  751. /* make inferior process */
  752.       if (!(j = fork ())) {
  753. if (!fork ()) { /* make grandchild so it's inherited by init */
  754.   char *argv[4];/* prepare argument vector for */
  755.   sprintf (tmp,"%d",fd);
  756.   argv[0] = LOCKPGM; argv[1] = tmp;
  757.   argv[2] = file; argv[3] = NIL;
  758. /* set parent's I/O to my O/I */
  759.   dup2 (pi[1],1); dup2 (pi[1],2); dup2 (po[0],0);
  760. /* close all unnecessary descriptors */
  761.   for (j = max (20,max (max (pi[0],pi[1]),max (po[0],po[1])));
  762.        j >= 3; --j) if (j != fd) close (j);
  763. /* be our own process group */
  764.   setpgrp (0,getpid ());
  765. /* now run it */
  766.   execv (argv[0],argv);
  767. }
  768. _exit (1); /* child is done */
  769.       }
  770.       else if (j > 0) { /* reap child; grandchild now owned by init */
  771. grim_pid_reap (j,NIL);
  772. /* read response from locking program */
  773. if ((read (pi[0],tmp,1) == 1) && (tmp[0] == '+')) {
  774. /* success, record pipes */
  775.   base->pipei = pi[0]; base->pipeo = po[1];
  776. /* close child's side of the pipes */
  777.   close (pi[1]); close (po[0]);
  778.   return LONGT;
  779. }
  780.       }
  781.       close (po[0]); close (po[1]);
  782.     }
  783.     close (pi[0]); close (pi[1]);
  784.   }
  785.   if (lockEaccesError) {/* punt silently if paranoid site */
  786.     sprintf (tmp,"Mailbox vulnerable - directory %.80s",hitch);
  787.     if (s = strrchr (tmp,'/')) *s = '';
  788.     strcat (tmp," must have 1777 protection");
  789.     mm_log (tmp,WARN);
  790.   }
  791.   base->lock[0] = ''; /* give up on lock file */
  792. }
  793.       case EEXIST: /* file already exists */
  794. break; /* try again */
  795.       default: /* some other failure */
  796. sprintf (tmp,"Mailbox vulnerable - error creating %.80s: %s",
  797.  hitch,strerror (errno));
  798. mm_log (tmp,WARN); /* this is probably not good */
  799. base->lock[0] = ''; /* don't use lock files */
  800. break;
  801.       }
  802. /* if failed to make lock file and retry OK */
  803.     if ((ld < 0) && base->lock) {
  804.       if (!(i%15)) { /* time to notify? */
  805. sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
  806.  file,i);
  807. mm_log (tmp,WARN);
  808.       }
  809.       sleep (1); /* wait 1 second before next try */
  810.     }
  811.   } while (i-- && (ld < 0) && base->lock[0]);
  812. /* failed if no longer a lock name */
  813.   if (!base->lock[0]) return NIL;
  814. /* make sure others can break the lock */
  815.   chmod (base->lock,(int) lock_protection);
  816.   return LONGT;
  817. }
  818. /* Dot-lock file unlocker
  819.  * Accepts: lock file name
  820.  * Returns: T if success, NIL if failure
  821.  */
  822. long dotlock_unlock (DOTLOCK *base)
  823. {
  824.   long ret = LONGT;
  825.   if (base && base->lock[0]) {
  826.     if (base->pipei >= 0) { /* if running through a pipe unlocker */
  827.       ret = (write (base->pipeo,"+",1) == 1);
  828. /* nuke the pipes */
  829.       close (base->pipei); close (base->pipeo);
  830.     }
  831.     else ret = !unlink (base->lock);
  832.   }
  833.   return ret;
  834. }
  835. /* Lock file name
  836.  * Accepts: scratch buffer
  837.  *     file name
  838.  *     type of locking operation (LOCK_SH or LOCK_EX)
  839.  *     pointer to return PID of locker
  840.  * Returns: file descriptor of lock or negative if error
  841.  */
  842. int lockname (char *lock,char *fname,int op,long *pid)
  843. {
  844.   struct stat sbuf;
  845.   *pid = 0; /* no locker PID */
  846.   return stat (fname,&sbuf) ? -1 : lock_work (lock,&sbuf,op,pid);
  847. }
  848. /* Lock file descriptor
  849.  * Accepts: file descriptor
  850.  *     lock file name buffer
  851.  *     type of locking operation (LOCK_SH or LOCK_EX)
  852.  * Returns: file descriptor of lock or negative if error
  853.  */
  854. int lockfd (int fd,char *lock,int op)
  855. {
  856.   struct stat sbuf;
  857.   return fstat (fd,&sbuf) ? -1 : lock_work (lock,&sbuf,op,NIL);
  858. }
  859. /* Lock file name worker
  860.  * Accepts: lock file name
  861.  *     pointer to stat() buffer
  862.  *     type of locking operation (LOCK_SH or LOCK_EX)
  863.  *     pointer to return PID of locker
  864.  * Returns: file descriptor of lock or negative if error
  865.  */
  866. int lock_work (char *lock,void *sb,int op,long *pid)
  867. {
  868.   struct stat lsb,fsb;
  869.   struct stat *sbuf = sb;
  870.   char tmp[MAILTMPLEN];
  871.   long i;
  872.   int fd;
  873.   if (pid) *pid = 0; /* initialize return PID */
  874. /* make temporary lock file name */
  875.   sprintf (lock,"/tmp/.%lx.%lx",(unsigned long) sbuf->st_dev,
  876.    (unsigned long) sbuf->st_ino);
  877.   while (T) { /* until get a good lock */
  878.     do switch ((int) chk_notsymlink (lock,&lsb)) {
  879.     case 1: /* exists just once */
  880.       if (((fd = open (lock,O_RDWR,lock_protection)) >= 0) ||
  881.   (errno != ENOENT) || (chk_notsymlink (lock,&lsb) >= 0)) break;
  882.     case -1: /* name doesn't exist */
  883.       fd = open (lock,O_RDWR|O_CREAT|O_EXCL,lock_protection);
  884.       break;
  885.     default: /* multiple hard links */
  886.       mm_log ("hard link to lock name",ERROR);
  887.       syslog (LOG_CRIT,"SECURITY PROBLEM: hard link to lock name: %.80s",lock);
  888.     case 0: /* symlink (already did syslog) */
  889.       return -1; /* fail: no lock file */
  890.     } while ((fd < 0) && (errno == EEXIST));
  891.     if (fd < 0) { /* failed to get file descriptor */
  892.       syslog (LOG_INFO,"Mailbox lock file %s open failure: %s",lock,
  893.       strerror (errno));
  894.       return -1; /* fail: can't open lock file */
  895.     }
  896. /* non-blocking form */
  897.     if (op & LOCK_NB) i = flock (fd,op);
  898.     else { /* blocking form */
  899.       (*mailblocknotify) (BLOCK_FILELOCK,NIL);
  900.       i = flock (fd,op);
  901.       (*mailblocknotify) (BLOCK_NONE,NIL);
  902.     }
  903.     if (i) { /* failed, get other process' PID */
  904.       if (pid && !fstat (fd,&fsb) && (i = min (fsb.st_size,MAILTMPLEN-1)) &&
  905.   (read (fd,tmp,i) == i) && !(tmp[i] = 0) && ((i = atol (tmp)) > 0))
  906. *pid = i;
  907.       close (fd); /* failed, give up on lock */
  908.       return -1; /* fail: can't lock */
  909.     }
  910. /* make sure this lock is good for us */
  911.     if (!lstat (lock,&lsb) && ((lsb.st_mode & S_IFMT) != S_IFLNK) &&
  912. !fstat (fd,&fsb) && (lsb.st_dev == fsb.st_dev) &&
  913. (lsb.st_ino == fsb.st_ino) && (fsb.st_nlink == 1)) break;
  914.     close (fd); /* lock not right, drop fd and try again */
  915.   }
  916. /* make sure mode OK (don't use fchmod()) */
  917.   chmod (lock,(int) lock_protection);
  918.   return fd; /* success */
  919. }
  920. /* Check to make sure not a symlink
  921.  * Accepts: file name
  922.  *     stat buffer
  923.  * Returns: -1 if doesn't exist, NIL if symlink, else number of hard links
  924.  */
  925. long chk_notsymlink (char *name,void *sb)
  926. {
  927.   struct stat *sbuf = sb;
  928. /* name exists? */
  929.   if (lstat (name,sbuf)) return -1;
  930. /* forbid symbolic link */
  931.   if ((sbuf->st_mode & S_IFMT) == S_IFLNK) {
  932.     mm_log ("symbolic link on lock name",ERROR);
  933.     syslog (LOG_CRIT,"SECURITY PROBLEM: symbolic link on lock name: %.80s",
  934.     name);
  935.     return NIL;
  936.   }
  937.   return (long) sbuf->st_nlink; /* return number of hard links */
  938. }
  939. /* Unlock file descriptor
  940.  * Accepts: file descriptor
  941.  *     lock file name from lockfd()
  942.  */
  943. void unlockfd (int fd,char *lock)
  944. {
  945. /* delete the file if no sharers */
  946.   if (!flock (fd,LOCK_EX|LOCK_NB)) unlink (lock);
  947.   flock (fd,LOCK_UN); /* unlock it */
  948.   close (fd); /* close it */
  949. }
  950. /* Set proper file protection for mailbox
  951.  * Accepts: mailbox name
  952.  *     actual file path name
  953.  * Returns: T, always
  954.  */
  955. long set_mbx_protections (char *mailbox,char *path)
  956. {
  957.   struct stat sbuf;
  958.   int mode = (int) mbx_protection;
  959.   if (*mailbox == '#') { /* possible namespace? */
  960.       if (((mailbox[1] == 'f') || (mailbox[1] == 'F')) &&
  961.   ((mailbox[2] == 't') || (mailbox[2] == 'T')) &&
  962.   ((mailbox[3] == 'p') || (mailbox[3] == 'P')) &&
  963.   (mailbox[4] == '/')) mode = (int) ftp_protection;
  964.       else if (((mailbox[1] == 'p') || (mailbox[1] == 'P')) &&
  965.        ((mailbox[2] == 'u') || (mailbox[2] == 'U')) &&
  966.        ((mailbox[3] == 'b') || (mailbox[3] == 'B')) &&
  967.        ((mailbox[4] == 'l') || (mailbox[4] == 'L')) &&
  968.        ((mailbox[5] == 'i') || (mailbox[5] == 'I')) &&
  969.        ((mailbox[6] == 'c') || (mailbox[6] == 'C')) &&
  970.        (mailbox[7] == '/')) mode = (int) public_protection;
  971.       else if (((mailbox[1] == 's') || (mailbox[1] == 'S')) &&
  972.        ((mailbox[2] == 'h') || (mailbox[2] == 'H')) &&
  973.        ((mailbox[3] == 'a') || (mailbox[3] == 'A')) &&
  974.        ((mailbox[4] == 'r') || (mailbox[4] == 'R')) &&
  975.        ((mailbox[5] == 'e') || (mailbox[5] == 'E')) &&
  976.        ((mailbox[6] == 'd') || (mailbox[6] == 'D')) &&
  977.        (mailbox[7] == '/')) mode = (int) shared_protection;
  978.   }
  979. /* if a directory */
  980.   if (!stat (path,&sbuf) && ((sbuf.st_mode & S_IFMT) == S_IFDIR)) {
  981. /* set owner search if allow read or write */
  982.     if (mode & 0600) mode |= 0100;
  983.     if (mode & 060) mode |= 010;/* set group search if allow read or write */
  984.     if (mode & 06) mode |= 01; /* set world search if allow read or write */
  985.   }
  986.   chmod (path,mode); /* set the new protection, ignore failure */
  987.   return LONGT;
  988. }
  989. /* Determine default prototype stream to user
  990.  * Accepts: type (NIL for create, T for append)
  991.  * Returns: default prototype stream
  992.  */
  993. MAILSTREAM *default_proto (long type)
  994. {
  995.   myusername (); /* make sure initialized */
  996. /* return default driver's prototype */
  997.   return type ? appendProto : createProto;
  998. }
  999. /* Set up user flags for stream
  1000.  * Accepts: MAIL stream
  1001.  * Returns: MAIL stream with user flags set up
  1002.  */
  1003. MAILSTREAM *user_flags (MAILSTREAM *stream)
  1004. {
  1005.   int i;
  1006.   myusername (); /* make sure initialized */
  1007.   for (i = 0; i < NUSERFLAGS && userFlags[i]; ++i)
  1008.     if (!stream->user_flags[i]) stream->user_flags[i] = cpystr (userFlags[i]);
  1009.   return stream;
  1010. }
  1011. /* Return nth user flag
  1012.  * Accepts: user flag number
  1013.  * Returns: flag
  1014.  */
  1015. char *default_user_flag (unsigned long i)
  1016. {
  1017.   myusername (); /* make sure initialized */
  1018.   return userFlags[i];
  1019. }
  1020. /* Process rc file
  1021.  * Accepts: file name
  1022.  *     .mminit flag
  1023.  * Don't even think of using this feature.
  1024.  */
  1025. void dorc (char *file,long flag)
  1026. {
  1027.   int i;
  1028.   char *s,*t,*k,tmp[MAILTMPLEN],tmpx[MAILTMPLEN];
  1029.   extern MAILSTREAM CREATEPROTO;
  1030.   extern MAILSTREAM EMPTYPROTO;
  1031.   DRIVER *d;
  1032.   FILE *f = fopen (file,"r");
  1033. /* no file or ill-advised usage */
  1034.   if (!(f && (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'n')) &&
  1035. (flag ||
  1036.  (!strcmp (s,"I accept the risk for IMAP toolkit 4.1.n") &&
  1037.   (s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'n')))))) return;
  1038.   do {
  1039.     *t++ = ''; /* tie off line, find second space */
  1040.     if ((k = strchr (s,' ')) && (k = strchr (++k,' '))) {
  1041.       *k++ = ''; /* tie off two words*/
  1042.       lcase (s); /* make case-independent */
  1043.       if (!(userFlags[0] || strcmp (s,"set keywords"))) {
  1044. k = strtok (k,", "); /* yes, get first keyword */
  1045. /* copy keyword list */
  1046. for (i = 0; k && i < NUSERFLAGS; ++i) {
  1047.   if (userFlags[i]) fs_give ((void **) &userFlags[i]);
  1048.   userFlags[i] = cpystr (k);
  1049.   k = strtok (NIL,", ");
  1050. }
  1051.       }
  1052.       else if (!flag) { /* none of these valid in .mminit */
  1053. if (!(createProto || strcmp (s,"set new-folder-format"))) {
  1054.   if (!strcmp (lcase (k),"same-as-inbox"))
  1055.     createProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
  1056.     strcmp (d->name,"dummy")) ?
  1057.       ((*d->open) (NIL)) : &CREATEPROTO;
  1058.   else if (!strcmp (k,"system-standard")) createProto = &CREATEPROTO;
  1059.   else { /* see if a driver name */
  1060.     for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
  1061.  d && strcmp (d->name,k); d = d->next);
  1062.     if (d) createProto = (*d->open) (NIL);
  1063.     else { /* duh... */
  1064.       sprintf (tmpx,"Unknown new folder format in %s: %s",file,k);
  1065.       mm_log (tmpx,WARN);
  1066.     }
  1067.   }
  1068. }
  1069. if (!(appendProto || strcmp (s,"set empty-folder-format"))) {
  1070.   if (!strcmp (lcase (k),"same-as-inbox"))
  1071.     appendProto = ((d = mail_valid (NIL,"INBOX",NIL)) &&
  1072.     strcmp (d->name,"dummy")) ?
  1073.       ((*d->open) (NIL)) : &EMPTYPROTO;
  1074.   else if (!strcmp (k,"system-standard")) appendProto = &EMPTYPROTO;
  1075.   else { /* see if a driver name */
  1076.     for (d = (DRIVER *) mail_parameters (NIL,GET_DRIVERS,NIL);
  1077.  d && strcmp (d->name,k); d = d->next);
  1078.     if (d) appendProto = (*d->open) (NIL);
  1079.     else { /* duh... */
  1080.       sprintf (tmpx,"Unknown empty folder format in %s: %s",file,k);
  1081.       mm_log (tmpx,WARN);
  1082.     }
  1083.   }
  1084. }
  1085. else if (!strcmp (s,"set from-widget"))
  1086.   mail_parameters (NIL,SET_FROMWIDGET,strcmp (lcase (k),"header-only")
  1087.    ? (void *) T : NIL);
  1088. else if (!strcmp (s,"set black-box-directory")) {
  1089.   if (blackBoxDir) /* users aren't allowed to do this */
  1090.     mm_log ("Can't set black-box-directory in user init",ERROR);
  1091.   else blackBoxDir = cpystr (k);
  1092. }
  1093. else if (!strcmp (s,"set black-box-default-home-directory")) {
  1094.   if (!blackBoxDir)
  1095.     mm_log ("Must set black-box-directory before default-home",ERROR);
  1096.   else if (blackBoxDefaultHome)
  1097.     mm_log ("Can't set black-box-default-home-directory in user init",
  1098.     ERROR);
  1099.   else blackBoxDefaultHome = cpystr (k);
  1100. }
  1101. else if (!strcmp (s,"set local-host")) {
  1102.   fs_give ((void **) &myLocalHost);
  1103.   myLocalHost = cpystr (k);
  1104. }
  1105. else if (!strcmp (s,"set news-active-file")) {
  1106.   fs_give ((void **) &newsActive);
  1107.   newsActive = cpystr (k);
  1108. }
  1109. else if (!strcmp (s,"set news-spool-directory")) {
  1110.   fs_give ((void **) &newsSpool);
  1111.   newsSpool = cpystr (k);
  1112. }
  1113. else if (!strcmp (s,"set news-state-file")) {
  1114.   fs_give ((void **) &myNewsrc);
  1115.   myNewsrc = cpystr (k);
  1116. }
  1117. else if (!strcmp (s,"set anonymous-home-directory")) {
  1118.   fs_give ((void **) &anonymousHome);
  1119.   anonymousHome = cpystr (k);
  1120. }
  1121. else if (!strcmp (s,"set ftp-export-directory")) {
  1122.   fs_give ((void **) &ftpHome);
  1123.   ftpHome = cpystr (k);
  1124. }
  1125. else if (!strcmp (s,"set public-home-directory")) {
  1126.   fs_give ((void **) &publicHome);
  1127.   publicHome = cpystr (k);
  1128. }
  1129. else if (!strcmp (s,"set shared-home-directory")) {
  1130.   fs_give ((void **) &sharedHome);
  1131.   sharedHome = cpystr (k);
  1132. }
  1133. else if (!strcmp (s,"set system-inbox")) {
  1134.   fs_give ((void **) &sysInbox);
  1135.   sysInbox = cpystr (k);
  1136. }
  1137. else if (!strcmp (s,"set rsh-command"))
  1138.   mail_parameters (NIL,SET_RSHCOMMAND,(void *) k);
  1139. else if (!strcmp (s,"set rsh-path"))
  1140.   mail_parameters (NIL,SET_RSHPATH,(void *) k);
  1141. else if (!strcmp (s,"set tcp-open-timeout"))
  1142.   mail_parameters (NIL,SET_OPENTIMEOUT,(void *) atol (k));
  1143. else if (!strcmp (s,"set tcp-read-timeout"))
  1144.   mail_parameters (NIL,SET_READTIMEOUT,(void *) atol (k));
  1145. else if (!strcmp (s,"set tcp-write-timeout"))
  1146.   mail_parameters (NIL,SET_WRITETIMEOUT,(void *) atol (k));
  1147. else if (!strcmp (s,"set rsh-timeout"))
  1148.   mail_parameters (NIL,SET_RSHTIMEOUT,(void *) atol (k));
  1149. else if (!strcmp (s,"set maximum-login-trials"))
  1150.   mail_parameters (NIL,SET_MAXLOGINTRIALS,(void *) atol (k));
  1151. else if (!strcmp (s,"set lookahead"))
  1152.   mail_parameters (NIL,SET_LOOKAHEAD,(void *) atol (k));
  1153. else if (!strcmp (s,"set prefetch"))
  1154.   mail_parameters (NIL,SET_PREFETCH,(void *) atol (k));
  1155. else if (!strcmp (s,"set close-on-error"))
  1156.   mail_parameters (NIL,SET_CLOSEONERROR,(void *) atol (k));
  1157. else if (!strcmp (s,"set imap-port"))
  1158.   mail_parameters (NIL,SET_IMAPPORT,(void *) atol (k));
  1159. else if (!strcmp (s,"set pop3-port"))
  1160.   mail_parameters (NIL,SET_POP3PORT,(void *) atol (k));
  1161. else if (!strcmp (s,"set uid-lookahead"))
  1162.   mail_parameters (NIL,SET_UIDLOOKAHEAD,(void *) atol (k));
  1163. else if (!strcmp (s,"set mailbox-protection"))
  1164.   mbx_protection = atol (k);
  1165. else if (!strcmp (s,"set directory-protection"))
  1166.   dir_protection = atol (k);
  1167. else if (!strcmp (s,"set lock-protection"))
  1168.   lock_protection = atol (k);
  1169. else if (!strcmp (s,"set ftp-protection"))
  1170.   ftp_protection = atol (k);
  1171. else if (!strcmp (s,"set public-protection"))
  1172.   public_protection = atol (k);
  1173. else if (!strcmp (s,"set shared-protection"))
  1174.   shared_protection = atol (k);
  1175. else if (!strcmp (s,"set dot-lock-file-timeout"))
  1176.   locktimeout = atol (k);
  1177. else if (!strcmp (s,"set disable-fcntl-locking"))
  1178.   disableFcntlLock = atol (k);
  1179. else if (!strcmp (s,"set lock-eacces-error"))
  1180.   lockEaccesError = atol (k);
  1181. else if (!strcmp (s,"set list-maximum-level"))
  1182.   list_max_level = atol (k);
  1183. else if (!strcmp (s,"set allowed-login-attempts"))
  1184.   logtry = atoi (k);
  1185. else if (!strcmp (s,"set try-alt-driver-first"))
  1186.   mail_parameters (NIL,SET_TRYALTFIRST,(void *) atol (k));
  1187. else if (!strcmp (s,"set allow-reverse-dns"))
  1188.   mail_parameters (NIL,SET_ALLOWREVERSEDNS,(void *) atol (k));
  1189.       }
  1190.     }
  1191.   }
  1192.   while ((s = fgets (tmp,MAILTMPLEN,f)) && (t = strchr (s,'n')));
  1193.   fclose (f); /* flush the file */
  1194. }
  1195. /* INBOX create function for tmail/dmail use only
  1196.  * Accepts: mail stream
  1197.  *     path name buffer, preloaded with driver-dependent path
  1198.  * Returns: T on success, NIL on failure
  1199.  *
  1200.  * This routine is evil and a truly incredible kludge.  It is private for
  1201.  * tmail/dmail and is not supported for any other application.
  1202.  */
  1203. long path_create (MAILSTREAM *stream,char *path)
  1204. {
  1205.   long ret;
  1206. /* do the easy thing if not a black box */
  1207.   if (!blackBox) return mail_create (stream,path);
  1208. /* toss out driver dependent names */
  1209.   printf (path,"%s/INBOX",myhomedir ());
  1210.   blackBox = NIL; /* well that's evil - evil is going on */
  1211.   ret = mail_create (stream,path);
  1212.   blackBox = T; /* restore the box */
  1213.   return ret;
  1214. }
  1215. /* Default block notify routine
  1216.  * Accepts: reason for calling
  1217.  *     data
  1218.  * Returns: data
  1219.  */
  1220. void *mm_blocknotify (int reason,void *data)
  1221. {
  1222.   void *ret = data;
  1223.   switch (reason) {
  1224.   case BLOCK_SENSITIVE: /* entering sensitive code */
  1225.     data = (void *) max (alarm (0),1);
  1226.     break;
  1227.   case BLOCK_NONSENSITIVE: /* exiting sensitive code */
  1228.     if ((unsigned int) data) alarm ((unsigned int) data);
  1229.     break;
  1230.   default: /* ignore all other reasons */
  1231.     break;
  1232.   }
  1233.   return ret;
  1234. }