⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tmail.c

📁 广泛使用的邮件服务器!同时
💻 C
📖 第 1 页 / 共 2 页
字号:
/* ======================================================================== * 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 + -