diff options
| author | Sam Varshavchik | 2018-07-16 21:39:43 -0400 | 
|---|---|---|
| committer | Sam Varshavchik | 2018-07-16 21:39:43 -0400 | 
| commit | 711f59c72f26a1af7865c70edeac54db12eac076 (patch) | |
| tree | f66485c395028804f8c25bfbe21832bbf9f98eab | |
| parent | 0184320a0c202d3727840624dbeaac9b3b10a6de (diff) | |
| parent | 443a747d471c570e3a3d100097430d80615572c3 (diff) | |
| download | courier-libs-711f59c72f26a1af7865c70edeac54db12eac076.tar.bz2 | |
Merge branch 'eai'
| -rw-r--r-- | imap/capability.c | 4 | ||||
| -rw-r--r-- | imap/fetch.c | 40 | ||||
| -rw-r--r-- | imap/imapd.c | 533 | ||||
| -rw-r--r-- | imap/imapd.h | 1 | ||||
| -rw-r--r-- | imap/imaplogin.c | 3 | ||||
| -rw-r--r-- | imap/imaptoken.c | 208 | ||||
| -rw-r--r-- | imap/imaptoken.h | 2 | ||||
| -rw-r--r-- | imap/imapwrite.h | 3 | ||||
| -rw-r--r-- | imap/msgenvelope.c | 5 | ||||
| -rw-r--r-- | imap/smap.c | 3 | ||||
| -rw-r--r-- | maildir/Makefile.am | 4 | ||||
| -rw-r--r-- | maildir/maildirfilter.c | 5 | ||||
| -rw-r--r-- | maildir/maildirfilter.h | 1 | ||||
| -rw-r--r-- | maildir/maildirfilter2.c | 2 | ||||
| -rw-r--r-- | maildir/maildirinfo.c | 29 | ||||
| -rw-r--r-- | maildir/maildirinfo.h | 6 | ||||
| -rw-r--r-- | maildir/maildirinfo2.c | 148 | ||||
| -rw-r--r-- | maildir/maildirmake.c | 436 | ||||
| -rw-r--r-- | maildir/maildirmake.sgml | 323 | ||||
| -rw-r--r-- | maildir/testmaildirfilter.c | 2 | ||||
| -rw-r--r-- | rfc2045/rfc2045.h | 2 | 
21 files changed, 1542 insertions, 218 deletions
| diff --git a/imap/capability.c b/imap/capability.c index 25e77a2..2a83bb8 100644 --- a/imap/capability.c +++ b/imap/capability.c @@ -122,7 +122,7 @@ void imapcapability()  		if (imap_externalauth())  			writes(" AUTH=EXTERNAL");  	} -			 +  	p=getenv("OUTBOX"); @@ -134,4 +134,6 @@ void imapcapability()  	if (magictrash())  		writes(" XMAGICTRASH"); + +	writes(" ENABLE UTF8=ACCEPT");  } diff --git a/imap/fetch.c b/imap/fetch.c index 54e7139..1eb52dd 100644 --- a/imap/fetch.c +++ b/imap/fetch.c @@ -18,6 +18,7 @@  #include	<sys/types.h>  #include	<sys/stat.h> +#include	"imapd.h"  #include	"imaptoken.h"  #include	"imapwrite.h"  #include	"imapscanclient.h" @@ -248,6 +249,7 @@ int do_fetch(unsigned long n, int byuid, void *p)  	struct	rfc2045 *rfc2045p;  	int	seen;  	int	open_err; +	const char *cannot_open_because="";  	fp=NULL;  	open_err=0; @@ -275,16 +277,30 @@ int do_fetch(unsigned long n, int byuid, void *p)  	rfc2045p=0;  	while (fi)  	{ -		if (fetchitem(&fp, &open_err, fi, ¤t_maildir_info, n-1, -			&rfc2045p))	seen=1; +		int rc=fetchitem(&fp, &open_err, fi, ¤t_maildir_info, n-1, +				 &rfc2045p); + +		if (rc > 0) +			seen=1; +		if (rc < 0) +		{ +			open_err=1; +			cannot_open_because= +				" because it is a Unicode message and your" +				" mail client did not enable Unicode support." +				" Please use a mail client that supports" +				" IMAP with UTF-8 (see" +				" https://tools.ietf.org/html/rfc6855.html)"; +		}  		if ((fi=fi->next) != 0)	writes(" ");  	}  	writes(")\r\n");  	if (open_err)  	{ -		writes("* NO Cannot open message "); +		writes("* NO [ALERT] Cannot open message ");  		writen(n); +		writes(cannot_open_because);  		writes("\r\n");  		return (0);  	} @@ -327,6 +343,7 @@ static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi,  	int	parsemime=0;  	int	rc=0;  	int	do_open=1; +	int	mimecorrectness=0;  	if (strcmp(fi->name, "ALL") == 0)  	{ @@ -345,15 +362,20 @@ static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi,  		if (fi->bodysection)  		{  			fetchfunc= &fetchmsgbody; +			mimecorrectness=1;  			rc=1;  		}  	}  	else if (strcmp(fi->name, "BODY.PEEK") == 0)  	{  		parsemime=1; +		mimecorrectness=1;  		fetchfunc= &body;  		if (fi->bodysection) +		{  			fetchfunc= &fetchmsgbody; +			mimecorrectness=1; +		}  	}  	else if (strcmp(fi->name, "ENVELOPE") == 0)  	{ @@ -382,11 +404,13 @@ static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi,  	else if (strcmp(fi->name, "RFC822") == 0)  	{  		fetchfunc= &rfc822; +		mimecorrectness=1;  		rc=1;  	}  	else if (strcmp(fi->name, "RFC822.HEADER") == 0)  	{  		fetchfunc= &rfc822header; +		mimecorrectness=1;  	}  	else if (strcmp(fi->name, "RFC822.SIZE") == 0)  	{ @@ -396,6 +420,7 @@ static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi,  	else if (strcmp(fi->name, "RFC822.TEXT") == 0)  	{  		parsemime=1; +		mimecorrectness=1;  		fetchfunc= &rfc822text;  	}  	else if (strcmp(fi->name, "UID") == 0) @@ -415,11 +440,18 @@ static int fetchitem(FILE **fp, int *open_err, struct fetchinfo *fi,  		}  	} +	if (mimecorrectness && !enabled_utf8) +		parsemime=1; +  	if (parsemime && !*mimep)  	{  		*mimep=fetch_alloc_rfc2045(msgnum, *fp);  	} +	if (mimecorrectness && !enabled_utf8 && +	    ((*mimep)->rfcviolation & RFC2045_ERR8BITHEADER)) +		return -1; +  	(*fetchfunc)(*fp, fi, i, msgnum, *mimep);  	return (rc);  } @@ -704,7 +736,7 @@ off_t cache_virtual_chars;  off_t cache_phys_chars;  	headermimep=mimep; -  +  	while (p && isdigit((int)(unsigned char)*p))  	{  	unsigned long n=0; diff --git a/imap/imapd.c b/imap/imapd.c index fca7149..78a689c 100644 --- a/imap/imapd.c +++ b/imap/imapd.c @@ -1,5 +1,5 @@  /* -** Copyright 1998 - 2011 Double Precision, Inc. +** Copyright 1998 - 2018 Double Precision, Inc.  ** See COPYING for distribution information.  */ @@ -85,6 +85,7 @@  #include	"maildir/maildirinfo.h"  #include	"maildir/loginexec.h"  #include	"rfc822/rfc822.h" +#include	"rfc2045/rfc2045.h"  #include	<courier-unicode.h>  #include	"maildir/maildirkeywords.h" @@ -140,6 +141,8 @@ char *current_mailbox_acl;  dev_t homedir_dev;  ino_t homedir_ino; +int enabled_utf8=0; +  void rfc2045_error(const char *p)  {  	if (write(2, p, strlen(p)) < 0) @@ -147,6 +150,19 @@ void rfc2045_error(const char *p)  	_exit(0);  } +void writemailbox(const char *mailbox) +{ +	char *encoded=imap_filename_to_foldername(enabled_utf8, mailbox); + +	if (!encoded) +	{ +		fprintf(stderr, "ERR: imap_filename_to_foldername(%s) failed\n", +			mailbox); +		exit(1); +	} +	writeqs(encoded); +	free(encoded); +}  extern int maildirsize_read(const char *,int *,off_t *,unsigned *,unsigned *,struct stat *); @@ -330,10 +346,10 @@ int is_reserved_name(const char *name)  	return 0;  } -char *decode_valid_mailbox(const char *p, int autosubscribe) +static char *decode_valid_mailbox_utf8(const char *p, int autosubscribe)  {  	struct maildir_info mi; -	char *q, *r; +	char *r;  	if (maildir_info_imap_find(&mi, p, getenv("AUTHENTICATED")) < 0)  	{ @@ -342,7 +358,7 @@ char *decode_valid_mailbox(const char *p, int autosubscribe)  	if (mi.homedir && mi.maildir)  	{ -		q=maildir_name2dir(mi.homedir, mi.maildir); +		char *q=maildir_name2dir(mi.homedir, mi.maildir);  		if (q)  		{ @@ -403,6 +419,22 @@ char *decode_valid_mailbox(const char *p, int autosubscribe)  	return (NULL);  } +char *decode_valid_mailbox(const char *mailbox, int autosubscribe) +{ +	char *p=imap_foldername_to_filename(enabled_utf8, mailbox); +	char *q; + +	if (!p) +	{ +		errno=EINVAL; +		return NULL; +	} + +	q=decode_valid_mailbox_utf8(p, autosubscribe); +	free(p); +	return q; +} +  static int decode_date_time(char *p, time_t *tret)  {  unsigned	i; @@ -561,7 +593,7 @@ static char *parse_mailbox_error(const char *tag,  				** a real folder (such as this one).  				*/ -	int autosubscribe)	/* Really DUMP clients that do a LIST, +	int autosubscribe)	/* Really DUMB clients that do a LIST,  				** and don't bother to check if a folder is  				** subscribed to, or not (Pine)  				*/ @@ -572,17 +604,17 @@ char	*mailbox;  		curtoken->tokentype != IT_ATOM &&  		curtoken->tokentype != IT_QUOTED_STRING)  	{ -		mailbox=0; +		writes(tag); +		writes(" BAD Invalid command\r\n"); +		return (0);  	} -	else -	{ -		if (ok_hierarchy && (mailbox=strrchr(curtoken->tokenbuf, -			HIERCH)) && mailbox[1] == 0) -				*mailbox=0; -		mailbox=decode_valid_mailbox(curtoken->tokenbuf, -			autosubscribe); -	} +	if (ok_hierarchy && (mailbox=strrchr(curtoken->tokenbuf, +					     HIERCH)) && mailbox[1] == 0) +		*mailbox=0; + +	mailbox=decode_valid_mailbox(curtoken->tokenbuf, +				     autosubscribe);  	if ( mailbox == 0)  	{ @@ -680,19 +712,23 @@ static int store_mailbox(const char *tag, const char *mailbox,  			 struct	imapflags *flags,  			 struct libmail_kwMessage *keywords,  			 time_t	timestamp, -			 unsigned long nbytes, +			 struct imaptoken *curtoken,  			 unsigned long *new_uidv,  			 unsigned long *new_uid)  { -char	*tmpname; -char	*newname; -char	*p; -char    *e; -FILE	*fp; -unsigned long n; -static const char nowrite[]=" NO [ALERT] Cannot create message - no write permission or out of disk space.\r\n"; -int	lastnl; -int     rb; +	unsigned long nbytes=curtoken->tokennum; +	char	*tmpname; +	char	*newname; +	char	*p; +	char    *e; +	FILE	*fp; +	unsigned long n; +	static const char nowrite[]=" NO [ALERT] Cannot create message - no write permission or out of disk space.\r\n"; +	int	lastnl; +	int     rb; +	int	errflag; +	struct rfc2045 *rfc2045_parser; +	const char *errmsg=nowrite;  	fp=maildir_mkfilename(mailbox, flags, 0, &tmpname, &newname); @@ -710,6 +746,8 @@ int     rb;  	current_temp_fd=fileno(fp);  	current_temp_fn=tmpname; +	rfc2045_parser=rfc2045_alloc(); +  	while (nbytes)  	{  		read_string(&p, &n, nbytes); @@ -726,31 +764,53 @@ int     rb;  			}  			else if (e)  			{ +				rfc2045_parse(rfc2045_parser, p, e-p);  				rb = fwrite(p, 1, e-p, fp);  			}  			else  			{ +				rfc2045_parse(rfc2045_parser, p, n);  				rb = fwrite(p, 1, n, fp);  			}  			n -= rb;  			p += rb;  		}  	} -	if (!lastnl) putc('\n', fp); +	if (!lastnl) +	{ +		putc('\n', fp); +		rfc2045_parse(rfc2045_parser, "\n", 1); +	}  	current_temp_fd=-1;  	current_temp_fn=NULL; +	errflag=0;  	if (fflush(fp) || ferror(fp))  	{  		fprintf(stderr,                          "ERR: error storing a message, user=%s, errno=%d\n",                                  getenv("AUTHENTICATED"), errno); +		errflag=1; +	} +	if ((rfc2045_parser->rfcviolation & RFC2045_ERR8BITHEADER) && +	    curtoken->tokentype != IT_LITERAL8_STRING_START) +	{ +		errmsg=" NO [ALERT] Your IMAP client does not appear to " +			"correctly implement Unicode messages, " +			"see https://tools.ietf.org/html/rfc6855.html\r\n"; +		errflag=1; +	} + +	rfc2045_free(rfc2045_parser); + +	if (errflag) +	{  		fclose(fp);  		unlink(tmpname);  		writes(tag); -		writes(nowrite); +		writes(errmsg);  		free(tmpname);  		free(newname);  		return (-1); @@ -2413,7 +2473,7 @@ static int list_callback(const char *hiersep,  				   different order */  				writes("* ACLFAILED \""); -				writeqs(mailbox); +				writemailbox(mailbox);  				writes("\"");  				accessdenied("LIST(MYRIGHTS)",  					     mailbox, @@ -2440,7 +2500,7 @@ static int list_callback(const char *hiersep,  				   different order */  				writes("* ACLFAILED \""); -				writeqs(mailbox); +				writemailbox(mailbox);  				writes("\"");  				accessdenied("LIST(ACL)",  					     mailbox, @@ -2480,7 +2540,7 @@ static int list_callback(const char *hiersep,  	writes(") ");  	writes(hiersep);  	writes(" \""); -	writeqs(mailbox); +	writemailbox(mailbox);  	writes("\"");  	if (flags & (LIST_ACL|LIST_MYRIGHTS|LIST_POSTADDRESS)) @@ -2872,6 +2932,18 @@ char *get_myrightson(const char *mailbox)  	return rights;  } +char *get_myrightson_folder(const char *folder) +{ +	char *p=imap_foldername_to_filename(enabled_utf8, folder); +	char *r; + +	if (!p) +		return NULL; + +	r=get_myrightson(p); +	free(p); +	return r; +}  char *compute_myrights(maildir_aclt_list *l, const char *l_owner)  { @@ -3113,26 +3185,37 @@ static int aclcmd(const char *tag)  	case IT_LPAREN:  		while ((curtoken=nexttoken_nouc())->tokentype != IT_RPAREN)  		{ +			char *p; +  			if (curtoken->tokentype != IT_QUOTED_STRING &&  				curtoken->tokentype != IT_ATOM &&  				curtoken->tokentype != IT_NUMBER)  			{ +				writes(tag); +				writes(" BAD Invalid command\r\n"); +				return 0; +			} + +			p=imap_foldername_to_filename(enabled_utf8, +						      curtoken->tokenbuf); +			if (!p) +			{  				errno=EINVAL;  				return -1;  			} -  			mblist=NULL; -			if (mailbox_scan("", curtoken->tokenbuf, 0, +			if (mailbox_scan("", p, 0,  					 aclmailbox_scan, &mblist) ||  			    aclmailbox_merge(mblist, &mailboxlist))  			{ +				free(p);  				free_tempmailboxlist(mblist);  				free_mailboxlist(mailboxlist);  				return -1;  			} - +			free(p);  			free_tempmailboxlist(mblist);  		}  		break; @@ -3141,17 +3224,27 @@ static int aclcmd(const char *tag)  	case IT_NUMBER:  		mblist=NULL; -		if (mailbox_scan("", curtoken->tokenbuf, LIST_CHECK1FOLDER, -				 aclmailbox_scan, &mblist) || -		    aclmailbox_merge(mblist, &mailboxlist)) -  		{ +			char *p=imap_foldername_to_filename(enabled_utf8, +							    curtoken->tokenbuf); + +			if (mailbox_scan("", p, LIST_CHECK1FOLDER, +					 aclmailbox_scan, &mblist) || +			    aclmailbox_merge(mblist, &mailboxlist)) +			{ +				free(p); +				free_tempmailboxlist(mblist); +				free_mailboxlist(mailboxlist); +				return -1; +			} +			free(p);  			free_tempmailboxlist(mblist); -			free_mailboxlist(mailboxlist); -			return -1;  		} -		free_tempmailboxlist(mblist);  		break; +	case IT_ERROR: +		writes(tag); +		writes(" BAD Invalid command\r\n"); +		return 0;  	}  	rc=0; @@ -3288,7 +3381,7 @@ static void aclfailed(const char *mailbox, const char *identifier)  	if (!identifier)  	{  		writes("* ACLFAILED \""); -		writeqs(mailbox); +		writemailbox(mailbox);  		writes("\" ");  		writes(strerror(errno));  		writes("\r\n"); @@ -3296,7 +3389,7 @@ static void aclfailed(const char *mailbox, const char *identifier)  	}  	writes("* RIGHTS-INFO \""); -	writeqs(mailbox); +	writemailbox(mailbox);  	writes("\" \"");  	writeqs(identifier);  	writes("\" "); @@ -3318,7 +3411,7 @@ static int acl_settable_folder(char *mailbox,  	if (mi->homedir == NULL || mi->maildir == NULL)  	{  		writes("* ACLFAILED \""); -		writeqs(mailbox); +		writemailbox(mailbox);  		writes("\" ACLs may not be modified for special mailbox\r\n");  		maildir_info_destroy(mi);  		*mailbox=0; @@ -3481,7 +3574,7 @@ static int aclstore(const char *tag, struct temp_acl_mailbox_list *mailboxes)  			if (acl_rights[0] == 0)  			{  				writes("* ACLFAILED \""); -				writeqs(mailboxes[i].mailbox); +				writemailbox(mailboxes[i].mailbox);  				writes("\"");  				accessdenied("ACL STORE",  					     mailboxes[i].mailbox, @@ -3585,7 +3678,7 @@ static int aclset(const char *tag, struct temp_acl_mailbox_list *mailboxes)  			{  				maildir_info_destroy(&mi);  				writes("* ACLFAILED \""); -				writeqs(mailboxes[i].mailbox); +				writemailbox(mailboxes[i].mailbox);  				writes("\"");  				accessdenied("ACL SET", mailboxes[i].mailbox,  					     ACL_ADMINISTER); @@ -3657,7 +3750,7 @@ static int acldelete(const char *tag, struct temp_acl_mailbox_list *mailboxes)  			if (acl_rights[0] == 0)  			{  				writes("* ACLFAILED \""); -				writeqs(mailboxes[i].mailbox); +				writemailbox(mailboxes[i].mailbox);  				writes("\"");  				accessdenied("ACL DELETE",  					     mailboxes[i].mailbox, @@ -3817,6 +3910,7 @@ static int append(const char *tag, const char *mailbox, const char *path)  	unsigned long new_uidv, new_uid;  	char access_rights[8];  	struct imaptoken *curtoken; +	int need_rparen;  	if (access(path, 0))  	{ @@ -3896,9 +3990,35 @@ static int append(const char *tag, const char *mailbox, const char *path)  	else if (curtoken->tokentype == IT_NIL)  		curtoken=nexttoken_noparseliteral(); -	if (curtoken->tokentype != IT_LITERAL_STRING_START) +	need_rparen=0; + +	if (enabled_utf8 && curtoken->tokentype == IT_ATOM && +	    strcmp(curtoken->tokenbuf, "UTF8") == 0) +	{ +		curtoken=nexttoken(); +		if (curtoken->tokentype != IT_LPAREN) +		{ +			libmail_kwmDestroy(keywords); +			return (-1); +		} + +		curtoken=nexttoken_noparseliteral(); +		if (curtoken->tokentype != IT_LITERAL8_STRING_START) +		{ +			libmail_kwmDestroy(keywords); + +			/* Don't break the protocol level */ +			convert_literal_tokens(curtoken); +			return (-1); +		} +		need_rparen=1; +	} +	else if (curtoken->tokentype != IT_LITERAL_STRING_START)  	{  		libmail_kwmDestroy(keywords); + +		/* Don't break the protocol level */ +		convert_literal_tokens(curtoken);  		return (-1);  	} @@ -3908,7 +4028,7 @@ static int append(const char *tag, const char *mailbox, const char *path)  			  acl_flags_adjust(access_rights, &flags)  			  ? NULL:keywords,  			  timestamp, -			  curtoken->tokennum, &new_uidv, &new_uid)) +			  curtoken, &new_uidv, &new_uid))  	{  		libmail_kwmDestroy(keywords);  		unread('\n'); @@ -3916,6 +4036,14 @@ static int append(const char *tag, const char *mailbox, const char *path)  	}  	libmail_kwmDestroy(keywords); +	if (need_rparen) +	{ +		if (nexttoken()->tokentype != IT_RPAREN) +		{ +			return (-1); +		} +	} +  	if (nexttoken()->tokentype != IT_EOL)  	{  		return (-1); @@ -4121,6 +4249,13 @@ static int validate_charset(const char *tag, char **charset)  	if (*charset == NULL)  		*charset=my_strdup("UTF-8"); +	if (enabled_utf8 && strcmp(*charset, "UTF-8")) +	{ +		writes(tag); +		writes(" BAD [BADCHARSET] Only UTF-8 charset is valid after enabling RFC 6855 support\r\n"); +		return (-1); +	} +  	conv=unicode_convert_tou_init(*charset, &ucptr, &ucsize, 1);  	if (!conv) @@ -4246,8 +4381,15 @@ int	uid=0;  			if (curtoken->tokentype != IT_QUOTED_STRING &&  				curtoken->tokentype != IT_ATOM &&  				curtoken->tokentype != IT_NUMBER) -				return (-1); -			reference=my_strdup(curtoken->tokenbuf); +			{ +				writes(tag); +				writes(" BAD Invalid command\r\n"); +				return (0); +			} +			reference=imap_foldername_to_filename +				(enabled_utf8, curtoken->tokenbuf); +			if (!reference) +				return -1;  		}  		curtoken=nexttoken_nouc(); @@ -4258,8 +4400,20 @@ int	uid=0;  			if (curtoken->tokentype != IT_QUOTED_STRING &&  				curtoken->tokentype != IT_ATOM &&  				curtoken->tokentype != IT_NUMBER) -				return (-1); -			name=my_strdup(curtoken->tokenbuf); +			{ +				free(reference); +				writes(tag); +				writes(" BAD Invalid command\r\n"); +				return(0); +			} +			name=imap_foldername_to_filename(enabled_utf8, +							 curtoken->tokenbuf); + +			if (!name) +			{ +				free(reference); +				return -1; +			}  		}  		if (nexttoken()->tokentype != IT_EOL)	return (-1); @@ -4295,15 +4449,25 @@ int	uid=0;  	{  		struct	imaptoken *tok=nexttoken_nouc();  		struct maildir_info mi; +		char *mailbox;  		if (tok->tokentype != IT_NUMBER &&  			tok->tokentype != IT_ATOM &&  			tok->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0); +		} -		if (maildir_info_imap_find(&mi, tok->tokenbuf, +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    tok->tokenbuf); +		if (!mailbox || +		    maildir_info_imap_find(&mi, mailbox,  					   getenv("AUTHENTICATED")) < 0)  		{ +			if (mailbox) +				free(mailbox);  			writes(tag);  			writes(" NO Invalid mailbox name.\r\n");  			return (0); @@ -4319,19 +4483,21 @@ int	uid=0;  				maildir_info_destroy(&mi);  				writes(tag);  				accessdenied("APPEND", -					     tok->tokenbuf, +					     mailbox,  					     ACL_INSERT); +				free(mailbox);  				return 0;  			} -			rc=append(tag, tok->tokenbuf, p); +			rc=append(tag, mailbox, p);  			free(p);  			maildir_info_destroy(&mi); +			free(mailbox);  			return (rc);  		}  		else if (mi.mailbox_type == MAILBOXTYPE_OLDSHARED)  		{ -			char *p=strchr(tok->tokenbuf, '.'); +			char *p=strchr(mailbox, '.');  			if (p && (p=maildir_shareddir(".", p+1)) != NULL)  			{ @@ -4342,13 +4508,14 @@ int	uid=0;  				strcat(strcpy(q, p), "/shared");  				free(p); -				rc=append(tag, tok->tokenbuf, q); +				rc=append(tag, mailbox, q);  				free(q); +				free(mailbox);  				maildir_info_destroy(&mi);  				return rc;  			}  		} - +		free(mailbox);  		writes(tag);  		accessdenied("APPEND", "folder", ACL_INSERT);  		return (0); @@ -4358,17 +4525,27 @@ int	uid=0;  	{  		char	qroot[20];  		struct maildir_info minfo; - +		char *mailbox;  		curtoken=nexttoken_nouc();  		if (curtoken->tokentype != IT_NUMBER &&  			curtoken->tokentype != IT_ATOM &&  			curtoken->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0); +		} + +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); -		if (maildir_info_imap_find(&minfo, curtoken->tokenbuf, +		if (!mailbox || +		    maildir_info_imap_find(&minfo, mailbox,  					   getenv("AUTHENTICATED")))  		{ +			if (mailbox) +				free(mailbox);  			writes(tag);  			writes(" NO Invalid mailbox name.\r\n");  			return (0); @@ -4389,13 +4566,14 @@ int	uid=0;  		writes("*");  		writes(" QUOTAROOT \""); -		writeqs(curtoken->tokenbuf); +		writemailbox(mailbox);  		writes("\" \"");  		writes(qroot);  		writes("\"\r\n");  		quotainfo_out(qroot);  		writes(tag);  		writes(" OK GETQUOTAROOT Ok.\r\n"); +		free(mailbox);  		return(0);  	} @@ -4407,6 +4585,34 @@ int	uid=0;  		return(0);  	} +	if (strcmp(curtoken->tokenbuf, "ENABLE") == 0) +	{ +		while (nexttoken()->tokentype != IT_EOL) +		{ +			switch (curtoken->tokentype) { +			case IT_NUMBER: +			case IT_ATOM: +			case IT_QUOTED_STRING: +				if (strcmp(curtoken->tokenbuf, "UTF8=ACCEPT") +				    == 0) +				{ +					enabled_utf8=1; +				} +				continue; +			default: +				return -1; +			} +		} + +		writes("* ENABLED"); +		if (enabled_utf8) +			writes(" UTF8=ACCEPT"); +		writes("\r\n"); +		writes(tag); +		writes(" OK Options enabled\r\n"); +		return (0); +	} +  	if (strcmp(curtoken->tokenbuf, "GETQUOTA") == 0)  	{  		curtoken=nexttoken_nouc(); @@ -4442,7 +4648,14 @@ int	uid=0;  		if ( mailbox == 0)  			return (0); -		orig_mailbox=my_strdup(curtoken->tokenbuf); +		orig_mailbox=imap_foldername_to_filename(enabled_utf8, +							 curtoken->tokenbuf); + +		if (!orig_mailbox) +		{ +			free(mailbox); +			return -1; +		}  		curtoken=nexttoken();  		oneonly=0; @@ -4521,7 +4734,7 @@ int	uid=0;  		writes("*");  		writes(" STATUS \""); -		writeqs(orig_mailbox); +		writemailbox(orig_mailbox);  		writes("\" (");  		p="";  		if (get_messages) @@ -4599,7 +4812,11 @@ int	uid=0;  		if (curtoken->tokentype != IT_NUMBER &&  			curtoken->tokentype != IT_ATOM &&  			curtoken->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return 0; +		}  		isdummy=0; @@ -4610,13 +4827,20 @@ int	uid=0;  			isdummy=1;	/* Ignore hierarchy creation */  		} -		if (maildir_info_imap_find(&mi, curtoken->tokenbuf, +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); +		if (!mailbox) +			return -1; + +		if (maildir_info_imap_find(&mi, mailbox,  					   getenv("AUTHENTICATED")))  		{  			writes(tag);  			writes(" NO Invalid mailbox name.\r\n"); +			free(mailbox);  			return (0);  		} +		free(mailbox);  		if (!mi.homedir || !mi.maildir)  		{ @@ -4655,7 +4879,15 @@ int	uid=0;  		}  		if (isdummy)	*p=HIERCH; -		orig_mailbox=my_strdup(curtoken->tokenbuf); +		orig_mailbox=imap_foldername_to_filename(enabled_utf8, +							 curtoken->tokenbuf); + +		if (!orig_mailbox) +		{ +			free(mailbox); +			maildir_info_destroy(&mi); +			return (-1); +		}  		if (nexttoken()->tokentype != IT_EOL)  		{ @@ -4711,7 +4943,7 @@ int	uid=0;  		}  		writes(tag);  		writes(" OK \""); -		writeqs(orig_mailbox); +		writemailbox(orig_mailbox);  		writes("\" created.\r\n");  		/* @@ -4745,7 +4977,11 @@ int	uid=0;  		if (curtoken->tokentype != IT_NUMBER &&  			curtoken->tokentype != IT_ATOM &&  			curtoken->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0); +		}  		p=strrchr(curtoken->tokenbuf, HIERCH);  		if (p && p[1] == '\0')		/* Ignore hierarchy DELETE */ @@ -4757,14 +4993,18 @@ int	uid=0;  			return (0);  		} -		mailbox_name=my_strdup(curtoken->tokenbuf);  		mailbox=parse_mailbox_error(tag, curtoken, 1, 0);  		if ( mailbox == 0) +			return 0; + +		mailbox_name=imap_foldername_to_filename(enabled_utf8, +							 curtoken->tokenbuf); + +		if (!mailbox_name)  		{ -			free(mailbox_name); -			return (0); +			free(mailbox); +			return (-1);  		} -  		if (nexttoken()->tokentype != IT_EOL)  		{  			free(mailbox_name); @@ -4781,10 +5021,10 @@ int	uid=0;  			return (0);  		} -		if (strncmp(curtoken->tokenbuf, SHARED HIERCHS, -			sizeof(SHARED HIERCHS)-1) == 0) +		if (strncmp(mailbox_name, SHARED HIERCHS, +			    sizeof(SHARED HIERCHS)-1) == 0)  		{ -			maildir_shared_unsubscribe(0, curtoken->tokenbuf+ +			maildir_shared_unsubscribe(0, mailbox_name+  						   sizeof(SHARED HIERCHS)-1);  			free(mailbox_name);  			free(mailbox); @@ -4836,6 +5076,7 @@ int	uid=0;  		char *p;  		struct maildir_info mi1, mi2;  		const char *errmsg; +		char *mailbox;  		curtoken=nexttoken_nouc(); @@ -4844,16 +5085,23 @@ int	uid=0;  		    curtoken->tokentype != IT_QUOTED_STRING)  		{  			writes(tag); -			writes(" NO Invalid mailbox\r\n"); +			writes(" BAD Invalid command\r\n");  			return (0);  		}  		if ((p=strrchr(curtoken->tokenbuf, HIERCH))  && p[1] == 0)  			*p=0; -		if (maildir_info_imap_find(&mi1, curtoken->tokenbuf, +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); + +		if (!mailbox) +			return -1; + +		if (maildir_info_imap_find(&mi1, mailbox,  					   getenv("AUTHENTICATED")) < 0)  		{ +			free(mailbox);  			writes(tag);  			writes(" NO Invalid mailbox name.\r\n");  			return (0); @@ -4861,6 +5109,7 @@ int	uid=0;  		if (mi1.homedir == NULL || mi1.maildir == NULL)  		{ +			free(mailbox);  			maildir_info_destroy(&mi1);  			writes(tag);  			writes(" NO Invalid mailbox\r\n"); @@ -4868,11 +5117,12 @@ int	uid=0;  		}  		{ -			CHECK_RIGHTSM(curtoken->tokenbuf, +			CHECK_RIGHTSM(mailbox,  				      rename_rights, ACL_DELETEFOLDER);  			if (rename_rights[0] == 0)  			{ +				free(mailbox);  				maildir_info_destroy(&mi1);  				writes(tag);  				accessdenied("RENAME", curtoken->tokenbuf, @@ -4880,7 +5130,7 @@ int	uid=0;  				return (0);  			}  		} - +		free(mailbox);  		curtoken=nexttoken_nouc();  		if (curtoken->tokentype != IT_NUMBER && @@ -4888,7 +5138,9 @@ int	uid=0;  			curtoken->tokentype != IT_QUOTED_STRING)  		{  			maildir_info_destroy(&mi1); -			return (-1); +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0);  		}  		if ((p=strrchr(curtoken->tokenbuf, HIERCH)) && p[1] == 0) @@ -4896,22 +5148,33 @@ int	uid=0;  			*p=0;  		} +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); + +		if (!mailbox) +		{ +			maildir_info_destroy(&mi1); +			return -1; +		} -		if (maildir_info_imap_find(&mi2, curtoken->tokenbuf, +		if (maildir_info_imap_find(&mi2, mailbox,  					   getenv("AUTHENTICATED")) < 0)  		{ +			free(mailbox);  			maildir_info_destroy(&mi1);  			writes(tag);  			writes(" NO Invalid mailbox name.\r\n");  			return (0);  		} -		if (check_parent_create(tag, "RENAME", curtoken->tokenbuf)) +		if (check_parent_create(tag, "RENAME", mailbox))  		{ +			free(mailbox);  			maildir_info_destroy(&mi1);  			maildir_info_destroy(&mi2);  			return 0;  		} +		free(mailbox);  		if (nexttoken()->tokentype != IT_EOL)  		{ @@ -4970,7 +5233,7 @@ int	uid=0;  		if ( mailbox == 0)  			return (0); -		current_mailbox_acl=get_myrightson(curtoken->tokenbuf); +		current_mailbox_acl=get_myrightson_folder(curtoken->tokenbuf);  		if (current_mailbox_acl == NULL)  		{  			free(mailbox); @@ -5049,7 +5312,11 @@ int	uid=0;  		if (curtoken->tokentype != IT_NUMBER &&  			curtoken->tokentype != IT_ATOM &&  			curtoken->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0); +		}  		p=strrchr(curtoken->tokenbuf, HIERCH);  		if (p && p[1] == '\0')		/* Ignore hierarchy DELETE */ @@ -5061,7 +5328,10 @@ int	uid=0;  			return (0);  		} -		mailbox=my_strdup(curtoken->tokenbuf); +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); +		if (!mailbox) +			return -1;  		if (nexttoken()->tokentype != IT_EOL)  			return (-1); @@ -5126,7 +5396,11 @@ int	uid=0;  		if (curtoken->tokentype != IT_NUMBER &&  			curtoken->tokentype != IT_ATOM &&  			curtoken->tokentype != IT_QUOTED_STRING) -			return (-1); +		{ +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0); +		}  		p=strrchr(curtoken->tokenbuf, HIERCH);  		if (p && p[1] == '\0')		/* Ignore hierarchy DELETE */ @@ -5138,7 +5412,12 @@ int	uid=0;  			return (0);  		} -		mailbox=my_strdup(curtoken->tokenbuf); +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); + +		if (!mailbox) +			return -1; +  		if (nexttoken()->tokentype != IT_EOL)  			return (-1); @@ -5241,7 +5520,10 @@ int	uid=0;  			return 0;  		free(mailbox); -		mailbox=my_strdup(curtoken->tokenbuf); +		mailbox=imap_foldername_to_filename(enabled_utf8, +						    curtoken->tokenbuf); +		if (!mailbox) +			return -1;  		if (maildir_info_imap_find(&mi, mailbox,  					   getenv("AUTHENTICATED")) < 0) @@ -5355,6 +5637,7 @@ int	uid=0;  		maildir_aclt_list l;  		char *mailbox_owner;  		char *mb; +		char *f;  		curtoken=nexttoken_nouc(); @@ -5363,22 +5646,29 @@ int	uid=0;  			return 0;  		free(mb); +		f=imap_foldername_to_filename(enabled_utf8, curtoken->tokenbuf); + +		if (!f) +			return -1; +  		{ -			CHECK_RIGHTSM(curtoken->tokenbuf, +			CHECK_RIGHTSM(f,  				      acl_rights,  				      ACL_ADMINISTER);  			if (acl_rights[0] == 0)  			{  				writes(tag); -				accessdenied("GETACL", curtoken->tokenbuf, +				accessdenied("GETACL", f,  					     ACL_ADMINISTER); +				free(f);  				return 0;  			}  		} -		if (get_acllist(&l, curtoken->tokenbuf, +		if (get_acllist(&l, f,  				&mailbox_owner) < 0)  		{ +			free(f);  			writes(tag);  			writes(" NO Cannot retrieve ACLs for mailbox.\r\n");  			return 0; @@ -5386,13 +5676,14 @@ int	uid=0;  		free(mailbox_owner);  		writes("* ACL \""); -		writeqs(curtoken->tokenbuf); +		writemailbox(f);  		writes("\"");  		maildir_aclt_list_enum(&l, getacl_cb, NULL);  		writes("\r\n");  		writes(tag);  		writes(" OK GETACL completed.\r\n");  		maildir_aclt_list_destroy(&l); +		free(f);  		return 0;  	} @@ -5408,9 +5699,14 @@ int	uid=0;  		if (!mb)  			return 0;  		free(mb); +		mb=imap_foldername_to_filename(enabled_utf8, +					       curtoken->tokenbuf); + +		if (!mb) +			return -1;  		{ -			char *myrights=get_myrightson(curtoken->tokenbuf); +			char *myrights=get_myrightson(mb);  			if (!strchr(myrights, ACL_LOOKUP[0]) &&  			    !strchr(myrights, ACL_READ[0]) && @@ -5422,23 +5718,23 @@ int	uid=0;  			{  				free(myrights);  				writes(tag); -				accessdenied("GETACL", curtoken->tokenbuf, +				accessdenied("GETACL", mb,  					     ACL_ADMINISTER); +				free(mb);  				return 0;  			}  			free(myrights);  		} -		if (get_acllist(&l, curtoken->tokenbuf, +		if (get_acllist(&l, mb,  				&mailbox_owner) < 0)  		{ +			free(mb);  			writes(tag);  			writes(" NO Cannot retrieve ACLs for mailbox.\r\n");  			return 0;  		} -		mb=my_strdup(curtoken->tokenbuf); -  		switch ((curtoken=nexttoken_nouc())->tokentype) {  		case IT_QUOTED_STRING:  		case IT_ATOM: @@ -5452,7 +5748,7 @@ int	uid=0;  		}  		writes("* LISTRIGHTS \""); -		writeqs(mb); +		writemailbox(mb);  		writes("\" \"");  		writeqs(curtoken->tokenbuf);  		writes("\""); @@ -5510,6 +5806,7 @@ int	uid=0;  	if (strcmp(curtoken->tokenbuf, "MYRIGHTS") == 0)  	{  		char *mb; +		char *f;  		curtoken=nexttoken_nouc(); @@ -5518,8 +5815,12 @@ int	uid=0;  			return 0;  		free(mb); +		f=imap_foldername_to_filename(enabled_utf8, curtoken->tokenbuf); +		if (!f) +			return -1; +  		{ -			char *myrights=get_myrightson(curtoken->tokenbuf); +			char *myrights=get_myrightson(f);  			if (!strchr(myrights, ACL_LOOKUP[0]) &&  			    !strchr(myrights, ACL_READ[0]) && @@ -5531,25 +5832,28 @@ int	uid=0;  			{  				free(myrights);  				writes(tag); -				accessdenied("GETACL", curtoken->tokenbuf, +				accessdenied("GETACL", f,  					     ACL_ADMINISTER); +				free(f);  				return 0;  			}  			free(myrights);  		} -		mb=get_myrightson(curtoken->tokenbuf); +		mb=get_myrightson(f);  		if (!mb)  		{ +			free(f);  			writes(tag);  			writes(" NO Cannot retrieve ACLs for mailbox.\r\n");  			return 0;  		}  		writes("* MYRIGHTS \""); -		writeqs(curtoken->tokenbuf); +		writemailbox(f);  		writes("\" \""); +		free(f);  		writeacl1(mb);  		free(mb); @@ -5740,6 +6044,14 @@ int	uid=0;  		if (curtoken->tokentype == IT_ATOM &&  			strcmp(curtoken->tokenbuf, "CHARSET") == 0)  		{ +			if (enabled_utf8) +			{ +				writes(tag); +				writes(" NO CHARSET is not valid in UTF8 mode " +				       "as per RFC 6855\r\n"); +				return (0); +			} +  			curtoken=nexttoken();  			if (curtoken->tokentype != IT_ATOM &&  				curtoken->tokentype != IT_QUOTED_STRING) @@ -6089,7 +6401,9 @@ int	uid=0;  			curtoken->tokentype != IT_QUOTED_STRING)  		{  			free(msgset); -			return (-1); +			writes(tag); +			writes(" BAD Invalid command\r\n"); +			return (0);  		}  		mailbox=decode_valid_mailbox(curtoken->tokenbuf, 1); @@ -6117,7 +6431,11 @@ int	uid=0;  		}  		{ -			CHECK_RIGHTSM(curtoken->tokenbuf, +			char *f=imap_foldername_to_filename(enabled_utf8, +							    curtoken->tokenbuf); +			if (!f) +				return -1; +			CHECK_RIGHTSM(f,  				      append_rights,  				      ACL_INSERT ACL_DELETEMSGS  				      ACL_SEEN ACL_WRITE); @@ -6126,11 +6444,12 @@ int	uid=0;  			{  				writes(tag);  				accessdenied("COPY", -					     curtoken->tokenbuf, +					     f,  					     ACL_INSERT); +				free(f);  				return 0;  			} - +			free(f);  			strcpy(access_rights, append_rights);  		} diff --git a/imap/imapd.h b/imap/imapd.h index d7966a1..7325ec9 100644 --- a/imap/imapd.h +++ b/imap/imapd.h @@ -12,6 +12,7 @@  #define	NEWMSG_FLAG	'*'	/* Prefixed to mimeinfo to indicate new msg */ +extern int enabled_utf8;  #define	is_sharedsubdir(dir) \  	(strncmp((dir), SHAREDSUBDIR "/", \ diff --git a/imap/imaplogin.c b/imap/imaplogin.c index a445a35..8e2f9c9 100644 --- a/imap/imaplogin.c +++ b/imap/imaplogin.c @@ -37,7 +37,7 @@  #include	"tcpd/spipe.h"  #include	"numlib/numlib.h"  #include	"tcpd/tlsclient.h" - +#include	"imapd.h"  FILE *debugfile=0;  extern void initcapability(); @@ -47,6 +47,7 @@ extern int have_starttls();  extern int tlsrequired();  extern int authenticate(const char *, char *, int);  unsigned long header_count=0, body_count=0;	/* Dummies */ +int enabled_utf8=0;  extern unsigned long bytes_received_count; /* counter for received bytes (imaptoken.c) */  extern unsigned long bytes_sent_count; /* counter for sent bytes (imapwrite.c) */ diff --git a/imap/imaptoken.c b/imap/imaptoken.c index 7df8846..a11310a 100644 --- a/imap/imaptoken.c +++ b/imap/imaptoken.c @@ -1,5 +1,5 @@  /* -** Copyright 1998 - 2005 Double Precision, Inc. +** Copyright 1998 - 2018 Double Precision, Inc.  ** See COPYING for distribution information.  */ @@ -16,6 +16,8 @@  #include	<sys/types.h>  #include	<sys/time.h>  #include	"numlib/numlib.h" +#include	"imapd.h" +#include	<courier-unicode.h>  #if	HAVE_UNISTD_H  #include	<unistd.h>  #endif @@ -222,8 +224,59 @@ void smap_readline(char *buffer, size_t bufsize)  #endif +static int ignore_output_func(const char *ptr, size_t cnt, void *ignore) +{ +	return 0; +} + +static struct imaptoken *do_readtoken_nolog(int touc); +  static struct imaptoken *do_readtoken(int touc)  { +	struct imaptoken *tok=do_readtoken_nolog(touc); + +	if (debugfile) +	{ +		char	*p=0; + +		fprintf(debugfile, "READ: "); +		switch (tok->tokentype) { +		case IT_ATOM: +			p=curtoken.tokenbuf; fprintf(debugfile, "ATOM"); break; +		case IT_NUMBER: +			p=curtoken.tokenbuf; fprintf(debugfile, "NUMBER"); break; +		case IT_QUOTED_STRING: +			p=curtoken.tokenbuf; fprintf(debugfile, "QUOTED_STRING"); break; +		case IT_LPAREN: +			fprintf(debugfile, "LPAREN"); break; +		case IT_RPAREN: +			fprintf(debugfile, "RPAREN"); break; +		case IT_NIL: +			fprintf(debugfile, "NIL"); break; +		case IT_ERROR: +			fprintf(debugfile, "ERROR"); break; +		case IT_EOL: +			fprintf(debugfile, "EOL"); break; +		case IT_LBRACKET: +			fprintf(debugfile, "LBRACKET"); break; +		case IT_RBRACKET: +			fprintf(debugfile, "RBRACKET"); break; +		case IT_LITERAL_STRING_START: +			fprintf(debugfile, "{%lu}", tok->tokennum); break; +		case IT_LITERAL8_STRING_START: +			fprintf(debugfile, "~{%lu}", tok->tokennum); break; +		} + +		if (p) +			fprintf(debugfile, ": %s", p); +		fprintf(debugfile, "\n"); +		fflush(debugfile); +	} +	return tok; +} + +static struct imaptoken *do_readtoken_nolog(int touc) +{  int	c=0;  unsigned l; @@ -269,6 +322,8 @@ unsigned l;  	if (c == '"')  	{ +		int bit8=0; +  		l=0;  		while ((c=READ()) != '"')  		{ @@ -280,16 +335,62 @@ unsigned l;  				curtoken.tokentype=IT_ERROR;  				return (&curtoken);  			} -			if (l < 8192) +			if (l >= BUFSIZ)  			{ -				appendch(c); +				writes("* NO [ALERT] IMAP command too long.\r\n"); +				curtoken.tokentype=IT_ERROR;  			} +			if (c & 0x80) +				bit8=1; +			appendch(c);  		}  		appendch(0);  		curtoken.tokentype=IT_QUOTED_STRING; + +		/* +		** Strings must be valid UTF-8. Shortcut check. +		*/ + +		if (bit8) +		{ +			int errptr=0; + +			unicode_convert_handle_t h= +				unicode_convert_init("utf-8", +						     unicode_u_ucs4_native, +						     ignore_output_func, +						     (void *)0); + +			if (unicode_convert(h, curtoken.tokenbuf, +					    strlen(curtoken.tokenbuf))) +			{ +				curtoken.tokentype=IT_ERROR; +			} + +			if (unicode_convert_deinit(h, &errptr) || errptr) +			{ +				curtoken.tokentype=IT_ERROR; +			} +		}  		return (&curtoken);  	} +	/* +	** Parse LITERAL or LITERAL8 +	*/ +	curtoken.tokentype=IT_LITERAL_STRING_START; + +	if (c == '~') +	{ +		c=READ(); +		if (c != '{') +		{ +			curtoken.tokentype=IT_ERROR; +			return (&curtoken); +		} +		curtoken.tokentype=IT_LITERAL8_STRING_START; +	} +  	if (c == '{')  	{  		curtoken.tokennum=0; @@ -313,7 +414,6 @@ unsigned l;  			curtoken.tokentype=IT_ERROR;  			return (&curtoken);  		} -		curtoken.tokentype=IT_LITERAL_STRING_START;  		return (&curtoken);  	} @@ -339,9 +439,11 @@ unsigned l;  	}  	while (c != '\r' && c != '\n' -		&& !isspace((int)(unsigned char)c) -		&& c != '\\' && c != '"' && c != LPAREN_CHAR && c != RPAREN_CHAR -		&& c != '{' && c != '}' && c != LBRACKET_CHAR && c != RBRACKET_CHAR) +	       && !isspace((int)(unsigned char)c) +	       && (((unsigned char)c) & 0x80) == 0 +	       && c != '\\' && c != '"' && c != LPAREN_CHAR && c != RPAREN_CHAR +	       && c != '~' +	       && c != '{' && c != '}' && c != LBRACKET_CHAR && c != RBRACKET_CHAR)  	{  		curtoken.tokentype=IT_ATOM;  		if (l < IT_MAX_ATOM_SIZE) @@ -352,7 +454,7 @@ unsigned l;  		}  		else  		{ -			write_error_exit("max atom size too small");   +			write_error_exit("max atom size too small");  		}  		c=READ();  	} @@ -371,65 +473,52 @@ unsigned l;  static struct imaptoken *readtoken(int touc)  { -struct imaptoken *tok=do_readtoken(touc); +	struct imaptoken *tok=do_readtoken(touc); -	if (tok->tokentype == IT_LITERAL_STRING_START) -	{ -	unsigned long nbytes=curtoken.tokennum; +	convert_literal_tokens(tok); +	return (tok); +} -		if (nbytes > 8192) -		{ -			writes("* NO [ALERT] IMAP command too long.\r\n"); -			tok->tokentype=IT_ERROR; -		} -		else -		{ -		unsigned long i; +/* +** If a token is a LITERAL, read it and replace it with a QUOTED_STRING. +*/ -			writes("+ OK\r\n"); -			writeflush(); -			alloc_tokenbuf(nbytes+1); -			for (i=0; i<nbytes; i++) -				tok->tokenbuf[i]= READ(); -			tok->tokenbuf[i]=0; -			tok->tokentype=IT_QUOTED_STRING; -		} -	} +void convert_literal_tokens(struct imaptoken *tok) +{ +	unsigned long nbytes; -	if (debugfile) +	if (tok->tokentype != IT_LITERAL_STRING_START && +	    tok->tokentype != IT_LITERAL8_STRING_START) +		return; + +	nbytes=curtoken.tokennum; + +	if (nbytes > BUFSIZ) +	{ +		writes("* NO [ALERT] IMAP command too long.\r\n"); +		tok->tokentype=IT_ERROR; +	} +	else  	{ -	char	*p=0; +		unsigned long i; -		fprintf(debugfile, "READ: "); -		switch (tok->tokentype) { -		case IT_ATOM: -			p=curtoken.tokenbuf; fprintf(debugfile, "ATOM"); break; -		case IT_NUMBER: -			p=curtoken.tokenbuf; fprintf(debugfile, "NUMBER"); break; -		case IT_QUOTED_STRING: -			p=curtoken.tokenbuf; fprintf(debugfile, "QUOTED_STRING"); break; -		case IT_LPAREN: -			fprintf(debugfile, "LPAREN"); break; -		case IT_RPAREN: -			fprintf(debugfile, "RPAREN"); break; -		case IT_NIL: -			fprintf(debugfile, "NIL"); break; -		case IT_ERROR: -			fprintf(debugfile, "ERROR"); break; -		case IT_EOL: -			fprintf(debugfile, "EOL"); break; -		case IT_LBRACKET: -			fprintf(debugfile, "LBRACKET"); break; -		case IT_RBRACKET: -			fprintf(debugfile, "RBRACKET"); break; -		} +		/* -		if (p) -			fprintf(debugfile, ": %s", p); -		fprintf(debugfile, "\n"); -		fflush(debugfile); +		  RFC4466: In addition, the non-terminal "literal8" defined +		  in [BINARY] got extended to allow for non-synchronizing +		  literals if both [BINARY] and [LITERAL+] extensions are +		  supported by the server. + +		*/ + +		writes("+ OK\r\n"); +		writeflush(); +		alloc_tokenbuf(nbytes+1); +		for (i=0; i<nbytes; i++) +			tok->tokenbuf[i]= READ(); +		tok->tokenbuf[i]=0; +		tok->tokentype=IT_QUOTED_STRING;  	} -	return (tok);  }  struct imaptoken *nexttoken(void) @@ -564,4 +653,3 @@ int ismsgset_str(const char *p)  	if (*p)	return (0);  	return (1);  } -		 diff --git a/imap/imaptoken.h b/imap/imaptoken.h index 65f8119..0fdafc9 100644 --- a/imap/imaptoken.h +++ b/imap/imaptoken.h @@ -29,6 +29,7 @@ struct imaptoken {  #define	IT_EOL			8  #define	IT_LBRACKET		9  #define	IT_RBRACKET		10 +#define	IT_LITERAL8_STRING_START 11  struct imaptoken *nexttoken(void);  struct imaptoken *currenttoken(void); @@ -36,6 +37,7 @@ struct imaptoken *nexttoken_nouc(void);  struct imaptoken *nexttoken_noparseliteral(void);  struct imaptoken *nexttoken_okbracket(void);  struct imaptoken *nexttoken_nouc_okbracket(void); +void convert_literal_tokens(struct imaptoken *tok);  int ismsgset(struct imaptoken *);  	/* See if this token is a syntactically valid message set */ diff --git a/imap/imapwrite.h b/imap/imapwrite.h index 6fa677c..97ec90c 100644 --- a/imap/imapwrite.h +++ b/imap/imapwrite.h @@ -2,7 +2,7 @@  #define	imapwrite_h  /* -** Copyright 1998 - 1999 Double Precision, Inc. +** Copyright 1998 - 2018 Double Precision, Inc.  ** See COPYING for distribution information.  */ @@ -11,6 +11,7 @@ void writeflush();  void writemem(const char *, size_t);  void writes(const char *);  void writeqs(const char *); +void writemailbox(const char *);  void writen(unsigned long n);  void write_error_exit(const char *);  #endif diff --git a/imap/msgenvelope.c b/imap/msgenvelope.c index 60564df..c454a1d 100644 --- a/imap/msgenvelope.c +++ b/imap/msgenvelope.c @@ -14,7 +14,7 @@  #include	<ctype.h>  #include	<stdlib.h>  #include	<string.h> - +#include	"imapd.h"  #define	MAX_HEADER_SIZE	8192 @@ -62,7 +62,8 @@ void msgappends(void (*writefunc)(const char *, size_t),  	char *q=0;  	for (i=0; i<l; i++) -		if (s[i] & 0x80)	/* Illegal 8-bit header content */ +		if (!enabled_utf8 && +		    (s[i] & 0x80))	/* Illegal 8-bit header content */  		{  			char *p=malloc(l+1); diff --git a/imap/smap.c b/imap/smap.c index 2b528a9..6f43a6e 100644 --- a/imap/smap.c +++ b/imap/smap.c @@ -354,7 +354,7 @@ static void list_callback(const char *f, void *vp)  	struct list_callback_utf8 *utf8=(struct list_callback_utf8 *)vp;  	maildir_aclt_list l; -	char **fn=maildir_smapfn_fromutf7(f); +	char **fn=maildir_smapfn_fromutf8(f);  	if (!fn)  	{ @@ -3215,6 +3215,7 @@ void smap()  	char rights_buf[40]; +	enabled_utf8=1;  	imapscan_init(¤t_maildir_info);  	memset(&add_flags, 0, sizeof(add_flags)); diff --git a/maildir/Makefile.am b/maildir/Makefile.am index 148c75c..60f0681 100644 --- a/maildir/Makefile.am +++ b/maildir/Makefile.am @@ -34,7 +34,7 @@ libmaildir_la_SOURCES=autoresponse.c autoresponse.h \  	maildirfilter.h maildirfiltertypelist.h\  	maildirflags.c maildirmkdir.c \  	maildirgetquota.c maildirgetquota.h \ -	maildirinfo.c maildirinfo.h \ +	maildirinfo.c maildirinfo.h maildirinfo2.c \  	maildirkeywords.c maildirkeywords2.c maildirkeywords3.c \  	maildirkeywords4.cpp \  	maildirkeywords.h maildirlist.c maildirlock.c \ @@ -63,7 +63,7 @@ maildirmake_DEPENDENCIES=libmaildir.la \  			../rfc822/librfc822.la  maildirmake_LDADD=libmaildir.la \  			../numlib/libnumlib.la \ -			../rfc822/librfc822.la -lcourier-unicode +			../rfc822/librfc822.la -lcourier-unicode @LIBPCRE@  maildirmake_LDFLAGS=-static  testmaildirfilter_SOURCES=maildirfiltertypelist.h testmaildirfilter.c diff --git a/maildir/maildirfilter.c b/maildir/maildirfilter.c index a04ba0d..82702a1 100644 --- a/maildir/maildirfilter.c +++ b/maildir/maildirfilter.c @@ -175,7 +175,7 @@ static int maildir_filter_ruleupdate_utf8(struct maildirfilter *r,  	/* rule name: may not already exist */  	*errcode=MF_ERR_EXISTS; -	 +  	for (pom=r->first; pom->next; pom=pom->next) {  	    if (p!=pom && !strcmp(name, pom->rulename_utf8))  		return (-1); @@ -508,8 +508,7 @@ static void print_pattern(FILE *f, int flags, const char *v)  }  int maildir_filter_saverules(struct maildirfilter *r, const char *filename, -		 const char *maildir, -		 const char *maildirpath, const char *fromaddr) +			     const char *maildirpath, const char *fromaddr)  {  FILE	*f=fopen(filename, "w");  struct maildirfilterrule *p; diff --git a/maildir/maildirfilter.h b/maildir/maildirfilter.h index 3d4bfdf..44a32de 100644 --- a/maildir/maildirfilter.h +++ b/maildir/maildirfilter.h @@ -113,7 +113,6 @@ int maildir_filter_ruleupdate(struct maildirfilter *, struct maildirfilterrule *  int maildir_filter_saverules(struct maildirfilter *,  		 const char *,		/* Filename */ -		 const char *,		/* Maildir */  		 const char *,		/* Path to maildir from mailfilter */  		 const char *);		/* The return address */ diff --git a/maildir/maildirfilter2.c b/maildir/maildirfilter2.c index 3a541d3..922ad13 100644 --- a/maildir/maildirfilter2.c +++ b/maildir/maildirfilter2.c @@ -213,7 +213,7 @@ int maildir_filter_savemaildirfilter(struct maildirfilter *mf, const char *maild  	strcat(strcpy(createInfo.newname, maildir), "/maildirfilter.tmp");  	rc=maildir_filter_saverules(mf, createInfo.tmpname, -				    maildir, maildirpath, from); +				    maildirpath, from);  	if (rc == 0 && rename(createInfo.tmpname, createInfo.newname))  		rc= -1;  	maildir_tmpcreate_free(&createInfo); diff --git a/maildir/maildirinfo.c b/maildir/maildirinfo.c index 95f3837..5db7f89 100644 --- a/maildir/maildirinfo.c +++ b/maildir/maildirinfo.c @@ -418,12 +418,12 @@ int maildir_info_imap_find(struct maildir_info *info, const char *path,  /***************************************************************************/  /* -** Maildir folders are named in IMAP-compatible modified-UTF7 encoding, +** Maildir folders are named in IMAP-compatible modified-UTF8 encoding,  ** with periods as hierarchy delimiters.  One exception: ".", "/", "~", and -** ":" are also encoded using modified-UTF7, making folder names that contain +** ":" are also encoded using modified-UTF8, making folder names that contain  ** those characters incompatible with IMAP.  ** -** smaptoUtf7 crates a modified-UTF7-encoded folder name from a vector +** smap_to_utf8 crates a modified-UTF8-encoded folder name from a vector  ** of UTF-8 words.  **  ** input:  "INBOX" "a" "b" @@ -431,7 +431,7 @@ int maildir_info_imap_find(struct maildir_info *info, const char *path,  **  */ -static char *smaptoUtf7(char **ptr) +static char *smap_to_utf8(char **ptr)  {  	char *f=NULL;  	char *n; @@ -439,8 +439,8 @@ static char *smaptoUtf7(char **ptr)  	while ((n=*ptr++) != NULL && *n)  	{  		char *p=unicode_convert_tobuf(n, "utf-8", -						unicode_x_imap_modutf7 " ./~:", -						NULL); +					      unicode_x_smap_modutf8, +					      NULL);  		if (!p)  		{ @@ -471,15 +471,13 @@ static char *smaptoUtf7(char **ptr)  }  /* -** Legacy IMAP creates maildir folders using modified-UTF7. -** -** Convert modified-UTF7 folder name into an array of UTF-8 words, that +** Convert modified-UTF8 folder name into an array of UTF-8 words, that  ** represent a folder name.  */ -char **maildir_smapfn_fromutf7(const char *modutf7) +char **maildir_smapfn_fromutf8(const char *modutf8)  { -	char *p=strdup(modutf7), *q; +	char *p=strdup(modutf8), *q;  	size_t n, i;  	char **fn; @@ -513,8 +511,8 @@ char **maildir_smapfn_fromutf7(const char *modutf7)  			}  		fn[n]=unicode_convert_tobuf(q, -					      unicode_x_imap_modutf7 " ./~:", -					      "utf-8", NULL); +					    unicode_x_smap_modutf8, +					    "utf-8", NULL);  		q += i;  		if (!fn[n]) @@ -558,7 +556,7 @@ static void get_existing_callback(const char *f, void *vp)  	if (gefi->pathname)  		return; -	fn=maildir_smapfn_fromutf7(f); +	fn=maildir_smapfn_fromutf8(f);  	if (!fn)  	{  		perror(f); @@ -601,7 +599,7 @@ static char *smap_path(const char *homedir,  	char *n, *p;  	struct stat stat_buf; -	if ((n=smaptoUtf7(words)) == NULL) +	if ((n=smap_to_utf8(words)) == NULL)  		return NULL;  	p=maildir_name2dir(homedir, n); @@ -935,4 +933,3 @@ static size_t munge_complex(const char *orig, char *n)  	if (n) *n=0;  	return cnt+1;  } - diff --git a/maildir/maildirinfo.h b/maildir/maildirinfo.h index 85eca68..b8f4d47 100644 --- a/maildir/maildirinfo.h +++ b/maildir/maildirinfo.h @@ -20,6 +20,10 @@ struct maildir_info {  	char *owner;  }; +char *imap_foldername_to_filename(int utf8_format, const char *foldername); + +char *imap_filename_to_foldername(int utf8_format, const char *filename); +  void maildir_info_destroy(struct maildir_info *); /* Deallocate memory */  int maildir_info_imap_find(struct maildir_info *info, const char *path, @@ -60,7 +64,7 @@ extern int maildir_info_suppress(const char *maildir);  int maildir_info_smap_find(struct maildir_info *info, char **folder,  			   const char *myid); -char **maildir_smapfn_fromutf7(const char *modutf7); +char **maildir_smapfn_fromutf8(const char *modutf8);  void maildir_smapfn_free(char **fn);  /* diff --git a/maildir/maildirinfo2.c b/maildir/maildirinfo2.c new file mode 100644 index 0000000..fcae14c --- /dev/null +++ b/maildir/maildirinfo2.c @@ -0,0 +1,148 @@ +/* +** Copyright 2018 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<stdio.h> +#include	<stdlib.h> +#include	<string.h> +#include	<errno.h> +#include	<ctype.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif + +#include	"maildirinfo.h" +#include	<courier-unicode.h> + + +/* Count the size of the converted filename or foldername string */ +static void count_utf8(const char *str, size_t n, void *arg) +{ +	*(size_t *)arg += n; +} + +/* Save the converted filename or foldername string */ +static void save_utf8(const char *str, size_t n, void *arg) +{ +	char **p=(char **)arg; + +	memcpy( (*p), str, n); +	*p += n; +} + +/* +** Split a string at periods, convert each split range between charsets. +*/ +static int foldername_filename_convert(const char *src_chset, +				       const char *dst_chset, +				       const char *foldername, +				       void (*closure)(const char *, size_t, +						       void *), +				       void *arg) +{ +	unicode_convert_handle_t h; +	char *cbufptr; +	size_t cbufsize; +	size_t l; +	int conversion_error; + +	while (*foldername) +	{ +		if (*foldername == '.') +		{ +			(*closure)(foldername, 1, arg); +			++foldername; +			continue; +		} + +		h=unicode_convert_tocbuf_init(src_chset, +					      dst_chset, +					      &cbufptr, +					      &cbufsize, +					      0); +		if (!h) +		{ +			errno=EINVAL; +			return -1; +		} + +		for (l=0; foldername[l] && foldername[l] != '.'; ++l) +			; + +		unicode_convert(h, foldername, l); + +		if (unicode_convert_deinit(h, &conversion_error) == 0) +		{ +			closure(cbufptr, cbufsize, arg); +			free(cbufptr); +			if (conversion_error) +			{ +				errno=EILSEQ; +				return -1; +			} +		} +		else +			return -1; +		foldername += l; +	} +	return 0; +} + +/* Convert either a foldername or a filename to the other */ + +static char *foldername_filename_convert_tobuf(const char *src_chset, +					       const char *dst_chset, +					       const char *foldername) +{ +	char *utf8, *p; +	size_t l=1; + +	if (foldername_filename_convert(src_chset, +					dst_chset, +					foldername, count_utf8, &l)) +		return NULL; + +	utf8=malloc(l+1); + +	if (!utf8) +		return NULL; + +	p=utf8; +	if (foldername_filename_convert(src_chset, +					dst_chset, +					foldername, save_utf8, &p)) +	{ +		free(utf8); +		return NULL; +	} + +	*p=0; + +	return utf8; +} + +char *imap_foldername_to_filename(int utf8_format, const char *foldername) +{ +	if (utf8_format) +		return strdup(foldername); + +	return foldername_filename_convert_tobuf +		(unicode_x_imap_modutf7, +		 "utf-8", +		 foldername); +} + +char *imap_filename_to_foldername(int utf8_format, const char *filename) +{ +	if (utf8_format) +		return strdup(filename); + +	return foldername_filename_convert_tobuf +		("utf-8", +		 unicode_x_imap_modutf7, +		 filename); +} diff --git a/maildir/maildirmake.c b/maildir/maildirmake.c index 9e14eb4..18e3da5 100644 --- a/maildir/maildirmake.c +++ b/maildir/maildirmake.c @@ -1,5 +1,5 @@  /* -** Copyright 1998 - 2006Double Precision, Inc. +** Copyright 1998 - 2018 Double Precision, Inc.  ** See COPYING for distribution information.  */ @@ -25,8 +25,10 @@  #include	"maildircreate.h"  #include	"maildirmisc.h" +#include	"maildirinfo.h"  #include	"maildirsharedrc.h"  #include	"maildirquota.h" +#include	"maildirfilter.h"  #include	<courier-unicode.h>  static void usage() @@ -194,6 +196,421 @@ int	found;  	exit(0);  } +/***************************************************************************** + +Convert modified-UTF7 folder names to UTF-8 (sort-of). + +*****************************************************************************/ + +struct convertutf8_list { +	struct convertutf8_list *next; +	char *rename_from; +	char *rename_to; +}; + +struct convertutf8_status { +	struct convertutf8_list *list; +	int status; +}; + +/* Find folders that need to change */ + +static void convertutf8_build_list(const char *inbox_name, +				   void *arg) +{ +	struct convertutf8_status *status= +		(struct convertutf8_status *)arg; +	struct convertutf8_list *list; + +	char *converted=imap_foldername_to_filename(0, inbox_name); + +	if (!converted) +	{ +		fprintf(stderr, +			"Error: %s: does not appear to be valid" +			" modified-UTF7\n", +			inbox_name); +		status->status=1; +		return; +	} + +	if (strcmp(converted, inbox_name) == 0) +	{ +		free(converted); +		return; +	} +	list=(struct convertutf8_list *)malloc(sizeof(struct convertutf8_list)); + +	if (!list || !(list->rename_from=strdup(inbox_name))) +	{ +		perror("malloc"); +		exit(1); +	} + +	list->rename_to=converted; +	list->next=status->list; +	status->list=list; +} + +void convertutf8(const char *maildir, const char *mailfilter, int doit) +{ +	struct convertutf8_status status; +	struct convertutf8_list *list; +	char *courierimapsubscribed; +	char *courierimapsubscribed_new; +	FILE *courierimapsubscribed_fp; +	struct maildirfilter mf; +	int mf_status; +	char *mailfilter_newname=0; + +	memset(&status, 0, sizeof(status)); +	memset(&mf, 0, sizeof(mf)); + +	printf("Checking %s:\n", maildir); + +	maildir_list(maildir, convertutf8_build_list, &status); + +	if (status.status) +		exit(status.status); + +	if (mailfilter) +	{ +		struct maildirfilterrule *r; + +		/* +		** Try to convert folder references from mailfilter +		*/ + +		mailfilter_newname=malloc(strlen(mailfilter)+10); + +		strcat(strcpy(mailfilter_newname, mailfilter), ".new"); +		mf_status=maildir_filter_loadrules(&mf, mailfilter); + +		if (mf_status != MF_LOADOK && mf_status != MF_LOADNOTFOUND) +		{ +			fprintf(stderr, "Error: cannot load %s\n", +				mailfilter); +		} + +		for (r=mf.first; r; r=r->next) +		{ +			char *converted; + +			/* Look for deliveries to a folder */ + +			if (strncmp(r->tofolder, "INBOX.", 6)) +				continue; + +			converted=imap_foldername_to_filename(0, r->tofolder); + +			if (!converted) +			{ +				fprintf(stderr, "Error: %s: " +					"%s: does not appear to be valid " +					"modified-UTF7\n", +					mailfilter, +					r->tofolder); +				status.status=1; +			} + +			if (strcmp(converted, r->tofolder) == 0) +			{ +				free(converted); +				continue; +			} + +			printf("Mail filter to %s updated to %s\n", +			       r->tofolder, converted); + +			free(r->tofolder); +			r->tofolder=converted; +		} + +		if (mf_status == MF_LOADOK) +		{ +			FILE *fp=fopen(mailfilter, "r"); +			char detected_from[1024]; +			char detected_tomaildir[1024]; +			char buffer[1024]; +			struct stat st_buf; + +			/* +			** We need to know the FROM address and the MAILDIR +			** reference. Attempt to ham-fistedly parse the +			** current filter file. +			*/ + +			detected_from[0]=0; +			detected_tomaildir[0]=0; + +			if (!fp) +			{ +				perror(mailfilter); +				exit(1); +			} + +			while (fgets(buffer, sizeof(buffer), fp)) +			{ +				char *p, *q; + +				if (strncmp(buffer, "FROM='", 6) == 0) +				{ +					char *p=strchr(buffer+6, '\''); + +					if (!p) +					{ +						fprintf(stderr, +							"Cannot parse %s\n", +							mailfilter); +						status.status=1; +					} +					else +					{ +						*p=0; +						strcpy(detected_from, buffer+6); + +						/* +						  Unescape, because saverules() +						  escapes it. +						*/ + +						for (q=p=detected_from; *p; p++) +						{ +							if (*p == '\\' && p[1]) +								++p; +							*q=*p; +							++q; +						} +						*q=0; +					} +				} + +				if (strncmp(buffer, "to \"", 4) == 0) +				{ +					p=strstr(buffer+4, "/.\""); + +					if (!p) +					{ +						fprintf(stderr, +							"Cannot parse %s\n", +							mailfilter); +						status.status=1; +					} +					else +					{ +						*p=0; +					} +					strcpy(detected_tomaildir, buffer+4); +				} +			} +			fclose(fp); +			if (detected_from[0] == 0 || +			    detected_tomaildir[0] == 0) +			{ +				fprintf(stderr, +					"Failed to parse %s\n", +					mailfilter); +				status.status=1; +			} +			maildir_filter_saverules(&mf, mailfilter_newname, +						 detected_tomaildir, +						 detected_from); + +			if (stat(mailfilter, &st_buf) || +			    chmod(mailfilter_newname, st_buf.st_mode)) +			{ +				perror(mailfilter); +				exit(1); +			} +			/* +			** If we're root, preserve the ownership and permission +			*/ +			if (geteuid() == 0) +			{ +				if (chown(mailfilter_newname, st_buf.st_uid, +					  st_buf.st_gid)) +				{ +					perror(mailfilter_newname); +					exit(1); +				} +			} +		} +	} +	else +	{ +		mf_status=MF_LOADNOTFOUND; +	} + +	courierimapsubscribed=malloc(strlen(maildir)+100); +	courierimapsubscribed_new=malloc(strlen(maildir)+100); + +	strcat(strcpy(courierimapsubscribed, maildir), +	       "/courierimapsubscribed"); +	strcat(strcpy(courierimapsubscribed_new, maildir), +	       "/courierimapsubscribed.new"); + +	/* +	** Update folder references in the IMAP subscription file. +	*/ + +	if ((courierimapsubscribed_fp=fopen(courierimapsubscribed, "r")) +	    != NULL) +	{ +		char buffer[2048]; +		FILE *new_fp=fopen(courierimapsubscribed_new, "w"); +		char *converted; +		struct stat st_buf; + +		while (fgets(buffer, sizeof(buffer), courierimapsubscribed_fp)) +		{ +			char *p=strchr(buffer, '\n'); + +			if (!p) +			{ +				fprintf(stderr, "Error: courierimapsubscribed: " +					"folder name too long\n"); +				status.status=1; +				continue; +			} + +			*p=0; + +			converted=imap_foldername_to_filename(0, buffer); + +			if (!converted) +			{ +				fprintf(stderr, "Error: courierimapsubscribed: " +					"%s: does not appear to be valid " +					"modified-UTF7\n", +					buffer); +				status.status=1; +				continue; +			} +			fprintf(new_fp, "%s\n", converted); + +			if (strcmp(buffer, converted)) +			{ +				printf("Subscription to %s changed to %s\n", +				       buffer, converted); +			} +			free(converted); +		} +		if (fflush(new_fp) || fclose(new_fp)) +		{ +			exit(1); +		} +		fclose(courierimapsubscribed_fp); + +		/* +		** If we're root, preserve the ownership and permission +		*/ +		if (stat(courierimapsubscribed, &st_buf) || +		    chmod(courierimapsubscribed_new, st_buf.st_mode)) +		{ +			perror(courierimapsubscribed); +			exit(1); +		} + +		if (geteuid() == 0) +		{ +			if (chown(courierimapsubscribed_new, st_buf.st_uid, +				  st_buf.st_gid)) +			{ +				perror(courierimapsubscribed_new); +				exit(1); +			} +		} +	} + +	if (status.status) +	{ +		unlink(courierimapsubscribed_new); +		if (mf_status == MF_LOADOK) +			unlink(mailfilter_newname); +		exit(status.status); +	} +	for (list=status.list; list; list=list->next) +	{ +		char *frompath, *topath; + +		printf("Rename %s to %s\n", list->rename_from, list->rename_to); + +		frompath=malloc(strlen(maildir)+strlen(list->rename_from)); +		topath=malloc(strlen(maildir)+strlen(list->rename_to)); + +		if (!frompath || !topath) +		{ +			perror("malloc"); +			exit(1); +		} + +		strcat(strcpy(frompath, maildir), "/"); +		strcat(strcpy(topath, maildir), "/"); + +		/* They all have the INBOX. prefix, strip it off */ +		strcat(frompath, strchr(list->rename_from, '.')); +		strcat(topath, strchr(list->rename_to, '.')); + +		if (doit) +		{ +			if (rename(frompath, topath)) +			{ +				fprintf(stderr, +					"FATAL ERROR RENAMING %s to %s: %s\n", +					frompath, topath, strerror(errno)); +				status.status=1; +			} +		} +		free(frompath); +		free(topath); +	} + +	if (doit) +	{ +		if (courierimapsubscribed_fp) +		{ +			printf("Updating %s\n", courierimapsubscribed); + +			if (rename(courierimapsubscribed_new, +				   courierimapsubscribed)) +			{ +				fprintf(stderr, +					"FATAL ERROR RENAMING %s to %s: %s\n", +					courierimapsubscribed_new, +					courierimapsubscribed, strerror(errno)); +				status.status=1; +			} +		} + +		if (mf_status == MF_LOADOK) +		{ +			printf("Updating %s\n", mailfilter); + +			if (rename(mailfilter_newname, mailfilter)) +			{ +				fprintf(stderr, +					"FATAL ERROR RENAMING %s to %s: %s\n", +					mailfilter_newname, mailfilter, +					strerror(errno)); +				status.status=1; +			} +		} +	} +	else +	{ +		if (courierimapsubscribed_fp) +		{ +			printf("Verified %s\n", courierimapsubscribed); +		} + +		if (mf_status == MF_LOADOK) +		{ +			printf("Verified %s\n", mailfilter); +		} +	} +	exit(status.status); +} +  int main(int argc, char *argv[])  {  const char *maildir, *folder=0; @@ -228,7 +645,7 @@ char	*tmp=0;  			folder=unicode_convert_tobuf(p,  						       unicode_default_chset(), -						       unicode_x_imap_modutf7, +						       unicode_x_smap_modutf8,  						       &converr);  			if (converr || !folder) @@ -297,6 +714,21 @@ char	*tmp=0;  			continue;  		} +		if (strcmp(argv[argn], "--convutf8") == 0 && argc-argn > 1) +		{ +			char *maildir=argv[++argn]; +			char *mailfilter=argn < argc ? argv[++argn]:0; +			convertutf8(maildir, mailfilter, 1); +			exit(0); +		} + +		if (strcmp(argv[argn], "--checkutf8") == 0 && argc-argn > 1) +		{ +			char *maildir=argv[++argn]; +			char *mailfilter=argn < argc ? argv[++argn]:0; +			convertutf8(maildir, mailfilter, 0); +			exit(0); +		}  		usage();  	} diff --git a/maildir/maildirmake.sgml b/maildir/maildirmake.sgml index b1a7d6d..6ad9aaa 100644 --- a/maildir/maildirmake.sgml +++ b/maildir/maildirmake.sgml @@ -1,5 +1,5 @@  <!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> -<!-- Copyright 1998 - 2009 Double Precision, Inc.  See COPYING for --> +<!-- Copyright 1998 - 2018 Double Precision, Inc.  See COPYING for -->  <!-- distribution information. -->  <refentry>    <info><author><firstname>Sam</firstname><surname>Varshavchik</surname><contrib>Author</contrib></author><productname>Courier Mail Server</productname></info> @@ -28,7 +28,7 @@      <para>  The <command>maildirmake</command> command creates maildirs, and -maildir folders. +maildir folders and performs some routine maintenance on them.  This documentation  describes the <command>maildirmake</command> command from the  <application>Courier</application> mail server, @@ -46,9 +46,7 @@ slightly different permissions which allows creation of publicly-shared  folders.</simpara>  	</listitem>        </varlistentry> -    </variablelist> -    <variablelist>        <varlistentry><term><literal>-q</literal> <replaceable>quota</replaceable></term>          <listitem><simpara>install a quota on the maildir.  	    See @@ -59,30 +57,24 @@ folders.</simpara>  	  </simpara>  	</listitem>        </varlistentry> -    </variablelist> -    <variablelist>        <varlistentry><term><literal>-f</literal> <replaceable>folder</replaceable></term>          <listitem><simpara>do not create a maildir, but create a folder in an  existing maildir.</simpara>  	</listitem>        </varlistentry> -    </variablelist> -    <variablelist>        <varlistentry><term><literal>-F</literal> <replaceable>folder</replaceable></term>          <listitem><simpara>Like the <literal>-f</literal> option, except  	    that the folder's name is given using the system locale's  	    character set. Non-Latin characters in the folder's name  	    must be given to the <literal>-f</literal> option using IMAP's -	    modified-UTF7 encoding. The <literal>-F</literal> option +	    UTF8 encoding. The <literal>-F</literal> option  	    takes the folder name specified using the console's character -	    set..</simpara> +	    set.</simpara>  	</listitem>        </varlistentry> -    </variablelist> -    <variablelist>        <varlistentry><term><literal>-s</literal> <replaceable>mode</replaceable></term>          <listitem><simpara>create a publicly accessible folder in an  existing sharable maildir.  First, use the <option>-S</option> option to @@ -100,9 +92,7 @@ your own system group to access messages in this folder (instead of  everyone).</simpara>  	</listitem>        </varlistentry> -    </variablelist> -    <variablelist>        <varlistentry><term><literal>--add</literal> <replaceable>name</replaceable>=<replaceable>pathname</replaceable>,  <literal>--del</literal> <replaceable>name</replaceable></term>          <listitem><simpara> @@ -110,6 +100,29 @@ create or delete the directories and links needed to  access shared folders.  See below for more information.</simpara>  	</listitem>        </varlistentry> +      <varlistentry> +	<term><literal>--checkutf8</literal> <replaceable>maildir</replaceable> <replaceable>maildirfilter</replaceable></term> +	<listitem> +	  <simpara> +	    Perform a sanity check to verify that a pre-unicode format +	    maildir can be converted to a unicode-format maildir. +	    See <quote>Converting pre-unicode format maildirs</quote>, below, +	    for more information. +	  </simpara> +	</listitem> +      </varlistentry> + +      <varlistentry> +	<term><literal>--convutf8</literal> <replaceable>maildir</replaceable> <replaceable>maildirfilter</replaceable></term> +	<listitem> +	  <simpara> +	    Convert a pre-unicode format +	    maildir can be converted to a unicode-format maildir. +	    See <quote>Converting pre-unicode format maildirs</quote>, below, +	    for more information. +	  </simpara> +	</listitem> +      </varlistentry>      </variablelist>      <refsect2> @@ -270,6 +283,288 @@ own.</para>      </refsect2>    </refsect1> + +  <refsect1> +    <title>Converting pre-unicode format maildirs</title> + +    <para> +      This section is relevant to: + +    </para> + +    <itemizedlist> +      <listitem> +	<para> +	  Updating <application>Courier-IMAP</application> to version 5.0, and +	  later, from prior versions of <application>Courier-IMAP</application>, or: +	</para> +      </listitem> +      <listitem> +	<para> +	  Updating <application>SqWebmail</application> to version 6.0, and +	  later, from prior versions of <application>SqWebmail</application>, or: +	</para> +      </listitem> +      <listitem> +	<para> +	  Updating Courier to version 1.0, and +	  later, from prior versions of Courier. +	</para> +      </listitem> +    </itemizedlist> + +    <para> +      These versions have been updated to implement native Unicode +      support in several E-mail-related protocols. It is already expected +      that updating Internet standards to use native Unicode-formatted +      E-mail messages will not be 100% backwards-compatible, in terms of +      E-mail client support. +      Given that, this major update to Unicode will also introduce some +      backwards-incompatible changes to the internal structure of maildirs, +      as a long term major to simplify Unicode support going forward. +      Might as well go through the pain of a major upgrade once. +    </para> + +    <para> +      <command>maildirmake</command>'s <option>--checkutf8</option> and +      <option>--convutf8</option> options are tools to aid in conversion of +      existing mailbox to the new Unicode-based naming standard. +    </para> + +    <refsect2> +      <title>Background</title> + +      <para> +	Mail folders in a maildir are hidden subdirectories. For example: +	a folder name <quote>Mailing list</quote> is a maildir subdirectory +	named <filename>$HOME/Maildir/.Mailing list</filename> (with +	<filename>$HOME/Maildir</filename> being the main mailbox). +      </para> + +      <para> +	Prior to the unicode update, non-English characters in folder names +	were implemented using a convention that was derived from the +	non-standard <quote>modified-UTF7</quote> encoding used by IMAP. +	A folder named <quote>Résumé</quote> was in a maildir subdirectory +	named <filename>$HOME/Maildir/.R&AOk-sum&AOk-</filename>. +	The current versions of Courier, <application>Courier-IMAP</application>, and SqWebmail, +	will simply create <filename>$HOME/Maildir/.Résumé</filename> +	using the <acronym>UTF8</acronym> encoding, which will appear simply +	as a <quote>.Résumé</quote> (hidden) subdirectory on modern +	UTF8-based systems. +      </para> + +      <para> +	Consequently, any existing maildirs that have non-English folders +	must be converted as part of updating to the current version of +	Courier, <application>Courier-IMAP</application>, and SqWebmail from pre-unicode versions. +	At this time +	this does not occur as part of automatically updating to the current +	version, and must be done manually given the wide variety of individual +	mail server configurations that are possible. +      </para> +    </refsect2> + +    <refsect2> +      <title>Unicode conversion overview</title> + +      <para> +	Updating from pre-unicode versions involves: +      </para> + +      <itemizedlist> +	<listitem> +	  <para> +	    Renaming the actual maildir folders, +	    <filename>$HOME/Maildir/.<replaceable>names</replaceable></filename> +	    into unicode names (using <acronym>UTF8</acronym>). +	  </para> +	</listitem> + +	<listitem> +	  <para> +	    Updating the +	    <filename>$HOME/Maildir/courierimapsubscribed</filename>, +	    which is a list of subscribed IMAP folders, if it exists. +	  </para> +	</listitem> + +	<listitem> +	  <para> +	    Updating any +	    <application>maildrop</application> +	    mail filtering recipe, +	    <filename>$HOME/.mailfilter</filename>, if it exists, to reference +	    the unicode maildir folders; or updating any custom site mail +	    filtering engine that delivers to maildir folders, to reference +	    the correct subdirectory names. +	  </para> +	</listitem> +      </itemizedlist> +    </refsect2> +    <refsect2> +      <title>Unicode conversion steps</title> + +      <para> +	The <option>--checkutf8</option> and +	<option>--convutf8</option> options to +	<command>maildirmake</command> convert a single maildir to the new +	unicode format: +      </para> + +      <blockquote> +	<informalexample> +	  <programlisting> +$ ./maildirmake --checkutf8 ~/Maildir ~/.mailfilter +Checking /home/mrsam/Maildir: +Mail filter to INBOX.R&AOk-sum&AOk- updated to INBOX.Résumé +Subscription to INBOX.R&AOk-sum&AOk- changed to INBOX.Résumé +Rename INBOX.R&AOk-sum&AOk- to INBOX.Résumé +Verified /home/mrsam/Maildir/courierimapsubscribed +Verified /home/mrsam/.mailfilter +$ ./maildirmake --convutf8 ~/Maildir ~/.mailfilter +Checking /home/mrsam/Maildir: +Mail filter to INBOX.R&AOk-sum&AOk- updated to INBOX.Résumé +Subscription to INBOX.R&AOk-sum&AOk- changed to INBOX.Résumé +Rename INBOX.R&AOk-sum&AOk- to INBOX.Résumé +Updating /home/mrsam/Maildir/courierimapsubscribed +Updating /home/mrsam/.mailfilter</programlisting> +	  </informalexample> +      </blockquote> + +      <para> +	<option>--checkutf8</option> goes through the motions of converting +	a single maildir to Unicode, but without making any actual changes. +	<option>--convutf8</option> does the conversion for real. +	The first required parameter is the maildir to convert. The +	second parameter is optional, and specifies the corresponding +	<command>maildrop</command> filtering recipe, +	<emphasis>but only if <application>SqWebMail</application></emphasis> +	generates the mail filtering recipes. +	<application>SqWebMail</application>'s mail filtering recipes are +	parsable, and can be automatically-converted. +	Non-<application>SqWebMail</application>-generated +	<filename>.mailfilter</filename>s cannot be converted automatically. +	The second parameter must be omitted, and the mail filtering recipe +	must be converted by hand. +      </para> + +      <note> +	<para> +	  Any manual work is only needed if maildirs have folders with +	  non-English names. Ignore everything you've just read if all +	  folder names are English-only. +	  <option>--checkutf8</option> and +	  <option>--convutf8</option> will not do anything, and nothing +	  needs to be done. +	</para> +      </note> + +      <para> +	To convert all mailboxes to Unicode all at once: +      </para> + +      <itemizedlist> +	<listitem> +	  <para> +	    Write a shell script to run <option>--checkutf8</option> on +	    all your mailboxes. It's ok to explicitly specify each mailbox's +	    <filename>.mailfilter</filename>, when using +	    <application>SqWebMail</application> even if a particular +	    mailbox does not use it. It will be ignored. +	  </para> + +	  <para> +	    It is safe to run <option>--checkutf8</option> without shutting +	    down your mail server. A non-zero exit from +	    <option>--checkutf8</option> indicates a problem (see below) +	    for a particular maildir. +	  </para> +	</listitem> + +	<listitem> +	  <para> +	    Once <option>--checkutf8</option> does not find any problems +	    with any mailbox, shut down the mail server, run +	    <option>--checkutf8</option> one more time for all mailboxes, +	    then if everything goes well, upgrade +	    <application>Courier</application>, +	    <application>Courier-IMAP</application>, or +	    <application>SqWebMail</application> and +	    run +	    <option>--convutf8</option> on every mailbox before restarting +	    the server. +	  </para> +	</listitem> +      </itemizedlist> + +      <note> +	<para> +	  <option>--convutf8</option> is a one-shot deal. Do not run +	  <option>--convutf8</option> a second time after it successfully +	  converted a maildir. In nearly all cases nothing will happen, +	  but there are rare edge cases where some folder names may +	  get garbled, or it fails completely. +	</para> +      </note> +    </refsect2> + +    <refsect2> +      <title>Resolving unicode conversion problems</title> + +      <para> +	The only likely problems that might be encountered is the fall-out +	from buggy IMAP clients that did not follow the pre-Unicode naming +	convention for non-Latin folder names. The customized IMAP +	<quote>modified-UTF7</quote> encoding convention for non-Latin +	folder names is mostly an IMAP client convention, and the +	pre-Unicode version of <application>Courier-IMAP</application> did +	not enforce it. The server just uses the name from the IMAP client, +	as is. +      </para> + +      <para> +	Unicode conversion (<option>--checkutf8</option> or +	<option>--convutf8</option>) fails if it finds a folder name that +	does not correctly use IMAP's +	<quote>modified-UTF7</quote> encoding. This can only be resolved +	manually, by renaming the folder. This may also involve manually +	editing <filename>courierimapsubscribed</filename> and +	<filename>.mailfilter</filename> if they exist. The bad folder name +	should be removed from +	<filename>courierimapsubscribed</filename>. For +	<filename>.mailfilter</filename> it is sufficient to remove only +	the comments that precede the actual <command>maildrop</command> rule, +	and <option>--convutf8</option> will remove the entire rule, by itself. +	<option>--convutf8</option> actually reads only the machine-parsable +	comments in <command>SqWebMail</command>-generated +	<filename>.mailfilter</filename> (plus a few other things in the +	file), and replaces the +	<filename>.mailfilter</filename> with the Unicode version based +	solely on the parsed data. +      </para> +    </refsect2> + +    <refsect2> +      <title>After the Unicode conversion</title> + +      <para> +	The current, Unicode version of <application>Courier-IMAP</application> +	supports both Unicode and non-Unicode +	IMAP clients; however unlike the pre-Unicode version, +	<application>Courier-IMAP</application> rejects requests from +	non-Unicode IMAP clients to use or create folders that are not +	properly encoded. +      </para> + +      <para> +	Encountering a bad folder during conversion strongly suggests the +	usage of an IMAP client that does not correctly encode non-English +	folder names. Such an IMAP client will likely have problems after +	the conversion. +      </para> +    </refsect2> +  </refsect1>    <refsect1>      <title>SEE ALSO</title> diff --git a/maildir/testmaildirfilter.c b/maildir/testmaildirfilter.c index c50d358..a8804d5 100644 --- a/maildir/testmaildirfilter.c +++ b/maildir/testmaildirfilter.c @@ -79,7 +79,7 @@ const char *charset=getenv("CHARSET");  	}  	unlink("maildirsize"); -	errcode=maildir_filter_saverules(&mf, "testrules2", ".", "Maildir", "nobody@example.com"); +	errcode=maildir_filter_saverules(&mf, "testrules2", "Maildir", "nobody@example.com");  	if (errcode)  	{  		fprintf(stderr, "Error saving testrules2: %d\n", errcode); diff --git a/rfc2045/rfc2045.h b/rfc2045/rfc2045.h index 90c2b56..87773d9 100644 --- a/rfc2045/rfc2045.h +++ b/rfc2045/rfc2045.h @@ -60,6 +60,8 @@ struct rfc2045 {  	unsigned rfcviolation;	/* Boo-boos */  #define	RFC2045_ERR8BITHEADER	1	/* 8 bit characters in headers */ +	/* But this is now OK, in UTF8 mode */ +  #define	RFC2045_ERR8BITCONTENT	2	/* 8 bit contents, but no 8bit  					content-transfer-encoding */  #define	RFC2045_ERR2COMPLEX	4	/* Too many nested contents */ | 
