+#include <pwd.h>
+#include <time.h>
+#include "sendmail.h"
+#include <sys/stat.h>
+
+SCCSID(@(#)envelope.c 3.1 %G%);
+
+/*
+** NEWENVELOPE -- allocate a new envelope
+**
+** Supports inheritance.
+**
+** Parameters:
+** e -- the new envelope to fill in.
+**
+** Returns:
+** e.
+**
+** Side Effects:
+** none.
+*/
+
+ENVELOPE *
+newenvelope(e)
+ register ENVELOPE *e;
+{
+ register HDR *bh;
+ register HDR **nhp;
+ register ENVELOPE *parent;
+ extern putheader(), putbody();
+ extern ENVELOPE BlankEnvelope;
+
+ parent = CurEnv;
+ if (e == CurEnv)
+ parent = e->e_parent;
+ clear((char *) e, sizeof *e);
+ bmove((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
+ e->e_parent = parent;
+ e->e_ctime = curtime();
+ e->e_puthdr = putheader;
+ e->e_putbody = putbody;
+ bh = BlankEnvelope.e_header;
+ nhp = &e->e_header;
+ while (bh != NULL)
+ {
+ *nhp = (HDR *) xalloc(sizeof *bh);
+ bmove((char *) bh, (char *) *nhp, sizeof *bh);
+ bh = bh->h_link;
+ nhp = &(*nhp)->h_link;
+ }
+ if (CurEnv->e_xfp != NULL)
+ (void) fflush(CurEnv->e_xfp);
+
+ return (e);
+}
+\f/*
+** DROPENVELOPE -- deallocate an envelope.
+**
+** Parameters:
+** e -- the envelope to deallocate.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** housekeeping necessary to dispose of an envelope.
+** Unlocks this queue file.
+*/
+
+dropenvelope(e)
+ register ENVELOPE *e;
+{
+ bool queueit = FALSE;
+ register ADDRESS *q;
+
+#ifdef DEBUG
+ if (tTd(50, 1))
+ {
+ printf("dropenvelope %x id=", e);
+ xputs(e->e_id);
+ printf(" flags=%o\n", e->e_flags);
+ }
+#endif DEBUG
+#ifdef LOG
+ if (LogLevel > 10)
+ syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d",
+ e->e_id == NULL ? "(none)" : e->e_id,
+ e->e_flags, getpid());
+#endif LOG
+
+ /* we must have an id to remove disk files */
+ if (e->e_id == NULL)
+ return;
+
+ /*
+ ** Extract state information from dregs of send list.
+ */
+
+ for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+ {
+ if (bitset(QQUEUEUP, q->q_flags))
+ queueit = TRUE;
+ }
+
+ /*
+ ** Send back return receipts as requested.
+ */
+
+ if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags))
+ {
+ auto ADDRESS *rlist;
+
+ sendto(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist);
+ (void) returntosender("Return receipt", rlist, FALSE);
+ }
+
+ /*
+ ** See if this message has timed out
+ */
+
+ if (bitset(EF_TIMEOUT, e->e_flags) && queueit)
+ timeout(e);
+
+ /*
+ ** Arrange to send error messages if there are fatal errors.
+ */
+
+ if (bitset(EF_FATALERRS, e->e_flags))
+ savemail(e);
+
+ /*
+ ** Instantiate or deinstantiate the queue.
+ */
+
+ if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
+ bitset(EF_CLRQUEUE, e->e_flags))
+ {
+ if (e->e_dfp != NULL)
+ (void) fclose(e->e_dfp);
+ if (e->e_df != NULL)
+ xunlink(e->e_df);
+ xunlink(queuename(e, 'q'));
+ }
+ else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
+ queueup(e, FALSE, FALSE);
+
+ /* now unlock the job */
+ if (e->e_xfp != NULL)
+ (void) fclose(e->e_xfp);
+ unlockqueue(e);
+
+ /* make sure that this envelope is marked unused */
+ e->e_id = e->e_df = NULL;
+ e->e_dfp = e->e_xfp = NULL;
+}
+\f/*
+** CLEARENVELOPE -- clear an envelope without unlocking
+**
+** This is normally used by a child process to get a clean
+** envelope without disturbing the parent.
+**
+** Parameters:
+** e -- the envelope to clear.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Closes files associated with the envelope.
+** Marks the envelope as unallocated.
+*/
+
+clearenvelope(e)
+ register ENVELOPE *e;
+{
+ /* clear out any file information */
+ if (e->e_xfp != NULL)
+ (void) fclose(e->e_xfp);
+ if (e->e_dfp != NULL)
+ (void) fclose(e->e_dfp);
+ e->e_xfp = e->e_dfp = NULL;
+
+ /* now expunge names of objects */
+ e->e_df = e->e_id = NULL;
+
+ /* and the flags which are now meaningless */
+ e->e_flags = 0;
+}
+\f/*
+** UNLOCKQUEUE -- unlock the queue entry for a specified envelope
+**
+** Parameters:
+** e -- the envelope to unlock.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** unlocks the queue for `e'.
+*/
+
+unlockqueue(e)
+ ENVELOPE *e;
+{
+ /* remove the transcript */
+#ifdef DEBUG
+ if (!tTd(51, 4))
+#endif DEBUG
+ xunlink(queuename(e, 'x'));
+
+ /* last but not least, remove the lock */
+ xunlink(queuename(e, 'l'));
+}
+\f/*
+** INITSYS -- initialize instantiation of system
+**
+** In Daemon mode, this is done in the child.
+**
+** Parameters:
+** none.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** Initializes the system macros, some global variables,
+** etc. In particular, the current time in various
+** forms is set.
+*/
+
+initsys()
+{
+ auto time_t now;
+ static char cbuf[5]; /* holds hop count */
+ static char dbuf[30]; /* holds ctime(tbuf) */
+ static char pbuf[10]; /* holds pid */
+ static char tbuf[20]; /* holds "current" time */
+ static char ybuf[10]; /* holds tty id */
+ register char *p;
+ register struct tm *tm;
+ extern char *ttyname();
+ extern char *arpadate();
+ extern struct tm *gmtime();
+ extern char *macvalue();
+ extern char Version[];
+
+ /*
+ ** Give this envelope a reality.
+ ** I.e., an id, a transcript, and a creation time.
+ */
+
+ openxscript(CurEnv);
+ CurEnv->e_ctime = curtime();
+
+ /*
+ ** Set OutChannel to something useful if stdout isn't it.
+ ** This arranges that any extra stuff the mailer produces
+ ** gets sent back to the user on error (because it is
+ ** tucked away in the transcript).
+ */
+
+ if (OpMode == MD_DAEMON && QueueRun)
+ OutChannel = CurEnv->e_xfp;
+
+ /*
+ ** Set up some basic system macros.
+ */
+
+ /* process id */
+ (void) sprintf(pbuf, "%d", getpid());
+ define('p', pbuf, CurEnv);
+
+ /* hop count */
+ (void) sprintf(cbuf, "%d", CurEnv->e_hopcount);
+ define('c', cbuf, CurEnv);
+
+ /* time as integer, unix time, arpa time */
+ now = curtime();
+ tm = gmtime(&now);
+ (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon,
+ tm->tm_mday, tm->tm_hour, tm->tm_min);
+ define('t', tbuf, CurEnv);
+ (void) strcpy(dbuf, ctime(&now));
+ *index(dbuf, '\n') = '\0';
+ if (macvalue('d', CurEnv) == NULL)
+ define('d', dbuf, CurEnv);
+ p = newstr(arpadate(dbuf));
+ if (macvalue('a', CurEnv) == NULL)
+ define('a', p, CurEnv);
+ define('b', p, CurEnv);
+
+ /* version */
+ define('v', Version, CurEnv);
+
+ /* tty name */
+ if (macvalue('y', CurEnv) == NULL)
+ {
+ p = ttyname(2);
+ if (p != NULL)
+ {
+ if (rindex(p, '/') != NULL)
+ p = rindex(p, '/') + 1;
+ (void) strcpy(ybuf, p);
+ define('y', ybuf, CurEnv);
+ }
+ }
+}
+\f/*
+** QUEUENAME -- build a file name in the queue directory for this envelope.
+**
+** Assigns an id code if one does not already exist.
+** This code is very careful to avoid trashing existing files
+** under any circumstances.
+** We first create an nf file that is only used when
+** assigning an id. This file is always empty, so that
+** we can never accidently truncate an lf file.
+**
+** Parameters:
+** e -- envelope to build it in/from.
+** type -- the file type, used as the first character
+** of the file name.
+**
+** Returns:
+** a pointer to the new file name (in a static buffer).
+**
+** Side Effects:
+** Will create the lf and qf files if no id code is
+** already assigned. This will cause the envelope
+** to be modified.
+*/
+
+char *
+queuename(e, type)
+ register ENVELOPE *e;
+ char type;
+{
+ static char buf[MAXNAME];
+ static int pid = -1;
+ char c1 = 'A';
+ char c2 = 'A';
+
+ if (e->e_id == NULL)
+ {
+ char qf[20];
+ char lf[20];
+ char nf[20];
+
+ /* find a unique id */
+ if (pid != getpid())
+ {
+ /* new process -- start back at "AA" */
+ pid = getpid();
+ c1 = 'A';
+ c2 = 'A' - 1;
+ }
+ (void) sprintf(qf, "qfAA%05d", pid);
+ strcpy(lf, qf);
+ lf[0] = 'l';
+ strcpy(nf, qf);
+ nf[0] = 'n';
+
+ while (c1 < '~' || c2 < 'Z')
+ {
+ int i;
+
+ if (c2 >= 'Z')
+ {
+ c1++;
+ c2 = 'A' - 1;
+ }
+ qf[2] = lf[2] = nf[2] = c1;
+ qf[3] = lf[3] = nf[3] = ++c2;
+# ifdef DEBUG
+ if (tTd(7, 20))
+ printf("queuename: trying \"%s\"\n", nf);
+# endif DEBUG
+ if (access(lf, 0) >= 0 || access(qf, 0) >= 0)
+ continue;
+ errno = 0;
+ i = creat(nf, FileMode);
+ if (i < 0)
+ {
+ (void) unlink(nf); /* kernel bug */
+ continue;
+ }
+ (void) close(i);
+ i = link(nf, lf);
+ (void) unlink(nf);
+ if (i < 0)
+ continue;
+ if (link(lf, qf) >= 0)
+ break;
+ (void) unlink(lf);
+ }
+ if (c1 >= '~' && c2 >= 'Z')
+ {
+ syserr("queuename: Cannot create \"%s\" in \"%s\"",
+ lf, QueueDir);
+ exit(EX_OSERR);
+ }
+ e->e_id = newstr(&qf[2]);
+ define('i', e->e_id, e);
+# ifdef DEBUG
+ if (tTd(7, 1))
+ printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
+# endif DEBUG
+ }
+
+ if (type == '\0')
+ return (NULL);
+ (void) sprintf(buf, "%cf%s", type, e->e_id);
+# ifdef DEBUG
+ if (tTd(7, 2))
+ printf("queuename: %s\n", buf);
+# endif DEBUG
+ return (buf);
+}
+\f/*
+** OPENXSCRIPT -- Open transcript file
+**
+** Creates a transcript file for possible eventual mailing or
+** sending back.
+**
+** Parameters:
+** e -- the envelope to create the transcript in/for.
+**
+** Returns:
+** none
+**
+** Side Effects:
+** Creates the transcript file.
+*/
+
+openxscript(e)
+ register ENVELOPE *e;
+{
+ register char *p;
+
+ if (e->e_xfp != NULL)
+ return;
+ p = queuename(e, 'x');
+ e->e_xfp = fopen(p, "w");
+ if (e->e_xfp == NULL)
+ syserr("Can't create %s", p);
+ else
+ (void) chmod(p, 0644);
+}
+\f/*
+** SETSENDER -- set the person who this message is from
+**
+** Under certain circumstances allow the user to say who
+** s/he is (using -f or -r). These are:
+** 1. The user's uid is zero (root).
+** 2. The user's login name is in an approved list (typically
+** from a network server).
+** 3. The address the user is trying to claim has a
+** "!" character in it (since #2 doesn't do it for
+** us if we are dialing out for UUCP).
+** A better check to replace #3 would be if the
+** effective uid is "UUCP" -- this would require me
+** to rewrite getpwent to "grab" uucp as it went by,
+** make getname more nasty, do another passwd file
+** scan, or compile the UID of "UUCP" into the code,
+** all of which are reprehensible.
+**
+** Assuming all of these fail, we figure out something
+** ourselves.
+**
+** Parameters:
+** from -- the person we would like to believe this message
+** is from, as specified on the command line.
+**
+** Returns:
+** none.
+**
+** Side Effects:
+** sets sendmail's notion of who the from person is.
+*/
+
+setsender(from)
+ char *from;
+{
+ register char **pvp;
+ register struct passwd *pw = NULL;
+ char *realname = NULL;
+ char buf[MAXNAME];
+ extern char *macvalue();
+ extern char **prescan();
+ extern bool safefile();
+ extern char *FullName;
+
+# ifdef DEBUG
+ if (tTd(45, 1))
+ printf("setsender(%s)\n", from);
+# endif DEBUG
+
+ /*
+ ** Figure out the real user executing us.
+ ** Username can return errno != 0 on non-errors.
+ */
+
+ if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
+ realname = from;
+ if (realname == NULL || realname[0] == '\0')
+ {
+ extern char *username();
+
+ realname = username();
+ errno = 0;
+ }
+ if (realname == NULL || realname[0] == '\0')
+ {
+ extern struct passwd *getpwuid();
+
+ pw = getpwuid(getruid());
+ if (pw != NULL)
+ realname = pw->pw_name;
+ }
+ if (realname == NULL || realname[0] == '\0')
+ {
+ syserr("Who are you?");
+ realname = "root";
+ }
+
+ /*
+ ** Determine if this real person is allowed to alias themselves.
+ */
+
+ if (from != NULL)
+ {
+ extern bool trusteduser();
+
+ if (!trusteduser(realname) &&
+# ifdef DEBUG
+ (!tTd(1, 9) || getuid() != geteuid()) &&
+# endif DEBUG
+ index(from, '!') == NULL && getuid() != 0)
+ {
+ /* network sends -r regardless (why why why?) */
+ /* syserr("%s, you cannot use the -f flag", realname); */
+ from = NULL;
+ }
+ }
+
+ SuprErrs = TRUE;
+ if (from == NULL || parse(from, &CurEnv->e_from, 1) == NULL)
+ {
+ from = newstr(realname);
+ (void) parse(from, &CurEnv->e_from, 1);
+ }
+ else
+ FromFlag = TRUE;
+ CurEnv->e_from.q_flags |= QDONTSEND;
+ SuprErrs = FALSE;
+
+ if (pw == NULL && CurEnv->e_from.q_mailer == LocalMailer)
+ {
+ extern struct passwd *getpwnam();
+
+ pw = getpwnam(CurEnv->e_from.q_user);
+ }
+
+ /*
+ ** Process passwd file entry.
+ */
+
+ if (pw != NULL)
+ {
+ /* extract home directory */
+ CurEnv->e_from.q_home = newstr(pw->pw_dir);
+
+ /* run user's .mailcf file */
+ define('z', CurEnv->e_from.q_home, CurEnv);
+ expand("$z/.mailcf", buf, &buf[sizeof buf - 1], CurEnv);
+ if (safefile(buf, getruid(), S_IREAD))
+ readcf(buf, FALSE);
+
+ /* if the user has given fullname already, don't redefine */
+ if (FullName == NULL)
+ FullName = macvalue('x', CurEnv);
+ if (FullName[0] == '\0')
+ FullName = NULL;
+
+ /* extract full name from passwd file */
+ if (FullName == NULL && pw->pw_gecos != NULL)
+ {
+ buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf);
+ if (buf[0] != '\0')
+ FullName = newstr(buf);
+ }
+ if (FullName != NULL)
+ define('x', FullName, CurEnv);
+ }
+
+#ifndef V6
+ if (CurEnv->e_from.q_home == NULL)
+ CurEnv->e_from.q_home = getenv("HOME");
+#endif V6
+ CurEnv->e_from.q_uid = getuid();
+ CurEnv->e_from.q_gid = getgid();
+ if (CurEnv->e_from.q_uid != 0)
+ {
+ DefUid = CurEnv->e_from.q_uid;
+ DefGid = CurEnv->e_from.q_gid;
+ }
+
+ /*
+ ** Rewrite the from person to dispose of possible implicit
+ ** links in the net.
+ */
+
+ pvp = prescan(from, '\0');
+ if (pvp == NULL)
+ {
+ syserr("cannot prescan from (%s)", from);
+ finis();
+ }
+ rewrite(pvp, 3);
+ rewrite(pvp, 1);
+ cataddr(pvp, buf, sizeof buf);
+ define('f', newstr(buf), CurEnv);
+
+ /* save the domain spec if this mailer wants it */
+ if (bitset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags))
+ {
+ extern char **copyplist();
+
+ while (*pvp != NULL && strcmp(*pvp, "@") != 0)
+ pvp++;
+ if (*pvp != NULL)
+ CurEnv->e_fromdomain = copyplist(pvp, TRUE);
+ }
+}
+\f/*
+** TRUSTEDUSER -- tell us if this user is to be trusted.
+**
+** Parameters:
+** user -- the user to be checked.
+**
+** Returns:
+** TRUE if the user is in an approved list.
+** FALSE otherwise.
+**
+** Side Effects:
+** none.
+*/
+
+bool
+trusteduser(user)
+ char *user;
+{
+ register char **ulist;
+ extern char *TrustedUsers[];
+
+ for (ulist = TrustedUsers; *ulist != NULL; ulist++)
+ if (strcmp(*ulist, user) == 0)
+ return (TRUE);
+ return (FALSE);
+}