if (fflush(f) < 0 ||
if (fflush(f) != 0 ||
mailfile(filename, mailer, ctladdr, sfflags, e)	char *volatile filename;	MAILER *volatile mailer;	ADDRESS *ctladdr;	volatile long sfflags;	register ENVELOPE *e;{	register FILE *f;	register pid_t pid = -1;	volatile int mode;	int len;	off_t curoff;	bool suidwarn = geteuid() == 0;	char *p;	char *volatile realfile;	EVENT *ev;	char buf[MAXLINE + 1];	char targetfile[MAXPATHLEN + 1];	if (tTd(11, 1))	{		dprintf("mailfile %s\n  ctladdr=", filename);		printaddr(ctladdr, FALSE);	}	if (mailer == NULL)		mailer = FileMailer;	if (e->e_xfp != NULL)		(void) fflush(e->e_xfp);	/*	**  Special case /dev/null.  This allows us to restrict file	**  delivery to regular files only.	*/	if (strcmp(filename, "/dev/null") == 0)		return EX_OK;	/* check for 8-bit available */	if (bitset(EF_HAS8BIT, e->e_flags) &&	    bitnset(M_7BITS, mailer->m_flags) &&	    (bitset(EF_DONT_MIME, e->e_flags) ||	     !(bitset(MM_MIME8BIT, MimeMode) ||	       (bitset(EF_IS_MIME, e->e_flags) &&		bitset(MM_CVTMIME, MimeMode)))))	{		e->e_status = "5.6.3";		usrerrenh(e->e_status,		       "554 Cannot send 8-bit data to 7-bit destination");		return EX_DATAERR;	}	/* Find the actual file */	if (SafeFileEnv != NULL && SafeFileEnv[0] != '\0')	{		len = strlen(SafeFileEnv);		if (strncmp(SafeFileEnv, filename, len) == 0)			filename += len;		if (len + strlen(filename) + 1 > MAXPATHLEN)		{			syserr("mailfile: filename too long (%s/%s)",			       SafeFileEnv, filename);			return EX_CANTCREAT;		}		(void) strlcpy(targetfile, SafeFileEnv, sizeof targetfile);		realfile = targetfile + len;		if (targetfile[len - 1] != '/')			(void) strlcat(targetfile, "/", sizeof targetfile);		if (*filename == '/')			filename++;		(void) strlcat(targetfile, filename, sizeof targetfile);	}	else if (mailer->m_rootdir != NULL)	{		expand(mailer->m_rootdir, targetfile, sizeof targetfile, e);		len = strlen(targetfile);		if (strncmp(targetfile, filename, len) == 0)			filename += len;		if (len + strlen(filename) + 1 > MAXPATHLEN)		{			syserr("mailfile: filename too long (%s/%s)",			       targetfile, filename);			return EX_CANTCREAT;		}		realfile = targetfile + len;		if (targetfile[len - 1] != '/')			(void) strlcat(targetfile, "/", sizeof targetfile);		if (*filename == '/')			(void) strlcat(targetfile, filename + 1,				       sizeof targetfile);		else			(void) strlcat(targetfile, filename, sizeof targetfile);	}	else	{		if (strlen(filename) > MAXPATHLEN)		{			syserr("mailfile: filename too long (%s)", filename);			return EX_CANTCREAT;		}		(void) strlcpy(targetfile, filename, sizeof targetfile);		realfile = targetfile;	}	/*	**  Fork so we can change permissions here.	**	Note that we MUST use fork, not vfork, because of	**	the complications of calling subroutines, etc.	*/	DOFORK(fork);	if (pid < 0)		return EX_OSERR;	else if (pid == 0)	{		/* child -- actually write to file */		struct stat stb;		MCI mcibuf;		int err;		volatile int oflags = O_WRONLY|O_APPEND;		if (e->e_lockfp != NULL)			(void) close(fileno(e->e_lockfp));		(void) setsignal(SIGINT, SIG_DFL);		(void) setsignal(SIGHUP, SIG_DFL);		(void) setsignal(SIGTERM, SIG_DFL);		(void) umask(OldUmask);		e->e_to = filename;		ExitStat = EX_OK;		if (setjmp(CtxMailfileTimeout) != 0)		{			exit(EX_TEMPFAIL);		}		if (TimeOuts.to_fileopen > 0)			ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0);		else			ev = NULL;		/* check file mode to see if setuid */		if (stat(targetfile, &stb) < 0)			mode = FileMode;		else			mode = stb.st_mode;		/* limit the errors to those actually caused in the child */		errno = 0;		ExitStat = EX_OK;		/* Allow alias expansions to use the S_IS{U,G}ID bits */		if ((ctladdr != NULL && !bitset(QALIAS, ctladdr->q_flags)) ||		    bitset(SFF_RUNASREALUID, sfflags))		{			/* ignore setuid and setgid bits */			mode &= ~(S_ISGID|S_ISUID);			if (tTd(11, 20))				dprintf("mailfile: ignoring setuid/setgid bits\n");		}		/* we have to open the dfile BEFORE setuid */		if (e->e_dfp == NULL && bitset(EF_HAS_DF, e->e_flags))		{			char *df = queuename(e, 'd');			e->e_dfp = fopen(df, "r");			if (e->e_dfp == NULL)			{				syserr("mailfile: Cannot open %s for %s from %s",					df, e->e_to, e->e_from.q_paddr);			}		}		/* select a new user to run as */		if (!bitset(SFF_RUNASREALUID, sfflags))		{			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))			{				RealUserName = NULL;				RealUid = mailer->m_uid;				if (RunAsUid != 0 && RealUid != RunAsUid)				{					/* Only root can change the uid */					syserr("mailfile: insufficient privileges to change uid");					exit(EX_TEMPFAIL);				}			}			else if (bitset(S_ISUID, mode))			{				RealUserName = NULL;				RealUid = stb.st_uid;			}			else if (ctladdr != NULL && ctladdr->q_uid != 0)			{				if (ctladdr->q_ruser != NULL)					RealUserName = ctladdr->q_ruser;				else					RealUserName = ctladdr->q_user;				RealUid = ctladdr->q_uid;			}			else if (mailer != NULL && mailer->m_uid != 0)			{				RealUserName = DefUser;				RealUid = mailer->m_uid;			}			else			{				RealUserName = DefUser;				RealUid = DefUid;			}			/* select a new group to run as */			if (bitnset(M_SPECIFIC_UID, mailer->m_flags))			{				RealGid = mailer->m_gid;				if (RunAsUid != 0 &&				    (RealGid != getgid() ||				     RealGid != getegid()))				{					/* Only root can change the gid */					syserr("mailfile: insufficient privileges to change gid");					exit(EX_TEMPFAIL);				}			}			else if (bitset(S_ISGID, mode))				RealGid = stb.st_gid;			else if (ctladdr != NULL &&				 ctladdr->q_uid == DefUid &&				 ctladdr->q_gid == 0)			{				/*				**  Special case:  This means it is an				**  alias and we should act as DefaultUser.				**  See alias()'s comments.				*/				RealGid = DefGid;				RealUserName = DefUser;			}			else if (ctladdr != NULL && ctladdr->q_uid != 0)				RealGid = ctladdr->q_gid;			else if (mailer != NULL && mailer->m_gid != 0)				RealGid = mailer->m_gid;			else				RealGid = DefGid;		}		/* last ditch */		if (!bitset(SFF_ROOTOK, sfflags))		{			if (RealUid == 0)				RealUid = DefUid;			if (RealGid == 0)				RealGid = DefGid;		}		/* set group id list (needs /etc/group access) */		if (RealUserName != NULL && !DontInitGroups)		{			if (initgroups(RealUserName, RealGid) == -1 && suidwarn)			{				syserr("mailfile: initgroups(%s, %d) failed",					RealUserName, RealGid);				exit(EX_TEMPFAIL);			}		}		else		{			GIDSET_T gidset[1];			gidset[0] = RealGid;			if (setgroups(1, gidset) == -1 && suidwarn)			{				syserr("mailfile: setgroups() failed");				exit(EX_TEMPFAIL);			}		}		/*		**  If you have a safe environment, go into it.		*/		if (realfile != targetfile)		{			*realfile = '\0';			if (tTd(11, 20))				dprintf("mailfile: chroot %s\n", targetfile);			if (chroot(targetfile) < 0)			{				syserr("mailfile: Cannot chroot(%s)",				       targetfile);				exit(EX_CANTCREAT);			}			*realfile = '/';		}		if (tTd(11, 40))			dprintf("mailfile: deliver to %s\n", realfile);		if (chdir("/") < 0)		{			syserr("mailfile: cannot chdir(/)");			exit(EX_CANTCREAT);		}		/* now reset the group and user ids */		endpwent();		if (setgid(RealGid) < 0 && suidwarn)		{			syserr("mailfile: setgid(%ld) failed", (long) RealGid);			exit(EX_TEMPFAIL);		}		vendor_set_uid(RealUid);		if (setuid(RealUid) < 0 && suidwarn)		{			syserr("mailfile: setuid(%ld) failed", (long) RealUid);			exit(EX_TEMPFAIL);		}		if (tTd(11, 2))			dprintf("mailfile: running as r/euid=%d/%d, r/egid=%d/%d\n",				(int) getuid(), (int) geteuid(),				(int) getgid(), (int) getegid());		/* move into some "safe" directory */		if (mailer->m_execdir != NULL)		{			char *q;			for (p = mailer->m_execdir; p != NULL; p = q)			{				q = strchr(p, ':');				if (q != NULL)					*q = '\0';				expand(p, buf, sizeof buf, e);				if (q != NULL)					*q++ = ':';				if (tTd(11, 20))					dprintf("mailfile: trydir %s\n", buf);				if (buf[0] != '\0' && chdir(buf) >= 0)					break;			}		}		/*		**  Recheck the file after we have assumed the ID of the		**  delivery user to make sure we can deliver to it as		**  that user.  This is necessary if sendmail is running		**  as root and the file is on an NFS mount which treats		**  root as nobody.		*/#if HASLSTAT		if (bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))			err = stat(realfile, &stb);		else			err = lstat(realfile, &stb);#else /* HASLSTAT */		err = stat(realfile, &stb);#endif /* HASLSTAT */		if (err < 0)		{			stb.st_mode = ST_MODE_NOFILE;			mode = FileMode;			oflags |= O_CREAT|O_EXCL;		}		else if (bitset(S_IXUSR|S_IXGRP|S_IXOTH, mode) ||			 (!bitnset(DBS_FILEDELIVERYTOHARDLINK,				   DontBlameSendmail) &&			  stb.st_nlink != 1) ||			 (realfile != targetfile && !S_ISREG(mode)))			exit(EX_CANTCREAT);		else			mode = stb.st_mode;		if (!bitnset(DBS_FILEDELIVERYTOSYMLINK, DontBlameSendmail))			sfflags |= SFF_NOSLINK;		if (!bitnset(DBS_FILEDELIVERYTOHARDLINK, DontBlameSendmail))			sfflags |= SFF_NOHLINK;		sfflags &= ~SFF_OPENASROOT;		f = safefopen(realfile, oflags, mode, sfflags);		if (f == NULL)		{			if (transienterror(errno))			{				usrerr("454 4.3.0 cannot open %s: %s",				       shortenstring(realfile, MAXSHORTSTR),				       errstring(errno));				exit(EX_TEMPFAIL);			}			else			{				usrerr("554 5.3.0 cannot open %s: %s",				       shortenstring(realfile, MAXSHORTSTR),				       errstring(errno));				exit(EX_CANTCREAT);			}		}		if (filechanged(realfile, fileno(f), &stb))		{			syserr("554 5.3.0 file changed after open");			exit(EX_CANTCREAT);		}		if (fstat(fileno(f), &stb) < 0)		{			syserr("554 5.3.0 cannot fstat %s", errstring(errno));			exit(EX_CANTCREAT);		}		curoff = stb.st_size;		if (ev != NULL)			clrevent(ev);		memset(&mcibuf, '\0', sizeof mcibuf);		mcibuf.mci_mailer = mailer;		mcibuf.mci_out = f;		if (bitnset(M_7BITS, mailer->m_flags))			mcibuf.mci_flags |= MCIF_7BIT;		/* clear out per-message flags from connection structure */		mcibuf.mci_flags &= ~(MCIF_CVT7TO8|MCIF_CVT8TO7);		if (bitset(EF_HAS8BIT, e->e_flags) &&		    !bitset(EF_DONT_MIME, e->e_flags) &&		    bitnset(M_7BITS, mailer->m_flags))			mcibuf.mci_flags |= MCIF_CVT8TO7;#if MIME7TO8		if (bitnset(M_MAKE8BIT, mailer->m_flags) &&		    !bitset(MCIF_7BIT, mcibuf.mci_flags) &&		    (p = hvalue("Content-Transfer-Encoding", e->e_header)) != NULL &&		    (strcasecmp(p, "quoted-printable") == 0 ||		     strcasecmp(p, "base64") == 0) &&		    (p = hvalue("Content-Type", e->e_header)) != NULL)		{			/* may want to convert 7 -> 8 */			/* XXX should really parse it here -- and use a class XXX */			if (strncasecmp(p, "text/plain", 10) == 0 &&			    (p[10] == '\0' || p[10] == ' ' || p[10] == ';'))				mcibuf.mci_flags |= MCIF_CVT7TO8;		}#endif /* MIME7TO8 */		putfromline(&mcibuf, e);		(*e->e_puthdr)(&mcibuf, e->e_header, e, M87F_OUTER);		(*e->e_putbody)(&mcibuf, e, NULL);		putline("\n", &mcibuf);		if (fflush(f) < 0 ||		    (SuperSafe && fsync(fileno(f)) < 0) ||		    ferror(f))		{			setstat(EX_IOERR);#if !NOFTRUNCATE			(void) ftruncate(fileno(f), curoff);#endif /* !NOFTRUNCATE */		}		/* reset ISUID & ISGID bits for paranoid systems */#if HASFCHMOD		(void) fchmod(fileno(f), (MODE_T) mode);#else /* HASFCHMOD */		(void) chmod(filename, (MODE_T) mode);#endif /* HASFCHMOD */		if (fclose(f) < 0)			setstat(EX_IOERR);		(void) fflush(stdout);		(void) setuid(RealUid);		exit(ExitStat);		/* NOTREACHED */	}	else	{		/* parent -- wait for exit status */		int st;		st = waitfor(pid);		if (st == -1)		{			syserr("mailfile: %s: wait", mailer->m_name);			return EX_SOFTWARE;		}		if (WIFEXITED(st))			return (WEXITSTATUS(st));		else		{			syserr("mailfile: %s: child died on signal %d",			       mailer->m_name, st);			return EX_UNAVAILABLE;		}		/* NOTREACHED */	}	return EX_UNAVAILABLE;	/* avoid compiler warning on IRIX */}