📄 tmail.c
字号:
/* ======================================================================== * Copyright 1988-2007 University of Washington * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * * ======================================================================== *//* * Program: Mail Delivery Module * * Author: Mark Crispin * Networks and Distributed Computing * Computing & Communications * University of Washington * Administration Building, AG-44 * Seattle, WA 98195 * Internet: MRC@CAC.Washington.EDU * * Date: 5 April 1993 * Last Edited: 30 October 2008 */#include <stdio.h>#include <pwd.h>#include <errno.h>extern int errno; /* just in case */#include <sysexits.h>#include <sys/file.h>#include <sys/stat.h>#include "c-client.h"#include "tquota.h"/* Globals */char *version = "22"; /* tmail edit version */int debug = NIL; /* debugging (don't fork) */int trycreate = NIL; /* flag saying gotta create before appending */int critical = NIL; /* flag saying in critical code */char *sender = NIL; /* message origin */char *inbox = NIL; /* inbox file */long precedence = 0; /* delivery precedence - used by quota hook */DRIVER *format = NIL; /* desired format *//* Function prototypes */void file_string_init (STRING *s,void *data,unsigned long size);char file_string_next (STRING *s);void file_string_setpos (STRING *s,unsigned long i);int main (int argc,char *argv[]);int deliver (FILE *f,unsigned long msglen,char *user);long ibxpath (MAILSTREAM *ds,char **mailbox,char *path);int deliver_safely (MAILSTREAM *prt,STRING *st,char *mailbox,char *path, uid_t uid,char *tmp);int delivery_unsafe (char *path,uid_t uid,struct stat *sbuf,char *tmp);int fail (char *string,int code);char *getusername (char *s,char **t);/* File string driver for file stringstructs */STRINGDRIVER file_string = { file_string_init, /* initialize string structure */ file_string_next, /* get next byte in string structure */ file_string_setpos /* set position in string structure */};/* Cache buffer for file stringstructs */#define CHUNKLEN 16384char chunk[CHUNKLEN];/* Initialize file string structure for file stringstruct * Accepts: string structure * pointer to string * size of string */void file_string_init (STRING *s,void *data,unsigned long size){ s->data = data; /* note fd */ s->size = size; /* note size */ s->chunk = chunk; s->chunksize = (unsigned long) CHUNKLEN; SETPOS (s,0); /* set initial position */}/* Get next character from file stringstruct * Accepts: string structure * Returns: character, string structure chunk refreshed */char file_string_next (STRING *s){ char c = *s->curpos++; /* get next byte */ SETPOS (s,GETPOS (s)); /* move to next chunk */ return c; /* return the byte */}/* Set string pointer position for file stringstruct * Accepts: string structure * new position */void file_string_setpos (STRING *s,unsigned long i){ if (i > s->size) i = s->size; /* don't permit setting beyond EOF */ s->offset = i; /* set new offset */ s->curpos = s->chunk; /* reset position */ /* set size of data */ if (s->cursize = min (s->chunksize,SIZE (s))) { /* move to that position in the file */ fseek ((FILE *) s->data,s->offset,SEEK_SET); fread (s->curpos,sizeof (char),(unsigned int) s->cursize,(FILE *) s->data); }}/* Main program */int main (int argc,char *argv[]){ FILE *f = NIL; int pid,c,ret = 0; unsigned long msglen,status = 0; char *s,tmp[MAILTMPLEN]; uid_t ruid = getuid (); struct passwd *pwd; openlog ("tmail",LOG_PID,LOG_MAIL);#include "linkage.c" /* make sure have some arguments */ if (--argc < 1) _exit (fail ("usage: tmail [-D] user[+folder]",EX_USAGE)); /* process all flags */ while (argc && (*(s = *++argv)) == '-') { argc--; /* gobble this argument */ switch (s[1]) { /* what is this flag? */ case 'D': /* debug */ debug = T; /* don't fork */ break; case 'I': /* inbox specifier */ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE)); if (argc--) inbox = cpystr (*++argv); else _exit (fail ("missing argument to -I",EX_USAGE)); break; case 'f': /* new name for this flag */ case 'r': /* flag giving return path */ if (sender) _exit (fail ("duplicate -f or -r",EX_USAGE)); if (argc--) sender = cpystr (*++argv); else _exit (fail ("missing argument to -f or -r",EX_USAGE)); break; case 'b': /* create INBOX in this format */ if (inbox || format) _exit (fail ("duplicate -b or -I",EX_USAGE)); if (!argc--) _exit (fail ("missing argument to -b",EX_USAGE)); if (!(format = mail_parameters (NIL,GET_DRIVER,*++argv))) _exit (fail ("unknown format to -b",EX_USAGE)); else if (!(format->flags & DR_LOCAL) || !compare_cstring (format->name,"dummy")) _exit (fail ("invalid format to -b",EX_USAGE)); break; /* following flags are undocumented */ case 'p': /* precedence for quota */ if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2); else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s))) precedence = atol (s); else _exit (fail ("missing argument to -p",EX_USAGE)); break; case 'd': /* obsolete flag meaning multiple users */ break; /* ignore silently */ /* -s has been deprecated and replaced by the -s and -k flags in dmail. * dmail's -k flag does what -s once did in tmail; dmail's -s flag * takes no argument and just sets \Seen. Flag setting is more properly * done in dmail which runs as the user and is clearly at the user's * behest. Since tmail runs privileged, -s would have to be disabled * unless the caller is also privileged. */ case 's': /* obsolete flag meaning delivery flags */ if (!argc--) /* takes an argument */ _exit (fail ("missing argument to deprecated flag",EX_USAGE)); syslog (LOG_INFO,"tmail called with deprecated flag: -s %.200s",*++argv); break; default: /* anything else */ _exit (fail ("unknown switch",EX_USAGE)); } } if (!argc) ret = fail ("no recipients",EX_USAGE); else if (!(f = tmpfile ())) ret = fail ("can't make temp file",EX_TEMPFAIL); else { /* build delivery headers */ if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender); /* start Received line: */ fprintf (f,"Received: via tmail-%s.%s",CCLIENTVERSION,version); /* not root or daemon? */ if (ruid && !((pwd = getpwnam ("daemon")) && (ruid == pwd->pw_uid))) { pwd = getpwuid (ruid); /* get unprivileged user's information */ if (inbox || format) { if (pwd) sprintf (tmp,"user %.80s",pwd->pw_name); else sprintf (tmp,"UID %ld",(long) ruid); strcat (tmp," is not privileged to use -b or -I"); _exit (fail (tmp,EX_USAGE)); } fputs (" (invoked by ",f); if (pwd) fprintf (f,"user %s",pwd->pw_name); else fprintf (f,"UID %ld",(long) ruid); fputs (")",f); } /* write "for" if single recipient */ if (argc == 1) fprintf (f," for %s",*argv); fputs ("; ",f); rfc822_date (tmp); fputs (tmp,f); fputs ("\015\012",f); /* copy text from standard input */ if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) || (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE)); if (s[-1] == '\015') { /* nuke leading "From " line */ if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f); while ((c = getchar ()) != EOF) putc (c,f); } else { mm_log ("tmail called with LF-only newlines",WARN); if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') || (tmp[3] != 'm') || (tmp[4] != ' ')) { *s++ = '\015'; /* overwrite NL with CRLF */ *s++ = '\012'; *s = '\0'; /* tie off string */ fputs (tmp,f); /* write line */ } /* copy text from standard input */ while ((c = getchar ()) != EOF) { /* add CR if needed */ if (c == '\012') putc ('\015',f); putc (c,f); } } msglen = ftell (f); /* size of message */ fflush (f); /* make sure all changes written out */ if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL); else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL); /* single delivery */ else if (argc == 1) ret = deliver (f,msglen,*argv); else do { /* multiple delivery uses daughter forks */ if ((pid = fork ()) < 0) ret = fail (strerror (errno),EX_OSERR); else if (pid) { /* mother process */ grim_pid_reap_status (pid,NIL,(void *) status); /* normal termination? */ if (!ret) ret = (status & 0xff) ? EX_SOFTWARE : (status & 0xff00) >> 8; } /* daughter process */ else _exit (deliver (f,msglen,*argv)); } while (--argc && *argv++); mm_dlog (ret ? "error in delivery" : "all recipients delivered"); } if (f) fclose (f); /* all done with temporary file */ _exit (ret); /* normal exit */ return 0; /* stupid gcc */}/* Deliver message to recipient list * Accepts: file description of message temporary file * size of message temporary file in bytes * recipient name * Returns: NIL if success, else error code */int deliver (FILE *f,unsigned long msglen,char *user){ MAILSTREAM *ds = NIL; char *s,*t,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN]; struct passwd *pwd; STRING st; struct stat sbuf; uid_t duid; uid_t euid = geteuid (); /* get user record */ if (!(pwd = getpwnam (getusername (user,&mailbox)))) { sprintf (tmp,"no such user as %.80s",user); return fail (tmp,EX_NOUSER); } /* absurd is absurd */ if (mailbox && (strlen (mailbox) > 256)) return fail ("absurd folder name",EX_NOUSER); /* big security hole if this is allowed */ if (!(duid = pwd->pw_uid)) return fail ("mail to root prohibited",EX_NOUSER); /* log in as user if different than euid */ if ((duid != euid) && !loginpw (pwd,1,&user)) { sprintf (tmp,"unable to log in UID %ld from UID %ld", (long) duid,(long) euid); return fail (tmp,EX_NOUSER); } /* can't use pwd after this point */ env_init (pwd->pw_name,pwd->pw_dir); sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX"); mm_dlog (tmp); /* prepare stringstruct */ INIT (&st,file_string,(void *) f,msglen); if (mailbox) { /* non-INBOX name */ switch (mailbox[0]) { /* make sure a valid name */ default: /* other names, try to deliver if not INBOX */ if (!strstr (mailbox,"..") && !strstr (mailbox,"//") && !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] && !deliver_safely (NIL,&st,mailbox,path,duid,tmp)) return NIL; case '%': case '*': /* wildcards not valid */ case '#': /* namespace name not valid */ case '/': /* absolute path names not valid */ case '~': /* user names not valid */ sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox); mm_log (tmp,WARN); break; } mm_dlog ("retrying delivery to INBOX"); SETPOS (&st,0); /* rewind stringstruct just in case */ } /* -I specified and not "-I INBOX"? */ if (inbox && !(((inbox[0] == 'I') || (inbox[0] == 'i')) && ((inbox[1] == 'N') || (inbox[1] == 'n')) && ((inbox[2] == 'B') || (inbox[2] == 'b')) && ((inbox[3] == 'O') || (inbox[3] == 'o')) && ((inbox[4] == 'X') || (inbox[4] == 'x')) && !inbox[5])) { DRIVER *dv = NIL; /* "-I #driver.xxx/name"? */ if ((*inbox == '#') && ((inbox[1] == 'd') || (inbox[1] == 'D')) && ((inbox[2] == 'r') || (inbox[2] == 'R')) && ((inbox[3] == 'i') || (inbox[3] == 'I')) && ((inbox[4] == 'v') || (inbox[4] == 'V')) && ((inbox[5] == 'e') || (inbox[5] == 'E')) && ((inbox[6] == 'r') || (inbox[6] == 'R')) && (inbox[7] == '.') && (s = strchr (inbox+8,'/'))) { *s = '\0'; /* temporarily tie off driver name */ if (!((dv = mail_parameters (NIL,GET_DRIVER,(void *) (inbox+8))) && (mailboxfile (path,s[1] ? s + 1 : "&&&&&") == path) && (s[1] || ((t = strstr (path,"&&&&&")) && strcpy (t,"INBOX"))))) { path[0] = '\0'; /* bad -I argument, no path resolved */ sprintf (tmp,"Unable to resolve driver in %.80s, -I ignored",inbox); mm_log (tmp,WARN); } *s = '/'; /* restore delimiter */ } /* resolve "-I other" specification */ else if (mailboxfile (path,inbox) && path[0]) { /* resolution succeeded, impute driver */ if (!strcmp (inbox,"mail.txt")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "tenex"); else if (!strcmp (inbox,"INBOX.MTX")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "mtx"); else if (!strcmp (inbox,"mbox")) dv = mail_parameters (NIL,GET_DRIVER,(void *) "unix"); } else { /* bad -I argument */ path[0] = '\0'; /* no path resolved */ sprintf (tmp,"Unable to resolve %.80s, -I ignored",inbox); mm_log (tmp,WARN); } if (*path) { /* -I successfully resolved a path? */ MAILSTREAM dpr; dpr.dtb = dv; if (dv) ds = &dpr; /* supplicate to the Evil One if necessary */ if (lstat (path,&sbuf) && !path_create (ds,path)) { /* the Evil One rejected the plea */ sprintf (tmp,"Unable to create %.80s, -I ignored",path); mm_log (tmp,WARN); } /* now attempt delivery */ else return deliver_safely (ds,&st,inbox,path,duid,tmp); } } /* no -I, resolve "INBOX" into path */ if (mailboxfile (path,mailbox = "INBOX") && !path[0]) { /* clear box, get generic INBOX prototype */ if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE))) fatal ("no INBOX prototype"); /* standard system driver? */ if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) { strcpy (path,sysinbox ());/* use system INBOX */ if (!lstat (path,&sbuf)) /* deliver to existing system INBOX */ return deliver_safely (ds,&st,mailbox,path,duid,tmp); } else { /* other driver, try ~/INBOX */ if ((mailboxfile (path,"&&&&&") == path) && (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") && !lstat (path,&sbuf)){ /* deliver to existing ~/INBOX */ sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name); return deliver_safely (ds,&st,cpystr (tmp),path,duid,tmp); } } /* not dummy, deliver to driver imputed path */ if (strcmp (ds->dtb->name,"dummy")) return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -