imapext-2007

diff src/tmail/tmail.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/tmail/tmail.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,800 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2007 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	Mail Delivery Module
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		Networks and Distributed Computing
    1.22 + *		Computing & Communications
    1.23 + *		University of Washington
    1.24 + *		Administration Building, AG-44
    1.25 + *		Seattle, WA  98195
    1.26 + *		Internet: MRC@CAC.Washington.EDU
    1.27 + *
    1.28 + * Date:	5 April 1993
    1.29 + * Last Edited:	30 October 2008
    1.30 + */
    1.31 +
    1.32 +#include <stdio.h>
    1.33 +#include <pwd.h>
    1.34 +#include <errno.h>
    1.35 +extern int errno;		/* just in case */
    1.36 +#include <sysexits.h>
    1.37 +#include <sys/file.h>
    1.38 +#include <sys/stat.h>
    1.39 +#include "c-client.h"
    1.40 +#include "tquota.h"
    1.41 +
    1.42 +
    1.43 +/* Globals */
    1.44 +
    1.45 +char *version = "22";		/* tmail edit version */
    1.46 +int debug = NIL;		/* debugging (don't fork) */
    1.47 +int trycreate = NIL;		/* flag saying gotta create before appending */
    1.48 +int critical = NIL;		/* flag saying in critical code */
    1.49 +char *sender = NIL;		/* message origin */
    1.50 +char *inbox = NIL;		/* inbox file */
    1.51 +long precedence = 0;		/* delivery precedence - used by quota hook */
    1.52 +DRIVER *format = NIL;		/* desired format */
    1.53 +
    1.54 +
    1.55 +/* Function prototypes */
    1.56 +
    1.57 +void file_string_init (STRING *s,void *data,unsigned long size);
    1.58 +char file_string_next (STRING *s);
    1.59 +void file_string_setpos (STRING *s,unsigned long i);
    1.60 +int main (int argc,char *argv[]);
    1.61 +int deliver (FILE *f,unsigned long msglen,char *user);
    1.62 +long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
    1.63 +int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
    1.64 +		    uid_t uid,char *tmp);
    1.65 +int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
    1.66 +int fail (char *string,int code);
    1.67 +char *getusername (char *s,char **t);
    1.68 +
    1.69 +
    1.70 +/* File string driver for file stringstructs */
    1.71 +
    1.72 +STRINGDRIVER file_string = {
    1.73 +  file_string_init,		/* initialize string structure */
    1.74 +  file_string_next,		/* get next byte in string structure */
    1.75 +  file_string_setpos		/* set position in string structure */
    1.76 +};
    1.77 +
    1.78 +
    1.79 +/* Cache buffer for file stringstructs */
    1.80 +
    1.81 +#define CHUNKLEN 16384
    1.82 +char chunk[CHUNKLEN];
    1.83 +
    1.84 +/* Initialize file string structure for file stringstruct
    1.85 + * Accepts: string structure
    1.86 + *	    pointer to string
    1.87 + *	    size of string
    1.88 + */
    1.89 +
    1.90 +void file_string_init (STRING *s,void *data,unsigned long size)
    1.91 +{
    1.92 +  s->data = data;		/* note fd */
    1.93 +  s->size = size;		/* note size */
    1.94 +  s->chunk = chunk;
    1.95 +  s->chunksize = (unsigned long) CHUNKLEN;
    1.96 +  SETPOS (s,0);			/* set initial position */
    1.97 +}
    1.98 +
    1.99 +
   1.100 +/* Get next character from file stringstruct
   1.101 + * Accepts: string structure
   1.102 + * Returns: character, string structure chunk refreshed
   1.103 + */
   1.104 +
   1.105 +char file_string_next (STRING *s)
   1.106 +{
   1.107 +  char c = *s->curpos++;	/* get next byte */
   1.108 +  SETPOS (s,GETPOS (s));	/* move to next chunk */
   1.109 +  return c;			/* return the byte */
   1.110 +}
   1.111 +
   1.112 +
   1.113 +/* Set string pointer position for file stringstruct
   1.114 + * Accepts: string structure
   1.115 + *	    new position
   1.116 + */
   1.117 +
   1.118 +void file_string_setpos (STRING *s,unsigned long i)
   1.119 +{
   1.120 +  if (i > s->size) i = s->size;	/* don't permit setting beyond EOF */
   1.121 +  s->offset = i;		/* set new offset */
   1.122 +  s->curpos = s->chunk;		/* reset position */
   1.123 +				/* set size of data */
   1.124 +  if (s->cursize = min (s->chunksize,SIZE (s))) {
   1.125 +				/* move to that position in the file */
   1.126 +    fseek ((FILE *) s->data,s->offset,SEEK_SET);
   1.127 +    fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
   1.128 +  }
   1.129 +}
   1.130 +
   1.131 +/* Main program */
   1.132 +
   1.133 +int main (int argc,char *argv[])
   1.134 +{
   1.135 +  FILE *f = NIL;
   1.136 +  int pid,c,ret = 0;
   1.137 +  unsigned long msglen,status = 0;
   1.138 +  char *s,tmp[MAILTMPLEN];
   1.139 +  uid_t ruid = getuid ();
   1.140 +  struct passwd *pwd;
   1.141 +  openlog ("tmail",LOG_PID,LOG_MAIL);
   1.142 +#include "linkage.c"
   1.143 +				/* make sure have some arguments */
   1.144 +  if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
   1.145 +				/* process all flags */
   1.146 +  while (argc && (*(s = *++argv)) == '-') {
   1.147 +    argc--;			/* gobble this argument */
   1.148 +    switch (s[1]) {		/* what is this flag? */
   1.149 +    case 'D':			/* debug */
   1.150 +      debug = T;		/* don't fork */
   1.151 +      break;
   1.152 +    case 'I':			/* inbox specifier */
   1.153 +      if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
   1.154 +      if (argc--) inbox = cpystr (*++argv);
   1.155 +      else _exit (fail ("missing argument to -I",EX_USAGE));
   1.156 +      break;
   1.157 +    case 'f':			/* new name for this flag */
   1.158 +    case 'r':			/* flag giving return path */
   1.159 +      if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE));
   1.160 +      if (argc--) sender = cpystr (*++argv);
   1.161 +      else _exit (fail ("missing argument to -f or -r",EX_USAGE));
   1.162 +      break;
   1.163 +    case 'b':			/* create INBOX in this format */
   1.164 +      if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
   1.165 +      if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE));
   1.166 +      if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv)))
   1.167 +	_exit (fail ("unknown format to -b",EX_USAGE));
   1.168 +      else if (!(format->flags & DR_LOCAL) ||
   1.169 +	       !compare_cstring (format->name,"dummy"))
   1.170 +	_exit (fail ("invalid format to -b",EX_USAGE));
   1.171 +      break;
   1.172 +    /* following flags are undocumented */
   1.173 +    case 'p':			/* precedence for quota */
   1.174 +      if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
   1.175 +      else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
   1.176 +	precedence = atol (s);
   1.177 +      else _exit (fail ("missing argument to -p",EX_USAGE));
   1.178 +      break;
   1.179 +    case 'd':			/* obsolete flag meaning multiple users */
   1.180 +      break;			/* ignore silently */
   1.181 +    /* -s has been deprecated and replaced by the -s and -k flags in dmail.
   1.182 +     * dmail's -k flag does what -s once did in tmail; dmail's -s flag
   1.183 +     * takes no argument and just sets \Seen.  Flag setting is more properly
   1.184 +     * done in dmail which runs as the user and is clearly at the user's
   1.185 +     * behest.  Since tmail runs privileged, -s would have to be disabled
   1.186 +     * unless the caller is also privileged.
   1.187 +     */
   1.188 +    case 's':			/* obsolete flag meaning delivery flags */
   1.189 +      if (!argc--)		/* takes an argument */
   1.190 +	_exit (fail ("missing argument to deprecated flag",EX_USAGE));
   1.191 +      syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv);
   1.192 +      break;
   1.193 +    default:			/* anything else */
   1.194 +      _exit (fail ("unknown switch",EX_USAGE));
   1.195 +    }
   1.196 +  }
   1.197 +
   1.198 +  if (!argc) ret = fail ("no recipients",EX_USAGE);
   1.199 +  else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
   1.200 +  else {			/* build delivery headers */
   1.201 +    if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
   1.202 +				/* start Received line: */
   1.203 +    fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version);
   1.204 +				/* not root or daemon? */
   1.205 +    if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
   1.206 +      pwd = getpwuid (ruid);	/* get unprivileged user's information */
   1.207 +      if (inbox || format) {
   1.208 +	if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
   1.209 +	else sprintf (tmp,"UID %ld",(long) ruid);
   1.210 +	strcat (tmp," is not privileged to use -b or -I");
   1.211 +	_exit (fail (tmp,EX_USAGE));
   1.212 +      }
   1.213 +      fputs (" (invoked by ",f);
   1.214 +      if (pwd) fprintf (f,"user %s",pwd->pw_name);
   1.215 +      else fprintf (f,"UID %ld",(long) ruid);
   1.216 +      fputs (")",f);
   1.217 +    }
   1.218 +				/* write "for" if single recipient */
   1.219 +    if (argc == 1) fprintf (f," for %s",*argv);
   1.220 +    fputs ("; ",f);
   1.221 +    rfc822_date (tmp);
   1.222 +    fputs (tmp,f);
   1.223 +    fputs ("\015\012",f);
   1.224 +				/* copy text from standard input */
   1.225 +    if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
   1.226 +	(s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
   1.227 +    if (s[-1] == '\015') {	/* nuke leading "From " line */
   1.228 +      if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
   1.229 +	  (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
   1.230 +      while ((c = getchar ()) != EOF) putc (c,f);
   1.231 +    }
   1.232 +    else {
   1.233 +      mm_log ("tmail called with LF-only newlines",WARN);
   1.234 +      if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
   1.235 +	  (tmp[3] != 'm') || (tmp[4] != ' ')) {
   1.236 +	*s++ = '\015';		/* overwrite NL with CRLF */
   1.237 +	*s++ = '\012';
   1.238 +	*s = '\0';		/* tie off string */
   1.239 +	fputs (tmp,f);		/* write line */
   1.240 +      }
   1.241 +				/* copy text from standard input */
   1.242 +      while ((c = getchar ()) != EOF) {
   1.243 +				/* add CR if needed */
   1.244 +	if (c == '\012') putc ('\015',f);
   1.245 +	putc (c,f);
   1.246 +      }
   1.247 +    }
   1.248 +    msglen = ftell (f);		/* size of message */
   1.249 +    fflush (f);			/* make sure all changes written out */
   1.250 +
   1.251 +    if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
   1.252 +    else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
   1.253 +				/* single delivery */
   1.254 +    else if (argc == 1) ret = deliver (f,msglen,*argv);
   1.255 +    else do {			/* multiple delivery uses daughter forks */
   1.256 +      if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
   1.257 +      else if (pid) {		/* mother process */
   1.258 +	grim_pid_reap_status (pid,NIL,(void *) status);
   1.259 +				/* normal termination? */
   1.260 +	if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
   1.261 +      }
   1.262 +				/* daughter process */
   1.263 +      else _exit (deliver (f,msglen,*argv));
   1.264 +    } while (--argc && *argv++);
   1.265 +    mm_dlog (ret ? "error in delivery" : "all recipients delivered");
   1.266 +  }
   1.267 +  if (f) fclose (f);		/* all done with temporary file */
   1.268 +  _exit (ret);			/* normal exit */
   1.269 +  return 0;			/* stupid gcc */
   1.270 +}
   1.271 +
   1.272 +/* Deliver message to recipient list
   1.273 + * Accepts: file description of message temporary file
   1.274 + *	    size of message temporary file in bytes
   1.275 + *	    recipient name
   1.276 + * Returns: NIL if success, else error code
   1.277 + */
   1.278 +
   1.279 +int deliver (FILE *f,unsigned long msglen,char *user)
   1.280 +{
   1.281 +  MAILSTREAM *ds = NIL;
   1.282 +  char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
   1.283 +  struct passwd *pwd;
   1.284 +  STRING st;
   1.285 +  struct stat sbuf;
   1.286 +  uid_t duid;
   1.287 +  uid_t euid = geteuid ();
   1.288 +				/* get user record */
   1.289 +  if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
   1.290 +    sprintf (tmp,"no such user as %.80s",user);
   1.291 +    return fail (tmp,EX_NOUSER);
   1.292 +  }
   1.293 +				/* absurd is absurd */
   1.294 +  if (mailbox && (strlen (mailbox) > 256))
   1.295 +    return fail ("absurd folder name",EX_NOUSER);
   1.296 +				/* big security hole if this is allowed */
   1.297 +  if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
   1.298 +				/* log in as user if different than euid */
   1.299 +  if ((duid != euid) && !loginpw (pwd,1,&user)) {
   1.300 +    sprintf (tmp,"unable to log in UID %ld from UID %ld",
   1.301 +	     (long) duid,(long) euid);
   1.302 +    return fail (tmp,EX_NOUSER);
   1.303 +  }
   1.304 +				/* can't use pwd after this point */
   1.305 +  env_init (pwd->pw_name,pwd->pw_dir);
   1.306 +  sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
   1.307 +  mm_dlog (tmp);
   1.308 +				/* prepare stringstruct */
   1.309 +  INIT (&st,file_string,(void *) f,msglen);
   1.310 +  if (mailbox) {		/* non-INBOX name */
   1.311 +    switch (mailbox[0]) {	/* make sure a valid name */
   1.312 +    default:			/* other names, try to deliver if not INBOX */
   1.313 +      if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
   1.314 +	  !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
   1.315 +	  !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
   1.316 +    case '%': case '*':		/* wildcards not valid */
   1.317 +    case '#':			/* namespace name not valid */
   1.318 +    case '/':			/* absolute path names not valid */
   1.319 +    case '~':			/* user names not valid */
   1.320 +      sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
   1.321 +      mm_log (tmp,WARN);
   1.322 +      break;
   1.323 +    }
   1.324 +    mm_dlog ("retrying delivery to INBOX");
   1.325 +    SETPOS (&st,0);		/* rewind stringstruct just in case */
   1.326 +  }
   1.327 +
   1.328 +				/* -I specified and not "-I INBOX"? */
   1.329 +  if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
   1.330 +		 ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
   1.331 +		 ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
   1.332 +		 ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
   1.333 +		 ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
   1.334 +    DRIVER *dv = NIL;
   1.335 +				/* "-I #driver.xxx/name"? */
   1.336 +    if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
   1.337 +	((inbox[2] == 'r') || (inbox[2] == 'R')) &&
   1.338 +	((inbox[3] == 'i') || (inbox[3] == 'I')) &&
   1.339 +	((inbox[4] == 'v') || (inbox[4] == 'V')) &&
   1.340 +	((inbox[5] == 'e') || (inbox[5] == 'E')) &&
   1.341 +	((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
   1.342 +	(s = strchr (inbox+8,'/'))) {
   1.343 +      *s = '\0';		/* temporarily tie off driver name */
   1.344 +      if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
   1.345 +	    (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
   1.346 +	    (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
   1.347 +	path[0] = '\0';		/* bad -I argument, no path resolved */
   1.348 +	sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
   1.349 +	mm_log (tmp,WARN);
   1.350 +      }
   1.351 +      *s = '/';			/* restore delimiter */
   1.352 +    }
   1.353 +				/* resolve "-I other" specification */
   1.354 +    else if (mailboxfile (path,inbox) && path[0]) {
   1.355 +				/* resolution succeeded, impute driver */
   1.356 +      if (!strcmp (inbox,"mail.txt"))
   1.357 +	dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
   1.358 +      else if (!strcmp (inbox,"INBOX.MTX"))
   1.359 +	dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
   1.360 +      else if (!strcmp (inbox,"mbox"))
   1.361 +	dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
   1.362 +    }
   1.363 +    else {			/* bad -I argument */
   1.364 +      path[0] = '\0';		/* no path resolved */
   1.365 +      sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
   1.366 +      mm_log (tmp,WARN);
   1.367 +    }
   1.368 +    if (*path) {		/* -I successfully resolved a path? */
   1.369 +      MAILSTREAM dpr;
   1.370 +      dpr.dtb = dv;
   1.371 +      if (dv) ds = &dpr;
   1.372 +				/* supplicate to the Evil One if necessary */
   1.373 +      if (lstat (path,&sbuf) && !path_create (ds,path)) {
   1.374 +				/* the Evil One rejected the plea */
   1.375 +	sprintf (tmp,"Unable to create %.80s, -I ignored",path);
   1.376 +	mm_log (tmp,WARN);
   1.377 +      }
   1.378 +				/* now attempt delivery */
   1.379 +      else return deliver_safely (ds,&st,inbox,path,duid,tmp);
   1.380 +    }
   1.381 +  }
   1.382 +
   1.383 +				/* no -I, resolve "INBOX" into path */
   1.384 +  if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
   1.385 +				/* clear box, get generic INBOX prototype */
   1.386 +    if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
   1.387 +      fatal ("no INBOX prototype");
   1.388 +				/* standard system driver? */
   1.389 +    if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
   1.390 +      strcpy (path,sysinbox ());/* use system INBOX */
   1.391 +      if (!lstat (path,&sbuf))	/* deliver to existing system INBOX */
   1.392 +	return deliver_safely (ds,&st,mailbox,path,duid,tmp);
   1.393 +    }
   1.394 +    else {			/* other driver, try ~/INBOX */
   1.395 +      if ((mailboxfile (path,"&&&&&") == path) &&
   1.396 +	  (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
   1.397 +	  !lstat (path,&sbuf)){	/* deliver to existing ~/INBOX */
   1.398 +	sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
   1.399 +	return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
   1.400 +      }
   1.401 +    }
   1.402 +				/* not dummy, deliver to driver imputed path */
   1.403 +    if (strcmp (ds->dtb->name,"dummy"))
   1.404 +      return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
   1.405 +	deliver_safely (ds,&st,mailbox,path,duid,tmp) :
   1.406 +	  fail ("unable to resolve INBOX path",EX_CANTCREAT);
   1.407 +				/* dummy, empty imputed append path exist? */
   1.408 +    if (ibxpath (ds = default_proto (T),&mailbox,path) &&
   1.409 +	!lstat (path,&sbuf) && !sbuf.st_size)
   1.410 +      return deliver_safely (ds,&st,mailbox,path,duid,tmp);
   1.411 +				/* impute path that we will create */
   1.412 +    if (!ibxpath (ds = format ? (format->open) (NIL) : default_proto (NIL),
   1.413 +		  &mailbox,path))
   1.414 +      return fail ("unable to resolve INBOX",EX_CANTCREAT);
   1.415 +  }
   1.416 +				/* black box, must create, get create proto */
   1.417 +  else if (lstat (path,&sbuf)) ds = default_proto (NIL);
   1.418 +  else {			/* black box, existing file */
   1.419 +				/* empty file, get append prototype */
   1.420 +    if (!sbuf.st_size) ds = default_proto (T);
   1.421 +				/* non-empty, get prototype from its data */
   1.422 +    else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
   1.423 +      fatal ("no INBOX prototype");
   1.424 +				/* error if unknown format */
   1.425 +    if (!strcmp (ds->dtb->name,"phile"))
   1.426 +      return fail ("unknown format INBOX",EX_UNAVAILABLE);
   1.427 +				/* otherwise can deliver to it */
   1.428 +    return deliver_safely (ds,&st,mailbox,path,duid,tmp);
   1.429 +  }
   1.430 +  sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
   1.431 +  mm_dlog (tmp);
   1.432 +				/* supplicate to the Evil One */
   1.433 +  if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
   1.434 +  sprintf (tmp,"created %.80s",path);
   1.435 +  mm_dlog (tmp);
   1.436 +				/* deliver the message */
   1.437 +  return deliver_safely (ds,&st,mailbox,path,duid,tmp);
   1.438 +}
   1.439 +
   1.440 +/* Resolve INBOX from driver prototype into mailbox name and filesystem path
   1.441 + * Accepts: driver prototype
   1.442 + * 	    pointer to mailbox name string pointer
   1.443 + *	    buffer to return mailbox path
   1.444 + * Returns: T if success, NIL if error
   1.445 + */
   1.446 +
   1.447 +long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
   1.448 +{
   1.449 +  char *s,tmp[MAILTMPLEN];
   1.450 +  long ret = T;
   1.451 +  if (!ds) ret = NIL;
   1.452 +  else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
   1.453 +    strcpy (path,sysinbox ());	/* use system INBOX for unix and MMDF */
   1.454 +  else if (!strcmp (ds->dtb->name,"tenex"))
   1.455 +    ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
   1.456 +  else if (!strcmp (ds->dtb->name,"mtx"))
   1.457 +    ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
   1.458 +  else if (!strcmp (ds->dtb->name,"mbox"))
   1.459 +    ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
   1.460 +				/* better not be a namespace driver */
   1.461 +  else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
   1.462 +				/* INBOX in home directory */
   1.463 +  else ret = ((mailboxfile (path,"&&&&&") == path) &&
   1.464 +	      (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
   1.465 +  if (ret) {			/* don't bother if lossage */
   1.466 +    sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
   1.467 +    *mailbox = cpystr (tmp);	/* name of INBOX in this namespace */
   1.468 +  }
   1.469 +  return ret;
   1.470 +}
   1.471 +
   1.472 +/* Deliver safely
   1.473 + * Accepts: prototype stream to force mailbox format
   1.474 + *	    stringstruct of message temporary file
   1.475 + *	    mailbox name
   1.476 + *	    filesystem path name
   1.477 + *	    user id
   1.478 + *	    scratch buffer for messages
   1.479 + * Returns: NIL if success, else error code
   1.480 + */
   1.481 +
   1.482 +int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
   1.483 +		    uid_t uid,char *tmp)
   1.484 +{
   1.485 +  struct stat sbuf;
   1.486 +  int i = delivery_unsafe (path,uid,&sbuf,tmp);
   1.487 +  if (i) return i;		/* give up now if delivery unsafe */
   1.488 +				/* directory, not file */
   1.489 +  if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
   1.490 +    if (sbuf.st_mode & 0001) {	/* listable directories may be worrisome */
   1.491 +      sprintf (tmp,"WARNING: directory %.80s is listable",path);
   1.492 +      mm_log (tmp,WARN);
   1.493 +    }
   1.494 +  }
   1.495 +  else {			/* file, not directory */
   1.496 +    if (sbuf.st_nlink != 1) {	/* multiple links may be worrisome */
   1.497 +      sprintf (tmp,"WARNING: multiple links to file %.80s",path);
   1.498 +      mm_log (tmp,WARN);
   1.499 +    }
   1.500 +    if (sbuf.st_mode & 0111) {	/* executable files may be worrisome */
   1.501 +      sprintf (tmp,"WARNING: file %.80s is executable",path);
   1.502 +      mm_log (tmp,WARN);
   1.503 +    }
   1.504 +  }
   1.505 +  if (sbuf.st_mode & 0002) {	/* public-write files may be worrisome */
   1.506 +    sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
   1.507 +    mm_log (tmp,WARN);
   1.508 +  }
   1.509 +  if (sbuf.st_mode & 0004) {	/* public-write files may be worrisome */
   1.510 +    sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
   1.511 +    mm_log (tmp,WARN);
   1.512 +  }
   1.513 +				/* check site-written quota procedure */
   1.514 +  if (!tmail_quota (st,path,uid,tmp,sender,precedence)) return fail (tmp,-1);
   1.515 +				/* so far, so good */
   1.516 +  sprintf (tmp,"%s appending to %.80s (%s %.80s)",
   1.517 +	   prt ? prt->dtb->name : "default",mailbox,
   1.518 +	   ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
   1.519 +  mm_dlog (tmp);
   1.520 +				/* do the append now! */
   1.521 +  if (!mail_append (prt,mailbox,st)) {
   1.522 +    sprintf (tmp,"message delivery failed to %.80s",path);
   1.523 +    return fail (tmp,EX_CANTCREAT);
   1.524 +  }
   1.525 +				/* note success */
   1.526 +  sprintf (tmp,"delivered to %.80s",path);
   1.527 +  mm_log (tmp,NIL);
   1.528 +				/* make sure nothing evil this way comes */
   1.529 +  return delivery_unsafe (path,uid,&sbuf,tmp);
   1.530 +}
   1.531 +
   1.532 +/* Verify that delivery is safe
   1.533 + * Accepts: path name
   1.534 + *	    user id
   1.535 + *	    stat buffer
   1.536 + *	    scratch buffer for messages
   1.537 + * Returns: NIL if delivery is safe, error code if unsafe
   1.538 + */
   1.539 +
   1.540 +int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
   1.541 +{
   1.542 +  u_short type;
   1.543 +  sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
   1.544 +  mm_dlog (tmp);
   1.545 +				/* prepare message just in case */
   1.546 +  sprintf (tmp,"delivery to %.80s unsafe: ",path);
   1.547 +				/* unsafe if can't get its status */
   1.548 +  if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
   1.549 +  else if (sbuf->st_uid != uid)	/* unsafe if UID does not match */
   1.550 +    sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
   1.551 +	     (long) sbuf->st_uid,(long) uid);
   1.552 +				/* check file type */
   1.553 +  else switch (sbuf->st_mode & S_IFMT) {
   1.554 +  case S_IFDIR:			/* directory is always OK */
   1.555 +    return NIL;
   1.556 +  case S_IFREG:			/* file is unsafe if setuid */
   1.557 +    if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
   1.558 +				/* or setgid */
   1.559 +    else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
   1.560 +    else return NIL;		/* otherwise safe */
   1.561 +    break;
   1.562 +  case S_IFCHR: strcat (tmp,"character special"); break;
   1.563 +  case S_IFBLK: strcat (tmp,"block special"); break;
   1.564 +  case S_IFLNK: strcat (tmp,"symbolic link"); break;
   1.565 +  case S_IFSOCK: strcat (tmp,"socket"); break;
   1.566 +  default:
   1.567 +    sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
   1.568 +  }
   1.569 +  return fail (tmp,EX_CANTCREAT);
   1.570 +}
   1.571 +
   1.572 +/* Report an error
   1.573 + * Accepts: string to output
   1.574 + */
   1.575 +
   1.576 +int fail (char *string,int code)
   1.577 +{
   1.578 +  mm_log (string,ERROR);	/* pass up the string */
   1.579 +  switch (code) {
   1.580 +#if T
   1.581 +  case EX_USAGE:
   1.582 +  case EX_OSERR:
   1.583 +  case EX_SOFTWARE:
   1.584 +  case EX_NOUSER:
   1.585 +  case EX_CANTCREAT:
   1.586 +  case EX_UNAVAILABLE:
   1.587 +    code = EX_TEMPFAIL;		/* coerce these to TEMPFAIL */
   1.588 +#endif
   1.589 +    break;
   1.590 +  case -1:			/* quota failure... */
   1.591 +    code = EX_CANTCREAT;	/* ...really returns this code */
   1.592 +    break;
   1.593 +  default:
   1.594 +    break;
   1.595 +  }
   1.596 +  return code;			/* error code to return */
   1.597 +}
   1.598 +
   1.599 +
   1.600 +/* Get user name from username+mailbox specifier
   1.601 + * Accepts: username/mailbox specifier
   1.602 + *	    pointer to return location for mailbox specifier
   1.603 + * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
   1.604 + */
   1.605 +
   1.606 +char *getusername (char *s,char **t)
   1.607 +{
   1.608 +  if (*t = strchr (s,'+')) {	/* have a mailbox specifier? */
   1.609 +    *(*t)++ = '\0';		/* yes, tie off user name */
   1.610 +				/* user+ and user+INBOX same as user */
   1.611 +    if (!**t || !compare_cstring ((unsigned char *) *t,"INBOX")) *t = NIL;
   1.612 +  }
   1.613 +  return s;			/* return user name */
   1.614 +}
   1.615 +
   1.616 +/* Co-routines from MAIL library */
   1.617 +
   1.618 +
   1.619 +/* Message matches a search
   1.620 + * Accepts: MAIL stream
   1.621 + *	    message number
   1.622 + */
   1.623 +
   1.624 +void mm_searched (MAILSTREAM *stream,unsigned long msgno)
   1.625 +{
   1.626 +  fatal ("mm_searched() call");
   1.627 +}
   1.628 +
   1.629 +
   1.630 +/* Message exists (i.e. there are that many messages in the mailbox)
   1.631 + * Accepts: MAIL stream
   1.632 + *	    message number
   1.633 + */
   1.634 +
   1.635 +void mm_exists (MAILSTREAM *stream,unsigned long number)
   1.636 +{
   1.637 +  fatal ("mm_exists() call");
   1.638 +}
   1.639 +
   1.640 +
   1.641 +/* Message expunged
   1.642 + * Accepts: MAIL stream
   1.643 + *	    message number
   1.644 + */
   1.645 +
   1.646 +void mm_expunged (MAILSTREAM *stream,unsigned long number)
   1.647 +{
   1.648 +  fatal ("mm_expunged() call");
   1.649 +}
   1.650 +
   1.651 +
   1.652 +/* Message flags update seen
   1.653 + * Accepts: MAIL stream
   1.654 + *	    message number
   1.655 + */
   1.656 +
   1.657 +void mm_flags (MAILSTREAM *stream,unsigned long number)
   1.658 +{
   1.659 +}
   1.660 +
   1.661 +/* Mailbox found
   1.662 + * Accepts: MAIL stream
   1.663 + *	    delimiter
   1.664 + *	    mailbox name
   1.665 + *	    mailbox attributes
   1.666 + */
   1.667 +
   1.668 +void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
   1.669 +{
   1.670 +  fatal ("mm_list() call");
   1.671 +}
   1.672 +
   1.673 +
   1.674 +/* Subscribed mailbox found
   1.675 + * Accepts: MAIL stream
   1.676 + *	    delimiter
   1.677 + *	    mailbox name
   1.678 + *	    mailbox attributes
   1.679 + */
   1.680 +
   1.681 +void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
   1.682 +{
   1.683 +  fatal ("mm_lsub() call");
   1.684 +}
   1.685 +
   1.686 +
   1.687 +/* Mailbox status
   1.688 + * Accepts: MAIL stream
   1.689 + *	    mailbox name
   1.690 + *	    mailbox status
   1.691 + */
   1.692 +
   1.693 +void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
   1.694 +{
   1.695 +  fatal ("mm_status() call");
   1.696 +}
   1.697 +
   1.698 +/* Notification event
   1.699 + * Accepts: MAIL stream
   1.700 + *	    string to log
   1.701 + *	    error flag
   1.702 + */
   1.703 +
   1.704 +void mm_notify (MAILSTREAM *stream,char *string,long errflg)
   1.705 +{
   1.706 +  char tmp[MAILTMPLEN];
   1.707 +  tmp[11] = '\0';		/* see if TRYCREATE */
   1.708 +  if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
   1.709 +  mm_log (string,errflg);	/* just do mm_log action */
   1.710 +}
   1.711 +
   1.712 +
   1.713 +/* Log an event for the user to see
   1.714 + * Accepts: string to log
   1.715 + *	    error flag
   1.716 + */
   1.717 +
   1.718 +void mm_log (char *string,long errflg)
   1.719 +{
   1.720 +  if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
   1.721 +  else {			/* ordinary logging */
   1.722 +    fprintf (stderr,"%s\n",string);
   1.723 +    switch (errflg) {  
   1.724 +    case NIL:			/* no error */
   1.725 +      syslog (LOG_INFO,"%s",string);
   1.726 +      break;
   1.727 +    case PARSE:			/* parsing problem */
   1.728 +    case WARN:			/* warning */
   1.729 +      syslog (LOG_WARNING,"%s",string);
   1.730 +      break;
   1.731 +    case ERROR:			/* error */
   1.732 +    default:
   1.733 +      syslog (LOG_ERR,"%s",string);
   1.734 +      break;
   1.735 +    }
   1.736 +  }
   1.737 +}
   1.738 +
   1.739 +
   1.740 +/* Log an event to debugging telemetry
   1.741 + * Accepts: string to log
   1.742 + */
   1.743 +
   1.744 +void mm_dlog (char *string)
   1.745 +{
   1.746 +  if (debug) fprintf (stderr,"%s\n",string);
   1.747 +  syslog (LOG_DEBUG,"%s",string);
   1.748 +}
   1.749 +
   1.750 +/* Get user name and password for this host
   1.751 + * Accepts: parse of network mailbox name
   1.752 + *	    where to return user name
   1.753 + *	    where to return password
   1.754 + *	    trial count
   1.755 + */
   1.756 +
   1.757 +void mm_login (NETMBX *mb,char *username,char *password,long trial)
   1.758 +{
   1.759 +  fatal ("mm_login() call");
   1.760 +}
   1.761 +
   1.762 +
   1.763 +/* About to enter critical code
   1.764 + * Accepts: stream
   1.765 + */
   1.766 +
   1.767 +void mm_critical (MAILSTREAM *stream)
   1.768 +{
   1.769 +  critical = T;			/* note in critical code */
   1.770 +}
   1.771 +
   1.772 +
   1.773 +/* About to exit critical code
   1.774 + * Accepts: stream
   1.775 + */
   1.776 +
   1.777 +void mm_nocritical (MAILSTREAM *stream)
   1.778 +{
   1.779 +  critical = NIL;		/* note not in critical code */
   1.780 +}
   1.781 +
   1.782 +
   1.783 +/* Disk error found
   1.784 + * Accepts: stream
   1.785 + *	    system error code
   1.786 + *	    flag indicating that mailbox may be clobbered
   1.787 + * Returns: T if user wants to abort
   1.788 + */
   1.789 +
   1.790 +long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
   1.791 +{
   1.792 +  return T;
   1.793 +}
   1.794 +
   1.795 +
   1.796 +/* Log a fatal error event
   1.797 + * Accepts: string to log
   1.798 + */
   1.799 +
   1.800 +void mm_fatal (char *string)
   1.801 +{
   1.802 +  printf ("?%s\n",string);	/* shouldn't happen normally */
   1.803 +}

UW-IMAP'd extensions by yuuji