imapext-2007

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

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
rev   line source
yuuji@0 1 /* ========================================================================
yuuji@0 2 * Copyright 1988-2007 University of Washington
yuuji@0 3 *
yuuji@0 4 * Licensed under the Apache License, Version 2.0 (the "License");
yuuji@0 5 * you may not use this file except in compliance with the License.
yuuji@0 6 * You may obtain a copy of the License at
yuuji@0 7 *
yuuji@0 8 * http://www.apache.org/licenses/LICENSE-2.0
yuuji@0 9 *
yuuji@0 10 *
yuuji@0 11 * ========================================================================
yuuji@0 12 */
yuuji@0 13
yuuji@0 14 /*
yuuji@0 15 * Program: Mail Delivery Module
yuuji@0 16 *
yuuji@0 17 * Author: Mark Crispin
yuuji@0 18 * Networks and Distributed Computing
yuuji@0 19 * Computing & Communications
yuuji@0 20 * University of Washington
yuuji@0 21 * Administration Building, AG-44
yuuji@0 22 * Seattle, WA 98195
yuuji@0 23 * Internet: MRC@CAC.Washington.EDU
yuuji@0 24 *
yuuji@0 25 * Date: 5 April 1993
yuuji@0 26 * Last Edited: 30 October 2008
yuuji@0 27 */
yuuji@0 28
yuuji@0 29 #include <stdio.h>
yuuji@0 30 #include <pwd.h>
yuuji@0 31 #include <errno.h>
yuuji@0 32 extern int errno; /* just in case */
yuuji@0 33 #include <sysexits.h>
yuuji@0 34 #include <sys/file.h>
yuuji@0 35 #include <sys/stat.h>
yuuji@0 36 #include "c-client.h"
yuuji@0 37 #include "tquota.h"
yuuji@0 38
yuuji@0 39
yuuji@0 40 /* Globals */
yuuji@0 41
yuuji@0 42 char *version = "22"; /* tmail edit version */
yuuji@0 43 int debug = NIL; /* debugging (don't fork) */
yuuji@0 44 int trycreate = NIL; /* flag saying gotta create before appending */
yuuji@0 45 int critical = NIL; /* flag saying in critical code */
yuuji@0 46 char *sender = NIL; /* message origin */
yuuji@0 47 char *inbox = NIL; /* inbox file */
yuuji@0 48 long precedence = 0; /* delivery precedence - used by quota hook */
yuuji@0 49 DRIVER *format = NIL; /* desired format */
yuuji@0 50
yuuji@0 51
yuuji@0 52 /* Function prototypes */
yuuji@0 53
yuuji@0 54 void file_string_init (STRING *s,void *data,unsigned long size);
yuuji@0 55 char file_string_next (STRING *s);
yuuji@0 56 void file_string_setpos (STRING *s,unsigned long i);
yuuji@0 57 int main (int argc,char *argv[]);
yuuji@0 58 int deliver (FILE *f,unsigned long msglen,char *user);
yuuji@0 59 long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);
yuuji@0 60 int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
yuuji@0 61 uid_t uid,char *tmp);
yuuji@0 62 int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);
yuuji@0 63 int fail (char *string,int code);
yuuji@0 64 char *getusername (char *s,char **t);
yuuji@0 65
yuuji@0 66
yuuji@0 67 /* File string driver for file stringstructs */
yuuji@0 68
yuuji@0 69 STRINGDRIVER file_string = {
yuuji@0 70 file_string_init, /* initialize string structure */
yuuji@0 71 file_string_next, /* get next byte in string structure */
yuuji@0 72 file_string_setpos /* set position in string structure */
yuuji@0 73 };
yuuji@0 74
yuuji@0 75
yuuji@0 76 /* Cache buffer for file stringstructs */
yuuji@0 77
yuuji@0 78 #define CHUNKLEN 16384
yuuji@0 79 char chunk[CHUNKLEN];
yuuji@0 80
yuuji@0 81 /* Initialize file string structure for file stringstruct
yuuji@0 82 * Accepts: string structure
yuuji@0 83 * pointer to string
yuuji@0 84 * size of string
yuuji@0 85 */
yuuji@0 86
yuuji@0 87 void file_string_init (STRING *s,void *data,unsigned long size)
yuuji@0 88 {
yuuji@0 89 s->data = data; /* note fd */
yuuji@0 90 s->size = size; /* note size */
yuuji@0 91 s->chunk = chunk;
yuuji@0 92 s->chunksize = (unsigned long) CHUNKLEN;
yuuji@0 93 SETPOS (s,0); /* set initial position */
yuuji@0 94 }
yuuji@0 95
yuuji@0 96
yuuji@0 97 /* Get next character from file stringstruct
yuuji@0 98 * Accepts: string structure
yuuji@0 99 * Returns: character, string structure chunk refreshed
yuuji@0 100 */
yuuji@0 101
yuuji@0 102 char file_string_next (STRING *s)
yuuji@0 103 {
yuuji@0 104 char c = *s->curpos++; /* get next byte */
yuuji@0 105 SETPOS (s,GETPOS (s)); /* move to next chunk */
yuuji@0 106 return c; /* return the byte */
yuuji@0 107 }
yuuji@0 108
yuuji@0 109
yuuji@0 110 /* Set string pointer position for file stringstruct
yuuji@0 111 * Accepts: string structure
yuuji@0 112 * new position
yuuji@0 113 */
yuuji@0 114
yuuji@0 115 void file_string_setpos (STRING *s,unsigned long i)
yuuji@0 116 {
yuuji@0 117 if (i > s->size) i = s->size; /* don't permit setting beyond EOF */
yuuji@0 118 s->offset = i; /* set new offset */
yuuji@0 119 s->curpos = s->chunk; /* reset position */
yuuji@0 120 /* set size of data */
yuuji@0 121 if (s->cursize = min (s->chunksize,SIZE (s))) {
yuuji@0 122 /* move to that position in the file */
yuuji@0 123 fseek ((FILE *) s->data,s->offset,SEEK_SET);
yuuji@0 124 fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data);
yuuji@0 125 }
yuuji@0 126 }
yuuji@0 127
yuuji@0 128 /* Main program */
yuuji@0 129
yuuji@0 130 int main (int argc,char *argv[])
yuuji@0 131 {
yuuji@0 132 FILE *f = NIL;
yuuji@0 133 int pid,c,ret = 0;
yuuji@0 134 unsigned long msglen,status = 0;
yuuji@0 135 char *s,tmp[MAILTMPLEN];
yuuji@0 136 uid_t ruid = getuid ();
yuuji@0 137 struct passwd *pwd;
yuuji@0 138 openlog ("tmail",LOG_PID,LOG_MAIL);
yuuji@0 139 #include "linkage.c"
yuuji@0 140 /* make sure have some arguments */
yuuji@0 141 if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE));
yuuji@0 142 /* process all flags */
yuuji@0 143 while (argc && (*(s = *++argv)) == '-') {
yuuji@0 144 argc--; /* gobble this argument */
yuuji@0 145 switch (s[1]) { /* what is this flag? */
yuuji@0 146 case 'D': /* debug */
yuuji@0 147 debug = T; /* don't fork */
yuuji@0 148 break;
yuuji@0 149 case 'I': /* inbox specifier */
yuuji@0 150 if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
yuuji@0 151 if (argc--) inbox = cpystr (*++argv);
yuuji@0 152 else _exit (fail ("missing argument to -I",EX_USAGE));
yuuji@0 153 break;
yuuji@0 154 case 'f': /* new name for this flag */
yuuji@0 155 case 'r': /* flag giving return path */
yuuji@0 156 if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE));
yuuji@0 157 if (argc--) sender = cpystr (*++argv);
yuuji@0 158 else _exit (fail ("missing argument to -f or -r",EX_USAGE));
yuuji@0 159 break;
yuuji@0 160 case 'b': /* create INBOX in this format */
yuuji@0 161 if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE));
yuuji@0 162 if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE));
yuuji@0 163 if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv)))
yuuji@0 164 _exit (fail ("unknown format to -b",EX_USAGE));
yuuji@0 165 else if (!(format->flags & DR_LOCAL) ||
yuuji@0 166 !compare_cstring (format->name,"dummy"))
yuuji@0 167 _exit (fail ("invalid format to -b",EX_USAGE));
yuuji@0 168 break;
yuuji@0 169 /* following flags are undocumented */
yuuji@0 170 case 'p': /* precedence for quota */
yuuji@0 171 if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
yuuji@0 172 else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
yuuji@0 173 precedence = atol (s);
yuuji@0 174 else _exit (fail ("missing argument to -p",EX_USAGE));
yuuji@0 175 break;
yuuji@0 176 case 'd': /* obsolete flag meaning multiple users */
yuuji@0 177 break; /* ignore silently */
yuuji@0 178 /* -s has been deprecated and replaced by the -s and -k flags in dmail.
yuuji@0 179 * dmail's -k flag does what -s once did in tmail; dmail's -s flag
yuuji@0 180 * takes no argument and just sets \Seen. Flag setting is more properly
yuuji@0 181 * done in dmail which runs as the user and is clearly at the user's
yuuji@0 182 * behest. Since tmail runs privileged, -s would have to be disabled
yuuji@0 183 * unless the caller is also privileged.
yuuji@0 184 */
yuuji@0 185 case 's': /* obsolete flag meaning delivery flags */
yuuji@0 186 if (!argc--) /* takes an argument */
yuuji@0 187 _exit (fail ("missing argument to deprecated flag",EX_USAGE));
yuuji@0 188 syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv);
yuuji@0 189 break;
yuuji@0 190 default: /* anything else */
yuuji@0 191 _exit (fail ("unknown switch",EX_USAGE));
yuuji@0 192 }
yuuji@0 193 }
yuuji@0 194
yuuji@0 195 if (!argc) ret = fail ("no recipients",EX_USAGE);
yuuji@0 196 else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL);
yuuji@0 197 else { /* build delivery headers */
yuuji@0 198 if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
yuuji@0 199 /* start Received line: */
yuuji@0 200 fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version);
yuuji@0 201 /* not root or daemon? */
yuuji@0 202 if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) {
yuuji@0 203 pwd = getpwuid (ruid); /* get unprivileged user's information */
yuuji@0 204 if (inbox || format) {
yuuji@0 205 if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name);
yuuji@0 206 else sprintf (tmp,"UID %ld",(long) ruid);
yuuji@0 207 strcat (tmp," is not privileged to use -b or -I");
yuuji@0 208 _exit (fail (tmp,EX_USAGE));
yuuji@0 209 }
yuuji@0 210 fputs (" (invoked by ",f);
yuuji@0 211 if (pwd) fprintf (f,"user %s",pwd->pw_name);
yuuji@0 212 else fprintf (f,"UID %ld",(long) ruid);
yuuji@0 213 fputs (")",f);
yuuji@0 214 }
yuuji@0 215 /* write "for" if single recipient */
yuuji@0 216 if (argc == 1) fprintf (f," for %s",*argv);
yuuji@0 217 fputs ("; ",f);
yuuji@0 218 rfc822_date (tmp);
yuuji@0 219 fputs (tmp,f);
yuuji@0 220 fputs ("\015\012",f);
yuuji@0 221 /* copy text from standard input */
yuuji@0 222 if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
yuuji@0 223 (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
yuuji@0 224 if (s[-1] == '\015') { /* nuke leading "From " line */
yuuji@0 225 if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
yuuji@0 226 (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
yuuji@0 227 while ((c = getchar ()) != EOF) putc (c,f);
yuuji@0 228 }
yuuji@0 229 else {
yuuji@0 230 mm_log ("tmail called with LF-only newlines",WARN);
yuuji@0 231 if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
yuuji@0 232 (tmp[3] != 'm') || (tmp[4] != ' ')) {
yuuji@0 233 *s++ = '\015'; /* overwrite NL with CRLF */
yuuji@0 234 *s++ = '\012';
yuuji@0 235 *s = '\0'; /* tie off string */
yuuji@0 236 fputs (tmp,f); /* write line */
yuuji@0 237 }
yuuji@0 238 /* copy text from standard input */
yuuji@0 239 while ((c = getchar ()) != EOF) {
yuuji@0 240 /* add CR if needed */
yuuji@0 241 if (c == '\012') putc ('\015',f);
yuuji@0 242 putc (c,f);
yuuji@0 243 }
yuuji@0 244 }
yuuji@0 245 msglen = ftell (f); /* size of message */
yuuji@0 246 fflush (f); /* make sure all changes written out */
yuuji@0 247
yuuji@0 248 if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
yuuji@0 249 else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
yuuji@0 250 /* single delivery */
yuuji@0 251 else if (argc == 1) ret = deliver (f,msglen,*argv);
yuuji@0 252 else do { /* multiple delivery uses daughter forks */
yuuji@0 253 if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR);
yuuji@0 254 else if (pid) { /* mother process */
yuuji@0 255 grim_pid_reap_status (pid,NIL,(void *) status);
yuuji@0 256 /* normal termination? */
yuuji@0 257 if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8;
yuuji@0 258 }
yuuji@0 259 /* daughter process */
yuuji@0 260 else _exit (deliver (f,msglen,*argv));
yuuji@0 261 } while (--argc && *argv++);
yuuji@0 262 mm_dlog (ret ? "error in delivery" : "all recipients delivered");
yuuji@0 263 }
yuuji@0 264 if (f) fclose (f); /* all done with temporary file */
yuuji@0 265 _exit (ret); /* normal exit */
yuuji@0 266 return 0; /* stupid gcc */
yuuji@0 267 }
yuuji@0 268
yuuji@0 269 /* Deliver message to recipient list
yuuji@0 270 * Accepts: file description of message temporary file
yuuji@0 271 * size of message temporary file in bytes
yuuji@0 272 * recipient name
yuuji@0 273 * Returns: NIL if success, else error code
yuuji@0 274 */
yuuji@0 275
yuuji@0 276 int deliver (FILE *f,unsigned long msglen,char *user)
yuuji@0 277 {
yuuji@0 278 MAILSTREAM *ds = NIL;
yuuji@0 279 char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
yuuji@0 280 struct passwd *pwd;
yuuji@0 281 STRING st;
yuuji@0 282 struct stat sbuf;
yuuji@0 283 uid_t duid;
yuuji@0 284 uid_t euid = geteuid ();
yuuji@0 285 /* get user record */
yuuji@0 286 if (!(pwd = getpwnam (getusername (user,&mailbox)))) {
yuuji@0 287 sprintf (tmp,"no such user as %.80s",user);
yuuji@0 288 return fail (tmp,EX_NOUSER);
yuuji@0 289 }
yuuji@0 290 /* absurd is absurd */
yuuji@0 291 if (mailbox && (strlen (mailbox) > 256))
yuuji@0 292 return fail ("absurd folder name",EX_NOUSER);
yuuji@0 293 /* big security hole if this is allowed */
yuuji@0 294 if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER);
yuuji@0 295 /* log in as user if different than euid */
yuuji@0 296 if ((duid != euid) && !loginpw (pwd,1,&user)) {
yuuji@0 297 sprintf (tmp,"unable to log in UID %ld from UID %ld",
yuuji@0 298 (long) duid,(long) euid);
yuuji@0 299 return fail (tmp,EX_NOUSER);
yuuji@0 300 }
yuuji@0 301 /* can't use pwd after this point */
yuuji@0 302 env_init (pwd->pw_name,pwd->pw_dir);
yuuji@0 303 sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
yuuji@0 304 mm_dlog (tmp);
yuuji@0 305 /* prepare stringstruct */
yuuji@0 306 INIT (&st,file_string,(void *) f,msglen);
yuuji@0 307 if (mailbox) { /* non-INBOX name */
yuuji@0 308 switch (mailbox[0]) { /* make sure a valid name */
yuuji@0 309 default: /* other names, try to deliver if not INBOX */
yuuji@0 310 if (!strstr (mailbox,"..") && !strstr (mailbox,"//") &&
yuuji@0 311 !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
yuuji@0 312 !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL;
yuuji@0 313 case '%': case '*': /* wildcards not valid */
yuuji@0 314 case '#': /* namespace name not valid */
yuuji@0 315 case '/': /* absolute path names not valid */
yuuji@0 316 case '~': /* user names not valid */
yuuji@0 317 sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
yuuji@0 318 mm_log (tmp,WARN);
yuuji@0 319 break;
yuuji@0 320 }
yuuji@0 321 mm_dlog ("retrying delivery to INBOX");
yuuji@0 322 SETPOS (&st,0); /* rewind stringstruct just in case */
yuuji@0 323 }
yuuji@0 324
yuuji@0 325 /* -I specified and not "-I INBOX"? */
yuuji@0 326 if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) &&
yuuji@0 327 ((inbox[1] == 'N') || (inbox[1] == 'n')) &&
yuuji@0 328 ((inbox[2] == 'B') || (inbox[2] == 'b')) &&
yuuji@0 329 ((inbox[3] == 'O') || (inbox[3] == 'o')) &&
yuuji@0 330 ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) {
yuuji@0 331 DRIVER *dv = NIL;
yuuji@0 332 /* "-I #driver.xxx/name"? */
yuuji@0 333 if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) &&
yuuji@0 334 ((inbox[2] == 'r') || (inbox[2] == 'R')) &&
yuuji@0 335 ((inbox[3] == 'i') || (inbox[3] == 'I')) &&
yuuji@0 336 ((inbox[4] == 'v') || (inbox[4] == 'V')) &&
yuuji@0 337 ((inbox[5] == 'e') || (inbox[5] == 'E')) &&
yuuji@0 338 ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') &&
yuuji@0 339 (s = strchr (inbox+8,'/'))) {
yuuji@0 340 *s = '\0'; /* temporarily tie off driver name */
yuuji@0 341 if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) &&
yuuji@0 342 (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) &&
yuuji@0 343 (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) {
yuuji@0 344 path[0] = '\0'; /* bad -I argument, no path resolved */
yuuji@0 345 sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox);
yuuji@0 346 mm_log (tmp,WARN);
yuuji@0 347 }
yuuji@0 348 *s = '/'; /* restore delimiter */
yuuji@0 349 }
yuuji@0 350 /* resolve "-I other" specification */
yuuji@0 351 else if (mailboxfile (path,inbox) && path[0]) {
yuuji@0 352 /* resolution succeeded, impute driver */
yuuji@0 353 if (!strcmp (inbox,"mail.txt"))
yuuji@0 354 dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex");
yuuji@0 355 else if (!strcmp (inbox,"INBOX.MTX"))
yuuji@0 356 dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx");
yuuji@0 357 else if (!strcmp (inbox,"mbox"))
yuuji@0 358 dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix");
yuuji@0 359 }
yuuji@0 360 else { /* bad -I argument */
yuuji@0 361 path[0] = '\0'; /* no path resolved */
yuuji@0 362 sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox);
yuuji@0 363 mm_log (tmp,WARN);
yuuji@0 364 }
yuuji@0 365 if (*path) { /* -I successfully resolved a path? */
yuuji@0 366 MAILSTREAM dpr;
yuuji@0 367 dpr.dtb = dv;
yuuji@0 368 if (dv) ds = &dpr;
yuuji@0 369 /* supplicate to the Evil One if necessary */
yuuji@0 370 if (lstat (path,&sbuf) && !path_create (ds,path)) {
yuuji@0 371 /* the Evil One rejected the plea */
yuuji@0 372 sprintf (tmp,"Unable to create %.80s, -I ignored",path);
yuuji@0 373 mm_log (tmp,WARN);
yuuji@0 374 }
yuuji@0 375 /* now attempt delivery */
yuuji@0 376 else return deliver_safely (ds,&st,inbox,path,duid,tmp);
yuuji@0 377 }
yuuji@0 378 }
yuuji@0 379
yuuji@0 380 /* no -I, resolve "INBOX" into path */
yuuji@0 381 if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
yuuji@0 382 /* clear box, get generic INBOX prototype */
yuuji@0 383 if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
yuuji@0 384 fatal ("no INBOX prototype");
yuuji@0 385 /* standard system driver? */
yuuji@0 386 if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
yuuji@0 387 strcpy (path,sysinbox ());/* use system INBOX */
yuuji@0 388 if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */
yuuji@0 389 return deliver_safely (ds,&st,mailbox,path,duid,tmp);
yuuji@0 390 }
yuuji@0 391 else { /* other driver, try ~/INBOX */
yuuji@0 392 if ((mailboxfile (path,"&&&&&") == path) &&
yuuji@0 393 (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
yuuji@0 394 !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */
yuuji@0 395 sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
yuuji@0 396 return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp);
yuuji@0 397 }
yuuji@0 398 }
yuuji@0 399 /* not dummy, deliver to driver imputed path */
yuuji@0 400 if (strcmp (ds->dtb->name,"dummy"))
yuuji@0 401 return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
yuuji@0 402 deliver_safely (ds,&st,mailbox,path,duid,tmp) :
yuuji@0 403 fail ("unable to resolve INBOX path",EX_CANTCREAT);
yuuji@0 404 /* dummy, empty imputed append path exist? */
yuuji@0 405 if (ibxpath (ds = default_proto (T),&mailbox,path) &&
yuuji@0 406 !lstat (path,&sbuf) && !sbuf.st_size)
yuuji@0 407 return deliver_safely (ds,&st,mailbox,path,duid,tmp);
yuuji@0 408 /* impute path that we will create */
yuuji@0 409 if (!ibxpath (ds = format ? (format->open) (NIL) : default_proto (NIL),
yuuji@0 410 &mailbox,path))
yuuji@0 411 return fail ("unable to resolve INBOX",EX_CANTCREAT);
yuuji@0 412 }
yuuji@0 413 /* black box, must create, get create proto */
yuuji@0 414 else if (lstat (path,&sbuf)) ds = default_proto (NIL);
yuuji@0 415 else { /* black box, existing file */
yuuji@0 416 /* empty file, get append prototype */
yuuji@0 417 if (!sbuf.st_size) ds = default_proto (T);
yuuji@0 418 /* non-empty, get prototype from its data */
yuuji@0 419 else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
yuuji@0 420 fatal ("no INBOX prototype");
yuuji@0 421 /* error if unknown format */
yuuji@0 422 if (!strcmp (ds->dtb->name,"phile"))
yuuji@0 423 return fail ("unknown format INBOX",EX_UNAVAILABLE);
yuuji@0 424 /* otherwise can deliver to it */
yuuji@0 425 return deliver_safely (ds,&st,mailbox,path,duid,tmp);
yuuji@0 426 }
yuuji@0 427 sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
yuuji@0 428 mm_dlog (tmp);
yuuji@0 429 /* supplicate to the Evil One */
yuuji@0 430 if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
yuuji@0 431 sprintf (tmp,"created %.80s",path);
yuuji@0 432 mm_dlog (tmp);
yuuji@0 433 /* deliver the message */
yuuji@0 434 return deliver_safely (ds,&st,mailbox,path,duid,tmp);
yuuji@0 435 }
yuuji@0 436
yuuji@0 437 /* Resolve INBOX from driver prototype into mailbox name and filesystem path
yuuji@0 438 * Accepts: driver prototype
yuuji@0 439 * pointer to mailbox name string pointer
yuuji@0 440 * buffer to return mailbox path
yuuji@0 441 * Returns: T if success, NIL if error
yuuji@0 442 */
yuuji@0 443
yuuji@0 444 long ibxpath (MAILSTREAM *ds,char **mailbox,char *path)
yuuji@0 445 {
yuuji@0 446 char *s,tmp[MAILTMPLEN];
yuuji@0 447 long ret = T;
yuuji@0 448 if (!ds) ret = NIL;
yuuji@0 449 else if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf"))
yuuji@0 450 strcpy (path,sysinbox ()); /* use system INBOX for unix and MMDF */
yuuji@0 451 else if (!strcmp (ds->dtb->name,"tenex"))
yuuji@0 452 ret = (mailboxfile (path,"mail.txt") == path) ? T : NIL;
yuuji@0 453 else if (!strcmp (ds->dtb->name,"mtx"))
yuuji@0 454 ret = (mailboxfile (path,"INBOX.MTX") == path) ? T : NIL;
yuuji@0 455 else if (!strcmp (ds->dtb->name,"mbox"))
yuuji@0 456 ret = (mailboxfile (path,"mbox") == path) ? T : NIL;
yuuji@0 457 /* better not be a namespace driver */
yuuji@0 458 else if (ds->dtb->flags & DR_NAMESPACE) return NIL;
yuuji@0 459 /* INBOX in home directory */
yuuji@0 460 else ret = ((mailboxfile (path,"&&&&&") == path) &&
yuuji@0 461 (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX")) ? T : NIL;
yuuji@0 462 if (ret) { /* don't bother if lossage */
yuuji@0 463 sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
yuuji@0 464 *mailbox = cpystr (tmp); /* name of INBOX in this namespace */
yuuji@0 465 }
yuuji@0 466 return ret;
yuuji@0 467 }
yuuji@0 468
yuuji@0 469 /* Deliver safely
yuuji@0 470 * Accepts: prototype stream to force mailbox format
yuuji@0 471 * stringstruct of message temporary file
yuuji@0 472 * mailbox name
yuuji@0 473 * filesystem path name
yuuji@0 474 * user id
yuuji@0 475 * scratch buffer for messages
yuuji@0 476 * Returns: NIL if success, else error code
yuuji@0 477 */
yuuji@0 478
yuuji@0 479 int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path,
yuuji@0 480 uid_t uid,char *tmp)
yuuji@0 481 {
yuuji@0 482 struct stat sbuf;
yuuji@0 483 int i = delivery_unsafe (path,uid,&sbuf,tmp);
yuuji@0 484 if (i) return i; /* give up now if delivery unsafe */
yuuji@0 485 /* directory, not file */
yuuji@0 486 if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
yuuji@0 487 if (sbuf.st_mode & 0001) { /* listable directories may be worrisome */
yuuji@0 488 sprintf (tmp,"WARNING: directory %.80s is listable",path);
yuuji@0 489 mm_log (tmp,WARN);
yuuji@0 490 }
yuuji@0 491 }
yuuji@0 492 else { /* file, not directory */
yuuji@0 493 if (sbuf.st_nlink != 1) { /* multiple links may be worrisome */
yuuji@0 494 sprintf (tmp,"WARNING: multiple links to file %.80s",path);
yuuji@0 495 mm_log (tmp,WARN);
yuuji@0 496 }
yuuji@0 497 if (sbuf.st_mode & 0111) { /* executable files may be worrisome */
yuuji@0 498 sprintf (tmp,"WARNING: file %.80s is executable",path);
yuuji@0 499 mm_log (tmp,WARN);
yuuji@0 500 }
yuuji@0 501 }
yuuji@0 502 if (sbuf.st_mode & 0002) { /* public-write files may be worrisome */
yuuji@0 503 sprintf (tmp,"WARNING: file %.80s is publicly-writable",path);
yuuji@0 504 mm_log (tmp,WARN);
yuuji@0 505 }
yuuji@0 506 if (sbuf.st_mode & 0004) { /* public-write files may be worrisome */
yuuji@0 507 sprintf (tmp,"WARNING: file %.80s is publicly-readable",path);
yuuji@0 508 mm_log (tmp,WARN);
yuuji@0 509 }
yuuji@0 510 /* check site-written quota procedure */
yuuji@0 511 if (!tmail_quota (st,path,uid,tmp,sender,precedence)) return fail (tmp,-1);
yuuji@0 512 /* so far, so good */
yuuji@0 513 sprintf (tmp,"%s appending to %.80s (%s %.80s)",
yuuji@0 514 prt ? prt->dtb->name : "default",mailbox,
yuuji@0 515 ((sbuf.st_mode & S_IFMT) == S_IFDIR) ? "directory" : "file",path);
yuuji@0 516 mm_dlog (tmp);
yuuji@0 517 /* do the append now! */
yuuji@0 518 if (!mail_append (prt,mailbox,st)) {
yuuji@0 519 sprintf (tmp,"message delivery failed to %.80s",path);
yuuji@0 520 return fail (tmp,EX_CANTCREAT);
yuuji@0 521 }
yuuji@0 522 /* note success */
yuuji@0 523 sprintf (tmp,"delivered to %.80s",path);
yuuji@0 524 mm_log (tmp,NIL);
yuuji@0 525 /* make sure nothing evil this way comes */
yuuji@0 526 return delivery_unsafe (path,uid,&sbuf,tmp);
yuuji@0 527 }
yuuji@0 528
yuuji@0 529 /* Verify that delivery is safe
yuuji@0 530 * Accepts: path name
yuuji@0 531 * user id
yuuji@0 532 * stat buffer
yuuji@0 533 * scratch buffer for messages
yuuji@0 534 * Returns: NIL if delivery is safe, error code if unsafe
yuuji@0 535 */
yuuji@0 536
yuuji@0 537 int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp)
yuuji@0 538 {
yuuji@0 539 u_short type;
yuuji@0 540 sprintf (tmp,"Verifying safe delivery to %.80s by UID %ld",path,(long) uid);
yuuji@0 541 mm_dlog (tmp);
yuuji@0 542 /* prepare message just in case */
yuuji@0 543 sprintf (tmp,"delivery to %.80s unsafe: ",path);
yuuji@0 544 /* unsafe if can't get its status */
yuuji@0 545 if (lstat (path,sbuf)) strcat (tmp,strerror (errno));
yuuji@0 546 else if (sbuf->st_uid != uid) /* unsafe if UID does not match */
yuuji@0 547 sprintf (tmp + strlen (tmp),"uid mismatch (%ld != %ld)",
yuuji@0 548 (long) sbuf->st_uid,(long) uid);
yuuji@0 549 /* check file type */
yuuji@0 550 else switch (sbuf->st_mode & S_IFMT) {
yuuji@0 551 case S_IFDIR: /* directory is always OK */
yuuji@0 552 return NIL;
yuuji@0 553 case S_IFREG: /* file is unsafe if setuid */
yuuji@0 554 if (sbuf->st_mode & S_ISUID) strcat (tmp,"setuid file");
yuuji@0 555 /* or setgid */
yuuji@0 556 else if (sbuf->st_mode & S_ISGID) strcat (tmp,"setgid file");
yuuji@0 557 else return NIL; /* otherwise safe */
yuuji@0 558 break;
yuuji@0 559 case S_IFCHR: strcat (tmp,"character special"); break;
yuuji@0 560 case S_IFBLK: strcat (tmp,"block special"); break;
yuuji@0 561 case S_IFLNK: strcat (tmp,"symbolic link"); break;
yuuji@0 562 case S_IFSOCK: strcat (tmp,"socket"); break;
yuuji@0 563 default:
yuuji@0 564 sprintf (tmp + strlen (tmp),"file type %07o",(unsigned int) type);
yuuji@0 565 }
yuuji@0 566 return fail (tmp,EX_CANTCREAT);
yuuji@0 567 }
yuuji@0 568
yuuji@0 569 /* Report an error
yuuji@0 570 * Accepts: string to output
yuuji@0 571 */
yuuji@0 572
yuuji@0 573 int fail (char *string,int code)
yuuji@0 574 {
yuuji@0 575 mm_log (string,ERROR); /* pass up the string */
yuuji@0 576 switch (code) {
yuuji@0 577 #if T
yuuji@0 578 case EX_USAGE:
yuuji@0 579 case EX_OSERR:
yuuji@0 580 case EX_SOFTWARE:
yuuji@0 581 case EX_NOUSER:
yuuji@0 582 case EX_CANTCREAT:
yuuji@0 583 case EX_UNAVAILABLE:
yuuji@0 584 code = EX_TEMPFAIL; /* coerce these to TEMPFAIL */
yuuji@0 585 #endif
yuuji@0 586 break;
yuuji@0 587 case -1: /* quota failure... */
yuuji@0 588 code = EX_CANTCREAT; /* ...really returns this code */
yuuji@0 589 break;
yuuji@0 590 default:
yuuji@0 591 break;
yuuji@0 592 }
yuuji@0 593 return code; /* error code to return */
yuuji@0 594 }
yuuji@0 595
yuuji@0 596
yuuji@0 597 /* Get user name from username+mailbox specifier
yuuji@0 598 * Accepts: username/mailbox specifier
yuuji@0 599 * pointer to return location for mailbox specifier
yuuji@0 600 * Returns: user name, mailbox specifier value NIL if INBOX, patches out +
yuuji@0 601 */
yuuji@0 602
yuuji@0 603 char *getusername (char *s,char **t)
yuuji@0 604 {
yuuji@0 605 if (*t = strchr (s,'+')) { /* have a mailbox specifier? */
yuuji@0 606 *(*t)++ = '\0'; /* yes, tie off user name */
yuuji@0 607 /* user+ and user+INBOX same as user */
yuuji@0 608 if (!**t || !compare_cstring ((unsigned char *) *t,"INBOX")) *t = NIL;
yuuji@0 609 }
yuuji@0 610 return s; /* return user name */
yuuji@0 611 }
yuuji@0 612
yuuji@0 613 /* Co-routines from MAIL library */
yuuji@0 614
yuuji@0 615
yuuji@0 616 /* Message matches a search
yuuji@0 617 * Accepts: MAIL stream
yuuji@0 618 * message number
yuuji@0 619 */
yuuji@0 620
yuuji@0 621 void mm_searched (MAILSTREAM *stream,unsigned long msgno)
yuuji@0 622 {
yuuji@0 623 fatal ("mm_searched() call");
yuuji@0 624 }
yuuji@0 625
yuuji@0 626
yuuji@0 627 /* Message exists (i.e. there are that many messages in the mailbox)
yuuji@0 628 * Accepts: MAIL stream
yuuji@0 629 * message number
yuuji@0 630 */
yuuji@0 631
yuuji@0 632 void mm_exists (MAILSTREAM *stream,unsigned long number)
yuuji@0 633 {
yuuji@0 634 fatal ("mm_exists() call");
yuuji@0 635 }
yuuji@0 636
yuuji@0 637
yuuji@0 638 /* Message expunged
yuuji@0 639 * Accepts: MAIL stream
yuuji@0 640 * message number
yuuji@0 641 */
yuuji@0 642
yuuji@0 643 void mm_expunged (MAILSTREAM *stream,unsigned long number)
yuuji@0 644 {
yuuji@0 645 fatal ("mm_expunged() call");
yuuji@0 646 }
yuuji@0 647
yuuji@0 648
yuuji@0 649 /* Message flags update seen
yuuji@0 650 * Accepts: MAIL stream
yuuji@0 651 * message number
yuuji@0 652 */
yuuji@0 653
yuuji@0 654 void mm_flags (MAILSTREAM *stream,unsigned long number)
yuuji@0 655 {
yuuji@0 656 }
yuuji@0 657
yuuji@0 658 /* Mailbox found
yuuji@0 659 * Accepts: MAIL stream
yuuji@0 660 * delimiter
yuuji@0 661 * mailbox name
yuuji@0 662 * mailbox attributes
yuuji@0 663 */
yuuji@0 664
yuuji@0 665 void mm_list (MAILSTREAM *stream,int delimiter,char *name,long attributes)
yuuji@0 666 {
yuuji@0 667 fatal ("mm_list() call");
yuuji@0 668 }
yuuji@0 669
yuuji@0 670
yuuji@0 671 /* Subscribed mailbox found
yuuji@0 672 * Accepts: MAIL stream
yuuji@0 673 * delimiter
yuuji@0 674 * mailbox name
yuuji@0 675 * mailbox attributes
yuuji@0 676 */
yuuji@0 677
yuuji@0 678 void mm_lsub (MAILSTREAM *stream,int delimiter,char *name,long attributes)
yuuji@0 679 {
yuuji@0 680 fatal ("mm_lsub() call");
yuuji@0 681 }
yuuji@0 682
yuuji@0 683
yuuji@0 684 /* Mailbox status
yuuji@0 685 * Accepts: MAIL stream
yuuji@0 686 * mailbox name
yuuji@0 687 * mailbox status
yuuji@0 688 */
yuuji@0 689
yuuji@0 690 void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
yuuji@0 691 {
yuuji@0 692 fatal ("mm_status() call");
yuuji@0 693 }
yuuji@0 694
yuuji@0 695 /* Notification event
yuuji@0 696 * Accepts: MAIL stream
yuuji@0 697 * string to log
yuuji@0 698 * error flag
yuuji@0 699 */
yuuji@0 700
yuuji@0 701 void mm_notify (MAILSTREAM *stream,char *string,long errflg)
yuuji@0 702 {
yuuji@0 703 char tmp[MAILTMPLEN];
yuuji@0 704 tmp[11] = '\0'; /* see if TRYCREATE */
yuuji@0 705 if (!strcmp (ucase (strncpy (tmp,string,11)),"[TRYCREATE]")) trycreate = T;
yuuji@0 706 mm_log (string,errflg); /* just do mm_log action */
yuuji@0 707 }
yuuji@0 708
yuuji@0 709
yuuji@0 710 /* Log an event for the user to see
yuuji@0 711 * Accepts: string to log
yuuji@0 712 * error flag
yuuji@0 713 */
yuuji@0 714
yuuji@0 715 void mm_log (char *string,long errflg)
yuuji@0 716 {
yuuji@0 717 if (trycreate)mm_dlog(string);/* debug logging only if trycreate in effect */
yuuji@0 718 else { /* ordinary logging */
yuuji@0 719 fprintf (stderr,"%s\n",string);
yuuji@0 720 switch (errflg) {
yuuji@0 721 case NIL: /* no error */
yuuji@0 722 syslog (LOG_INFO,"%s",string);
yuuji@0 723 break;
yuuji@0 724 case PARSE: /* parsing problem */
yuuji@0 725 case WARN: /* warning */
yuuji@0 726 syslog (LOG_WARNING,"%s",string);
yuuji@0 727 break;
yuuji@0 728 case ERROR: /* error */
yuuji@0 729 default:
yuuji@0 730 syslog (LOG_ERR,"%s",string);
yuuji@0 731 break;
yuuji@0 732 }
yuuji@0 733 }
yuuji@0 734 }
yuuji@0 735
yuuji@0 736
yuuji@0 737 /* Log an event to debugging telemetry
yuuji@0 738 * Accepts: string to log
yuuji@0 739 */
yuuji@0 740
yuuji@0 741 void mm_dlog (char *string)
yuuji@0 742 {
yuuji@0 743 if (debug) fprintf (stderr,"%s\n",string);
yuuji@0 744 syslog (LOG_DEBUG,"%s",string);
yuuji@0 745 }
yuuji@0 746
yuuji@0 747 /* Get user name and password for this host
yuuji@0 748 * Accepts: parse of network mailbox name
yuuji@0 749 * where to return user name
yuuji@0 750 * where to return password
yuuji@0 751 * trial count
yuuji@0 752 */
yuuji@0 753
yuuji@0 754 void mm_login (NETMBX *mb,char *username,char *password,long trial)
yuuji@0 755 {
yuuji@0 756 fatal ("mm_login() call");
yuuji@0 757 }
yuuji@0 758
yuuji@0 759
yuuji@0 760 /* About to enter critical code
yuuji@0 761 * Accepts: stream
yuuji@0 762 */
yuuji@0 763
yuuji@0 764 void mm_critical (MAILSTREAM *stream)
yuuji@0 765 {
yuuji@0 766 critical = T; /* note in critical code */
yuuji@0 767 }
yuuji@0 768
yuuji@0 769
yuuji@0 770 /* About to exit critical code
yuuji@0 771 * Accepts: stream
yuuji@0 772 */
yuuji@0 773
yuuji@0 774 void mm_nocritical (MAILSTREAM *stream)
yuuji@0 775 {
yuuji@0 776 critical = NIL; /* note not in critical code */
yuuji@0 777 }
yuuji@0 778
yuuji@0 779
yuuji@0 780 /* Disk error found
yuuji@0 781 * Accepts: stream
yuuji@0 782 * system error code
yuuji@0 783 * flag indicating that mailbox may be clobbered
yuuji@0 784 * Returns: T if user wants to abort
yuuji@0 785 */
yuuji@0 786
yuuji@0 787 long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
yuuji@0 788 {
yuuji@0 789 return T;
yuuji@0 790 }
yuuji@0 791
yuuji@0 792
yuuji@0 793 /* Log a fatal error event
yuuji@0 794 * Accepts: string to log
yuuji@0 795 */
yuuji@0 796
yuuji@0 797 void mm_fatal (char *string)
yuuji@0 798 {
yuuji@0 799 printf ("?%s\n",string); /* shouldn't happen normally */
yuuji@0 800 }

UW-IMAP'd extensions by yuuji