diff options
Diffstat (limited to 'maildrop/main.C')
| -rw-r--r-- | maildrop/main.C | 1009 | 
1 files changed, 1009 insertions, 0 deletions
| diff --git a/maildrop/main.C b/maildrop/main.C new file mode 100644 index 0000000..9d281eb --- /dev/null +++ b/maildrop/main.C @@ -0,0 +1,1009 @@ +/* +** Copyright 1998 - 2009 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#include	"lexer.h" +#include	"recipe.h" +#include	"varlist.h" +#include	"funcs.h" +#include	"tempfile.h" +#include	"message.h" +#include	"messageinfo.h" +#include	"xconfig.h" +#include	"exittrap.h" +#include	"maildrop.h" +#include	"config.h" +#include	"setgroupid.h" +#include	<sys/types.h> + +#if	HAVE_LOCALE_H +#include	<locale.h> +#endif + +#if HAVE_SYS_STAT_H +#include	<sys/stat.h> +#endif +#include	<sysexits.h> +#include	<string.h> +#include	<stdio.h> +#include	<stdlib.h> +#include	<ctype.h> +#include	<pwd.h> +#include	<grp.h> +#include	"../dbobj.h" +#if AUTHLIB +#include	<courierauth.h> +#endif + +#if	HAS_GETHOSTNAME +#else +extern "C" int gethostname(const char *, size_t); +#endif + + +extern void setprocgroup(); + +static Message m1, m2; +extern char **environ; +static int errexit=EX_TEMPFAIL; +int quota_warn_percent = -1; +const char *quota_warn_message=0; + +static const char *defaults_vars[]={"LOCKEXT","LOCKSLEEP","LOCKTIMEOUT", +					"LOCKREFRESH", "PATH", "SENDMAIL", +					"MAILDIRQUOTA"}; +static const char *defaults_vals[]={LOCKEXT_DEF,LOCKSLEEP_DEF,LOCKTIMEOUT_DEF, +					LOCKREFRESH_DEF, DEFAULT_PATH, +					SENDMAIL_DEF, ""}; + +Maildrop maildrop; + +Maildrop::Maildrop() +{ +	verbose_level=0; +	isdelivery=0; +	sigfpe=0; +	includelevel=0; +	embedded_mode=0; +	msgptr= &m1; +	savemsgptr= &m2; +#if AUTHLIB_TEMPREJECT +	authlib_essential=1; +#else +	authlib_essential=0; +#endif +} + +static void help() +{ +	mout << "Usage: maildrop [options] [-d user] [arg] [arg] ...\n"; +	mout << "       maildrop [options] [filterfile [arg] [arg] ...\n"; +} + +static void bad() +{ +	errexit=EX_TEMPFAIL; +	throw "Bad command line arguments, -h for help."; +} + +static void nouser() +{ +	merr << "Invalid user specified.\n"; +	exit(EX_NOUSER); +} + +static void nochangeuidgid() +{ +	errexit=EX_TEMPFAIL; +	throw "Cannot set my user or group id."; +} + +static int trusted_user(uid_t uid) +{ +static char trusted_users[]=TRUSTED_USERS; +static char buf[ sizeof(trusted_users) ]; +char	*p; + +	strcpy(buf, trusted_users); +	for (p=buf; (p=strtok(p, " ")) != 0; p=0) +	{ +	struct	passwd *q=getpwnam(p); + +		if (q && q->pw_uid == uid) +			return (1); +	} +	return (0); +} + +static int trusted_group(gid_t gid) +{ +static char trusted_groups[]=TRUSTED_GROUPS; +static char buf[ sizeof(trusted_groups) ]; +char	*p; + +	strcpy(buf, trusted_groups); +	for (p=buf; (p=strtok(p, " ")) != 0; p=0) +	{ +	struct	group *q=getgrnam(p); + +		if (q && (gid_t)q->gr_gid == gid) +			return (1); +	} +	return (0); +} + +static int trusted_uidgid(uid_t uid, gid_t gid, gid_t gid2) +{ +	if (trusted_user(uid) || trusted_group(gid) || +		trusted_group(gid2)) +		return (1); +	return (0); +} + +static void sethostname(Buffer &buf) +{ +char    hostname[256]; + +        hostname[0]=0; +        gethostname(hostname, 256); +        hostname[sizeof(hostname)-1]=0; + +	buf=hostname; +} + + +static void copyright() +{ +static const char msg[]="maildrop " VERSION " Copyright 1998-2005 Double Precision, Inc." + +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +#ifdef DbObj +	"GDBM/DB extensions enabled." +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +#endif +#if AUTHLIB +	"Courier Authentication Library extension enabled." +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +#endif +	"Maildir quota extension are now always enabled." +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +	"This program is distributed under the terms of the GNU General Public" +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +        "License. See COPYING for additional information." +#if CRLF_TERM +	"\r\n" +#else +	"\n" +#endif +  +		; + +	mout << msg; +	mout.flush(); +} + +void Maildrop::reset_vars() +{ +int	i; +Buffer	name, value; + +	for (i=0; i<(int)(sizeof(defaults_vars)/sizeof(defaults_vars[0])); i++) +	{ +		name=defaults_vars[i]; +		value=defaults_vals[i]; +		SetVar(name, value); +	} +	name="HOME"; +	SetVar(name, maildrop.init_home); +	name="LOGNAME"; +	SetVar(name, maildrop.init_logname); +	name="SHELL"; +	SetVar(name, maildrop.init_shell); +	name="DEFAULT"; +	SetVar(name, maildrop.init_default); + +	name="UMASK"; +	value="077"; +	SetVar(name, value); + +	if (maildrop.init_quota.Length() > 0) +	{ +		name="MAILDIRQUOTA"; +		SetVar(name, maildrop.init_quota); +	} +} + +#if AUTHLIB +// Authlib lookup + +static int callback_authlib(struct authinfo *auth, +			    void *void_arg) +{ +	Maildrop &maildrop=*(Maildrop *)void_arg; + +	if (VerboseLevel() > 1) +	{ +		Buffer b; + +		b.set(auth->sysgroupid); +		b.push(0); + +		merr << "maildrop: authlib: groupid=" +		     << b << "\n"; +	} + +	setgroupid(auth->sysgroupid); + +	uid_t u; +	if (auth->sysusername) +	{ +		struct	passwd *q=getpwnam(auth->sysusername); + +		if (q == NULL) +		{ +			merr << "Cannot find system user " +			     << auth->sysusername +			     << "\n"; + +			nochangeuidgid(); +		} + +		u=q->pw_uid; +	} +	else +		u=*auth->sysuserid; + +	if (VerboseLevel() > 1) +	{ +		Buffer b; + +		b.set(u); +		b.push(0); + +		merr << "maildrop: authlib: userid=" +		     << b << "\n"; +	} + +	setuid(u); + +	if ( getuid() != u) +		nochangeuidgid(); + +	if (VerboseLevel() > 1) +	{ +		merr << "maildrop: authlib: logname=" +		     << auth->address +		     << ", home=" +		     << auth->homedir +		     << ", mail=" +		     << (auth->maildir ? auth->maildir:"(default)") +		     << "\n"; +	} + +	maildrop.init_home=auth->homedir; +	maildrop.init_logname=auth->address; +	maildrop.init_shell="/bin/sh"; +	maildrop.init_default=auth->maildir ? auth->maildir: +		GetDefaultMailbox(auth->address); + +	if ( auth->quota ) +		maildrop.init_quota=auth->quota; + +	return 0; +} + +int find_in_authlib(Maildrop *maildrop, const char* user) +{ +	int rc=auth_getuserinfo("login", +				user, callback_authlib, maildrop); + +	if (rc == 0) +		return 1; + +	if ((rc > 0) && (maildrop->authlib_essential == 1)) +	{ +		errexit=EX_TEMPFAIL; +		throw "Temporary authentication failure."; +	} + +	return 0; +} +#else +int find_in_authlib(Maildrop *maildrop, const char* user) +{ +	return 0; +} +#endif + +static void tempfail(const char *msg) +{ +	errexit = EX_TEMPFAIL; +	throw msg; +} + +static int run(int argc, char **argv) +{ +int	argn; +const	char *deliverymode=0; +char *embedded_filter=0; +const	char *from=0; +int     explicit_from=0; +Buffer	recipe; +uid_t	orig_uid; +gid_t	orig_gid, orig_gid2; +Buffer	extra_headers; +struct passwd *my_pw; +int	found; +#if	HAVE_COURIER +#if	RESTRICT_TRUSTED +const	char *numuidgid=0; +#endif +#endif + + +	umask( 0007 ); +	for (argn=1; argn < argc; ) +	{ +		if (argv[argn][0] != '-')	break; +		if (strcmp(argv[argn], "--") == 0)	{ ++argn; break; } + +	char	optc=argv[argn][1]; +	const char *optarg=argv[argn]+2; + +		++argn; +		switch (optc)	{ + +#if	HAVE_COURIER +#if	RESTRICT_TRUSTED +		case 'D': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			numuidgid=optarg; +			break; +#endif +#endif +		case 'd': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			deliverymode=optarg; +			break; +		case 'V': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			maildrop.verbose_level=atoi(optarg); +			break; +		case 'v': +			copyright(); +			return (0); +		case 'm': +			maildrop.embedded_mode=1; +			break; +		case 'M': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			maildrop.embedded_mode=1; +			if (!deliverymode)	deliverymode=""; +			if (!*optarg) +			{ +				help(); +				return (EX_TEMPFAIL); +			} +			embedded_filter=(char *)malloc(strlen(optarg)+1); +			if (!embedded_filter)	outofmem(); +			strcpy(embedded_filter, optarg); +			{ +			char *p; + +				for (p=embedded_filter; *p; p++) +					if (*p == SLASH_CHAR || *p == '.') +						*p=':'; +			} +			break; +		case 'A': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			if (*optarg) +			{ +				extra_headers += optarg; +				extra_headers += '\n'; +			} +			break; +		case 'f': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			if (*optarg) +			{ +				from=optarg; +				explicit_from=1; +			} +			break; +		case 'w': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			if (*optarg) +				quota_warn_percent=atoi(optarg); +			break; +		case 'W': +			if (!*optarg && argn < argc)	optarg=argv[argn++]; +			if (*optarg) +				quota_warn_message=optarg; +			break; +		case 'a': +			maildrop.authlib_essential=1; +			break; +		case 'h': +			help(); +			return (EX_TEMPFAIL); +		default: +			bad(); +		} +	} + +	my_pw=0; +	found=0; +	orig_uid=getuid(); +	orig_gid=getgid(); +	orig_gid2=getegid(); +	if (!deliverymode && argn < argc) +		recipe=argv[argn++]; +	else +	{ +		if (!deliverymode)	deliverymode=""; + +		if (*deliverymode) +		{ +			found = find_in_authlib(&maildrop, deliverymode); + +			if ( !found ) +			{ +				my_pw=getpwnam(deliverymode); +				if (!my_pw) +					nouser(); +#if	RESET_GID +				setgroupid(my_pw->pw_gid); +#else +				setgroupid(getegid()); +#endif +				setuid(my_pw->pw_uid); +				if (getuid() != my_pw->pw_uid) +					nochangeuidgid(); // Security violation. + +				maildrop.init_home=my_pw->pw_dir; +				maildrop.init_logname=my_pw->pw_name; +				maildrop.init_shell= +					my_pw->pw_shell && *my_pw->pw_shell +						? my_pw->pw_shell:"/bin/sh"; +				maildrop.init_default= +					GetDefaultMailbox(my_pw->pw_name); +				found=1; +			} +		} +		maildrop.isdelivery=1; +#if	RESTRICT_TRUSTED +		if ( getuid() != orig_uid && !trusted_uidgid(orig_uid, +			orig_gid, orig_gid2)) +		{ +			errexit=EX_TEMPFAIL; +			throw "You are not a trusted user."; +					// Security violation +		} +#endif +	} + +#if	HAVE_COURIER +#if	RESTRICT_TRUSTED +	if (numuidgid) +	{ +	uid_t	un=0; +	gid_t	gn=getgid(); + +		if (deliverymode && *deliverymode) +		{ +			errexit=EX_TEMPFAIL; +			throw "Cannot use both -d and -D options."; +		} + +		if ( !trusted_uidgid(orig_uid, orig_gid, orig_gid2)) +		{ +			errexit=EX_TEMPFAIL; +			throw "You are not authorized to use the -D option."; +		} + +		if (!isdigit( (int)(unsigned char)*numuidgid)) +		{ +			errexit=EX_TEMPFAIL; +			throw "Invalid -D option."; +		} + +		do +		{ +			un=un * 10 + (*numuidgid++ - '0'); +		} while (isdigit( (int)(unsigned char)*numuidgid)); + +		if ( *numuidgid ) +		{ +			if ( *numuidgid++ != '/' || +				!isdigit( (int)(unsigned char)*numuidgid)) +			{ +				errexit=EX_TEMPFAIL; +				throw "Invalid -D option."; +			} +			gn=0; +			do +			{ +				gn=gn * 10 + (*numuidgid++ - '0'); +			} while (isdigit( (int)(unsigned char)*numuidgid)); + +			if ( *numuidgid ) +			{ +				errexit=EX_TEMPFAIL; +				throw "Invalid -D option."; +			} +		} +		setgroupid(gn); +		setuid(un); +		deliverymode=""; +		orig_uid=un;	/* See below for another Courier hook */ +	} +#endif +#endif + + +#if	RESET_GID +	setgroupid(getgid()); +#endif + +uid_t	my_u=getuid(); + +	setuid(my_u);	// Drop any setuid privileges. + +	if (!found) +	{ +#if HAVE_COURIER +		if (!deliverymode) +#endif +		{ +			my_pw=getpwuid(my_u); +			if (!my_pw) +			{ +				errexit=EX_TEMPFAIL; +				throw "Cannot determine my username."; +			} + +			maildrop.init_home=my_pw->pw_dir; +			maildrop.init_logname=my_pw->pw_name; +			maildrop.init_shell= +				my_pw->pw_shell && *my_pw->pw_shell +					? my_pw->pw_shell:"/bin/sh"; +			maildrop.init_default= +				GetDefaultMailbox(my_pw->pw_name); +		} +	} + +int	i; +Buffer	name; +Buffer	value; + +	for (i=0; environ[i]; i++) +	{ +		name=environ[i]; + +		char	*p=strchr(environ[i], '='); + +		value= p ? (name.Length(p - environ[i]), p+1):""; + +		if (maildrop.isdelivery) +		{ +			if (name == "LANG" || +			    name == "LANGUAGE" || +			    strncmp(name, "LC_", 3) == 0) +				; +			else +				continue; +		} + +		SetVar(name, value); +	} + +	i=1; +	while (argn < argc) +	{ +		name=""; +		name.append( (unsigned long)i); +		value=argv[argn++]; +		SetVar(name, value); +		++i; +	} + +#if	HAVE_COURIER +	if (deliverymode && orig_uid == getuid()) +	{ +	const char *p; + +		if ((p=getenv("HOME")) && *p) +			maildrop.init_home=p; + +		if ((p=getenv("LOGNAME")) && *p) +			maildrop.init_logname=p; + +		if ((p=getenv("SHELL")) && *p) +			maildrop.init_shell=p; + +		p=getenv("MAILDROPDEFAULT"); + +		if (!p || !*p) +		{ +			p=getenv("LOCAL"); + +			if (p && *p) +				p=GetDefaultMailbox(p); +			else +				p="./Maildir"; +		} +		maildrop.init_default=p; + +		if ((p=getenv("MAILDIRQUOTA")) && *p) +			maildrop.init_quota=p; +	} +#endif + +	if (deliverymode) +	{ +	struct	stat	buf; +	Buffer	b; + +		b=maildrop.init_home; +		b += '\0'; + +	const char *h=b; + +		if (VerboseLevel() > 1) +			merr << "maildrop: Changing to " << h << "\n"; + +		if (chdir(h) < 0) +		{ +			errexit=EX_TEMPFAIL; +			throw "Unable to change to home directory."; +		} +		recipe=".mailfilter"; + +		if ( stat(".", &buf) < 0) +			tempfail("Cannot stat() home directory."); +		if ( !S_ISDIR(buf.st_mode)) +			tempfail("Home directory is not a directory."); +		if ( buf.st_mode & S_IWOTH) +			tempfail("Invalid home directory permissions - world writable."); +		if ( buf.st_uid != getuid()) +			tempfail("Home directory owned by wrong user."); + +		// Quietly terminate if the sticky bit is set on the homedir + +		if ( buf.st_mode & S_ISVTX) +			return (EX_TEMPFAIL); + +		if (embedded_filter) +		{ +			i=stat(".mailfilters", &buf); + +			if ( i < 0 && errno != ENOENT) +				tempfail("Unable to read $HOME/.mailfilters."); +			else if ( i >= 0) +			{ +				if ( !S_ISDIR(buf.st_mode)) +					tempfail("$HOME/.mailfilters should be a directory."); +				if ( buf.st_mode & (S_IRWXO|S_IRWXG)) +					tempfail("Invalid permissions on $HOME/.mailfilters - remove world and group perms."); +				if ( buf.st_uid != getuid()) +					tempfail("Invalid user ownership of $HOME/.mailfilters."); +			} +			recipe = embedded_filter; +		} +	} +#if	SHARED_TEMPDIR + +#else +	maildrop.tempdir=maildrop.init_home; +	maildrop.tempdir += "/" TEMPDIR; +	maildrop.tempdir += '\0'; +	mkdir( (const char *)maildrop.tempdir, 0700 ); +#endif +	maildrop.reset_vars(); + +Buffer	msg; + +	maildrop.global_timer.Set(GLOBAL_TIMEOUT); +	maildrop.msgptr->Init(0);	// Read message from standard input. +	maildrop.msginfo.info( *maildrop.msgptr ); +	maildrop.msgptr->ExtraHeaders(extra_headers); +	maildrop.msgptr->setmsgsize(); + + +	if (!from)	from=""; +	if (*from)	maildrop.msginfo.fromname=from; + +// If invoking user is trusted, trust the From line, else set it to invoking +// user. + +	if ( +#if KEEP_FROMLINE + +// The original From_ line is kept, if necessary + +#else + +// If invoking user is trusted, trust the From line, else set it to invoking +// user. +		!trusted_uidgid(orig_uid, orig_gid, orig_gid2) || +#endif + +		maildrop.msginfo.fromname.Length() == 0) +	{ +		maildrop.msginfo.fromname=maildrop.init_logname; +	} + +	if (explicit_from) +		maildrop.msginfo.fromname=from; + +	name="FROM"; +	value=maildrop.msginfo.fromname; +	SetVar(name, value); + +	if (VerboseLevel() > 1) +	{ +		msg.reset(); +		msg.append("Message start at "); +		msg.append((unsigned long)maildrop.msginfo.msgoffset); +		msg.append(" bytes, envelope sender="); +		if (maildrop.msginfo.fromname.Length() > 0) +			msg += maildrop.msginfo.fromname; +		msg.append("\n"); +		msg += '\0'; +		merr.write(msg); +	} + +	name="HOSTNAME"; +	sethostname(value); +	SetVar(name, value); + +int	fd; + +	// +	// + +	if (!embedded_filter && deliverymode) +	{ +	Recipe r; +	Lexer in; + +		fd=in.Open(ETCDIR "/maildroprc"); +		if (fd < 0) +		{ +			if (errno != ENOENT) +			{ +				errexit=EX_TEMPFAIL; +				throw "Error opening " ETCDIR "/maildroprc."; +			} +		} +		else +		{ +			if (r.ParseRecipe(in) < 0) +				return (EX_TEMPFAIL); +			r.ExecuteRecipe(); +		} +	} + +Recipe	r; +Lexer	in; + +#ifdef	DEFAULTEXT +int	firstdefault=1; +#endif + +	name="MAILFILTER"; +	value=recipe; +	SetVar(name, value); + +	for (;;) +	{ +		if (embedded_filter) +		{ +			msg=".mailfilters/"; +			msg += recipe; +			if (VerboseLevel() > 1) +				merr << "maildrop: Attempting " << msg << "\n"; +			msg += '\0'; +			fd=in.Open((const char *)msg); +		} +		else +		{ +			msg=recipe; +			if (VerboseLevel() > 1) +				merr << "maildrop: Attempting " << msg << "\n"; +			msg += '\0'; +			fd=in.Open((const char *)msg); +			break; +		} +#ifndef	DEFAULTEXT +		break; +#else +		if (fd >= 0)	break; +		if (errno != ENOENT)	break; + +		if (firstdefault) +		{ +			recipe += DEFAULTEXT; +			firstdefault=0; +			continue; +		} + +		// Pop DEFAULTEXT bytes from end of recipe name + +		for (fd=sizeof(DEFAULTEXT)-1; fd; --fd) +			recipe.pop(); + +		while (recipe.Length()) +		{ +			if (recipe.pop() == '-') +			{ +				recipe += DEFAULTEXT; +				break; +			} +		} +		if (recipe.Length() == 0) +		{ +			msg=".mailfilters/"; +			msg += DEFAULTEXT+1; +			if (VerboseLevel() > 1) +				merr << "maildrop: Attempting " << msg << "\n"; +			msg += '\0'; +			fd=in.Open((const char *)msg); +			break; +		} +#endif +	} + +	if (fd < 0) +	{ +		// +		//  If we are operating in delivery mode, it's ok if +		//  .mailfilter does not exist. +		// +		if (!deliverymode || errno != ENOENT) +		{ +		static char buf[80]; + +			sprintf(buf, "Unable to open filter file, errno=%d.", +				errno); +			errexit=EX_TEMPFAIL; +			throw buf; +		} +	} +	else +	{ +	struct	stat	stat_buf; + +		setprocgroup(); + +		if (fstat( fd, &stat_buf) != 0) +			tempfail("stat() failed."); + +		if (!S_ISREG(stat_buf.st_mode)) +			tempfail("mailfilter file isn't a regular file."); +		if (stat_buf.st_mode & (S_IRWXO | S_IRWXG)) +			tempfail("Cannot have world/group permissions on the filter file - for your own good."); +		if (stat_buf.st_uid != getuid()) +			tempfail("mailfilter file is owned by the wrong user."); + +		if (r.ParseRecipe(in) < 0) +			return (EX_TEMPFAIL); + +//		if (maildrop.msgptr->MessageSize() > 0) +			r.ExecuteRecipe(); +	} +// +// If a message is successfully delivered, an exception is thrown. +// If we're here, we should deliver to the default mailbox. +// + +	if (!maildrop.embedded_mode) +	{ +		name="DEFAULT"; + +	const char *v=GetVarStr(name); + +		if (!v) +		{ +			errexit=EX_TEMPFAIL; +			throw "DEFAULT mailbox not defined."; +		} + +		value=v; +		value += '\0'; +		if (delivery((const char *)value) < 0) +			return (EX_TEMPFAIL); +	} + +	value="EXITCODE"; +	return ( GetVar(value)->Int("0") ); +} + +int main(int argc, char **argv) +{ + +#if HAVE_SETLOCALE +	setlocale(LC_ALL, "C"); +#endif + +	_exit(Maildrop::trap(run, argc, argv)); +} + +const char *GetDefaultMailbox(const char *username) +{ +static Buffer buf; +int	isfile=0; + +	buf=""; + +const	char *p=DEFAULT_DEF; + +	if (*p != SLASH_CHAR)	// Relative to home directory +	{ +		buf=maildrop.init_home; +		buf.push(SLASH_CHAR); +		isfile=1; +	} + +	while (*p) +	{ +		if (*p != '=') +		{ +			buf.push(*p); +			++p; +		} + +	const char *q=username; + +		while (*p == '=') +		{ +			buf.push (*q ? *q:'.'); +			if (*q)	q++; +			p++; +		} +	} + +	if (!isfile) +	{ +		buf.push(SLASH_CHAR); +		buf += username; +	} +	buf += '\0'; +	return (buf); +} + +#if	SHARED_TEMPDIR + +#else +const char *TempName() +{ +Buffer	t; + +	t=(const char *)maildrop.tempdir; +	t += "/tmp."; +	t += '\0'; +	return (TempName((const char *)t, 0)); +} +#endif | 
