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