hi,
On Tue, Sep 03, 2013 at 11:34:31PM -0500, guns wrote:
> The changes are done conservatively, and the style is in line with the
> rest of the code. I am happy to make any and all amendments that you
> propose and will be awaiting your response.
>
i took a somewhat more aggressive and "creative" approach to the
problem, after all. please review and test the attached patches.
>From ab9d9258ae05943498badaadc09110aa74a9444f Mon Sep 17 00:00:00 2001
From: Oswald Buddenhagen <[email protected]>
Date: Wed, 25 Sep 2013 20:55:32 +0200
Subject: [PATCH 1/2] support backslashes and quotes in quoted IMAP strings
the RFCs require it - well hidden in the BNF at the bottom.
patch somewhat inspired by "guns" <[email protected]>.
---
src/drv_imap.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 136 insertions(+), 25 deletions(-)
diff --git a/src/drv_imap.c b/src/drv_imap.c
index 950f4ab..bd940ae 100644
--- a/src/drv_imap.c
+++ b/src/drv_imap.c
@@ -355,6 +355,88 @@ submit_imap_cmd( imap_store_t *ctx, struct imap_cmd *cmd )
return send_imap_cmd( ctx, cmd );
}
+/* Minimal printf() replacement that supports an %\s format sequence to print backslash-escaped
+ * string literals. Note that this does not automatically add quotes around the printed string,
+ * so it is possible to concatenate multiple segments. */
+static char *
+imap_vprintf( const char *fmt, va_list ap )
+{
+ const char *s, *es;
+ char *d, *ed;
+ int maxlen;
+ char c;
+ char buf[1024]; /* Minimal supported command buffer size per IMAP spec. */
+
+ d = buf;
+ ed = d + sizeof(buf);
+ s = fmt;
+ for (;;) {
+ c = *fmt;
+ if (!c || c == '%') {
+ int l = fmt - s;
+ if (d + l > ed)
+ oob();
+ memcpy( d, s, l );
+ d += l;
+ if (!c) {
+ l = d - buf;
+ ed = nfmalloc( l + 1 );
+ memcpy( ed, buf, l );
+ ed[l] = 0;
+ return ed;
+ }
+ maxlen = INT_MAX;
+ c = *++fmt;
+ if (c == '\\') {
+ c = *++fmt;
+ if (c != 's') {
+ fputs( "Fatal: unsupported escaped format specifier. Please report a bug.\n", stderr );
+ abort();
+ }
+ s = va_arg( ap, const char * );
+ while ((c = *s++)) {
+ if (d + 2 > ed)
+ oob();
+ if (c == '\\' || c == '"')
+ *d++ = '\\';
+ *d++ = c;
+ }
+ } else { /* \\ cannot be combined with anything else. */
+ if (c == '.') {
+ c = *++fmt;
+ if (c != '*') {
+ fputs( "Fatal: unsupported string length specification. Please report a bug.\n", stderr );
+ abort();
+ }
+ maxlen = va_arg( ap , int );
+ c = *++fmt;
+ }
+ if (c == 'c') {
+ if (d + 1 > ed)
+ oob();
+ *d++ = (char)va_arg( ap , int );
+ } else if (c == 's') {
+ s = va_arg( ap, const char * );
+ es = memchr( s, 0, maxlen );
+ l = es ? es - s : maxlen;
+ if (d + l > ed)
+ oob();
+ memcpy( d, s, l );
+ d += l;
+ } else if (c == 'd') {
+ d += nfsnprintf( d, ed - d, "%d", va_arg( ap , int ) );
+ } else {
+ fputs( "Fatal: unsupported format specifier. Please report a bug.\n", stderr );
+ abort();
+ }
+ }
+ s = ++fmt;
+ } else {
+ fmt++;
+ }
+ }
+}
+
static int
imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response ),
@@ -366,7 +448,7 @@ imap_exec( imap_store_t *ctx, struct imap_cmd *cmdp,
cmdp = new_imap_cmd( sizeof(*cmdp) );
cmdp->param.done = done;
va_start( ap, fmt );
- nfvasprintf( &cmdp->cmd, fmt, ap );
+ cmdp->cmd = imap_vprintf( fmt, ap );
va_end( ap );
return submit_imap_cmd( ctx, cmdp );
}
@@ -457,10 +539,25 @@ imap_refcounted_done_box( imap_store_t *ctx ATTR_UNUSED, struct imap_cmd *cmd, i
imap_refcounted_done( sts );
}
+static const char *
+imap_strchr( const char *s, char tc )
+{
+ for (;; s++) {
+ char c = *s;
+ if (c == '\\')
+ c = *++s;
+ if (!c)
+ return 0;
+ if (c == tc)
+ return s;
+ }
+}
+
static char *
next_arg( char **ps )
{
- char *ret, *s;
+ char *ret, *s, *d;
+ char c;
assert( ps );
s = *ps;
@@ -473,20 +570,30 @@ next_arg( char **ps )
return 0;
}
if (*s == '"') {
- ++s;
- ret = s;
- s = strchr( s, '"' );
+ s++;
+ ret = d = s;
+ while ((c = *s++) != '"') {
+ if (c == '\\')
+ c = *s++;
+ if (!c) {
+ *ps = 0;
+ return 0;
+ }
+ *d++ = c;
+ }
+ *d = 0;
} else {
ret = s;
- while (*s && !isspace( (unsigned char)*s ))
+ while ((c = *s)) {
+ if (isspace( (unsigned char)c )) {
+ *s++ = 0;
+ break;
+ }
s++;
+ }
}
- if (s) {
- if (*s)
- *s++ = 0;
- if (!*s)
- s = 0;
- }
+ if (!*s)
+ s = 0;
*ps = s;
return ret;
@@ -529,8 +636,9 @@ static int
parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
{
list_t *cur, **curp;
- char *s = *sp, *p;
+ char *s = *sp, *d, *p;
int bytes;
+ char c;
assert( sts );
assert( sts->level > 0 );
@@ -595,12 +703,15 @@ parse_imap_list( imap_store_t *ctx, char **sp, parse_list_state_t *sts )
} else if (*s == '"') {
/* quoted string */
s++;
- p = s;
- for (; *s != '"'; s++)
- if (!*s)
+ p = d = s;
+ while ((c = *s++) != '"') {
+ if (c == '\\')
+ c = *s++;
+ if (!c)
goto bail;
- cur->len = s - p;
- s++;
+ *d++ = c;
+ }
+ cur->len = d - p;
cur->val = nfmalloc( cur->len + 1 );
memcpy( cur->val, p, cur->len );
cur->val[cur->len] = 0;
@@ -1140,7 +1251,7 @@ imap_socket_read( void *aux )
cmd2->gen.param.high_prio = 1;
p = strchr( cmdp->cmd, '"' );
if (imap_exec( ctx, &cmd2->gen, get_cmd_result_p2,
- "CREATE %.*s", strchr( p + 1, '"' ) - p + 1, p ) < 0)
+ "CREATE %.*s", imap_strchr( p + 1, '"' ) - p + 1, p ) < 0)
return;
continue;
}
@@ -1571,7 +1682,7 @@ imap_open_store_authenticate2( imap_store_t *ctx )
#endif
warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
imap_exec( ctx, 0, imap_open_store_authenticate2_p2,
- "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass );
+ "LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass );
return;
bail:
@@ -1695,7 +1806,7 @@ imap_select( store_t *gctx, int create,
cmd->gen.param.create = create;
cmd->gen.param.trycreate = 1;
imap_exec( ctx, &cmd->gen, imap_done_simple_box,
- "SELECT \"%s\"", buf );
+ "SELECT \"%\\s\"", buf );
free( buf );
}
@@ -1927,7 +2038,7 @@ imap_trash_msg( store_t *gctx, message_t *msg,
return;
}
imap_exec( ctx, &cmd->gen, imap_done_simple_msg,
- "UID COPY %d \"%s\"", msg->uid, buf );
+ "UID COPY %d \"%\\s\"", msg->uid, buf );
free( buf );
}
@@ -1981,10 +2092,10 @@ imap_store_msg( store_t *gctx, msg_data_t *data, int to_trash,
# pragma GCC diagnostic pop
#endif
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
- "APPEND \"%s\" %s\"%s\" ", buf, flagstr, datestr );
+ "APPEND \"%\\s\" %s\"%\\s\" ", buf, flagstr, datestr );
} else {
imap_exec( ctx, &cmd->gen, imap_store_msg_p2,
- "APPEND \"%s\" %s", buf, flagstr );
+ "APPEND \"%\\s\" %s", buf, flagstr );
}
free( buf );
}
@@ -2038,7 +2149,7 @@ imap_list( store_t *gctx, int flags,
if (((flags & LIST_PATH) &&
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
- "LIST \"\" \"%s*\"", ctx->prefix ) < 0) ||
+ "LIST \"\" \"%\\s*\"", ctx->prefix ) < 0) ||
((flags & LIST_INBOX) && (!(flags & LIST_PATH) || *ctx->prefix) &&
imap_exec( ctx, imap_refcounted_new_cmd( sts ), imap_refcounted_done_box,
"LIST \"\" INBOX*" ) < 0))
--
1.8.0.1.18.g93341d8
>From 4c99c3c4067fab0957fdc57ecd66cfb6c2754164 Mon Sep 17 00:00:00 2001
From: Oswald Buddenhagen <[email protected]>
Date: Wed, 25 Sep 2013 20:56:52 +0200
Subject: [PATCH 2/2] support backslash-escaping in the config file
note that no attempt is made at making this work in the compat wrapper.
---
src/config.c | 16 +++++++++++++---
src/mbsync.1 | 3 ++-
2 files changed, 15 insertions(+), 4 deletions(-)
diff --git a/src/config.c b/src/config.c
index 2c2a086..7bf8aa3 100644
--- a/src/config.c
+++ b/src/config.c
@@ -48,7 +48,7 @@ static char *
get_arg( conffile_t *cfile, int required, int *comment )
{
char *ret, *p, *t;
- int quoted;
+ int escaped, quoted;
char c;
p = cfile->rest;
@@ -64,9 +64,14 @@ get_arg( conffile_t *cfile, int required, int *comment )
}
ret = 0;
} else {
- for (quoted = 0, ret = t = p; c; c = *p) {
+ for (escaped = 0, quoted = 0, ret = t = p; c; c = *p) {
p++;
- if (c == '"')
+ if (escaped && c >= 32) {
+ escaped = 0;
+ *t++ = c;
+ } else if (c == '\\')
+ escaped = 1;
+ else if (c == '"')
quoted ^= 1;
else if (!quoted && isspace( (unsigned char) c ))
break;
@@ -74,6 +79,11 @@ get_arg( conffile_t *cfile, int required, int *comment )
*t++ = c;
}
*t = 0;
+ if (escaped) {
+ error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
+ cfile->err = 1;
+ ret = 0;
+ }
if (quoted) {
error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
cfile->err = 1;
diff --git a/src/mbsync.1 b/src/mbsync.1
index 965e65f..bdf5d3c 100644
--- a/src/mbsync.1
+++ b/src/mbsync.1
@@ -88,7 +88,8 @@ If specified twice, suppress warning messages as well.
The configuration file is mandatory; \fBmbsync\fR will not run without it.
Lines starting with a hash mark (\fB#\fR) are comments and are ignored entirely.
Configuration items are keywords followed by one or more arguments;
-arguments containing spaces must be enclosed in double quotes (\fB"\fR).
+arguments containing spaces must be enclosed in double quotes (\fB"\fR),
+and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
All keywords (including those used as arguments) are case-insensitive.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
in all options which represent local paths.
--
1.8.0.1.18.g93341d8
------------------------------------------------------------------------------
October Webinars: Code for Performance
Free Intel webinars can help you accelerate application performance.
Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from
the latest Intel processors and coprocessors. See abstracts and register >
http://pubads.g.doubleclick.net/gampad/clk?id=60133471&iu=/4140/ostg.clktrk
_______________________________________________
isync-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/isync-devel