summaryrefslogtreecommitdiff
path: root/imap/src/osdep/nt/env_nt.c
diff options
context:
space:
mode:
Diffstat (limited to 'imap/src/osdep/nt/env_nt.c')
-rw-r--r--imap/src/osdep/nt/env_nt.c774
1 files changed, 774 insertions, 0 deletions
diff --git a/imap/src/osdep/nt/env_nt.c b/imap/src/osdep/nt/env_nt.c
new file mode 100644
index 00000000..18bc2369
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.c
@@ -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
+ */
+
+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"
+
+/* 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;
+}
+
+/* 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);
+}
+
+/* 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) ();
+}
+
+/* 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;
+}
+
+/* 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;
+ }
+
+ /* 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;
+}
+
+/* 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;
+}
+
+/* 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:";
+}
+
+/* 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;
+}
+
+/* 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;
+}
+
+/* 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 */
+}
+
+/* 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 */
+}
+
+/* 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 */
+}
+
+/* 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;
+}
+
+/* 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;
+}