diff options
Diffstat (limited to 'sqwebmail/sqwebmail.c')
| -rw-r--r-- | sqwebmail/sqwebmail.c | 2745 | 
1 files changed, 2745 insertions, 0 deletions
| diff --git a/sqwebmail/sqwebmail.c b/sqwebmail/sqwebmail.c new file mode 100644 index 0000000..3604568 --- /dev/null +++ b/sqwebmail/sqwebmail.c @@ -0,0 +1,2745 @@ +/* +** Copyright 1998 - 2009 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +/* +*/ +#include	"sqwebmail.h" +#include	"sqconfig.h" +#include	"auth.h" +#include	"folder.h" +#include	"pref.h" +#include	"maildir.h" +#include	"cgi/cgi.h" +#include	"pref.h" +#include	"mailinglist.h" +#include	"newmsg.h" +#include	"pcp.h" +#include	"acl.h" +#include	"addressbook.h" +#include	"autoresponse.h" +#include	"http11/http11.h" +#include	"random128/random128.h" +#include	"maildir/maildirmisc.h" +#include	"maildir/maildirinfo.h" +#include	"maildir/maildiraclt.h" +#include	"liblock/config.h" +#include	"liblock/liblock.h" +#include	"rfc822/rfc822hdr.h" +#include	"courierauth.h" +#include	<stdio.h> +#include	<errno.h> +#include	<stdlib.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#include	<string.h> +#include	<signal.h> +#include	<sys/types.h> +#include	<sys/stat.h> +#include	<sys/types.h> +#include        <sys/socket.h> +#if	HAVE_FCNTL_H +#include	<fcntl.h> +#endif +#if	HAVE_LOCALE_H +#if	HAVE_SETLOCALE +#include	<locale.h> +#if	USE_LIBCHARSET +#if	HAVE_LOCALCHARSET_H +#include	<localcharset.h> +#elif	HAVE_LIBCHARSET_H +#include	<libcharset.h> +#endif	/* HAVE_LOCALCHARSET_H */ +#elif	HAVE_LANGINFO_CODESET +#include	<langinfo.h> +#endif	/* USE_LIBCHARSET */ +#endif	/* HAVE_SETLOCALE */ +#endif	/* HAVE_LOCALE_H */ +#include	<ctype.h> +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#if	HAVE_SYS_SELECT_H +#include	<sys/select.h> +#endif +#if	HAVE_SYS_UIO_H +#include	<sys/uio.h> +#endif +#define	MD5_INTERNAL +#include	"md5/md5.h" + +#include	<courierauthdebug.h> +#include	"maildir/maildircache.h" +#include	"maildir/maildiraclt.h" +#include	"maildir/maildirnewshared.h" +#include	"mailfilter.h" +#include	"numlib/numlib.h" +#include	"gpglib/gpglib.h" +#include	"gpg.h" +#if	HAVE_CRYPT_H +#include	<crypt.h> +#endif +#if     NEED_CRYPT_PROTOTYPE +extern char *crypt(const char *, const char *); +#endif +#include	"htmllibdir.h" + +#include	"logindomainlist.h" + +#include	"strftime.h" + +extern void spell_show(); +extern void spell_check_continue(); +extern void print_safe(const char *); +extern void ldaplist(); +extern int ldapsearch(); +extern void doldapsearch(); + +extern void sent_gpgerrtxt(); +extern void sent_gpgerrresume(); +extern const char *redirect_hash(const char *); + +const char *sqwebmail_mailboxid=0; +const char *sqwebmail_folder=0; + +#define ALL_RIGHTS \ +	ACL_ADMINISTER \ +	ACL_CREATE \ +	ACL_EXPUNGE \ +	ACL_INSERT \ +	ACL_LOOKUP \ +	ACL_READ \ +	ACL_SEEN \ +	ACL_DELETEMSGS \ +	ACL_WRITE \ +	ACL_DELETEFOLDER +char sqwebmail_folder_rights[sizeof(ALL_RIGHTS)]; + +const char *sqwebmail_sessiontoken=0; + +const char *sqwebmail_content_language=0; +const char *sqwebmail_content_locale; +const char *sqwebmail_system_charset=0; +static char *sys_locale_charset=0; + +const char *sqwebmail_content_ispelldict; +const char *sqwebmail_content_charset; + +dev_t sqwebmail_homedir_dev; +ino_t sqwebmail_homedir_ino; + +static int noimages=0; + +time_t	login_time; + +extern int nochangepass(); + +/* Need to cache the following environment variables */ +static const char * const authvars[] = { "AUTHADDR", "AUTHFULLNAME", +					 "OPTIONS", "AUTHENTICATED", 0 }; + +#ifdef	GZIP +static int gzip_save_fd; + +#endif + +static const char *sqwebmail_formname; + +extern void attachments_head(const char *, const char *, const char *); +extern void attachments_opts(const char *); +extern void doattach(const char *, const char *); + +static void timezonelist(); + +struct template_stack { +	struct template_stack *next; +	FILE *fp; +} ; + +static struct template_stack *template_stack=NULL; + +char *trim_spaces(const char *s); + +size_t get_timeoutsoft() +{ +	time_t n=TIMEOUTSOFT; +	const char *p; + +	p=getenv("SQWEBMAIL_TIMEOUTSOFT"); + +	if (p && *p) +		n=atoi(p); + +	return n; +} + +size_t get_timeouthard() +{ +	time_t n=TIMEOUTHARD; +	const char *p; + +	p=getenv("SQWEBMAIL_TIMEOUTHARD"); + +	if (p && *p) +		n=atoi(p); + +	return n; +} + +void fake_exit(int n) +{ +	maildir_cache_cancel(); +	exit(n); +} + + +/* Stub to catch aborts from authlib */ + +void authexit(int n) +{ +	fake_exit(n); +} + +/* enomem() used to be just an out-of-memory handler.  Now, I use it as a +** generic failure type of a deal. +*/ + +void rfc2045_error(const char *p) +{ +	error(p); +} + +void print_attrencodedlen(const char *p, size_t len, int oknl, FILE *fp) +{ +	for (; len; p++, --len) +	{ +		switch (*p)	{ +		case '<': +			fprintf(fp, "<"); +			continue; +		case '>': +			fprintf(fp, ">"); +			continue; +		case '&': +			fprintf(fp, "&"); +			continue; +		case '"': +			fprintf(fp, """); +			continue; +		case '\n': +			if (oknl) +			{ +				if (oknl == 2) +				{ +					fprintf(fp, "<br />"); +					continue; +				} +				putc('\n', fp); +				continue; +			} +		default: +			if (!ISCTRL(*p)) +			{ +				putc(*p, fp); +				continue; +			} +		} +		fprintf(fp, "&#%d;", (int)(unsigned char)*p); +	} +} + +void output_attrencoded_fp(const char *p, FILE *fp) +{ +	print_attrencodedlen(p, strlen(p), 0, fp); +} + +void output_attrencoded(const char *p) +{ +	output_attrencoded_fp(p, stdout); +} + +void output_attrencoded_oknl_fp(const char *p, FILE *fp) +{ +	print_attrencodedlen(p, strlen(p), 1, fp); +} + +void output_attrencoded_oknl(const char *p) +{ +	output_attrencoded_oknl_fp(p, stdout); +} + +void output_attrencoded_nltobr(const char *p) +{ +	print_attrencodedlen(p, strlen(p), 2, stdout); +} + +void output_urlencoded(const char *p) +{ +char	*q=cgiurlencode(p); + +	printf("%s", q); +	free(q); +} + +void output_loginscriptptr() +{ +#if	USE_HTTPS_LOGIN +const	char *p=cgihttpsscriptptr(); +#elif	USE_RELATIVE_URL +const	char *p=cgirelscriptptr(); +#else +const	char *p=cgihttpscriptptr(); +#endif + +	printf("%s", p); +} + +const char *nonloginscriptptr() +{ +#if	USE_HTTPS +	return (cgihttpsscriptptr()); +#elif	USE_RELATIVE_URL +	return (cgirelscriptptr()); +#else +	return (cgihttpscriptptr()); +#endif +} + + +void output_scriptptr() +{ +const	char *p=nonloginscriptptr(); + +	printf("%s", p); +	if (sqwebmail_mailboxid) +	{ +	char	*q=cgiurlencode(sqwebmail_mailboxid); +	char	buf[NUMBUFSIZE]; + +		printf("/login/%s/%s/%s", q, +			sqwebmail_sessiontoken ?  sqwebmail_sessiontoken:" ", +			libmail_str_time_t(login_time, buf)); +		free(q); +	} +} + +void output_loginscriptptr_get() +{ +	output_loginscriptptr(); +	if (sqwebmail_mailboxid) +	{ +	char	*q=cgiurlencode(sqwebmail_mailboxid); +	char	buf[NUMBUFSIZE]; + +		printf("/login/%s/%s/%s", q, +			sqwebmail_sessiontoken ?  sqwebmail_sessiontoken:" ", +			libmail_str_time_t(login_time, buf)); +		free(q); +	} +} + +char *scriptptrget() +{ +char	*q=0; +size_t	l=0; +int	i; +char	buf[NUMBUFSIZE]; + +#define	ADD(s) {const char *zz=(s); if (i) strcat(q, zz); l += strlen(zz);} +#define ADDE(ue) { char *yy=cgiurlencode(ue); ADD(yy); free(yy); } + +	for (i=0; i<2; i++) +	{ +		if (i && (q=malloc(l+1)) == 0)	enomem(); +		if (i)	*q=0; +		ADD( nonloginscriptptr() ); +		if (!sqwebmail_mailboxid) +		{ +			ADD("?"); +			continue; +		} + +		ADD("/login/"); +		ADDE(sqwebmail_mailboxid); +		ADD("/"); +		ADD(sqwebmail_sessiontoken ? sqwebmail_sessiontoken:" "); +		ADD("/"); +		ADD(libmail_str_time_t(login_time, buf)); + +		ADD( "?" ); +		if (sqwebmail_folder) +		{ +			ADD("folder="); +			ADDE(sqwebmail_folder); +		} +	} +#undef	ADD +#undef	ADDE +	return (q); +} + +void output_scriptptrget() +{ +char	*p=scriptptrget(); + +	printf("%s", p); +	free(p); +	return; +} + +void output_scriptptrpostinfo() +{ +	if (sqwebmail_folder) +	{ +		printf("<input type=\"hidden\" name=\"folder\" value=\""); +		output_attrencoded(sqwebmail_folder); +		printf("\" />"); +	} + +	if (*cgi("folderdir"))	/* In folders.html */ +	{ +		printf("<input type=\"hidden\" name=\"folderdir\" value=\""); +		output_attrencoded(cgi("folderdir")); +		printf("\" />"); +	} +} + +void error(const char *errmsg) +{ +	cginocache(); +	printf("Content-Type: text/html; charset=us-ascii\n\n" +		"<html><head><title>%s</title></head><body><h1>%s</h1></body></html>\n", +		errmsg, errmsg); +	cleanup(); +	fake_exit(1); +} + +void error2(const char *file, int line) +{ +	cginocache(); +	printf("Content-Type: text/html; charset=us-ascii\n\n" +		"<html><head><title>Internal error</title></head><body>" +		"<h1>Internal error (module %s, line %d) - contact system administrator</h1>" +		"</body></html>\n", +		file, line); +	cleanup(); +	fake_exit(1); +} + +void error3(const char *file, int line, const char *msg1, const char *msg2, int err) +{ +	cginocache(); +	if (err == -1) err = errno; +	printf("Content-Type: text/html; charset=us-ascii\n\n" +		"<html><head><title>Internal error</title></head><body>" +		"<h1>Internal error (module %s, line %d) - contact system administrator</h1>" +		"<pre>%s\n%s\n%s</pre>" +		"</body></html>\n", +		file, line, msg1?msg1:"", msg2?msg2:"", err?strerror(err):""); +	cleanup(); +	fake_exit(1); +} + + +char *get_templatedir() +{ +char	*templatedir=getenv("SQWEBMAIL_TEMPLATEDIR"); +	 +	if (!templatedir || !*templatedir)	templatedir=HTMLLIBDIR; + +	return templatedir; +} + + +char *get_imageurl() +{ +char	*imageurl=getenv("SQWEBMAIL_IMAGEURL"); +	 +	if (!imageurl || !*imageurl)	imageurl=IMGPATH; + +	return imageurl; +} + + +FILE *open_langform(const char *lang, const char *formname, +		    int print_header) +{ +char	*formpath; +FILE	*f; +char	*templatedir=get_templatedir(); +	 +	/* templatedir/lang/formname */ + +	if (!(formpath=malloc(strlen(templatedir)+3+ +		strlen(lang)+strlen(formname)))) +		error("Out of memory."); + +	strcat(strcat(strcat(strcat(strcpy(formpath, templatedir), "/"), +		lang), "/"), formname); + +	f=fopen(formpath, "r"); + +	free(formpath); +	 +	if (f && print_header) +		printf("Content-Language: %s\n", lang); +	if (f) +		fcntl(fileno(f), F_SETFD, FD_CLOEXEC); +	return (f); +} + +int ishttps() +{ +	const char *p=getenv("HTTPS"); + +	return (p && strcasecmp(p, "on") == 0); +} + +struct var_put_buf { +	char argbuf[3072]; +	char *argp; +	size_t argn; +} ; + +static void var_put_buf_func(int c, void *p) +{ +	struct var_put_buf *pp=(struct var_put_buf *)p; + +	if (pp->argn) +	{ +		*pp->argp++=c; +		--pp->argn; +	} +} + +static void pass_image_through(int c, void *p) +{ +	putchar(c); +} + +static void output_image( FILE *f, +			  void (*output_func)(int, void *), void *void_arg) +{ +	int c; + +	/* +	  Conditional image.  It's formatted as follows: + +	  @@filename,width=x height=y@text@ +            ^ +            | +	    ----- we're at this point now. + +	  If images are enabled, we replace that with an IMG tag we build from +	  filename,width=x, height=y. +	  If images are disabled, we replace all of this with text. + +	*/ + +#define	MKIMG(c)	(*output_func)((c), void_arg) + +	if (noimages) +	{ +		while ((c=getc(f)) >= 0 +		       && c != '@') +			; +		while ((c=getc(f)) >= 0 +		       && c != '@') +			MKIMG(c); +	} +	else +	{ +		char	*p; + +		MKIMG('<'); +		MKIMG('i'); +		MKIMG('m'); +		MKIMG('g'); +		MKIMG(' '); +		MKIMG('s'); +		MKIMG('r'); +		MKIMG('c'); +		MKIMG('='); +		MKIMG('"'); +		for (p=get_imageurl(); *p; p++) +			MKIMG(*p); + +		MKIMG('/'); +		while ((c=getc(f)) >= 0 +		       && c != '@' && c != ',') +			MKIMG(c); +		MKIMG('"'); +		MKIMG(' '); +		if (c == ',') +			c=getc(f); +		while (c >= 0 && c != '@') +		{ +			MKIMG(c); +			c=getc(f); +		} +		while ((c=getc(f)) >= 0 && c != '@') +			; +		MKIMG(' '); +		MKIMG('/'); +		MKIMG('>'); +	} +} + +/* ---- time zone list ---- */ + +static int timezonefile( int (*callback_func)(const char *, const char *, +					      void *), void *callback_arg) +{ +	FILE *f=NULL; +	char buffer[BUFSIZ]; + +	if (sqwebmail_content_language) +		f=open_langform(sqwebmail_content_language, "TIMEZONELIST", 0); + +	if (!f)	f=open_langform(HTTP11_DEFAULTLANG, "TIMEZONELIST", 0); + +	if (!f) +		return (0); + +	while (fgets(buffer, sizeof(buffer), f) != NULL) +	{ +		char *p=strchr(buffer, '\n'); +		char *tz; +		int rc; + +		if (p) *p=0; + +		p=strchr(buffer, '#'); +		if (p) *p=0; + +		for (p=buffer; *p; p++) +			if (!isspace((int)(unsigned char)*p)) +				break; + +		if (!*p) +			continue; + +		tz=p; +		while (*p) +		{ +			if (isspace((int)(unsigned char)*p)) +				break; +			++p; +		} +		if (*p) *p++=0; +		while (*p && isspace((int)(unsigned char)*p)) +			++p; + +		if (strcmp(p, "*") == 0) +			p=""; +		if (strcmp(tz, "*") == 0) +			tz=""; + +		rc= (*callback_func)(tz, p, callback_arg); + +		if (rc) +		{ +			fclose(f); +			return (rc); +		} +	} +	fclose(f); +	return (0); +} + +static int callback_timezonelist(const char *, const char *, void *); + +static void timezonelist() +{ +	printf("<select name=\"timezonelist\" class=\"timezonelist\">"); +	timezonefile(callback_timezonelist, NULL); +	printf("</select>\n"); +} + +static int callback_timezonelist(const char *tz, const char *n, void *dummy) +{ +	printf("<option value=\"%s\">", tz); +	output_attrencoded(n); +	printf("</option>\n"); +	return (0); +} + +static int set_timezone(const char *p) +{ +	static char *s_buffer=0; +	char *buffer; + +	if (!p || !*p || strcmp(p, "*") == 0) +		return (0); + +	buffer=malloc(strlen(p)+10); +	if (!buffer) +		return (0); +	strcat(strcpy(buffer, "TZ="), p); + +	putenv(buffer); + +	if (s_buffer) +		free(buffer); +	s_buffer=buffer; + +	return (0); +} + +static int callback_get_timezone(const char *, const char *, void *); + +/* Return TZ selected from login dropdown */ + +static char *get_timezone() +{ +	char *langptr=0; + +	timezonefile(callback_get_timezone, &langptr); + +	if (!langptr) +	{ +		langptr=strdup(""); +		if (!langptr) +			enomem(); +	} + +	if (*langptr == 0) +	{ +		free(langptr); +		langptr=strdup("*"); +		if (!langptr) +			enomem(); +	} + +	return(langptr); +} + +static int callback_get_timezone(const char *tz, const char *n, void *dummy) +{ +	if (strcmp(tz, cgi("timezonelist")) == 0) +	{ +		char **p=(char **)dummy; + +		if (*p) +			free(*p); + +		*p=strdup(tz); +	} +	return (0); +} + +/* ------------------------ */ + +static FILE *do_open_form(const char *formname, int flag) +{ +	struct template_stack *ts; +	FILE	*f=NULL; + +	if ((ts=(struct template_stack *)malloc(sizeof(struct template_stack))) +	    == NULL) +		return (NULL); + +	if (sqwebmail_content_language) +		f=open_langform(sqwebmail_content_language, formname, flag); +	if (!f)	f=open_langform(HTTP11_DEFAULTLANG, formname, flag); + +	if (!f) +	{ +		free(ts); +		return (NULL); +	} + +	ts->next=template_stack; +	template_stack=ts; +	ts->fp=f; +	return (f); +} + +static void do_close_form() +{ +	struct template_stack *ts=template_stack; + +	if (!ts) +		enomem(); + +	fclose(ts->fp); +	template_stack=ts->next; +	free(ts); +} + +static void do_output_form_loop(FILE *); + +static void fix_xml_header(FILE *f) +{ +	char linebuf[80]; + +	/* +	** Some templates now have an <?xml > header.  Adjust the +	** encoding to match the selected default.  Yes, it's a dirty hack, +	** and I'm proud of it, since it allows me to continue editing the +	** HTML templates in Amaya. +	*/ + +	if (fgets(linebuf, sizeof(linebuf), f) == NULL) +		return; + +	if (strncasecmp(linebuf, "<?xml version=", 14) == 0) +		sprintf(linebuf, "<?xml version=\"1.0\" encoding=\"%s\"?>\n", +			sqwebmail_content_charset); + +	printf("%s", linebuf); +} + +void output_form(const char *formname) +{ +	FILE	*f; + +#ifdef	GZIP +	int	dogzip; +	int	pipefd[2]; +	pid_t	pid= -1; +#endif + +	noimages= auth_getoptionenvint("wbnoimages"); + +	f=do_open_form(formname, 1); + +	sqwebmail_formname=formname; + +	if (!f)	error("Can't open form template."); + +	/* +	** Except for the dummy frame window (and the tiny empty frame), +	** and the window containing the print preview of the message, +	** expire everything. +	*/ + +	if (strcmp(formname, "index.html") && strcmp(formname, "empty.html") && +		strcmp(formname, "print.html")) +		cginocache(); + +#ifdef	GZIP + +	dogzip=0; +	if (strcmp(formname, "readmsg.html") == 0 || +	    strcmp(formname, "folder.html") == 0 || +	    strcmp(formname, "folders.html") == 0 || +	    strcmp(formname, "gpg.html") == 0) +	{ +	const char *p=getenv("HTTP_ACCEPT_ENCODING"); + +		if (p) +		{ +		char	*q=strdup(p), *r; + +			if (!q)	enomem(); +			for (r=q; *r; r++) +				*r= tolower((int)(unsigned char)*r); +			for (r=q; (r=strtok(r, ", ")) != 0; r=0) +				if (strcmp(r, "gzip") == 0) +				{ +					dogzip=1; +					if (pipe(pipefd)) +						enomem(); +				} +			free(q); +		} +	} +#endif + +	/* Do not send a Vary header for attachment downloads */ + +	if (*cgi("download") == 0) +		printf("Vary: Accept-Language\n"); + +#ifdef	GZIP +	if (dogzip) +		printf("Content-Encoding: gzip\n"); +#endif + +	printf("Content-Type: text/html"); + +	if (sqwebmail_content_charset) +		printf("; charset=%s", sqwebmail_content_charset); + +	printf("\n\n"); + +#ifdef	GZIP +	if (dogzip) +	{ +		fflush(stdout); +		while ((pid=fork()) == -1) +			sleep(5); +		if (pid == 0) +		{ +			dup2(pipefd[0], 0); +			close(pipefd[0]); +			close(pipefd[1]); +			execl(GZIP, "gzip", "-c", (char *)0); +			fprintf(stderr,  +			       "ERR: Cannot execute " GZIP ": %s\n", +			       strerror(errno)); +			exit(1); +		} + +		gzip_save_fd=dup(1); +		dup2(pipefd[1], 1); +		close(pipefd[1]); +		close(pipefd[0]); +	} +#endif +	fix_xml_header(f); +	do_output_form_loop(f); +	do_close_form(); + +#ifdef	GZIP +	if (pid > 0) +	{ +	int	waitstat; +	pid_t	p2; + +		/* Restore original stdout */ + +		fflush(stdout); +		dup2(gzip_save_fd, 1); +		close(gzip_save_fd); +		gzip_save_fd= -1; +		while ((p2=wait(&waitstat)) >= 0 && p2 != pid) +			; +	} +#endif +} + +static FILE *openinclude(const char *); + + +void insert_include(const char *inc_name) +{ +	FILE *ff=openinclude(inc_name); +	do_output_form_loop(ff); +	do_close_form(); +} + +static void do_output_form_loop(FILE *f) +{ +	int	c, c2, c3; + +	while ((c=getc(f)) >= 0) +	{ +	char	kw[64]; + +		if (c != '[') +		{ +			putchar(c); +			continue; +		} +		c=getc(f); +		if (c != '#') +		{ +			putchar('['); +			ungetc(c,f); +			continue; +		} +		c=getc(f); +		if (c == '?') +		{ +			c=getc(f); +			if (c < '0' || c > '9') +			{ +				putchar('['); +				putchar('#'); +				putchar('?'); +				putchar(c); +				continue; +			} +			if ( +			    ( c == '0' && nochangepass()) || +			    (c == '1' && strncmp(cgi("folderdir"), +						 SHARED ".", +						 sizeof(SHARED)) == 0) || +			    (c == '2' && strncmp(cgi("folderdir"), +						 SHARED ".", +						 sizeof(SHARED))) || +			    (c == '4' && maildir_filter_hasmaildirfilter(".")) || +			    (c == '5' && libmail_gpg_has_gpg(GPGDIR)) || +			    (c == '6' && !ishttps()) || +			    (c == '7' && !sqpcp_has_calendar()) || +			    (c == '8' && !sqpcp_has_groupware()) +			    ) +			{ +				while ((c=getc(f)) != EOF) +				{ +					if (c != '[')	continue; +					if ( getc(f) != '#')	continue; +					if ( getc(f) != '?')	continue; +					if ( getc(f) != '#')	continue; +					if ( getc(f) == ']')	break; +				} +			} +			continue; +		} + +		if (c == '$') +		{ +			struct var_put_buf buf; + +			buf.argp=buf.argbuf; +			buf.argn=sizeof(buf.argbuf)-1; + +			while ((c=getc(f)) >= 0 && c != '\n') +			{ +				if (c == '#') +				{ +					c=getc(f); +					if (c == ']')	break; +					ungetc(c, f); +					c='#'; +				} + +				if (c == '@') +				{ +					c=getc(f); +					if (c == '@') +					{ +						output_image(f, +							     var_put_buf_func, +							     &buf); +						continue; +					} +					ungetc(c, f); +					c='@'; +				} +				var_put_buf_func(c, &buf); +			} +			*buf.argp=0; +			addarg(buf.argbuf); +			continue; +		} + +		if (c == '@') +		{ +			output_image(f, pass_image_through, NULL); +			c=getc(f); +			if (c == '#') +			{ +				c=getc(f); +				if (c == ']') +					continue; +			} +			if (c != EOF) +				ungetc(c, f); +			continue; +		} + +		if (!isalnum(c) && c != ':') +		{ +			putchar('['); +			putchar('#'); +			ungetc(c, f); +			continue; +		} +		c2=0; +		while (c != EOF && (isalnum(c) || c == ':' || c == '_')) +		{ +			if (c2 < sizeof(kw)-1) +				kw[c2++]=c; +			c=getc(f); +		} +		kw[c2]=0; +		c2=c; + +		if (c2 != '#') +		{ +			putchar('['); +			putchar('#'); +			printf("%s", kw); +			ungetc(c2, f); +			continue; +		} + +		if ((c3=getc(f)) != ']') +		{ +			putchar('['); +			putchar('#'); +			printf("%s", kw); +			putchar(c2); +			ungetc(c3, f); +			continue; +		} + +		if (strcmp(kw, "a") == 0) +		{ +			addressbook(); +		} +		else if (strcmp(kw, "d") == 0) +		{ +			const char *f=cgi("folderdir"); +			char *origc, *c; +			const char *sep=""; + +			origc=c=folder_fromutf7(f); + +			if (*c && strcmp(c, INBOX)) +			{ +				printf(" - "); + +				if (strcmp(c, NEWSHAREDSP) == 0 || +				    strncmp(c, NEWSHAREDSP ".", +					    sizeof(NEWSHAREDSP)) == 0) +				{ +					printf("%s", getarg("PUBLICFOLDERS")); +					sep="."; +				} + +				c=strchr(c, '.'); +				if (c) +				{ +					printf(sep); +					print_safe(c+1); +				} +			} +			free(origc); +		} +		else if (strcmp(kw, "D") == 0) +		{ +			const char *p=cgi("folder"); +			const char *q=strrchr(p, '.'); + +				if (q) +				{ +				char	*r=malloc(q-p+1); + +					if (!r)	enomem(); +					memcpy(r, p, q-p); +					r[q-p]=0; +					output_urlencoded(r); +					free(r); +				} +		} +		else if (strcmp(kw, "G") == 0) +		{ +			output_attrencoded(login_returnaddr()); +		} +		else if (strcmp(kw, "r") == 0) +		{ +			output_attrencoded(cgi("redirect")); +		} +		else if (strcmp(kw, "s") == 0) +		{ +			output_scriptptrget(); +		} +		else if (strcmp(kw, "S") == 0) +		{ +			output_loginscriptptr(); +		} +		else if (strcmp(kw, "R") == 0) +		{ +			output_loginscriptptr_get(); +		} +		else if (strcmp(kw, "p") == 0) +		{ +			output_scriptptr(); +		} +		else if (strcmp(kw, "P") == 0) +		{ +			output_scriptptrpostinfo(); +		} +		else if (strcmp(kw, "f") == 0) +		{ +			folder_contents_title(); +		} +		else if (strcmp(kw, "F") == 0) +		{ +			folder_contents(sqwebmail_folder, atol(cgi("pos"))); +		} +		else if (strcmp(kw, "n") == 0) +		{ +			folder_initnextprev(sqwebmail_folder, atol(cgi("pos"))); +		} +		else if (strcmp(kw, "N") == 0) +		{ +			folder_nextprev(); +		} +		else if (strcmp(kw, "m") == 0) +		{ +			folder_msgmove(); +		} +		else if (strcmp(kw, "M") == 0) +		{ +			folder_showmsg(sqwebmail_folder, atol(cgi("pos"))); +		} +		else if (strcmp(kw, "T") == 0) +		{ +			folder_showtransfer(); +		} +		else if (strcmp(kw, "L") == 0) +		{ +			folder_list(); +		} +		else if (strcmp(kw, "l") == 0) +		{ +			folder_list2(); +		} +		else if (strcmp(kw, "E") == 0) +		{ +			folder_rename_list(); +		} +		else if (strcmp(kw, "W") == 0) +		{ +			newmsg_init(sqwebmail_folder, cgi("pos")); +		} +		else if (strcmp(kw, "z") == 0) +		{ +			pref_isdisplayfullmsg(); +		} +		else if (strcmp(kw, "y") == 0) +		{ +			pref_isoldest1st(); +		} +		else if (strcmp(kw, "H") == 0) +		{ +			pref_displayhtml(); +		} +		else if (strcmp(kw, "FLOWEDTEXT") == 0) +		{ +			pref_displayflowedtext(); +		} +		else if (strcmp(kw, "NOARCHIVE") == 0) +		{ +			pref_displaynoarchive(); +		} +		else if (strcmp(kw, "NOAUTORENAMESENT") == 0) +		{ +			pref_displaynoautorenamesent(); +		} +		else if (strcmp(kw, "x") == 0) +		{ +			pref_setprefs(); +		} +		else if (strcmp(kw, "w") == 0) +		{ +			pref_sortorder(); +		} +		else if (strcmp(kw, "t") == 0) +		{ +			pref_signature(); +		} +		else if (strcmp(kw, "u") == 0) +		{ +			pref_pagesize(); +		} +		else if (strcmp(kw, "v") == 0) +		{ +			pref_displayautopurge(); +		} +		else if (strcmp(kw, "A") == 0) +		{ +			attachments_head(sqwebmail_folder, cgi("pos"), +				cgi("draft")); +		} +		else if (strcmp(kw, "ATTACHOPTS") == 0) +		{ +			attachments_opts(cgi("draft")); +		} +		else if (strcmp(kw, "GPGERR") == 0) +		{ +			sent_gpgerrtxt(); +		} +		else if (strcmp(kw, "GPGERRRESUME") == 0) +		{ +			sent_gpgerrresume(); +		} +#ifdef	ISPELL +		else if (strcmp(kw, "K") == 0) +		{ +			spell_show(); +		} +#endif +#ifdef	BANNERPROG +		else if (strcmp(kw, "B") == 0) +		{ +			char banargbuf[31]; +			int	i=0; +			int	wait_stat; +			pid_t	p, p2; + +				if ((c=getc(f)) != '{') +					ungetc(c, f); +				else	while ((c=getc(f)), isalnum(c)) +						if (i < sizeof(banargbuf)-1) +							banargbuf[i++]=c; +				banargbuf[i]=0; +				fflush(stdout); + +				if ( (p=fork()) == 0 ) +				{ +					execl(BANNERPROG, BANNERPROG, +						sqwebmail_formname, +						banargbuf, (char *)0); +					_exit(0); +				} +				if (p > 0) +				{ +					while ((p2=wait(&wait_stat)) > 0 && +						p2 != p) +						; +				} +		} +#endif +		else if (strcmp(kw, "h") == 0) +		{ +			FILE *fp=fopen(LOGINDOMAINLIST, "r"); + +			if (fp) { +				/* parse LOGINDOMAINLIST and print proper output */ +				print_logindomainlist(fp); +				fclose(fp); +			} +		} +		else if (strcmp(kw, "o") == 0) +		{ +			ldaplist(); +		} +		else if (strcmp(kw, "O") == 0) +		{ +			doldapsearch(); +		} +		else if (strcmp(kw, "IMAGEURL") == 0) +		{ +			printf("%s", get_imageurl()); +		}			 +		else if (strcmp(kw, "LOADMAILFILTER") == 0) +		{ +			mailfilter_init(); +		} +		else if (strcmp(kw, "MAILFILTERLIST") == 0) +		{ +			mailfilter_list(); +		} +		else if (strcmp(kw, "MAILFILTERLISTFOLDERS") == 0) +		{ +			mailfilter_listfolders(); +		} +		else if (strcmp(kw, "QUOTA") == 0) +		{ +			folder_showquota(); +		} +		else if (strcmp(kw, "NICKLIST") == 0) +		{ +		        ab_listselect(); +		} +		else if (strcmp(kw, "LISTPUB") == 0) +		{ +			gpglistpub(); +		} +		else if (strcmp(kw, "LISTSEC") == 0) +		{ +			gpglistsec(); +		} +		else if (strcmp(kw, "KEYIMPORT") == 0) +		{ +			folder_keyimport(sqwebmail_folder, atol(cgi("pos"))); +		} +		else if (strcmp(kw, "GPGCREATE") == 0) +		{ +			gpgcreate(); +		} +		else if (strcmp(kw, "DOGPG") == 0) +		{ +			gpgdo(); +		} +		else if (strcmp(kw, "ATTACHPUB") == 0) +		{ +			gpgselectpubkey(); +		} +		else if (strcmp(kw, "ATTACHSEC") == 0) +		{ +			gpgselectprivkey(); +		} +		else if (strcmp(kw, "MAILINGLISTS") == 0) +		{ +			char *p=getmailinglists(); + +			/* <sigh> amaya inserts a bunch of spaces that mess +			** things up in Netscape. +			*/ + +			output_attrencoded(p ? p:""); +			if (p) +				free(p); +		} +		else if (strcmp(kw, "AUTORESPONSE") == 0) +		{ +			autoresponse(); +		} +		else if (strcmp(kw, "AUTORESPONSE_LIST") == 0) +		{ +			autoresponselist(); +		} +		else if (strcmp(kw, "AUTORESPONSE_PICK") == 0) +		{ +			autoresponsepick(); +		} +		else if (strcmp(kw, "AUTORESPONSE_DELETE") == 0) +		{ +			autoresponsedelete(); +		} +		else if (strcmp(kw, "SQWEBMAILCSS") == 0) +		{ +			printf("%s/sqwebmail.css", get_imageurl()); +		} +		else if (strcmp(kw, "timezonelist") == 0) +		{ +			timezonelist(); +		} +		else if (strcmp(kw, "PREFWEEK") == 0) +		{ +			pref_displayweekstart(); +		} +		else if (strcmp(kw, "NEWEVENT") == 0) +		{ +			sqpcp_newevent(); +		} +		else if (strcmp(kw, "RECURRING") == 0) +		{ +			printf("%s", getarg("RECURRING")); +		} +		else if (strcmp(kw, "EVENTSTART") == 0) +		{ +			sqpcp_eventstart(); +		} +		else if (strcmp(kw, "EVENTEND") == 0) +		{ +			sqpcp_eventend(); +		} +		else if (strcmp(kw, "EVENTFROM") == 0) +		{ +			sqpcp_eventfrom(); +		} +		else if (strcmp(kw, "EVENTTIMES") == 0) +		{ +			sqpcp_eventtimes(); +		} +		else if (strcmp(kw, "EVENTPARTICIPANTS") == 0) +		{ +			sqpcp_eventparticipants(); +		} +		else if (strcmp(kw, "EVENTTEXT") == 0) +		{ +			sqpcp_eventtext(); +		} +		else if (strcmp(kw, "EVENTATTACH") == 0) +		{ +			sqpcp_eventattach(); +		} +		else if (strcmp(kw, "EVENTSUMMARY") == 0) +		{ +			sqpcp_summary(); +		} +		else if (strcmp(kw, "CALENDARTODAY") == 0) +		{ +			sqpcp_todays_date(); +		} +		else if (strcmp(kw, "CALENDARWEEKLYLINK") == 0) +		{ +			sqpcp_weeklylink(); +		} +		else if (strcmp(kw, "CALENDARMONTHLYLINK") == 0) +		{ +			sqpcp_monthlylink(); +		} +		else if (strcmp(kw, "CALENDARTODAYV") == 0) +		{ +			sqpcp_todays_date_verbose(); +		} +		else if (strcmp(kw, "CALENDARDAYVIEW") == 0) +		{ +			sqpcp_daily_view(); +		} +		else if (strcmp(kw, "CALENDARPREVDAY") == 0) +		{ +			sqpcp_prevday(); +		} +		else if (strcmp(kw, "CALENDARNEXTDAY") == 0) +		{ +			sqpcp_nextday(); +		} +		else if (strcmp(kw, "CALENDARWEEK") == 0) +		{ +			sqpcp_show_cal_week(); +		} +		else if (strcmp(kw, "CALENDARNEXTWEEK") == 0) +		{ +			sqpcp_show_cal_nextweek(); +		} +		else if (strcmp(kw, "CALENDARPREVWEEK") == 0) +		{ +			sqpcp_show_cal_prevweek(); +		} +		else if (strcmp(kw, "CALENDARWEEKVIEW") == 0) +		{ +			sqpcp_displayweek(); +		} +		else if (strcmp(kw, "CALENDARMONTH") == 0) +		{ +			sqpcp_show_cal_month(); +		} +		else if (strcmp(kw, "CALENDARNEXTMONTH") == 0) +		{ +			sqpcp_show_cal_nextmonth(); +		} +		else if (strcmp(kw, "CALENDARPREVMONTH") == 0) +		{ +			sqpcp_show_cal_prevmonth(); +		} +		else if (strcmp(kw, "CALENDARMONTHVIEW") == 0) +		{ +			sqpcp_displaymonth(); +		} +		else if (strcmp(kw, "EVENTDISPLAYINIT") == 0) +		{ +			sqpcp_displayeventinit(); +		} +		else if (strcmp(kw, "EVENTDELETEINIT") == 0) +		{ +			sqpcp_deleteeventinit(); +		} +		else if (strcmp(kw, "EVENTDISPLAY") == 0) +		{ +			sqpcp_displayevent(); +		} +		else if (strcmp(kw, "EVENTBACKLINK") == 0) +		{ +			sqpcp_eventbacklink(); +		} +		else if (strcmp(kw, "EVENTEDITLINK") == 0) +		{ +			sqpcp_eventeditlink(); +		} +		else if (strcmp(kw, "EVENTCANCELUNCANCELLINK") == 0) +		{ +			sqpcp_eventcanceluncancellink(); +		} +		else if (strcmp(kw, "EVENTCANCELUNCANCELLINK") == 0) +		{ +			sqpcp_eventcanceluncancellink(); +		} +		else if (strcmp(kw, "EVENTCANCELUNCANCELIMAGE") == 0) +		{ +			sqpcp_eventcanceluncancelimage(); +		} +		else if (strcmp(kw, "EVENTCANCELUNCANCELTEXT") == 0) +		{ +			sqpcp_eventcanceluncanceltext(); +		} +		else if (strcmp(kw, "EVENTDELETELINK") == 0) +		{ +			sqpcp_eventdeletelink(); +		} +		else if (strcmp(kw, "EVENTACL") == 0) +		{ +			sqpcp_eventacl(); +		} +		else if (strcmp(kw, "ABOOKNAMELIST") == 0) +		{ +			ab_addrselect(); +		} +		else if (strcmp(kw, "LISTRIGHTS") == 0) +			listrights(); +		else if (strcmp(kw, "GETACL") == 0) +			getacl(); +		else if (strcmp(kw, "MSGPOS") == 0) +		{ +			printf("%ld", atol(cgi("pos"))+1); +		} +		else if (strncmp(kw, "radio:", 6) == 0) +		{ +		const char *name=strtok(kw+6, ":"); +		const char *value=strtok(0, ":"); + +			if (name && value) +			{ +				printf("<input type=\"radio\" name=\"%s\"" +					" value=\"%s\"", +					name, value); +				if ( strcmp(cgi(name), value) == 0) +					printf(" checked=\"checked\""); +				printf(" />"); +			} +		} +		else if (strncmp(kw, "checkbox:", 9) == 0) +		{ +		const char *name=strtok(kw+9, ":"); +		const char *cgivar=strtok(0, ":"); + +			if (name && cgivar) +			{ +				printf("<input type=\"checkbox\" name=\"%s\"" +					"%s />", +					name, +					*cgi(cgivar) ? " checked=\"checked\"":""); +			} +		} +		else if (strncmp(kw, "input:", 6) == 0) +		{ +			output_attrencoded(cgi(kw+6)); +		} +		else if (strncmp(kw, "select:", 7) == 0) +		{ +		const char *name=strtok(kw+7, ":"); +		const char *class=strtok(0, ":"); +		const char *size=strtok(0, ":"); + +			printf("<select name=\"%s\"", name ? name:""); +			if (class)	printf(" class=\"%s\"", class); +			if (size)	printf(" size=\"%s\"", size); +			printf(">"); +		} +		else if (strncmp(kw, "option:", 7) == 0) +		{ +		const char *name=strtok(kw+7, ":"); +		const char *cgivar=strtok(0, ":"); +		const char *cgival=strtok(0, ":"); + +			printf("<option value=\"%s\"", name ? name:""); +			if (cgivar && cgival && +				strcmp(cgi(cgivar), cgival) == 0) +				printf(" selected='selected'"); +			printf(">"); +		} +		else if (strcmp(kw, "endoption") == 0) +			printf("</option>"); +		else if (strcmp(kw, "endselect") == 0) +			printf("</select>"); +		else if (strncmp(kw, "env:", 4) == 0) { +			const char *val = getenv(kw+4); +			if (val) output_attrencoded(val); +		} +		else if (strncmp(kw, "include:", 8) == 0) +		{ +			insert_include(kw+8); +		} +		else if (strcmp(kw, "endinclude") == 0) +		{ +			break; +		} +	} +} + +/* Include another template file */ + +static FILE *openinclude(const char *p) +{ +	char buffer[BUFSIZ]; +	FILE *f; + +	buffer[0]=0; +	strncat(buffer, p, 100); +	strcat(buffer, ".inc.html"); + +	f=do_open_form(buffer, 0); + +	if (!f) +		error("Can't open form template."); + +	while (fgets(buffer, sizeof(buffer), f)) +	{ +		const char *p=strchr(buffer, '['); + +		if (!p) +			continue; + +		if (strncmp(p, "[#begininclude#]", 16) == 0) +		{ +			break; +		} +	} +	return (f); +} + + +/* Top level HTTP redirect without referencing a particular mailbox */ + +static void http_redirect_top(const char *app) +{ +const	char *p=nonloginscriptptr(); +char	*buf=malloc(strlen(p)+strlen(app)+2); + +	if (!buf)	enomem(); +	strcat(strcpy(buf, p), app); +	cgiredirect(buf); +	free(buf); +} + +/* HTTP redirects within a given mailbox, various formats */ + +void http_redirect_argu(const char *fmt, unsigned long un) +{ +char	buf[MAXLONGSIZE]; + +	sprintf(buf, "%lu", un); +	http_redirect_argss(fmt, buf, ""); +} + +void http_redirect_argss(const char *fmt, const char *arg1, const char *arg2) +{ +	http_redirect_argsss(fmt, arg1, arg2, ""); +} + +void http_redirect_argsss(const char *fmt, const char *arg1, const char *arg2, +	const char *arg3) +{ +char *base=scriptptrget(); +char *arg1s=cgiurlencode(arg1); +char *arg2s=cgiurlencode(arg2); +char *arg3s=cgiurlencode(arg3); +char *q; + +	/* We generate a Location: redirected_url header.  The actual +	** header is generated in cgiredirect, we just build it here */ + +	q=malloc(strlen(base)+strlen(fmt)+strlen(arg1s)+strlen(arg2s)+ +			strlen(arg3s)+1); +	if (!q)	enomem(); +	strcpy(q, base); +	sprintf(q+strlen(q), fmt, arg1s, arg2s, arg3s); +	cgiredirect(q); +	free(q); +	free(arg1s); +	free(arg2s); +	free(arg3s); +	free(base); +} + +void output_user_form(const char *formname) +{ +char	*p; + +	if (!*formname || strchr(formname, '.') || strchr(formname, '/')) +		error("Invalid request."); + +	if ((strcmp(formname, "filter") == 0 +	     || strcmp(formname, "autoresponse") == 0) +	    && maildir_filter_hasmaildirfilter(".")) +		/* Script kiddies... */ +		formname="nofilter"; + +	if (strcmp(formname, "filter") == 0 && *cgi("do.submitfilter")) +		mailfilter_submit(); + +	if (strcmp(formname, "gpg") == 0 && libmail_gpg_has_gpg(GPGDIR)) +		error("Invalid request."); + +	if (strcmp(formname, "gpgcreate") == 0 && libmail_gpg_has_gpg(GPGDIR)) +		error("Invalid request."); + +	if (*cgi("ldapsearch"))	/* Special voodoo for LDAP address book stuff */ +	{ +		if (ldapsearch() == 0) +		{ +			output_form("ldapsearch.html"); +			return; +		} +	} + +	/* +	** In order to hide the session ID in the URL of the message what +	** we do is that the initial URL, that contains setcookie=1, results +	** in us setting a temporary cookie that contains the session ID, +	** then we return a redirect to a url which has /printmsg/ in the +	** PATH_INFO, instead of the session ID.  The code in main() +	** traps /printmsg/ PATH_INFO, fetches the path info from the +	** cookie, and punts after resetting setcookie to 0. +	*/ + +	if (strcmp(formname, "print") == 0 && *cgi("setcookie") == '1') +	{ +	const char *qs=getenv("QUERY_STRING"); +	const char *pi=getenv("PATH_INFO"); +	const char *nl; +	char	*buf; + +		if (!pi)	pi=""; +		if (!pi)	pi=""; + +		nl=nonloginscriptptr(); + +		buf=malloc(strlen(nl) + sizeof("/printmsg/print?")+strlen(qs)); +		if (!buf)	enomem(); +		strcat(strcat(strcpy(buf, nl), "/printmsg/print?"), qs); +		cginocache(); +		cgi_setcookie("sqwebmail-pi", pi); +		printf("Refresh: 0; URL=%s\n", buf); +		free(buf); +		output_form("printredirect.html"); +		return; +	} + +	if (strcmp(cgi("fromscreen"), "mailfilter") == 0) +		maildir_filter_endmaildirfilter(".");	/* Remove the temp file */ + +	if (strcmp(formname, "logout") == 0) +	{ +		unlink(IPFILE); +		http_redirect_top(""); +		return; +	} + +	if (strcmp(formname, "fetch") == 0) +	{ +		folder_download( sqwebmail_folder, atol(cgi("pos")), +			cgi("mimeid") ); +		return; +	} + +	if (strcmp(formname, "delmsg") == 0) +	{ +		folder_delmsg( atol(cgi("pos"))); +		return; +	} + +	if (strcmp(formname, "donewmsg") == 0) +	{ +		newmsg_do(sqwebmail_folder); +		return; +	} + +	if (strcmp(formname, "doattach") == 0) +	{ +		doattach(sqwebmail_folder, cgi("draft")); +		return; +	} + +	if (strcmp(formname, "folderdel") == 0) +	{ +		folder_delmsgs(sqwebmail_folder, atol(cgi("pos"))); +		return; +	} +	if (strcmp(formname, "spellchk") == 0) +	{ +#ifdef	ISPELL +		spell_check_continue(); +#else +		printf("Status: 404"); +#endif +		return; +	} + +	if (sqpcp_loggedin()) +	{ +		if (*cgi("do.neweventpreview")) +		{ +			sqpcp_preview(); +			return; +		} + +		if (*cgi("do.neweventsave")) +		{ +			sqpcp_save(); +			return; +		} + +		if (*cgi("do.neweventpostpone")) +		{ +			sqpcp_postpone(); +			return; +		} + +		if (*cgi("do.neweventdeleteattach")) +		{ +			sqpcp_deleteattach(); +			return; +		} + +		if (*cgi("do.neweventupload")) +		{ +			sqpcp_uploadattach(); +			return; +		} + +		if (*cgi("do.neweventuppubkey")) +		{ +			sqpcp_attachpubkey(); +			return; +		} + +		if (*cgi("do.neweventupprivkey")) +		{ +			sqpcp_attachprivkey(); +			return; +		} +		if (*cgi("do.eventdelete")) +		{ +			sqpcp_dodelete(); +			return; +		} +	} + +	if (strcmp(formname, "event-edit") == 0) +	{ +		formname="folders"; +		if (sqpcp_loggedin()) +		{ +			formname="eventshow";	/* default */ +			if (sqpcp_eventedit() == 0) +				formname="newevent"; +		} +	} + + +	if (strcmp(formname, "open-draft") == 0) +	{ +		formname="newmsg"; +		if (sqpcp_has_calendar()) +			/* DRAFTS may contain event files */ +		{ +			const char *n=cgi("draft"); +			char *filename; +			FILE *fp; + +			CHECKFILENAME(n); + +			filename=maildir_find(INBOX "." DRAFTS, n); + +			if (filename) +			{ +				if ((fp=fopen(filename, "r")) != NULL) +				{ +					struct rfc822hdr h; + +					rfc822hdr_init(&h, 8192); + +					while (rfc822hdr_read(&h, fp, NULL, 0) +					       == 0) +					{ +						if (strcasecmp(h.header, +							       "X-Event") == 0) +						{ +							formname="newevent"; +							cgi_put("draftmessage", +								cgi("draft")); +							break; +						} +					} +					rfc822hdr_free(&h); +					fclose(fp); +				} +				free(filename); +			} +		} +	} + +	if (strcmp(formname, "newevent") == 0 || +	    strcmp(formname, "eventdaily") == 0 || +	    strcmp(formname, "eventweekly") == 0 || +	    strcmp(formname, "eventmonthly") == 0 || +	    strcmp(formname, "eventshow") == 0 || +	    strcmp(formname, "eventacl") == 0) +	{ +		if (!sqpcp_has_calendar() || +		    !sqpcp_loggedin()) +			formname="folders";	/* Naughty boy */ +	} + +	if (*cgi("do.search")) +	{ +		folder_search(sqwebmail_folder, atol(cgi("pos"))); +		return; +	} +	p=malloc(strlen(formname)+6); +	if (!p)	enomem(); + +	strcat(strcpy(p, formname),".html"); +	output_form(p); +	free(p); +} + + +extern void folder_cleanup(); +extern void maildir_cleanup(); +extern void mailfilter_cleanup(); + +#ifdef	ISPELL +extern void ispell_cleanup(); +#endif + +void cleanup() +{ +	sqwebmail_formname = NULL; +	sqwebmail_mailboxid=0; +	sqwebmail_folder=0; +	sqwebmail_sessiontoken=0; +	sqwebmail_content_language=0; +	sqwebmail_content_locale=0; +	sqwebmail_system_charset=0; +	if (sys_locale_charset) +		free(sys_locale_charset); +	sys_locale_charset=0; +	sqwebmail_content_ispelldict=0; +	folder_cleanup(); +	maildir_cleanup(); +	mailfilter_cleanup(); +#ifdef ISPELL +	ispell_cleanup(); +#endif + +#ifdef GZIP +	if (gzip_save_fd >= 0)	/* Restore original stdout */ +	{ +		dup2(gzip_save_fd, 1); +		close(gzip_save_fd); +		gzip_save_fd= -1; +	} +#endif + +	libmail_gpg_cleanup(); +	freeargs(); +	sqpcp_close(); +} + + + +static RETSIGTYPE catch_sig(int n) +{ +	n=n; +	cleanup(); +	maildir_cache_cancel(); +	exit(0); +} + +static void setlang() +{ +	static char *lang_buf=0; +	char *p; + +	if (sqwebmail_content_locale && *sqwebmail_content_locale +	    && (p=malloc(sizeof("LANG=")+strlen(sqwebmail_content_locale)))!=0) +	{ +		strcat(strcpy(p, "LANG="), sqwebmail_content_locale); +		putenv(p); +		if (lang_buf) +			free(lang_buf); +		lang_buf=p; +	} +} + +static void init_default_locale() +{ +char	*templatedir=get_templatedir(); +char	*cl=http11_best_content_language(templatedir, +			getenv("HTTP_ACCEPT_LANGUAGE")); + +	sqwebmail_content_language= +				http11_content_language(templatedir, cl); +	sqwebmail_content_locale= +			http11_content_locale(templatedir, cl); +	sqwebmail_content_ispelldict= +			http11_content_ispelldict(templatedir, cl); +	sqwebmail_content_charset= +			http11_content_charset(templatedir, cl); + +	free(cl); +#if	HAVE_LOCALE_H +#if	HAVE_SETLOCALE +	setlocale(LC_ALL, sqwebmail_content_locale); +#if	USE_LIBCHARSET +	setlocale(LC_CTYPE, sqwebmail_content_locale); +	sqwebmail_system_charset = locale_charset(); +#elif	HAVE_LANGINFO_CODESET +	setlocale(LC_CTYPE, sqwebmail_content_locale); +	sqwebmail_system_charset = sys_locale_charset=strdup(nl_langinfo(CODESET)); +#else +	sqwebmail_system_charset = NULL; +#endif	/* USE_LIBCHARSET */ +	setlocale(LC_CTYPE, "C"); +	setlang(); +#endif +#endif +} + +void rename_sent_folder(int really) +{ +	char buf[128]; +	char yyyymm[128]; +	const char *yyyymmp; + +	time_t t; +	struct tm *tm; +	char *pp; + +	if (really) +		(void)maildir_create(INBOX "." SENT); /* No matter what */ + +	time(&t); +	tm=localtime(&t); +	if (!tm) +		return; + +	if (tm->tm_mon == 0) +	{ +		tm->tm_mon=11; +		--tm->tm_year; +	} +	else +		--tm->tm_mon; + +	if (strftime (yyyymm, sizeof(yyyymm), "%Y%m", tm) == 0) +		return; + +	if ((yyyymmp=read_sqconfig(".", SENTSTAMP, NULL)) != NULL && +	    strcmp(yyyymm, yyyymmp) == 0) +		return; + +	if (strftime (buf, sizeof(buf), "." SENT ".%Y.%m-%b", tm) == 0) +		return; + +	pp=folder_toutf7(buf); + +	if (really) +		rename("." SENT, pp); +	free(pp); +	if (really) +		(void)maildir_create(INBOX "." SENT); + +	write_sqconfig(".", SENTSTAMP, yyyymm); +} + +static int valid_redirect(); + +static void redirect(const char *url) +{ +	if (valid_redirect()) +	{ +		printf("Refresh: 0; URL=%s\n", url); +		output_form("redirect.html"); +		return; +	} + +	printf("Content-Type: text/plain\n\n" +	       "The URL you clicked on is no longer valid.\n"); +	return; +} + +static int valid_redirect() +{ +	const char *timestamp=cgi("timestamp"), *p; +	unsigned long timestamp_n; +	time_t timestamp_t; +	time_t now; + +	if (sscanf(timestamp, "%lu", ×tamp_n) != 1) +		return 0; + +	timestamp_t=(time_t)timestamp_n; +	time(&now); + +	if (now < timestamp_t || now > timestamp_t + get_timeouthard()) +		return 0; + +	p=redirect_hash(timestamp); + +	if (*p == 0 || strcmp(cgi("md5"), p)) +		return 0; +	return 1; +} + +static void main2(); + +static void usage() +{ +	fprintf(stderr, "sqwebmaild does not accept command arguments.\n" +		"Use sqwebmaild.rc script to start sqwebmaild as a daemon.\n"); +	exit(1); +} + +static void call_main2(void *dummy) +{ +	main2(); +	cleanup(); +} + +int main(int argc, char **argv) +{ +	const char *prefork; +	int n; + +#if 0 +	if (getenv("SQWEBMAIL_DEBUG")) +	{ +		main2(); +		return (0); +	} +#endif + +	courier_authdebug_login_init(); + +	if (argc > 1) +		usage(); + +	prefork=getenv("SQWEBMAIL_PREFORK"); + +	n=prefork ? atoi(prefork):5; + +	cgi_daemon(n, SOCKFILENAME, NULL, call_main2, NULL); + +	return (0); +} + +static int setuidgid(uid_t u, gid_t g, const char *dir, void *dummy) +{ +	if (setgid(g) || setuid(u)) +	{ +		fprintf(stderr, +			"CRIT: Cache - can't setuid/setgid to %u/%u\n", +		       (unsigned)u, (unsigned)g); +		return (-1); +	} + +	if (chdir(dir)) +	{ +		fprintf(stderr, +			"CRIT: Cache - can't chdir to %s: %s\n", dir, strerror(errno)); +		return (-1); +	} +	return (0); +} + +static void main2() +{ +const char	*u; +const char	*ip_addr; +char	*pi; +char	*pi_malloced; +int	reset_cookie=0; +time_t	timeouthard=get_timeouthard(); + + +#ifdef	GZIP +	gzip_save_fd= -1; +#endif +	u=ip_addr=pi=NULL; + +	ip_addr=getenv("REMOTE_ADDR"); + +#if 0 +	{ +		FILE *f; + +		f=fopen("/tmp/pid", "w"); +		fprintf(f, "%d\n", (int)getpid()); +		fclose(f); +		sleep(10); +	} +#endif + +	/* +	 * Note: if we get a signal during FastCGI processing, this means +	 * means we need to terminate so that the webserver can respawn us. +	 * Exception is SIGPIPE which we just ignore (this is what we get +	 * if we try to write data to a client which goes away before +	 * we finished sending them the reply) +	 */ + +	signal(SIGHUP, catch_sig); +	signal(SIGINT, catch_sig); +	signal(SIGPIPE, catch_sig); +	signal(SIGTERM, catch_sig); + +	if (!ip_addr)	ip_addr="127.0.0.1"; + +	umask(0077); + +	{ +		timeouthard=get_timeouthard(); +	} + +	if (maildir_cache_init(timeouthard, CACHEDIR, CACHEOWNER, authvars)) +	{ +		printf("Content-Type: text/plain\n\nmaildir_cache_init() failed\n"); +		fake_exit(0); +	} + +	pi=getenv("PATH_INFO"); + +	pi_malloced=0; +	sqpcp_init(); + +	if (pi && strncmp(pi, "/printmsg/", 10) == 0) +	{ +		/* See comment in output_user_form */ + +		pi_malloced=pi=cgi_cookie("sqwebmail-pi"); +		if (*pi_malloced == 0) +		{ +			free(pi_malloced); +			setgid(getgid()); +			setuid(getuid()); +			output_form("printnocookie.html"); +			return; +		} +		reset_cookie=1; +		cgi_setcookie("sqwebmail-pi", "DELETED"); +	} + +	if (pi && strncmp(pi, "/login/", 7) == 0) +	{ +	const char	*p; +	time_t	last_time, current_time; +	char	*q; +	time_t	timeoutsoft=get_timeoutsoft(); + +		/* Logging into the mailbox */ + +		pi=strdup(pi); +		if (pi_malloced)	free(pi_malloced); + +		if (!pi)	enomem(); + +		(void)strtok(pi, "/");	/* Skip login */ +		u=strtok(NULL, "/");	/* mailboxid */ +		sqwebmail_sessiontoken=strtok(NULL, "/"); /* sessiontoken */ +		q=strtok(NULL, "/");	/* login time */ +		login_time=0; +		while (q && *q >= '0' && *q <= '9') +			login_time=login_time * 10 + (*q++ - '0'); + +		if (maildir_cache_search(u, login_time, setuidgid, NULL) +		    && prelogin(u)) +		{ +			free(pi); +			error("Unable to access your mailbox, sqwebmail permissions may be wrong."); +		} + +		time(¤t_time); + +		/* Ok, boys and girls, time to validate the connection as +		** follows */ + +		if (	!sqwebmail_sessiontoken + +		/* 1. Read IPFILE.  Check that it's timestamp is current enough, +		** and the session hasn't timed out. +		*/ + +			|| !(p=read_sqconfig(".", IPFILE, &last_time)) + +/*			|| last_time > current_time	*/ + +			|| last_time + timeouthard < current_time + +		/* 2. IPFILE will contain seven words - IP address, session +		** token, language, locale, ispell dictionary, +		** timezone, charset.  Validate both. +		*/ +			|| !(q=strdup(p)) +			|| !(p=strtok(q, " ")) +			|| (strcmp(p, ip_addr) && strcmp(p, "none")) +			|| !(p=strtok(NULL, " ")) +			|| strcmp(p, sqwebmail_sessiontoken) +			|| !(p=strtok(NULL, " ")) +			|| !(sqwebmail_content_language=strdup(p)) +			|| !(p=strtok(NULL, " ")) +			|| !(sqwebmail_content_locale=strdup(p)) +			|| !(p=strtok(NULL, " ")) +			|| !(sqwebmail_content_ispelldict=strdup(p)) +			|| !(p=strtok(NULL, " ")) +			|| set_timezone(p) +			|| !(p=strtok(NULL, " ")) +			|| !(sqwebmail_content_charset=strdup(p)) + +		/* 3. Check the timestamp on the TIMESTAMP file.  See if the +		** session has reached its soft timeout. +		*/ + +			|| !read_sqconfig(".", TIMESTAMP, &last_time) + +/*			|| last_time > current_time	*/ + +			|| last_time + timeoutsoft < current_time) +		{ +			setgid(getgid()); +			setuid(getuid());	/* Drop root prevs */ +			if (chdir("/") < 0) +			{ +				output_form("expired.html"); +				return; +			} +			cgi_setup(); +			init_default_locale(); +			free(pi); + +			u=getenv("SQWEBMAIL_SHAREDMUNGENAMES"); + +			maildir_info_munge_complex(u && *u); + +			if (strcmp(cgi("form"), "logout") == 0) +				/* Already logged out, and the link +				** had target=_parent tag. +				*/ +			{ +				http_redirect_top(""); +				return; +			} +			output_form("expired.html"); +			return; +		} +		free(q); +		cgiformdatatempdir("tmp"); +		cgi_setup();	/* Read CGI environment */ +		if (reset_cookie) +			cgi_put("setcookie", "0"); + +		/* Update soft timeout stamp */ + +		write_sqconfig(".", TIMESTAMP, ""); + +		/* We must always have the folder CGI arg */ + +		if (!*(sqwebmail_folder=cgi("folder"))) +		{ +			init_default_locale(); +			output_form("expired.html"); +			free(pi); +			return; +		} + +		sqwebmail_mailboxid=u; + +		{ +			struct stat stat_buf; + +			if (stat(".", &stat_buf) < 0) +			{ +				output_form("expired.html"); +				free(pi); +				return; +			} + +			sqwebmail_homedir_dev=stat_buf.st_dev; +			sqwebmail_homedir_ino=stat_buf.st_ino; +		} + +#if	HAVE_LOCALE_H +#if	HAVE_SETLOCALE +		setlocale(LC_ALL, sqwebmail_content_locale); +#if	USE_LIBCHARSET +		setlocale(LC_CTYPE, sqwebmail_content_locale); +		sqwebmail_system_charset = locale_charset(); +#elif	HAVE_LANGINFO_CODESET +		setlocale(LC_CTYPE, sqwebmail_content_locale); +		sqwebmail_system_charset = sys_locale_charset +			= strdup(nl_langinfo(CODESET)); +#else +		sqwebmail_system_charset = NULL; +#endif  /* USE_LIBCHARSET */ +		setlocale(LC_CTYPE, "C"); +		setlang(); +#endif +#endif +		CHECKFILENAME(sqwebmail_folder); + +		strcpy(sqwebmail_folder_rights, ALL_RIGHTS); +		acl_computeRightsOnFolder(sqwebmail_folder, +					  sqwebmail_folder_rights); + +		pref_init(); +		(void)sqpcp_loggedin(); +		if (auth_getoptionenvint("disableshared")) +		{ +			maildir_acl_disabled=1; +			maildir_newshared_disabled=1; +		} + +		if (strcmp(cgi("form"), "empty")) +		{ +			if (*cgi("refresh")) +			{ +				printf("Refresh: %ld; URL=", +				       (long)get_timeoutsoft()/2); +				output_scriptptrget(); +				printf("&empty=1&refresh=1\n"); +			} +		} + +		output_user_form(cgi("form")); +		free(pi); +	} +	else +		/* Must be one of those special forms */ +	{ +	char	*rm; +	long	n; + +		if (pi_malloced)	free(pi_malloced); + +		if ((rm=getenv("REQUEST_METHOD")) == 0 || +			(strcmp(rm, "POST") == 0 && +				((rm=getenv("CONTENT_TYPE")) != 0 && +				strncasecmp(rm,"multipart/form-data;", 20) +					== 0))) +			emsg("multipart/formdata posts not allowed",""); + +		/* Some additional safety checks */ + +		rm=getenv("CONTENT_LENGTH"); +		n= rm ? atol(rm):0; +		if (n < 0 || n > 256)	enomem(); + +		cgi_setup(); +		init_default_locale(); + +		if (*(u=trim_spaces(cgi("username")))) +			/* Request to log in */ +		{ +		const char *p=cgi("password"); +		const char *mailboxid; +		const char *u2=cgi("logindomain"); +		char	*ubuf=malloc(strlen(u)+strlen(u2)+2); + +			if (ubuf == NULL) enomem(); +			strcpy(ubuf, u); +			if (*u2) +				strcat(strcat(ubuf, "@"), u2); + +			maildir_cache_start(); + +			if (*p && (mailboxid=do_login(ubuf, p, ip_addr)) +			    != 0) +			{ +				char	*q; +				const	char *saveip=ip_addr; +				char	*tz; + +				sqwebmail_mailboxid=mailboxid; +				sqwebmail_folder="INBOX"; +				sqwebmail_sessiontoken=random128(); + +				tz=get_timezone(); +				if (*cgi("sameip") == 0) +					saveip="none"; + +				q=malloc(strlen(saveip) +					 +strlen(sqwebmail_sessiontoken) +					 +strlen(sqwebmail_content_language) +					 +strlen(sqwebmail_content_ispelldict) +					 +strlen(sqwebmail_content_charset) +					 +strlen(tz) +					 +strlen(sqwebmail_content_locale)+7); +				if (!q)	enomem(); +				sprintf(q, "%s %s %s %s %s %s %s", saveip, +					sqwebmail_sessiontoken, +					sqwebmail_content_language, +					sqwebmail_content_locale, +					sqwebmail_content_ispelldict, +					tz, +					sqwebmail_content_charset); +				write_sqconfig(".", IPFILE, q); +				free(q); +				free(tz); +				time(&login_time); +				{ +					char buf[1024]; + +					buf[sizeof(buf)-1]=0; +					if (getcwd(buf, sizeof(buf)-1) == 0) +					{ +						fprintf(stderr, +						       "CRIT: getcwd() failed: %s\n",strerror(errno)); +						fake_exit(1); +					} /* oops */ + +					maildir_cache_save(mailboxid, +							   login_time, +							   buf, +							   geteuid(), getegid() +							   ); + +				} +				write_sqconfig(".", TIMESTAMP, ""); +#if	HAVE_LOCALE_H +#if	HAVE_SETLOCALE +				setlocale(LC_ALL, sqwebmail_content_locale); +#if	USE_LIBCHARSET +				setlocale(LC_CTYPE, sqwebmail_content_locale); +				sqwebmail_system_charset = locale_charset(); +#elif	HAVE_LANGINFO_CODESET +				setlocale(LC_CTYPE, sqwebmail_content_locale); + +				sqwebmail_system_charset = sys_locale_charset +					= strdup(nl_langinfo(CODESET)); +#else +				sqwebmail_system_charset = NULL; +#endif	/* USE_LIBCHARSET */ +				setlocale(LC_CTYPE, "C"); +				setlang(); +#endif +#endif +				pref_init(); +				(void)maildir_create(INBOX "." DRAFTS); + +				if (!pref_noautorenamesent) +					(void)rename_sent_folder(1); +				(void)maildir_create(INBOX "." SENT); +				(void)maildir_create(INBOX "." TRASH); +				maildir_autopurge(); +				unlink(SHAREDPATHCACHE); + +				sqpcp_login(ubuf, p); +				maildir_acl_reset("."); + +				http_redirect_argss(*cgi("inpublic") ? +						    "&form=folders": +						    "&form=refreshfr", "", ""); +				free(ubuf); +				return; +			} +			maildir_cache_cancel(); + +			free(ubuf); +			setgid(getgid()); +			setuid(getuid()); +			output_form("invalid.html");	/* Invalid login */ +			return; +		} + +		setgid(getgid()); +		setuid(getuid()); +		if ( *(u=cgi("redirect"))) +			/* Redirection request to hide the referral tag */ +		{ +			redirect(u); +		} +		else if ( *cgi("noframes") == '1') +			output_form("login.html");	/* Main frame */ +		else +		if ( *cgi("empty") == '1') +		{ +			output_form("empty.html");	/* Minor frameset */ +		} + +/* +** Apparently we can't show just SCRIPT NAME as our frameset due to some +** weird bug in Communicator which, under certain conditions, will get +** confused figuring out which page views have expired.  POSTs with URLs +** referring to SCRIPT_NAME will be replied with an expiration header, and +** Communicator will assume that index.html also has expired, forcing a +** frameset reload the next time the Communicator window is resized, +** essentially logging the user off. +*/ + +		else if (*cgi("index") == '1') +			output_form("index.html");	/* Frameset Window */ +		else +		{ +			http_redirect_top("?index=1"); +		} +				 +		return; +	} +	return; +} + +#ifdef	malloc + +#undef	malloc +#undef	realloc +#undef	free +#undef	strdup +#undef	calloc + +static void *allocp[1000]; + +extern void *malloc(size_t), *realloc(void *, size_t), free(void *), +	*calloc(size_t, size_t); +extern char *strdup(const char *); + +char *my_strdup(const char *c) +{ +size_t	i; + +	for (i=0; i<sizeof(allocp)/sizeof(allocp[0]); i++) +		if (!allocp[i]) +			return (allocp[i]=strdup(c)); +	abort(); +	return (0); +} + +void *my_malloc(size_t n) +{ +size_t	i; + +	for (i=0; i<sizeof(allocp)/sizeof(allocp[0]); i++) +		if (!allocp[i]) +			return (allocp[i]=malloc(n)); +	abort(); +	return (0); +} + +void *my_calloc(size_t a, size_t b) +{ +size_t	i; + +	for (i=0; i<sizeof(allocp)/sizeof(allocp[0]); i++) +		if (!allocp[i]) +			return (allocp[i]=calloc(a,b)); +	abort(); +	return (0); +} + +void *my_realloc(void *p, size_t s) +{ +size_t	i; + +	for (i=0; i<sizeof(allocp)/sizeof(allocp[0]); i++) +		if (p && allocp[i] == p) +		{ +		void	*q=realloc(p, s); + +			if (q)	allocp[i]=q; +			return (q); +		} +	abort(); +} + +void my_free(void *p) +{ +size_t i; + +	for (i=0; i<sizeof(allocp)/sizeof(allocp[0]); i++) +		if (p && allocp[i] == p) +		{ +			free(p); +			allocp[i]=0; +			return; +		} +	abort(); +} +#endif + +/* Trim leading and trailing white spaces from string */ + +char *trim_spaces(const char *s) +{ +	char *p, *q; + +	p=strdup(s); +	if (!p) +		enomem(); + +	if (*p) +	{ +		for (q=p+strlen(p)-1; q >= p && isspace(*q); q--) +			*q=0; + +		for (q=p; *q && isspace(*q); q++) +			; +		if (p != q) +			p=q; +	} + +	return (p); +} + | 
