summaryrefslogtreecommitdiff
path: root/imap/src/tmail
diff options
context:
space:
mode:
Diffstat (limited to 'imap/src/tmail')
-rw-r--r--imap/src/tmail/Makefile53
-rw-r--r--imap/src/tmail/tmail.1205
-rw-r--r--imap/src/tmail/tmail.c800
-rw-r--r--imap/src/tmail/tquota.c45
-rw-r--r--imap/src/tmail/tquota.h32
5 files changed, 1135 insertions, 0 deletions
diff --git a/imap/src/tmail/Makefile b/imap/src/tmail/Makefile
new file mode 100644
index 00000000..ce221430
--- /dev/null
+++ b/imap/src/tmail/Makefile
@@ -0,0 +1,53 @@
+# ========================================================================
+# 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: tmail Makefile
+#
+# 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: 5 April 1993
+# Last Edited: 10 September 2007
+
+
+C = ../c-client
+CCLIENTLIB = $C/c-client.a
+SHELL = /bin/sh
+
+# Get local definitions from c-client directory
+
+CC = `cat $C/CCTYPE`
+CFLAGS = -I$C `cat $C/CFLAGS`
+LDFLAGS = $(CCLIENTLIB) `cat $C/LDFLAGS`
+
+tmail: $(CCLIENTLIB) tmail.o tquota.o
+ $(CC) $(CFLAGS) -o tmail tmail.o tquota.o $(LDFLAGS)
+
+tmail.o: $C/mail.h $C/misc.h $C/osdep.h tquota.h
+
+tquota.o: tquota.h
+
+$(CCLIENTLIB):
+ cd $C;make
+
+clean:
+ rm -f *.o tmail
+
+# A monument to a hack of long ago and far away...
+love:
+ @echo 'not war?'
diff --git a/imap/src/tmail/tmail.1 b/imap/src/tmail/tmail.1
new file mode 100644
index 00000000..a34d4bb9
--- /dev/null
+++ b/imap/src/tmail/tmail.1
@@ -0,0 +1,205 @@
+.ig
+ * ========================================================================
+ * 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
+ *
+ *
+ * ========================================================================
+..
+.TH TMAIL 1 "September 27, 2007"
+.SH NAME
+tmail \- Mail Delivery Module
+.nh
+.SH SYNOPSIS
+.B tmail
+.I [-b format] [\-D] [-f from_name] [\-I inbox_specifier] user[+folder] ...
+.SH DESCRIPTION
+.I tmail
+delivers mail to a user's INBOX or a designated folder.
+.I tmail
+may be configured as a drop-in replacement for
+.IR binmail (1),
+.IR mail.local (1)
+or any program intended for use for mail delivery by a mail delivery program
+such as
+.IR sendmail (8).
+.PP
+.I tmail
+is intended to be used for direct delivery by the mailer daemon;
+.IR dmail (1)
+is the preferred tool for user applications, e.g. a mail delivery
+filter such as
+.IR procmail (1) .
+If
+.I tmail
+is used for a user application,
+then the calling program must be aware of the restrictions noted below.
+.PP
+When
+.I tmail
+exits, it returns exit status values to enable the mail delivery program
+to determine whether a message was delivered successfully or had a
+temporary (requeue for later delivery) or permanent (return to sender)
+failure.
+.PP
+If the
+.I +folder
+extension is included in the user argument,
+.I tmail
+will attempt to deliver to the designated folder. If the folder does not
+exist or the extension is not included, the message is delivered to the
+user's INBOX.
+If delivery is to INBOX and no INBOX currently exists,
+.I tmail
+will create a new INBOX, using the \fB-I\fR or \fB-b\fR flag if specified.
+.I tmail
+recognizes the format of an existing INBOX or folder, and appends the new
+message in that format.
+.PP
+The \fB-b\fR flag specifies a format to create INBOX if INBOX does not
+already exist. This flag requires privileges, and can not be used with
+\fB-I\fR. The argument is
+a format name such as mix, mbx, etc.
+.PP
+The \fB-D\fR flag specifies debugging; this enables additional message
+telemetry.
+.PP
+The \fB-f\fR or \fB-r\fR flag is used by
+the mail delivery program to specify a Return-Path. The header
+.br
+ Return-Path: <\fIfrom_name\fR>
+.br
+is prepended to the message before delivery.
+.PP
+The \fB-I\fR flag is used by the mail delivery program
+to specify an alternative INBOX name. This flag requires privileges,
+and can not be used with \fB-b\fR. This affects the location and format
+of INBOX. If specified, it should be in one of three forms:
+.sp
+The first form of argument to \fB-I\fR is the string "INBOX", which
+means to write to the system default inbox using the system default
+mailbox format. These system defaults are defined when the c-client
+library is built.
+.sp
+The second form of argument to \fB-I\fR is a delivery specification,
+consisting of "#driver.", a c-client mailbox format driver name, "/",
+and a file name. This will write to the specified file in the
+specified format. For example, #driver.mbx/INBOX will write to file
+"INBOX" in the home directory in mbx format; and
+#driver.unix/mail/incoming will write to file "incoming" in the
+user's "mail" subdirectory in unix (default UNIX) format.
+.sp
+The third form of argument to \fB-I\fR is any other name. Normally,
+this will write to the specified file on the user's home directory in
+the specified format. However, certain names are special. These are:
+.PP
+.nf
+ value equivalant to
+ ----- -------------
+ INBOX.MTX #driver.mtx/INBOX.MTX
+ mbox #driver.unix/mbox
+ mail.txt #driver.tenex/mail.txt
+.fi
+.PP
+If \fB-I\fR is not specified, the default action is \fB-I INBOX\fR.
+.PP
+If multiple recipients are specified on the command line,
+.I tmail
+spawns one child process per recipient to perform actual delivery. This
+way of calling
+.I tmail
+is not recommended; see below under
+.IR RESTRICTIONS .
+.SH INSTALLATION
+If
+.I tmail
+is to be used for mail delivery from the mail delivery program, it
+.I must
+be installed setuid root.
+.sp
+If sendmail is the mail delivery program,
+.I tmail
+is invoked from sendmail.cf. Look for the "Mlocal" line, and substitute
+the path name for the
+.I tmail
+binary in place of /bin/mail, /usr/lib/mail.local, etc. You should also
+add the flag to invoke
+.I tmail
+with CRLF style newlines; this is usually done with E=\\r\\n in the Mlocal
+line.
+.sp
+Here is an example of an Mlocal line in sendmail version 8:
+.sp
+.nf
+Mlocal, P=/usr/local/etc/tmail, F=lsDFMAw5:/|@qPrn+,
+ S=10/30, R=20/40, E=\\r\\n, T=DNS/RFC822/X-Unix,
+ A=tmail $u
+.fi
+.PP
+If
+.I tmail
+is to be called with the \fB-I\fR flag, it must be invoked with both
+real and effective UID root. Many sendmail configurations invoke the
+local mailer as the sending user when that user is local, which
+will prevent \fB-b\fR or \fB-I\fR from working.
+.SH SECURITY CONSIDERATIONS
+If
+.I tmail
+is invoked by an ordinary user, the Received: header line will
+indicate the name or UID of the user that invoked it.
+.PP
+Ordinary users are not permitted to use the \fB-b\fR or \fB-I\fR flag since
+otherwise a user could create any file on another user's directory.
+.PP
+.I tmail
+can deliver mail to home directories. In addition,
+.I tmail
+can be used to deliver mail to other mail folders in a home directory
+or an inferior directory of a home directory.
+.SH RESTRICTIONS
+The calling program should invoke
+.I tmail
+with CRLF newlines, otherwise
+.I tmail
+will complain in syslog.
+.PP
+Absolute pathnames and
+.I ~user
+specifications are not permitted in
+.I +folder
+extensions.
+.PP
+Ordinary users are not permitted to use the \fB-I\fR flag.
+.PP
+IMAP4 namespace names are not yet supported in
+.I +folder
+extensions.
+.PP
+It is not possible to use
+.I tmail
+to deliver to
+.IR mh (1)
+format mailboxes.
+.PP
+If delivery to multiple users is specified and delivery to any single user
+fails, the entire delivery will be reported as having failed, even though
+delivery to other users may have succeeded. If
+.I tmail
+is used for mail delivery from
+.IR sendmail (8),
+a separate tmail invocation should be done for each user. Otherwise a
+delivery failure for a single user in a message going to multiple users
+will cause multiple deliveries to all the other users every time
+.IR sendmail (8),
+retries.
+.SH AUTHOR
+Mark Crispin, MRC@CAC.Washington.EDU
+.SH "SEE ALSO"
+binmail(1)
+.br
+sendmail(8)
diff --git a/imap/src/tmail/tmail.c b/imap/src/tmail/tmail.c
new file mode 100644
index 00000000..ed5fc589
--- /dev/null
+++ b/imap/src/tmail/tmail.c
@@ -0,0 +1,800 @@
+/* ========================================================================
+ * 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: Mail Delivery 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: 5 April 1993
+ * Last Edited: 30 October 2008
+ */
+
+#include <stdio.h>
+#include <pwd.h>
+#include <errno.h>
+extern int errno; /* just in case */
+#include <sysexits.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include "c-client.h"
+#include "tquota.h"
+
+
+/* Globals */
+
+char *version = "22"; /* tmail edit version */
+int debug = NIL; /* debugging (don't fork) */
+int trycreate = NIL; /* flag saying gotta create before appending */
+int critical = NIL; /* flag saying in critical code */
+char *sender = NIL; /* message origin */
+char *inbox = NIL; /* inbox file */
+long precedence = 0; /* delivery precedence - used by quota hook */
+DRIVER *format = NIL; /* desired format */
+
+
+/* Function prototypes */
+
+void file_string_init (STRING *s,void *data,unsigned long size);
+char file_string_next (STRING *s);
+void file_string_setpos (STRING *s,unsigned long i);
+int main (int argc,char *argv[]);
+int deliver (FILE *f,unsigned long msglen,char *user);
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp);
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
+int fail (char *string,int code);
+char *getusername (char *s,char **t);
+
+
+/* File string driver for file stringstructs */
+
+STRINGDRIVER file_string = {
+ file_string_init, /* initialize string structure */
+ file_string_next, /* get next byte in string structure */
+ file_string_setpos /* set position in string structure */
+};
+
+
+/* Cache buffer for file stringstructs */
+
+#define CHUNKLEN 16384
+char chunk[CHUNKLEN];
+
+/* Initialize file string structure for file stringstruct
+ * Accepts: string structure
+ * pointer to string
+ * size of string
+ */
+
+void file_string_init (STRING *s,void *data,unsigned long size)
+{
+ s->data = data; /* note fd */
+ s->size = size; /* note size */
+ s->chunk = chunk;
+ s->chunksize = (unsigned long) CHUNKLEN;
+ SETPOS (s,0); /* set initial position */
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+
+char file_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 file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+
+void file_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 */
+ fseek ((FILE *) s->data,s->offset,SEEK_SET);
+ fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
+ }
+}
+
+/* Main program */
+
+int main (int argc,char *argv[])
+{
+ FILE *f = NIL;
+ int pid,c,ret = 0;
+ unsigned long msglen,status = 0;
+ char *s,tmp[MAILTMPLEN];
+ uid_t ruid = getuid ();
+ struct passwd *pwd;
+ openlog ("tmail",LOG_PID,LOG_MAIL);
+#include "linkage.c"
+ /* make sure have some arguments */
+ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
+ /* process all flags */
+ while (argc && (*(s = *++argv)) == '-') {
+ argc--; /* gobble this argument */
+ switch (s[1]) { /* what is this flag? */
+ case 'D': /* debug */
+ debug = T; /* don't fork */
+ break;
+ case 'I': /* inbox specifier */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (argc--) inbox = cpystr (*++argv);
+ else _exit (fail ("missing argument to -I",EX_USAGE));
+ break;
+ case 'f': /* new name for this flag */
+ case 'r': /* flag giving return path */
+ if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE));
+ if (argc--) sender = cpystr (*++argv);
+ else _exit (fail ("missing argument to -f or -r",EX_USAGE));
+ break;
+ case 'b': /* create INBOX in this format */
+ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
+ if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE));
+ if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv)))
+ _exit (fail ("unknown format to -b",EX_USAGE));
+ else if (!(format->flags & DR_LOCAL) ||
+ !compare_cstring (format->name,"dummy"))
+ _exit (fail ("invalid format to -b",EX_USAGE));
+ break;
+ /* following flags are undocumented */
+ case 'p': /* precedence for quota */
+ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
+ else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
+ precedence = atol (s);
+ else _exit (fail ("missing argument to -p",EX_USAGE));
+ break;
+ case 'd': /* obsolete flag meaning multiple users */
+ break; /* ignore silently */
+ /* -s has been deprecated and replaced by the -s and -k flags in dmail.
+ * dmail's -k flag does what -s once did in tmail; dmail's -s flag
+ * takes no argument and just sets \Seen. Flag setting is more properly
+ * done in dmail which runs as the user and is clearly at the user's
+ * behest. Since tmail runs privileged, -s would have to be disabled
+ * unless the caller is also privileged.
+ */
+ case 's': /* obsolete flag meaning delivery flags */
+ if (!argc--) /* takes an argument */
+ _exit (fail ("missing argument to deprecated flag",EX_USAGE));
+ syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv);
+ break;
+ default: /* anything else */
+ _exit (fail ("unknown switch",EX_USAGE));
+ }
+ }
+
+ if (!argc) ret = fail ("no recipients",EX_USAGE);
+ else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
+ else { /* build delivery headers */
+ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
+ /* start Received line: */
+ fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version);
+ /* not root or daemon? */
+ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
+ pwd = getpwuid (ruid); /* get unprivileged user's information */
+ if (inbox || format) {
+ if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
+ else sprintf (tmp,"UID %ld",(long) ruid);
+ strcat (tmp," is not privileged to use -b or -I");
+ _exit (fail (tmp,EX_USAGE));
+ }
+ fputs (" (invoked by ",f);
+ if (pwd) fprintf (f,"user %s",pwd->pw_name);
+ else fprintf (f,"UID %ld",(long) ruid);
+ fputs (")",f);
+ }
+ /* write "for" if single recipient */
+ if (argc == 1) fprintf (f," for %s",*argv);
+ fputs ("; ",f);
+ rfc822_date (tmp);
+ fputs (tmp,f);
+ fputs ("\015\012",f);
+ /* copy text from standard input */
+ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
+ (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
+ if (s[-1] == '\015') { /* nuke leading "From " line */
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
+ while ((c = getchar ()) != EOF) putc (c,f);
+ }
+ else {
+ mm_log ("tmail called with LF-only newlines",WARN);
+ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
+ (tmp[3] != 'm') || (tmp[4] != ' ')) {
+ *s++ = '\015'; /* overwrite NL with CRLF */
+ *s++ = '\012';
+ *s = '\0'; /* tie off string */
+ fputs (tmp,f); /* write line */
+ }
+ /* copy text from standard input */
+ while ((c = getchar ()) != EOF) {
+ /* add CR if needed */
+ if (c == '\012') putc ('\015',f);
+ putc (c,f);
+ }
+ }
+ msglen = ftell (f); /* size of message */
+ fflush (f); /* make sure all changes written out */
+
+ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
+ else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
+ /* single delivery */
+ else if (argc == 1) ret = deliver (f,msglen,*argv);
+ else do { /* multiple delivery uses daughter forks */
+ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
+ else if (pid) { /* mother process */
+ grim_pid_reap_status (pid,NIL,(void *) status);
+ /* normal termination? */
+ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
+ }
+ /* daughter process */
+ else _exit (deliver (f,msglen,*argv));
+ } while (--argc && *argv++);
+ mm_dlog (ret ? "error in delivery" : "all recipients delivered");
+ }
+ if (f) fclose (f); /* all done with temporary file */
+ _exit (ret); /* normal exit */
+ return 0; /* stupid gcc */
+}
+
+/* Deliver message to recipient list
+ * Accepts: file description of message temporary file
+ * size of message temporary file in bytes
+ * recipient name
+ * Returns: NIL if success, else error code
+ */
+
+int deliver (FILE *f,unsigned long msglen,char *user)
+{
+ MAILSTREAM *ds = NIL;
+ char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
+ struct passwd *pwd;
+ STRING st;
+ struct stat sbuf;
+ uid_t duid;
+ uid_t euid = geteuid ();
+ /* get user record */
+ if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
+ sprintf (tmp,"no such user as %.80s",user);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* absurd is absurd */
+ if (mailbox && (strlen (mailbox) > 256))
+ return fail ("absurd folder name",EX_NOUSER);
+ /* big security hole if this is allowed */
+ if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
+ /* log in as user if different than euid */
+ if ((duid != euid) && !loginpw (pwd,1,&user)) {
+ sprintf (tmp,"unable to log in UID %ld from UID %ld",
+ (long) duid,(long) euid);
+ return fail (tmp,EX_NOUSER);
+ }
+ /* can't use pwd after this point */
+ env_init (pwd->pw_name,pwd->pw_dir);
+ sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
+ mm_dlog (tmp);
+ /* prepare stringstruct */
+ INIT (&st,file_string,(void *) f,msglen);
+ if (mailbox) { /* non-INBOX name */
+ switch (mailbox[0]) { /* make sure a valid name */
+ default: /* other names, try to deliver if not INBOX */
+ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
+ !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
+ !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
+ case '%': case '*': /* wildcards not valid */
+ case '#': /* namespace name not valid */
+ case '/': /* absolute path names not valid */
+ case '~': /* user names not valid */
+ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
+ mm_log (tmp,WARN);
+ break;
+ }
+ mm_dlog ("retrying delivery to INBOX");
+ SETPOS (&st,0); /* rewind stringstruct just in case */
+ }
+
+ /* -I specified and not "-I INBOX"? */
+ if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
+ ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
+ ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
+ ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
+ ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
+ DRIVER *dv = NIL;
+ /* "-I #driver.xxx/name"? */
+ if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
+ ((inbox[2] == 'r') || (inbox[2] == 'R')) &&
+ ((inbox[3] == 'i') || (inbox[3] == 'I')) &&
+ ((inbox[4] == 'v') || (inbox[4] == 'V')) &&
+ ((inbox[5] == 'e') || (inbox[5] == 'E')) &&
+ ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
+ (s = strchr (inbox+8,'/'))) {
+ *s = '\0'; /* temporarily tie off driver name */
+ if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
+ (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
+ (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
+ path[0] = '\0'; /* bad -I argument, no path resolved */
+ sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ *s = '/'; /* restore delimiter */
+ }
+ /* resolve "-I other" specification */
+ else if (mailboxfile (path,inbox) && path[0]) {
+ /* resolution succeeded, impute driver */
+ if (!strcmp (inbox,"mail.txt"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
+ else if (!strcmp (inbox,"INBOX.MTX"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
+ else if (!strcmp (inbox,"mbox"))
+ dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
+ }
+ else { /* bad -I argument */
+ path[0] = '\0'; /* no path resolved */
+ sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
+ mm_log (tmp,WARN);
+ }
+ if (*path) { /* -I successfully resolved a path? */
+ MAILSTREAM dpr;
+ dpr.dtb = dv;
+ if (dv) ds = &dpr;
+ /* supplicate to the Evil One if necessary */
+ if (lstat (path,&sbuf) && !path_create (ds,path)) {
+ /* the Evil One rejected the plea */
+ sprintf (tmp,"Unable to create %.80s, -I ignored",path);
+ mm_log (tmp,WARN);
+ }
+ /* now attempt delivery */
+ else return deliver_safely (ds,&st,inbox,path,duid,tmp);
+ }
+ }
+
+ /* no -I, resolve "INBOX" into path */
+ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
+ /* clear box, get generic INBOX prototype */
+ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* standard system driver? */
+ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
+ strcpy (path,sysinbox ());/* use system INBOX */
+ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ else { /* other driver, try ~/INBOX */
+ if ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
+ !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
+ }
+ }
+ /* not dummy, deliver to driver imputed path */
+ if (strcmp (ds->dtb->name,"dummy"))
+ return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
+ deliver_safely (ds,&st,mailbox,path,duid,tmp) :
+ fail ("unable to resolve INBOX path",EX_CANTCREAT);
+ /* dummy, empty imputed append path exist? */
+ if (ibxpath (ds = default_proto (T),&mailbox,path) &&
+ !lstat (path,&sbuf) && !sbuf.st_size)
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ /* impute path that we will create */
+ if (!ibxpath (ds = format ? (format->open) (NIL) : default_proto (NIL),
+ &mailbox,path))
+ return fail ("unable to resolve INBOX",EX_CANTCREAT);
+ }
+ /* black box, must create, get create proto */
+ else if (lstat (path,&sbuf)) ds = default_proto (NIL);
+ else { /* black box, existing file */
+ /* empty file, get append prototype */
+ if (!sbuf.st_size) ds = default_proto (T);
+ /* non-empty, get prototype from its data */
+ else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
+ fatal ("no INBOX prototype");
+ /* error if unknown format */
+ if (!strcmp (ds->dtb->name,"phile"))
+ return fail ("unknown format INBOX",EX_UNAVAILABLE);
+ /* otherwise can deliver to it */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+ }
+ sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
+ mm_dlog (tmp);
+ /* supplicate to the Evil One */
+ if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
+ sprintf (tmp,"created %.80s",path);
+ mm_dlog (tmp);
+ /* deliver the message */
+ return deliver_safely (ds,&st,mailbox,path,duid,tmp);
+}
+
+/* Resolve INBOX from driver prototype into mailbox name and filesystem path
+ * Accepts: driver prototype
+ * pointer to mailbox name string pointer
+ * buffer to return mailbox path
+ * Returns: T if success, NIL if error
+ */
+
+long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
+{
+ char *s,tmp[MAILTMPLEN];
+ long ret = T;
+ if (!ds) ret = NIL;
+ else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
+ strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
+ else if (!strcmp (ds->dtb->name,"tenex"))
+ ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mtx"))
+ ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
+ else if (!strcmp (ds->dtb->name,"mbox"))
+ ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
+ /* better not be a namespace driver */
+ else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
+ /* INBOX in home directory */
+ else ret = ((mailboxfile (path,"&&&&&") == path) &&
+ (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
+ if (ret) { /* don't bother if lossage */
+ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
+ *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
+ }
+ return ret;
+}
+
+/* Deliver safely
+ * Accepts: prototype stream to force mailbox format
+ * stringstruct of message temporary file
+ * mailbox name
+ * filesystem path name
+ * user id
+ * scratch buffer for messages
+ * Returns: NIL if success, else error code
+ */
+
+int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
+ uid_t uid,char *tmp)
+{
+ struct stat sbuf;
+ int i = delivery_unsafe (path,uid,&sbuf,tmp);
+ if (i) return i; /* give up now if delivery unsafe */
+ /* directory, not file */
+ if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
+ if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
+ sprintf (tmp,"WARNING: directory %.80s is listable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ else { /* file, not directory */
+ if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
+ sprintf (tmp,"WARNING: multiple links to file %.80s",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is executable",path);
+ mm_log (tmp,WARN);
+ }
+ }
+ if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
+ mm_log (tmp,WARN);
+ }
+ if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
+ sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
+ mm_log (tmp,WARN);
+ }
+ /* check site-written quota procedure */
+ if (!tmail_quota (st,path,uid,tmp,sender,precedence)) return fail (tmp,-1);
+ /* so far, so good */
+ sprintf (tmp,"%s appending to %.80s (%s %.80s)",
+ prt ? prt->dtb->name : "default",mailbox,
+ ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
+ mm_dlog (tmp);
+ /* do the append now! */
+ if (!mail_append (prt,mailbox,st)) {
+ sprintf (tmp,"message delivery failed to %.80s",path);
+ return fail (tmp,EX_CANTCREAT);
+ }
+ /* note success */
+ sprintf (tmp,"delivered to %.80s",path);
+ mm_log (tmp,NIL);
+ /* make sure nothing evil this way comes */
+ return delivery_unsafe (path,uid,&sbuf,tmp);
+}
+
+/* Verify that delivery is safe
+ * Accepts: path name
+ * user id
+ * stat buffer
+ * scratch buffer for messages
+ * Returns: NIL if delivery is safe, error code if unsafe
+ */
+
+int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
+{
+ u_short type;
+ sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
+ mm_dlog (tmp);
+ /* prepare message just in case */
+ sprintf (tmp,"delivery to %.80s unsafe: ",path);
+ /* unsafe if can't get its status */
+ if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
+ else if (sbuf->st_uid != uid) /* unsafe if UID does not match */
+ sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
+ (long) sbuf->st_uid,(long) uid);
+ /* check file type */
+ else switch (sbuf->st_mode & S_IFMT) {
+ case S_IFDIR: /* directory is always OK */
+ return NIL;
+ case S_IFREG: /* file is unsafe if setuid */
+ if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
+ /* or setgid */
+ else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
+ else return NIL; /* otherwise safe */
+ break;
+ case S_IFCHR: strcat (tmp,"character special"); break;
+ case S_IFBLK: strcat (tmp,"block special"); break;
+ case S_IFLNK: strcat (tmp,"symbolic link"); break;
+ case S_IFSOCK: strcat (tmp,"socket"); break;
+ default:
+ sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
+ }
+ return fail (tmp,EX_CANTCREAT);
+}
+
+/* Report an error
+ * Accepts: string to output
+ */
+
+int fail (char *string,int code)
+{
+ mm_log (string,ERROR); /* pass up the string */
+ switch (code) {
+#if T
+ case EX_USAGE:
+ case EX_OSERR:
+ case EX_SOFTWARE:
+ case EX_NOUSER:
+ case EX_CANTCREAT:
+ case EX_UNAVAILABLE:
+ code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
+#endif
+ break;
+ case -1: /* quota failure... */
+ code = EX_CANTCREAT; /* ...really returns this code */
+ break;
+ default:
+ break;
+ }
+ return code; /* error code to return */
+}
+
+
+/* Get user name from username+mailbox specifier
+ * Accepts: username/mailbox specifier
+ * pointer to return location for mailbox specifier
+ * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
+ */
+
+char *getusername (char *s,char **t)
+{
+ if (*t = strchr (s,'+')) { /* have a mailbox specifier? */
+ *(*t)++ = '\0'; /* yes, tie off user name */
+ /* user+ and user+INBOX same as user */
+ if (!**t || !compare_cstring ((unsigned char *) *t,"INBOX")) *t = NIL;
+ }
+ return s; /* return user name */
+}
+
+/* Co-routines from MAIL library */
+
+
+/* Message matches a search
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_searched (MAILSTREAM *stream,unsigned long msgno)
+{
+ fatal ("mm_searched() call");
+}
+
+
+/* Message exists (i.e. there are that many messages in the mailbox)
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_exists (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_exists() call");
+}
+
+
+/* Message expunged
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_expunged (MAILSTREAM *stream,unsigned long number)
+{
+ fatal ("mm_expunged() call");
+}
+
+
+/* Message flags update seen
+ * Accepts: MAIL stream
+ * message number
+ */
+
+void mm_flags (MAILSTREAM *stream,unsigned long number)
+{
+}
+
+/* Mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_list() call");
+}
+
+
+/* Subscribed mailbox found
+ * Accepts: MAIL stream
+ * delimiter
+ * mailbox name
+ * mailbox attributes
+ */
+
+void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
+{
+ fatal ("mm_lsub() call");
+}
+
+
+/* Mailbox status
+ * Accepts: MAIL stream
+ * mailbox name
+ * mailbox status
+ */
+
+void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
+{
+ fatal ("mm_status() call");
+}
+
+/* Notification event
+ * Accepts: MAIL stream
+ * string to log
+ * error flag
+ */
+
+void mm_notify (MAILSTREAM *stream,char *string,long errflg)
+{
+ char tmp[MAILTMPLEN];
+ tmp[11] = '\0'; /* see if TRYCREATE */
+ if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
+ mm_log (string,errflg); /* just do mm_log action */
+}
+
+
+/* Log an event for the user to see
+ * Accepts: string to log
+ * error flag
+ */
+
+void mm_log (char *string,long errflg)
+{
+ if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
+ else { /* ordinary logging */
+ fprintf (stderr,"%s\n",string);
+ switch (errflg) {
+ case NIL: /* no error */
+ syslog (LOG_INFO,"%s",string);
+ break;
+ case PARSE: /* parsing problem */
+ case WARN: /* warning */
+ syslog (LOG_WARNING,"%s",string);
+ break;
+ case ERROR: /* error */
+ default:
+ syslog (LOG_ERR,"%s",string);
+ break;
+ }
+ }
+}
+
+
+/* Log an event to debugging telemetry
+ * Accepts: string to log
+ */
+
+void mm_dlog (char *string)
+{
+ if (debug) fprintf (stderr,"%s\n",string);
+ syslog (LOG_DEBUG,"%s",string);
+}
+
+/* Get user name and password for this host
+ * Accepts: parse of network mailbox name
+ * where to return user name
+ * where to return password
+ * trial count
+ */
+
+void mm_login (NETMBX *mb,char *username,char *password,long trial)
+{
+ fatal ("mm_login() call");
+}
+
+
+/* About to enter critical code
+ * Accepts: stream
+ */
+
+void mm_critical (MAILSTREAM *stream)
+{
+ critical = T; /* note in critical code */
+}
+
+
+/* About to exit critical code
+ * Accepts: stream
+ */
+
+void mm_nocritical (MAILSTREAM *stream)
+{
+ critical = NIL; /* note not in critical code */
+}
+
+
+/* Disk error found
+ * Accepts: stream
+ * system error code
+ * flag indicating that mailbox may be clobbered
+ * Returns: T if user wants to abort
+ */
+
+long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
+{
+ return T;
+}
+
+
+/* Log a fatal error event
+ * Accepts: string to log
+ */
+
+void mm_fatal (char *string)
+{
+ printf ("?%s\n",string); /* shouldn't happen normally */
+}
diff --git a/imap/src/tmail/tquota.c b/imap/src/tmail/tquota.c
new file mode 100644
index 00000000..16552eb2
--- /dev/null
+++ b/imap/src/tmail/tquota.c
@@ -0,0 +1,45 @@
+/* ========================================================================
+ * 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: Mail Delivery Module Quota Hook
+ *
+ * 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: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+#include "c-client.h"
+
+/* Site-written routine to validate delivery per quota and policy
+ * Accepts: stringstruct of message temporary file
+ * filesystem path
+ * recipient user id
+ * return path
+ * buffer to write error message
+ * precedence setting
+ * Returns: T if can deliver, or NIL if quota issue and must bounce
+ */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence)
+{
+ return LONGT; /* dummy success return */
+}
diff --git a/imap/src/tmail/tquota.h b/imap/src/tmail/tquota.h
new file mode 100644
index 00000000..1cd11c01
--- /dev/null
+++ b/imap/src/tmail/tquota.h
@@ -0,0 +1,32 @@
+/* ========================================================================
+ * 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: Mail Delivery Module Quota Hook
+ *
+ * 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: 10 September 2007
+ * Last Edited: 10 September 2007
+ */
+
+/* Function prototypes */
+
+long tmail_quota (STRING *msg,char *path,uid_t uid,char *tmp,char *sender,
+ long precedence);