summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSam Varshavchik2018-07-16 21:39:43 -0400
committerSam Varshavchik2018-07-16 21:39:43 -0400
commit711f59c72f26a1af7865c70edeac54db12eac076 (patch)
treef66485c395028804f8c25bfbe21832bbf9f98eab
parent0184320a0c202d3727840624dbeaac9b3b10a6de (diff)
parent443a747d471c570e3a3d100097430d80615572c3 (diff)
downloadcourier-libs-711f59c72f26a1af7865c70edeac54db12eac076.tar.bz2
Merge branch 'eai'
-rw-r--r--imap/capability.c4
-rw-r--r--imap/fetch.c40
-rw-r--r--imap/imapd.c533
-rw-r--r--imap/imapd.h1
-rw-r--r--imap/imaplogin.c3
-rw-r--r--imap/imaptoken.c208
-rw-r--r--imap/imaptoken.h2
-rw-r--r--imap/imapwrite.h3
-rw-r--r--imap/msgenvelope.c5
-rw-r--r--imap/smap.c3
-rw-r--r--maildir/Makefile.am4
-rw-r--r--maildir/maildirfilter.c5
-rw-r--r--maildir/maildirfilter.h1
-rw-r--r--maildir/maildirfilter2.c2
-rw-r--r--maildir/maildirinfo.c29
-rw-r--r--maildir/maildirinfo.h6
-rw-r--r--maildir/maildirinfo2.c148
-rw-r--r--maildir/maildirmake.c436
-rw-r--r--maildir/maildirmake.sgml323
-rw-r--r--maildir/testmaildirfilter.c2
-rw-r--r--rfc2045/rfc2045.h2
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, &current_maildir_info, n-1,
- &rfc2045p)) seen=1;
+ int rc=fetchitem(&fp, &open_err, fi, &current_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(&current_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&amp;AOk-sum&amp;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&amp;AOk-sum&amp;AOk- updated to INBOX.Résumé
+Subscription to INBOX.R&amp;AOk-sum&amp;AOk- changed to INBOX.Résumé
+Rename INBOX.R&amp;AOk-sum&amp;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&amp;AOk-sum&amp;AOk- updated to INBOX.Résumé
+Subscription to INBOX.R&amp;AOk-sum&amp;AOk- changed to INBOX.Résumé
+Rename INBOX.R&amp;AOk-sum&amp;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 */