]> granicus.if.org Git - uw-imap/commitdiff
add files for 2008-02-15T22:48:09Z
authorUnknown <>
Fri, 15 Feb 2008 22:48:09 +0000 (22:48 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Fri, 7 Sep 2018 00:02:39 +0000 (00:02 +0000)
src/osdep/nt/env_nt.c [new file with mode: 0644]

diff --git a/src/osdep/nt/env_nt.c b/src/osdep/nt/env_nt.c
new file mode 100644 (file)
index 0000000..18bc236
--- /dev/null
@@ -0,0 +1,774 @@
+/* ========================================================================
+ * 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
+ *
+ * 
+ * ========================================================================
+ */
+
+/*
+ * Program:    NT environment routines
+ *
+ * Author:     Mark Crispin
+ *             UW Technology
+ *             University of Washington
+ *             Seattle, WA  98195
+ *             Internet: MRC@Washington.EDU
+ *
+ * Date:       1 August 1988
+ * Last Edited:        15 February 2008
+ */
+\f
+static char *myUserName = NIL; /* user name */
+static char *myLocalHost = NIL;        /* local host name */
+static char *myHomeDir = NIL;  /* home directory name */
+static char *myNewsrc = NIL;   /* newsrc file name */
+static char *sysInbox = NIL;   /* system inbox name */
+static long list_max_level = 5;        /* maximum level of list recursion */
+                               /* block environment init */
+static short block_env_init = NIL;
+static short no822tztext = NIL;        /* disable RFC [2]822 timezone text */
+                               /* home namespace */
+static NAMESPACE nshome = {"",'\\',NIL,NIL};
+                               /* UNIX other user namespace */
+static NAMESPACE nsother = {"#user.",'\\',NIL,NIL};
+                               /* namespace list */
+static NAMESPACE *nslist[3] = {&nshome,&nsother,NIL};
+static long alarm_countdown = 0;/* alarm count down */
+static void (*alarm_rang) ();  /* alarm interrupt function */
+static unsigned int rndm = 0;  /* initial `random' number */
+static int server_nli = 0;     /* server and not logged in */
+static int logtry = 3;         /* number of login tries */
+                               /* block notification */
+static blocknotify_t mailblocknotify = mm_blocknotify;
+                               /* callback to get username */
+static userprompt_t mailusername = NIL;
+static long is_nt = -1;                /* T if NT, NIL if not NT, -1 unknown */
+static HINSTANCE netapi = NIL;
+typedef NET_API_STATUS (CALLBACK *GETINFO) (LPCWSTR,LPCWSTR,DWORD,LPBYTE *);
+static GETINFO getinfo = NIL;
+
+#include "write.c"             /* include safe writing routines */
+#include "pmatch.c"            /* include wildcard pattern matcher */
+
+
+/* Get all authenticators */
+
+#include "auths.c"
+\f
+/* 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_USERPROMPT :
+    mailusername = (userprompt_t) value;
+  case GET_USERPROMPT :
+    ret = (void *) mailusername;
+    break;
+  case SET_HOMEDIR:
+    if (myHomeDir) fs_give ((void **) &myHomeDir);
+    myHomeDir = cpystr ((char *) value);
+  case GET_HOMEDIR:
+    ret = (void *) myHomeDir;
+    break;
+  case SET_LOCALHOST:
+    myLocalHost = cpystr ((char *) value);
+  case GET_LOCALHOST:
+    if (myLocalHost) fs_give ((void **) &myLocalHost);
+    ret = (void *) myLocalHost;
+    break;
+  case SET_NEWSRC:
+    if (myNewsrc) fs_give ((void **) &myNewsrc);
+    myNewsrc = cpystr ((char *) value);
+  case GET_NEWSRC:
+    if (!myNewsrc) {           /* set news file name if not defined */
+      char tmp[MAILTMPLEN];
+      sprintf (tmp,"%s\\NEWSRC",myhomedir ());
+      myNewsrc = cpystr (tmp);
+    }
+    ret = (void *) myNewsrc;
+    break;
+  case SET_SYSINBOX:
+    if (sysInbox) fs_give ((void **) &sysInbox);
+    sysInbox = cpystr ((char *) value);
+  case GET_SYSINBOX:
+    ret = (void *) sysInbox;
+    break;
+  case SET_LISTMAXLEVEL:
+    list_max_level = (long) value;
+  case GET_LISTMAXLEVEL:
+    ret = (void *) list_max_level;
+    break;
+  case SET_DISABLE822TZTEXT:
+    no822tztext = value ? T : NIL;
+  case GET_DISABLE822TZTEXT:
+    ret = (void *) (no822tztext ? 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;
+  }
+  return ret;
+}
+\f
+/* 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);
+  if (suffix) {                        /* append timezone suffix if desired */
+    char *tz;
+    tzset ();                  /* get timezone from TZ environment stuff */
+    tz = tzname[daylight ? (((struct tm *) t)->tm_isdst > 0) : 0];
+    if (tz && tz[0]) {
+      char *s;
+      for (s = tz; *s; s++) if (*s & 0x80) return;
+      sprintf (date + strlen (date)," (%.50s)",tz);
+    }
+  }
+}
+
+
+/* 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);
+}
+\f
+/* Return random number
+ */
+
+long random (void)
+{
+  if (!rndm) srand (rndm = (unsigned) time (0L));
+  return (long) rand ();
+}
+
+
+/* Set alarm timer
+ * Accepts: new value
+ * Returns: old alarm value
+ */
+
+long alarm (long seconds)
+{
+  long ret = alarm_countdown;
+  alarm_countdown = seconds;
+  return ret;
+}
+
+
+/* The clock ticked
+ */
+
+void CALLBACK clock_ticked (UINT IDEvent,UINT uReserved,DWORD dwUser,
+                           DWORD dwReserved1,DWORD dwReserved2)
+{
+  if (alarm_rang && !--alarm_countdown) (*alarm_rang) ();
+}
+\f
+/* 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)
+{
+  if (!check_nt ()) {
+    if (!auth_md5.server) fatal ("Can't run on Windows without MD5 database");
+    server_nli = T;            /* Windows server not logged in */
+  }
+                               /* only do this if for init call */
+  if (server && service && sslservice) {
+    long port;
+    struct servent *sv;
+                               /* set server name in syslog */
+    openlog (server,LOG_PID,LOG_MAIL);
+    fclose (stderr);           /* possibly save a process ID */
+    /* 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);
+      }
+    }
+                               /* make sure stdout does binary */
+    setmode (fileno (stdin),O_BINARY);
+    setmode (fileno (stdout),O_BINARY);
+  }
+  alarm_rang = clkint;         /* note the clock interrupt */
+  timeBeginPeriod (1000);      /* set the timer interval */
+  timeSetEvent (1000,1000,clock_ticked,NIL,TIME_PERIODIC);
+}
+
+
+/* Wait for stdin input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long server_input_wait (long seconds)
+{
+  fd_set rfd,efd;
+  struct timeval tmo;
+  FD_ZERO (&rfd);
+  FD_ZERO (&efd);
+  FD_SET (0,&rfd);
+  FD_SET (0,&efd);
+  tmo.tv_sec = seconds; tmo.tv_usec = 0;
+  return select (1,&rfd,0,&efd,&tmo) ? LONGT : NIL;
+}
+\f
+/* Server log in
+ * Accepts: user name string
+ *         password string
+ *         authenticating user name string
+ *         argument count
+ *         argument vector
+ * Returns: T if password validated, NIL otherwise
+ */
+
+static int gotprivs = NIL;     /* once-only flag to grab privileges */
+
+long server_login (char *user,char *pass,char *authuser,int argc,char *argv[])
+{
+  HANDLE hdl;
+  LUID tcbpriv;
+  TOKEN_PRIVILEGES tkp;
+  char *s;
+                               /* need to get privileges? */
+  if (!gotprivs++ && check_nt ()) {
+                               /* hack for inetlisn */
+    if (argc >= 2) myClientHost = argv[1];
+                               /* get process token and TCB priv value */
+    if (!(OpenProcessToken (GetCurrentProcess (),
+                           TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,&hdl) &&
+         LookupPrivilegeValue ((LPSTR) NIL,SE_TCB_NAME,&tcbpriv)))
+      return NIL;
+    tkp.PrivilegeCount = 1;    /* want to enable this privilege */
+    tkp.Privileges[0].Luid = tcbpriv;
+    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+                               /* enable it */
+    AdjustTokenPrivileges (hdl,NIL,&tkp,sizeof (TOKEN_PRIVILEGES),
+                          (PTOKEN_PRIVILEGES) NIL,(PDWORD) NIL);
+                               /* make sure it won */
+    if (GetLastError() != ERROR_SUCCESS) return NIL;
+  }
+\f
+                               /* cretins still haven't given up */
+  if ((strlen (user) >= MAILTMPLEN) ||
+      (authuser && (strlen (authuser) >= MAILTMPLEN)))
+    syslog (LOG_ALERT,"SYSTEM BREAK-IN ATTEMPT, host=%.80s",tcp_clienthost ());
+  else if (logtry > 0) {       /* still have available logins? */
+                               /* authentication user not supported */
+    if (authuser && *authuser && compare_cstring (authuser,user))
+      mm_log ("Authentication id must match authorization id",ERROR);
+    if (check_nt ()) {         /* NT: authserver_login() call not supported */
+      if (!pass) mm_log ("Unsupported authentication mechanism",ERROR);
+      else if ((               /* try to login and impersonate the guy */
+#ifdef LOGIN32_LOGON_NETWORK
+               LogonUser (user,".",pass,LOGON32_LOGON_NETWORK,
+                          LOGON32_PROVIDER_DEFAULT,&hdl) ||
+#endif
+               LogonUser (user,".",pass,LOGON32_LOGON_INTERACTIVE,
+                          LOGON32_PROVIDER_DEFAULT,&hdl) ||
+               LogonUser (user,".",pass,LOGON32_LOGON_BATCH,
+                          LOGON32_PROVIDER_DEFAULT,&hdl) ||
+               LogonUser (user,".",pass,LOGON32_LOGON_SERVICE,
+                          LOGON32_PROVIDER_DEFAULT,&hdl)) &&
+              ImpersonateLoggedOnUser (hdl)) return env_init (user,NIL);
+    }
+    else {                     /* Win9x: done if from authserver_login() */
+      if (!pass) server_nli = NIL;
+                               /* otherwise check MD5 database */
+      else if (s = auth_md5_pwd (user)) {
+                               /* change NLI state based on pwd match */
+       server_nli = strcmp (s,pass);
+       memset (s,0,strlen (s));/* erase sensitive information */
+       fs_give ((void **) &s); /* flush erased password */
+      }
+                               /* success if no longer NLI */
+      if (!server_nli) return env_init (user,NIL);
+    }
+  }
+  s = (logtry-- > 0) ? "Login failure" : "Excessive login attempts";
+                               /* note the failure in the syslog */
+  syslog (LOG_INFO,"%s user=%.80s host=%.80s",s,user,tcp_clienthost ());
+  sleep (3);                   /* slow down possible cracker */
+  return NIL;
+}
+\f
+/* Authenticated server log in
+ * Accepts: user name string
+ *         authentication 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 server_login (user,NIL,authuser,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[])
+{
+  return server_login ("Guest",NIL,NIL,argc,argv);
+}
+
+
+/* Initialize environment
+ * Accepts: user name
+ *          home directory, or NIL to use default
+ * Returns: T, always
+ */
+
+long env_init (char *user,char *home)
+{
+                               /* don't init if blocked */
+  if (block_env_init) return LONGT;
+  if (myUserName) fatal ("env_init called twice!");
+  myUserName = cpystr (user);  /* remember user name */
+  if (!myHomeDir)              /* only if home directory not set up yet */
+    myHomeDir = (home && *home) ? cpystr (home) : win_homedir (user);
+  return T;
+}
+\f
+/* Check if NT
+ * Returns: T if NT, NIL if Win9x
+ */
+
+int check_nt (void)
+{
+  if (is_nt < 0) {             /* not yet set up? */
+    OSVERSIONINFO ver;
+    ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+    GetVersionEx (&ver);
+    is_nt = (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) ? T : NIL;
+  }
+  return is_nt;
+}
+
+
+/* Return Windows home directory
+ * Accepts: user name
+ * Returns: home directory
+ */
+
+char *win_homedir (char *user)
+{
+  char *s,*t,tmp[MAILTMPLEN];
+  PUSER_INFO_1 ui;
+                               /* Win9x default */
+  if (!check_nt ()) sprintf (tmp,"%s\\My Documents",defaultDrive ());
+                               /* get from user info on NT */
+  else if ((netapi || (netapi = LoadLibrary ("netapi32.dll"))) &&
+          (getinfo ||
+           (getinfo = (GETINFO) GetProcAddress (netapi,"NetUserGetInfo"))) &&
+          MultiByteToWideChar (CP_ACP,0,user,strlen (user) + 1,
+                               (WCHAR *) tmp,MAILTMPLEN) &&
+          !(*getinfo) (NIL,(LPWSTR) &tmp,1,(LPBYTE *) &ui) &&
+          WideCharToMultiByte (CP_ACP,0,ui->usri1_home_dir,-1,
+                               tmp,MAILTMPLEN,NIL,NIL) && tmp[0]) {
+                               /* make sure doesn't end with delimiter */
+    if ((*(s = tmp + strlen (tmp) - 1) == '\\') || (*s == '/')) *s = '\0';
+  }
+                               /* no home dir, found Win2K user profile? */
+  else if ((s = getenv ("USERPROFILE")) && (t = strrchr (s,'\\'))) {      
+    strncpy (tmp,s,t-s);       /* copy up to user name */
+    sprintf (tmp+(t-s),"\\%.100s\\My Documents",user);
+  }
+                               /* last resort NT default */
+  else sprintf (tmp,"%s\\users\\default",defaultDrive ());
+  return cpystr (tmp);
+}
+
+
+/* Return default drive
+ * Returns: default drive
+ */
+
+static char *defaultDrive (void)
+{
+  char *s = getenv ("SystemDrive");
+  return (s && *s) ? s : "C:";
+}
+\f
+/* Return my user name
+ * Accepts: pointer to optional flags
+ * Returns: my user name
+ */
+
+char *myusername_full (unsigned long *flags)
+{
+  UCHAR usr[MAILTMPLEN];
+  DWORD len = MAILTMPLEN;
+  char *user,*path,*d,*p,pth[MAILTMPLEN];
+  char *ret = "SYSTEM";
+                               /* get user name if don't have it yet */
+  if (!myUserName && !server_nli &&
+                               /* use callback, else logon name */
+      ((mailusername && (user = (char *) (*mailusername) ())) ||
+       (GetUserName (usr,&len) && _stricmp (user = (char *) usr,"SYSTEM")))) {
+    if (block_env_init) {      /* don't env_init if blocked */
+      if (flags) *flags = MU_LOGGEDIN;
+      return user;
+    }
+                               /* try HOMEPATH, then HOME */
+    if (p = getenv ("HOMEPATH"))
+      sprintf (path = pth,"%s%s",
+              (d = getenv ("HOMEDRIVE")) ? d : defaultDrive (),p);
+    else if (!(path = getenv ("HOME")))
+      sprintf (path = pth,"%s\\My Documents",defaultDrive ());
+                               /* make sure doesn't end with delimiter */
+    if ((*(p = path + strlen (path) -1) == '\\') || (*p == '/')) *p = '\0';
+    env_init (user,path);      /* initialize environment */
+  }
+  if (myUserName) {            /* logged in? */
+    if (flags)                 /* Guest is an anonymous user */
+      *flags = _stricmp (myUserName,"Guest") ? MU_LOGGEDIN : MU_ANONYMOUS;
+    ret = myUserName;          /* return user name */
+  }
+  else if (flags) *flags = MU_NOTLOGGEDIN;
+  return ret;
+}
+\f
+/* Return my local host name
+ * Returns: my local host name
+ */
+
+char *mylocalhost (void)
+{
+  if (!myLocalHost) {
+    char tmp[MAILTMPLEN];
+    if (!wsa_initted++) {      /* init Windows Sockets */
+      WSADATA wsock;
+      if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+       wsa_initted = 0;
+       return "random-pc";     /* try again later? */
+      }
+    }
+    myLocalHost = cpystr ((gethostname (tmp,MAILTMPLEN-1) == SOCKET_ERROR) ?
+                         "random-pc" : tcp_canonical (tmp));
+  }
+  return myLocalHost;
+}
+\f
+/* Return my home directory name
+ * Returns: my home directory name
+ */
+
+char *myhomedir ()
+{
+  if (!myHomeDir) myusername ();/* initialize if first time */
+  return myHomeDir ? myHomeDir : "";
+}
+
+
+/* Return system standard INBOX
+ * Accepts: buffer string
+ */
+
+char *sysinbox ()
+{
+  char tmp[MAILTMPLEN];
+  if (!sysInbox) {             /* initialize if first time */
+    if (check_nt ()) sprintf (tmp,MAILFILE,myUserName);
+    else sprintf (tmp,"%s\\INBOX",myhomedir ());
+    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;
+  }
+  else strcpy (dst,myhomedir());/* no arguments, wants home directory */
+  return dst;                  /* return the name */
+}
+\f
+/* 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)
+{
+  char homedev[3];
+  char *dir = myhomedir ();
+  if (dir[0] && isalpha (dir[0]) && (dir[1] == ':')) {
+    homedev[0] = dir[0];       /* copy home device */
+    homedev[1] = dir[1];
+    homedev[2] = '\0';
+  }
+  else homedev[0] = '\0';      /* ??no home device?? */
+  *dst = '\0';                 /* default to empty string */
+                               /* check for INBOX */
+  if (!compare_cstring (name,"INBOX"));
+                               /* reject names with / */
+  else if (strchr (name,'/')) dst = NIL;
+  else switch (*name) {
+  case '#':                    /* namespace names */
+    if (((name[1] == 'u') || (name[1] == 'U')) &&
+       ((name[2] == 's') || (name[2] == 'S')) &&
+       ((name[3] == 'e') || (name[3] == 'E')) &&
+       ((name[4] == 'r') || (name[4] == 'R')) && (name[5] == '.')) {
+                               /* copy user name to destination buffer */
+      for (dir = dst,name += 6; *name && (*name != '\\'); *dir++ = *name++);
+      *dir++ = '\0';           /* tie off user name */
+                               /* look up homedir for user name */
+      if (dir = win_homedir (dst)) {
+                               /* build resulting name */
+       sprintf (dst,"%s\\%s",dir,name);
+       fs_give ((void **) &dir);
+      }
+      else dst = NIL;
+    }
+    else dst = NIL;            /* unknown namespace name */
+    break;
+  case '\\':                   /* absolute path on default drive? */
+    sprintf (dst,"%s%s",homedev,name);
+    break;
+  default:                     /* any other name */
+    if (name[1] == ':') {      /* some other drive? */
+      if (name[2] == '\\') strcpy (dst,name);
+      else sprintf (dst,"%c:\\%s",name[0],name+2);
+    }
+                               /* build home-directory relative name */
+    else sprintf (dst,"%s\\%s",dir,name);
+  }
+  return dst;                  /* return it */
+}
+\f
+/* Lock file name
+ * Accepts: return buffer for file name
+ *         file name
+ *         locking to be placed on file if non-NIL
+ * Returns: file descriptor of lock or -1 if error
+ */
+
+int lockname (char *lock,char *fname,int op)
+{
+  int ld;
+  char c,*s;
+                               /* Win2K and Win98 have TEMP under windir */
+  if (!((s = lockdir (lock,getenv ("windir"),"TEMP")) ||
+                               /* NT4, NT3.x and Win95 use one of these */
+       (s = lockdir (lock,getenv ("TEMP"),NIL)) ||
+       (s = lockdir (lock,getenv ("TMP"),NIL)) ||
+       (s = lockdir (lock,getenv ("TMPDIR"),NIL)) ||
+                               /* try one of these */
+       (s = lockdir (lock,defaultDrive (),"WINNT\\TEMP")) ||
+       (s = lockdir (lock,defaultDrive (),"WINDOWS\\TEMP")) ||
+                               /* C:\TEMP is last resort */
+       (s = lockdir (lock,defaultDrive (),"TEMP")))) {
+    mm_log ("Unable to find temporary directory",ERROR);
+    return -1;
+  }
+                               /* generate file name */
+  while (c = *fname++) switch (c) {
+  case '/': case '\\': case ':':
+    *s++ = '!';                        /* convert bad chars to ! */
+    break;
+  default:
+    *s++ = c;
+    break;
+  }
+  *s++ = c;                    /* tie off name */
+                               /* get the lock */
+  if (((ld = open (lock,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) >= 0) && op)
+    flock (ld,op);             /* apply locking function */
+  return ld;                   /* return locking file descriptor */
+}
+\f
+/* Build lock directory, check to see if it exists
+ * Accepts: return buffer for lock directory
+ *         first part of possible name
+ *         optional second part
+ * Returns: pointer to end of buffer if buffer has a good name, else NIL
+ */
+
+char *lockdir (char *lock,char *first,char *last)
+{
+  struct stat sbuf;
+  char c,*s;
+  if (first && *first) {       /* first part must be non-NIL */
+                               /* copy first part */
+    for (s = lock; c = *first++; *s++ = (c == '/') ? '\\' : c);
+    if (last && *last) {       /* copy last part if specified */
+                               /* write trailing \ in case not in first */
+      if (s[-1] != '\\') *s++ = '\\';
+      while (c = *last++) *s++ = (c == '/') ? '\\' : c;
+    }
+    if (s[-1] == '\\') --s;    /* delete trailing \ if any */
+    *s = s[1] = '\0';          /* tie off name at this point */
+    if (!stat (lock,&sbuf)) {  /* does the name exist? */
+      *s++ = '\\';             /* yes, reinstall trailing \ */
+      return s;                        /* return the name */
+    }
+  }
+  return NIL;                  /* failed */
+}
+
+
+/* Unlock file descriptor
+ * Accepts: file descriptor
+ *         lock file name from lockfd()
+ */
+
+void unlockfd (int fd,char *lock)
+{
+  flock (fd,LOCK_UN);          /* unlock it */
+  close (fd);                  /* close it */
+}
+
+
+/* Determine default prototype stream to user
+ * Accepts: type (NIL for create, T for append)
+ * Returns: default prototype stream
+ */
+
+MAILSTREAM *default_proto (long type)
+{
+  extern MAILSTREAM CREATEPROTO,APPENDPROTO;
+  return type ? &APPENDPROTO : &CREATEPROTO;
+}
+\f
+/* 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 *) alarm (0);
+    break;
+  case BLOCK_NONSENSITIVE:     /* exiting sensitive code */
+    if ((unsigned int) data) alarm ((unsigned int) data);
+    break;
+  default:                     /* ignore all other reasons */
+    break;
+  }
+  return ret;
+}