1 /* ========================================================================
2 * Copyright 2008 Mark Crispin
3 * ========================================================================
7 * Program: Mail Delivery Module
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
29 extern int errno
; /* just in case */
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
,
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
78 /* Initialize file string structure for file stringstruct
79 * Accepts: string structure
84 void file_string_init (STRING
*s
,void *data
,unsigned long size
)
86 s
->data
= data
; /* note fd */
87 s
->size
= size
; /* note size */
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
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
);
127 int main (int argc
,char *argv
[])
131 unsigned long msglen
,status
= 0;
132 char *s
,tmp
[MAILTMPLEN
];
133 uid_t ruid
= getuid ();
135 openlog ("tmail",LOG_PID
,LOG_MAIL
);
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 */
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
));
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
));
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
));
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
));
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
);
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
);
212 /* write "for" if single recipient */
213 if (argc
== 1) fprintf (f
," for %s",*argv
);
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
);
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 */
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
);
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
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
];
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");
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
);
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])) {
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
);
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
);
362 if (*path
) { /* -I successfully resolved a path? */
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
);
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
),
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
);
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
);
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
];
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 */
467 * Accepts: prototype stream to force mailbox format
468 * stringstruct of message temporary file
470 * filesystem path name
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
,
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
);
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
);
494 if (sbuf
.st_mode
& 0111) { /* executable files may be worrisome */
495 sprintf (tmp
,"WARNING: file %.80s is executable",path
);
499 if (sbuf
.st_mode
& 0002) { /* public-write files may be worrisome */
500 sprintf (tmp
,"WARNING: file %.80s is publicly-writable",path
);
503 if (sbuf
.st_mode
& 0004) { /* public-write files may be worrisome */
504 sprintf (tmp
,"WARNING: file %.80s is publicly-readable",path
);
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
);
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
);
520 sprintf (tmp
,"delivered to %.80s",path
);
522 /* make sure nothing evil this way comes */
523 return delivery_unsafe (path
,uid
,&sbuf
,tmp
);
526 /* Verify that delivery is safe
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
)
537 sprintf (tmp
,"Verifying safe delivery to %.80s by UID %ld",path
,(long) uid
);
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 */
550 case S_IFREG
: /* file is unsafe if setuid */
551 if (sbuf
->st_mode
& S_ISUID
) strcat (tmp
,"setuid file");
553 else if (sbuf
->st_mode
& S_ISGID
) strcat (tmp
,"setgid file");
554 else return NIL
; /* otherwise safe */
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;
561 sprintf (tmp
+ strlen (tmp
),"file type %07o",(unsigned int) type
);
563 return fail (tmp
,EX_CANTCREAT
);
567 * Accepts: string to output
570 int fail (char *string
,int code
)
572 mm_log (string
,ERROR
); /* pass up the string */
581 code
= EX_TEMPFAIL
; /* coerce these to TEMPFAIL */
584 case -1: /* quota failure... */
585 code
= EX_CANTCREAT
; /* ...really returns this code */
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 +,
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
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
632 void mm_exists (MAILSTREAM
*stream
,unsigned long number
)
634 fatal ("mm_exists() call");
639 * Accepts: MAIL stream
643 void mm_expunged (MAILSTREAM
*stream
,unsigned long number
)
645 fatal ("mm_expunged() call");
649 /* Message flags update seen
650 * Accepts: MAIL stream
654 void mm_flags (MAILSTREAM
*stream
,unsigned long number
)
659 * Accepts: MAIL stream
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
678 void mm_lsub (MAILSTREAM
*stream
,int delimiter
,char *name
,long attributes
)
680 fatal ("mm_lsub() call");
685 * Accepts: MAIL stream
690 void mm_status (MAILSTREAM
*stream
,char *mailbox
,MAILSTATUS
*status
)
692 fatal ("mm_status() call");
695 /* Notification event
696 * Accepts: MAIL stream
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
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
);
721 case NIL
: /* no error */
722 syslog (LOG_INFO
,"%s",string
);
724 case PARSE
: /* parsing problem */
725 case WARN
: /* warning */
726 syslog (LOG_WARNING
,"%s",string
);
728 case ERROR
: /* error */
730 syslog (LOG_ERR
,"%s",string
);
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
754 void mm_login (NETMBX
*mb
,char *username
,char *password
,long trial
)
756 fatal ("mm_login() call");
760 /* About to enter critical code
764 void mm_critical (MAILSTREAM
*stream
)
766 critical
= T
; /* note in critical code */
770 /* About to exit critical code
774 void mm_nocritical (MAILSTREAM
*stream
)
776 critical
= NIL
; /* note not in critical 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
)
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 */