Re: [PATCH] Implementing IncludeCmd (feature request #17)

2023-08-17 Thread Michiel van den Heuvel
Hi Oswald,

> > It might be worthwhile to set `cfile->error` here too, and
> > I'll gladly amend the patch if you agree.
>
> yeah, i'd do that.

Done!

> theoretically yes, though in practice i'd recommend that users just call 
> a script, because the extra layer of quoting/escaping makes things ugly 
> and easy to get wrong. add it if you can't help yourself. ;)

> so just call a script already! ;)

Oh you should see my personal configuration. So many inline scripts
under multiple layers of escaping. But, with my own troubles in getting
a clean implementation plus the feedback, I think I can help myself ;) 

External scripts it is.

> eval => included, i guess.
> also, that space afterwards should be probably a colon, otherwise it's
> kinda confusing and weird-looking.

Changed it, but are you sure? Now that the command is called `Eval`? The
space *was* weird-looking, thanks. I was already unhappy about it.

> on that matter, have you tested all error scenarios?

The ones I added? Or all that were modified? On the ones that I added,
yes, except for failure of popen and the `ret < 0` case in `eval_pclose`.
I have to admit I do not really know how to trigger those.

> > +void
> > +eval_popen( conffile_t *cfile, const char *cmd )
> 
> should return an int (zero is error), which should lead to early 
> termination.

It was missing the `cfile->err` plumbing. Which is fixed, assuming that's
what you meant, in the attached implementation. I could also return 0 from
getcline in that case, but that does not match the rest of the parser. Is
a failed popen that different?

> ... but here it gets "interesting":
> seeing the command after dequoting may be actually helpful, especially 
> when multi-line commands are supported. though of course this can be 
> ridiculously long. but i guess we'll get away with just qouting it.

Added this.

> > +char *
> > +read_cline( conffile_t *cfile )
>
> i think that should be just an int.

Fixed (?) by comparing it to `NULL` when returning. Also made it less
convoluted.

> >+.SS Dynamic options
>
> why a separate section? it doesn't really fit the pattern for the *Cmd 
> commands.

I didn't really know where to put it. It can occur literally everywhere,
which no other options (like the `*Cmd`) do, which is why I did not add
it there. This time I put it next to the global options.

> >+void
> >+conf_error( const conffile_t *cfile, const char* fmt, ... )
>
> second asterisk has space on the wrong side.
> this repeats for several other string arguments.
> but i think this is actually the only whitespace mistake you've made, 
> which may be a first among non-trivial external isync contributions 
> during my "reign". ^^

Hehehe :) Nice trophy. I don't suppose you have some formatter config
file?

(Hopefully) All your other proposed changes were incorporated in these
revised patches.

Cheers!>From 92cc071aeb14cab302eb0db206a30e2c5c78623b Mon Sep 17 00:00:00 2001
From: Michiel van den Heuvel 
Date: Thu, 10 Aug 2023 05:20:55 +0200
Subject: [PATCH 1/2] Refactor printing configuration errors

In preparation for adding the output of commands as configuration lines,
which will complicate printing.
---
 src/config.c  | 106 +-
 src/config.h  |   3 ++
 src/driver.c  |   6 +--
 src/drv_imap.c|  46 +++-
 src/drv_maildir.c |   9 ++--
 5 files changed, 72 insertions(+), 98 deletions(-)

diff --git a/src/config.c b/src/config.c
index 456bd47..0de2cb2 100644
--- a/src/config.c
+++ b/src/config.c
@@ -61,6 +61,31 @@ expand_strdup( const char *s, const conffile_t *cfile )
 	}
 }
 
+void
+conf_error( conffile_t *cfile, const char *fmt, ... )
+{
+	va_list va;
+
+	flushn();
+	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	va_start( va, fmt );
+	vfprintf( stderr, fmt, va );
+	va_end( va );
+	cfile->err = 1;
+}
+
+void
+conf_sys_error( conffile_t *cfile, const char *fmt, ... )
+{
+	va_list va;
+
+	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	va_start( va, fmt );
+	vsys_error( fmt, va );
+	va_end( va );
+	cfile->err = 1;
+}
+
 char *
 get_arg( conffile_t *cfile, int required, int *comment )
 {
@@ -76,8 +101,7 @@ get_arg( conffile_t *cfile, int required, int *comment )
 		if (comment)
 			*comment = (c == '#');
 		if (required) {
-			error( "%s:%d: parameter missing\n", cfile->file, cfile->line );
-			cfile->err = 1;
+			conf_error( cfile, "parameter missing\n" );
 		}
 		ret = NULL;
 	} else {
@@ -98,13 +122,11 @@ get_arg( conffile_t *cfile, int required, int *comment )
 		}
 		*t = 0;
 		if (escaped) {
-			error( "%s:%d: unterminated escape sequence\n", cfile->file, cfile->line );
-			cfile->err = 1;
+			conf_error( cfile, "unterminated escape sequence\n" );
 			ret = NULL;
 		}
 		if (quoted) {
-			error( "%s:%d: missing closing quote\n", cfile->file, cfile->line );
-			cfile->err = 1;
+			conf_error( cfile, "missing closing quote\n" );
 			ret = NULL;
 		}
 	}
@@ -124,9 +146,7 @@ 

Re: Socket Error, Possible protocol violation

2023-08-17 Thread Oswald Buddenhagen

On Thu, Aug 17, 2023 at 07:59:09AM -0400, brittanderson--- via isync-devel 
wrote:

The error has reoccured. It is:

Socket error: receive buffer full. Probably protocol error.

hmm, this might in fact be one of the two regressions that prevent me 
from releasing 1.5.0 - i honestly don't remember, as it's been over a 
year now - i'm sidetracked by other projects.


Happy to provide more details if you can point me in the best 
direction.


i don't think that's necessary. it "just" requires me looking really 
hard at the code. the culprit must be one of the last commits to 
socket.c (git-bisect'ing it would not hurt). if someone else wants to 
take a shot at analyzing it, that would be most welcome.


regards


___
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel


Re: Socket Error, Possible protocol violation

2023-08-17 Thread brittanderson--- via isync-devel
The error has reoccured. It is:

Socket error: receive buffer full. Probably protocol error.

running =mbsync -D university=

Gives an output that includes this snippet:

#+Begin_quote

N: Called get_uidnext, ret=1273
-> log: F 1 1273 (save UIDNEXT of near side)
-> log: # 192877 0 yMXnDcoTZKtm (new TUID)
N: Called get_memory_usage, ret=0
F: [ 9] Enter fetch_msg, uid=192877, want_flags=no, want_date=no
F: >>> 6 UID FETCH 192877 (BODY.PEEK[])
F: [ 9] Leave fetch_msg
F: [ 7] Callback leave load_box
F: * 3 FETCH (UID 192877 BODY[] {587687}
F: === (98304 bytes omitted)
F: === (65536 bytes omitted)
F: === (10 bytes omitted)
F: === (10 bytes omitted)
Socket error: receive buffer full. Probably protocol error.

#+End_quote

I cannot find anything to match the offending email on my (near) side.
When I log on to microsoft365 and deleted the most recent email from my
inbox the error went away.

Happy to provide more details if you can point me in the best direction.

Thank you, Britt


Oswald Buddenhagen  writes:

> DuckDuckGo was unable to verify sender identity
>
> On Tue, Aug 15, 2023 at 02:31:11PM -0400, brittanderson--- via isync-devel 
> wrote:
>>Oswald Buddenhagen  writes:
>>> the two sides have different uids. the ones appearing in the imap
>>> stream don't match the local mailboxes. the complete output contains
>>> the local (or more precisely, near-side) uids as well, and their
>>> pairings with remote (far-side) ones.
>>
>>I saw the pairs, but there was no element with the same UID as the error
>>message about the socket error. Do the UID in the error reports come
>>from elsewhere as well?
>>
> you'd need to show some logs for me to have a clue what you're talking
> about.
>
>>For example, something related to davmail or the
>>microsoft exchange server I am talking to?
>>
> imap allows the server to include free-form text into error messages,
> so it's theoretically possible.
>
> regards
>
>
> ___
> isync-devel mailing list
> isync-devel@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/isync-devel



___
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel


Re: [PATCH] Implementing IncludeCmd (feature request #17)

2023-08-17 Thread Oswald Buddenhagen

hi!

On Thu, Aug 17, 2023 at 10:59:48AM +0200, Michiel van den Heuvel wrote:

I named the command `Eval` instead of `IncludeCmd` because it seemed
clearer to me, but you're welcome to change that.

my thinking was that IncludeCmd would nicely complement a hypothetical 
Include command, following the structure of the pre-existing *Cmd 
commands.

the code needs no renaming, eval is nice and short.


Patch 1 was necessary to print error line numbers correctly after adding
`Eval` without modifying every line where errors are printed.


It might be worthwhile to set `cfile->error` here too, and I'll gladly 
amend the patch if you agree.



yeah, i'd do that.


On the other hand, it is a formatting function...


nah, the name doesn't say so.


Patch 2 is where the actual work happens. I indeed tried to follow
`cred_from_cmd`.



Nesting `Eval` statements is disallowed. Implementing
this would certainly be doable, but I don't really see the point.  


agreed.


I was working on an extra patch, which would extend the parser to
allow multiline values for slightly longer commands. [...]

 - Do you see merit in this feature? I don't currently need it, but
   like configuration file formats which offer possibilities to split
   lines if they get too long.

theoretically yes, though in practice i'd recommend that users just call 
a script, because the extra layer of quoting/escaping makes things ugly 
and easy to get wrong. add it if you can't help yourself. ;)



 - If so, what would you think is the better implementation? Just make
   the buffer really large (what is large?) and error if a script is
   longer? The current line limit is 1024. [...]

i think the static 1k buffer is just fine - that amounts to a script 
with up to ~20 lines, and it seems a tad insane to inline more than 
that.



 - Do you agree with the syntax? This still requires backslashes even
   when quoted, which I like due to the missing closing quote error
   which would otherwise be hard to correctly detect.

i guess. it also has the advantage of somewhat signaling the fact of the 
extra layer of quoting.



 - What should the behaviour be when not quoted?
A literal newline (current implementation)?

that seems convenient, but it violates expectations about how line 
continuations usually behave, regardless of whether quoted or not.



Read it as whitespace?

that would be more conventional. it would of course also mean that users 
need to include `&&` (or `;`) to chain the commands, which many would 
probably get wrong on first try.


the alternative would be more like a here-doc, and would warrant a 
corresponding syntax. seems like mild overkill, though.


so just call a script already! ;)


Subject: [PATCH 2/2] Add Eval directive to config parser

--- a/src/config.c
+++ b/src/config.c

+void


static


+conf_print_loc( const conffile_t *cfile )
+{
+   if (cfile->eval_fp != NULL) {


i treat pointers a booleans in conditionals.
also, excess braces.


+   fprintf( stderr, "%s:%d:eval %d: ", cfile->file, cfile->line, 
cfile->eval_line );


eval => included, i guess.
also, that space afterwards should be probably a colon, otherwise it's 
kinda confusing and weird-looking.


on that matter, have you tested all error scenarios?


+void

should return an int (zero is error), which should lead to early 
termination.

also, static.


+eval_popen( conffile_t *cfile, const char *cmd )
+{
+   if (*cmd == '+') {
+   flushn();
+   cmd++;
+   }

that makes no sense here - unlike the credential commands, this is 
executed at program startup where no progress info has been printed yet.

don't forget to prune it from the docu as well.


+   if (!(cfile->eval_fp = popen( cmd, "r" ))) {
+   sys_error( "%s failed", cmd );

i think it makes no sense to print the command here, as failure here 
would be a fork() failure or something like that. so just "popen" should 
do.

(though i wonder how things would look like in an msys build.)


+void


as above.


+eval_pclose( conffile_t *cfile )
+{
+   int ret;
+
+   ret = pclose( cfile->eval_fp );
+   cfile->eval_fp = NULL;
+   if (ret) {
+   if (ret < 0)
+   conf_sys_error( cfile, "command failed" );
+   else if (WIFSIGNALED( ret ))
+   conf_error( cfile, "command crashed\n" );
+   else
+   conf_error( cfile, "command exited with status %d\n", 
WEXITSTATUS( ret ) );


... but here it gets "interesting":
seeing the command after dequoting may be actually helpful, especially 
when multi-line commands are supported. though of course this can be 
ridiculously long. but i guess we'll get away with just qouting it.


here-doc syntax would make printing the command superfluous, because it 
would be used verbatim anyway ... until someone actually implements 
expandos (like %{Host}).



+char *


i think that should be just an int.
also, static.



[PATCH] Implementing IncludeCmd (feature request #17)

2023-08-17 Thread Michiel van den Heuvel
Hi!

It took some time, but here is an implementation of feature request
#17. I named the command `Eval` instead of `IncludeCmd` because it seemed
clearer to me, but you're welcome to change that.

Patch 1 was necessary to print error line numbers correctly after adding
`Eval` without modifying every line where errors are printed. It might
be worthwhile to set `cfile->error` here too, and I'll gladly amend the
patch if you agree. On the other hand, it is a formatting function...

Patch 2 is where the actual work happens. I indeed tried to follow
`cred_from_cmd`. Nesting `Eval` statements is disallowed. Implementing
this would certainly be doable, but I don't really see the point.  

I was working on an extra patch, which would extend the parser to
allow multiline values for slightly longer commands. For example:

Eval "\
echo User $USER \
echo Path $XDG_DATA_HOME/notmuch/default \
"

Due to the way the `cfile->buf` buffer is aliased by `cfile->cmd` etc
this proved to be a little bit more difficult than I initially thought. I
have a few questions:

  - Do you see merit in this feature? I don't currently need it, but
like configuration file formats which offer possibilities to split
lines if they get too long.
  - If so, what would you think is the better implementation? Just make
the buffer really large (what is large?) and error if a script is
longer? The current line limit is 1024. The other option I see is
dynamically reallocating the buffer (I have already played with this,
but sadly that implementation was not aware of comments) and updating
the pointers in `cfile` as needed. Maybe throw in a `restrict`.
  - Do you agree with the syntax? This still requires backslashes even
when quoted, which I like due to the missing closing quote error
which would otherwise be hard to correctly detect.
  - What should the behaviour be when not quoted? A literal newline
(current implementation)? Read it as whitespace?

Anyway, valgrind was an issue on my end and is fixed. So it has been
tested with it, hopefully no leaks this time ;)

Once again, feedback is very welcome!

Kind regards,  
Michiel van den Heuvel>From 1ef12e2b3f205af4324565183b700e27ecc88830 Mon Sep 17 00:00:00 2001
From: Michiel van den Heuvel 
Date: Wed, 16 Aug 2023 20:36:48 +0200
Subject: [PATCH 2/2] Add Eval directive to config parser

---
 src/config.c | 81 +---
 src/config.h |  2 ++
 src/mbsync.1 | 10 +++
 3 files changed, 89 insertions(+), 4 deletions(-)

diff --git a/src/config.c b/src/config.c
index 3aa3f7c..6e08dd9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -61,13 +61,23 @@ expand_strdup( const char *s, const conffile_t *cfile )
 	}
 }
 
+void
+conf_print_loc( const conffile_t *cfile )
+{
+	if (cfile->eval_fp != NULL) {
+		fprintf( stderr, "%s:%d:eval %d: ", cfile->file, cfile->line, cfile->eval_line );
+	} else {
+		fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	}
+}
+
 void
 conf_error( const conffile_t *cfile, const char* fmt, ... )
 {
 	va_list va;
 
 	flushn();
-	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	conf_print_loc( cfile );
 	va_start( va, fmt );
 	vfprintf( stderr, fmt, va );
 	va_end( va );
@@ -78,7 +88,7 @@ conf_sys_error( const conffile_t *cfile, const char* msg, ... )
 {
 	va_list va;
 
-	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	conf_print_loc( cfile );
 	va_start( va, msg );
 	vsys_error( msg, va );
 	va_end( va );
@@ -326,6 +336,58 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
 	return 1;
 }
 
+void
+eval_popen( conffile_t *cfile, const char *cmd )
+{
+	if (*cmd == '+') {
+		flushn();
+		cmd++;
+	}
+	if (!(cfile->eval_fp = popen( cmd, "r" ))) {
+		sys_error( "%s failed", cmd );
+		return;
+	}
+	cfile->eval_line = 0;
+}
+
+void
+eval_pclose( conffile_t *cfile )
+{
+	int ret;
+
+	ret = pclose( cfile->eval_fp );
+	cfile->eval_fp = NULL;
+	if (ret) {
+		if (ret < 0)
+			conf_sys_error( cfile, "command failed" );
+		else if (WIFSIGNALED( ret ))
+			conf_error( cfile, "command crashed\n" );
+		else
+			conf_error( cfile, "command exited with status %d\n", WEXITSTATUS( ret ) );
+	}
+}
+
+char *
+read_cline( conffile_t *cfile )
+{
+	char *ret;
+
+	ret = cfile->buf;
+	if (cfile->eval_fp) {
+		ret = fgets( cfile->buf, cfile->bufl, cfile->eval_fp );
+		cfile->eval_line++;
+		if (!ret) {
+			eval_pclose( cfile );
+			ret = cfile->buf;
+		}
+	}
+	if (!cfile->eval_fp && ret) {
+		ret = fgets( cfile->buf, cfile->bufl, cfile->fp );
+		cfile->line++;
+	}
+	return ret;
+}
+
 int
 getcline( conffile_t *cfile )
 {
@@ -336,14 +398,24 @@ getcline( conffile_t *cfile )
 		conf_error( cfile, "excess token '%s'\n", arg );
 		cfile->err = 1;
 	}
-	while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
-		cfile->line++;
+	while (read_cline( cfile )) {
 		cfile->rest = cfile->buf;
 		if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL,  ))) {
 			if (comment)