summaryrefslogtreecommitdiff
path: root/imap/src/osdep/nt
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /imap/src/osdep/nt
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'imap/src/osdep/nt')
-rwxr-xr-ximap/src/osdep/nt/drivers.bat33
-rwxr-xr-ximap/src/osdep/nt/drivraux.bat30
-rw-r--r--imap/src/osdep/nt/dummy.h43
-rw-r--r--imap/src/osdep/nt/dummynt.c724
-rw-r--r--imap/src/osdep/nt/env_nt.c774
-rw-r--r--imap/src/osdep/nt/env_nt.h68
-rw-r--r--imap/src/osdep/nt/fdstring.c99
-rw-r--r--imap/src/osdep/nt/fdstring.h39
-rw-r--r--imap/src/osdep/nt/fs_nt.c62
-rw-r--r--imap/src/osdep/nt/ftl_nt.c38
-rw-r--r--imap/src/osdep/nt/ip4_nt.c184
-rw-r--r--imap/src/osdep/nt/ip6_nt.c288
-rw-r--r--imap/src/osdep/nt/kerb_mit.c74
-rw-r--r--imap/src/osdep/nt/kerb_w2k.c699
-rw-r--r--imap/src/osdep/nt/mailfile.h29
-rw-r--r--imap/src/osdep/nt/makefile.nt118
-rw-r--r--imap/src/osdep/nt/makefile.ntk118
-rw-r--r--imap/src/osdep/nt/makefile.w2k119
-rw-r--r--imap/src/osdep/nt/mbxnt.c1694
-rwxr-xr-ximap/src/osdep/nt/mkautaux.bat31
-rwxr-xr-ximap/src/osdep/nt/mkauths.bat33
-rw-r--r--imap/src/osdep/nt/mtxnt.c1232
-rw-r--r--imap/src/osdep/nt/nl_nt.c61
-rw-r--r--imap/src/osdep/nt/os_nt.c48
-rw-r--r--imap/src/osdep/nt/os_nt.h60
-rw-r--r--imap/src/osdep/nt/os_ntk.c51
-rw-r--r--imap/src/osdep/nt/os_old.c48
-rw-r--r--imap/src/osdep/nt/os_w2k.c49
-rw-r--r--imap/src/osdep/nt/pmatch.c89
-rw-r--r--imap/src/osdep/nt/pseudo.c36
-rw-r--r--imap/src/osdep/nt/pseudo.h30
-rwxr-xr-ximap/src/osdep/nt/setproto.bat29
-rw-r--r--imap/src/osdep/nt/ssl_none.c141
-rw-r--r--imap/src/osdep/nt/ssl_nt.c721
-rw-r--r--imap/src/osdep/nt/ssl_old.c625
-rw-r--r--imap/src/osdep/nt/ssl_w2k.c683
-rw-r--r--imap/src/osdep/nt/tcp_nt.c916
-rw-r--r--imap/src/osdep/nt/tcp_nt.h51
-rw-r--r--imap/src/osdep/nt/tenexnt.c1310
-rw-r--r--imap/src/osdep/nt/unixnt.c2297
-rw-r--r--imap/src/osdep/nt/unixnt.h161
-rw-r--r--imap/src/osdep/nt/write.c59
-rw-r--r--imap/src/osdep/nt/yunchan.c286
-rw-r--r--imap/src/osdep/nt/yunchan.h86
44 files changed, 14366 insertions, 0 deletions
diff --git a/imap/src/osdep/nt/drivers.bat b/imap/src/osdep/nt/drivers.bat
new file mode 100755
index 00000000..0964f537
--- /dev/null
+++ b/imap/src/osdep/nt/drivers.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+REM Erase old driver linkage
+IF EXIST LINKAGE.* DEL LINKAGE.*
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL DRIVRAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/drivraux.bat b/imap/src/osdep/nt/drivraux.bat
new file mode 100755
index 00000000..30649a78
--- /dev/null
+++ b/imap/src/osdep/nt/drivraux.bat
@@ -0,0 +1,30 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Driver Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 11 October 1989
+REM Last Edited:30 August 2006
+
+ECHO extern DRIVER %1driver; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO mail_link (^&%1driver); /* link in the %1 driver */ >> LINKAGE.C
+if "%OS%" == "" ECHO mail_link (&%1driver); /* link in the %1 driver */ >> LINKAGE.C
diff --git a/imap/src/osdep/nt/dummy.h b/imap/src/osdep/nt/dummy.h
new file mode 100644
index 00000000..32650e06
--- /dev/null
+++ b/imap/src/osdep/nt/dummy.h
@@ -0,0 +1,43 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Dummy routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 9 May 1991
+ * Last Edited: 30 August 2006
+ */
+
+/* Exported function prototypes */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat);
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long scan_contents (DRIVER *dtb,char *name,char *contents,
+ unsigned long csiz,unsigned long fsiz);
+long dummy_scan_contents (char *name,char *contents,unsigned long csiz,
+ unsigned long fsiz);
+long dummy_create (MAILSTREAM *stream,char *mailbox);
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode);
+long dummy_delete (MAILSTREAM *stream,char *mailbox);
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname);
+char *dummy_file (char *dst,char *name);
+long dummy_canonicalize (char *tmp,char *ref,char *pat);
diff --git a/imap/src/osdep/nt/dummynt.c b/imap/src/osdep/nt/dummynt.c
new file mode 100644
index 00000000..b44d271f
--- /dev/null
+++ b/imap/src/osdep/nt/dummynt.c
@@ -0,0 +1,724 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: Dummy routines for NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 24 May 1993
+ * Last Edited: 1 June 2007
+ */
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <direct.h>
+#include "mail.h"
+#include "osdep.h"
+#include <sys\stat.h>
+#include <dos.h>
+#include "dummy.h"
+#include "misc.h"
+
+/* Function prototypes */
+
+DRIVER *dummy_valid (char *name);
+void *dummy_parameters (long function,void *value);
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level);
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents);
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox);
+MAILSTREAM *dummy_open (MAILSTREAM *stream);
+void dummy_close (MAILSTREAM *stream,long options);
+long dummy_ping (MAILSTREAM *stream);
+void dummy_check (MAILSTREAM *stream);
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options);
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+/* Dummy routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER dummydriver = {
+ "dummy", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ dummy_valid, /* mailbox is valid for us */
+ dummy_parameters, /* manipulate parameters */
+ dummy_scan, /* scan mailboxes */
+ dummy_list, /* list mailboxes */
+ dummy_lsub, /* list subscribed mailboxes */
+ dummy_subscribe, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ dummy_create, /* create mailbox */
+ dummy_delete, /* delete mailbox */
+ dummy_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ dummy_open, /* open mailbox */
+ dummy_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message structure */
+ NIL, /* fetch header */
+ NIL, /* fetch text */
+ NIL, /* fetch message data */
+ NIL, /* unique identifier */
+ NIL, /* message number from UID */
+ NIL, /* modify flags */
+ NIL, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ dummy_ping, /* ping mailbox to see if still alive */
+ dummy_check, /* check for new messages */
+ dummy_expunge, /* expunge deleted messages */
+ dummy_copy, /* copy messages to another mailbox */
+ dummy_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+
+ /* prototype stream */
+MAILSTREAM dummyproto = {&dummydriver};
+
+/* Dummy validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *dummy_valid (char *name)
+{
+ char *s,*t,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if (name && *name && (*name != '{') && (s = mailboxfile (tmp,name))) {
+ /* indeterminate INBOX */
+ if (!*s) return &dummydriver;
+ /* remove trailing \ */
+ if ((t = strrchr (s,'\\')) && !t[1]) *t = '\0';
+ if (!stat (s,&sbuf)) switch (sbuf.st_mode & S_IFMT) {
+ case S_IFREG: /* file */
+ case S_IFDIR: /* future use */
+ return &dummydriver;
+ }
+ }
+ return NIL;
+}
+
+
+/* Dummy manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *dummy_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Dummy scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void dummy_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ char *s,test[MAILTMPLEN],file[MAILTMPLEN];
+ long i = 0;
+ if (!pat || !*pat) { /* empty pattern? */
+ if (dummy_canonicalize (test,ref,"*")) {
+ /* tie off name at root */
+ if (s = strchr (test,'\\')) *++s = '\0';
+ else test[0] = '\0';
+ dummy_listed (stream,'\\',test,LATT_NOSELECT,NIL);
+ }
+ }
+ /* get canonical form of name */
+ else if (dummy_canonicalize (test,ref,pat)) {
+ /* found any wildcards? */
+ if (s = strpbrk (test,"%*")) {
+ /* yes, copy name up to that point */
+ strncpy (file,test,(size_t) (i = s - test));
+ file[i] = '\0'; /* tie off */
+ }
+ else strcpy (file,test); /* use just that name then */
+ /* find directory name */
+ if (s = strrchr (file,'\\')) {
+ *++s = '\0'; /* found, tie off at that point */
+ s = file;
+ }
+ /* silly case */
+ else if (file[0] == '#') s = file;
+ /* do the work */
+ dummy_list_work (stream,s,test,contents,0);
+ if (pmatch ("INBOX",test)) /* always an INBOX */
+ dummy_listed (stream,NIL,"INBOX",LATT_NOINFERIORS,contents);
+ }
+}
+
+/* Dummy list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ dummy_scan (stream,ref,pat,NIL);
+}
+
+
+/* Dummy list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void dummy_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ void *sdb = NIL;
+ char *s,*t,test[MAILTMPLEN];
+ int showuppers = pat[strlen (pat) - 1] == '%';
+ /* get canonical form of name */
+ if (dummy_canonicalize (test,ref,pat) && (s = sm_read (&sdb))) do
+ if (*s != '{') {
+ if (pmatch_full (s,test,'\\')) {
+ if (pmatch (s,"INBOX")) mm_lsub (stream,NIL,s,LATT_NOINFERIORS);
+ else mm_lsub (stream,'\\',s,NIL);
+ }
+ else while (showuppers && (t = strrchr (s,'\\'))) {
+ *t = '\0'; /* tie off the name */
+ if (pmatch_full (s,test,'\\')) mm_lsub (stream,'\\',s,LATT_NOSELECT);
+ }
+ }
+ while (s = sm_read (&sdb)); /* until no more subscriptions */
+}
+
+
+/* Dummy subscribe to mailbox
+ * Accepts: mail stream
+ * mailbox to add to subscription list
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_subscribe (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* must be valid local mailbox */
+ if ((s = mailboxfile (tmp,mailbox)) && *s && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) return sm_subscribe (mailbox);
+ sprintf (tmp,"Can't subscribe %.80s: not a mailbox",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy list mailboxes worker routine
+ * Accepts: mail stream
+ * directory name to search
+ * search pattern
+ * string to scan
+ * search level
+ */
+
+void dummy_list_work (MAILSTREAM *stream,char *dir,char *pat,char *contents,
+ long level)
+{
+ struct _finddata_t f;
+ struct stat sbuf;
+ long fhandle;
+ char tmp[MAILTMPLEN];
+ size_t len = 0;
+ /* punt if bogus name */
+ if (!mailboxdir (tmp,dir,NIL)) return;
+ /* make directory wildcard */
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*");
+ /* do nothing if can't open directory */
+ if ((fhandle = _findfirst (tmp,&f)) >= 0) {
+ /* list it if at top-level */
+ if (!level && dir && pmatch_full (dir,pat,'\\'))
+ dummy_listed (stream,'\\',dir,LATT_NOSELECT,contents);
+ /* scan directory */
+ if (!dir || dir[(len = strlen (dir)) - 1] == '\\') do
+ if (((f.name[0] != '.') ||
+ (f.name[1] && ((f.name[1] != '.') || f.name[2]))) &&
+ ((len + strlen (f.name)) <= NETMAXMBX)) {
+ /* see if name is useful */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* make sure useful and can get info */
+ if ((pmatch_full (tmp,pat,'\\') ||
+ pmatch_full (strcat (tmp,"\\"),pat,'\\') ||
+ dmatch (tmp,pat,'\\')) &&
+ mailboxdir (tmp,dir,f.name) && tmp[0] && !stat (tmp,&sbuf)) {
+ /* now make name we'd return */
+ if (dir) sprintf (tmp,"%s%s",dir,f.name);
+ else strcpy (tmp,f.name);
+ /* only interested in file type */
+ switch (sbuf.st_mode & S_IFMT) {
+ case S_IFDIR: /* directory? */
+ if (pmatch_full (tmp,pat,'\\')) {
+ if (!dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))break;
+ strcat (tmp,"\\");/* set up for dmatch call */
+ }
+ /* try again with trailing \ */
+ else if (pmatch_full (strcat (tmp,"\\"),pat,'\\') &&
+ !dummy_listed (stream,'\\',tmp,LATT_NOSELECT,contents))
+ break;
+ if (dmatch (tmp,pat,'\\') &&
+ (level < (long) mail_parameters (NIL,GET_LISTMAXLEVEL,NIL)))
+ dummy_list_work (stream,tmp,pat,contents,level+1);
+ break;
+ case S_IFREG: /* ordinary name */
+ if (pmatch_full (tmp,pat,'\\') && !pmatch ("INBOX",tmp))
+ dummy_listed (stream,'\\',tmp,LATT_NOINFERIORS,contents);
+ break;
+ }
+ }
+ }
+ while (!_findnext (fhandle,&f));
+ _findclose(fhandle);
+ }
+}
+
+/* Mailbox found
+ * Accepts: hierarchy delimiter
+ * mailbox name
+ * attributes
+ * contents to search before calling mm_list()
+ * Returns: T, always
+ */
+
+#define BUFSIZE 4*MAILTMPLEN
+
+long dummy_listed (MAILSTREAM *stream,char delimiter,char *name,
+ long attributes,char *contents)
+{
+ struct stat sbuf;
+ struct _finddata_t f;
+ int fd,nochild;
+ long fhandle,csiz,ssiz,bsiz;
+ char *s,*buf,tmp[MAILTMPLEN];
+ /* if not \NoInferiors */
+ if (!(attributes & LATT_NOINFERIORS) && mailboxdir (tmp,name,NIL) &&
+ strcat (tmp,(tmp[strlen (tmp) -1] == '\\') ? "*.*" : "\\*.*") &&
+ ((fhandle = _findfirst (tmp,&f)) >= 0)) {
+ nochild = T;
+ do if ((f.name[0] != '.') || (f.name[1] && ((f.name[1] != '.') ||
+ f.name[2]))) nochild = NIL;
+ while (nochild && !_findnext (fhandle,&f));
+ attributes |= nochild ? LATT_HASNOCHILDREN : LATT_HASCHILDREN;
+ _findclose (fhandle); /* all done, flush directory */
+ }
+ if (contents) { /* want to search contents? */
+ /* forget it if can't select or open */
+ if ((attributes & LATT_NOSELECT) || !(csiz = strlen (contents)) ||
+ !(s = dummy_file (tmp,name)) || stat (s,&sbuf) ||
+ (csiz > sbuf.st_size) || ((fd = open (tmp,O_RDONLY,NIL)) < 0))
+ return T;
+ /* get buffer including slop */
+ buf = (char *) fs_get (BUFSIZE + (ssiz = 4 * ((csiz / 4) + 1)) + 1);
+ memset (buf,'\0',ssiz); /* no slop area the first time */
+ while (sbuf.st_size) { /* until end of file */
+ read (fd,buf+ssiz,bsiz = min (sbuf.st_size,BUFSIZE));
+ if (search ((unsigned char *) buf,bsiz+ssiz,
+ (unsigned char *) contents,csiz)) break;
+ memcpy (buf,buf+BUFSIZE,ssiz);
+ sbuf.st_size -= bsiz; /* note that we read that much */
+ }
+ fs_give ((void **) &buf); /* flush buffer */
+ close (fd); /* finished with file */
+ if (!sbuf.st_size) return T;/* not found */
+ }
+ /* notify main program */
+ mm_list (stream,delimiter,name,attributes);
+ return T;
+}
+
+/* Dummy create mailbox
+ * Accepts: mail stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create (MAILSTREAM *stream,char *mailbox)
+{
+ char tmp[MAILTMPLEN];
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox))
+ return dummy_create_path (stream,tmp,NIL);
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+
+/* Dummy create path
+ * Accepts: mail stream
+ * path name to create
+ * directory mode
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_create_path (MAILSTREAM *stream,char *path,long dirmode)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN];
+ int fd;
+ long ret = NIL;
+ char *t = strrchr (path,'\\');
+ char *pt = (path[1] == ':') ? path + 2 : path;
+ int wantdir = t && !t[1];
+ if (wantdir) *t = '\0'; /* flush trailing delimiter for directory */
+ /* found superior to this name? */
+ if ((s = strrchr (pt,'\\')) && (s != pt)) {
+ strncpy (tmp,path,(size_t) (s - path));
+ tmp[s - path] = '\0'; /* make directory name for stat */
+ c = *++s; /* tie off in case need to recurse */
+ *s = '\0';
+ /* name doesn't exist, create it */
+ if ((stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) &&
+ !dummy_create_path (stream,path,dirmode)) return NIL;
+ *s = c; /* restore full name */
+ }
+ if (wantdir) { /* want to create directory? */
+ ret = !mkdir (path);
+ *t = '\\'; /* restore directory delimiter */
+ }
+ /* create file */
+ else if ((fd = open (path,O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE)) >= 0)
+ ret = !close (fd); /* close file */
+ if (!ret) { /* error? */
+ sprintf (tmp,"Can't create mailbox node %.80s: %.80s",path,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ return ret; /* return status */
+}
+
+/* Dummy delete mailbox
+ * Accepts: mail stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_delete (MAILSTREAM *stream,char *mailbox)
+{
+ struct stat sbuf;
+ char *s,tmp[MAILTMPLEN];
+ if (!(s = dummy_file (tmp,mailbox))) {
+ sprintf (tmp,"Can't delete - invalid name: %.80s",s);
+ mm_log (tmp,ERROR);
+ }
+ /* no trailing \ */
+ if ((s = strrchr (tmp,'\\')) && !s[1]) *s = '\0';
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) == S_IFDIR) ?
+ rmdir (tmp) : unlink (tmp)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",mailbox,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return T; /* return success */
+}
+
+
+/* Mail rename mailbox
+ * Accepts: mail stream
+ * old mailbox name
+ * new mailbox name
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ struct stat sbuf;
+ char c,*s,tmp[MAILTMPLEN],mbx[MAILTMPLEN],oldname[MAILTMPLEN];
+ long ret = NIL;
+ /* no trailing \ allowed */
+ if (!dummy_file (oldname,old) || !(s = dummy_file (mbx,newname)) ||
+ stat (oldname,&sbuf) || ((s = strrchr (s,'\\')) && !s[1] &&
+ ((sbuf.st_mode & S_IFMT) != S_IFDIR))) {
+ sprintf (mbx,"Can't rename %.80s to %.80s: invalid name",old,newname);
+ mm_log (mbx,ERROR);
+ return NIL;
+ }
+ if (s) { /* found a directory delimiter? */
+ if (!s[1]) *s = '\0'; /* ignore trailing delimiter */
+ /* found superior to destination name? */
+ else if ((s != mbx) && ((mbx[1] != ':') || (s != mbx + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (mbx,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,mbx)) return NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ }
+ /* rename of non-ex INBOX creates dest */
+ if (!compare_cstring (old,"INBOX") && stat (oldname,&sbuf))
+ return dummy_create (NIL,mbx);
+ if (rename (oldname,mbx)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %.80s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ return LONGT; /* return success */
+}
+
+/* Dummy open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *dummy_open (MAILSTREAM *stream)
+{
+ int fd;
+ char err[MAILTMPLEN],tmp[MAILTMPLEN];
+ struct stat sbuf;
+ /* OP_PROTOTYPE call */
+ if (!stream) return &dummyproto;
+ err[0] = '\0'; /* no error message yet */
+ /* can we open the file? */
+ if (!dummy_file (tmp,stream->mailbox))
+ sprintf (err,"Can't open this name: %.80s",stream->mailbox);
+ else if ((fd = open (tmp,O_RDONLY,NIL)) < 0) {
+ /* no, error unless INBOX */
+ if (compare_cstring (stream->mailbox,"INBOX"))
+ sprintf (err,"%.80s: %.80s",strerror (errno),stream->mailbox);
+ }
+ else { /* file had better be empty then */
+ fstat (fd,&sbuf); /* sniff at its size */
+ close (fd);
+ if (sbuf.st_size) /* bogus format if non-empty */
+ sprintf (err,"%.80s (file %.80s) is not in valid mailbox format",
+ stream->mailbox,tmp);
+ }
+ if (err[0]) { /* if an error happened */
+ mm_log (err,stream->silent ? WARN : ERROR);
+ return NIL;
+ }
+ else if (!stream->silent) { /* only if silence not requested */
+ mail_exists (stream,0); /* say there are 0 messages */
+ mail_recent (stream,0); /* and certainly no recent ones! */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ stream->inbox = T; /* note that it's an INBOX */
+ return stream; /* return success */
+}
+
+
+/* Dummy close
+ * Accepts: MAIL stream
+ * options
+ */
+
+void dummy_close (MAILSTREAM *stream,long options)
+{
+ /* return silently */
+}
+
+/* Dummy ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long dummy_ping (MAILSTREAM *stream)
+{
+ MAILSTREAM *test;
+ /* time to do another test? */
+ if (time (0) >= ((time_t) (stream->gensym + 30))) {
+ /* has mailbox format changed? */
+ if ((test = mail_open (NIL,stream->mailbox,OP_PROTOTYPE)) &&
+ (test->dtb != stream->dtb) &&
+ (test = mail_open (NIL,stream->mailbox,NIL))) {
+ /* preserve some resources */
+ test->original_mailbox = stream->original_mailbox;
+ stream->original_mailbox = NIL;
+ test->sparep = stream->sparep;
+ stream->sparep = NIL;
+ test->sequence = stream->sequence;
+ mail_close ((MAILSTREAM *) /* flush resources used by dummy stream */
+ memcpy (fs_get (sizeof (MAILSTREAM)),stream,
+ sizeof (MAILSTREAM)));
+ /* swap the streams */
+ memcpy (stream,test,sizeof (MAILSTREAM));
+ fs_give ((void **) &test);/* flush test now that copied */
+ /* make sure application knows */
+ mail_exists (stream,stream->recent = stream->nmsgs);
+ }
+ /* still hasn't changed */
+ else stream->gensym = (unsigned long) time (0);
+ }
+ return T;
+}
+
+
+/* Dummy check mailbox
+ * Accepts: MAIL stream
+ * No-op for readonly files, since read/writer can expunge it from under us!
+ */
+
+void dummy_check (MAILSTREAM *stream)
+{
+ dummy_ping (stream); /* invoke ping */
+}
+
+
+/* Dummy expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long dummy_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ return LONGT;
+}
+
+/* Dummy copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * options
+ * Returns: T if copy successful, else NIL
+ */
+
+long dummy_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ if ((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) fatal ("Impossible dummy_copy");
+ return NIL;
+}
+
+
+/* Dummy append message string
+ * Accepts: mail stream
+ * destination mailbox
+ * append callback function
+ * data for callback
+ * Returns: T on success, NIL on failure
+ */
+
+long dummy_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd = -1;
+ int e;
+ char tmp[MAILTMPLEN];
+ MAILSTREAM *ts = default_proto (T);
+ if (compare_cstring (mailbox,"INBOX") && dummy_file (tmp,mailbox) &&
+ ((fd = open (tmp,O_RDONLY,NIL)) < 0)) {
+ if ((e = errno) == ENOENT) /* failed, was it no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",
+ (long) NIL);
+ sprintf (tmp,"%.80s: %.80s",strerror (e),mailbox);
+ mm_log (tmp,ERROR); /* pass up error */
+ return NIL; /* always fails */
+ }
+ if (fd >= 0) { /* found file? */
+ fstat (fd,&sbuf); /* get its size */
+ close (fd); /* toss out the fd */
+ if (sbuf.st_size) ts = NIL; /* non-empty file? */
+ }
+ if (ts) return (*ts->dtb->append) (stream,mailbox,af,data);
+ sprintf (tmp,"Indeterminate mailbox format: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+}
+
+/* Dummy mail generate file string
+ * Accepts: temporary buffer to write into
+ * mailbox name string
+ * Returns: local file string or NIL if failure
+ */
+
+char *dummy_file (char *dst,char *name)
+{
+ char *s = mailboxfile (dst,name);
+ /* return our standard inbox */
+ return (s && !*s) ? strcpy (dst,sysinbox ()) : s;
+}
+
+
+/* Dummy canonicalize name
+ * Accepts: buffer to write name
+ * reference
+ * pattern
+ * Returns: T if success, NIL if failure
+ */
+
+long dummy_canonicalize (char *tmp,char *ref,char *pat)
+{
+ unsigned long i;
+ char *s,dev[4];
+ /* initially no device */
+ dev[0] = dev[1] = dev[2] = dev[3] = '\0';
+ if (ref) switch (*ref) { /* preliminary reference check */
+ case '{': /* remote names not allowed */
+ return NIL; /* disallowed */
+ case '\0': /* empty reference string */
+ break;
+ default: /* all other names */
+ if (ref[1] == ':') { /* start with device name? */
+ dev[0] = *ref++; dev[1] = *ref++;
+ }
+ break;
+ }
+ if (pat[1] == ':') { /* device name in pattern? */
+ dev[0] = *pat++; dev[1] = *pat++;
+ ref = NIL; /* ignore reference */
+ }
+ switch (*pat) {
+ case '#': /* namespace names */
+ if (mailboxfile (tmp,pat)) strcpy (tmp,pat);
+ else return NIL; /* unknown namespace */
+ break;
+ case '{': /* remote names not allowed */
+ return NIL;
+ case '\\': /* rooted name */
+ ref = NIL; /* ignore reference */
+ break;
+ }
+ /* make sure device names are rooted */
+ if (dev[0] && (*(ref ? ref : pat) != '\\')) dev[2] = '\\';
+ /* build name */
+ sprintf (tmp,"%s%s%s",dev,ref ? ref : "",pat);
+ ucase (tmp); /* force upper case */
+ /* count wildcards */
+ for (i = 0, s = tmp; *s; *s++) if ((*s == '*') || (*s == '%')) ++i;
+ if (i > MAXWILDCARDS) { /* ridiculous wildcarding? */
+ MM_LOG ("Excessive wildcards in LIST/LSUB",ERROR);
+ return NIL;
+ }
+ return T;
+}
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;
+}
diff --git a/imap/src/osdep/nt/env_nt.h b/imap/src/osdep/nt/env_nt.h
new file mode 100644
index 00000000..95575246
--- /dev/null
+++ b/imap/src/osdep/nt/env_nt.h
@@ -0,0 +1,68 @@
+/* ========================================================================
+ * Copyright 1988-2006 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
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+#define SUBSCRIPTIONFILE(t) sprintf (t,"%s\\MAILBOX.LST",myhomedir ())
+#define SUBSCRIPTIONTEMP(t) sprintf (t,"%s\\MAILBOX.TMP",myhomedir ())
+
+/* Note: the \CRAM-MD5.PWD file only works on DOS-based Windows (Win9x/Me)
+ * and not on NT-based Windows (WinNT/2K/XP). If installed on NT-based
+ * Windows, servers will advertise CRAM-MD5 authentication but it will
+ * never succeed.
+ */
+
+#define MD5ENABLE "\\cram-md5.pwd"
+
+#define L_SET SEEK_SET
+
+/* Function prototypes */
+
+#include "env.h"
+
+void rfc822_fixed_date (char *date);
+long env_init (char *user,char *home);
+int check_nt (void);
+char *win_homedir (char *user);
+static char *defaultDrive (void);
+char *myusername_full (unsigned long *flags);
+#define MU_LOGGEDIN 0
+#define MU_NOTLOGGEDIN 1
+#define MU_ANONYMOUS 2
+#define myusername() \
+ myusername_full (NIL)
+char *sysinbox ();
+char *mailboxdir (char *dst,char *dir,char *name);
+int lockname (char *lock,char *fname,int op);
+char *lockdir (char *lock,char *first,char *last);
+void unlockfd (int fd,char *lock);
+long safe_write (int fd,char *buf,long nbytes);
+void *mm_blocknotify (int reason,void *data);
+long random ();
+#if _MSC_VER < 700
+#define getpid random
+#endif
diff --git a/imap/src/osdep/nt/fdstring.c b/imap/src/osdep/nt/fdstring.c
new file mode 100644
index 00000000..7a491f7d
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.c
@@ -0,0 +1,99 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 4 April 2007
+ */
+
+#include "mail.h"
+#include "osdep.h"
+#include "misc.h"
+#include "fdstring.h"
+
+/* String driver for fd stringstructs */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size);
+static char fd_string_next (STRING *s);
+static void fd_string_setpos (STRING *s,unsigned long i);
+
+STRINGDRIVER fd_string = {
+ fd_string_init, /* initialize string structure */
+ fd_string_next, /* get next byte in string structure */
+ fd_string_setpos /* set position in string structure */
+};
+
+
+/* Initialize string structure for fd stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+static void fd_string_init (STRING *s,void *data,unsigned long size)
+{
+ FDDATA *d = (FDDATA *) data;
+ /* note fd */
+ s->data = (void *) (unsigned long) d->fd;
+ s->data1 = d->pos; /* note file offset */
+ s->size = size; /* note size */
+ s->curpos = s->chunk = d->chunk;
+ s->chunksize = (unsigned long) d->chunksize;
+ s->offset = 0; /* initial position */
+ /* and size of data */
+ s->cursize = min (s->chunksize,size);
+ /* move to that position in the file */
+ lseek (d->fd,d->pos,L_SET);
+ read (d->fd,s->chunk,(size_t) s->cursize);
+}
+
+/* Get next character from fd stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+static char fd_string_next (STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for fd stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+static void fd_string_setpos (STRING *s,unsigned long i)
+{
+ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
+ s->offset = i; /* set new offset */
+ s->curpos = s->chunk; /* reset position */
+ /* set size of data */
+ if (s->cursize = min (s->chunksize,SIZE (s))) {
+ /* move to that position in the file */
+ lseek ((long) s->data,s->data1 + s->offset,L_SET);
+ read ((long) s->data,s->curpos,(size_t) s->cursize);
+ }
+}
diff --git a/imap/src/osdep/nt/fdstring.h b/imap/src/osdep/nt/fdstring.h
new file mode 100644
index 00000000..d0a021bf
--- /dev/null
+++ b/imap/src/osdep/nt/fdstring.h
@@ -0,0 +1,39 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: File descriptor string routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 April 1997
+ * Last Edited: 30 August 2006
+ */
+
+/* Driver-dependent data passed to init method */
+
+typedef struct fd_data {
+ int fd; /* file descriptor */
+ unsigned long pos; /* initial position */
+ char *chunk; /* I/O buffer chunk */
+ unsigned long chunksize; /* I/O buffer chunk length */
+} FDDATA;
+
+
+extern STRINGDRIVER fd_string;
diff --git a/imap/src/osdep/nt/fs_nt.c b/imap/src/osdep/nt/fs_nt.c
new file mode 100644
index 00000000..03908328
--- /dev/null
+++ b/imap/src/osdep/nt/fs_nt.c
@@ -0,0 +1,62 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Free storage management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+
+void *fs_get (size_t size)
+{
+ void *block = malloc (size ? size : (size_t) 1);
+ if (!block) fatal ("Out of memory");
+ return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ * new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+ if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+ fatal ("Can't resize memory");
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+ free (*block);
+ *block = NIL;
+}
diff --git a/imap/src/osdep/nt/ftl_nt.c b/imap/src/osdep/nt/ftl_nt.c
new file mode 100644
index 00000000..9e65ef55
--- /dev/null
+++ b/imap/src/osdep/nt/ftl_nt.c
@@ -0,0 +1,38 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: DOS/VMS/TOPS-20 crash management routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+
+/* Report a fatal error
+ * Accepts: string to output
+ */
+
+void fatal (char *string)
+{
+ mm_fatal (string); /* pass up the string */
+ abort (); /* die horribly */
+}
diff --git a/imap/src/osdep/nt/ip4_nt.c b/imap/src/osdep/nt/ip4_nt.c
new file mode 100644
index 00000000..23d399e1
--- /dev/null
+++ b/imap/src/osdep/nt/ip4_nt.c
@@ -0,0 +1,184 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: UNIX IPv4 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define SADRLEN sizeof (struct sockaddr)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ?
+ inet_ntoa (SADR4ADR (sadr)) : "NON-IPv4";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ return (sadr->sa_family == PF_INET) ? ntohs (SADR4PORT (sadr)) : -1;
+}
+
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+{
+ unsigned long adr;
+ struct in_addr *ret;
+ /* get address */
+ if ((adr = inet_addr (text)) == -1) ret = NIL;
+ else { /* make in_addr */
+ ret = (struct in_addr *) fs_get (*len = ADR4LEN);
+ *family = AF_INET; /* IPv4 */
+ ret->s_addr = adr; /* set address */
+ }
+ return (void *) ret;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address (always 4 in IPv4)
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address or NIL if error
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ struct hostent *he;
+ return ((sadr->sa_family == PF_INET) &&
+ (he = gethostbyaddr ((char *) &SADR4ADR (sadr),ADR4LEN,AF_INET))) ?
+ (char *) he->h_name : NIL;
+}
+
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ char **adl,tmp[MAILTMPLEN];
+ struct hostent *he;
+ if (name) { /* first lookup? */
+ /* yes, do case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (he = gethostbyname (lcase (strcpy (tmp,name))))) {
+ adl = he->h_addr_list;
+ if (len) *len = he->h_length;
+ if (family) *family = he->h_addrtype;
+ if (canonical) *canonical = (char *) he->h_name;
+ if (next) *next = (void *) adl;
+ }
+ else { /* error */
+ adl = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (adl = (char **) *next)) *next = ++adl;
+ else adl = NIL; /* failure */
+ return adl ? (void *) *adl : NIL;
+}
diff --git a/imap/src/osdep/nt/ip6_nt.c b/imap/src/osdep/nt/ip6_nt.c
new file mode 100644
index 00000000..c15dfbc0
--- /dev/null
+++ b/imap/src/osdep/nt/ip6_nt.c
@@ -0,0 +1,288 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: UNIX IPv6 routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 18 December 2003
+ * Last Edited: 30 August 2006
+ */
+
+
+/*
+ * There is some amazingly bad design in IPv6 sockets.
+ *
+ * Supposedly, the new getnameinfo() and getaddrinfo() functions create an
+ * abstraction that is not dependent upon IPv4 or IPv6. However, the
+ * definition of getnameinfo() requires that the caller pass the length of
+ * the sockaddr instead of deriving it from sa_family. The man page says
+ * that there's an sa_len member in the sockaddr, but actually there isn't.
+ * This means that any caller to getnameinfo() and getaddrinfo() has to know
+ * the size for the protocol family used by that sockaddr.
+ *
+ * The new sockaddr_in6 is bigger than the generic sockaddr (which is what
+ * connect(), accept(), bind(), getpeername(), getsockname(), etc. expect).
+ * Rather than increase the size of sockaddr, there's a new sockaddr_storage
+ * which is only usable for allocating space.
+ */
+
+#define SADRLEN sizeof (struct sockaddr_storage)
+
+#define SADR4(sadr) ((struct sockaddr_in *) sadr)
+#define SADR4LEN sizeof (struct sockaddr_in)
+#define SADR4ADR(sadr) SADR4 (sadr)->sin_addr
+#define ADR4LEN sizeof (struct in_addr)
+#define SADR4PORT(sadr) SADR4 (sadr)->sin_port
+
+#define SADR6(sadr) ((struct sockaddr_in6 *) sadr)
+#define SADR6LEN sizeof (struct sockaddr_in6)
+#define SADR6ADR(sadr) SADR6 (sadr)->sin6_addr
+#define ADR6LEN sizeof (struct in6_addr)
+#define SADR6PORT(sadr) SADR6 (sadr)->sin6_port
+
+
+/* IP abstraction layer */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr);
+long ip_sockaddrtoport (struct sockaddr *sadr);
+void *ip_stringtoaddr (char *text,size_t *len,int *family);
+struct sockaddr *ip_newsockaddr (size_t *len);
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len);
+char *ip_sockaddrtoname (struct sockaddr *sadr);
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next);
+
+/* Return IP address string from socket address
+ * Accepts: socket address
+ * Returns: IP address as name string
+ */
+
+char *ip_sockaddrtostring (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NUMERICHOST))
+ return tmp;
+ break;
+ }
+ return "NON-IP";
+}
+
+
+/* Return port from socket address
+ * Accepts: socket address
+ * Returns: port number or -1 if can't determine it
+ */
+
+long ip_sockaddrtoport (struct sockaddr *sadr)
+{
+ switch (sadr->sa_family) {
+ case PF_INET:
+ return ntohs (SADR4PORT (sadr));
+ case PF_INET6:
+ return ntohs (SADR6PORT (sadr));
+ }
+ return -1;
+}
+
+/* Return IP address from string
+ * Accepts: name string
+ * pointer to returned length
+ * pointer to returned address family
+ * Returns: address if valid, length and family updated, or NIL
+ */
+
+void *ip_stringtoaddr (char *text,size_t *len,int *family)
+
+{
+ char tmp[MAILTMPLEN];
+ static struct addrinfo *hints;
+ struct addrinfo *ai;
+ void *adr = NIL;
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ hints->ai_family = AF_UNSPEC;/* allow any address family */
+ hints->ai_socktype = SOCK_STREAM;
+ /* numeric name only */
+ hints->ai_flags = AI_NUMERICHOST;
+ }
+ /* case-independent lookup */
+ if (text && (strlen (text) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (tmp,text)),NIL,hints,&ai))) {
+ switch (*family = ai->ai_family) {
+ case AF_INET: /* IPv4 */
+ adr = fs_get (*len = ADR4LEN);
+ memcpy (adr,(void *) &SADR4ADR (ai->ai_addr),*len);
+ break;
+ case AF_INET6: /* IPv6 */
+ adr = fs_get (*len = ADR6LEN);
+ memcpy (adr,(void *) &SADR6ADR (ai->ai_addr),*len);
+ break;
+ }
+ freeaddrinfo (ai); /* free addrinfo */
+ }
+ return adr;
+}
+
+/* Create a maximum-size socket address
+ * Accepts: pointer to return maximum socket address length
+ * Returns: new, empty socket address of maximum size
+ */
+
+struct sockaddr *ip_newsockaddr (size_t *len)
+{
+ return (struct sockaddr *) memset (fs_get (SADRLEN),0,*len = SADRLEN);
+}
+
+
+/* Stuff a socket address
+ * Accepts: address family
+ * IPv4 address
+ * length of address
+ * port number
+ * pointer to return socket address length
+ * Returns: socket address
+ */
+
+struct sockaddr *ip_sockaddr (int family,void *adr,size_t adrlen,
+ unsigned short port,size_t *len)
+{
+ struct sockaddr *sadr = ip_newsockaddr (len);
+ switch (family) { /* build socket address based upon family */
+ case AF_INET: /* IPv4 */
+ sadr->sa_family = PF_INET;
+ /* copy host address */
+ memcpy (&SADR4ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR4PORT (sadr) = htons (port);
+ *len = SADR4LEN;
+ break;
+ case AF_INET6: /* IPv6 */
+ sadr->sa_family = PF_INET6;
+ /* copy host address */
+ memcpy (&SADR6ADR (sadr),adr,adrlen);
+ /* copy port number in network format */
+ SADR6PORT (sadr) = htons (port);
+ *len = SADR6LEN;
+ break;
+ default: /* non-IP?? */
+ sadr->sa_family = PF_UNSPEC;
+ break;
+ }
+ return sadr;
+}
+
+/* Return name from socket address
+ * Accepts: socket address
+ * Returns: canonical name for that address or NIL if none
+ */
+
+char *ip_sockaddrtoname (struct sockaddr *sadr)
+{
+ static char tmp[NI_MAXHOST];
+ switch (sadr->sa_family) {
+ case PF_INET: /* IPv4 */
+ if (!getnameinfo (sadr,SADR4LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ case PF_INET6: /* IPv6 */
+ if (!getnameinfo (sadr,SADR6LEN,tmp,NI_MAXHOST,NIL,NIL,NI_NAMEREQD))
+ return tmp;
+ break;
+ }
+ return NIL;
+}
+
+/* Return address from name
+ * Accepts: name or NIL to return next address
+ * pointer to previous/returned length
+ * pointer to previous/returned address family
+ * pointer to previous/returned canonical name
+ * pointer to previous/return state for next-address calls
+ * Returns: address with length/family/canonical updated if needed, or NIL
+ */
+
+void *ip_nametoaddr (char *name,size_t *len,int *family,char **canonical,
+ void **next)
+{
+ struct addrinfo *cur = NIL;
+ static struct addrinfo *hints;
+ static struct addrinfo *ai = NIL;
+ static char lcname[MAILTMPLEN];
+ if (!hints) { /* hints set up yet? */
+ hints = (struct addrinfo *) /* one-time setup */
+ memset (fs_get (sizeof (struct addrinfo)),0,sizeof (struct addrinfo));
+ /* allow any address family */
+ hints->ai_family = AF_UNSPEC;
+ hints->ai_socktype = SOCK_STREAM;
+ /* need canonical name */
+ hints->ai_flags = AI_CANONNAME;
+ }
+ if (name) { /* name supplied? */
+ if (ai) {
+ freeaddrinfo (ai); /* free old addrinfo */
+ ai = NIL;
+ }
+ /* case-independent lookup */
+ if ((strlen (name) < MAILTMPLEN) &&
+ (!getaddrinfo (lcase (strcpy (lcname,name)),NIL,hints,&ai))) {
+ cur = ai; /* current block */
+ if (canonical) /* set canonical name */
+ *canonical = cur->ai_canonname ? cur->ai_canonname : lcname;
+ /* remember as next block */
+ if (next) *next = (void *) ai;
+ }
+ else { /* error */
+ cur = NIL;
+ if (len) *len = 0;
+ if (family) *family = 0;
+ if (canonical) *canonical = NIL;
+ if (next) *next = NIL;
+ }
+ }
+ /* return next in series */
+ else if (next && (cur = ((struct addrinfo *) *next)->ai_next)) {
+ *next = cur; /* set as last address */
+ /* set canonical in case changed */
+ if (canonical && cur->ai_canonname) *canonical = cur->ai_canonname;
+ }
+
+ if (cur) { /* got data? */
+ if (family) *family = cur->ai_family;
+ switch (cur->ai_family) {
+ case AF_INET:
+ if (len) *len = ADR4LEN;
+ return (void *) &SADR4ADR (cur->ai_addr);
+ case AF_INET6:
+ if (len) *len = ADR6LEN;
+ return (void *) &SADR6ADR (cur->ai_addr);
+ }
+ }
+ if (len) *len = 0; /* error return */
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_mit.c b/imap/src/osdep/nt/kerb_mit.c
new file mode 100644
index 00000000..57ede524
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_mit.c
@@ -0,0 +1,74 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: MIT Kerberos routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 4 March 2003
+ * Last Edited: 30 August 2006
+ */
+
+#define PROTOTYPE(x) x
+#include <gssapi/gssapi_generic.h>
+#include <gssapi/gssapi_krb5.h>
+
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ switch (error) {
+ case KRB5KRB_AP_ERR_TKT_EXPIRED:
+ case KRB5_FCC_NOFILE: /* MIT */
+ case KRB5_CC_NOTFOUND: /* Heimdal */
+ return LONGT;
+ }
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/kerb_w2k.c b/imap/src/osdep/nt/kerb_w2k.c
new file mode 100644
index 00000000..38d0ce30
--- /dev/null
+++ b/imap/src/osdep/nt/kerb_w2k.c
@@ -0,0 +1,699 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: GSSAPI Kerberos Shim 5 for Windows 2000/XP IMAP Toolkit
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 6 March 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* The purpose of this module is to be a shim, so that the auth_gss.c module
+ * (written for MIT Kerberos) will compile, link, and run with SSPI Kerberos
+ * on Windows 2000 systems.
+ * There is no attempt whatsoever to make this be a complete implementation
+ * of GSSAPI. A number of shortcuts were taken that a real GSSAPI
+ * implementation for SSPI can't do.
+ * Nor is there any attempt to make the types identical with MIT Kerberos;
+ * you can't link this library with object files compiled with the MIT
+ * Kerberos .h files.
+ */
+
+
+/* GSSAPI generic definitions */
+
+
+#define SECURITY_WIN32
+#include <security.h>
+
+
+/* GSSAPI types for which we use SSPI equivalent types */
+
+typedef ULONG OM_uint32;
+typedef PCredHandle gss_cred_id_t;
+typedef ULONG gss_cred_usage_t;
+typedef PCtxtHandle gss_ctx_id_t;
+typedef SEC_CHAR * gss_name_t;
+typedef ULONG gss_qop_t;
+
+
+/* Major status codes */
+
+#define GSS_S_COMPLETE SEC_E_OK
+#define GSS_S_BAD_MECH SEC_E_SECPKG_NOT_FOUND
+#define GSS_S_CONTINUE_NEEDED SEC_I_CONTINUE_NEEDED
+#define GSS_S_CREDENTIALS_EXPIRED SEC_E_CERT_EXPIRED
+#define GSS_S_FAILURE SEC_E_INTERNAL_ERROR
+#define GSS_S_NO_CRED SEC_E_NO_CREDENTIALS
+#define GSS_S_NO_CONTEXT SEC_E_INVALID_HANDLE
+
+
+/* Flag bits for context-level services */
+
+#define GSS_C_DELEG_FLAG ISC_REQ_DELEGATE
+#define GSS_C_MUTUAL_FLAG ISC_REQ_MUTUAL_AUTH
+#define GSS_C_REPLAY_FLAG ISC_REQ_REPLAY_DETECT
+#define GSS_C_SEQUENCE_FLAG ISC_REQ_SEQUENCE_DETECT
+#define GSS_C_CONF_FLAG ISC_REQ_CONFIDENTIALITY
+#define GSS_C_INTEG_FLAG ISC_REQ_INTEGRITY
+
+
+/* Credential usage options */
+
+#define GSS_C_BOTH SECPKG_CRED_BOTH
+#define GSS_C_INITIATE SECPKG_CRED_OUTBOUND
+#define GSS_C_ACCEPT SECPKG_CRED_INBOUND
+
+
+/* Major status codes defined by shim */
+
+#define GSS_S_BAD_BINDINGS 100
+#define GSS_S_BAD_NAME 101
+#define GSS_S_BAD_NAMETYPE 102
+#define GSS_S_BAD_STATUS 103
+
+/* GSSAPI types as used in GSSAPI */
+
+
+/* Buffer */
+
+typedef struct gss_buffer_desc_struct {
+ size_t length;
+ void *value;
+} gss_buffer_desc,*gss_buffer_t;
+
+
+/* Object identifier */
+
+typedef struct gss_OID_desc_struct {
+ OM_uint32 length;
+ void *elements;
+} gss_OID_desc,*gss_OID;
+
+typedef struct gss_OID_set_desc_struct {
+ size_t count;
+ gss_OID elements;
+} gss_OID_set_desc,*gss_OID_set;
+
+
+/* Unused, but needed in prototypes */
+
+typedef void * gss_channel_bindings_t;
+
+
+/* Default constants */
+
+#define GSS_C_EMPTY_BUFFER {0,NIL}
+#define GSS_C_NO_BUFFER ((gss_buffer_t) NIL)
+#define GSS_C_NO_OID ((gss_OID) NIL)
+#define GSS_C_NO_CONTEXT ((gss_ctx_id_t) NIL)
+#define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) NIL)
+#define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) NIL)
+#define GSS_C_QOP_DEFAULT NIL
+
+
+/* Status code types for gss_display_status */
+
+#define GSS_C_GSS_CODE 1
+#define GSS_C_MECH_CODE 2
+
+
+/* GSSAPI constants */
+
+const gss_OID gss_nt_service_name;
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+const gss_OID gss_mech_krb5;
+const gss_OID_set gss_mech_set_krb5;
+
+/* GSSAPI prototypes */
+
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle);
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec);
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token);
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type);
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string);
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name);
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec);
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer);
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle);
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name);
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer);
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state);
+
+/* Kerberos definitions */
+
+long kerberos_server_valid (void);
+long kerberos_try_kinit (OM_uint32 error);
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[]);
+
+
+#define STRING WINSTRING /* conflict with mail.h */
+#include <NTSecAPI.h>
+
+/* GSSAPI build-in object identifiers */
+
+static gss_OID_desc oids[] = { /* stupid C language makes this necessary */
+ {10,"\052\206\110\206\367\022\001\002\001\004"},
+ {9,"\052\206\110\206\367\022\001\002\002"}
+};
+
+ /* stupid C language ditto */
+static gss_OID_set_desc oidsets[] = {
+ {1,(gss_OID) oids+1}
+};
+
+ /* these are the real OIDs */
+const gss_OID gss_nt_service_name = oids+0;
+const gss_OID gss_mech_krb5 = oids+1;
+const gss_OID_set gss_mech_set_krb5 = oidsets+0;
+
+
+/* Other globals */
+
+ /* substitute for GSS_C_NO_CREDENTIAL */
+static gss_cred_id_t gss_default_cred = NIL;
+
+/* GSSAPI import name (convert to full service principal name)
+ * Accepts: pointer to return minor status
+ * buffer containining input name
+ * type of input name
+ * pointer to return output internal name
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_import_name (OM_uint32 *minor_status,
+ gss_buffer_t input_name_buffer,
+ gss_OID input_name_type,gss_name_t *output_name)
+{
+ OM_uint32 major_status = GSS_S_COMPLETE;
+ TimeStamp expiry;
+ static CredHandle gss_cred;
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (!gss_default_cred) { /* default credentials set up yet? */
+ if (AcquireCredentialsHandle/* no, acquire them now */
+ (NIL,MICROSOFT_KERBEROS_NAME_A,SECPKG_CRED_OUTBOUND,NIL,NIL,NIL,NIL,
+ &gss_cred,&expiry) != SEC_E_OK) return GSS_S_FAILURE;
+ /* have default credentials now */
+ gss_default_cred = &gss_cred;
+ }
+ /* must be the gss_nt_service_name format */
+ if (input_name_type != gss_nt_service_name)
+ major_status = GSS_S_BAD_NAMETYPE;
+ /* name must be of sane length */
+ else if (input_name_buffer->length > (MAILTMPLEN/2))
+ major_status = GSS_S_BAD_NAME;
+ else { /* copy name */
+ memcpy (tmp,input_name_buffer->value,input_name_buffer->length);
+ tmp[input_name_buffer->length] = '\0';
+ if (s = strchr (tmp,'@')) { /* find service/host/delimiter */
+ *s = '/'; /* convert to full service principal name */
+ *output_name = cpystr (tmp);
+ }
+ else major_status = GSS_S_BAD_NAME;
+ }
+ return major_status;
+}
+
+/* GSSAPI Initialize security context
+ * Accepts: pointer to return minor status
+ * claimant credential handle
+ * context (NIL means "none assigned yet")
+ * desired principal
+ * desired mechanisms
+ * required context attributes
+ * desired lifetime
+ * input channel bindings
+ * input token buffer
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_init_sec_context (OM_uint32 *minor_status,
+ gss_cred_id_t claimant_cred_handle,
+ gss_ctx_id_t *context_handle,
+ gss_name_t target_name,gss_OID mech_type,
+ OM_uint32 req_flags,OM_uint32 time_req,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_buffer_t input_token,
+ gss_OID *actual_mech_type,
+ gss_buffer_t output_token,OM_uint32 *ret_flags,
+ OM_uint32 *time_rec)
+{
+ OM_uint32 i;
+ OM_uint32 major_status;
+ TimeStamp expiry;
+ SecBuffer ibuf[1],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ *minor_status = 0; /* never any minor status */
+ /* error if non-default time requested */
+ if (time_req) return GSS_S_FAILURE;
+ if (mech_type && memcmp (mech_type,gss_mech_krb5,sizeof (gss_OID)))
+ return GSS_S_BAD_MECH;
+ /* ditto if any channel bindings */
+ if (input_chan_bindings != GSS_C_NO_CHANNEL_BINDINGS)
+ return GSS_S_BAD_BINDINGS;
+
+ /* apply default credential if necessary */
+ if (claimant_cred_handle == GSS_C_NO_CREDENTIAL)
+ claimant_cred_handle = gss_default_cred;
+ /* create output buffer storage as needed */
+ req_flags |= ISC_REQ_ALLOCATE_MEMORY;
+ /* make output buffer */
+ obuf[0].BufferType = SECBUFFER_TOKEN;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ /* output buffer descriptor */
+ obufs.ulVersion = SECBUFFER_VERSION;
+ obufs.cBuffers = 1;
+ obufs.pBuffers = obuf;
+ /* first time caller? */
+ if (*context_handle == GSS_C_NO_CONTEXT) {
+ /* yes, set up output context handle */
+ PCtxtHandle ctx = (PCtxtHandle) fs_get (sizeof (CtxtHandle));
+ major_status = InitializeSecurityContext (claimant_cred_handle,NIL,
+ target_name,req_flags,0,
+ SECURITY_NETWORK_DREP,NIL,0,ctx,
+ &obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ *context_handle = ctx; /* return updated context */
+ }
+ else { /* no, make SSPI buffer from GSSAPI buffer */
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[0].cbBuffer = input_token->length;
+ ibuf[0].pvBuffer = input_token->value;
+ /* input buffer descriptor */
+ ibufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf;
+ major_status = InitializeSecurityContext (claimant_cred_handle,
+ *context_handle,target_name,
+ req_flags,0,
+ SECURITY_NETWORK_DREP,&ibufs,0,
+ *context_handle,&obufs,
+ ret_flags ? ret_flags : &i,
+ &expiry);
+ }
+ /* return output */
+ output_token->value = obuf[0].pvBuffer;
+ output_token->length = obuf[0].cbBuffer;
+ /* in case client wanted lifetime returned */
+ if (time_rec) *time_rec = expiry.LowPart;
+ return major_status;
+}
+
+/* GSSAPI display status text
+ * Accepts: pointer to return minor status
+ * status to display
+ * status type
+ * message context for continuation
+ * buffer to write status string
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_display_status (OM_uint32 *minor_status,OM_uint32 status_value,
+ int status_type,gss_OID mech_type,
+ OM_uint32 *message_context,
+ gss_buffer_t status_string)
+{
+ char *s,tmp[MAILTMPLEN];
+ *minor_status = 0; /* never any minor status */
+ if (*message_context) return GSS_S_FAILURE;
+ switch (status_type) { /* what type of status code? */
+ case GSS_C_GSS_CODE: /* major_status */
+ switch (status_value) { /* analyze status value */
+ case GSS_S_FAILURE:
+ s = "Unspecified failure"; break;
+ case GSS_S_CREDENTIALS_EXPIRED:
+ s = "Credentials expired"; break;
+ case GSS_S_BAD_BINDINGS:
+ s = "Bad bindings"; break;
+ case GSS_S_BAD_MECH:
+ s = "Bad mechanism type"; break;
+ case GSS_S_BAD_NAME:
+ s = "Bad name"; break;
+ case GSS_S_BAD_NAMETYPE:
+ s = "Bad name type"; break;
+ case GSS_S_BAD_STATUS:
+ s = "Bad status"; break;
+ case GSS_S_NO_CONTEXT:
+ s = "Invalid context handle"; break;
+ case GSS_S_NO_CRED:
+ s = "Unable to authenticate to Kerberos service";
+ mail_parameters (NIL,DISABLE_AUTHENTICATOR,"GSSAPI");
+ break;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ s = "No authenticating authority"; break;
+ case SEC_E_TARGET_UNKNOWN:
+ s = "Destination server unknown to Kerberos service"; break;
+ default:
+ sprintf (s = tmp,"SSPI code %lx",status_value);
+ }
+ break;
+ case GSS_C_MECH_CODE: /* minor status - drop into default */
+ default:
+ return GSS_S_BAD_STATUS; /* bad status type */
+ }
+ /* return status string */
+ status_string->length = strlen (status_string->value = cpystr (s));
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI delete security context
+ * Accepts: pointer to return minor status
+ * context to delete
+ * output context token
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_delete_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_buffer_t output_token)
+{
+ OM_uint32 major_status;
+ *minor_status = 0; /* never any minor status */
+ /* output token not supported */
+ major_status = output_token ? GSS_S_FAILURE :
+ DeleteSecurityContext (*context_handle);
+ fs_give ((void **) context_handle);
+ return major_status;
+}
+
+
+/* GSSAPI release buffer
+ * Accepts: pointer to return minor status
+ * buffer to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_buffer (OM_uint32 *minor_status,gss_buffer_t buffer)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (&buffer->value);
+ return GSS_S_COMPLETE;
+}
+
+
+/* GSSAPI release name
+ * Accepts: pointer to return minor status
+ * pointer to name to release
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_name (OM_uint32 *minor_status,gss_name_t *input_name)
+{
+ *minor_status = 0; /* never any minor status */
+ fs_give (input_name);
+ return GSS_S_COMPLETE;
+}
+
+/* GSSAPI wrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * requested confidentiality
+ * requested quality of protection
+ * input message buffer
+ * pointer to return confidentiality state
+ * output message buffer
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_wrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ int conf_req_flag,gss_qop_t qop_req,
+ gss_buffer_t input_message_buffer,int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[3];
+ SecBufferDesc bufs;
+ SecPkgContext_Sizes sizes;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = conf_req_flag; /* same as requested */
+ if ((major_status = /* get trailer and padding sizes */
+ QueryContextAttributes (context_handle,SECPKG_ATTR_SIZES,&sizes)) ==
+ SEC_E_OK) {
+ /* create big enough output buffer */
+ output_message_buffer->value =
+ fs_get (sizes.cbSecurityTrailer + input_message_buffer->length +
+ sizes.cbBlockSize);
+ /* MSDN claims that for EncryptMessage() in Kerberos, you need an
+ * uninitialized SECBUFFER_STREAM_HEADER; a SECBUFFER_DATA that "contains
+ * the message to be encrypted. The message is encrypted in place,
+ * overwriting the original contents of its buffer"; an uninitialized
+ * SECBUFFER_STREAM_TRAILER, and an uninitialized SECBUFFER_EMPTY. I've
+ * never been able to get it to work that way.
+ */
+ bufs.cBuffers = 3; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ buf[0].BufferType = SECBUFFER_TOKEN;
+ buf[0].pvBuffer = output_message_buffer->value;
+ buf[0].cbBuffer = sizes.cbSecurityTrailer;
+ /* I/O buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = ((char *) buf[0].pvBuffer) + buf[0].cbBuffer;
+ buf[1].cbBuffer = input_message_buffer->length;
+ memcpy (buf[1].pvBuffer,input_message_buffer->value,buf[1].cbBuffer);
+ buf[2].BufferType = SECBUFFER_PADDING;
+ buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer;
+ buf[2].cbBuffer = sizes.cbBlockSize;
+ if ((major_status = EncryptMessage (context_handle,qop_req,&bufs,0)) ==
+ GSS_S_COMPLETE) {
+ /* slide data as necessary (how annoying!) */
+ unsigned long i = sizes.cbSecurityTrailer - buf[0].cbBuffer;
+ if (i) buf[1].pvBuffer =
+ memmove (((char *) buf[0].pvBuffer) + buf[0].cbBuffer,
+ buf[1].pvBuffer,buf[1].cbBuffer);
+ if (i += (input_message_buffer->length - buf[1].cbBuffer))
+ buf[1].pvBuffer = memmove (((char *)buf[1].pvBuffer) + buf[1].cbBuffer,
+ buf[2].pvBuffer,buf[2].cbBuffer);
+ output_message_buffer->length = buf[0].cbBuffer + buf[1].cbBuffer +
+ buf[2].cbBuffer;
+ }
+ else fs_give (&output_message_buffer->value);
+ }
+ return major_status; /* return status */
+}
+
+/* GSSAPI unwrap data
+ * Accepts: pointer to return minor status
+ * context handle
+ * input message buffer
+ * output message buffer
+ * pointer to return confidentiality state
+ * pointer to return quality of protection
+ * Returns: major status, always
+ */
+
+OM_uint32 gss_unwrap (OM_uint32 *minor_status,gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t output_message_buffer,int *conf_state,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 major_status;
+ SecBuffer buf[2];
+ SecBufferDesc bufs;
+ *minor_status = NIL; /* never any minor status */
+ *conf_state = NIL; /* or confidentiality state */
+ /* MSDN implies that all that is needed for DecryptMessage() in Kerberos
+ * is a single SECBUFFER_DATA which "contains the encrypted message. The
+ * encrypted message is decrypted in place, overwriting the original
+ * contents of its buffer." I've never been able to get it to work without
+ * using a SECBUFFER_STREAM for input and an uninitialized SECBUFFER_DATA
+ * for output.
+ * It *does* overwrite the input buffer, but not at the same point; e.g.
+ * with an input pointer of 0xa140a8 and size of 53, the output ends up
+ * at 0xa140d5 and size of 4.
+ */
+ bufs.cBuffers = 2; /* set up buffer descriptor */
+ bufs.pBuffers = buf;
+ bufs.ulVersion = SECBUFFER_VERSION;
+ /* input buffer */
+ buf[0].BufferType = SECBUFFER_STREAM;
+ buf[0].pvBuffer = input_message_buffer->value;
+ buf[0].cbBuffer = input_message_buffer->length;
+ /* output buffer */
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].pvBuffer = NIL;
+ buf[1].cbBuffer = 0;
+ /* decrypt and copy to output buffer */
+ if ((major_status = DecryptMessage (context_handle,&bufs,0,qop_state)) ==
+ SEC_E_OK)
+ memcpy (output_message_buffer->value = fs_get (buf[1].cbBuffer),
+ buf[1].pvBuffer,output_message_buffer->length = buf[1].cbBuffer);
+ return major_status; /* return status */
+}
+
+/* From here on are server-only functions, currently unused */
+
+
+/* GSSAPI acquire credentials
+ * Accepts: pointer to return minor status
+ * desired principal
+ * desired lifetime
+ * desired mechanisms
+ * credentials usage
+ * pointer to return credentials handle
+ * pointer to return mechanisms
+ * pointer to return lifetime
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_acquire_cred (OM_uint32 *minor_status,gss_name_t desired_name,
+ OM_uint32 time_req,gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,OM_uint32 *time_rec)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI release credentials
+ * Accepts: pointer to return minor status
+ * credentials handle to free
+ * Returns: GSS_S_COMPLETE, always
+ */
+
+OM_uint32 gss_release_cred (OM_uint32 *minor_status,gss_cred_id_t *cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* GSSAPI Accept security context
+ * Accepts: pointer to return minor status
+ * context
+ * acceptor credentials
+ * input token buffer
+ * input channel bindings
+ * pointer to return source name
+ * pointer to return mechanism type
+ * buffer to return output token
+ * pointer to return flags
+ * pointer to return context lifetime
+ * pointer to return delegated credentials
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_accept_sec_context (OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
+ gss_cred_id_t acceptor_cred_handle,
+ gss_buffer_t input_token_buffer,
+ gss_channel_bindings_t input_chan_bindings,
+ gss_name_t *src_name,gss_OID *mech_type,
+ gss_buffer_t output_token,
+ OM_uint32 *ret_flags,OM_uint32 *time_rec,
+ gss_cred_id_t *delegated_cred_handle)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+
+/* GSSAPI return printable name
+ * Accepts: pointer to return minor status
+ * internal name
+ * buffer to return output name
+ * output name type
+ * Returns: GSS_S_FAILURE, always
+ */
+
+OM_uint32 gss_display_name (OM_uint32 *minor_status,gss_name_t input_name,
+ gss_buffer_t output_name_buffer,
+ gss_OID *output_name_type)
+{
+ *minor_status = 0; /* never any minor status */
+ return GSS_S_FAILURE; /* server only */
+}
+
+/* Kerberos server valid check
+ * Returns: T if have keytab, NIL otherwise
+ */
+
+long kerberos_server_valid ()
+{
+ return NIL;
+}
+
+
+/* Kerberos check for missing or expired credentials
+ * Returns: T if should suggest running kinit, NIL otherwise
+ */
+
+long kerberos_try_kinit (OM_uint32 error)
+{
+ return NIL;
+}
+
+/* Kerberos server log in
+ * Accepts: authorization ID as user name
+ * authentication ID as Kerberos principal
+ * argument count
+ * argument vector
+ * Returns: logged in user name if logged in, NIL otherwise
+ */
+
+char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
+{
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/mailfile.h b/imap/src/osdep/nt/mailfile.h
new file mode 100644
index 00000000..056e66b7
--- /dev/null
+++ b/imap/src/osdep/nt/mailfile.h
@@ -0,0 +1,29 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Mail Spool file name
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 8 February 1996
+ * Last Edited: 30 August 2006
+ */
+
+#define MAILFILE "C:\\WINSMTP\\%s.MBX"
diff --git a/imap/src/osdep/nt/makefile.nt b/imap/src/osdep/nt/makefile.nt
new file mode 100644
index 00000000..0ea96e5a
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.nt
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 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: Portable C client makefile -- NT version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_nt.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_nt.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_md5.c auth_pla.c auth_log.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_nt.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.ntk b/imap/src/osdep/nt/makefile.ntk
new file mode 100644
index 00000000..e383e0f8
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.ntk
@@ -0,0 +1,118 @@
+# ========================================================================
+# Copyright 1988-2007 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: Portable C client makefile -- NT version + Kerberos
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32 /D_WIN32_WINNT=0x0400 /I\k5\include
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip4_nt.c
+ copy ip4_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_ntk.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_ntk.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_nt.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_mit.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_ntk.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/makefile.w2k b/imap/src/osdep/nt/makefile.w2k
new file mode 100644
index 00000000..a3d62ad6
--- /dev/null
+++ b/imap/src/osdep/nt/makefile.w2k
@@ -0,0 +1,119 @@
+# ========================================================================
+# Copyright 1988-2007 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: Portable C client makefile -- Windows 2000/XP version
+#
+# Author: Mark Crispin
+# Networks and Distributed Computing
+# Computing & Communications
+# University of Washington
+# Administration Building, AG-44
+# Seattle, WA 98195
+# Internet: MRC@CAC.Washington.EDU
+#
+# Date: 11 May 1989
+# Last Edited: 23 May 2007
+
+
+IP=6
+EXTRAAUTHENTICATORS =
+EXTRADRIVERS =
+EXTRACFLAGS =
+AUTHENTICATORS = ext gss md5 pla log
+DRIVERS = imap nntp pop3 mbx mtx tenex unix
+CREATEDRIVER = mbx
+APPENDDRIVER = unix
+OSCOMPAT = /DWIN32
+VSCOMPAT = /D_CRT_SECURE_NO_DEPRECATE /D_CRT_NONSTDC_NO_DEPRECATE
+CFLAGS = /MT /W3 /Ox /DCHUNKSIZE=65536 $(OSCOMPAT) $(VSCOMPAT) -nologo /I.. $(EXTRACFLAGS)
+CC = cl
+CCLIENTLIB = cclient.lib
+
+all: $(CCLIENTLIB)
+
+.c.obj:
+ $(CC) -c $(CFLAGS) $*.c
+
+osdep.h: os_nt.h
+ copy os_nt.h osdep.h
+ drivers $(EXTRADRIVERS) $(DRIVERS) dummy
+ setproto $(CREATEDRIVER) $(APPENDDRIVER)
+ echo ssl_onceonlyinit (); >> linkage.c
+ mkauths $(EXTRAAUTHENTICATORS) $(AUTHENTICATORS)
+ echo mail_versioncheck (CCLIENTVERSION); >> linkage.c
+
+ip_nt.c: ip$(IP)_nt.c
+ copy ip$(IP)_nt.c ip_nt.c
+
+mail.obj: mail.h misc.h osdep.h mail.c
+
+misc.obj: mail.h misc.h misc.c
+
+fdstring.obj: mail.h misc.h osdep.h fdstring.h fdstring.c
+
+flstring.obj: mail.h misc.h osdep.h flstring.h flstring.c
+
+netmsg.obj: mail.h misc.h netmsg.h osdep.h netmsg.c
+
+newsrc.obj: mail.h misc.h newsrc.h osdep.h newsrc.c
+
+rfc822.obj: mail.h rfc822.h misc.h rfc822.c
+
+smanager.obj: mail.h misc.h smanager.c
+
+utf8.obj: mail.h misc.h osdep.h utf8.h
+
+utf8aux.obj: mail.h misc.h osdep.h utf8.h
+
+imap4r1.obj: mail.h imap4r1.h misc.h osdep.h imap4r1.c
+
+nntp.obj: mail.h nntp.h smtp.h rfc822.h misc.h osdep.h nntp.c
+
+pop3.obj: mail.h rfc822.h misc.h osdep.h pop3.c
+
+smtp.obj: mail.h smtp.h rfc822.h misc.h osdep.h smtp.c
+
+os_w2k.obj: mail.h osdep.h env_nt.h fs.h ftl.h nl.h tcp.h tcp_nt.h yunchan.h \
+ os_w2k.c fs_nt.c ftl_nt.c nl_nt.c env_nt.c ssl_w2k.c ssl_none.c \
+ ip_nt.c tcp_nt.c yunchan.c pmatch.c write.c \
+ mailfile.h auth_gss.c auth_md5.c auth_pla.c auth_log.c kerb_w2k.c
+
+mbxnt.obj: mail.h misc.h osdep.h mbxnt.c
+
+mtxnt.obj: mail.h misc.h osdep.h mtxnt.c
+
+tenexnt.obj: mail.h misc.h osdep.h tenexnt.c
+
+unixnt.obj: mail.h unixnt.h pseudo.h misc.h osdep.h unixnt.c
+
+dummynt.obj: mail.h dummy.h misc.h osdep.h dummynt.c
+
+pseudo.obj: pseudo.h
+
+$(CCLIENTLIB): mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+ if exist $(CCLIENTLIB) del $(CCLIENTLIB)
+ LIB /NOLOGO /OUT:cclient.lib \
+ mail.obj misc.obj fdstring.obj flstring.obj netmsg.obj \
+ newsrc.obj rfc822.obj smanager.obj utf8.obj utf8aux.obj \
+ imap4r1.obj nntp.obj pop3.obj smtp.obj os_w2k.obj \
+ mbxnt.obj mtxnt.obj tenexnt.obj unixnt.obj dummynt.obj pseudo.obj
+
+clean:
+ del *.lib *.obj linkage.* osdep.* ip_nt.c auths.c *.exe *.exp || rem
+
+# A monument to a hack of long ago and far away...
+
+love:
+ @echo not war?
diff --git a/imap/src/osdep/nt/mbxnt.c b/imap/src/osdep/nt/mbxnt.c
new file mode 100644
index 00000000..bb3d4cc0
--- /dev/null
+++ b/imap/src/osdep/nt/mbxnt.c
@@ -0,0 +1,1694 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: MBX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 3 October 1995
+ * Last Edited: 28 September 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+
+/* Build parameters */
+
+#define HDRSIZE 2048
+
+/* MBX I/O stream local data */
+
+typedef struct mbx_local {
+ unsigned int flagcheck: 1; /* if ping should sweep for flags */
+ unsigned int expok: 1; /* if expunging OK in ping */
+ unsigned int expunged : 1; /* if one or more expunged messages */
+ int fd; /* file descriptor for I/O */
+ int ld; /* lock file descriptor */
+ int ffuserflag; /* first free user flag */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ char lock[MAILTMPLEN]; /* buffer to write lock name */
+} MBXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MBXLOCAL *) stream->local)
+
+/* Function prototypes */
+
+DRIVER *mbx_valid (char *name);
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags);
+void *mbx_parameters (long function,void *value);
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mbx_create (MAILSTREAM *stream,char *mailbox);
+long mbx_delete (MAILSTREAM *stream,char *mailbox);
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mbx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mbx_open (MAILSTREAM *stream);
+void mbx_close (MAILSTREAM *stream,long options);
+void mbx_abort (MAILSTREAM *stream);
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags);
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mbx_ping (MAILSTREAM *stream);
+void mbx_check (MAILSTREAM *stream);
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options);
+void mbx_snarf (MAILSTREAM *stream);
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mbx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok);
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mbx_update_header (MAILSTREAM *stream);
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags);
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr);
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags);
+long mbx_flaglock (MAILSTREAM *stream);
+
+/* MBX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mbxdriver = {
+ "mbx", /* driver name */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_LOCKING,
+ /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ mbx_valid, /* mailbox is valid for us */
+ mbx_parameters, /* manipulate parameters */
+ mbx_scan, /* scan mailboxes */
+ mbx_list, /* list mailboxes */
+ mbx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mbx_create, /* create mailbox */
+ mbx_delete, /* delete mailbox */
+ mbx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mbx_open, /* open mailbox */
+ mbx_close, /* close mailbox */
+ mbx_flags, /* fetch message "fast" attributes */
+ mbx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mbx_header, /* fetch message header */
+ mbx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mbx_flag, /* modify flags */
+ mbx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mbx_ping, /* ping mailbox to see if still alive */
+ mbx_check, /* check for new messages */
+ mbx_expunge, /* expunge deleted messages */
+ mbx_copy, /* copy messages to another mailbox */
+ mbx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mbxproto = {&mbxdriver};
+
+/* MBX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mbx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ int fd = mbx_isvalid (NIL,name,tmp,NIL,NIL,NIL);
+ if (fd < 0) return NIL;
+ close (fd); /* don't need the fd now */
+ return &mbxdriver;
+}
+
+
+/* MBX mail test for valid mailbox
+ * Accepts: returned stream with valid mailbox keywords
+ * mailbox name
+ * buffer to write file name
+ * returned lock fd
+ * returned lock name
+ * RW flags or NIL for readonly
+ * Returns: file descriptor if valid, NIL otherwise
+ */
+
+#define MBXISVALIDNOUID 0x1 /* RW, don't do UID action */
+#define MBXISVALIDUID 0x2 /* RW, do UID action */
+
+int mbx_isvalid (MAILSTREAM **stream,char *name,char *file,int *ld,char *lock,
+ long flags)
+{
+ int fd,upd;
+ int ret = -1;
+ unsigned long i;
+ long j,k;
+ off_t pos;
+ char c,*s,*t,hdr[HDRSIZE];
+ struct stat sbuf;
+ struct utimbuf times;
+ int error = EINVAL; /* assume invalid argument */
+ if (ld) *ld = -1; /* initially no lock */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG) &&
+ ((fd = open (file,(flags ? O_RDWR : O_RDONLY)|O_BINARY,NIL)) >= 0)) {
+ error = -1; /* assume bogus format */
+ if (((((j = read (fd,hdr,HDRSIZE)) == HDRSIZE) && (hdr[0] == '*')) ||
+ /* locked, set byte 0 to "*", read rest */
+ ((j < 0) && (lseek (fd,1,L_SET) == 1) &&
+ (read (fd,hdr+1,HDRSIZE-1) == (HDRSIZE-1)) && (hdr[0] = '*'))) &&
+ (hdr[1] == 'm') && (hdr[2] == 'b') && (hdr[3] == 'x') &&
+ (hdr[4] == '*') && (hdr[5] == '\015') && (hdr[6] == '\012') &&
+ isxdigit (hdr[7]) && isxdigit (hdr[8]) && isxdigit (hdr[9]) &&
+ isxdigit (hdr[10]) && isxdigit (hdr[11]) && isxdigit (hdr[12]) &&
+ isxdigit (hdr[13]) && isxdigit (hdr[14]) && isxdigit (c = hdr[15]) &&
+ isxdigit (hdr[16]) && isxdigit (hdr[17]) && isxdigit (hdr[18]) &&
+ isxdigit (hdr[19]) && isxdigit (hdr[20]) && isxdigit (hdr[21]) &&
+ isxdigit (hdr[22]) && (hdr[23] == '\015') && (hdr[24] == '\012')) {
+ ret = fd; /* mbx format */
+
+ if (stream) { /* lock if making a mini-stream */
+ if (flock (fd,LOCK_SH) ||
+ (flags && ((*ld = lockname (lock,file,LOCK_EX)) < 0))) ret = -1;
+ /* reread data now that locked */
+ else if (lseek (fd,0,L_SET) ||
+ (read (fd,hdr+1,HDRSIZE-1) != (HDRSIZE-1))) ret = -1;
+ else {
+ *stream = (MAILSTREAM *) memset (fs_get (sizeof (MAILSTREAM)),0,
+ sizeof (MAILSTREAM));
+ hdr[15] = '\0'; /* tie off UIDVALIDITY */
+ (*stream)->uid_validity = strtoul (hdr+7,NIL,16);
+ hdr[15] = c; /* now get UIDLAST */
+ (*stream)->uid_last = strtoul (hdr+15,NIL,16);
+ /* parse user flags */
+ for (i = 0, s = hdr + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (strlen (s) <= MAXUSERFLAG)
+ (*stream)->user_flags[i] = cpystr (s);
+ }
+ /* make sure have true UIDLAST */
+ if (flags & MBXISVALIDUID) {
+ for (upd = NIL,pos = 2048, k = 0; pos < sbuf.st_size;
+ pos += (j + k)) {
+ /* read header for this message */
+ lseek (fd,pos,L_SET);
+ if ((j = read (fd,hdr,64)) >= 0) {
+ hdr[j] = '\0';
+ if ((s = strchr (hdr,'\015')) && (s[1] == '\012')) {
+ *s = '\0';
+ k = s + 2 - hdr;
+ if ((s = strchr (hdr,',')) && (j = strtol (s+1,&s,10)) &&
+ (*s == ';') && (s = strchr (s+1,'-'))) {
+ /* get UID if there is any */
+ i = strtoul (++s,&t,16);
+ if (!*t && (t == (s + 8)) && (i <= (*stream)->uid_last)) {
+ if (!i) {
+ lseek (fd,pos + s - hdr,L_SET);
+ sprintf (hdr,"%08lx",++(*stream)->uid_last);
+ write (fd,hdr,8);
+ upd = T;
+ }
+ continue;
+ }
+ }
+ }
+ ret = -1; /* error, give up */
+ *stream = mail_close (*stream);
+ pos = sbuf.st_size + 1;
+ j = k = 0;
+ }
+ }
+
+ if (upd) { /* need to update hdr with new UIDLAST? */
+ lseek (fd,15,L_SET);
+ sprintf (hdr,"%08lx",(*stream)->uid_last);
+ write (fd,hdr,8);
+ }
+ }
+ }
+ }
+ }
+ if (ret != fd) close (fd); /* close the file */
+ else lseek (fd,0,L_SET); /* else rewind to start */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ /* in case INBOX but not mbx format */
+ else if (((error = errno) == ENOENT) && !compare_cstring (name,"INBOX"))
+ error = -1;
+ if ((ret < 0) && ld && (*ld >= 0)) {
+ unlockfd (*ld,lock);
+ *ld = -1;
+ }
+ errno = error; /* return as last error */
+ return ret; /* return what we should */
+}
+
+/* MBX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mbx_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_ONETIMEEXPUNGEATPING:
+ if (value) ((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok = T;
+ case GET_ONETIMEEXPUNGEATPING:
+ if (value) ret = (void *)
+ (((MBXLOCAL *) ((MAILSTREAM *) value)->local)->expok ? VOIDT : NIL);
+ break;
+ }
+ return ret;
+}
+
+
+/* MBX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mbx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MBX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MBX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mbx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MBX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[HDRSIZE];
+ long ret = NIL;
+ int i,fd;
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create (stream,s)) {
+ /* done if made directory */
+ if ((s = strrchr (s,'\\')) && !s[1]) return T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else {
+ memset (tmp,'\0',HDRSIZE);/* initialize header */
+ sprintf (s = tmp,"*mbx*\015\012%08lx00000000\015\012",
+ (unsigned long) time (0));
+ for (i = 0; i < NUSERFLAGS; ++i)
+ sprintf (s += strlen (s),"%s\015\012",
+ (stream && stream->user_flags[i]) ? stream->user_flags[i] :
+ "");
+ if (write (fd,tmp,HDRSIZE) != HDRSIZE) {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",
+ mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else ret = T; /* success */
+ close (fd); /* close file */
+ }
+ }
+ return ret;
+}
+
+
+/* MBX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mbx_rename (stream,mailbox,NIL);
+}
+
+/* MBX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_RDWR|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* recreate file if renamed INBOX */
+ if (ret && !compare_cstring (old,"INBOX")) mbx_create (NIL,"INBOX");
+ return ret; /* return success */
+}
+
+/* MBX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mbx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ short silent;
+ char tmp[MAILTMPLEN];
+ if (!stream) return &mbxproto;/* return prototype for OP_PROTOTYPE call */
+ if (stream->local) fatal ("mbx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_RDWR|O_BINARY,NIL)) < 0) {
+ if ((fd = open (tmp,O_RDONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+
+ stream->local = memset (fs_get (sizeof (MBXLOCAL)),NIL,sizeof (MBXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->ld = -1; /* no flaglock */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get parse/append permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = HDRSIZE; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->expok = LOCAL->flagcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ silent = stream->silent; /* defer events */
+ stream->silent = T;
+ if (mbx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ stream->silent = silent; /* now notify upper level */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,stream->recent);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted = stream->perm_flagged =
+ stream->perm_answered = stream->perm_draft = stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ stream->kwd_create = (stream->user_flags[NUSERFLAGS-1] || stream->rdonly) ?
+ NIL : T; /* can we create new user flags? */
+ return stream; /* return stream to caller */
+}
+
+/* MBX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mbx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ /* do an expunge if requested */
+ if (options & CL_EXPUNGE) mbx_expunge (stream,NIL,NIL);
+ else { /* otherwise do a checkpoint to purge */
+ LOCAL->expok = T; /* possible expunged messages */
+ mbx_ping (stream);
+ }
+ stream->silent = silent; /* restore previous status */
+ mbx_abort (stream);
+ }
+}
+
+
+/* MBX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void mbx_abort (MAILSTREAM *stream)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MBX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mbx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (mbx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence && !elt->valid)
+ mbx_elt (stream,i,NIL);
+}
+
+/* MBX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mbx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ unsigned long i;
+ char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get header position, possibly header */
+ i = mbx_hdrpos (stream,msgno,length,&s);
+ if (!s) { /* mbx_hdrpos() returned header? */
+ lseek (LOCAL->fd,i,L_SET); /* no, get to header position */
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,s = LOCAL->buf,*length);
+ }
+ s[*length] = '\0'; /* tie off string */
+ return s;
+}
+
+/* MBX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T on success, NIL on failure
+ */
+
+long mbx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = mbx_elt (stream,msgno,NIL);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen && mbx_flaglock (stream)) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mbx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (!LOCAL) return NIL; /* mbx_flaglock() could have aborted */
+ /* find header position */
+ i = mbx_hdrpos (stream,msgno,&j,NIL);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return LONGT; /* success */
+}
+
+/* MBX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ * Unlocks flag lock
+ */
+
+void mbx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ /* make sure the update takes */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld >= 0)) {
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ /* update header */
+ if ((LOCAL->ffuserflag < NUSERFLAGS) &&
+ stream->user_flags[LOCAL->ffuserflag]) mbx_update_header (stream);
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+ if (LOCAL->ld >= 0) { /* unlock now */
+ unlockfd (LOCAL->ld,LOCAL->lock);
+ LOCAL->ld = -1;
+ }
+}
+
+
+/* MBX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mbx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ if (mbx_flaglock (stream)) mbx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MBX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mbx_ping (MAILSTREAM *stream)
+{
+ unsigned long i,pos;
+ long ret = NIL;
+ int ld;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ ret = LONGT; /* assume OK */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ /* allow expunge if permitted at ping */
+ if (mail_parameters (NIL,GET_EXPUNGEATPING,NIL)) LOCAL->expok = T;
+ /* if external modification */
+ if (LOCAL->filetime && (LOCAL->filetime < sbuf.st_mtime))
+ LOCAL->flagcheck = T; /* upgrade to flag checking */
+ /* new mail or flagcheck handling needed? */
+ if (((sbuf.st_size - LOCAL->filesize) || LOCAL->flagcheck ||
+ !stream->nmsgs) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_EX)) >= 0)) {
+ if (!LOCAL->flagcheck) ret = mbx_parse (stream);
+ /* sweep mailbox for changed message status */
+ else if (ret = mbx_parse (stream)) {
+ unsigned long recent = 0;
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; )
+ if (elt = mbx_elt (stream,i,LOCAL->expok)) {
+ if (elt->recent) ++recent;
+ ++i;
+ }
+ mail_recent (stream,recent);
+ LOCAL->flagcheck = NIL; /* got all the updates */
+ }
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ if (ret) { /* must still be alive */
+ if (!LOCAL->expunged) /* look for holes if none known yet */
+ for (i = 1, pos = HDRSIZE;
+ !LOCAL->expunged && (i <= stream->nmsgs);
+ i++, pos += elt->private.special.text.size + elt->rfc822_size)
+ if ((elt = mail_elt (stream,i))->private.special.offset != pos)
+ LOCAL->expunged = T;/* found a hole */
+ /* burp any holes */
+ if (LOCAL->expunged && !stream->rdonly) {
+ if (mbx_rewrite (stream,&i,NIL)) fatal ("expunge on check");
+ if (i) { /* any space reclaimed? */
+ LOCAL->expunged = NIL;/* no more pending expunge */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",i);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ }
+ LOCAL->expok = NIL; /* no more expok */
+ }
+ }
+ return ret; /* return result of the parse */
+}
+
+/* MBX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mbx_check (MAILSTREAM *stream)
+{
+ if (LOCAL) LOCAL->expok = T; /* mark that a check is desired */
+ if (mbx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+
+/* MBX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T if success, NIL if failure
+ */
+
+long mbx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long nexp,reclaimed;
+ if (ret = sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) {
+ if (!mbx_ping (stream)); /* do nothing if stream dead */
+ else if (stream->rdonly) /* won't do on readonly files! */
+ mm_log ("Expunge ignored on readonly mailbox",WARN);
+ /* if expunged any messages */
+ else if (nexp = mbx_rewrite (stream,&reclaimed,sequence ? -1 : 1)) {
+ sprintf (LOCAL->buf,"Expunged %lu messages",nexp);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else if (reclaimed) { /* or if any prior expunged space reclaimed */
+ sprintf (LOCAL->buf,"Reclaimed %lu bytes of expunged space",reclaimed);
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ }
+ return ret;
+}
+
+/* MBX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mbx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k,m;
+ long ret = LONGT;
+ int fd,ld;
+ char *s,*t,file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) mail_parameters (NIL,GET_COPYUID,NIL);
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *dstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ cu ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ /* got file? */
+ if ((fd = open (dummy_file (file,mailbox),O_RDWR|O_CREAT|O_BINARY,
+ S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ mail_date(LOCAL->buf,elt);/* build target header */
+ /* get target keyword mask */
+ for (j = elt->user_flags, k = 0; j; )
+ if (s = stream->user_flags[find_rightmost_bit (&j)])
+ for (m = 0; (m < NUSERFLAGS) && (t = dstream->user_flags[m]); m++)
+ if (!compare_cstring (s,t) && (k |= 1 << m)) break;
+ sprintf (LOCAL->buf+strlen(LOCAL->buf),",%lu;%08lx%04x-%08lx\015\012",
+ elt->rfc822_size,k,(unsigned)
+ ((fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)),cu ? ++dstream->uid_last : 0);
+ /* write target header */
+ if (ret = (write (fd,LOCAL->buf,strlen (LOCAL->buf)) > 0)) {
+ for (k = elt->rfc822_size; ret && (j = min (k,LOCAL->buflen)); k -= j){
+ read (LOCAL->fd,LOCAL->buf,j);
+ ret = write (fd,LOCAL->buf,j) >= 0;
+ }
+ if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,dstream->uid_last);
+ }
+ }
+ }
+
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ if (cu && ret) { /* return sets if doing COPYUID */
+ (*cu) (stream,mailbox,dstream->uid_validity,source,dest);
+ lseek (fd,15,L_SET); /* update UIDLAST */
+ sprintf (LOCAL->buf,"%08lx",dstream->uid_last);
+ write (fd,LOCAL->buf,8);
+ }
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE) && mbx_flaglock (stream)) {
+ for (i = 1; i <= stream->nmsgs; i++) if (mail_elt (stream,i)->sequence) {
+ /* mark message deleted */
+ mbx_elt (stream,i,NIL)->deleted = T;
+ /* recalculate status */
+ mbx_update_status (stream,i,NIL);
+ }
+ /* update flags */
+ mbx_flag (stream,NIL,NIL,NIL);
+ }
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* MBX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mbx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = NIL;
+ MAILSTREAM *dstream = NIL;
+ appenduid_t au = (appenduid_t) mail_parameters (NIL,GET_APPENDUID,NIL);
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ /* make sure valid mailbox */
+ /* make sure valid mailbox */
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* can create INBOX here */
+ mbx_create (dstream = stream ? stream : &mbxproto,"INBOX");
+ if ((fd = mbx_isvalid (&dstream,mailbox,file,&ld,lock,
+ au ? MBXISVALIDUID : MBXISVALIDNOUID)) < 0)
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MBX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MBX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (dstream,data,&flags,&date,&message)) close (fd);
+ else if (!(df = fdopen (fd,"r+b"))) {
+ MM_LOG ("Unable to reopen append mailbox",ERROR);
+ close (fd);
+ }
+ else {
+ mm_critical (dstream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ fseek (df,sbuf.st_size,SEEK_SET);
+ errno = 0;
+ for (ret = LONGT; ret && message; ) {
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (dstream,flags,&uf);
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%08lx%04lx-%08lx\015\012",tmp,i = SIZE (message),
+ uf,(unsigned long) f,au ? ++dstream->uid_last : 0) < 0)
+ ret = NIL;
+ else { /* write message */
+ size_t j;
+ if (!message->cursize) SETPOS (message,GETPOS (message));
+ while (i && (j = fwrite (message->curpos,1,message->cursize,df))) {
+ i -= j;
+ SETPOS (message,GETPOS (message) + j);
+ }
+ /* get next message */
+ if (i || !(*af) (dstream,data,&flags,&date,&message)) ret = NIL;
+ else if (au) mail_append_set (dst,dstream->uid_last);
+ }
+ }
+
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ /* revert file */
+ ftruncate (fd,sbuf.st_size);
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (au && ret) { /* return sets if doing APPENDUID */
+ (*au) (mailbox,dstream->uid_validity,dst);
+ fseek (df,15,SEEK_SET); /* update UIDLAST */
+ fprintf (df,"%08lx",dstream->uid_last);
+ }
+ else mail_free_searchset (&dst);
+ if (ret) times.actime = time (0) - 1;
+ /* else preserve \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ /* preserve mtime */
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ mm_nocritical (dstream); /* release critical */
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ if (dstream != stream) mail_close (dstream);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MBX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mbx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j,k,m;
+ off_t curpos = LOCAL->filesize;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long recent = stream->recent;
+ unsigned long lastuid = 0;
+ short dirty = NIL;
+ short added = NIL;
+ short silent = stream->silent;
+ short uidwarn = T;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %lu to %lu!",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ lseek (LOCAL->fd,0,L_SET); /* rewind file */
+ /* read internal header */
+ read (LOCAL->fd,LOCAL->buf,HDRSIZE);
+ LOCAL->buf[HDRSIZE] = '\0'; /* tie off header */
+ c = LOCAL->buf[15]; /* save first character of last UID */
+ LOCAL->buf[15] = '\0';
+ /* parse UID validity */
+ stream->uid_validity = strtoul (LOCAL->buf + 7,NIL,16);
+ LOCAL->buf[15] = c; /* restore first character of last UID */
+ /* parse last UID */
+ i = strtoul (LOCAL->buf + 15,NIL,16);
+ stream->uid_last = stream->rdonly ? max (i,stream->uid_last) : i;
+ /* parse user flags */
+ for (i = 0, s = LOCAL->buf + 25;
+ (i < NUSERFLAGS) && (t = strchr (s,'\015')) && (t - s);
+ i++, s = t + 2) {
+ *t = '\0'; /* tie off flag */
+ if (!stream->user_flags[i] && (strlen (s) <= MAXUSERFLAG))
+ stream->user_flags[i] = cpystr (s);
+ }
+ LOCAL->ffuserflag = (int) i; /* first free user flag */
+
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %.80s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if (!(isxdigit (t[1]) && isxdigit (t[2]) && isxdigit (t[3]) &&
+ isxdigit (t[4]) && isxdigit (t[5]) && isxdigit (t[6]) &&
+ isxdigit (t[7]) && isxdigit (t[8]) && isxdigit (t[9]) &&
+ isxdigit (t[10]) && isxdigit (t[11]) && isxdigit (t[12]))) {
+ sprintf (tmp,"Unable to parse message flags at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ if ((t[13] != '-') || t[22] ||
+ !(isxdigit (t[14]) && isxdigit (t[15]) && isxdigit (t[16]) &&
+ isxdigit (t[17]) && isxdigit (t[18]) && isxdigit (t[19]) &&
+ isxdigit (t[20]) && isxdigit (t[21]))) {
+ sprintf (tmp,"Unable to parse message UID at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+
+ *s++ = '\0'; *t++ = '\0'; /* break up fields */
+ /* get message size */
+ if (!(j = strtoul (s,(char **) &x,10)) && (!(x && *x))) {
+ sprintf (tmp,"Unable to parse message size at %lu: %.80s,%.80s;%.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf,(char *) s,
+ (char *) t);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if (((off_t) (curpos + i + j)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ (unsigned long) curpos,(unsigned long) (curpos + i + j),
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* parse UID */
+ if ((m = strtoul (t+13,NIL,16)) &&
+ ((m <= lastuid) || (m > stream->uid_last))) {
+ if (uidwarn) {
+ sprintf (tmp,"Invalid UID %08lx in message %lu, rebuilding UIDs",
+ m,nmsgs+1);
+ mm_log (tmp,WARN);
+ uidwarn = NIL;
+ /* restart UID validity */
+ stream->uid_validity = (unsigned long) time (0);
+ }
+ m = 0; /* lose this UID */
+ dirty = T; /* mark dirty, set new lastuid */
+ stream->uid_last = lastuid;
+ }
+
+ t[12] = '\0'; /* parse system flags */
+ if ((k = strtoul (t+8,NIL,16)) & fEXPUNGED) {
+ if (m) lastuid = m; /* expunge message, update last UID seen */
+ else { /* no UID assigned? */
+ lastuid = ++stream->uid_last;
+ dirty = T;
+ }
+ }
+ else { /* not expunged, swell the cache */
+ added = T; /* note that a new message was added */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ /* parse the date */
+ if (!mail_parse_date (elt,LOCAL->buf)) {
+ sprintf (tmp,"Unable to parse message date at %lu: %.80s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mbx_abort (stream);
+ return NIL;
+ }
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* and internal header size */
+ elt->private.special.text.size = i;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ elt->rfc822_size = j; /* note message size */
+ /* calculate system flags */
+ if (k & fSEEN) elt->seen = T;
+ if (k & fDELETED) elt->deleted = T;
+ if (k & fFLAGGED) elt->flagged = T;
+ if (k & fANSWERED) elt->answered = T;
+ if (k & fDRAFT) elt->draft = T;
+ t[8] = '\0'; /* get user flags value */
+ elt->user_flags = strtoul (t,NIL,16);
+ /* UID already assigned? */
+ if (!(elt->private.uid = m) || !(k & fOLD)) {
+ elt->recent = T; /* no, mark as recent */
+ ++recent; /* count up a new recent message */
+ dirty = T; /* and must rewrite header */
+ /* assign new UID */
+ if (!elt->private.uid) elt->private.uid = ++stream->uid_last;
+ mbx_update_status (stream,elt->msgno,NIL);
+ }
+ /* update last parsed UID */
+ lastuid = elt->private.uid;
+ }
+ curpos += i + j; /* update position */
+ }
+
+ if (dirty && !stream->rdonly){/* update header */
+ mbx_update_header (stream);
+ fsync (LOCAL->fd); /* make sure all the UID updates take */
+ }
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MBX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * expunge OK flag
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mbx_elt (MAILSTREAM *stream,unsigned long msgno,long expok)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ /* get new flags */
+ if (mbx_read_flags (stream,elt) && expok) {
+ mail_expunged (stream,elt->msgno);
+ return NIL; /* return this message was expunged */
+ }
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MBX read flags from file
+ * Accepts: MAIL stream
+ * cache element
+ * Returns: non-NIL if message expunged
+ */
+
+unsigned long mbx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i;
+ struct stat sbuf;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag read!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ LOCAL->buf[13] = '\0'; /* tie off buffer */
+ /* calculate system flags */
+ i = strtoul (LOCAL->buf+9,NIL,16);
+ elt->seen = i & fSEEN ? T : NIL;
+ elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL;
+ elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->expunged |= i & fEXPUNGED ? T : NIL;
+ LOCAL->buf[9] = '\0'; /* tie off flags */
+ /* get user flags value */
+ elt->user_flags = strtoul (LOCAL->buf+1,NIL,16);
+ elt->valid = T; /* have valid flags now */
+ return i & fEXPUNGED;
+}
+
+/* MBX update header
+ * Accepts: MAIL stream
+ */
+
+#define NTKLUDGEOFFSET 7
+
+void mbx_update_header (MAILSTREAM *stream)
+{
+ int i;
+ char *s = LOCAL->buf;
+ memset (s,'\0',HDRSIZE); /* initialize header */
+ sprintf (s,"*mbx*\015\012%08lx%08lx\015\012",
+ stream->uid_validity,stream->uid_last);
+ for (i = 0; (i < NUSERFLAGS) && stream->user_flags[i]; ++i)
+ sprintf (s += strlen (s),"%s\015\012",stream->user_flags[i]);
+ LOCAL->ffuserflag = i; /* first free user flag */
+ /* can we create more user flags? */
+ stream->kwd_create = (i < NUSERFLAGS) ? T : NIL;
+ /* write reserved lines */
+ while (i++ < NUSERFLAGS) strcat (s,"\015\012");
+ while (T) { /* rewind file */
+ lseek (LOCAL->fd,NTKLUDGEOFFSET,L_SET);
+ /* write new header */
+ if (write (LOCAL->fd,LOCAL->buf + NTKLUDGEOFFSET,
+ HDRSIZE - NTKLUDGEOFFSET) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+}
+
+/* MBX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flags
+ */
+
+void mbx_update_status (MAILSTREAM *stream,unsigned long msgno,long flags)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mbx_read_flags (stream,elt);
+ else { /* readwrite */
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* make sure file size is good */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (LOCAL->buf,"Mailbox shrank from %lu to %lu in flag update!",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ fatal (LOCAL->buf);
+ }
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 24,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,14) < 0) {
+ sprintf (LOCAL->buf,"Unable to read old status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ if ((LOCAL->buf[0] != ';') || (LOCAL->buf[13] != '-')) {
+ LOCAL->buf[14] = '\0'; /* tie off buffer for error message */
+ sprintf (LOCAL->buf+50,"Invalid flags for message %lu (%lu %lu): %s",
+ elt->msgno,elt->private.special.offset,
+ elt->private.special.text.size,(char *) LOCAL->buf);
+ fatal (LOCAL->buf+50);
+ }
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%08lx%04x-%08lx",elt->user_flags,(unsigned)
+ (((elt->deleted && flags) ?
+ fEXPUNGED : (strtoul (LOCAL->buf+9,NIL,16)) & fEXPUNGED) +
+ (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft) + fOLD),elt->private.uid);
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 23,L_SET);
+ /* write new flags and UID */
+ if (write (LOCAL->fd,LOCAL->buf,21) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ }
+}
+
+/* MBX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * pointer to possible returned header
+ * Returns: position of header in file
+ */
+
+#define HDRBUFLEN 16384 /* good enough for most headers */
+#define SLOP 4 /* CR LF CR LF */
+
+unsigned long mbx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size,char **hdr)
+{
+ unsigned long siz,done;
+ long i;
+ unsigned char *s,*t,*te;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ if (hdr) *hdr = NIL; /* assume no header returned */
+ /* is header size known? */
+ if (*size = elt->private.msg.header.text.size) return ret;
+ /* paranoia check */
+ if (LOCAL->buflen < (HDRBUFLEN + SLOP))
+ fatal ("LOCAL->buf smaller than HDRBUFLEN");
+ lseek (LOCAL->fd,ret,L_SET); /* get to header position */
+ /* read HDRBUFLEN chunks with 4 byte slop */
+ for (done = siz = 0, s = LOCAL->buf;
+ (i = min ((long) (elt->rfc822_size - done),(long) HDRBUFLEN)) &&
+ (read (LOCAL->fd,s,i) == i);
+ done += i, siz += (t - LOCAL->buf) - SLOP, s = LOCAL->buf + SLOP) {
+ te = (t = s + i) - 12; /* calculate end of fast scan */
+ /* fast scan for CR */
+ for (s = LOCAL->buf; s < te;)
+ if (((*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015') ||
+ (*s++ == '\015') || (*s++ == '\015') || (*s++ == '\015')) &&
+ (*s == '\012') && (*++s == '\015') && (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ for (te = t - 3; (s < te);) /* final character-at-a-time scan */
+ if ((*s++ == '\015') && (*s == '\012') && (*++s == '\015') &&
+ (*++s == '\012')) {
+ *size = elt->private.msg.header.text.size = siz + (++s - LOCAL->buf);
+ if (hdr) *hdr = LOCAL->buf;
+ return ret;
+ }
+ if (i <= SLOP) break; /* end of data */
+ /* slide over last 4 bytes */
+ memmove (LOCAL->buf,t - SLOP,SLOP);
+ hdr = NIL; /* can't return header this way */
+ }
+ /* not found: header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ if (hdr) *hdr = LOCAL->buf; /* possibly return header too */
+ return ret;
+}
+
+/* MBX mail rewrite mailbox
+ * Accepts: MAIL stream
+ * pointer to return reclaimed size
+ * flags (0 = no expunge, 1 = expunge deleted, -1 = expunge sequence)
+ * Returns: number of expunged messages
+ */
+
+unsigned long mbx_rewrite (MAILSTREAM *stream,unsigned long *reclaimed,
+ long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos,ppos;
+ int ld;
+ unsigned long i,j,k,m,delta;
+ unsigned long n = *reclaimed = 0;
+ unsigned long recent = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ /* get parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ return 0;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime && !LOCAL->flagcheck &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->flagcheck = T;
+ if (!mbx_parse (stream)) { /* make sure see any newly-arrived messages */
+ unlockfd (ld,lock); /* failed?? */
+ return 0;
+ }
+ if (LOCAL->flagcheck) { /* sweep flags if need flagcheck */
+ LOCAL->filetime = sbuf.st_mtime;
+ for (i = 1; i <= stream->nmsgs; ++i) mbx_elt (stream,i,NIL);
+ LOCAL->flagcheck = NIL;
+ }
+
+ /* get exclusive access */
+ if (!flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ mm_critical (stream); /* go critical */
+ for (i = 1,delta = 0,pos = ppos = HDRSIZE; i <= stream->nmsgs; ) {
+ /* note if message not at predicted location */
+ if (m = (elt = mbx_elt (stream,i,NIL))->private.special.offset - ppos) {
+ ppos = elt->private.special.offset;
+ *reclaimed += m; /* note reclaimed message space */
+ delta += m; /* and as expunge delta */
+ }
+ /* number of bytes to smash or preserve */
+ ppos += (k = elt->private.special.text.size + elt->rfc822_size);
+ /* if need to expunge this message*/
+ if (flags && elt->deleted && ((flags > 0) || elt->sequence)) {
+ delta += k; /* number of bytes to delete */
+ mail_expunged(stream,i);/* notify upper levels */
+ n++; /* count up one more expunged message */
+ }
+ else { /* preserved message */
+ i++; /* count this message */
+ if (elt->recent) ++recent;
+ if (delta) { /* moved, note first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages yet */
+ else pos = elt->private.special.offset + k;
+ }
+ }
+ /* deltaed file size match position? */
+ if (m = (LOCAL->filesize -= delta) - pos) {
+ *reclaimed += m; /* probably an fEXPUNGED msg */
+ LOCAL->filesize = pos; /* set correct size */
+ }
+ /* truncate file after last message */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ fsync (LOCAL->fd); /* force disk update */
+ mm_nocritical (stream); /* release critical */
+ flock (LOCAL->fd,LOCK_SH); /* allow sharers again */
+ }
+
+ else { /* can't get exclusive */
+ flock (LOCAL->fd,LOCK_SH); /* recover previous shared mailbox lock */
+ /* do hide-expunge when shared */
+ if (flags) for (i = 1; i <= stream->nmsgs; ) {
+ if (elt = mbx_elt (stream,i,T)) {
+ /* make the message invisible */
+ if (elt->deleted && ((flags > 0) || elt->sequence)) {
+ mbx_update_status (stream,elt->msgno,LONGT);
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else {
+ i++; /* preserved message */
+ if (elt->recent) ++recent;
+ }
+ }
+ else n++; /* count up one more expunged message */
+ }
+ fsync (LOCAL->fd); /* force disk update */
+ }
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ return n; /* return number of expunged messages */
+}
+
+/* MBX mail lock for flag updating
+ * Accepts: stream
+ * Returns: T if successful, NIL if failure
+ */
+
+long mbx_flaglock (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ unsigned long i;
+ int ld;
+ char lock[MAILTMPLEN];
+ /* no-op if readonly or already locked */
+ if (!stream->rdonly && LOCAL && (LOCAL->fd >= 0) && (LOCAL->ld < 0)) {
+ /* lock now */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0) return NIL;
+ if (!LOCAL->flagcheck) { /* don't do this if flagcheck already needed */
+ if (LOCAL->filetime) { /* know previous time? */
+ fstat (LOCAL->fd,&sbuf);/* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->flagcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ if (!mbx_parse (stream)) {/* parse mailbox */
+ unlockfd (ld,lock); /* shouldn't happen */
+ return NIL;
+ }
+ if (LOCAL->flagcheck) /* invalidate cache if flagcheck */
+ for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->valid = NIL;
+ }
+ LOCAL->ld = ld; /* copy to stream for subsequent calls */
+ memcpy (LOCAL->lock,lock,MAILTMPLEN);
+ }
+ return LONGT;
+}
diff --git a/imap/src/osdep/nt/mkautaux.bat b/imap/src/osdep/nt/mkautaux.bat
new file mode 100755
index 00000000..c65022d2
--- /dev/null
+++ b/imap/src/osdep/nt/mkautaux.bat
@@ -0,0 +1,31 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator auxillary for NT/Win9x
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+ECHO extern AUTHENTICATOR auth_%1; >> LINKAGE.H
+REM Note the introduction of the caret to quote the ampersand in NT
+if "%OS%" == "Windows_NT" ECHO auth_link (^&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+if "%OS%" == "" ECHO auth_link (&auth_%1); /* link in the %1 authenticator */ >> LINKAGE.C
+ECHO #include "auth_%1.c" >> AUTHS.C
diff --git a/imap/src/osdep/nt/mkauths.bat b/imap/src/osdep/nt/mkauths.bat
new file mode 100755
index 00000000..d8c5e360
--- /dev/null
+++ b/imap/src/osdep/nt/mkauths.bat
@@ -0,0 +1,33 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Authenticator Linkage Generator for DOS and Windows
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 6 December 1995
+REM Last Edited:30 August 2006
+
+REM Erase old authenticators list
+IF EXIST AUTHS.C DEL AUTHS.C
+
+REM Now define the new list
+FOR %%D IN (%1 %2 %3 %4 %5 %6 %7 %8 %9) DO CALL MKAUTAUX %%D
+
+EXIT 0
diff --git a/imap/src/osdep/nt/mtxnt.c b/imap/src/osdep/nt/mtxnt.c
new file mode 100644
index 00000000..13f61586
--- /dev/null
+++ b/imap/src/osdep/nt/mtxnt.c
@@ -0,0 +1,1232 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: MTX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 15 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+#include "fdstring.h"
+
+/* MTX I/O stream local data */
+
+typedef struct mtx_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* last snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+} MTXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((MTXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *mtx_valid (char *name);
+int mtx_isvalid (char *name,char *file);
+void *mtx_parameters (long function,void *value);
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat);
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long mtx_create (MAILSTREAM *stream,char *mailbox);
+long mtx_delete (MAILSTREAM *stream,char *mailbox);
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname);
+long mtx_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *mtx_open (MAILSTREAM *stream);
+void mtx_close (MAILSTREAM *stream,long options);
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long mtx_ping (MAILSTREAM *stream);
+void mtx_check (MAILSTREAM *stream);
+void mtx_snarf (MAILSTREAM *stream);
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options);
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+long mtx_parse (MAILSTREAM *stream);
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno);
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag);
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* MTX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER mtxdriver = {
+ "mtx", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_CRLF|DR_NOSTICKY,
+ (DRIVER *) NIL, /* next driver */
+ mtx_valid, /* mailbox is valid for us */
+ mtx_parameters, /* manipulate parameters */
+ mtx_scan, /* scan mailboxes */
+ mtx_list, /* list mailboxes */
+ mtx_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ mtx_create, /* create mailbox */
+ mtx_delete, /* delete mailbox */
+ mtx_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ mtx_open, /* open mailbox */
+ mtx_close, /* close mailbox */
+ mtx_flags, /* fetch message "fast" attributes */
+ mtx_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ mtx_header, /* fetch message header */
+ mtx_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ mtx_flag, /* modify flags */
+ mtx_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ mtx_ping, /* ping mailbox to see if still alive */
+ mtx_check, /* check for new messages */
+ mtx_expunge, /* expunge deleted messages */
+ mtx_copy, /* copy messages to another mailbox */
+ mtx_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM mtxproto = {&mtxdriver};
+
+/* MTX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *mtx_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return mtx_isvalid (name,tmp) ? &mtxdriver : NIL;
+}
+
+
+/* MTX mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int mtx_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\015')) &&
+ (s[1] == '\012')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not mtx format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* MTX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *mtx_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* MTX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void mtx_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* MTX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* MTX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void mtx_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* MTX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* MTX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return mtx_rename (stream,mailbox,NIL);
+}
+
+/* MTX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long mtx_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* MTX mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *mtx_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &mtxproto;
+ if (stream->local) fatal ("mtx recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (MTXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (mtx_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* MTX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void mtx_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) mtx_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+
+/* MTX mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to see if some other process changed the flags
+ */
+
+void mtx_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ unsigned long i;
+ if (mtx_ping (stream) && /* ping mailbox, get new status for messages */
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if (mail_elt (stream,i)->sequence) mtx_elt (stream,i);
+}
+
+/* MTX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *mtx_header (MAILSTREAM *stream,unsigned long msgno,unsigned long *length,
+ long flags)
+{
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,mtx_hdrpos (stream,msgno,length),L_SET);
+ /* is buffer big enough? */
+ if (*length > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = *length) + 1);
+ }
+ LOCAL->buf[*length] = '\0'; /* tie off string */
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length);
+ return LOCAL->buf;
+}
+
+/* MTX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: T, always
+ */
+
+long mtx_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ FDDATA d;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mtx_elt (stream,msgno); /* get message status */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ mtx_update_status (stream,msgno,NIL);
+ mm_flags (stream,msgno);
+ }
+ /* find header position */
+ i = mtx_hdrpos (stream,msgno,&j);
+ d.fd = LOCAL->fd; /* set up file descriptor */
+ d.pos = i + j;
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE;
+ INIT (bs,fd_string,&d,elt->rfc822_size - j);
+ return T; /* success */
+}
+
+/* MTX mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void mtx_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* MTX mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void mtx_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ mtx_update_status (stream,elt->msgno,NIL);
+}
+
+/* MTX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long mtx_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) mtx_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (mtx_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* MTX mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void mtx_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (mtx_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* MTX mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long mtx_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ mtx_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!mtx_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = mtx_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* MTX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long mtx_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + elt->rfc822_size;
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* make sure all the updates take */
+ if (!(ret && (ret = !fsync (fd)))) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mtx_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ mtx_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* MTX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long mtx_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,uf;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &mtxproto;
+ /* make sure valid mailbox */
+ if (!mtx_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) mtx_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid MTX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a MTX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\015\012",tmp,i = SIZE (message),uf,
+ (unsigned long) f) < 0) ret = NIL;
+ else { /* write message */
+ if (i) do c = 0xff & SNX (message);
+ while ((putc (c,df) != EOF) && --i);
+ /* get next message */
+ if (i || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* MTX mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long mtx_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!((s = strchr (LOCAL->buf,'\015')) && (s[1] == '\012'))) {
+ sprintf (tmp,"Unable to find CRLF at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 2) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->rfc822_size = strtoul (s,(char **) &s,10)) && (!(s && *s)) &&
+ isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->rfc822_size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ mtx_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ mtx_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* MTX get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *mtx_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ mtx_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+/* MTX read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void mtx_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* MTX update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void mtx_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) mtx_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 14,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* MTX locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long mtx_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ int q = 0;
+ char *s,tmp[MAILTMPLEN];
+ MESSAGECACHE *elt = mtx_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for CRLF CRLF */
+ for (siz = 1,s = tmp; siz <= elt->rfc822_size; siz++) {
+ /* read another buffer as necessary */
+ if ((--i <= 0) && /* buffer empty? */
+ (read (LOCAL->fd,s = tmp,
+ i = min (elt->rfc822_size - siz,(long) MAILTMPLEN)) < 0))
+ return ret; /* I/O error? */
+ switch (q) { /* sniff at buffer */
+ case 0: /* first character */
+ q = (*s++ == '\015') ? 1 : 0;
+ break;
+ case 1: /* second character */
+ q = (*s++ == '\012') ? 2 : 0;
+ break;
+ case 2: /* third character */
+ q = (*s++ == '\015') ? 3 : 0;
+ break;
+ case 3: /* fourth character */
+ if (*s++ == '\012') { /* have the sequence? */
+ /* yes, note for later */
+ elt->private.msg.header.text.size = *size = siz;
+ return ret;
+ }
+ q = 0; /* lost... */
+ break;
+ }
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = elt->rfc822_size;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/nl_nt.c b/imap/src/osdep/nt/nl_nt.c
new file mode 100644
index 00000000..47cb7f0a
--- /dev/null
+++ b/imap/src/osdep/nt/nl_nt.c
@@ -0,0 +1,61 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Windows/TOPS-20 newline routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 1 August 1988
+ * Last Edited: 30 August 2006
+ */
+
+/* Copy string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long strcrlfcpy (unsigned char **dst,unsigned long *dstl,
+ unsigned char *src,unsigned long srcl)
+{
+ /* flush destination buffer if too small */
+ if (*dst && (srcl > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((size_t) (*dstl = srcl) + 1);
+ if (dstl) *dstl = srcl; /* return new buffer length to main program */
+ }
+ /* copy strings */
+ if (srcl) memcpy (*dst,src,(size_t) srcl);
+ *(*dst + srcl) = '\0'; /* tie off destination */
+ return srcl; /* return length */
+}
+
+
+/* Length of string after strcrlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long strcrlflen (STRING *s)
+{
+ return SIZE (s); /* no-brainer on DOS! */
+}
diff --git a/imap/src/osdep/nt/os_nt.c b/imap/src/osdep/nt/os_nt.c
new file mode 100644
index 00000000..0f6fea89
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_nt.h b/imap/src/osdep/nt/os_nt.h
new file mode 100644
index 00000000..650bb782
--- /dev/null
+++ b/imap/src/osdep/nt/os_nt.h
@@ -0,0 +1,60 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 May 1989
+ * Last Edited: 27 April 2007
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys\types.h>
+#include <time.h>
+#include <io.h>
+#include <conio.h>
+#include <process.h>
+#undef ERROR /* quell conflicting defintion warning */
+#include <windows.h>
+#include <lm.h>
+#undef ERROR
+#define ERROR (long) 2 /* must match mail.h */
+
+#if _MSC_VER >= 1400
+#define strtok_r strtok_s /* for some reason they called it this */
+#else
+/* strtok() is actually MT-safe in MSVC. Why is it that Microsoft can do
+ * their CRT right, but GNU, Sun, etc. can't?
+ */
+#define strtok_r(a,b,c) strtok(*(c) = a,b)
+#endif
+
+#include "env_nt.h"
+#include "fs.h"
+#include "ftl.h"
+#include "nl.h"
+#include "tcp.h"
+#include "yunchan.h"
+
+#undef noErr
+#undef MAC
diff --git a/imap/src/osdep/nt/os_ntk.c b/imap/src/osdep/nt/os_ntk.c
new file mode 100644
index 00000000..87c69123
--- /dev/null
+++ b/imap/src/osdep/nt/os_ntk.c
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Operating-system dependent routines -- NT version + Kerberos
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_mit.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_nt.c"
diff --git a/imap/src/osdep/nt/os_old.c b/imap/src/osdep/nt/os_old.c
new file mode 100644
index 00000000..5cd6c0c2
--- /dev/null
+++ b/imap/src/osdep/nt/os_old.c
@@ -0,0 +1,48 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Operating-system dependent routines -- NT version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 21 December 2007
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_old.c"
diff --git a/imap/src/osdep/nt/os_w2k.c b/imap/src/osdep/nt/os_w2k.c
new file mode 100644
index 00000000..c7af4faa
--- /dev/null
+++ b/imap/src/osdep/nt/os_w2k.c
@@ -0,0 +1,49 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Operating-system dependent routines -- Windows 2000 version
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+#include "tcp_nt.h" /* must be before osdep includes tcp.h */
+#undef ERROR /* quell conflicting def warning */
+#include "mail.h"
+#include "osdep.h"
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <sys\timeb.h>
+#include <fcntl.h>
+#include <sys\stat.h>
+#include "misc.h"
+#include "mailfile.h"
+
+#include "fs_nt.c"
+#include "ftl_nt.c"
+#include "nl_nt.c"
+#include "yunchan.c"
+#include "kerb_w2k.c"
+#include "tcp_nt.c" /* must be before env_nt.c */
+#include "env_nt.c"
+#include "ssl_w2k.c"
diff --git a/imap/src/osdep/nt/pmatch.c b/imap/src/osdep/nt/pmatch.c
new file mode 100644
index 00000000..95a0bb86
--- /dev/null
+++ b/imap/src/osdep/nt/pmatch.c
@@ -0,0 +1,89 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: IMAP Wildcard Matching Routines (case-independent)
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 15 June 2000
+ * Last Edited: 30 August 2006
+ */
+
+/* Wildcard pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if pattern matches base, else NIL
+ */
+
+long pmatch_full (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ /* % at end, OK if no inferiors */
+ if (!pat[1]) return (delim && strchr (s,delim)) ? NIL : T;
+ /* scan remainder of string until delimiter */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while ((*s != delim) && *s++);
+ break;
+ case '*': /* match 0 or more characters */
+ if (!pat[1]) return T; /* * at end, unconditional match */
+ /* scan remainder of string */
+ do if (pmatch_full (s,pat+1,delim)) return T;
+ while (*s++);
+ break;
+ case '\0': /* end of pattern */
+ return *s ? NIL : T; /* success if also end of base */
+ default: /* match this character */
+ return compare_uchar (*pat,*s) ? NIL : pmatch_full (s+1,pat+1,delim);
+ }
+ return NIL;
+}
+
+/* Directory pattern match
+ * Accepts: base string
+ * pattern string
+ * delimiter character
+ * Returns: T if base is a matching directory of pattern, else NIL
+ */
+
+long dmatch (unsigned char *s,unsigned char *pat,unsigned char delim)
+{
+ switch (*pat) {
+ case '%': /* non-recursive */
+ if (!*s) return T; /* end of base means have a subset match */
+ if (!*++pat) return NIL; /* % at end, no inferiors permitted */
+ /* scan remainder of string until delimiter */
+ do if (dmatch (s,pat,delim)) return T;
+ while ((*s != delim) && *s++);
+ if (*s && !s[1]) return T; /* ends with delimiter, must be subset */
+ return dmatch (s,pat,delim);/* do new scan */
+ case '*': /* match 0 or more characters */
+ return T; /* unconditional match */
+ case '\0': /* end of pattern */
+ break;
+ default: /* match this character */
+ if (*s) return compare_uchar (*pat,*s) ? NIL : dmatch (s+1,pat+1,delim);
+ /* end of base, return if at delimiter */
+ else if (*pat == delim) return T;
+ break;
+ }
+ return NIL;
+}
diff --git a/imap/src/osdep/nt/pseudo.c b/imap/src/osdep/nt/pseudo.c
new file mode 100644
index 00000000..1aae8a53
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.c
@@ -0,0 +1,36 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+/* Local sites may wish to alter this text */
+
+char *pseudo_from = "MAILER-DAEMON";
+char *pseudo_name = "Mail System Internal Data";
+char *pseudo_subject = "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA";
+char *pseudo_msg =
+ "This text is part of the internal format of your mail folder, and is not\na real message. It is created automatically by the mail system software.\nIf deleted, important folder data will be lost, and it will be re-created\nwith the data reset to initial values."
+ ;
diff --git a/imap/src/osdep/nt/pseudo.h b/imap/src/osdep/nt/pseudo.h
new file mode 100644
index 00000000..c9c07628
--- /dev/null
+++ b/imap/src/osdep/nt/pseudo.h
@@ -0,0 +1,30 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Pseudo Header Strings
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+extern char *pseudo_from,*pseudo_name,*pseudo_subject,*pseudo_msg;
diff --git a/imap/src/osdep/nt/setproto.bat b/imap/src/osdep/nt/setproto.bat
new file mode 100755
index 00000000..ce7cb1ef
--- /dev/null
+++ b/imap/src/osdep/nt/setproto.bat
@@ -0,0 +1,29 @@
+@ECHO OFF
+REM ========================================================================
+REM Copyright 1988-2006 University of Washington
+REM
+REM Licensed under the Apache License, Version 2.0 (the "License");
+REM you may not use this file except in compliance with the License.
+REM You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM
+REM ========================================================================
+
+REM Program: Set default prototype for DOS/NT
+REM
+REM Author: Mark Crispin
+REM Networks and Distributed Computing
+REM Computing & Communications
+REM University of Washington
+REM Administration Building, AG-44
+REM Seattle, WA 98195
+REM Internet: MRC@CAC.Washington.EDU
+REM
+REM Date: 9 October 1995
+REM Last Edited: 30 August 2006
+
+REM Set the default drivers
+ECHO #define CREATEPROTO %1proto >> LINKAGE.H
+ECHO #define APPENDPROTO %2proto >> LINKAGE.H
diff --git a/imap/src/osdep/nt/ssl_none.c b/imap/src/osdep/nt/ssl_none.c
new file mode 100644
index 00000000..e4dedda7
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_none.c
@@ -0,0 +1,141 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Dummy (no SSL) authentication/encryption module
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 7 February 2001
+ * Last Edited: 30 August 2006
+ */
+
+/* Init server for SSL
+ * Accepts: server name
+ */
+
+void ssl_server_init (char *server)
+{
+ syslog (LOG_ERR,"This server does not support SSL");
+ exit (1); /* punt this program too */
+}
+
+
+/* Start TLS
+ * Accepts: /etc/services service name
+ * Returns: cpystr'd error string if TLS failed, else NIL for success
+ */
+
+char *ssl_start_tls (char *server)
+{
+ return cpystr ("This server does not support TLS");
+}
+
+/* Get character
+ * Returns: character or EOF
+ */
+
+int PBIN (void)
+{
+ return getchar ();
+}
+
+
+/* Get string
+ * Accepts: destination string pointer
+ * number of bytes available
+ * Returns: destination string pointer or NIL if EOF
+ */
+
+char *PSIN (char *s,int n)
+{
+ return fgets (s,n,stdin);
+}
+
+
+/* Get record
+ * Accepts: destination string pointer
+ * number of bytes to read
+ * Returns: T if success, NIL otherwise
+ */
+
+long PSINR (char *s,unsigned long n)
+{
+ unsigned long i;
+ while (n && ((i = fread (s,1,n,stdin)) || (errno == EINTR))) s += i,n -= i;
+ return n ? NIL : LONGT;
+}
+
+
+/* Wait for input
+ * Accepts: timeout in seconds
+ * Returns: T if have input on stdin, else NIL
+ */
+
+long INWAIT (long seconds)
+{
+ return server_input_wait (seconds);
+}
+
+/* Put character
+ * Accepts: character
+ * Returns: character written or EOF
+ */
+
+int PBOUT (int c)
+{
+ return putchar (c);
+}
+
+
+/* Put string
+ * Accepts: source string pointer
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUT (char *s)
+{
+ return fputs (s,stdout);
+}
+
+
+/* Put record
+ * Accepts: source sized text
+ * Returns: 0 or EOF if error
+ */
+
+int PSOUTR (SIZEDTEXT *s)
+{
+ unsigned char *t;
+ unsigned long i,j;
+ for (t = s->data,i = s->size;
+ (i && ((j = fwrite (t,1,i,stdout)) || (errno == EINTR)));
+ t += j,i -= j);
+ return i ? EOF : NIL;
+}
+
+
+/* Flush output
+ * Returns: 0 or EOF if error
+ */
+
+int PFLUSH (void)
+{
+ return fflush (stdout);
+}
diff --git a/imap/src/osdep/nt/ssl_nt.c b/imap/src/osdep/nt/ssl_nt.c
new file mode 100644
index 00000000..c7efa486
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_nt.c
@@ -0,0 +1,721 @@
+/* ========================================================================
+ * 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: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+
+/* Define crypt32.dll stuff here in case a pre-IE5 Win9x system */
+
+typedef DWORD (CALLBACK *CNTS) (DWORD,PCERT_NAME_BLOB,DWORD,LPSTR,DWORD);
+typedef BOOL (CALLBACK *CGCC) (HCERTCHAINENGINE,PCCERT_CONTEXT,LPFILETIME,
+ HCERTSTORE,PCERT_CHAIN_PARA,DWORD,LPVOID,
+ PCCERT_CHAIN_CONTEXT *);
+typedef BOOL (CALLBACK *CVCCP) (LPCSTR,PCCERT_CHAIN_CONTEXT,
+ PCERT_CHAIN_POLICY_PARA,
+ PCERT_CHAIN_POLICY_STATUS);
+typedef VOID (CALLBACK *CFCC) (PCCERT_CHAIN_CONTEXT);
+typedef BOOL (CALLBACK *CFCCX) (PCCERT_CONTEXT);
+
+static CNTS certNameToStr = NIL;
+static CGCC certGetCertificateChain = NIL;
+static CVCCP certVerifyCertificateChainPolicy = NIL;
+static CFCC certFreeCertificateChain = NIL;
+static CFCCX certFreeCertificateContext = NIL;
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ if ((lib = LoadLibrary ("crypt32.dll")) &&
+ (certGetCertificateChain = (CGCC)
+ GetProcAddress (lib,"CertGetCertificateChain")) &&
+ (certVerifyCertificateChainPolicy = (CVCCP)
+ GetProcAddress (lib,"CertVerifyCertificateChainPolicy")) &&
+ (certFreeCertificateChain = (CFCC)
+ GetProcAddress (lib,"CertFreeCertificateChain")) &&
+ (certFreeCertificateContext = (CFCCX)
+ GetProcAddress (lib,"CertFreeCertificateContext")))
+ certNameToStr = (CNTS) GetProcAddress (lib,"CertNameToStrA");
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (certNameToStr && !(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ (*certNameToStr) (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!(*certGetCertificateChain)
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!(*certVerifyCertificateChainPolicy)
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ (*certFreeCertificateChain) (chain);
+ }
+ (*certFreeCertificateContext) (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_old.c b/imap/src/osdep/nt/ssl_old.c
new file mode 100644
index 00000000..121bd02e
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_old.c
@@ -0,0 +1,625 @@
+/* ========================================================================
+ * 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: SSL authentication/encryption module for Windows 9x and NT
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#if(_WIN32_WINNT < 0x0400)
+typedef unsigned int ALG_ID;
+#else
+#include <wincrypt.h>
+ALGIDDEF
+#endif
+#include <schnlsp.h>
+#include <issperr.h>
+
+ /* in case a binary runs on Windows 2000 */
+#ifndef ISC_REQ_MANUAL_CRED_VALIDATION
+#define ISC_REQ_MANUAL_CRED_VALIDATION 0x00080000
+#endif
+#ifndef SEC_E_UNTRUSTED_ROOT
+#define SEC_E_UNTRUSTED_ROOT ((HRESULT) 0x80090325L)
+#endif
+#ifndef SEC_E_CERT_EXPIRED
+#define SEC_E_CERT_EXPIRED ((HRESULT) 0x80090328L)
+#endif
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+ /* security function table */
+static SecurityFunctionTable *sft = NIL;
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ HINSTANCE lib;
+ FARPROC pi;
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (((lib = LoadLibrary ("schannel.dll")) ||
+ (lib = LoadLibrary ("security.dll"))) &&
+ (pi = GetProcAddress (lib,SECURITY_ENTRYPOINT)) &&
+ (sft = (SecurityFunctionTable *) pi ()) &&
+ !(sft->EnumerateSecurityPackages (&np,&pp))) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ char tmp[MAILTMPLEN];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR +
+ ((flags & NET_NOVALIDATECERT) ? ISC_REQ_MANUAL_CRED_VALIDATION :
+ ISC_REQ_MUTUAL_AUTH);
+ SCHANNEL_CRED tlscred;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (sft->AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = sft->InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ sft->FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (reason = ssl_analyze_status
+ (sft->QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf))
+ break; /* error getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ return "*Certificate has expired";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SChannel error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+
+ } while ((status = ((DECRYPT_MESSAGE_FN) sft->Reserved4)
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s = stream->ibuf;
+ size_t n = 0;
+ while (size) { /* until satisfied request */
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((((ENCRYPT_MESSAGE_FN) sft->Reserved3)
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ sft->DeleteSecurityContext (&stream->context);
+ sft->FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/ssl_w2k.c b/imap/src/osdep/nt/ssl_w2k.c
new file mode 100644
index 00000000..f5d8d1f5
--- /dev/null
+++ b/imap/src/osdep/nt/ssl_w2k.c
@@ -0,0 +1,683 @@
+/* ========================================================================
+ * 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: SSL authentication/encryption module for Windows 2000
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 September 1998
+ * Last Edited: 13 January 2008
+ */
+
+#define SECURITY_WIN32
+#include <sspi.h>
+#include <schannel.h>
+
+
+#define SSLBUFLEN 8192
+
+
+/* SSL I/O stream */
+
+typedef struct ssl_stream {
+ TCPSTREAM *tcpstream; /* TCP stream */
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ /* stream encryption sizes */
+ SecPkgContext_StreamSizes sizes;
+ size_t bufsize;
+ int ictr; /* input counter */
+ char *iptr; /* input pointer */
+ int iextractr; /* extra input counter */
+ char *iextraptr; /* extra input pointer */
+ char *ibuf; /* input buffer */
+ char *obuf; /* output buffer */
+} SSLSTREAM;
+
+#include "sslio.h"
+
+
+/* Function prototypes */
+
+static SSLSTREAM *ssl_start(TCPSTREAM *tstream,char *host,unsigned long flags);
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf);
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd);
+static long ssl_abort (SSLSTREAM *stream);
+
+/* Secure Sockets Layer network driver dispatch */
+
+static struct ssl_driver ssldriver = {
+ ssl_open, /* open connection */
+ ssl_aopen, /* open preauthenticated connection */
+ ssl_getline, /* get a line */
+ ssl_getbuffer, /* get a buffer */
+ ssl_soutr, /* output pushed data */
+ ssl_sout, /* output string */
+ ssl_close, /* close connection */
+ ssl_host, /* return host name */
+ ssl_remotehost, /* return remote host name */
+ ssl_port, /* return port number */
+ ssl_localhost /* return local host name */
+};
+
+static unsigned long ssltsz = 0;/* SSL maximum token length */
+
+/* One-time SSL initialization */
+
+static int sslonceonly = 0;
+
+void ssl_onceonlyinit (void)
+{
+ if (!sslonceonly++) { /* only need to call it once */
+ ULONG np;
+ SecPkgInfo *pp;
+ int i;
+ /* get security library */
+ if (!EnumerateSecurityPackages (&np,&pp)) {
+ /* look for an SSL package */
+ for (i = 0; (i < (int) np); i++) if (!strcmp (pp[i].Name,UNISP_NAME)) {
+ /* note maximum token size and name */
+ ssltsz = pp[i].cbMaxToken;
+ /* apply runtime linkage */
+ mail_parameters (NIL,SET_SSLDRIVER,(void *) &ssldriver);
+ mail_parameters (NIL,SET_SSLSTART,(void *) ssl_start);
+ return; /* all done */
+ }
+ }
+ }
+}
+
+/* SSL open
+ * Accepts: host name
+ * contact service name
+ * contact port number
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = tcp_open (host,service,port);
+ return stream ? ssl_start (stream,host,port) : NIL;
+}
+
+
+/* SSL authenticated open
+ * Accepts: host name
+ * service name
+ * returned user name buffer
+ * Returns: SSL stream if success else NIL
+ */
+
+SSLSTREAM *ssl_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* don't use this mechanism with SSL */
+}
+
+/* Start SSL/TLS negotiations
+ * Accepts: open TCP stream of session
+ * user's host name
+ * flags
+ * Returns: SSL stream if success else NIL
+ */
+
+static SSLSTREAM *ssl_start (TCPSTREAM *tstream,char *host,unsigned long flags)
+{
+ SECURITY_STATUS e;
+ ULONG a;
+ TimeStamp t;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ SCHANNEL_CRED tlscred;
+ CERT_CONTEXT *cert = NIL;
+ CERT_CHAIN_PARA chparam;
+ CERT_CHAIN_CONTEXT *chain;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA policy;
+ CERT_CHAIN_POLICY_PARA polparam;
+ CERT_CHAIN_POLICY_STATUS status;
+ char tmp[MAILTMPLEN],certname[256];
+ char *reason = NIL;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ LPSTR usage[] = {
+ szOID_PKIX_KP_SERVER_AUTH,
+ szOID_SERVER_GATED_CRYPTO,
+ szOID_SGC_NETSCAPE
+ };
+ PWSTR whost = NIL;
+ char *buf = (char *) fs_get (ssltsz);
+ unsigned long size = 0;
+ sslcertificatequery_t scq =
+ (sslcertificatequery_t) mail_parameters (NIL,GET_SSLCERTIFICATEQUERY,NIL);
+ sslfailure_t sf = (sslfailure_t) mail_parameters (NIL,GET_SSLFAILURE,NIL);
+ SSLSTREAM *stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ stream->tcpstream = tstream; /* bind TCP stream */
+ /* initialize TLS credential */
+ memset (&tlscred,0,sizeof (SCHANNEL_CRED));
+ tlscred.dwVersion = SCHANNEL_CRED_VERSION;
+ tlscred.grbitEnabledProtocols = SP_PROT_TLS1;
+
+ /* acquire credentials */
+ if (AcquireCredentialsHandle
+ (NIL,UNISP_NAME,SECPKG_CRED_OUTBOUND,NIL,(flags & NET_TLSCLIENT) ?
+ &tlscred : NIL,NIL,NIL,&stream->cred,&t)
+ != SEC_E_OK) reason = "Acquire credentials handle failed";
+ else while (!reason) { /* negotiate security context */
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = buf;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NIL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NIL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+ /* negotiate security */
+ e = InitializeSecurityContext
+ (&stream->cred,size ? &stream->context : NIL,host,req,0,
+ SECURITY_NETWORK_DREP,size? &ibufs:NIL,0,&stream->context,&obufs,&a,&t);
+ /* have an output buffer we need to send? */
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ if (!tcp_sout (stream->tcpstream,obuf[0].pvBuffer,obuf[0].cbBuffer))
+ reason = "Unexpected TCP output disconnect";
+ /* free the buffer */
+ FreeContextBuffer (obuf[0].pvBuffer);
+ }
+ if (!reason) switch (e) { /* negotiation state */
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break; /* server wants client auth */
+ case SEC_I_CONTINUE_NEEDED:
+ if (size) { /* continue, read any data? */
+ /* yes, anything regurgiated back to us? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (buf,buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ size = ibuf[1].cbBuffer;
+ break;
+ }
+ size = 0; /* otherwise, read more stuff from server */
+ }
+ case SEC_E_INCOMPLETE_MESSAGE:
+ /* need to read more data from server */
+ if (!tcp_getdata (stream->tcpstream))
+ reason = "Unexpected TCP input disconnect";
+ else {
+ memcpy (buf+size,stream->tcpstream->iptr,stream->tcpstream->ictr);
+ size += stream->tcpstream->ictr;
+ /* empty it from TCP's buffers */
+ stream->tcpstream->iptr += stream->tcpstream->ictr;
+ stream->tcpstream->ictr = 0;
+ }
+ break;
+
+ case SEC_E_OK: /* success, any data to be regurgitated? */
+ if (ibuf[1].BufferType == SECBUFFER_EXTRA) {
+ /* yes, set this as the new data */
+ memmove (stream->tcpstream->iptr = stream->tcpstream->ibuf,
+ buf + size - ibuf[1].cbBuffer,ibuf[1].cbBuffer);
+ stream->tcpstream->ictr = ibuf[1].cbBuffer;
+ }
+ if (!(flags & NET_NOVALIDATECERT)) {
+ /* need validation, make wchar of host */
+ if (!((size = MultiByteToWideChar (CP_ACP,0,host,-1,NIL,0)) &&
+ (whost = (PWSTR) fs_get (size*sizeof (WCHAR))) &&
+ MultiByteToWideChar (CP_ACP,0,host,-1,whost,size)))
+ fatal ("Can't make wchar of host name!");
+ /* get certificate */
+ if ((QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_REMOTE_CERT_CONTEXT,&cert) !=
+ SEC_E_OK) || !cert) {
+ reason = "*Unable to get certificate";
+ strcpy (certname,"<no certificate>");
+ }
+ else { /* get certificate subject name */
+ CertNameToStr (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ &cert->pCertInfo->Subject,CERT_X500_NAME_STR,
+ certname,255);
+ /* build certificate chain */
+ memset (&chparam,0,sizeof (chparam));
+ chparam.cbSize = sizeof (chparam);
+ chparam.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
+ chparam.RequestedUsage.Usage.rgpszUsageIdentifier = usage;
+ chparam.RequestedUsage.Usage.cUsageIdentifier =
+ sizeof (usage) / sizeof (LPSTR);
+ if (!CertGetCertificateChain
+ (NIL,cert,NIL,cert->hCertStore,&chparam,NIL,NIL,&chain))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else { /* validate certificate chain */
+ memset (&policy,0,sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA));
+ policy.cbStruct = sizeof (SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ policy.dwAuthType = AUTHTYPE_SERVER;
+ policy.fdwChecks = NIL;
+ policy.pwszServerName = whost;
+ memset (&polparam,0,sizeof (polparam));
+ polparam.cbSize = sizeof (polparam);
+ polparam.pvExtraPolicyPara = &policy;
+ memset (&status,0,sizeof (status));
+ status.cbSize = sizeof (status);
+ if (!CertVerifyCertificateChainPolicy
+ (CERT_CHAIN_POLICY_SSL,chain,&polparam,&status))
+ reason = ssl_analyze_status (GetLastError (),tmp);
+ else if (status.dwError)
+ reason = ssl_analyze_status (status.dwError,tmp);
+ CertFreeCertificateChain (chain);
+ }
+ CertFreeCertificateContext (cert);
+ }
+ if (whost) fs_give ((void **) &whost);
+
+ if (reason) { /* got an error? */
+ /* application callback */
+ if (scq) reason = (*scq) ((*reason == '*') ? reason + 1 : reason,
+ host,certname) ? NIL : "";
+ else if (*certname) { /* error message to return via mm_log() */
+ sprintf (buf,"*%.128s: %.255s",
+ (*reason == '*') ? reason + 1 : reason,certname);
+ reason = buf;
+ }
+ }
+ }
+ if (reason ||
+ (reason = ssl_analyze_status
+ (QueryContextAttributes
+ (&stream->context,SECPKG_ATTR_STREAM_SIZES,&stream->sizes),buf)))
+ break; /* error in certificate or getting sizes */
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ /* make maximum-sized buffers */
+ stream->bufsize = stream->sizes.cbHeader +
+ stream->sizes.cbMaximumMessage + stream->sizes.cbTrailer;
+ if (stream->sizes.cbMaximumMessage < SSLBUFLEN)
+ fatal ("cbMaximumMessage is less than SSLBUFLEN!");
+ else if (stream->sizes.cbMaximumMessage < 16384) {
+ sprintf (tmp,"WINDOWS BUG: cbMaximumMessage = %ld, should be 16384",
+ (long) stream->sizes.cbMaximumMessage);
+ mm_log (tmp,NIL);
+ }
+ stream->ibuf = (char *) fs_get (stream->bufsize);
+ stream->obuf = (char *) fs_get (stream->bufsize);
+ return stream;
+ default:
+ reason = ssl_analyze_status (e,buf);
+ }
+ }
+ ssl_close (stream); /* failed to do SSL */
+ stream = NIL; /* no stream returned */
+ switch (*reason) { /* analyze reason */
+ case '*': /* certificate failure */
+ ++reason; /* skip over certificate failure indication */
+ /* pass to error callback */
+ if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"Certificate failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ case '\0': /* user answered no to certificate callback */
+ if (flags & NET_TRYSSL) /* return dummy stream to stop tryssl */
+ stream = (SSLSTREAM *) memset (fs_get (sizeof (SSLSTREAM)),0,
+ sizeof (SSLSTREAM));
+ break;
+ default: /* non-certificate failure */
+ if (flags & NET_TRYSSL); /* no error output if tryssl */
+ /* pass to error callback */
+ else if (sf) (*sf) (host,reason,flags);
+ else { /* no error callback, build error message */
+ sprintf (tmp,"TLS/SSL failure for %.80s: %.512s",host,reason);
+ mm_log (tmp,ERROR);
+ }
+ break;
+ }
+ fs_give ((void **) &buf); /* flush temporary buffer */
+ return stream;
+}
+
+/* Generate error text from SSL error code
+ * Accepts: SSL status
+ * scratch buffer
+ * Returns: text if error status, else NIL
+ */
+
+static char *ssl_analyze_status (SECURITY_STATUS err,char *buf)
+{
+ switch (err) {
+ case SEC_E_OK: /* no error */
+ case SEC_I_CONTINUE_NEEDED:
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ case SEC_E_INCOMPLETE_MESSAGE:
+ return NIL;
+ case SEC_E_NO_AUTHENTICATING_AUTHORITY:
+ mm_log ("unexpected SEC_E_NO_AUTHENTICATING_AUTHORITY",NIL);
+ return "*No authority could be contacted for authentication";
+ case SEC_E_WRONG_PRINCIPAL:
+ mm_log ("unexpected SEC_E_WRONG_PRINCIPAL",NIL);
+ case CERT_E_CN_NO_MATCH:
+ return "*Server name does not match certificate";
+ case SEC_E_UNTRUSTED_ROOT:
+ mm_log ("unexpected SEC_E_UNTRUSTED_ROOT",NIL);
+ case CERT_E_UNTRUSTEDROOT:
+ return "*Self-signed certificate or untrusted authority";
+ case SEC_E_CERT_EXPIRED:
+ mm_log ("unexpected SEC_E_CERT_EXPIRED",NIL);
+ case CERT_E_EXPIRED:
+ return "*Certificate has expired";
+ case CERT_E_REVOKED:
+ return "*Certificate revoked";
+ case SEC_E_INVALID_TOKEN:
+ return "Invalid token, probably not an SSL server";
+ case SEC_E_UNSUPPORTED_FUNCTION:
+ return "SSL not supported on this machine - upgrade your system software";
+ }
+ sprintf (buf,"Unexpected SSPI or certificate error %lx - report this",err);
+ return buf;
+}
+
+/* SSL receive line
+ * Accepts: SSL stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *ssl_getline (SSLSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = ssl_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = ssl_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* SSL receive line or partial line
+ * Accepts: SSL stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *ssl_getline_work (SSLSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!ssl_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!ssl_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* SSL receive buffer
+ * Accepts: SSL stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getbuffer (SSLSTREAM *stream,unsigned long size,char *buffer)
+{
+ unsigned long n;
+ while (size > 0) { /* until request satisfied */
+ if (!ssl_getdata (stream)) return NIL;
+ n = min (size,stream->ictr);/* number of bytes to transfer */
+ /* do the copy */
+ memcpy (buffer,stream->iptr,n);
+ buffer += n; /* update pointer */
+ stream->iptr += n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -= n;
+ }
+ buffer[0] = '\0'; /* tie off string */
+ return T;
+}
+
+/* SSL receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long ssl_getdata (SSLSTREAM *stream)
+{
+ while (stream->ictr < 1) { /* decrypted buffer empty? */
+ SECURITY_STATUS status;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ size_t i;
+ size_t n = 0; /* initially no bytes to decrypt */
+ do { /* yes, make sure have data from TCP */
+ if (stream->iextractr) { /* have previous unread data? */
+ memcpy (stream->ibuf + n,stream->iextraptr,stream->iextractr);
+ n += stream->iextractr; /* update number of bytes read */
+ stream->iextractr = 0; /* no more extra data */
+ }
+ else { /* read from TCP */
+ if (!tcp_getdata (stream->tcpstream)) return ssl_abort (stream);
+ /* maximum amount of data to copy */
+ if (!(i = min (stream->bufsize - n,stream->tcpstream->ictr)))
+ fatal ("incomplete SecBuffer exceeds maximum buffer size");
+ /* do the copy */
+ memcpy (stream->ibuf + n,stream->tcpstream->iptr,i);
+ stream->tcpstream->iptr += i;
+ stream->tcpstream->ictr -= i;
+ n += i; /* update number of bytes to decrypt */
+ }
+ buf[0].cbBuffer = n; /* first SecBuffer gets data */
+ buf[0].pvBuffer = stream->ibuf;
+ buf[0].BufferType = SECBUFFER_DATA;
+ /* subsequent ones are for spares */
+ buf[1].BufferType = buf[2].BufferType = buf[3].BufferType =
+ SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ } while ((status = DecryptMessage
+ (&stream->context,&msg,0,NIL)) == SEC_E_INCOMPLETE_MESSAGE);
+ switch (status) {
+ case SEC_E_OK: /* won */
+ case SEC_I_RENEGOTIATE: /* won but lost it after this buffer */
+ /* hunt for a buffer */
+ for (i = 0; (i < 4) && (buf[i].BufferType != SECBUFFER_DATA) ; i++);
+ if (i < 4) { /* found a buffer? */
+ /* yes, set up pointer and counter */
+ stream->iptr = buf[i].pvBuffer;
+ stream->ictr = buf[i].cbBuffer;
+ /* any unprocessed data? */
+ while (++i < 4) if (buf[i].BufferType == SECBUFFER_EXTRA) {
+ /* yes, note for next time around */
+ stream->iextraptr = buf[i].pvBuffer;
+ stream->iextractr = buf[i].cbBuffer;
+ }
+ }
+ break;
+ default: /* anything else means we've lost */
+ return ssl_abort (stream);
+ }
+ }
+ return LONGT;
+}
+
+/* SSL send string as record
+ * Accepts: SSL stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long ssl_soutr (SSLSTREAM *stream,char *string)
+{
+ return ssl_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* SSL send string
+ * Accepts: SSL stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long ssl_sout (SSLSTREAM *stream,char *string,unsigned long size)
+{
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *s;
+ size_t n;
+ if (!stream->tcpstream) return NIL;
+ /* until request satisfied */
+ for (s = stream->ibuf,n = 0; size;) {
+ /* header */
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ memset (buf[0].pvBuffer = stream->obuf,0,
+ buf[0].cbBuffer = stream->sizes.cbHeader);
+ /* message (up to maximum size) */
+ buf[1].BufferType = SECBUFFER_DATA;
+ memcpy (buf[1].pvBuffer = stream->obuf + stream->sizes.cbHeader,string,
+ buf[1].cbBuffer = min (size,SSLBUFLEN));
+ /* trailer */
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ memset (buf[2].pvBuffer = ((char *) buf[1].pvBuffer) + buf[1].cbBuffer,0,
+ buf[2].cbBuffer = stream->sizes.cbTrailer);
+ /* spare */
+ buf[3].BufferType = SECBUFFER_EMPTY;
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4; /* number of SecBuffers */
+ msg.pBuffers = buf; /* first SecBuffer */
+ string += buf[1].cbBuffer;
+ size -= buf[1].cbBuffer; /* this many bytes processed */
+ /* encrypt and send message */
+ if ((EncryptMessage
+ (&stream->context,0,&msg,NIL) != SEC_E_OK) ||
+ !tcp_sout (stream->tcpstream,stream->obuf,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer))
+ return ssl_abort (stream);/* encryption or sending failed */
+ }
+ return LONGT;
+}
+
+/* SSL close
+ * Accepts: SSL stream
+ */
+
+void ssl_close (SSLSTREAM *stream)
+{
+ ssl_abort (stream); /* nuke the stream */
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* SSL abort stream
+ * Accepts: SSL stream
+ * Returns: NIL always
+ */
+
+static long ssl_abort (SSLSTREAM *stream)
+{
+ if (stream->tcpstream) { /* close TCP stream */
+ DeleteSecurityContext (&stream->context);
+ FreeCredentialHandle (&stream->cred);
+ tcp_close (stream->tcpstream);
+ stream->tcpstream = NIL;
+ }
+ if (stream->ibuf) fs_give ((void **) &stream->ibuf);
+ if (stream->obuf) fs_give ((void **) &stream->obuf);
+ return NIL;
+}
+
+/* SSL get host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_host (SSLSTREAM *stream)
+{
+ return tcp_host (stream->tcpstream);
+}
+
+
+/* SSL get remote host name
+ * Accepts: SSL stream
+ * Returns: host name for this stream
+ */
+
+char *ssl_remotehost (SSLSTREAM *stream)
+{
+ return tcp_remotehost (stream->tcpstream);
+}
+
+
+/* SSL return port for this stream
+ * Accepts: SSL stream
+ * Returns: port number for this stream
+ */
+
+unsigned long ssl_port (SSLSTREAM *stream)
+{
+ return tcp_port (stream->tcpstream);
+}
+
+
+/* SSL get local host name
+ * Accepts: SSL stream
+ * Returns: local host name
+ */
+
+char *ssl_localhost (SSLSTREAM *stream)
+{
+ return tcp_localhost (stream->tcpstream);
+}
+
+#include "ssl_none.c" /* currently no server support */
diff --git a/imap/src/osdep/nt/tcp_nt.c b/imap/src/osdep/nt/tcp_nt.c
new file mode 100644
index 00000000..a6d735dc
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.c
@@ -0,0 +1,916 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin from Mike Seibel's Winsock code
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 13 January 2007
+ */
+
+#include "ip_nt.c"
+
+
+#define TCPMAXSEND 32768
+
+/* Private functions */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst);
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd);
+long tcp_abort (TCPSTREAM *stream);
+long tcp_close_socket (SOCKET *sock);
+char *tcp_name (struct sockaddr *sadr,long flag);
+char *tcp_name_valid (char *s);
+
+
+/* Private data */
+
+int wsa_initted = 0; /* init ? */
+static int wsa_sock_open = 0; /* keep track of open sockets */
+static tcptimeout_t tmoh = NIL; /* TCP timeout handler routine */
+static long ttmo_open = 0; /* TCP timeouts, in seconds */
+static long ttmo_read = 0;
+static long ttmo_write = 0;
+static long allowreversedns = T;/* allow reverse DNS lookup */
+static long tcpdebug = NIL; /* extra TCP debugging telemetry */
+static char *myClientAddr = NIL;/* client host address */
+static char *myClientHost = NIL;/* client host name */
+static long myClientPort = -1; /* client port */
+static char *myServerAddr = NIL;/* server host address */
+static char *myServerHost = NIL;/* server host name */
+static long myServerPort = -1; /* server port */
+
+extern long maxposint; /* get this from write.c */
+
+/* TCP/IP manipulate parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tcp_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_TIMEOUT:
+ tmoh = (tcptimeout_t) value;
+ case GET_TIMEOUT:
+ ret = (void *) tmoh;
+ break;
+ case SET_OPENTIMEOUT:
+ ttmo_open = (long) value ? (long) value : (long) WSA_INFINITE;
+ case GET_OPENTIMEOUT:
+ ret = (void *) ttmo_open;
+ break;
+ case SET_READTIMEOUT:
+ ttmo_read = (long) value;
+ case GET_READTIMEOUT:
+ ret = (void *) ttmo_read;
+ break;
+ case SET_WRITETIMEOUT:
+ ttmo_write = (long) value;
+ case GET_WRITETIMEOUT:
+ ret = (void *) ttmo_write;
+ break;
+ case SET_ALLOWREVERSEDNS:
+ allowreversedns = (long) value;
+ case GET_ALLOWREVERSEDNS:
+ ret = (void *) allowreversedns;
+ break;
+ case SET_TCPDEBUG:
+ tcpdebug = (long) value;
+ case GET_TCPDEBUG:
+ ret = (void *) tcpdebug;
+ break;
+ }
+ return ret;
+}
+
+/* TCP/IP open
+ * Accepts: host name
+ * contact service name
+ * contact port number and optional silent flag
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_open (char *host,char *service,unsigned long port)
+{
+ TCPSTREAM *stream = NIL;
+ int i,family;
+ SOCKET sock = INVALID_SOCKET;
+ int silent = (port & NET_SILENT) ? T : NIL;
+ char *s,*hostname,tmp[MAILTMPLEN];
+ void *adr,*next;
+ size_t adrlen;
+ struct servent *sv = NIL;
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (i = (int) WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0; /* in case we try again */
+ sprintf (tmp,"Unable to start Windows Sockets (%d)",i);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ }
+ port &= 0xffff; /* erase flags */
+ /* lookup service */
+ if (service && (sv = getservbyname (service,"tcp")))
+ port = ntohs (sv->s_port);
+ /* The domain literal form is used (rather than simply the dotted decimal
+ as with other Windows programs) because it has to be a valid "host name"
+ in mailsystem terminology. */
+ /* look like domain literal? */
+ if (host[0] == '[' && host[(strlen (host))-1] == ']') {
+ strcpy (tmp,host+1); /* yes, copy number part */
+ tmp[strlen (tmp)-1] = '\0';
+ if (adr = ip_stringtoaddr (tmp,&adrlen,&family)) {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ sock = tcp_socket_open (family,adr,adrlen,(unsigned short) port,tmp,
+ hostname = host);
+ (*bn) (BLOCK_NONE,NIL);
+ fs_give ((void **) &adr);
+ }
+ else sprintf (tmp,"Bad format domain-literal: %.80s",host);
+ }
+
+ else { /* lookup host name */
+ if (tcpdebug) {
+ sprintf (tmp,"DNS resolution %.80s",host);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* look up name */
+ if (!(s = ip_nametoaddr (host,&adrlen,&family,&hostname,&next)))
+ sprintf (tmp,"Host not found (#%d): %s",WSAGetLastError (),host);
+ (*bn) (BLOCK_NONE,NIL);
+ if (s) { /* DNS resolution won? */
+ if (tcpdebug) mm_log ("DNS resolution done",TCPDEBUG);
+ wsa_sock_open++; /* prevent tcp_close_socket() from freeing in
+ loop */
+ do {
+ (*bn) (BLOCK_TCPOPEN,NIL);
+ if (((sock = tcp_socket_open (family,s,adrlen,(unsigned short) port,
+ tmp,hostname)) == INVALID_SOCKET) &&
+ (s = ip_nametoaddr (NIL,&adrlen,&family,&hostname,&next)) &&
+ !silent) mm_log (tmp,WARN);
+ (*bn) (BLOCK_NONE,NIL);
+ } while ((sock == INVALID_SOCKET) && s);
+ wsa_sock_open--; /* undo protection */
+ }
+ }
+ if (sock == INVALID_SOCKET) { /* do possible cleanup action */
+ if (!silent) mm_log (tmp,ERROR);
+ tcp_close_socket (&sock);
+ }
+ else { /* got a socket, create TCP/IP stream */
+ stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
+ sizeof (TCPSTREAM));
+ stream->port = port; /* port number */
+ /* init socket */
+ stream->tcpsi = stream->tcpso = sock;
+ stream->ictr = 0; /* init input counter */
+ /* copy official host name */
+ stream->host = cpystr (hostname);
+ if (tcpdebug) mm_log ("Stream open and ready for read",TCPDEBUG);
+ }
+ return stream; /* return success */
+}
+
+/* Open a TCP socket
+ * Accepts: protocol family
+ * address to connect to
+ * address length
+ * port
+ * scratch buffer
+ * host name
+ * Returns: socket if success, else SOCKET_ERROR with error string in scratch
+ */
+
+int tcp_socket_open (int family,void *adr,size_t adrlen,unsigned short port,
+ char *tmp,char *hst)
+{
+ int sock,err;
+ char *s,errmsg[100];
+ size_t len;
+ DWORD eo;
+ WSAEVENT event;
+ WSANETWORKEVENTS events;
+ unsigned long cmd = 0;
+ struct protoent *pt = getprotobyname ("tcp");
+ struct sockaddr *sadr = ip_sockaddr (family,adr,adrlen,port,&len);
+ sprintf (tmp,"Trying IP address [%s]",ip_sockaddrtostring (sadr));
+ mm_log (tmp,NIL);
+ /* get a TCP stream */
+ if ((sock = socket (sadr->sa_family,SOCK_STREAM,pt ? pt->p_proto : 0)) ==
+ INVALID_SOCKET)
+ sprintf (tmp,"Unable to create TCP socket (%d)",WSAGetLastError ());
+ else {
+ /* On Windows, FD_SETSIZE is the number of descriptors which can be
+ * held in an fd_set, as opposed to the maximum descriptor value on UNIX.
+ * Similarly, an fd_set in Windows is a vector of descriptor values, as
+ * opposed to a bitmask of set fds on UNIX. Thus, an fd_set can hold up
+ * to FD_SETSIZE values which can be larger than FD_SETSIZE, and the test
+ * that is used on UNIX is unnecessary here.
+ */
+ wsa_sock_open++; /* count this socket as open */
+ /* set socket nonblocking */
+ if (ttmo_open) WSAEventSelect (sock,event = WSACreateEvent (),FD_CONNECT);
+ else event = 0; /* no event */
+ /* open connection */
+ err = (connect (sock,sadr,len) == SOCKET_ERROR) ? WSAGetLastError () : NIL;
+ /* if timer in effect, wait for event */
+ if (event) while (err == WSAEWOULDBLOCK)
+ switch (eo = WSAWaitForMultipleEvents (1,&event,T,ttmo_open*1000,NIL)) {
+ case WSA_WAIT_EVENT_0: /* got an event? */
+ err = (WSAEnumNetworkEvents (sock,event,&events) == SOCKET_ERROR) ?
+ WSAGetLastError () : events.iErrorCode[FD_CONNECT_BIT];
+ break;
+ case WSA_WAIT_IO_COMPLETION:
+ break; /* fAlertable is NIL so shouldn't happen */
+ default: /* all other conditions */
+ err = eo; /* error from WSAWaitForMultipleEvents() */
+ break;
+ }
+
+ switch (err) { /* analyze result from connect and wait */
+ case 0: /* got a connection */
+ s = NIL;
+ if (event) { /* unset blocking mode */
+ WSAEventSelect (sock,event,NIL);
+ if (ioctlsocket (sock,FIONBIO,&cmd) == SOCKET_ERROR)
+ sprintf (s = errmsg,"Can't set blocking mode (%d)",
+ WSAGetLastError ());
+ }
+ break;
+ case WSAECONNREFUSED:
+ s = "Refused";
+ break;
+ case WSAENOBUFS:
+ s = "Insufficient system resources";
+ break;
+ case WSA_WAIT_TIMEOUT: case WSAETIMEDOUT:
+ s = "Timed out";
+ break;
+ case WSAEHOSTUNREACH:
+ s = "Host unreachable";
+ break;
+ default: /* horrible error 69 */
+ sprintf (s = errmsg,"Unknown error (%d)",err);
+ break;
+ }
+ /* flush event */
+ if (event) WSACloseEvent (event);
+ if (s) { /* got an error? */
+ sprintf (tmp,"Can't connect to %.80s,%ld: %.80s",hst,port,s);
+ tcp_close_socket (&sock); /* flush socket */
+ sock = INVALID_SOCKET;
+ }
+ }
+ fs_give ((void **) &sadr); /* and socket address */
+ return sock; /* return the socket */
+}
+
+/* TCP/IP authenticated open
+ * Accepts: NETMBX specifier
+ * service name
+ * returned user name buffer
+ * Returns: TCP/IP stream if success else NIL
+ */
+
+TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
+{
+ return NIL; /* always NIL on Windows */
+}
+
+/* TCP receive line
+ * Accepts: TCP stream
+ * Returns: text line string or NIL if failure
+ */
+
+char *tcp_getline (TCPSTREAM *stream)
+{
+ unsigned long n,contd;
+ char *ret = tcp_getline_work (stream,&n,&contd);
+ if (ret && contd) { /* got a line needing continuation? */
+ STRINGLIST *stl = mail_newstringlist ();
+ STRINGLIST *stc = stl;
+ do { /* collect additional lines */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ stc = stc->next = mail_newstringlist ();
+ ret = tcp_getline_work (stream,&n,&contd);
+ } while (ret && contd);
+ if (ret) { /* stash final part of line on list */
+ stc->text.data = (unsigned char *) ret;
+ stc->text.size = n;
+ /* determine how large a buffer we need */
+ for (n = 0, stc = stl; stc; stc = stc->next) n += stc->text.size;
+ ret = fs_get (n + 1); /* copy parts into buffer */
+ for (n = 0, stc = stl; stc; n += stc->text.size, stc = stc->next)
+ memcpy (ret + n,stc->text.data,stc->text.size);
+ ret[n] = '\0';
+ }
+ mail_free_stringlist (&stl);/* either way, done with list */
+ }
+ return ret;
+}
+
+/* TCP receive line or partial line
+ * Accepts: TCP stream
+ * pointer to return size
+ * pointer to return continuation flag
+ * Returns: text line string, size and continuation flag, or NIL if failure
+ */
+
+static char *tcp_getline_work (TCPSTREAM *stream,unsigned long *size,
+ long *contd)
+{
+ unsigned long n;
+ char *s,*ret,c,d;
+ *contd = NIL; /* assume no continuation */
+ /* make sure have data */
+ if (!tcp_getdata (stream)) return NIL;
+ for (s = stream->iptr, n = 0, c = '\0'; stream->ictr--; n++, c = d) {
+ d = *stream->iptr++; /* slurp another character */
+ if ((c == '\015') && (d == '\012')) {
+ ret = (char *) fs_get (n--);
+ memcpy (ret,s,*size = n); /* copy into a free storage string */
+ ret[n] = '\0'; /* tie off string with null */
+ return ret;
+ }
+ }
+ /* copy partial string from buffer */
+ memcpy ((ret = (char *) fs_get (n)),s,*size = n);
+ /* get more data from the net */
+ if (!tcp_getdata (stream)) fs_give ((void **) &ret);
+ /* special case of newline broken by buffer */
+ else if ((c == '\015') && (*stream->iptr == '\012')) {
+ stream->iptr++; /* eat the line feed */
+ stream->ictr--;
+ ret[*size = --n] = '\0'; /* tie off string with null */
+ }
+ else *contd = LONGT; /* continuation needed */
+ return ret;
+}
+
+/* TCP/IP receive buffer
+ * Accepts: TCP/IP stream
+ * size in bytes
+ * buffer to read into
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getbuffer (TCPSTREAM *stream,unsigned long size,char *s)
+{
+ unsigned long n;
+ /* make sure socket still alive */
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ /* can transfer bytes from buffer? */
+ if (n = min (size,stream->ictr)) {
+ memcpy (s,stream->iptr,n); /* yes, slurp as much as we can from it */
+ s += n; /* update pointer */
+ stream->iptr +=n;
+ size -= n; /* update # of bytes to do */
+ stream->ictr -=n;
+ }
+ if (size) {
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn=(blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Reading TCP buffer",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,s,(int) min (maxposint,size))) < 0) &&
+ (errno == EINTR));
+ else { /* socket case */
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,s,
+ (int) min (maxposint,size),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl),
+ stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP buffer read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP buffer read I/O error %d",errno);
+ else s = "TCP buffer read end of file";
+ mm_log (s,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ s += i; /* point at new place to write */
+ size -= i; /* reduce byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP buffer",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ }
+ *s = '\0'; /* tie off string */
+ return LONGT;
+}
+
+/* TCP/IP receive data
+ * Accepts: TCP/IP stream
+ * Returns: T if success, NIL otherwise
+ */
+
+long tcp_getdata (TCPSTREAM *stream)
+{
+ int i;
+ fd_set fds,efds;
+ struct timeval tmo;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ if (stream->tcpsi == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPREAD,NIL);
+ while (stream->ictr < 1) { /* if nothing in the buffer */
+ if (tcpdebug) mm_log ("Reading TCP data",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0);
+ time_t now = tl;
+ time_t ti = ttmo_read ? now + ttmo_read : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpsi,&fds);
+ FD_SET (stream->tcpsi,&efds);
+ errno = NIL; /* initially no error */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpsi+1,&fds,NIL,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* success from select, read what we can */
+ if (i > 0) while (((i = recv (stream->tcpsi,stream->ibuf,BUFLEN,0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP data read timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char *s,tmp[MAILTMPLEN];
+ if (i) sprintf (s = tmp,"TCP data read I/O error %d",errno);
+ else s = "TCP data read end of file";
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ stream->iptr = stream->ibuf;/* point at TCP buffer */
+ stream->ictr = i; /* set new byte count */
+ if (tcpdebug) mm_log ("Successfully read TCP data",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T;
+}
+
+/* TCP/IP send string as record
+ * Accepts: TCP/IP stream
+ * string pointer
+ * Returns: T if success else NIL
+ */
+
+long tcp_soutr (TCPSTREAM *stream,char *string)
+{
+ return tcp_sout (stream,string,(unsigned long) strlen (string));
+}
+
+
+/* TCP/IP send string
+ * Accepts: TCP/IP stream
+ * string pointer
+ * byte count
+ * Returns: T if success else NIL
+ */
+
+long tcp_sout (TCPSTREAM *stream,char *string,unsigned long size)
+{
+ int i;
+ struct timeval tmo;
+ fd_set fds,efds;
+ time_t t = time (0);
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ tmo.tv_sec = ttmo_write;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ if (stream->tcpso == INVALID_SOCKET) return NIL;
+ (*bn) (BLOCK_TCPWRITE,NIL);
+ while (size > 0) { /* until request satisfied */
+ if (tcpdebug) mm_log ("Writing to TCP",TCPDEBUG);
+ /* simple case if not a socket */
+ if (stream->tcpsi != stream->tcpso)
+ while (((i = write (stream->tcpso,string,min (size,TCPMAXSEND))) < 0) &&
+ (errno == EINTR));
+ else {
+ time_t tl = time (0); /* start of request */
+ time_t now = tl;
+ time_t ti = ttmo_write ? now + ttmo_write : 0;
+ tmo.tv_usec = 0;
+ FD_ZERO (&fds); /* initialize selection vector */
+ FD_ZERO (&efds); /* handle errors too */
+ /* set bit in selection vectors */
+ FD_SET (stream->tcpso,&fds);
+ FD_SET(stream->tcpso,&efds);
+ errno = NIL; /* block and write */
+ do { /* block under timeout */
+ tmo.tv_sec = (long) (ti ? ti - now : 0);
+ i = select (stream->tcpso+1,NIL,&fds,&efds,ti ? &tmo : NIL);
+ now = time (0); /* fake timeout if interrupt & time expired */
+ if ((i < 0) && ((errno = WSAGetLastError ()) == WSAEINTR) && ti &&
+ (ti <= now)) i = 0;
+ } while ((i < 0) && (errno == WSAEINTR));
+ /* OK to send data? */
+ if (i > 0) while (((i = send (stream->tcpso,string,
+ (int) min (size,TCPMAXSEND),0)) ==
+ SOCKET_ERROR) &&
+ ((errno = WSAGetLastError ()) == WSAEINTR));
+ else if (!i) { /* timeout, ignore if told to resume */
+ if (tmoh && (*tmoh) ((long) (now - t),(long) (now - tl), stream->host))
+ continue;
+ /* otherwise punt */
+ if (tcpdebug) mm_log ("TCP write timeout",TCPDEBUG);
+ return tcp_abort (stream);
+ }
+ }
+ if (i <= 0) { /* error seen? */
+ if (tcpdebug) {
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"TCP write I/O error %d",errno);
+ mm_log (tmp,TCPDEBUG);
+ }
+ return tcp_abort (stream);
+ }
+ string += i; /* how much we sent */
+ size -= i; /* count this size */
+ if (tcpdebug) mm_log ("successfully wrote to TCP",TCPDEBUG);
+ }
+ (*bn) (BLOCK_NONE,NIL);
+ return T; /* all done */
+}
+
+/* TCP/IP close
+ * Accepts: TCP/IP stream
+ */
+
+void tcp_close (TCPSTREAM *stream)
+{
+ tcp_abort (stream); /* nuke the sockets */
+ /* flush host names */
+ if (stream->host) fs_give ((void **) &stream->host);
+ if (stream->remotehost) fs_give ((void **) &stream->remotehost);
+ if (stream->localhost) fs_give ((void **) &stream->localhost);
+ fs_give ((void **) &stream); /* flush the stream */
+}
+
+
+/* TCP/IP abort sockets
+ * Accepts: TCP/IP stream
+ * Returns: NIL, always
+ */
+
+long tcp_abort (TCPSTREAM *stream)
+{
+ if (stream->tcpsi != stream->tcpso) tcp_close_socket (&stream->tcpso);
+ else stream->tcpso = INVALID_SOCKET;
+ return tcp_close_socket (&stream->tcpsi);
+}
+
+
+/* TCP/IP abort stream
+ * Accepts: WinSock socket
+ * Returns: NIL, always
+ */
+
+long tcp_close_socket (SOCKET *sock)
+{
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* something to close? */
+ if (sock && (*sock != INVALID_SOCKET)) {
+ (*bn) (BLOCK_TCPCLOSE,NIL);
+ closesocket (*sock); /* WinSock socket close */
+ *sock = INVALID_SOCKET;
+ (*bn) (BLOCK_NONE,NIL);
+ wsa_sock_open--; /* drop this socket */
+ }
+ /* no more open streams? */
+ if (wsa_initted && !wsa_sock_open) {
+ mm_log ("Winsock cleanup",NIL);
+ wsa_initted = 0; /* no more sockets, so... */
+ WSACleanup (); /* free up resources until needed */
+ }
+ return NIL;
+}
+
+/* TCP/IP get host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_host (TCPSTREAM *stream)
+{
+ return stream->host; /* use tcp_remotehost() if want guarantees */
+}
+
+
+/* TCP/IP get remote host name
+ * Accepts: TCP/IP stream
+ * Returns: host name for this stream
+ */
+
+char *tcp_remotehost (TCPSTREAM *stream)
+{
+ if (!stream->remotehost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->remotehost = /* get socket's peer name */
+ ((getpeername (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) ? cpystr (stream->host) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->remotehost;
+}
+
+
+/* TCP/IP return port for this stream
+ * Accepts: TCP/IP stream
+ * Returns: port number for this stream
+ */
+
+unsigned long tcp_port (TCPSTREAM *stream)
+{
+ return stream->port; /* return port number */
+}
+
+
+/* TCP/IP get local host name
+ * Accepts: TCP/IP stream
+ * Returns: local host name
+ */
+
+char *tcp_localhost (TCPSTREAM *stream)
+{
+ if (!stream->localhost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ stream->localhost = /* get socket's name */
+ ((stream->port & 0xffff000) ||
+ ((getsockname (stream->tcpsi,sadr,&sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0))) ? cpystr (mylocalhost ()) : tcp_name (sadr,NIL);
+ fs_give ((void **) &sadr);
+ }
+ return stream->localhost; /* return local host name */
+}
+
+/* TCP/IP get client host address (server calls only)
+ * Returns: client host address
+ */
+
+char *tcp_clientaddr ()
+{
+ if (!myClientAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientAddr;
+}
+
+
+/* TCP/IP get client host name (server calls only)
+ * Returns: client host name
+ */
+
+char *tcp_clienthost ()
+{
+ if (!myClientHost) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if ((getpeername (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myClientHost = cpystr ("UNKNOWN");
+ else { /* get stdin's peer name */
+ myClientHost = tcp_name (sadr,T);
+ if (!myClientAddr) myClientAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myClientPort < 0) myClientPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myClientHost;
+}
+
+
+/* TCP/IP get client port number (server calls only)
+ * Returns: client port number
+ */
+
+long tcp_clientport ()
+{
+ if (!myClientHost && !myClientAddr) tcp_clientaddr ();
+ return myClientPort;
+}
+
+/* TCP/IP get server host address (server calls only)
+ * Returns: server host address
+ */
+
+char *tcp_serveraddr ()
+{
+ if (!myServerAddr) {
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerAddr = cpystr ("UNKNOWN");
+ else { /* get stdin's name */
+ myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerAddr;
+}
+
+
+/* TCP/IP get server host name (server calls only)
+ * Returns: server host name
+ */
+
+char *tcp_serverhost ()
+{
+ if (!myServerHost) { /* once-only */
+ size_t sadrlen;
+ struct sockaddr *sadr = ip_newsockaddr (&sadrlen);
+ if (!wsa_initted++) { /* init Windows Sockets */
+ WSADATA wsock;
+ if (WSAStartup (WINSOCK_VERSION,&wsock)) {
+ wsa_initted = 0;
+ return "random-pc"; /* try again later? */
+ }
+ }
+ /* get stdin's name */
+ if ((getsockname (0,sadr,(void *) &sadrlen) == SOCKET_ERROR) ||
+ (sadrlen <= 0)) myServerHost = cpystr (mylocalhost ());
+ else { /* get stdin's name */
+ myServerHost = tcp_name (sadr,NIL);
+ if (!myServerAddr) myServerAddr = cpystr (ip_sockaddrtostring (sadr));
+ if (myServerPort < 0) myServerPort = ip_sockaddrtoport (sadr);
+ }
+ fs_give ((void **) &sadr);
+ }
+ return myServerHost;
+}
+
+
+/* TCP/IP get server port number (server calls only)
+ * Returns: server port number
+ */
+
+long tcp_serverport ()
+{
+ if (!myServerHost && !myServerAddr) tcp_serveraddr ();
+ return myServerPort;
+}
+
+/* TCP/IP return canonical form of host name
+ * Accepts: host name
+ * Returns: canonical form of host name
+ */
+
+char *tcp_canonical (char *name)
+{
+ char *ret,host[MAILTMPLEN];
+ blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
+ /* look like domain literal? */
+ if (name[0] == '[' && name[strlen (name) - 1] == ']') return name;
+ (*bn) (BLOCK_DNSLOOKUP,NIL);
+ if (tcpdebug) {
+ sprintf (host,"DNS canonicalization %.80s",name);
+ mm_log (host,TCPDEBUG);
+ }
+ /* get canonical name */
+ if (!ip_nametoaddr (name,NIL,NIL,&ret,NIL)) ret = name;
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("DNS canonicalization done",TCPDEBUG);
+ return ret;
+}
+
+
+/* TCP/IP return name from socket
+ * Accepts: socket
+ * verbose flag
+ * Returns: cpystr name
+ */
+
+char *tcp_name (struct sockaddr *sadr,long flag)
+{
+ char *ret,*t,adr[MAILTMPLEN],tmp[MAILTMPLEN];
+ sprintf (ret = adr,"[%.80s]",ip_sockaddrtostring (sadr));
+ if (allowreversedns) {
+ blocknotify_t bn = (blocknotify_t)mail_parameters(NIL,GET_BLOCKNOTIFY,NIL);
+ if (tcpdebug) {
+ sprintf (tmp,"Reverse DNS resolution %s",adr);
+ mm_log (tmp,TCPDEBUG);
+ }
+ (*bn) (BLOCK_DNSLOOKUP,NIL);/* quell alarms */
+ /* translate address to name */
+ if (t = tcp_name_valid (ip_sockaddrtoname (sadr))) {
+ /* produce verbose form if needed */
+ if (flag) sprintf (ret = tmp,"%s %s",t,adr);
+ else ret = t;
+ }
+ (*bn) (BLOCK_NONE,NIL); /* alarms OK now */
+ if (tcpdebug) mm_log ("Reverse DNS resolution done",TCPDEBUG);
+ }
+ return cpystr (ret);
+}
+
+/* Validate name
+ * Accepts: domain name
+ * Returns: T if valid, NIL otherwise
+ */
+
+char *tcp_name_valid (char *s)
+{
+ int c;
+ char *ret,*tail;
+ /* must be non-empty and not too long */
+ if ((ret = (s && *s) ? s : NIL) && (tail = ret + NETMAXHOST)) {
+ /* must be alnum, dot, or hyphen */
+ while ((c = *s++) && (s <= tail) &&
+ (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
+ ((c >= '0') && (c <= '9')) || (c == '-') || (c == '.')));
+ if (c) ret = NIL;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/tcp_nt.h b/imap/src/osdep/nt/tcp_nt.h
new file mode 100644
index 00000000..284cb226
--- /dev/null
+++ b/imap/src/osdep/nt/tcp_nt.h
@@ -0,0 +1,51 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Winsock TCP/IP routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 11 April 1989
+ * Last Edited: 30 August 2006
+ */
+
+/* TCP input buffer -- must be large enough to prevent overflow */
+
+#define BUFLEN 16384 /* 32768 causes stdin read() to barf */
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#undef ERROR /* quell conflicting definition diagnostic */
+
+
+/* TCP I/O stream (must be before osdep.h is included) */
+
+#define TCPSTREAM struct tcp_stream
+TCPSTREAM {
+ char *host; /* host name */
+ char *remotehost; /* remote host name */
+ unsigned long port; /* port number */
+ char *localhost; /* local host name */
+ SOCKET tcpsi; /* tcp socket */
+ SOCKET tcpso; /* tcp socket */
+ long ictr; /* input counter */
+ char *iptr; /* input pointer */
+ char ibuf[BUFLEN]; /* input buffer */
+};
diff --git a/imap/src/osdep/nt/tenexnt.c b/imap/src/osdep/nt/tenexnt.c
new file mode 100644
index 00000000..4805b7c5
--- /dev/null
+++ b/imap/src/osdep/nt/tenexnt.c
@@ -0,0 +1,1310 @@
+/* ========================================================================
+ * Copyright 1988-2007 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: Tenex mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 22 May 1990
+ * Last Edited: 18 June 2007
+ */
+
+
+/* FILE TIME SEMANTICS
+ *
+ * The atime is the last read time of the file.
+ * The mtime is the last flags update time of the file.
+ * The ctime is the last write time of the file.
+ *
+ * TEXT SIZE SEMANTICS
+ *
+ * Most of the text sizes are in internal (LF-only) form, except for the
+ * msg.text size. Beware.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <fcntl.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "misc.h"
+#include "dummy.h"
+
+/* TENEX I/O stream local data */
+
+typedef struct tenex_local {
+ unsigned int shouldcheck: 1; /* if ping should do a check instead */
+ unsigned int mustcheck: 1; /* if ping must do a check instead */
+ int fd; /* file descriptor for I/O */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ time_t lastsnarf; /* local snarf time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+} TENEXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((TENEXLOCAL *) stream->local)
+
+
+/* Function prototypes */
+
+DRIVER *tenex_valid (char *name);
+int tenex_isvalid (char *name,char *file);
+void *tenex_parameters (long function,void *value);
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat);
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long tenex_create (MAILSTREAM *stream,char *mailbox);
+long tenex_delete (MAILSTREAM *stream,char *mailbox);
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname);
+long tenex_status (MAILSTREAM *stream,char *mbx,long flags);
+MAILSTREAM *tenex_open (MAILSTREAM *stream);
+void tenex_close (MAILSTREAM *stream,long options);
+void tenex_fast (MAILSTREAM *stream,char *sequence,long flags);
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags);
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags);
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long tenex_ping (MAILSTREAM *stream);
+void tenex_check (MAILSTREAM *stream);
+void tenex_snarf (MAILSTREAM *stream);
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options);
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m);
+long tenex_parse (MAILSTREAM *stream);
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno);
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt);
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,
+ long syncflag);
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size);
+
+
+/* Tenex mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER tenexdriver = {
+ "tenex", /* driver name */
+ DR_LOCAL|DR_MAIL, /* driver flags */
+ (DRIVER *) NIL, /* next driver */
+ tenex_valid, /* mailbox is valid for us */
+ tenex_parameters, /* manipulate parameters */
+ tenex_scan, /* scan mailboxes */
+ tenex_list, /* list mailboxes */
+ tenex_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ tenex_create, /* create mailbox */
+ tenex_delete, /* delete mailbox */
+ tenex_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ tenex_open, /* open mailbox */
+ tenex_close, /* close mailbox */
+ tenex_flags, /* fetch message "fast" attributes */
+ tenex_flags, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ tenex_header, /* fetch message header */
+ tenex_text, /* fetch message body */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ tenex_flag, /* modify flags */
+ tenex_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ tenex_ping, /* ping mailbox to see if still alive */
+ tenex_check, /* check for new messages */
+ tenex_expunge, /* expunge deleted messages */
+ tenex_copy, /* copy messages to another mailbox */
+ tenex_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM tenexproto = {&tenexdriver};
+
+/* Tenex mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *tenex_valid (char *name)
+{
+ char tmp[MAILTMPLEN];
+ return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
+}
+
+
+/* Tenex mail test for valid mailbox
+ * Accepts: mailbox name
+ * buffer to return file name
+ * Returns: T if valid, NIL otherwise
+ */
+
+int tenex_isvalid (char *name,char *file)
+{
+ int fd;
+ int ret = NIL;
+ char *s,tmp[MAILTMPLEN];
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* if file, get its status */
+ if ((s = dummy_file (file,name)) && !stat (s,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
+ (s[-1] != '\015')) { /* valid format? */
+ *s = '\0'; /* tie off header */
+ /* must begin with dd-mmm-yy" */
+ ret = (((tmp[2] == '-' && tmp[6] == '-') ||
+ (tmp[1] == '-' && tmp[5] == '-')) &&
+ (s = strchr (tmp+18,',')) && strchr (s+2,';')) ? T : NIL;
+ }
+ else errno = -1; /* bogus format */
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if (sbuf.st_ctime > sbuf.st_atime) {
+ /* preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ /* in case INBOX but not tenex format */
+ else if ((errno == ENOENT) && !compare_cstring (name,"INBOX")) errno = -1;
+ return ret; /* return what we should */
+}
+
+
+/* Tenex manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *tenex_parameters (long function,void *value)
+{
+ return NIL;
+}
+
+/* Tenex mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void tenex_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* Tenex mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* Tenex mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void tenex_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* Tenex mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN];
+ if (s = dummy_file (mbx,mailbox)) return dummy_create (stream,s);
+ sprintf (mbx,"Can't create %.80s: invalid name",mailbox);
+ mm_log (mbx,ERROR);
+ return NIL;
+}
+
+
+/* Tenex mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return tenex_rename (stream,mailbox,NIL);
+}
+
+/* Tenex mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long tenex_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = LONGT;
+ char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ if (!dummy_file (file,old) ||
+ (newname && (!((s = mailboxfile (tmp,newname)) && *s) ||
+ ((s = strrchr (tmp,'\\')) && !s[1])))) {
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if ((fd = open (file,O_BINARY|O_RDWR,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox %.80s: %s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get exclusive parse/append permission */
+ if ((ld = lockname (lock,file,LOCK_EX)) < 0) {
+ mm_log ("Unable to lock rename mailbox",ERROR);
+ return NIL;
+ }
+ /* lock out other users */
+ if (flock (fd,LOCK_EX|LOCK_NB)) {
+ close (fd); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ mm_log (tmp,ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return NIL;
+ }
+
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,tmp)) ret = NIL;
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ /* rename the file */
+ if (ret && rename (file,tmp)) {
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ else {
+ flock (fd,LOCK_UN); /* release lock on the file */
+ close (fd); /* pacify NTFS */
+ if (unlink (file)) {
+ sprintf (tmp,"Can't delete mailbox %.80s: %.80s",old,strerror (errno));
+ mm_log (tmp,ERROR);
+ ret = NIL; /* set failure */
+ }
+ }
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ return ret; /* return success */
+}
+
+/* Tenex mail open
+ * Accepts: stream to open
+ * Returns: stream on success, NIL on failure
+ */
+
+MAILSTREAM *tenex_open (MAILSTREAM *stream)
+{
+ int fd,ld;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &tenexproto;
+ if (stream->local) fatal ("tenex recycle stream");
+ /* canonicalize the mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ }
+ if (stream->rdonly ||
+ (fd = open (tmp,O_BINARY|O_RDWR,NIL)) < 0) {
+ if ((fd = open (tmp,O_BINARY|O_RDONLY,NIL)) < 0) {
+ sprintf (tmp,"Can't open mailbox: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ else if (!stream->rdonly) { /* got it, but readonly */
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ stream->rdonly = T;
+ }
+ }
+ stream->local = fs_get (sizeof (TENEXLOCAL));
+ LOCAL->fd = fd; /* bind the file */
+ LOCAL->buf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->buflen = CHUNKSIZE - 1;
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ fs_give ((void **) &stream->mailbox);
+ stream->mailbox = cpystr (tmp);
+ /* get shared parse permission */
+ if ((ld = lockname (tmp,stream->mailbox,LOCK_SH)) < 0) {
+ mm_log ("Unable to lock open mailbox",ERROR);
+ return NIL;
+ }
+ flock (LOCAL->fd,LOCK_SH); /* lock the file */
+ unlockfd (ld,tmp); /* release shared parse permission */
+ LOCAL->filesize = 0; /* initialize parsed file size */
+ LOCAL->filetime = 0; /* time not set up yet */
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ stream->sequence++; /* bump sequence number */
+ stream->uid_validity = (unsigned long) time (0);
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ if (tenex_ping (stream) && !stream->nmsgs)
+ mm_log ("Mailbox is empty",(long) NIL);
+ if (!LOCAL) return NIL; /* failure if stream died */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft =
+ stream->rdonly ? NIL : T;
+ stream->perm_user_flags = stream->rdonly ? NIL : 0xffffffff;
+ return stream; /* return stream to caller */
+}
+
+/* Tenex mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void tenex_close (MAILSTREAM *stream,long options)
+{
+ if (stream && LOCAL) { /* only if a file is open */
+ int silent = stream->silent;
+ stream->silent = T; /* note this stream is dying */
+ if (options & CL_EXPUNGE) tenex_expunge (stream,NIL,NIL);
+ stream->silent = silent; /* restore previous status */
+ flock (LOCAL->fd,LOCK_UN); /* unlock local file */
+ close (LOCAL->fd); /* close the local file */
+ /* free local text buffer */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* Tenex mail fetch flags
+ * Accepts: MAIL stream
+ * sequence
+ * option flags
+ * Sniffs at file to get flags
+ */
+
+void tenex_flags (MAILSTREAM *stream,char *sequence,long flags)
+{
+ STRING bs;
+ MESSAGECACHE *elt;
+ unsigned long i;
+ if (stream && LOCAL &&
+ ((flags & FT_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)))
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ if (!elt->rfc822_size) { /* have header size yet? */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.special.text.size,L_SET);
+ /* resize bigbuf if necessary */
+ if (LOCAL->buflen < elt->private.msg.full.text.size) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buflen = elt->private.msg.full.text.size;
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen + 1);
+ }
+ /* tie off string */
+ LOCAL->buf[elt->private.msg.full.text.size] = '\0';
+ /* read in the message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.full.text.size);
+ INIT (&bs,mail_string,(void *) LOCAL->buf,
+ elt->private.msg.full.text.size);
+ /* calculate its CRLF size */
+ elt->rfc822_size = unix_crlflen (&bs);
+ }
+ tenex_elt (stream,i); /* get current flags from file */
+ }
+}
+
+/* TENEX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+char *tenex_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ char *s;
+ unsigned long i;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ /* get to header position */
+ lseek (LOCAL->fd,tenex_hdrpos (stream,msgno,&i),L_SET);
+ if (flags & FT_INTERNAL) {
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* slurp the data */
+ read (LOCAL->fd,LOCAL->buf,*length = i);
+ }
+ else {
+ s = (char *) fs_get (i + 1);/* get readin buffer */
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ return LOCAL->buf;
+}
+
+/* TENEX mail fetch message text (body only)
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T, always
+ */
+
+long tenex_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ /* get message status */
+ elt = tenex_elt (stream,msgno);
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ elt->seen = T; /* mark message as seen */
+ /* recalculate status */
+ tenex_update_status (stream,msgno,T);
+ mm_flags (stream,msgno);
+ }
+ if (flags & FT_INTERNAL) { /* if internal representation wanted */
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ if (i > LOCAL->buflen) { /* resize if not enough space */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get (LOCAL->buflen = i + 1);
+ }
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ /* slurp the data */
+ if (read (LOCAL->fd,LOCAL->buf,i) != (long) i) return NIL;
+ /* set up stringstruct for internal */
+ INIT (bs,mail_string,LOCAL->buf,i);
+ }
+ else { /* normal form, previous text cached? */
+ if (elt->private.uid == LOCAL->uid)
+ i = elt->private.msg.text.text.size;
+ else { /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* find header position */
+ i = tenex_hdrpos (stream,msgno,&j);
+ /* go to text position */
+ lseek (LOCAL->fd,i + j,L_SET);
+ s = (char *) fs_get ((i = tenex_size (stream,msgno) - j) + 1);
+ s[i] = '\0'; /* tie off string */
+ read (LOCAL->fd,s,i); /* slurp the data */
+ /* make CRLF copy of string */
+ i = elt->private.msg.text.text.size =
+ strcrlfcpy (&LOCAL->text.data,&LOCAL->text.size,s,i);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ /* set up stringstruct */
+ INIT (bs,mail_string,LOCAL->text.data,i);
+ }
+ return T; /* success */
+}
+
+/* Tenex mail modify flags
+ * Accepts: MAIL stream
+ * sequence
+ * flag(s)
+ * option flags
+ */
+
+void tenex_flag (MAILSTREAM *stream,char *sequence,char *flag,long flags)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read comes after all that */
+ utime (stream->mailbox,&times);
+ }
+}
+
+
+/* Tenex mail per-message modify flags
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void tenex_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ struct stat sbuf;
+ /* maybe need to do a checkpoint? */
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ LOCAL->filetime = 0; /* don't do this test for any other messages */
+ }
+ /* recalculate status */
+ tenex_update_status (stream,elt->msgno,NIL);
+}
+
+/* Tenex mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream still alive, NIL if not
+ */
+
+long tenex_ping (MAILSTREAM *stream)
+{
+ unsigned long i = 1;
+ long r = T;
+ int ld;
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ if (stream && LOCAL) { /* only if stream already open */
+ fstat (LOCAL->fd,&sbuf); /* get current file poop */
+ if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck) &&
+ (LOCAL->filetime < sbuf.st_mtime)) LOCAL->shouldcheck = T;
+ /* check for changed message status */
+ if (LOCAL->mustcheck || LOCAL->shouldcheck) {
+ LOCAL->filetime = sbuf.st_mtime;
+ if (LOCAL->shouldcheck) /* babble when we do this unilaterally */
+ mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
+ while (i <= stream->nmsgs) tenex_elt (stream,i++);
+ LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
+ }
+ /* get shared parse/append permission */
+ if ((sbuf.st_size != LOCAL->filesize) &&
+ ((ld = lockname (lock,stream->mailbox,LOCK_SH)) >= 0)) {
+ /* parse resulting mailbox */
+ r = (tenex_parse (stream)) ? T : NIL;
+ unlockfd (ld,lock); /* release shared parse/append permission */
+ }
+ }
+ return r; /* return result of the parse */
+}
+
+
+/* Tenex mail check mailbox (reparses status too)
+ * Accepts: MAIL stream
+ */
+
+void tenex_check (MAILSTREAM *stream)
+{
+ /* mark that a check is desired */
+ if (LOCAL) LOCAL->mustcheck = T;
+ if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
+}
+
+/* Tenex mail expunge mailbox
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long tenex_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ struct utimbuf times;
+ struct stat sbuf;
+ off_t pos = 0;
+ int ld;
+ unsigned long i = 1;
+ unsigned long j,k,m,recent;
+ unsigned long n = 0;
+ unsigned long delta = 0;
+ char lock[MAILTMPLEN];
+ MESSAGECACHE *elt;
+ if (!(ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ tenex_ping (stream))); /* parse sequence if given, ping stream */
+ else if (stream->rdonly) mm_log ("Expunge ignored on readonly mailbox",WARN);
+ else {
+ if (LOCAL->filetime && !LOCAL->shouldcheck) {
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
+ }
+ /* get exclusive access */
+ if ((ld = lockname (lock,stream->mailbox,LOCK_EX)) < 0)
+ mm_log ("Unable to lock expunge mailbox",ERROR);
+ /* make sure see any newly-arrived messages */
+ else if (!tenex_parse (stream));
+ /* get exclusive access */
+ else if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
+ flock (LOCAL->fd,LOCK_SH);/* recover previous lock */
+ mm_log ("Can't expunge because mailbox is in use by another process",
+ ERROR);
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+
+ else {
+ mm_critical (stream); /* go critical */
+ recent = stream->recent; /* get recent now that pinged and locked */
+ /* for each message */
+ while (i <= stream->nmsgs) {
+ /* get cache element */
+ elt = tenex_elt (stream,i);
+ /* number of bytes to smash or preserve */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ /* if need to expunge this message */
+ if (elt->deleted && (sequence ? elt->sequence : T)) {
+ /* if recent, note one less recent message */
+ if (elt->recent) --recent;
+ delta += k; /* number of bytes to delete */
+ /* notify upper levels */
+ mail_expunged (stream,i);
+ n++; /* count up one more expunged message */
+ }
+ else if (i++ && delta) {/* preserved message */
+ /* first byte to preserve */
+ j = elt->private.special.offset;
+ do { /* read from source position */
+ m = min (k,LOCAL->buflen);
+ lseek (LOCAL->fd,j,L_SET);
+ read (LOCAL->fd,LOCAL->buf,m);
+ pos = j - delta; /* write to destination position */
+ while (T) {
+ lseek (LOCAL->fd,pos,L_SET);
+ if (write (LOCAL->fd,LOCAL->buf,m) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ pos += m; /* new position */
+ j += m; /* next chunk, perhaps */
+ } while (k -= m); /* until done */
+ /* note the new address of this text */
+ elt->private.special.offset -= delta;
+ }
+ /* preserved but no deleted messages */
+ else pos = elt->private.special.offset + k;
+ }
+
+ if (n) { /* truncate file after last message */
+ if (pos != (LOCAL->filesize -= delta)) {
+ sprintf (LOCAL->buf,
+ "Calculated size mismatch %lu != %lu, delta = %lu",
+ (unsigned long) pos,(unsigned long) LOCAL->filesize,delta);
+ mm_log (LOCAL->buf,WARN);
+ LOCAL->filesize = pos;/* fix it then */
+ }
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ sprintf (LOCAL->buf,"Expunged %lu messages",n);
+ /* output the news */
+ mm_log (LOCAL->buf,(long) NIL);
+ }
+ else mm_log ("No messages deleted, so no update needed",(long) NIL);
+ fsync (LOCAL->fd); /* force disk update */
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* reset atime to now */
+ utime (stream->mailbox,&times);
+ mm_nocritical (stream); /* release critical */
+ /* notify upper level of new mailbox size */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ flock (LOCAL->fd,LOCK_SH);/* allow sharers again */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ }
+ }
+ return ret;
+}
+
+/* Tenex mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if success, NIL if failed
+ */
+
+long tenex_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ struct utimbuf times;
+ MESSAGECACHE *elt;
+ unsigned long i,j,k;
+ long ret = LONGT;
+ int fd,ld;
+ char file[MAILTMPLEN],lock[MAILTMPLEN];
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* got file? */
+ if ((fd = open (file,O_BINARY|O_RDWR|O_CREAT,S_IREAD|S_IWRITE)) < 0) {
+ sprintf (LOCAL->buf,"Unable to open copy mailbox: %.80s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ /* get exclusive parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock copy mailbox",ERROR);
+ mm_nocritical (stream);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
+
+ /* for each requested message */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ /* number of bytes to copy */
+ k = elt->private.special.text.size + tenex_size (stream,i);
+ do { /* read from source position */
+ j = min (k,LOCAL->buflen);
+ read (LOCAL->fd,LOCAL->buf,j);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ } while (ret && (k -= j));/* until done */
+ }
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR);
+ ftruncate (fd,sbuf.st_size);
+ }
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ close (fd); /* close the file */
+ mm_nocritical (stream); /* release critical */
+ /* delete all requested messages */
+ if (ret && (options & CP_MOVE)) {
+ for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = tenex_elt (stream,i))->sequence) {
+ elt->deleted = T; /* mark message deleted */
+ /* recalculate status */
+ tenex_update_status (stream,i,NIL);
+ }
+ if (!stream->rdonly) { /* make sure the update takes */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get current write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure atime remains greater */
+ utime (stream->mailbox,&times);
+ }
+ }
+ if (ret && mail_parameters (NIL,GET_COPYUID,NIL))
+ mm_log ("Can not return meaningful COPYUID with this mailbox format",WARN);
+ return ret;
+}
+
+/* Tenex mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+long tenex_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd,ld,c;
+ char *flags,*date,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *df;
+ MESSAGECACHE elt;
+ long f;
+ unsigned long i,j,uf,size;
+ STRING *message;
+ long ret = LONGT;
+ /* default stream to prototype */
+ if (!stream) stream = &tenexproto;
+ /* make sure valid mailbox */
+ if (!tenex_isvalid (mailbox,file)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) tenex_create (NIL,"INBOX");
+ else {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ /* falls through */
+ case 0: /* merely empty file? */
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid TENEX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a TENEX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get first message */
+ if (!(*af) (stream,data,&flags,&date,&message)) return NIL;
+
+ /* open destination mailbox */
+ if (((fd = open (file,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE))
+ < 0) || !(df = fdopen (fd,"ab"))) {
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get parse/append permission */
+ if (flock (fd,LOCK_SH) || ((ld = lockname (lock,file,LOCK_EX)) < 0)) {
+ mm_log ("Unable to lock append mailbox",ERROR);
+ close (fd);
+ return NIL;
+ }
+ mm_critical (stream); /* go critical */
+ fstat (fd,&sbuf); /* get current file size */
+ errno = 0;
+ do { /* parse flags */
+ if (!SIZE (message)) { /* guard against zero-length */
+ mm_log ("Append of zero-length message",ERROR);
+ ret = NIL;
+ break;
+ }
+ f = mail_parse_flags (stream,flags,&i);
+ /* reverse bits (dontcha wish we had CIRC?) */
+ for (uf = 0; i; uf |= 1 << (29 - find_rightmost_bit (&i)));
+ if (date) { /* parse date if given */
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ ret = NIL; /* mark failure */
+ break;
+ }
+ mail_date (tmp,&elt); /* write preseved date */
+ }
+ else internal_date (tmp); /* get current date in IMAP format */
+ i = GETPOS (message); /* remember current position */
+ for (j = SIZE (message), size = 0; j; --j)
+ if (SNX (message) != '\015') ++size;
+ SETPOS (message,i); /* restore position */
+ /* write header */
+ if (fprintf (df,"%s,%lu;%010lo%02lo\n",tmp,size,uf,(unsigned long) f) < 0)
+ ret = NIL;
+ else { /* write message */
+ while (size) if ((c = 0xff & SNX (message)) != '\015') {
+ if (putc (c,df) != EOF) --size;
+ else break;
+ }
+ /* get next message */
+ if (size || !(*af) (stream,data,&flags,&date,&message)) ret = NIL;
+ }
+ } while (ret && message);
+ /* if error... */
+ if (!ret || (fflush (df) == EOF)) {
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ close (fd); /* make sure fclose() doesn't corrupt us */
+ if (errno) {
+ sprintf (tmp,"Message append failed: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ ret = NIL;
+ }
+ if (ret) times.actime = time (0) - 1;
+ /* else preserved \Marked status */
+ else times.actime = (sbuf.st_ctime > sbuf.st_atime) ?
+ sbuf.st_atime : time (0);
+ times.modtime = sbuf.st_mtime;/* preserve mtime */
+ utime (file,&times); /* set the times */
+ fclose (df); /* close the file */
+ unlockfd (ld,lock); /* release exclusive parse/append permission */
+ mm_nocritical (stream); /* release critical */
+ if (ret && mail_parameters (NIL,GET_APPENDUID,NIL))
+ mm_log ("Can not return meaningful APPENDUID with this mailbox format",
+ WARN);
+ return ret;
+}
+
+/* Internal routines */
+
+
+/* Tenex mail return internal message size in bytes
+ * Accepts: MAIL stream
+ * message #
+ * Returns: internal size of message
+ */
+
+unsigned long tenex_size (MAILSTREAM *stream,unsigned long m)
+{
+ MESSAGECACHE *elt = mail_elt (stream,m);
+ return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->private.special.offset :
+ LOCAL->filesize) -
+ (elt->private.special.offset + elt->private.special.text.size);
+}
+
+/* Tenex mail parse mailbox
+ * Accepts: MAIL stream
+ * Returns: T if parse OK
+ * NIL if failure, stream aborted
+ */
+
+long tenex_parse (MAILSTREAM *stream)
+{
+ struct stat sbuf;
+ MESSAGECACHE *elt = NIL;
+ unsigned char c,*s,*t,*x;
+ char tmp[MAILTMPLEN];
+ unsigned long i,j;
+ long curpos = LOCAL->filesize;
+ long nmsgs = stream->nmsgs;
+ long recent = stream->recent;
+ short added = NIL;
+ short silent = stream->silent;
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ if (sbuf.st_size < curpos) { /* sanity check */
+ sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ stream->silent = T; /* don't pass up mm_exists() events yet */
+ while (sbuf.st_size - curpos){/* while there is stuff to parse */
+ /* get to that position in the file */
+ lseek (LOCAL->fd,curpos,L_SET);
+ if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
+ sprintf (tmp,"Unable to read internal header at %lu, size = %lu: %s",
+ (unsigned long) curpos,(unsigned long) sbuf.st_size,
+ i ? strerror (errno) : "no data read");
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ LOCAL->buf[i] = '\0'; /* tie off buffer just in case */
+ if (!(s = strchr (LOCAL->buf,'\012'))) {
+ sprintf (tmp,"Unable to find newline at %lu in %lu bytes, text: %s",
+ (unsigned long) curpos,i,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s = '\0'; /* tie off header line */
+ i = (s + 1) - LOCAL->buf; /* note start of text offset */
+ if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
+ sprintf (tmp,"Unable to parse internal header at %lu: %s",
+ (unsigned long) curpos,(char *) LOCAL->buf);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ *s++ = '\0'; *t++ = '\0'; /* tie off fields */
+
+ added = T; /* note that a new message was added */
+ /* swell the cache */
+ mail_exists (stream,++nmsgs);
+ /* instantiate an elt for this message */
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ elt->private.uid = ++stream->uid_last;
+ /* note file offset of header */
+ elt->private.special.offset = curpos;
+ /* in case error */
+ elt->private.special.text.size = 0;
+ /* header size not known yet */
+ elt->private.msg.header.text.size = 0;
+ x = s; /* parse the header components */
+ if (mail_parse_date (elt,LOCAL->buf) &&
+ (elt->private.msg.full.text.size = strtoul (s,(char **) &s,10)) &&
+ (!(s && *s)) && isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
+ isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
+ isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
+ isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])
+ elt->private.special.text.size = i;
+ else { /* oops */
+ sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
+ curpos,(char *) LOCAL->buf,(char *) x,(char *) t);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ /* make sure didn't run off end of file */
+ if ((curpos += (elt->private.msg.full.text.size + i)) > sbuf.st_size) {
+ sprintf (tmp,"Last message (at %lu) runs past end of file (%lu > %lu)",
+ elt->private.special.offset,(unsigned long) curpos,
+ (unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR);
+ tenex_close (stream,NIL);
+ return NIL;
+ }
+ c = t[10]; /* remember first system flags byte */
+ t[10] = '\0'; /* tie off flags */
+ j = strtoul (t,NIL,8); /* get user flags value */
+ t[10] = c; /* restore first system flags byte */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ /* calculate system flags */
+ if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
+ if (j & fDELETED) elt->deleted = T;
+ if (j & fFLAGGED) elt->flagged = T;
+ if (j & fANSWERED) elt->answered = T;
+ if (j & fDRAFT) elt->draft = T;
+ if (!(j & fOLD)) { /* newly arrived message? */
+ elt->recent = T;
+ recent++; /* count up a new recent message */
+ /* mark it as old */
+ tenex_update_status (stream,nmsgs,NIL);
+ }
+ }
+ fsync (LOCAL->fd); /* make sure all the fOLD flags take */
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ fstat (LOCAL->fd,&sbuf); /* get status again to ensure time is right */
+ LOCAL->filetime = sbuf.st_mtime;
+ if (added && !stream->rdonly){/* make sure atime updated */
+ struct utimbuf times;
+ times.actime = time (0);
+ times.modtime = LOCAL->filetime;
+ utime (stream->mailbox,&times);
+ }
+ stream->silent = silent; /* can pass up events now */
+ mail_exists (stream,nmsgs); /* notify upper level of new mailbox size */
+ mail_recent (stream,recent); /* and of change in recent messages */
+ return LONGT; /* return the winnage */
+}
+
+/* Tenex get cache element with status updating from file
+ * Accepts: MAIL stream
+ * message number
+ * Returns: cache element
+ */
+
+MESSAGECACHE *tenex_elt (MAILSTREAM *stream,unsigned long msgno)
+{
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ struct { /* old flags */
+ unsigned int seen : 1;
+ unsigned int deleted : 1;
+ unsigned int flagged : 1;
+ unsigned int answered : 1;
+ unsigned int draft : 1;
+ unsigned long user_flags;
+ } old;
+ old.seen = elt->seen; old.deleted = elt->deleted; old.flagged = elt->flagged;
+ old.answered = elt->answered; old.draft = elt->draft;
+ old.user_flags = elt->user_flags;
+ tenex_read_flags (stream,elt);
+ if ((old.seen != elt->seen) || (old.deleted != elt->deleted) ||
+ (old.flagged != elt->flagged) || (old.answered != elt->answered) ||
+ (old.draft != elt->draft) || (old.user_flags != elt->user_flags))
+ mm_flags (stream,msgno); /* let top level know */
+ return elt;
+}
+
+
+/* Tenex read flags from file
+ * Accepts: MAIL stream
+ * Returns: cache element
+ */
+
+void tenex_read_flags (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ unsigned long i,j;
+ /* noop if readonly and have valid flags */
+ if (stream->rdonly && elt->valid) return;
+ /* set the seek pointer */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* read the new flags */
+ if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
+ sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
+ fatal (LOCAL->buf);
+ }
+ /* calculate system flags */
+ i = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0');
+ elt->seen = i & fSEEN ? T : NIL; elt->deleted = i & fDELETED ? T : NIL;
+ elt->flagged = i & fFLAGGED ? T : NIL;
+ elt->answered = i & fANSWERED ? T : NIL; elt->draft = i & fDRAFT ? T : NIL;
+ LOCAL->buf[10] = '\0'; /* tie off flags */
+ j = strtoul(LOCAL->buf,NIL,8);/* get user flags value */
+ /* set up all valid user flags (reversed!) */
+ while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
+ stream->user_flags[i]) elt->user_flags |= 1 << i;
+ elt->valid = T; /* have valid flags now */
+}
+
+/* Tenex update status string
+ * Accepts: MAIL stream
+ * message number
+ * flag saying whether or not to sync
+ */
+
+void tenex_update_status (MAILSTREAM *stream,unsigned long msgno,long syncflag)
+{
+ struct utimbuf times;
+ struct stat sbuf;
+ MESSAGECACHE *elt = mail_elt (stream,msgno);
+ unsigned long j,k = 0;
+ /* readonly */
+ if (stream->rdonly || !elt->valid) tenex_read_flags (stream,elt);
+ else { /* readwrite */
+ j = elt->user_flags; /* get user flags */
+ /* reverse bits (dontcha wish we had CIRC?) */
+ while (j) k |= 1 << (29 - find_rightmost_bit (&j));
+ /* print new flag string */
+ sprintf (LOCAL->buf,"%010lo%02o",k,(unsigned)
+ (fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
+ (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered) +
+ (fDRAFT * elt->draft)));
+ while (T) { /* get to that place in the file */
+ lseek (LOCAL->fd,(off_t) elt->private.special.offset +
+ elt->private.special.text.size - 13,L_SET);
+ /* write new flags */
+ if (write (LOCAL->fd,LOCAL->buf,12) > 0) break;
+ mm_notify (stream,strerror (errno),WARN);
+ mm_diskerror (stream,errno,T);
+ }
+ if (syncflag) { /* sync if requested */
+ fsync (LOCAL->fd);
+ fstat (LOCAL->fd,&sbuf); /* get new write time */
+ times.modtime = LOCAL->filetime = sbuf.st_mtime;
+ times.actime = time (0); /* make sure read is later */
+ utime (stream->mailbox,&times);
+ }
+ }
+}
+
+/* Tenex locate header for a message
+ * Accepts: MAIL stream
+ * message number
+ * pointer to returned header size
+ * Returns: position of header in file
+ */
+
+unsigned long tenex_hdrpos (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *size)
+{
+ unsigned long siz;
+ long i = 0;
+ char c = '\0';
+ char *s = NIL;
+ MESSAGECACHE *elt = tenex_elt (stream,msgno);
+ unsigned long ret = elt->private.special.offset +
+ elt->private.special.text.size;
+ unsigned long msiz = tenex_size (stream,msgno);
+ /* is header size known? */
+ if (!(*size = elt->private.msg.header.text.size)) {
+ lseek (LOCAL->fd,ret,L_SET);/* get to header position */
+ /* search message for LF LF */
+ for (siz = 0; siz < msiz; siz++) {
+ if (--i <= 0) /* read another buffer as necessary */
+ read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
+ /* two newline sequence? */
+ if ((c == '\012') && (*s == '\012')) {
+ /* yes, note for later */
+ elt->private.msg.header.text.size = (*size = siz + 1);
+ return ret; /* return to caller */
+ }
+ else c = *s++; /* next character */
+ }
+ /* header consumes entire message */
+ elt->private.msg.header.text.size = *size = msiz;
+ }
+ return ret;
+}
diff --git a/imap/src/osdep/nt/unixnt.c b/imap/src/osdep/nt/unixnt.c
new file mode 100644
index 00000000..2e92d39a
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.c
@@ -0,0 +1,2297 @@
+/* ========================================================================
+ * 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: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * UW Technology
+ * University of Washington
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 27 March 2008
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include "mail.h"
+#include "osdep.h"
+#include <time.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/utime.h>
+#include "unixnt.h"
+#include "pseudo.h"
+#include "fdstring.h"
+#include "misc.h"
+#include "dummy.h"
+
+/* UNIX I/O stream local data */
+
+typedef struct unix_local {
+ unsigned int dirty : 1; /* disk copy needs updating */
+ unsigned int ddirty : 1; /* double-dirty, ping becomes checkpoint */
+ unsigned int pseudo : 1; /* uses a pseudo message */
+ unsigned int appending : 1; /* don't mark new messages as old */
+ int fd; /* mailbox file descriptor */
+ int ld; /* lock file descriptor */
+ char *lname; /* lock file name */
+ off_t filesize; /* file size parsed */
+ time_t filetime; /* last file time */
+ unsigned char *buf; /* temporary buffer */
+ unsigned long buflen; /* current size of temporary buffer */
+ unsigned long uid; /* current text uid */
+ SIZEDTEXT text; /* current text */
+ unsigned long textlen; /* current text length */
+ char *line; /* returned line */
+ char *linebuf; /* line readin buffer */
+ unsigned long linebuflen; /* current line readin buffer length */
+} UNIXLOCAL;
+
+
+/* Convenient access to local data */
+
+#define LOCAL ((UNIXLOCAL *) stream->local)
+
+
+/* UNIX protected file structure */
+
+typedef struct unix_file {
+ MAILSTREAM *stream; /* current stream */
+ off_t curpos; /* current file position */
+ off_t protect; /* protected position */
+ off_t filepos; /* current last written file position */
+ char *buf; /* overflow buffer */
+ size_t buflen; /* current overflow buffer length */
+ char *bufpos; /* current buffer position */
+} UNIXFILE;
+
+/* Function prototypes */
+
+DRIVER *unix_valid (char *name);
+void *unix_parameters (long function,void *value);
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents);
+void unix_list (MAILSTREAM *stream,char *ref,char *pat);
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat);
+long unix_create (MAILSTREAM *stream,char *mailbox);
+long unix_delete (MAILSTREAM *stream,char *mailbox);
+long unix_rename (MAILSTREAM *stream,char *old,char *newname);
+MAILSTREAM *unix_open (MAILSTREAM *stream);
+void unix_close (MAILSTREAM *stream,long options);
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags);
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags);
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags);
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt);
+long unix_ping (MAILSTREAM *stream);
+void unix_check (MAILSTREAM *stream);
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options);
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options);
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data);
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg);
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set);
+
+void unix_abort (MAILSTREAM *stream);
+char *unix_file (char *dst,char *name);
+int unix_lock (char *file,int flags,int mode,char *lock,int op);
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock);
+int unix_parse (MAILSTREAM *stream,char *lock,int op);
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size);
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr);
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag);
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags);
+long unix_extend (MAILSTREAM *stream,unsigned long size);
+void unix_write (UNIXFILE *f,char *s,unsigned long i);
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size);
+
+/* UNIX mail routines */
+
+
+/* Driver dispatch used by MAIL */
+
+DRIVER unixdriver = {
+ "unix", /* driver name */
+ /* driver flags */
+ DR_LOCAL|DR_MAIL|DR_NONEWMAILRONLY|DR_XPOINT,
+ (DRIVER *) NIL, /* next driver */
+ unix_valid, /* mailbox is valid for us */
+ unix_parameters, /* manipulate parameters */
+ unix_scan, /* scan mailboxes */
+ unix_list, /* list mailboxes */
+ unix_lsub, /* list subscribed mailboxes */
+ NIL, /* subscribe to mailbox */
+ NIL, /* unsubscribe from mailbox */
+ unix_create, /* create mailbox */
+ unix_delete, /* delete mailbox */
+ unix_rename, /* rename mailbox */
+ mail_status_default, /* status of mailbox */
+ unix_open, /* open mailbox */
+ unix_close, /* close mailbox */
+ NIL, /* fetch message "fast" attributes */
+ NIL, /* fetch message flags */
+ NIL, /* fetch overview */
+ NIL, /* fetch message envelopes */
+ unix_header, /* fetch message header */
+ unix_text, /* fetch message text */
+ NIL, /* fetch partial message text */
+ NIL, /* unique identifier */
+ NIL, /* message number */
+ NIL, /* modify flags */
+ unix_flagmsg, /* per-message modify flags */
+ NIL, /* search for message based on criteria */
+ NIL, /* sort messages */
+ NIL, /* thread messages */
+ unix_ping, /* ping mailbox to see if still alive */
+ unix_check, /* check for new messages */
+ unix_expunge, /* expunge deleted messages */
+ unix_copy, /* copy messages to another mailbox */
+ unix_append, /* append string message to mailbox */
+ NIL /* garbage collect stream */
+};
+
+ /* prototype stream */
+MAILSTREAM unixproto = {&unixdriver};
+
+ /* driver parameters */
+static long unix_fromwidget = T;
+
+/* UNIX mail validate mailbox
+ * Accepts: mailbox name
+ * Returns: our driver if name is valid, NIL otherwise
+ */
+
+DRIVER *unix_valid (char *name)
+{
+ int fd;
+ DRIVER *ret = NIL;
+ int c,r;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],*s,*t;
+ struct stat sbuf;
+ struct utimbuf times;
+ errno = EINVAL; /* assume invalid argument */
+ /* must be non-empty file */
+ if ((t = dummy_file (file,name)) && !stat (t,&sbuf) &&
+ ((sbuf.st_mode & S_IFMT) == S_IFREG)) {
+ if (!sbuf.st_size)errno = 0;/* empty file */
+ else if ((fd = open (file,O_BINARY|O_RDONLY,NIL)) >= 0) {
+ memset (tmp,'\0',MAILTMPLEN);
+ if (read (fd,tmp,MAILTMPLEN-1) <= 0) errno = -1;
+ else { /* ignore leading whitespace */
+ for (s = tmp,c = '\n';
+ (*s == '\r') || (*s == '\n') || (*s == ' ') || (*s == '\t');
+ c = *s++);
+ if (c == '\n') { /* at start of a line? */
+ VALID (s,t,r,c); /* yes, validate format */
+ if (r) ret = &unixdriver;
+ else errno = -1; /* invalid format */
+ }
+ }
+ close (fd); /* close the file */
+ /* \Marked status? */
+ if ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) {
+ /* yes, preserve atime and mtime */
+ times.actime = sbuf.st_atime;
+ times.modtime = sbuf.st_mtime;
+ utime (file,&times); /* set the times */
+ }
+ }
+ }
+ return ret; /* return what we should */
+}
+/* UNIX manipulate driver parameters
+ * Accepts: function code
+ * function-dependent value
+ * Returns: function-dependent return value
+ */
+
+void *unix_parameters (long function,void *value)
+{
+ void *ret = NIL;
+ switch ((int) function) {
+ case SET_FROMWIDGET:
+ unix_fromwidget = (long) value;
+ case GET_FROMWIDGET:
+ ret = (void *) unix_fromwidget;
+ break;
+ }
+ return ret;
+}
+
+/* UNIX mail scan mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ * string to scan
+ */
+
+void unix_scan (MAILSTREAM *stream,char *ref,char *pat,char *contents)
+{
+ if (stream) dummy_scan (NIL,ref,pat,contents);
+}
+
+
+/* UNIX mail list mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_list (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_list (NIL,ref,pat);
+}
+
+
+/* UNIX mail list subscribed mailboxes
+ * Accepts: mail stream
+ * reference
+ * pattern to search
+ */
+
+void unix_lsub (MAILSTREAM *stream,char *ref,char *pat)
+{
+ if (stream) dummy_lsub (NIL,ref,pat);
+}
+
+/* UNIX mail create mailbox
+ * Accepts: MAIL stream
+ * mailbox name to create
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_create (MAILSTREAM *stream,char *mailbox)
+{
+ char *s,mbx[MAILTMPLEN],tmp[MAILTMPLEN];
+ long ret = NIL;
+ int fd;
+ time_t ti = time (0);
+ if (!(s = dummy_file (mbx,mailbox))) {
+ sprintf (tmp,"Can't create %.80s: invalid name",mailbox);
+ mm_log (tmp,ERROR);
+ }
+ /* create underlying file */
+ else if (dummy_create_path (stream,s,NIL)) {
+ if ((s = strrchr (s,'\\')) && !s[1]) ret = T;
+ if ((fd = open (mbx,O_WRONLY|O_BINARY,NIL)) < 0) {
+ sprintf (tmp,"Can't reopen mailbox node %.80s: %s",mbx,strerror (errno));
+ mm_log (tmp,ERROR);
+ unlink (mbx); /* delete the file */
+ }
+ else { /* initialize header */
+ memset (tmp,'\0',MAILTMPLEN);
+ sprintf (tmp,"From %s %s",pseudo_from,ctime (&ti));
+ if (s = strpbrk (tmp,"\r\n")) *s = '\0';
+ strcat (tmp,"\r\nDate: ");
+ rfc822_fixed_date (s = tmp + strlen (tmp));
+ sprintf (s += strlen (s), /* write the pseudo-header */
+ "\r\nFrom: %s <%s@%s>\r\nSubject: %s\r\nX-IMAP: %010lu 0000000000\r\nStatus: RO\r\n\r\n%s\r\n\r\n",
+ pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) ti,pseudo_msg);
+ if (write (fd,tmp,strlen (tmp)) > 0) {
+ close (fd); /* close file */
+ ret = T;
+ }
+ else {
+ sprintf (tmp,"Can't initialize mailbox node %.80s: %s",mbx,
+ strerror (errno));
+ mm_log (tmp,ERROR);
+ close (fd); /* close file before unlinking */
+ unlink (mbx); /* delete the file */
+ }
+ }
+ }
+ return ret;
+}
+
+/* UNIX mail delete mailbox
+ * Accepts: MAIL stream
+ * mailbox name to delete
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_delete (MAILSTREAM *stream,char *mailbox)
+{
+ return unix_rename (stream,mailbox,NIL);
+}
+
+
+/* UNIX mail rename mailbox
+ * Accepts: MAIL stream
+ * old mailbox name
+ * new mailbox name (or NIL for delete)
+ * Returns: T on success, NIL on failure
+ */
+
+long unix_rename (MAILSTREAM *stream,char *old,char *newname)
+{
+ long ret = NIL;
+ char c,*s = NIL;
+ char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN],lockx[MAILTMPLEN];
+ int fd,ld;
+ struct stat sbuf;
+ mm_critical (stream); /* get the c-client lock */
+ if (!dummy_file (file,old) ||
+ (newname && (!(s = dummy_file (tmp,newname)) ||
+ ((s = strrchr (s,'\\')) && !s[1]))))
+ sprintf (tmp,newname ?
+ "Can't rename mailbox %.80s to %.80s: invalid name" :
+ "Can't delete mailbox %.80s: invalid name",
+ old,newname);
+ else if ((ld = lockname (lock,file,NIL)) < 0)
+ sprintf (tmp,"Can't get lock for mailbox %.80s",old);
+
+ else { /* lock out other c-clients */
+ if (flock (ld,LOCK_EX|LOCK_NB)) {
+ close (ld); /* couldn't lock, give up on it then */
+ sprintf (tmp,"Mailbox %.80s is in use by another process",old);
+ }
+ /* lock out non c-client applications */
+ else if ((fd = unix_lock (file,O_BINARY|O_RDWR,S_IREAD|S_IWRITE,lockx,
+ LOCK_EX)) < 0)
+ sprintf (tmp,"Can't lock mailbox %.80s: %s",old,strerror (errno));
+ else {
+ unix_unlock(fd,NIL,lockx);/* pacify evil NTFS */
+ if (newname) { /* want rename? */
+ /* found superior to destination name? */
+ if ((s = strrchr (tmp,'\\')) && (s != tmp) &&
+ ((tmp[1] != ':') || (s != tmp + 2))) {
+ c = s[1]; /* remember character after delimiter */
+ *s = s[1] = '\0'; /* tie off name at delimiter */
+ /* name doesn't exist, create it */
+ if (stat (tmp,&sbuf) || ((sbuf.st_mode & S_IFMT) != S_IFDIR)) {
+ *s = '\\'; /* restore delimiter */
+ if (!dummy_create (stream,newname)) {
+ flock (ld,LOCK_UN);
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ mm_nocritical (stream);
+ return NIL; /* couldn't create superior */
+ }
+ }
+ else *s = '\\'; /* restore delimiter */
+ s[1] = c; /* restore character after delimiter */
+ }
+ if (rename (file,tmp))
+ sprintf (tmp,"Can't rename mailbox %.80s to %.80s: %s",old,newname,
+ strerror (errno));
+ else ret = T; /* set success */
+ }
+ else if (unlink (file)) /* want delete */
+ sprintf (tmp,"Can't delete mailbox %.80s: %s",old,strerror (errno));
+ else ret = T; /* set success */
+ flock (ld,LOCK_UN); /* release c-client lock */
+ close (ld); /* close c-client lock */
+ unlink (lock); /* and delete it */
+ }
+ }
+ mm_nocritical (stream); /* no longer critical */
+ if (!ret) mm_log (tmp,ERROR); /* log error */
+ return ret; /* return success or failure */
+}
+
+/* UNIX mail open
+ * Accepts: Stream to open
+ * Returns: Stream on success, NIL on failure
+ */
+
+MAILSTREAM *unix_open (MAILSTREAM *stream)
+{
+ int fd;
+ char tmp[MAILTMPLEN];
+ /* return prototype for OP_PROTOTYPE call */
+ if (!stream) return &unixproto;
+ if (stream->local) fatal ("unix recycle stream");
+ stream->local = memset (fs_get (sizeof (UNIXLOCAL)),0,sizeof (UNIXLOCAL));
+ /* note if an INBOX or not */
+ stream->inbox = !compare_cstring (stream->mailbox,"INBOX");
+ /* canonicalize the stream mailbox name */
+ if (!dummy_file (tmp,stream->mailbox)) {
+ sprintf (tmp,"Can't open - invalid name: %.80s",stream->mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* flush old name */
+ fs_give ((void **) &stream->mailbox);
+ /* save canonical name */
+ stream->mailbox = cpystr (tmp);
+ LOCAL->fd = LOCAL->ld = -1; /* no file or state locking yet */
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = CHUNKSIZE) + 1);
+ LOCAL->text.data = (unsigned char *) fs_get (CHUNKSIZE);
+ LOCAL->text.size = CHUNKSIZE - 1;
+ LOCAL->linebuf = (char *) fs_get (CHUNKSIZE);
+ LOCAL->linebuflen = CHUNKSIZE - 1;
+ stream->sequence++; /* bump sequence number */
+ if (!stream->rdonly) { /* make lock for read/write access */
+ if ((fd = lockname (tmp,stream->mailbox,NIL)) < 0)
+ mm_log ("Can't open mailbox lock, access is readonly",WARN);
+ /* can get the lock? */
+ else if (flock (fd,LOCK_EX|LOCK_NB)) {
+ if (!stream->silent)
+ mm_log ("Mailbox is open by another process, access is readonly",WARN);
+ close (fd);
+ }
+ else { /* got the lock, nobody else can alter state */
+ LOCAL->ld = fd; /* note lock's fd and name */
+ LOCAL->lname = cpystr (tmp);
+ }
+ }
+
+ /* parse mailbox */
+ stream->nmsgs = stream->recent = 0;
+ /* will we be able to get write access? */
+ if ((LOCAL->ld >= 0) && access (stream->mailbox,02) && (errno == EACCES)) {
+ mm_log ("Can't get write access to mailbox, access is readonly",WARN);
+ flock (LOCAL->ld,LOCK_UN); /* release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ LOCAL->ld = -1; /* no more lock fd */
+ unlink (LOCAL->lname); /* delete it */
+ }
+ /* reset UID validity */
+ stream->uid_validity = stream->uid_last = 0;
+ if (stream->silent && !stream->rdonly && (LOCAL->ld < 0))
+ unix_abort (stream); /* abort if can't get RW silent stream */
+ /* parse mailbox */
+ else if (unix_parse (stream,tmp,LOCK_SH)) {
+ unix_unlock (LOCAL->fd,stream,tmp);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ }
+ if (!LOCAL) return NIL; /* failure if stream died */
+ /* make sure upper level knows readonly */
+ stream->rdonly = (LOCAL->ld < 0);
+ /* notify about empty mailbox */
+ if (!(stream->nmsgs || stream->silent)) mm_log ("Mailbox is empty",NIL);
+ if (!stream->rdonly) { /* flags stick if readwrite */
+ stream->perm_seen = stream->perm_deleted =
+ stream->perm_flagged = stream->perm_answered = stream->perm_draft = T;
+ /* have permanent keywords */
+ stream->perm_user_flags = 0xffffffff;
+ /* and maybe can create them too */
+ stream->kwd_create = stream->user_flags[NUSERFLAGS-1] ? NIL : T;
+ }
+ return stream; /* return stream alive to caller */
+}
+
+
+/* UNIX mail close
+ * Accepts: MAIL stream
+ * close options
+ */
+
+void unix_close (MAILSTREAM *stream,long options)
+{
+ int silent = stream->silent;
+ stream->silent = T; /* go silent */
+ /* expunge if requested */
+ if (options & CL_EXPUNGE) unix_expunge (stream,NIL,NIL);
+ /* else dump final checkpoint */
+ else if (LOCAL->dirty) unix_check (stream);
+ stream->silent = silent; /* restore old silence state */
+ unix_abort (stream); /* now punt the file and local data */
+}
+
+/* UNIX mail fetch message header
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned header text length
+ * option flags
+ * Returns: message header in RFC822 format
+ */
+
+ /* lines to filter from header */
+static STRINGLIST *unix_hlines = NIL;
+
+char *unix_header (MAILSTREAM *stream,unsigned long msgno,
+ unsigned long *length,long flags)
+{
+ MESSAGECACHE *elt;
+ unsigned char *s;
+ *length = 0; /* default to empty */
+ if (flags & FT_UID) return "";/* UID call "impossible" */
+ elt = mail_elt (stream,msgno);/* get cache */
+ if (!unix_hlines) { /* once only code */
+ STRINGLIST *lines = unix_hlines = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Status"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-Keywords"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-UID"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAP"));
+ lines = lines->next = mail_newstringlist ();
+ lines->text.size = strlen ((char *) (lines->text.data =
+ (unsigned char *) "X-IMAPbase"));
+ }
+ /* go to header position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.header.offset,L_SET);
+
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.header.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.header.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.header.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.header.text.size] = '\0';
+ }
+ else { /* need to make a CRLF version */
+ read (LOCAL->fd,s = (char *) fs_get (elt->private.msg.header.text.size+1),
+ elt->private.msg.header.text.size);
+ /* tie off string, and convert to CRLF */
+ s[elt->private.msg.header.text.size] = '\0';
+ *length = unix_crlfcpy (&LOCAL->buf,&LOCAL->buflen,s,
+ elt->private.msg.header.text.size);
+ fs_give ((void **) &s); /* free readin buffer */
+ }
+ *length = mail_filter (LOCAL->buf,*length,unix_hlines,FT_NOT);
+ return LOCAL->buf; /* return processed copy */
+}
+
+/* UNIX mail fetch message text
+ * Accepts: MAIL stream
+ * message # to fetch
+ * pointer to returned stringstruct
+ * option flags
+ * Returns: T on success, NIL if failure
+ */
+
+long unix_text (MAILSTREAM *stream,unsigned long msgno,STRING *bs,long flags)
+{
+ char *s;
+ unsigned long i;
+ MESSAGECACHE *elt;
+ /* UID call "impossible" */
+ if (flags & FT_UID) return NIL;
+ elt = mail_elt (stream,msgno);/* get cache element */
+ /* if message not seen */
+ if (!(flags & FT_PEEK) && !elt->seen) {
+ /* mark message seen and dirty */
+ elt->seen = elt->private.dirty = LOCAL->dirty = T;
+ mm_flags (stream,msgno);
+ }
+ s = unix_text_work (stream,elt,&i,flags);
+ INIT (bs,mail_string,s,i); /* set up stringstruct */
+ return T; /* success */
+}
+
+/* UNIX mail fetch message text worker routine
+ * Accepts: MAIL stream
+ * message cache element
+ * pointer to returned header text length
+ * option flags
+ */
+
+char *unix_text_work (MAILSTREAM *stream,MESSAGECACHE *elt,
+ unsigned long *length,long flags)
+{
+ FDDATA d;
+ STRING bs;
+ unsigned char c,*s,tmp[CHUNKSIZE];
+ /* go to text position */
+ lseek (LOCAL->fd,elt->private.special.offset +
+ elt->private.msg.text.offset,L_SET);
+ if (flags & FT_INTERNAL) { /* initial data OK? */
+ if (elt->private.msg.text.text.size > LOCAL->buflen) {
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen =
+ elt->private.msg.text.text.size) + 1);
+ }
+ /* read message */
+ read (LOCAL->fd,LOCAL->buf,elt->private.msg.text.text.size);
+ /* got text, tie off string */
+ LOCAL->buf[*length = elt->private.msg.text.text.size] = '\0';
+ return LOCAL->buf;
+ }
+ /* have it cached already? */
+ if (elt->private.uid != LOCAL->uid) {
+ /* not cached, cache it now */
+ LOCAL->uid = elt->private.uid;
+ /* is buffer big enough? */
+ if (elt->rfc822_size > LOCAL->text.size) {
+ /* excessively conservative, but the right thing is too hard to do */
+ fs_give ((void **) &LOCAL->text.data);
+ LOCAL->text.data = (unsigned char *)
+ fs_get ((LOCAL->text.size = elt->rfc822_size) + 1);
+ }
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = elt->private.special.offset + elt->private.msg.text.offset;
+ d.chunk = tmp; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,elt->private.msg.text.text.size);
+ for (s = (char *) LOCAL->text.data; SIZE (&bs);) switch (c = SNX (&bs)) {
+ case '\r': /* carriage return seen */
+ *s++ = c; /* copy it and any succeeding LF */
+ if (SIZE (&bs) && (CHR (&bs) == '\n')) *s++ = SNX (&bs);
+ break;
+ case '\n':
+ *s++ = '\r'; /* insert a CR */
+ default:
+ *s++ = c; /* copy characters */
+ }
+ *s = '\0'; /* tie off buffer */
+ /* calculate length of cached data */
+ LOCAL->textlen = s - LOCAL->text.data;
+ }
+ *length = LOCAL->textlen; /* return from cache */
+ return (char *) LOCAL->text.data;
+}
+
+/* UNIX per-message modify flag
+ * Accepts: MAIL stream
+ * message cache element
+ */
+
+void unix_flagmsg (MAILSTREAM *stream,MESSAGECACHE *elt)
+{
+ /* only after finishing */
+ if (elt->valid) elt->private.dirty = LOCAL->dirty = T;
+}
+
+
+/* UNIX mail ping mailbox
+ * Accepts: MAIL stream
+ * Returns: T if stream alive, else NIL
+ */
+
+long unix_ping (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ struct stat sbuf;
+ /* big no-op if not readwrite */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock) {
+ if (stream->rdonly) { /* does he want to give up readwrite? */
+ /* checkpoint if we changed something */
+ if (LOCAL->dirty) unix_check (stream);
+ flock (LOCAL->ld,LOCK_UN);/* release readwrite lock */
+ close (LOCAL->ld); /* close the readwrite lock file */
+ LOCAL->ld = -1; /* no more readwrite lock fd */
+ unlink (LOCAL->lname); /* delete the readwrite lock file */
+ }
+ else { /* get current mailbox size */
+ if (LOCAL->fd >= 0) fstat (LOCAL->fd,&sbuf);
+ else if (stat (stream->mailbox,&sbuf)) {
+ sprintf (LOCAL->buf,"Mailbox stat failed, aborted: %s",
+ strerror (errno));
+ MM_LOG (LOCAL->buf,ERROR);
+ unix_abort (stream);
+ return NIL;
+ }
+ /* parse if mailbox changed */
+ if ((LOCAL->ddirty || (sbuf.st_size != LOCAL->filesize)) &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* force checkpoint if double-dirty */
+ if (LOCAL->ddirty) unix_rewrite (stream,NIL,lock,NIL);
+ /* unlock mailbox */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* and stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+ }
+ }
+ return LOCAL ? LONGT : NIL; /* return if still alive */
+}
+
+/* UNIX mail check mailbox
+ * Accepts: MAIL stream
+ */
+
+void unix_check (MAILSTREAM *stream)
+{
+ char lock[MAILTMPLEN];
+ /* parse and lock mailbox */
+ if (LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* any unsaved changes? */
+ if (LOCAL->dirty && unix_rewrite (stream,NIL,lock,NIL)) {
+ if (!stream->silent) mm_log ("Checkpoint completed",NIL);
+ }
+ /* no checkpoint needed, just unlock */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ }
+}
+
+
+/* UNIX mail expunge mailbox
+ * Accepts: MAIL stream
+ * sequence to expunge if non-NIL
+ * expunge options
+ * Returns: T, always
+ */
+
+long unix_expunge (MAILSTREAM *stream,char *sequence,long options)
+{
+ long ret;
+ unsigned long i;
+ char lock[MAILTMPLEN];
+ char *msg = NIL;
+ /* parse and lock mailbox */
+ if (ret = (sequence ? ((options & EX_UID) ?
+ mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence)) : LONGT) &&
+ LOCAL && (LOCAL->ld >= 0) && !stream->lock &&
+ unix_parse (stream,lock,LOCK_EX)) {
+ /* check expunged messages if not dirty */
+ for (i = 1; !LOCAL->dirty && (i <= stream->nmsgs); i++) {
+ MESSAGECACHE *elt = mail_elt (stream,i);
+ if (mail_elt (stream,i)->deleted) LOCAL->dirty = T;
+ }
+ if (!LOCAL->dirty) { /* not dirty and no expunged messages */
+ unix_unlock (LOCAL->fd,stream,lock);
+ msg = "No messages deleted, so no update needed";
+ }
+ else if (unix_rewrite (stream,&i,lock,sequence ? LONGT : NIL)) {
+ if (i) sprintf (msg = LOCAL->buf,"Expunged %lu messages",i);
+ else msg = "Mailbox checkpointed, but no messages expunged";
+ }
+ /* rewrite failed */
+ else unix_unlock (LOCAL->fd,stream,lock);
+ mail_unlock (stream); /* unlock the stream */
+ mm_nocritical (stream); /* done with critical */
+ if (msg && !stream->silent) mm_log (msg,NIL);
+ }
+ else if (!stream->silent) mm_log("Expunge ignored on readonly mailbox",WARN);
+ return ret;
+}
+
+/* UNIX mail copy message(s)
+ * Accepts: MAIL stream
+ * sequence
+ * destination mailbox
+ * copy options
+ * Returns: T if copy successful, else NIL
+ */
+
+long unix_copy (MAILSTREAM *stream,char *sequence,char *mailbox,long options)
+{
+ struct stat sbuf;
+ int fd;
+ char *s,file[MAILTMPLEN],lock[MAILTMPLEN];
+ struct utimbuf times;
+ unsigned long i,j;
+ MESSAGECACHE *elt;
+ long ret = T;
+ mailproxycopy_t pc =
+ (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
+ copyuid_t cu = (copyuid_t) (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ?
+ NIL : mail_parameters (NIL,GET_COPYUID,NIL));
+ SEARCHSET *source = cu ? mail_newsearchset () : NIL;
+ SEARCHSET *dest = cu ? mail_newsearchset () : NIL;
+ MAILSTREAM *tstream = NIL;
+ if (!((options & CP_UID) ? mail_uid_sequence (stream,sequence) :
+ mail_sequence (stream,sequence))) return NIL;
+ /* make sure destination is valid */
+ if (!(unix_valid (mailbox) || !errno))
+ switch (errno) {
+ case ENOENT: /* no such file? */
+ if (compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
+ return NIL;
+ }
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ unix_create (NIL,"INBOX");/* create empty INBOX */
+ case EACCES: /* file protected */
+ sprintf (LOCAL->buf,"Can't access destination: %.80s",mailbox);
+ MM_LOG (LOCAL->buf,ERROR);
+ return NIL;
+ case EINVAL:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ default:
+ if (pc) return (*pc) (stream,sequence,mailbox,options);
+ sprintf (LOCAL->buf,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+
+ /* try to open rewrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (cu && !tstream) { /* wanted a COPYUID? */
+ sprintf (LOCAL->buf,"Unable to write-open mailbox for COPYUID: %.80s",
+ mailbox);
+ MM_LOG (LOCAL->buf,WARN);
+ cu = NIL; /* don't try to do COPYUID */
+ }
+ LOCAL->buf[0] = '\0';
+ mm_critical (stream); /* go critical */
+ if ((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (LOCAL->buf,"Can't open destination mailbox: %s",strerror (errno));
+ mm_log (LOCAL->buf,ERROR); /* log the error */
+ return NIL; /* failed */
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ /* write all requested messages to mailbox */
+ for (i = 1; ret && (i <= stream->nmsgs); i++)
+ if ((elt = mail_elt (stream,i))->sequence) {
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ if (LOCAL->buf[(j = elt->private.special.text.size) - 2] != '\r') {
+ LOCAL->buf[j - 1] = '\r';
+ LOCAL->buf[j++] = '\n';
+ }
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* internal header succeeded */
+ s = unix_header (stream,i,&j,NIL);
+ /* header size, sans trailing newline */
+ if (j && (s[j - 4] == '\r')) j -= 2;
+ if (write (fd,s,j) < 0) ret = NIL;
+ else { /* message header succeeded */
+ j = tstream ? /* write UIDPLUS data if have readwrite */
+ unix_xstatus (stream,LOCAL->buf,elt,++(tstream->uid_last),LONGT) :
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,NIL);
+ if (write (fd,LOCAL->buf,j) < 0) ret = NIL;
+ else { /* message status succeeded */
+ s = unix_text_work (stream,elt,&j,NIL);
+ if ((write (fd,s,j) < 0) || (write (fd,"\r\n",2) < 0))
+ ret = NIL;
+ else if (cu) { /* need to pass back new UID? */
+ mail_append_set (source,mail_uid (stream,i));
+ mail_append_set (dest,tstream->uid_last);
+ }
+ }
+ }
+ }
+ }
+
+ if (!ret || fsync (fd)) { /* force out the update */
+ sprintf (LOCAL->buf,"Message copy failed: %s",strerror (errno));
+ ftruncate (fd,sbuf.st_size);
+ ret = NIL;
+ }
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing COPYUID */
+ if (cu && ret) (*cu) (stream,mailbox,tstream->uid_validity,source,dest);
+ else { /* flush any sets we may have built */
+ mail_free_searchset (&source);
+ mail_free_searchset (&dest);
+ }
+ times.modtime = time (0); /* set mtime to now */
+ /* set atime to now-1 if successful copy */
+ if (ret) times.actime = times.modtime - 1;
+
+ else times.actime = /* else preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ utime (file,&times); /* set the times */
+ unix_unlock (fd,NIL,lock); /* unlock and close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ /* log the error */
+ if (!ret) mm_log (LOCAL->buf,ERROR);
+ /* delete if requested message */
+ else if (options & CP_MOVE) for (i = 1; i <= stream->nmsgs; i++)
+ if ((elt = mail_elt (stream,i))->sequence)
+ elt->deleted = elt->private.dirty = LOCAL->dirty = T;
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* UNIX mail append message from stringstruct
+ * Accepts: MAIL stream
+ * destination mailbox
+ * append callback
+ * data for callback
+ * Returns: T if append successful, else NIL
+ */
+
+#define BUFLEN 8*MAILTMPLEN
+
+long unix_append (MAILSTREAM *stream,char *mailbox,append_t af,void *data)
+{
+ struct stat sbuf;
+ int fd;
+ unsigned long i;
+ char *flags,*date,buf[BUFLEN],tmp[MAILTMPLEN],file[MAILTMPLEN],
+ lock[MAILTMPLEN];
+ struct utimbuf times;
+ FILE *sf,*df;
+ MESSAGECACHE elt;
+ STRING *message;
+ unsigned long uidlocation = 0;
+ appenduid_t au = (appenduid_t)
+ (mail_parameters (NIL,GET_USERHASNOLIFE,NIL) ? NIL :
+ mail_parameters (NIL,GET_APPENDUID,NIL));
+ SEARCHSET *dst = au ? mail_newsearchset () : NIL;
+ long ret = LONGT;
+ MAILSTREAM *tstream = NIL;
+ if (!stream) { /* stream specified? */
+ stream = &unixproto; /* no, default stream to prototype */
+ for (i = 0; i < NUSERFLAGS && stream->user_flags[i]; ++i)
+ fs_give ((void **) &stream->user_flags[i]);
+ }
+ if (!unix_valid (mailbox)) switch (errno) {
+ case ENOENT: /* no such file? */
+ if (!compare_cstring (mailbox,"INBOX")) {
+ mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
+ return NIL;
+ }
+ unix_create (NIL,"INBOX"); /* create empty INBOX */
+ case 0: /* merely empty file? */
+ tstream = stream;
+ break;
+ case EACCES: /* file protected */
+ sprintf (tmp,"Can't access destination: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ case EINVAL:
+ sprintf (tmp,"Invalid UNIX-format mailbox name: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ default:
+ sprintf (tmp,"Not a UNIX-format mailbox: %.80s",mailbox);
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ /* get sniffing stream for keywords */
+ else if (!(tstream = mail_open (NIL,mailbox,
+ OP_READONLY|OP_SILENT|OP_NOKOD|OP_SNIFF))) {
+ sprintf (tmp,"Unable to examine mailbox for APPEND: %.80s",mailbox);
+ MM_LOG (tmp,ERROR);
+ return NIL;
+ }
+
+ /* get first message */
+ if (!(*af) (tstream,data,&flags,&date,&message)) return NIL;
+ if (!(sf = tmpfile ())) { /* must have scratch file */
+ sprintf (tmp,".%lx.%lx",(unsigned long) time (0),(unsigned long)getpid ());
+ if (!stat (tmp,&sbuf) || !(sf = fopen (tmp,"wb+"))) {
+ sprintf (tmp,"Unable to create scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ unlink (tmp);
+ }
+ do { /* parse date */
+ if (!date) rfc822_date (date = tmp);
+ if (!mail_parse_date (&elt,date)) {
+ sprintf (tmp,"Bad date in append: %.80s",date);
+ mm_log (tmp,ERROR);
+ }
+ else { /* user wants to suppress time zones? */
+ if (mail_parameters (NIL,GET_NOTIMEZONES,NIL)) {
+ time_t when = mail_longdate (&elt);
+ date = ctime (&when); /* use traditional date */
+ }
+ /* use POSIX-style date */
+ else date = mail_cdate (tmp,&elt);
+ if (!SIZE (message)) mm_log ("Append of zero-length message",ERROR);
+ else if (!unix_collect_msg (tstream,sf,flags,date,message)) {
+ sprintf (tmp,"Error writing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ }
+ /* get next message */
+ else if ((*af) (tstream,data,&flags,&date,&message)) continue;
+ }
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ } while (message); /* until no more messages */
+ if (fflush (sf)) {
+ sprintf (tmp,"Error finishing scratch file: %.80s",strerror (errno));
+ mm_log (tmp,ERROR);
+ fclose (sf); /* punt scratch file */
+ return NIL; /* give up */
+ }
+ i = ftell (sf); /* size of scratch file */
+
+ /* close sniffing stream */
+ if (tstream != stream) tstream = mail_close (tstream);
+ mm_critical (stream); /* go critical */
+ /* try to open readwrite for UIDPLUS */
+ if ((tstream = mail_open_work (&unixdriver,NIL,mailbox,
+ OP_SILENT|OP_NOKOD)) && tstream->rdonly)
+ tstream = mail_close (tstream);
+ if (au && !tstream) { /* wanted an APPENDUID? */
+ sprintf (tmp,"Unable to re-open mailbox for APPENDUID: %.80s",mailbox);
+ MM_LOG (tmp,WARN);
+ au = NIL;
+ }
+ if (((fd = unix_lock (dummy_file (file,mailbox),
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IREAD|S_IWRITE,
+ lock,LOCK_EX)) < 0) || !(df = fdopen (fd,"ab"))) {
+ mm_nocritical (stream); /* done with critical */
+ sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ return NIL;
+ }
+ fstat (fd,&sbuf); /* get current file size */
+ rewind (sf);
+ times.modtime = time (0); /* set mtime to now */
+ /* write all messages */
+ if (!unix_append_msgs (tstream,sf,df,au ? dst : NIL) ||
+ (fflush (df) == EOF) || fsync (fd)) {
+ sprintf (buf,"Message append failed: %s",strerror (errno));
+ mm_log (buf,ERROR);
+ ftruncate (fd,sbuf.st_size);/* revert file */
+ times.actime = /* preserve \Marked status */
+ ((sbuf.st_ctime > sbuf.st_atime) || (sbuf.st_mtime > sbuf.st_atime)) ?
+ sbuf.st_atime : times.modtime;
+ ret = NIL; /* return error */
+ }
+ /* set atime to now-1 if successful copy */
+ else times.actime = times.modtime - 1;
+ utime (file,&times); /* set the times */
+ fclose (sf); /* done with scratch file */
+ /* force UIDVALIDITY assignment now */
+ if (tstream && !tstream->uid_validity)
+ tstream->uid_validity = (unsigned long) time (0);
+ /* return sets if doing APPENDUID */
+ if (au && ret) (*au) (mailbox,tstream->uid_validity,dst);
+ else mail_free_searchset (&dst);
+ flock (fd,LOCK_UN); /* unlock mailbox (can't use unix_unlock() */
+ if (lock && *lock) unlink (lock);
+ fclose (df); /* close mailbox */
+ if (tstream) { /* update last UID if we can */
+ UNIXLOCAL * local = (UNIXLOCAL *) tstream->local;
+ local->dirty = T; /* do a rewrite */
+ local->appending = T; /* but not at the cost of marking as old */
+ tstream = mail_close (tstream);
+ }
+ mm_nocritical (stream); /* release critical */
+ return ret;
+}
+
+/* Collect and write single message to append scratch file
+ * Accepts: MAIL stream
+ * scratch file
+ * flags
+ * date
+ * message stringstruct
+ * Returns: NIL if write error, else T
+ */
+
+int unix_collect_msg (MAILSTREAM *stream,FILE *sf,char *flags,char *date,
+ STRING *msg)
+{
+ unsigned char *s,*t;
+ unsigned long uf;
+ long f = mail_parse_flags (stream,flags,&uf);
+ /* write metadata */
+ if (fprintf (sf,"%ld %lu ",f,SIZE (msg) + 2) < 0) return NIL;
+ for (s = date; *s; *s++) switch (*s) {
+ default:
+ if (putc (*s,sf) == EOF) return NIL;
+ case '\r': case '\n':
+ break;
+ }
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (uf) /* write user flags */
+ if ((s = stream->user_flags[find_rightmost_bit (&uf)]) &&
+ (fprintf (sf," %s",s) < 0)) return NIL;
+ if (fputs ("\r\n",sf) == EOF) return NIL;
+ while (SIZE (msg)) { /* copy text to scratch file */
+ for (s = (unsigned char *) msg->curpos, t = s + msg->cursize; s < t; ++s)
+ if (!*s) *s = 0x80; /* disallow NUL */
+ /* write buffered text */
+ if (fwrite (msg->curpos,1,msg->cursize,sf) == msg->cursize)
+ SETPOS (msg,GETPOS (msg) + msg->cursize);
+ else return NIL; /* failed */
+ }
+ /* write trailing CRLF and return */
+ return (fputs ("\r\n",sf) == EOF) ? NIL : T;
+}
+
+/* Append messages from scratch file to mailbox
+ * Accepts: MAIL stream
+ * source file
+ * destination file
+ * uidset to update if non-NIL
+ * Returns: T if success, NIL if failure
+ */
+
+int unix_append_msgs (MAILSTREAM *stream,FILE *sf,FILE *df,SEARCHSET *set)
+{
+ int ti,zn,c;
+ long f;
+ unsigned long i,j;
+ char *x,tmp[MAILTMPLEN];
+ int hdrp = T;
+ /* get message metadata line */
+ while (fgets (tmp,MAILTMPLEN,sf)) {
+ if (!(isdigit (tmp[0]) && strchr (tmp,'\n'))) return NIL;
+ f = strtol (tmp,&x,10); /* get flags */
+ if (!((*x++ == ' ') && isdigit (*x))) return NIL;
+ i = strtoul (x,&x,10); /* get message size */
+ if ((*x++ != ' ') || /* build initial header */
+ (fprintf (df,"From %s@%s %sStatus: ",myusername(),mylocalhost(),x)<0)||
+ (f&fSEEN && (putc ('R',df) == EOF)) ||
+ (fputs ("\r\nX-Status: ",df) == EOF) ||
+ (f&fDELETED && (putc ('D',df) == EOF)) ||
+ (f&fFLAGGED && (putc ('F',df) == EOF)) ||
+ (f&fANSWERED && (putc ('A',df) == EOF)) ||
+ (f&fDRAFT && (putc ('T',df) == EOF)) ||
+ (fputs ("\r\nX-Keywords:",df) == EOF)) return NIL;
+ /* copy keywords */
+ while ((c = getc (sf)) != '\n') switch (c) {
+ case EOF:
+ return NIL;
+ default:
+ if (putc (c,df) == EOF) return NIL;
+ }
+ if ((putc ('\n',df) == EOF) ||
+ (set && (fprintf (df,"X-UID: %lu\r\n",++(stream->uid_last)) < 0)))
+ return NIL;
+
+ for (c = '\n'; i && fgets (tmp,MAILTMPLEN,sf); c = tmp[j-1]) {
+ /* get read line length */
+ if (i < (j = strlen (tmp))) fatal ("unix_append_msgs overrun");
+ i -= j; /* number of bytes left */
+ if (!j) continue; /* do nothing if line emptied */
+ /* complete line? */
+ if ((c == '\n')) switch (tmp[0]) {
+ case 'F': /* possible "From " (case counts here) */
+ if ((j > 4) && (tmp[0] == 'F') && (tmp[1] == 'r') && (tmp[2] == 'o') &&
+ (tmp[3] == 'm') && (tmp[4] == ' ')) {
+ if (!unix_fromwidget) {
+ VALID (tmp,x,ti,zn);/* conditional, only write widget if */
+ if (!ti) break; /* it looks like a valid header */
+ } /* write the widget */
+ if (putc ('>',df) == EOF) return NIL;
+ }
+ break;
+ case 'S': case 's': /* possible "Status:" */
+ if (hdrp && (j > 6) && ((tmp[1] == 't') || (tmp[1] == 'T')) &&
+ ((tmp[2] == 'a') || (tmp[2] == 'A')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'u') || (tmp[4] == 'U')) &&
+ ((tmp[5] == 's') || (tmp[5] == 'S')) && (tmp[6] == ':') &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case 'X': case 'x': /* possible X-??? header */
+ if (hdrp && (tmp[1] == '-') &&
+ /* possible X-UID: */
+ (((j > 5) && ((tmp[2] == 'U') || (tmp[2] == 'u')) &&
+ ((tmp[3] == 'I') || (tmp[3] == 'i')) &&
+ ((tmp[4] == 'D') || (tmp[4] == 'd')) && (tmp[5] == ':')) ||
+ /* possible X-IMAP: */
+ ((j > 6) && ((tmp[2] == 'I') || (tmp[2] == 'i')) &&
+ ((tmp[3] == 'M') || (tmp[3] == 'm')) &&
+ ((tmp[4] == 'A') || (tmp[4] == 'a')) &&
+ ((tmp[5] == 'P') || (tmp[5] == 'p')) &&
+ ((tmp[6] == ':') ||
+ /* or X-IMAPbase: */
+ ((j > 10) && ((tmp[6] == 'b') || (tmp[6] == 'B')) &&
+ ((tmp[7] == 'a') || (tmp[7] == 'A')) &&
+ ((tmp[8] == 's') || (tmp[8] == 'S')) &&
+ ((tmp[9] == 'e') || (tmp[9] == 'E')) && (tmp[10] == ':')))) ||
+ /* possible X-Status: */
+ ((j > 8) && ((tmp[2] == 'S') || (tmp[2] == 's')) &&
+ ((tmp[3] == 't') || (tmp[3] == 'T')) &&
+ ((tmp[4] == 'a') || (tmp[4] == 'A')) &&
+ ((tmp[5] == 't') || (tmp[5] == 'T')) &&
+ ((tmp[6] == 'u') || (tmp[6] == 'U')) &&
+ ((tmp[7] == 's') || (tmp[7] == 'S')) && (tmp[8] == ':')) ||
+ /* possible X-Keywords: */
+ ((j > 10) && ((tmp[2] == 'K') || (tmp[2] == 'k')) &&
+ ((tmp[3] == 'e') || (tmp[3] == 'E')) &&
+ ((tmp[4] == 'y') || (tmp[4] == 'Y')) &&
+ ((tmp[5] == 'w') || (tmp[5] == 'W')) &&
+ ((tmp[6] == 'o') || (tmp[6] == 'O')) &&
+ ((tmp[7] == 'r') || (tmp[7] == 'R')) &&
+ ((tmp[8] == 'd') || (tmp[8] == 'D')) &&
+ ((tmp[9] == 's') || (tmp[9] == 'S')) && (tmp[10] == ':'))) &&
+ (fputs ("X-Original-",df) == EOF)) return NIL;
+ break;
+ case '\n': /* blank line */
+ hdrp = NIL;
+ break;
+ default: /* nothing to do */
+ break;
+ }
+ /* just write the line */
+ if (fwrite (tmp,1,j,df) != j) return NIL;
+ }
+ if (i) return NIL; /* didn't read entire message */
+ /* update set */
+ if (stream) mail_append_set (set,stream->uid_last);
+ }
+ return T;
+}
+
+/* Internal routines */
+
+
+/* UNIX mail abort stream
+ * Accepts: MAIL stream
+ */
+
+void unix_abort (MAILSTREAM *stream)
+{
+ if (LOCAL) { /* only if a file is open */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ if (LOCAL->ld >= 0) { /* have a mailbox lock? */
+ flock (LOCAL->ld,LOCK_UN);/* yes, release the lock */
+ close (LOCAL->ld); /* close the lock file */
+ unlink (LOCAL->lname); /* and delete it */
+ }
+ if (LOCAL->lname) fs_give ((void **) &LOCAL->lname);
+ /* free local text buffers */
+ if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
+ if (LOCAL->text.data) fs_give ((void **) &LOCAL->text.data);
+ if (LOCAL->linebuf) fs_give ((void **) &LOCAL->linebuf);
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* nuke the local data */
+ fs_give ((void **) &stream->local);
+ stream->dtb = NIL; /* log out the DTB */
+ }
+}
+
+/* UNIX open and lock mailbox
+ * Accepts: file name to open/lock
+ * file open mode
+ * destination buffer for lock file name
+ * type of locking operation (LOCK_SH or LOCK_EX)
+ */
+
+int unix_lock (char *file,int flags,int mode,char *lock,int op)
+{
+ int fd,ld,j;
+ int i = LOCKTIMEOUT * 60 - 1;
+ char tmp[MAILTMPLEN];
+ time_t t;
+ struct stat sb;
+ sprintf (lock,"%s.lock",file);/* build lock filename */
+ do { /* until OK or out of tries */
+ t = time (0); /* get the time now */
+ /* try to get the lock */
+ if ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT|O_EXCL,S_IREAD|S_IWRITE))>=0)
+ close (ld); /* got it, close the lock file! */
+ else if (errno != EEXIST) { /* miscellaneous error */
+ sprintf (tmp,"Error creating %.80s: %s",lock,strerror (errno));
+ if (!(i%15)) mm_log (tmp,WARN);
+ }
+ /* lock exists, still active? */
+ else if (!stat (lock,&sb) && (t > sb.st_ctime + LOCKTIMEOUT * 60) &&
+ ((ld = open(lock,O_BINARY|O_WRONLY|O_CREAT,S_IREAD|S_IWRITE))>=0))
+ close (ld); /* got timed-out lock file */
+ else { /* active lock, try again */
+ if (!(i%15)) {
+ sprintf (tmp,"Mailbox %.80s is locked, will override in %d seconds...",
+ file,i);
+ mm_log (tmp,WARN);
+ }
+ sleep (1); /* wait a second before next retry */
+ }
+ } while (*lock && ld < 0 && i--);
+ /* open file */
+ if ((fd = open (file,flags,mode)) >= 0) flock (fd,op);
+ else { /* open failed */
+ j = errno; /* preserve error code */
+ if (*lock) unlink (lock); /* flush the lock file if any */
+ errno = j; /* restore error code */
+ }
+ return fd;
+}
+
+/* UNIX unlock and close mailbox
+ * Accepts: file descriptor
+ * (optional) mailbox stream to check atime/mtime
+ * (optional) lock file name
+ */
+
+void unix_unlock (int fd,MAILSTREAM *stream,char *lock)
+{
+ if (stream) { /* need to muck with times? */
+ struct stat sbuf;
+ struct utimbuf times;
+ time_t now = time (0);
+ fstat (fd,&sbuf); /* get file times */
+ if (LOCAL->ld >= 0) { /* yes, readwrite session? */
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else if (stream->recent) { /* readonly with recent messages */
+ if ((sbuf.st_atime >= sbuf.st_mtime) ||
+ (sbuf.st_atime >= sbuf.st_ctime))
+ /* keep past mtime, whack back atime */
+ times.actime = (times.modtime = (sbuf.st_mtime < now) ?
+ sbuf.st_mtime : now) - 1;
+ else now = 0; /* no time change needed */
+ }
+ /* readonly with no recent messages */
+ else if ((sbuf.st_atime < sbuf.st_mtime) ||
+ (sbuf.st_atime < sbuf.st_ctime)) {
+ times.actime = now; /* set atime to now */
+ /* set mtime to (now - 1) if necessary */
+ times.modtime = (now > sbuf.st_mtime) ? sbuf.st_mtime : now - 1;
+ }
+ else now = 0; /* no time change needed */
+ /* set the times, note change */
+ if (now && !utime (stream->mailbox,&times))
+ LOCAL->filetime = times.modtime;
+ }
+ flock (fd,LOCK_UN); /* release flock'ers */
+ if (!stream) close (fd); /* close the file if no stream */
+ /* flush the lock file if any */
+ if (lock && *lock) unlink (lock);
+}
+
+/* UNIX mail parse and lock mailbox
+ * Accepts: MAIL stream
+ * space to write lock file name
+ * type of locking operation
+ * Returns: T if parse OK, critical & mailbox is locked shared; NIL if failure
+ */
+
+int unix_parse (MAILSTREAM *stream,char *lock,int op)
+{
+ int zn;
+ unsigned long i,j,k,m;
+ unsigned char c,*s,*t,*u,tmp[MAILTMPLEN],date[30];
+ int ti = 0,retain = T;
+ unsigned long nmsgs = stream->nmsgs;
+ unsigned long prevuid = nmsgs ? mail_elt (stream,nmsgs)->private.uid : 0;
+ unsigned long recent = stream->recent;
+ unsigned long oldnmsgs = stream->nmsgs;
+ short silent = stream->silent;
+ short pseudoseen = NIL;
+ struct stat sbuf;
+ STRING bs;
+ FDDATA d;
+ MESSAGECACHE *elt;
+ mail_lock (stream); /* guard against recursion or pingers */
+ /* toss out previous descriptor */
+ if (LOCAL->fd >= 0) close (LOCAL->fd);
+ mm_critical (stream); /* open and lock mailbox (shared OK) */
+ if ((LOCAL->fd = unix_lock (stream->mailbox,
+ O_BINARY + ((LOCAL->ld >= 0) ? O_RDWR:O_RDONLY),
+ NIL,lock,op)) < 0) {
+ sprintf (tmp,"Mailbox open failed, aborted: %s",strerror (errno));
+ mm_log (tmp,ERROR);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ fstat (LOCAL->fd,&sbuf); /* get status */
+ /* validate change in size */
+ if (sbuf.st_size < LOCAL->filesize) {
+ sprintf (tmp,"Mailbox shrank from %lu to %lu bytes, aborted",
+ (unsigned long) LOCAL->filesize,(unsigned long) sbuf.st_size);
+ mm_log (tmp,ERROR); /* this is pretty bad */
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+
+ /* new data? */
+ else if (i = sbuf.st_size - LOCAL->filesize) {
+ d.fd = LOCAL->fd; /* yes, set up file descriptor */
+ d.pos = LOCAL->filesize; /* get to that position in the file */
+ d.chunk = LOCAL->buf; /* initial buffer chunk */
+ d.chunksize = CHUNKSIZE; /* file chunk size */
+ INIT (&bs,fd_string,&d,i); /* initialize stringstruct */
+ /* skip leading whitespace for broken MTAs */
+ while (((c = CHR (&bs)) == '\n') || (c == '\r') ||
+ (c == ' ') || (c == '\t')) SNX (&bs);
+ if (SIZE (&bs)) { /* read new data */
+ /* remember internal header position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ s = unix_mbxline (stream,&bs,&i);
+ t = NIL,zn = 0;
+ if (i) VALID (s,t,ti,zn); /* see if valid From line */
+ if (!ti) { /* someone pulled the rug from under us */
+ sprintf (tmp,"Unexpected changes to mailbox (try restarting): %.20s",
+ (char *) s);
+ mm_log (tmp,ERROR);
+ unix_unlock (LOCAL->fd,stream,lock);
+ unix_abort (stream);
+ mail_unlock (stream);
+ mm_nocritical (stream); /* done with critical */
+ return NIL;
+ }
+ stream->silent = T; /* quell main program new message events */
+ do { /* found a message */
+ /* instantiate first new message */
+ mail_exists (stream,++nmsgs);
+ (elt = mail_elt (stream,nmsgs))->valid = T;
+ recent++; /* assume recent by default */
+ elt->recent = T;
+ /* note position/size of internal header */
+ elt->private.special.offset = j;
+ elt->private.msg.header.offset = elt->private.special.text.size = i;
+
+ /* generate plausible IMAPish date string */
+ date[2] = date[6] = date[20] = '-'; date[11] = ' ';
+ date[14] = date[17] = ':';
+ /* dd */
+ date[0] = t[ti - 2]; date[1] = t[ti - 1];
+ /* mmm */
+ date[3] = t[ti - 6]; date[4] = t[ti - 5]; date[5] = t[ti - 4];
+ /* hh */
+ date[12] = t[ti + 1]; date[13] = t[ti + 2];
+ /* mm */
+ date[15] = t[ti + 4]; date[16] = t[ti + 5];
+ if (t[ti += 6] == ':') {/* ss */
+ date[18] = t[++ti]; date[19] = t[++ti];
+ ti++; /* move to space */
+ }
+ else date[18] = date[19] = '0';
+ /* yy -- advance over timezone if necessary */
+ if (zn == ti) ti += (((t[zn+1] == '+') || (t[zn+1] == '-')) ? 6 : 4);
+ date[7] = t[ti + 1]; date[8] = t[ti + 2];
+ date[9] = t[ti + 3]; date[10] = t[ti + 4];
+ /* zzz */
+ t = zn ? (t + zn + 1) : (unsigned char *) "LCL";
+ date[21] = *t++; date[22] = *t++; date[23] = *t++;
+ if ((date[21] != '+') && (date[21] != '-')) date[24] = '\0';
+ else { /* numeric time zone */
+ date[24] = *t++; date[25] = *t++;
+ date[26] = '\0'; date[20] = ' ';
+ }
+ /* set internal date */
+ if (!mail_parse_date (elt,date)) {
+ sprintf (tmp,"Unable to parse internal date: %s",(char *) date);
+ mm_log (tmp,WARN);
+ }
+
+ do { /* look for message body */
+ s = t = unix_mbxline (stream,&bs,&i);
+ if (i) switch (*s) { /* check header lines */
+ case 'X': /* possible X-???: line */
+ if (s[1] == '-') { /* must be immediately followed by hyphen */
+ /* X-Status: becomes Status: in S case */
+ if (s[2] == 'S' && s[3] == 't' && s[4] == 'a' && s[5] == 't' &&
+ s[6] == 'u' && s[7] == 's' && s[8] == ':') s += 2;
+ /* possible X-Keywords */
+ else if (s[2] == 'K' && s[3] == 'e' && s[4] == 'y' &&
+ s[5] == 'w' && s[6] == 'o' && s[7] == 'r' &&
+ s[8] == 'd' && s[9] == 's' && s[10] == ':') {
+ SIZEDTEXT uf;
+ retain = NIL; /* don't retain continuation */
+ s += 11; /* flush leading whitespace */
+ while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n'))){
+ while (*s == ' ') s++;
+ /* find end of keyword */
+ if (!(u = strpbrk (s," \n\r"))) u = s + strlen (s);
+ /* got a keyword? */
+ if ((k = (u - s)) && (k <= MAXUSERFLAG)) {
+ uf.data = (unsigned char *) s;
+ uf.size = k;
+ for (j = 0; (j < NUSERFLAGS) && stream->user_flags[j]; ++j)
+ if (!compare_csizedtext (stream->user_flags[j],&uf)) {
+ elt->user_flags |= ((long) 1) << j;
+ break;
+ }
+ }
+ s = u; /* advance to next keyword */
+ }
+ break;
+ }
+
+ /* possible X-IMAP */
+ else if ((s[2] == 'I') && (s[3] == 'M') && (s[4] == 'A') &&
+ (s[5] == 'P') && ((m = (s[6] == ':')) ||
+ ((s[6] == 'b') && (s[7] == 'a') &&
+ (s[8] == 's') && (s[9] == 'e') &&
+ (s[10] == ':')))) {
+ retain = NIL; /* don't retain continuation */
+ if ((nmsgs == 1) && !stream->uid_validity) {
+ /* advance to data */
+ s += m ? 7 : 11;
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0; /* slurp UID validity */
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ /* must have valid UID validity and UID last */
+ if (j && isdigit (*s)) {
+ /* pseudo-header seen if X-IMAP */
+ if (m) pseudoseen = LOCAL->pseudo = T;
+ /* save UID validity */
+ stream->uid_validity = j;
+ j = 0; /* slurp UID last */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* save UID last */
+ stream->uid_last = j;
+ /* process keywords */
+ for (j = 0; (*s != '\n') && ((*s != '\r')||(s[1] != '\n'));
+ s = u,j++) {
+ /* flush leading whitespace */
+ while (*s == ' ') s++;
+ u = strpbrk (s," \n\r");
+ /* got a keyword? */
+ if ((j < NUSERFLAGS) && (k = (u - s)) &&
+ (k <= MAXUSERFLAG)) {
+ if (stream->user_flags[j])
+ fs_give ((void **) &stream->user_flags[j]);
+ stream->user_flags[j] = (char *) fs_get (k + 1);
+ strncpy (stream->user_flags[j],s,k);
+ stream->user_flags[j][k] = '\0';
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ /* possible X-UID */
+ else if (s[2] == 'U' && s[3] == 'I' && s[4] == 'D' &&
+ s[5] == ':') {
+ retain = NIL; /* don't retain continuation */
+ /* only believe if have a UID validity */
+ if (stream->uid_validity && ((nmsgs > 1) || !pseudoseen)) {
+ s += 6; /* advance to UID value */
+ /* flush whitespace */
+ while (*s == ' ') s++;
+ j = 0;
+ /* found a digit? */
+ while (isdigit (*s)) {
+ j *= 10; /* yes, add it in */
+ j += *s++ - '0';
+ }
+ /* flush remainder of line */
+ while (*s != '\n') s++;
+ /* make sure not duplicated */
+ if (elt->private.uid)
+ sprintf (tmp,"Message %lu UID %lu already has UID %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,elt->private.uid);
+ /* make sure UID doesn't go backwards */
+ else if (j <= prevuid)
+ sprintf (tmp,"Message %lu UID %lu less than %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,prevuid + 1);
+#if 0 /* this is currently broken by UIDPLUS */
+ /* or skip by mailbox's recorded last */
+ else if (j > stream->uid_last)
+ sprintf (tmp,"Message %lu UID %lu greater than last %lu",
+ pseudoseen ? elt->msgno - 1 : elt->msgno,
+ j,stream->uid_last);
+#endif
+ else { /* normal UID case */
+ prevuid = elt->private.uid = j;
+#if 1 /* temporary kludge for UIDPLUS */
+ if (prevuid > stream->uid_last) {
+ stream->uid_last = prevuid;
+ LOCAL->ddirty = LOCAL->dirty = T;
+ }
+#endif
+ break; /* exit this cruft */
+ }
+ mm_log (tmp,WARN);
+ /* invalidate UID validity */
+ stream->uid_validity = 0;
+ elt->private.uid = 0;
+ }
+ break;
+ }
+ }
+ /* otherwise fall into S case */
+
+ case 'S': /* possible Status: line */
+ if (s[0] == 'S' && s[1] == 't' && s[2] == 'a' && s[3] == 't' &&
+ s[4] == 'u' && s[5] == 's' && s[6] == ':') {
+ retain = NIL; /* don't retain continuation */
+ s += 6; /* advance to status flags */
+ do switch (*s++) {/* parse flags */
+ case 'R': /* message read */
+ elt->seen = T;
+ break;
+ case 'O': /* message old */
+ if (elt->recent) {
+ elt->recent = NIL;
+ recent--; /* it really wasn't recent */
+ }
+ break;
+ case 'D': /* message deleted */
+ elt->deleted = T;
+ break;
+ case 'F': /* message flagged */
+ elt->flagged = T;
+ break;
+ case 'A': /* message answered */
+ elt->answered = T;
+ break;
+ case 'T': /* message is a draft */
+ elt->draft = T;
+ break;
+ default: /* some other crap */
+ break;
+ } while (*s && (*s != '\n') && ((*s != '\r') || (s[1] != '\n')));
+ break; /* all done */
+ }
+ /* otherwise fall into default case */
+
+ default: /* ordinary header line */
+ if ((*s == 'S') || (*s == 's') ||
+ (((*s == 'X') || (*s == 'x')) && (s[1] == '-'))) {
+ unsigned char *e,*v;
+ /* must match what mail_filter() does */
+ for (u = s,v = tmp,e = u + min (i,MAILTMPLEN - 1);
+ (u < e) && ((c = (*u ? *u : (*u = ' '))) != ':') &&
+ ((c > ' ') || ((c != ' ') && (c != '\t') &&
+ (c != '\r') && (c != '\n')));
+ *v++ = *u++);
+ *v = '\0'; /* tie off */
+ /* matches internal header? */
+ if (!compare_cstring (tmp,"STATUS") ||
+ !compare_cstring (tmp,"X-STATUS") ||
+ !compare_cstring (tmp,"X-KEYWORDS") ||
+ !compare_cstring (tmp,"X-UID") ||
+ !compare_cstring (tmp,"X-IMAP") ||
+ !compare_cstring (tmp,"X-IMAPBASE")) {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus %s header in message %lu",
+ (char *) tmp,elt->msgno);
+ mm_log (err,WARN);
+ retain = NIL; /* don't retain continuation */
+ break; /* different case or something */
+ }
+ }
+ /* retain or non-continuation? */
+ if (retain || ((*s != ' ') && (*s != '\t'))) {
+ retain = T; /* retaining continuation now */
+ /* line length in CRLF format newline */
+ k = i + (((i < 2) || (s[i - 2] != '\r')) ? 1 : 0);
+ /* header size */
+ elt->rfc822_size = elt->private.spare.data += k;
+ }
+ else {
+ char err[MAILTMPLEN];
+ sprintf (err,"Discarding bogus continuation in msg %lu: %.80s",
+ elt->msgno,(char *) s);
+ if (u = strpbrk (err,"\r\n")) *u = '\0';
+ mm_log (err,WARN);
+ break; /* different case or something */
+ }
+ break;
+ }
+ } while (i && (*t != '\n') && ((*t != '\r') || (t[1] != '\n')));
+ /* "internal" header sans trailing newline */
+ if (i) elt->private.spare.data -= 2;
+ /* assign a UID if none found */
+ if (((nmsgs > 1) || !pseudoseen) && !elt->private.uid) {
+ prevuid = elt->private.uid = ++stream->uid_last;
+ elt->private.dirty = T;
+ LOCAL->ddirty = T; /* force update */
+ }
+ else elt->private.dirty = elt->recent;
+
+ /* note size of header, location of text */
+ elt->private.msg.header.text.size =
+ (elt->private.msg.text.offset =
+ (LOCAL->filesize + GETPOS (&bs)) - elt->private.special.offset) -
+ elt->private.special.text.size;
+ k = m = 0; /* no previous line size yet */
+ /* note current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ if (i) do { /* look for next message */
+ s = unix_mbxline (stream,&bs,&i);
+ if (i) { /* got new data? */
+ VALID (s,t,ti,zn); /* yes, parse line */
+ if (!ti) { /* not a header line, add it to message */
+ if (s[i - 1] == '\n')
+ elt->rfc822_size +=
+ k = i + (m = (((i < 2) || s[i - 2] != '\r') ? 1 : 0));
+ else { /* file does not end with newline! */
+ elt->rfc822_size += i;
+ k = m = 0;
+ }
+ /* update current position */
+ j = LOCAL->filesize + GETPOS (&bs);
+ }
+ }
+ } while (i && !ti); /* until found a header */
+ elt->private.msg.text.text.size = j -
+ (elt->private.special.offset + elt->private.msg.text.offset);
+ if (k == 2) { /* last line was blank? */
+ elt->private.msg.text.text.size -= (m ? 1 : 2);
+ elt->rfc822_size -= 2;
+ }
+ /* until end of buffer */
+ } while (!stream->sniff && i);
+ if (pseudoseen) { /* flush pseudo-message if present */
+ /* decrement recent count */
+ if (mail_elt (stream,1)->recent) recent--;
+ /* and the exists count */
+ mail_exists (stream,nmsgs--);
+ mail_expunged(stream,1);/* fake an expunge of that message */
+ }
+ /* need to start a new UID validity? */
+ if (!stream->uid_validity) {
+ stream->uid_validity = (unsigned long) time (0);
+ if (nmsgs) { /* don't bother if empty file */
+ /* make dirty to restart UID epoch */
+ LOCAL->ddirty = LOCAL->dirty = T;
+ /* need to rewrite msg 1 if not pseudo */
+ if (!LOCAL->pseudo) mail_elt (stream,1)->private.dirty = T;
+ mm_log ("Assigning new unique identifiers to all messages",NIL);
+ }
+ }
+ stream->nmsgs = oldnmsgs; /* whack it back down */
+ stream->silent = silent; /* restore old silent setting */
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,nmsgs);
+ mail_recent (stream,recent);
+ /* mark dirty so O flags are set */
+ if (recent) LOCAL->dirty = T;
+ }
+ }
+ /* no change, don't babble if never got time */
+ else if (LOCAL->filetime && LOCAL->filetime != sbuf.st_mtime)
+ mm_log ("New mailbox modification time but apparently no changes",WARN);
+ /* update parsed file size and time */
+ LOCAL->filesize = sbuf.st_size;
+ LOCAL->filetime = sbuf.st_mtime;
+ return T; /* return the winnage */
+}
+
+/* UNIX read line from mailbox
+ * Accepts: mail stream
+ * stringstruct
+ * pointer to line size
+ * Returns: pointer to input line
+ */
+
+char *unix_mbxline (MAILSTREAM *stream,STRING *bs,unsigned long *size)
+{
+ unsigned long i,j,k,m;
+ char *s,*t,*te;
+ char *ret = "";
+ /* flush old buffer */
+ if (LOCAL->line) fs_give ((void **) &LOCAL->line);
+ /* if buffer needs refreshing */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ if (SIZE (bs)) { /* find newline */
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* difficult case if line spans buffer */
+ if ((i = s - bs->curpos) == bs->cursize) {
+ /* have space in line buffer? */
+ if (i > LOCAL->linebuflen) {
+ fs_give ((void **) &LOCAL->linebuf);
+ LOCAL->linebuf = (char *) fs_get (LOCAL->linebuflen = i);
+ }
+ /* remember what we have so far */
+ memcpy (LOCAL->linebuf,bs->curpos,i);
+ /* load next buffer */
+ SETPOS (bs,k = GETPOS (bs) + i);
+ /* end of fast scan */
+ te = (t = (s = bs->curpos) + bs->cursize) - 12;
+ /* fast scan in overlap buffer */
+ while (s < te) if ((*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n') ||
+ (*s++ == '\n') || (*s++ == '\n') || (*s++ == '\n')) {
+ --s; /* back up */
+ break; /* exit loop */
+ }
+
+ /* final character-at-a-time scan */
+ while ((s < t) && (*s != '\n')) ++s;
+ /* huge line? */
+ if ((j = s - bs->curpos) == bs->cursize) {
+ SETPOS (bs,GETPOS (bs) + j);
+ /* look for end of line (s-l-o-w!!) */
+ for (m = SIZE (bs); m && (SNX (bs) != '\n'); --m,++j);
+ SETPOS (bs,k); /* go back to where it started */
+ }
+ /* got size of data, make buffer for return */
+ ret = LOCAL->line = (char *) fs_get (i + j + 2);
+ /* copy first chunk */
+ memcpy (ret,LOCAL->linebuf,i);
+ while (j) { /* copy remainder */
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ memcpy (ret + i,bs->curpos,k = min (j,bs->cursize));
+ i += k; /* account for this much read in */
+ j -= k;
+ bs->curpos += k; /* increment new position */
+ bs->cursize -= k; /* eat that many bytes */
+ }
+ if (!bs->cursize) SETPOS (bs,GETPOS (bs));
+ /* read newline at end */
+ if (SIZE (bs)) ret[i++] = SNX (bs);
+ ret[i] = '\0'; /* makes debugging easier */
+ }
+ else { /* this is easy */
+ ret = bs->curpos; /* string it at this position */
+ bs->curpos += ++i; /* increment new position */
+ bs->cursize -= i; /* eat that many bytes */
+ }
+ *size = i; /* return that to user */
+ }
+ else *size = 0; /* end of data, return empty */
+ return ret;
+}
+
+/* UNIX make pseudo-header
+ * Accepts: MAIL stream
+ * buffer to write pseudo-header
+ * Returns: length of pseudo-header
+ */
+
+unsigned long unix_pseudo (MAILSTREAM *stream,char *hdr)
+{
+ int i;
+ char *s,*t,tmp[MAILTMPLEN];
+ time_t now = time(0);
+ rfc822_fixed_date (tmp);
+ sprintf (hdr,"From %s %.24s\r\nDate: %s\r\nFrom: %s <%s@%.80s>\r\nSubject: %s\r\nMessage-ID: <%lu@%.80s>\r\nX-IMAP: %010ld %010ld",
+ pseudo_from,ctime (&now),
+ tmp,pseudo_name,pseudo_from,mylocalhost (),pseudo_subject,
+ (unsigned long) now,mylocalhost (),stream->uid_validity,
+ stream->uid_last);
+ for (t = hdr + strlen (hdr),i = 0; i < NUSERFLAGS; ++i)
+ if (stream->user_flags[i])
+ sprintf (t += strlen (t)," %s",stream->user_flags[i]);
+ strcpy (t += strlen (t),"\r\nStatus: RO\r\n\r\n");
+ for (s = pseudo_msg,t += strlen (t); *s; *t++ = *s++)
+ if (*s == '\n') *t++ = '\r';
+ *t++ = '\r'; *t++ = '\n'; *t++ = '\r'; *t++ = '\n';
+ *t = '\0'; /* tie off pseudo header */
+ return t - hdr; /* return length of pseudo header */
+}
+
+/* UNIX make status string
+ * Accepts: MAIL stream
+ * destination string to write
+ * message cache entry
+ * UID to write if non-zero (else use elt->private.uid)
+ * non-zero flag to write UID (.LT. 0 to write UID base info too)
+ * Returns: length of string
+ */
+
+unsigned long unix_xstatus (MAILSTREAM *stream,char *status,MESSAGECACHE *elt,
+ unsigned long uid,long flag)
+{
+ char *t,stack[64];
+ char *s = status;
+ unsigned long n;
+ unsigned long pad = 50;
+ /* This used to use sprintf(), but thanks to certain cretinous C libraries
+ with horribly slow implementations of sprintf() I had to change it to this
+ mess. At least it should be fast. */
+ *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't'; *s++ = 'u'; *s++ = 's';
+ *s++ = ':'; *s++ = ' ';
+ if (elt->seen) *s++ = 'R';
+ /* only write O if have a UID */
+ if (flag && (!elt->recent || LOCAL->appending)) *s++ = 'O';
+ *s++ = '\r'; *s++ = '\n';
+ *s++ = 'X'; *s++ = '-'; *s++ = 'S'; *s++ = 't'; *s++ = 'a'; *s++ = 't';
+ *s++ = 'u'; *s++ = 's'; *s++ = ':'; *s++ = ' ';
+ if (elt->deleted) *s++ = 'D';
+ if (elt->flagged) *s++ = 'F';
+ if (elt->answered) *s++ = 'A';
+ if (elt->draft) *s++ = 'T';
+ *s++ = '\r'; *s++ = '\n';
+
+ *s++ = 'X'; *s++ = '-'; *s++ = 'K'; *s++ = 'e'; *s++ = 'y'; *s++ = 'w';
+ *s++ = 'o'; *s++ = 'r'; *s++ = 'd'; *s++ = 's'; *s++ = ':';
+ if (n = elt->user_flags) do {
+ *s++ = ' ';
+ for (t = stream->user_flags[find_rightmost_bit (&n)]; *t; *s++ = *t++);
+ } while (n);
+ n = s - status; /* get size of stuff so far */
+ /* pad X-Keywords to make size constant */
+ if (n < pad) for (n = pad - n; n > 0; --n) *s++ = ' ';
+ *s++ = '\r'; *s++ = '\n';
+ if (flag) { /* want to include UID? */
+ t = stack;
+ /* push UID digits on the stack */
+ n = uid ? uid : elt->private.uid;
+ do *t++ = (char) (n % 10) + '0';
+ while (n /= 10);
+ *s++ = 'X'; *s++ = '-'; *s++ = 'U'; *s++ = 'I'; *s++ = 'D'; *s++ = ':';
+ *s++ = ' ';
+ /* pop UID from stack */
+ while (t > stack) *s++ = *--t;
+ *s++ = '\r'; *s++ = '\n';
+ }
+ /* end of extended message status */
+ *s++ = '\r'; *s++ = '\n'; *s = '\0';
+ return s - status; /* return size of resulting string */
+}
+
+/* Rewrite mailbox file
+ * Accepts: MAIL stream, must be critical and locked
+ * return pointer to number of expunged messages if want expunge
+ * lock file name
+ * expunge sequence, not deleted flag
+ * Returns: T if success and mailbox unlocked, NIL if failure
+ */
+
+#define OVERFLOWBUFLEN 8192 /* initial overflow buffer length */
+
+long unix_rewrite (MAILSTREAM *stream,unsigned long *nexp,char *lock,
+ long flags)
+{
+ MESSAGECACHE *elt;
+ UNIXFILE f;
+ char *s;
+ struct utimbuf times;
+ long ret,flag;
+ unsigned long i,j;
+ unsigned long recent = stream->recent;
+ unsigned long size = LOCAL->pseudo ? unix_pseudo (stream,LOCAL->buf) : 0;
+ if (nexp) *nexp = 0; /* initially nothing expunged */
+ /* calculate size of mailbox after rewrite */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs; i++) {
+ elt = mail_elt (stream,i); /* get cache */
+ if (!(nexp && elt->deleted && (flags ? elt->sequence : T))) {
+ /* add RFC822 size of this message */
+ size += elt->private.special.text.size + elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag) +
+ elt->private.msg.text.text.size + 2;
+ flag = 1; /* only count X-IMAPbase once */
+ }
+ }
+ if (!size) { /* no messages and no pseudo, make one now */
+ size = unix_pseudo (stream,LOCAL->buf);
+ LOCAL->pseudo = T;
+ }
+ /* extend the file as necessary */
+ if (ret = unix_extend (stream,size)) {
+ /* Set up buffered I/O file structure
+ * curpos current position being written through buffering
+ * filepos current position being written physically to the disk
+ * bufpos current position being written in the buffer
+ * protect current maximum position that can be written to the disk
+ * before buffering is forced
+ * The code tries to buffer so that that disk is written in multiples of
+ * OVERBLOWBUFLEN bytes.
+ */
+ f.stream = stream; /* note mail stream */
+ f.curpos = f.filepos = 0; /* start of file */
+ f.protect = stream->nmsgs ? /* initial protection pointer */
+ mail_elt (stream,1)->private.special.offset : 8192;
+ f.bufpos = f.buf = (char *) fs_get (f.buflen = OVERFLOWBUFLEN);
+
+ if (LOCAL->pseudo) /* update pseudo-header */
+ unix_write (&f,LOCAL->buf,unix_pseudo (stream,LOCAL->buf));
+ /* loop through all messages */
+ for (i = 1,flag = LOCAL->pseudo ? 1 : -1; i <= stream->nmsgs;) {
+ elt = mail_elt (stream,i);/* get cache */
+ /* expunge this message? */
+ if (nexp && elt->deleted && (flags ? elt->sequence : T)) {
+ /* one less recent message */
+ if (elt->recent) --recent;
+ mail_expunged(stream,i);/* notify upper levels */
+ ++*nexp; /* count up one more expunged message */
+ }
+ else { /* preserve this message */
+ i++; /* advance to next message */
+ if ((flag < 0) || /* need to rewrite message? */
+ elt->private.dirty ||
+ (((unsigned long) f.curpos) != elt->private.special.offset) ||
+ (elt->private.msg.header.text.size !=
+ (elt->private.spare.data +
+ unix_xstatus (stream,LOCAL->buf,elt,NIL,flag)))) {
+ unsigned long newoffset = f.curpos;
+ /* yes, seek to internal header */
+ lseek (LOCAL->fd,elt->private.special.offset,L_SET);
+ read (LOCAL->fd,LOCAL->buf,elt->private.special.text.size);
+ /* protection pointer moves to RFC822 header */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.header.offset;
+ /* write internal header */
+ unix_write (&f,LOCAL->buf,elt->private.special.text.size);
+ /* get RFC822 header */
+ s = unix_header (stream,elt->msgno,&j,NIL);
+ /* in case this got decremented */
+ elt->private.msg.header.offset = elt->private.special.text.size;
+ /* header size, sans trailing newline */
+ if ((j < 4) || (s[j - 4] == '\r')) j -= 2;
+ if (j != elt->private.spare.data) fatal ("header size inconsistent");
+ /* protection pointer moves to RFC822 text */
+ f.protect = elt->private.special.offset +
+ elt->private.msg.text.offset;
+ unix_write (&f,s,j); /* write RFC822 header */
+ /* write status and UID */
+ unix_write (&f,LOCAL->buf,
+ j = unix_xstatus (stream,LOCAL->buf,elt,NIL,flag));
+ flag = 1; /* only write X-IMAPbase once */
+ /* new file header size */
+ elt->private.msg.header.text.size = elt->private.spare.data + j;
+
+ /* did text move? */
+ if (f.curpos != f.protect) {
+ /* get message text */
+ s = unix_text_work (stream,elt,&j,FT_INTERNAL);
+ /* can't happen it says here */
+ if (j > elt->private.msg.text.text.size)
+ fatal ("text size inconsistent");
+ /* new text offset, status/UID may change it */
+ elt->private.msg.text.offset = f.curpos - newoffset;
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : (f.curpos + j + 2);
+ unix_write (&f,s,j);/* write text */
+ /* write trailing newline */
+ unix_write (&f,"\r\n",2);
+ }
+ else { /* tie off header and status */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ /* new internal header offset */
+ elt->private.special.offset = newoffset;
+ elt->private.dirty =NIL;/* message is now clean */
+ }
+ else { /* no need to rewrite this message */
+ /* tie off previous message if needed */
+ unix_write (&f,NIL,NIL);
+ /* protection pointer moves to next message */
+ f.protect = (i <= stream->nmsgs) ?
+ mail_elt (stream,i)->private.special.offset : size;
+ /* locate end of message text */
+ j = f.filepos + elt->private.special.text.size +
+ elt->private.msg.header.text.size +
+ elt->private.msg.text.text.size;
+ /* trailing newline already there? */
+ if (f.protect == (off_t) (j + 2)) f.curpos = f.filepos = f.protect;
+ else { /* trailing newline missing, write it */
+ f.curpos = f.filepos = j;
+ unix_write (&f,"\r\n",2);
+ }
+ }
+ }
+ }
+
+ unix_write (&f,NIL,NIL); /* tie off final message */
+ if (size != ((unsigned long) f.filepos)) fatal ("file size inconsistent");
+ fs_give ((void **) &f.buf); /* free buffer */
+ /* make sure tied off */
+ ftruncate (LOCAL->fd,LOCAL->filesize = size);
+ fsync (LOCAL->fd); /* make sure the updates take */
+ if (size && (flag < 0)) fatal ("lost UID base information");
+ /* no longer dirty */
+ LOCAL->ddirty = LOCAL->dirty = NIL;
+ /* notify upper level of new mailbox sizes */
+ mail_exists (stream,stream->nmsgs);
+ mail_recent (stream,recent);
+ /* set atime to now, mtime a second earlier */
+ times.modtime = (times.actime = time (0)) -1;
+ /* set the times, note change */
+ if (!utime (stream->mailbox,&times)) LOCAL->filetime = times.modtime;
+ /* flush the lock file */
+ unix_unlock (LOCAL->fd,stream,lock);
+ }
+ return ret; /* return state from algorithm */
+}
+
+/* Extend UNIX mailbox file
+ * Accepts: MAIL stream
+ * new desired size
+ * Return: T if success, else NIL
+ */
+
+long unix_extend (MAILSTREAM *stream,unsigned long size)
+{
+ unsigned long i = (size > ((unsigned long) LOCAL->filesize)) ?
+ size - ((unsigned long) LOCAL->filesize) : 0;
+ if (i) { /* does the mailbox need to grow? */
+ if (i > LOCAL->buflen) { /* make sure have enough space */
+ /* this user won the lottery all right */
+ fs_give ((void **) &LOCAL->buf);
+ LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
+ }
+ memset (LOCAL->buf,'\0',i); /* get a block of nulls */
+ while (T) { /* until write successful or punt */
+ lseek (LOCAL->fd,LOCAL->filesize,L_SET);
+ if ((write (LOCAL->fd,LOCAL->buf,i) >= 0) && !fsync (LOCAL->fd)) break;
+ else {
+ long e = errno; /* note error before doing ftruncate */
+ ftruncate (LOCAL->fd,LOCAL->filesize);
+ if (mm_diskerror (stream,e,NIL)) {
+ fsync (LOCAL->fd); /* user chose to punt */
+ sprintf (LOCAL->buf,"Unable to extend mailbox: %s",strerror (e));
+ if (!stream->silent) mm_log (LOCAL->buf,ERROR);
+ return NIL;
+ }
+ }
+ }
+ }
+ return LONGT;
+}
+
+/* Write data to buffered file
+ * Accepts: buffered file pointer
+ * file data or NIL to indicate "flush buffer"
+ * date size (ignored for "flush buffer")
+ * Does not return until success
+ */
+
+void unix_write (UNIXFILE *f,char *buf,unsigned long size)
+{
+ unsigned long i,j,k;
+ if (buf) { /* doing buffered write? */
+ i = f->bufpos - f->buf; /* yes, get size of current buffer data */
+ /* yes, have space in current buffer chunk? */
+ if (j = i ? ((f->buflen - i) % OVERFLOWBUFLEN) : f->buflen) {
+ /* yes, fill up buffer as much as we can */
+ memcpy (f->bufpos,buf,k = min (j,size));
+ f->bufpos += k; /* new buffer position */
+ f->curpos += k; /* new current position */
+ if (j -= k) return; /* all done if still have buffer free space */
+ buf += k; /* full, get new unwritten data pointer */
+ size -= k; /* new data size */
+ i += k; /* new buffer data size */
+ }
+ /* This chunk of the buffer is full. See if can make some space by
+ * writing to the disk, if there's enough unprotected space to do so.
+ * Try to fill out any unaligned chunk, along with any subsequent full
+ * chunks that will fit in unprotected space.
+ */
+ /* any unprotected space we can write to? */
+ if (j = min (i,f->protect - f->filepos)) {
+ /* yes, filepos not at chunk boundary? */
+ if ((k = f->filepos % OVERFLOWBUFLEN) && ((k = OVERFLOWBUFLEN - k) < j))
+ j -= k; /* yes, and can write out partial chunk */
+ else k = 0; /* no partial chunk to write */
+ /* if at least a chunk free, write that too */
+ if (j > OVERFLOWBUFLEN) k += j - (j % OVERFLOWBUFLEN);
+ if (k) { /* write data if there is anything we can */
+ unix_phys_write (f,f->buf,k);
+ /* slide buffer */
+ if (i -= k) memmove (f->buf,f->buf + k,i);
+ f->bufpos = f->buf + i; /* new end of buffer */
+ }
+ }
+
+ /* Have flushed the buffer as best as possible. All done if no more
+ * data to write. Otherwise, if the buffer is empty AND if the unwritten
+ * data is larger than a chunk AND the unprotected space is also larger
+ * than a chunk, then write as many chunks as we can directly from the
+ * data. Buffer the rest, expanding the buffer as needed.
+ */
+ if (size) { /* have more data that we need to buffer? */
+ /* can write any of it to disk instead? */
+ if ((f->bufpos == f->buf) &&
+ ((j = min (f->protect - f->filepos,size)) > OVERFLOWBUFLEN)) {
+ /* write as much as we can right now */
+ unix_phys_write (f,buf,j -= (j % OVERFLOWBUFLEN));
+ buf += j; /* new data pointer */
+ size -= j; /* new data size */
+ f->curpos += j; /* advance current pointer */
+ }
+ if (size) { /* still have data that we need to buffer? */
+ /* yes, need to expand the buffer? */
+ if ((i = ((f->bufpos + size) - f->buf)) > f->buflen) {
+ /* note current position in buffer */
+ j = f->bufpos - f->buf;
+ i += OVERFLOWBUFLEN; /* yes, grow another chunk */
+ fs_resize ((void **) &f->buf,f->buflen = i - (i % OVERFLOWBUFLEN));
+ /* in case buffer relocated */
+ f->bufpos = f->buf + j;
+ }
+ /* buffer remaining data */
+ memcpy (f->bufpos,buf,size);
+ f->bufpos += size; /* new end of buffer */
+ f->curpos += size; /* advance current pointer */
+ }
+ }
+ }
+ else { /* flush buffer to disk */
+ unix_phys_write (f,f->buf,i = f->bufpos - f->buf);
+ f->bufpos = f->buf; /* reset buffer */
+ /* update positions */
+ f->curpos = f->protect = f->filepos;
+ }
+}
+
+/* Physical disk write
+ * Accepts: buffered file pointer
+ * buffer address
+ * buffer size
+ * Does not return until success
+ */
+
+void unix_phys_write (UNIXFILE *f,char *buf,size_t size)
+{
+ MAILSTREAM *stream = f->stream;
+ /* write data at desired position */
+ while (size && ((lseek (LOCAL->fd,f->filepos,L_SET) < 0) ||
+ (write (LOCAL->fd,buf,size) < 0))) {
+ int e;
+ char tmp[MAILTMPLEN];
+ sprintf (tmp,"Unable to write to mailbox: %s",strerror (e = errno));
+ mm_log (tmp,ERROR);
+ mm_diskerror (NIL,e,T); /* serious problem, must retry */
+ }
+ f->filepos += size; /* update file position */
+}
diff --git a/imap/src/osdep/nt/unixnt.h b/imap/src/osdep/nt/unixnt.h
new file mode 100644
index 00000000..1305184f
--- /dev/null
+++ b/imap/src/osdep/nt/unixnt.h
@@ -0,0 +1,161 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: UNIX mail routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 20 December 1989
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Validate line
+ * Accepts: pointer to candidate string to validate as a From header
+ * return pointer to end of date/time field
+ * return pointer to offset from t of time (hours of ``mmm dd hh:mm'')
+ * return pointer to offset from t of time zone (if non-zero)
+ * Returns: t,ti,zn set if valid From string, else ti is NIL
+ */
+
+#define VALID(s,x,ti,zn) { \
+ ti = 0; \
+ if ((*s == 'F') && (s[1] == 'r') && (s[2] == 'o') && (s[3] == 'm') && \
+ (s[4] == ' ')) { \
+ for (x = s + 5; *x && *x != '\012'; x++); \
+ if (*x) { \
+ if (x[-1] == '\015') --x; \
+ if (x - s >= 41) { \
+ for (zn = -1; x[zn] != ' '; zn--); \
+ if ((x[zn-1] == 'm') && (x[zn-2] == 'o') && (x[zn-3] == 'r') && \
+ (x[zn-4] == 'f') && (x[zn-5] == ' ') && (x[zn-6] == 'e') && \
+ (x[zn-7] == 't') && (x[zn-8] == 'o') && (x[zn-9] == 'm') && \
+ (x[zn-10] == 'e') && (x[zn-11] == 'r') && (x[zn-12] == ' '))\
+ x += zn - 12; \
+ } \
+ if (x - s >= 27) { \
+ if (x[-5] == ' ') { \
+ if (x[-8] == ':') zn = 0,ti = -5; \
+ else if (x[-9] == ' ') ti = zn = -9; \
+ else if ((x[-11] == ' ') && ((x[-10]=='+') || (x[-10]=='-'))) \
+ ti = zn = -11; \
+ } \
+ else if (x[-4] == ' ') { \
+ if (x[-9] == ' ') zn = -4,ti = -9; \
+ } \
+ else if (x[-6] == ' ') { \
+ if ((x[-11] == ' ') && ((x[-5] == '+') || (x[-5] == '-'))) \
+ zn = -6,ti = -11; \
+ } \
+ if (ti && !((x[ti - 3] == ':') && \
+ (x[ti -= ((x[ti - 6] == ':') ? 9 : 6)] == ' ') && \
+ (x[ti - 3] == ' ') && (x[ti - 7] == ' ') && \
+ (x[ti - 11] == ' '))) ti = 0; \
+ } \
+ } \
+ } \
+}
+
+/* You are not expected to understand this macro, but read the next page if
+ * you are not faint of heart.
+ *
+ * Known formats to the VALID macro are:
+ * From user Wed Dec 2 05:53 1992
+ * BSD From user Wed Dec 2 05:53:22 1992
+ * SysV From user Wed Dec 2 05:53 PST 1992
+ * rn From user Wed Dec 2 05:53:22 PST 1992
+ * From user Wed Dec 2 05:53 -0700 1992
+ * emacs From user Wed Dec 2 05:53:22 -0700 1992
+ * From user Wed Dec 2 05:53 1992 PST
+ * From user Wed Dec 2 05:53:22 1992 PST
+ * From user Wed Dec 2 05:53 1992 -0700
+ * Solaris From user Wed Dec 2 05:53:22 1992 -0700
+ *
+ * Plus all of the above with `` remote from xxx'' after it. Thank you very
+ * much, smail and Solaris, for making my life considerably more complicated.
+ */
+
+/*
+ * What? You want to understand the VALID macro anyway? Alright, since you
+ * insist. Actually, it isn't really all that difficult, provided that you
+ * take it step by step.
+ *
+ * Line 1 Initializes the return ti value to failure (0);
+ * Lines 2-3 Validates that the 1st-5th characters are ``From ''.
+ * Lines 4-6 Validates that there is an end of line and points x at it.
+ * Lines 7-14 First checks to see if the line is at least 41 characters long.
+ * If so, it scans backwards to find the rightmost space. From
+ * that point, it scans backwards to see if the string matches
+ * `` remote from''. If so, it sets x to point to the space at
+ * the start of the string.
+ * Line 15 Makes sure that there are at least 27 characters in the line.
+ * Lines 16-21 Checks if the date/time ends with the year (there is a space
+ * five characters back). If there is a colon three characters
+ * further back, there is no timezone field, so zn is set to 0
+ * and ti is set in front of the year. Otherwise, there must
+ * either to be a space four characters back for a three-letter
+ * timezone, or a space six characters back followed by a + or -
+ * for a numeric timezone; in either case, zn and ti become the
+ * offset of the space immediately before it.
+ * Lines 22-24 Are the failure case for line 14. If there is a space four
+ * characters back, it is a three-letter timezone; there must be a
+ * space for the year nine characters back. zn is the zone
+ * offset; ti is the offset of the space.
+ * Lines 25-28 Are the failure case for line 20. If there is a space six
+ * characters back, it is a numeric timezone; there must be a
+ * space eleven characters back and a + or - five characters back.
+ * zn is the zone offset; ti is the offset of the space.
+ * Line 29-32 If ti is valid, make sure that the string before ti is of the
+ * form www mmm dd hh:mm or www mmm dd hh:mm:ss, otherwise
+ * invalidate ti. There must be a colon three characters back
+ * and a space six or nine characters back (depending upon
+ * whether or not the character six characters back is a colon).
+ * There must be a space three characters further back (in front
+ * of the day), one seven characters back (in front of the month),
+ * and one eleven characters back (in front of the day of week).
+ * ti is set to be the offset of the space before the time.
+ *
+ * Why a macro? It gets invoked a *lot* in a tight loop. On some of the
+ * newer pipelined machines it is faster being open-coded than it would be if
+ * subroutines are called.
+ *
+ * Why does it scan backwards from the end of the line, instead of doing the
+ * much easier forward scan? There is no deterministic way to parse the
+ * ``user'' field, because it may contain unquoted spaces! Yes, I tested it to
+ * see if unquoted spaces were possible. They are, and I've encountered enough
+ * evil mail to be totally unwilling to trust that ``it will never happen''.
+ */
+
+/* Build parameters */
+
+#define KODRETRY 15 /* kiss-of-death retry in seconds */
+#define LOCKTIMEOUT 5 /* lock timeout in minutes */
diff --git a/imap/src/osdep/nt/write.c b/imap/src/osdep/nt/write.c
new file mode 100644
index 00000000..c7854815
--- /dev/null
+++ b/imap/src/osdep/nt/write.c
@@ -0,0 +1,59 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Write data, treating partial writes as an error
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 26 May 1995
+ * Last Edited: 30 August 2006
+ */
+
+/* The whole purpose of this unfortunate routine is to deal with DOS and
+ * certain cretinous versions of UNIX which decided that the "bytes actually
+ * written" return value from write() gave them license to use that for things
+ * that are really errors, such as disk quota exceeded, maximum file size
+ * exceeded, disk full, etc.
+ *
+ * BSD won't screw us this way on the local filesystem, but who knows what
+ * some NFS-mounted filesystem will do.
+ */
+
+#undef write
+
+/* Write data to file
+ * Accepts: file descriptor
+ * I/O vector structure
+ * number of vectors in structure
+ * Returns: number of bytes written if successful, -1 if failure
+ */
+
+long maxposint = (long)((((unsigned long) 1) << ((sizeof(int) * 8) - 1)) - 1);
+
+long safe_write (int fd,char *buf,long nbytes)
+{
+ long i,j;
+ if (nbytes > 0) for (i = nbytes; i; i -= j,buf += j) {
+ while (((j = write (fd,buf,(int) min (maxposint,i))) < 0) &&
+ (errno == EINTR));
+ if (j < 0) return j;
+ }
+ return nbytes;
+}
diff --git a/imap/src/osdep/nt/yunchan.c b/imap/src/osdep/nt/yunchan.c
new file mode 100644
index 00000000..8c1f6905
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.c
@@ -0,0 +1,286 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* Emulator for BSD flock() call
+ * Accepts: file descriptor
+ * operation bitmask
+ * Returns: 0 if successful, -1 if failure
+ */
+
+/* Our friends in Redmond have decided that you can not write to any segment
+ * which has a shared lock. This screws up the shared-write mailbox drivers
+ * (mbx, mtx, and tenex). As a workaround, we'll only lock the first byte of
+ * the file, meaning that you can't write that byte shared.
+ * This behavior seems to be new as of NT 4.0.
+ */
+
+int flock (int fd,int op)
+{
+ HANDLE hdl = (HANDLE) _get_osfhandle (fd);
+ DWORD flags = (op & LOCK_NB) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ OVERLAPPED offset = {NIL,NIL,0,0,NIL};
+ int ret = -1;
+ blocknotify_t bn = (blocknotify_t)
+ ((op & LOCK_NB) ? NIL : mail_parameters (NIL,GET_BLOCKNOTIFY,NIL));
+ if (hdl < 0) errno = EBADF; /* error in file descriptor */
+ else switch (op & ~LOCK_NB) { /* translate to LockFileEx() op */
+ case LOCK_EX: /* exclusive */
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ case LOCK_SH: /* shared */
+ if (!check_nt ()) return 0; /* always succeeds if not NT */
+ if (bn) (*bn) (BLOCK_FILELOCK,NIL);
+ /* bug for bug compatible with Unix */
+ UnlockFileEx (hdl,NIL,1,0,&offset);
+ /* lock the file as requested */
+ if (LockFileEx (hdl,flags,NIL,1,0,&offset)) ret = 0;
+ if (bn) (*bn) (BLOCK_NONE,NIL);
+ /* if failed */
+ if (ret) errno = (op & LOCK_NB) ? EAGAIN : EBADF;
+ break;
+ case LOCK_UN: /* unlock */
+ if (check_nt ()) UnlockFileEx (hdl,NIL,1,0,&offset);
+ ret = 0; /* always succeeds */
+ default: /* default */
+ errno = EINVAL; /* bad call */
+ break;
+ }
+ return ret;
+}
+
+/* Local storage */
+
+static char *loghdr; /* log file header string */
+static HANDLE loghdl = NIL; /* handle of event source */
+
+/* Emulator for BSD syslog() routine
+ * Accepts: priority
+ * message
+ * parameters
+ */
+
+void syslog (int priority,const char *message,...)
+{
+ va_list args;
+ LPTSTR strs[2];
+ char tmp[MAILTMPLEN]; /* callers must be careful not to pop this */
+ unsigned short etype;
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ /* default event source */
+ if (!loghdl) openlog ("c-client",LOG_PID,LOG_MAIL);
+ switch (priority) { /* translate UNIX type into NT type */
+ case LOG_ALERT:
+ etype = EVENTLOG_ERROR_TYPE;
+ break;
+ case LOG_INFO:
+ etype = EVENTLOG_INFORMATION_TYPE;
+ break;
+ default:
+ etype = EVENTLOG_WARNING_TYPE;
+ }
+ va_start (args,message); /* initialize vararg mechanism */
+ vsprintf (tmp,message,args); /* build message */
+ strs[0] = loghdr; /* write header */
+ strs[1] = tmp; /* then the message */
+ /* report the event */
+ ReportEvent (loghdl,etype,(unsigned short) priority,2000,NIL,2,0,strs,NIL);
+ va_end (args);
+}
+
+
+/* Emulator for BSD openlog() routine
+ * Accepts: identity
+ * options
+ * facility
+ */
+
+void openlog (const char *ident,int logopt,int facility)
+{
+ char tmp[MAILTMPLEN];
+ if (!check_nt ()) return; /* no-op on non-NT system */
+ if (loghdl) fatal ("Duplicate openlog()!");
+ loghdl = RegisterEventSource (NIL,ident);
+ sprintf (tmp,(logopt & LOG_PID) ? "%s[%d]" : "%s",ident,getpid ());
+ loghdr = cpystr (tmp); /* save header for later */
+}
+
+/* Copy Unix string with CRLF newlines
+ * Accepts: destination string
+ * pointer to size of destination string buffer
+ * source string
+ * length of source string
+ * Returns: length of copied string
+ */
+
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl)
+{
+ unsigned long i,j;
+ char *d = src;
+ /* count number of LF's in source string(s) */
+ for (i = srcl,j = 0; j < srcl; j++) if (*d++ == '\012') i++;
+ /* flush destination buffer if too small */
+ if (*dst && (i > *dstl)) fs_give ((void **) dst);
+ if (!*dst) { /* make a new buffer if needed */
+ *dst = (char *) fs_get ((*dstl = i) + 1);
+ if (dstl) *dstl = i; /* return new buffer length to main program */
+ }
+ d = *dst; /* destination string */
+ /* copy strings, inserting CR's before LF's */
+ while (srcl--) switch (*src) {
+ case '\015': /* unlikely carriage return */
+ *d++ = *src++; /* copy it and any succeeding linefeed */
+ if (srcl && *src == '\012') {
+ *d++ = *src++;
+ srcl--;
+ }
+ break;
+ case '\012': /* line feed? */
+ *d++ ='\015'; /* yes, prepend a CR, drop into default case */
+ default: /* ordinary chararacter */
+ *d++ = *src++; /* just copy character */
+ break;
+ }
+ *d = '\0'; /* tie off destination */
+ return d - *dst; /* return length */
+}
+
+/* Length of Unix string after unix_crlfcpy applied
+ * Accepts: source string
+ * Returns: length of string
+ */
+
+unsigned long unix_crlflen (STRING *s)
+{
+ unsigned long pos = GETPOS (s);
+ unsigned long i = SIZE (s);
+ unsigned long j = i;
+ while (j--) switch (SNX (s)) {/* search for newlines */
+ case '\015': /* unlikely carriage return */
+ if (j && (CHR (s) == '\012')) {
+ SNX (s); /* eat the line feed */
+ j--;
+ }
+ break;
+ case '\012': /* line feed? */
+ i++;
+ default: /* ordinary chararacter */
+ break;
+ }
+ SETPOS (s,pos); /* restore old position */
+ return i;
+}
+
+/* Undoubtably, I'm going to regret these two routines in the future. I
+ * regret them now. Their purpose is to work around two problems in the
+ * VC++ 6.0 C library:
+ * (1) tmpfile() creates the file in the current directory instead of a
+ * temporary directory
+ * (2) tmpfile() and fclose() think that on NT systems, it works to unlink
+ * the file while it's still open, so there's no need for the _tmpfname
+ * hook at fclose(). Unfortunately, that doesn't work in Win2K.
+ * I would be delighted to have a better alternative.
+ */
+
+#undef fclose /* use the real fclose() in close_file() */
+
+/* Substitute for Microsoft's tmpfile() that uses the real temporary directory
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+FILE *create_tempfile (void)
+{
+ FILE *ret = NIL;
+ char *s = _tempnam (getenv ("TEMP"),"msg");
+ if (s) { /* if got temporary name... */
+ /* open file, and stash name on _tmpfname */
+ if (ret = fopen (s,"w+b")) ret->_tmpfname = s;
+ else fs_give ((void **) &s);/* flush temporary string */
+ }
+ return ret;
+}
+
+
+/* Substitute for Microsoft's fclose() that always flushes _tmpfname
+ * Returns: FILE structure if success, NIL if failure
+ */
+
+int close_file (FILE *stream)
+{
+ int ret;
+ char *s = stream->_tmpfname;
+ stream->_tmpfname = NIL; /* just in case fclose() tries to delete it */
+ ret = fclose (stream); /* close the file */
+ if (s) { /* was there a _tmpfname? */
+ unlink (s); /* yup, delete it */
+ fs_give ((void **) &s); /* and flush the name */
+ }
+ return ret;
+}
+
+/* Get password from console
+ * Accepts: prompt
+ * Returns: password
+ */
+
+#define PWDLEN 128 /* used by Linux */
+
+char *getpass (const char *prompt)
+{
+ static char pwd[PWDLEN];
+ int ch,i,done;
+ fputs (prompt,stderr); /* output prompt */
+ for (i = done = 0; !done; ) switch (ch = _getch()) {
+ case 0x03: /* CTRL/C stops program */
+ _exit (1);
+ case '\b': /* BACKSPACE erase previous character */
+ if (i) pwd[--i] = '\0';
+ break;
+ case '\n': case '\r': /* CR or LF terminates string */
+ done = 1;
+ break;
+ default: /* any other character is a pwd char */
+ if (i < (PWDLEN - 1)) pwd[i++] = ch;
+ break;
+ }
+ pwd[i] = '\0'; /* tie off string with null */
+ putchar ('\n'); /* echo newline */
+ return pwd;
+}
diff --git a/imap/src/osdep/nt/yunchan.h b/imap/src/osdep/nt/yunchan.h
new file mode 100644
index 00000000..25d60568
--- /dev/null
+++ b/imap/src/osdep/nt/yunchan.h
@@ -0,0 +1,86 @@
+/* ========================================================================
+ * Copyright 1988-2006 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: Unix compatibility routines
+ *
+ * Author: Mark Crispin
+ * Networks and Distributed Computing
+ * Computing & Communications
+ * University of Washington
+ * Administration Building, AG-44
+ * Seattle, WA 98195
+ * Internet: MRC@CAC.Washington.EDU
+ *
+ * Date: 14 September 1996
+ * Last Edited: 30 August 2006
+ */
+
+
+/* DEDICATION
+ *
+ * This file is dedicated to my dog, Unix, also known as Yun-chan and
+ * Unix J. Terwilliker Jehosophat Aloysius Monstrosity Animal Beast. Unix
+ * passed away at the age of 11 1/2 on September 14, 1996, 12:18 PM PDT, after
+ * a two-month bout with cirrhosis of the liver.
+ *
+ * He was a dear friend, and I miss him terribly.
+ *
+ * Lift a leg, Yunie. Luv ya forever!!!!
+ */
+
+/* For flock() emulation */
+
+#define LOCK_SH 1
+#define LOCK_EX 2
+#define LOCK_NB 4
+#define LOCK_UN 8
+
+
+/* syslog() emulation */
+
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but signification condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until syslog() is called */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* if forking to log on console, don't wait() */
+
+#define tmpfile create_tempfile
+#define fclose close_file
+#define fsync _commit
+#define ftruncate chsize
+#define gethostid clock
+#define sleep(x) Sleep (1000 * x)
+
+
+long alarm (long seconds);
+int flock (int fd,int op);
+void openlog (const char *ident,int logopt,int facility);
+void syslog (int priority,const char *message,...);
+unsigned long unix_crlfcpy (char **dst,unsigned long *dstl,char *src,
+ unsigned long srcl);
+unsigned long unix_crlflen (STRING *s);
+FILE *create_tempfile (void);
+int close_file (FILE *stream);
+char *getpass (const char *prompt);