unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Kai Tetzlaff <emacs+bug@tetzco.de>
To: Eli Zaretskii <eliz@gnu.org>
Cc: herbert@gojira.at, 54154@debbugs.gnu.org, larsi@gnus.org
Subject: bug#54154: Emacs commit ae963e80a79f5a9184daabfc8197f211a39b136d (sieve-manage)
Date: Thu, 19 Jan 2023 13:38:13 +0100	[thread overview]
Message-ID: <873586d7ii.fsf@tetzco.de> (raw)
In-Reply-To: <83tu0nynla.fsf@gnu.org> (Eli Zaretskii's message of "Thu, 19 Jan 2023 09:45:21 +0200")

[-- Attachment #1: Type: text/plain, Size: 804 bytes --]

Hello Eli,

thanks for looking into this! 

Eli Zaretskii <eliz@gnu.org> writes:
> The duplicate use of with-current-buffer is sub-optimal,
> IMO.  What about the simpler code below:
>
>   (when sieve-manage-log
>     (let* ((existing-log-buffer (get-buffer sieve-manage-log))
>            (log-buffer (or existing-log-buffer
>                            (get-buffer-create sieve-manage-log))))
>       (with-current-buffer log-buffer
>         (unless existing-log-buffer
>           ;; Do this only once, when creating the log buffer.
>           (set-buffer-multibyte nil)
>           (buffer-disable-undo))
>         (goto-char (point-max))
>         (apply #'insert args)))))

Yes, that provides more insight into what the code intends to do. Here's
the patch (with additional updated doc string):


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: fix sieve-manage--append-to-log for emacs-29 --]
[-- Type: text/x-diff, Size: 2230 bytes --]

From 62d03f302125c0b1aab2e3ae4f5b12b531d30d74 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <emacs@tetzco.de>
Date: Thu, 19 Jan 2023 03:16:14 +0100
Subject: [PATCH] ; Fix bug in sieve-manage--append-to-log (emacs-29 only)

This is emacs-29 only, use more elaborate fix for Emacs 30.x (master).

* lisp/net/sieve-manage.el
(sieve-manage--append-to-log): Fix log buffer creation.
---
 lisp/net/sieve-manage.el | 26 ++++++++++++++++----------
 1 file changed, 16 insertions(+), 10 deletions(-)

diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index 5bee4f4c4ad..4866f788bff 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -168,19 +168,25 @@ sieve-manage-capability
 
 ;; Internal utility functions
 (defun sieve-manage--append-to-log (&rest args)
-  "Append ARGS to sieve-manage log buffer.
+  "Append ARGS to `sieve-manage-log' buffer.
 
 ARGS can be a string or a list of strings.
-The buffer to use for logging is specifified via
-`sieve-manage-log'. If it is nil, logging is disabled."
+The buffer to use for logging is specifified via `sieve-manage-log'.
+If it is nil, logging is disabled.
+
+When the `sieve-manage-log' buffer doesn't exist, it gets created (and
+configured with some initial settings)."
   (when sieve-manage-log
-    (with-current-buffer (or (get-buffer sieve-manage-log)
-                             (with-current-buffer
-                                 (get-buffer-create sieve-manage-log)
-                               (set-buffer-multibyte nil)
-                               (buffer-disable-undo)))
-      (goto-char (point-max))
-      (apply #'insert args))))
+    (let* ((existing-log-buffer (get-buffer sieve-manage-log))
+           (log-buffer (or existing-log-buffer
+                           (get-buffer-create sieve-manage-log))))
+      (with-current-buffer log-buffer
+        (unless existing-log-buffer
+          ;; Do this only once, when creating the log buffer.
+          (set-buffer-multibyte nil)
+          (buffer-disable-undo))
+        (goto-char (point-max))
+        (apply #'insert args)))))
 
 (defun sieve-manage--message (format-string &rest args)
   "Wrapper around `message' which also logs to sieve manage log.
-- 
2.39.0


[-- Attachment #3: Type: text/plain, Size: 7359 bytes --]


>>  ;; Internal utility functions
>> +(defun sieve-manage--set-internal-buffer-properties (buffer)
>> +  "Set BUFFER properties for internally used buffers.
>> +
>> +Used for process and log buffers, this function makes sure that
>> +those buffers keep received and sent data intact by:
>> +- setting the coding system to 'sieve-manage--coding-system',
>> +- setting `after-change-functions' to nil to avoid those
>> +  functions messing with buffer content.
>> +Also disables undo (to save a bit of memory and improve
>> +performance).
>> +
>> +Returns BUFFER."
>> +  (with-current-buffer buffer
>> +    (set-buffer-file-coding-system sieve-manage--coding-system)
>> +    (setq-local after-change-functions nil)
>> +    (buffer-disable-undo)
>> +    (current-buffer)))
>> +
>>  (defun sieve-manage--append-to-log (&rest args)
>>    "Append ARGS to sieve-manage log buffer.
>>  
>> @@ -175,10 +202,8 @@ sieve-manage--append-to-log
>>  `sieve-manage-log'. If it is nil, logging is disabled."
>>    (when sieve-manage-log
>>      (with-current-buffer (or (get-buffer sieve-manage-log)
>> -                             (with-current-buffer
>> -                                 (get-buffer-create sieve-manage-log)
>> -                               (set-buffer-multibyte nil)
>> -                               (buffer-disable-undo)))
>> +                             (sieve-manage--set-internal-buffer-properties
>> +                                 (get-buffer-create sieve-manage-log)))
>>        (goto-char (point-max))
>>        (apply #'insert args))))
>
> This still uses a less-than-elegant implementation that calls
> with-current-buffer twice.

Yes, true. But since `sieve-manage--set-internal-buffer-properties' is
used in two different places, the more elegant solution you suggested
above would require duplicating the body of the function in those
places. I just didn't see a better way. In particular because
`set-buffer-file-coding-system' and `setq-local' only work with
(current-buffer). If you can point me to code which can replace these
with something that takes BUFFER arguments, I can rewrite
`sieve-manage--set-internal-buffer-properties' and avoid using
`with-current-buffer'.

>>  (defun sieve-manage-encode (utf8-string)
>>    "Convert UTF8-STRING to managesieve protocol octets."
>> -  (encode-coding-string utf8-string 'raw-text t))
>> +  (encode-coding-string utf8-string sieve-manage--coding-system t))
>
> Why is the argument called utf8-string?  If it's indeed a string
> encoded in UTF-8, why do you need to encode it again with
> raw-text-unix? it should be a no-op in that case.  So please tell more
> about the underlying issue.

I chose the name as a hint to the user that the incoming string should
be UTF-8 encoded. But that is probably misleading since the string
itself doesn't have an encoding? So let's change the function to:

    (defun sieve-manage-encode (str)
      "Convert STR to managesieve protocol octets."
      (encode-coding-string str sieve-manage--coding-system t))

Regarding the potential double encoding: When sending data over the
network connection, `sieve-manage-encode' intends to make sure that
`utf8-string' data is converted to a byte/octet representation. I tried
to explain that in the doc string of `sieve-manage--coding-system':

    (defconst sieve-manage--coding-system 'raw-text-unix
      "Use 'raw-text-unix coding system for (network) communication.
    
    Sets the coding system used for the internal (process, log)
    buffers and the network stream created to communicate with the
    managesieve server.  Using 'raw-text encoding enables unibyte
    mode and makes sure that sent/received octets (bytes) remain
    untouched by the coding system.  The explicit use of `-unix`
    avoids EOL conversions (and thus keeps CRLF (\"\\r\\n\") intact).")

The original problem was that when communicating with the sievemanage
server, we need to handle length elements where we need make sure that
calculated values take into account that UTF-8 characters may comprise
multiple octets.

Even after reading the relevant sections of the documentation multiple
times I was (and am still) not sure what exactly the various coding
system settings do and how they interact with buffers and networking
functions. So forgive me if what I'm doing there looks weird to your
expert eyes.

When working on the original patch, I had several uhoh moments where
data sent to or received from the network seemed to have been
automatically modified by the coding system (unfortunately, I don't
remember the exact details). So I tried to eliminate any such automatic
modifications by using 'binary or 'raw-text encodings on code paths
which handle network data. Basically, my thinking was: 'better do things
twice/thrice/... before introducing new points of failure'.

>> @@ -244,8 +267,7 @@ sieve-manage-open-server
>>                   (open-network-stream
>>                    "SIEVE" buffer server port
>>                    :type stream
>> -                  ;; eol type unix is required to preserve "\r\n"
>> -                  :coding 'raw-text-unix
>> +                  :coding `(binary . ,sieve-manage--coding-system)
>
> Same question about encoding with raw-text-unix here: using it means
> some other code will need to encode the text with a real encoding,
> which in this case is UTF-8 (AFAIU the managesieve protocol RFC).  So
> why not use utf-8-unix here?

Same as above: I'm just not sure that this is the right thing.

But after thinking about it some more, I made the following changes (as
an experiment):

1. set `sieve-manage--coding-system' to 'utf-8-unix and
2. changed the call to `open-network-stream' in the patch above to
>> @@ -244,8 +267,7 @@ sieve-manage-open-server
>>                   (open-network-stream
>>                    "SIEVE" buffer server port
>>                    :type stream
>> -                  ;; eol type unix is required to preserve "\r\n"
>> -                  :coding 'raw-text-unix
>> +                  :coding sieve-manage--coding-system
instead of the previous, asymmetric mix of `binary and
`sieve-manage--coding-system'.

With these changes, there are still no issues when connecting to my
managesieve server which still contains a script with a name that
contains utf-8 multibyte characters. Also, the test I wrote still works
with that change.

So if you think that this makes things clearer, I'm happy to make these
changes. I'm just don't feel confident enough to do this without
additional guidance.

I was also experimenting with some additional changes with the hope to
to just use coding system settings instead of calling
`sieve-manage-encode'/`sieve-manage-decode'. But I couldn't get that to
work.

I added an updated set of Emacs 30.x patches with the changes described
above (plus an additional change in sieve.el which makes sure that the
sieve-manage buffer containing the list of available sieve scripts is
also using coding system 'utf-8-unix).

> Should the addition of BYE support be mentioned in NEWS?

I can certainly do that if you think that this is useful. It just seems
to be more of an internal detail which probably doesn't mean much to
most users.

> On balance, I think the additional patches should go to master,
> indeed.  But let's resolve the issues mentioned above first.

Ok, awaiting further input...



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0001-Fix-bug-in-sieve-manage-append-to-log-improve-sieve-.patch --]
[-- Type: text/x-diff, Size: 5943 bytes --]

From f8d255124777c7da5c029ac7be04d770cd4dc0f3 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <emacs@tetzco.de>
Date: Mon, 28 Feb 2022 11:08:07 +0100
Subject: [PATCH 1/4] Fix bug in sieve-manage--append-to-log, improve
 sieve-manage buffer config

Also update/add some comments/docs.

* lisp/net/sieve-manage.el
(sieve-manage-coding-system-for-read)
(sieve-manage-coding-system-for-write): Remove unused constants.
(sieve-manage-encode): Change misleading argument name (utf8-string -> str).
(sieve-manage--coding-system): New constant.
(sieve-manage--set-internal-buffer-properties): New function.
(sieve-manage-open-server): Use `sieve-manage--coding-system'.
(sieve-manage--append-to-log): Use
`sieve-manage--set-internal-buffer-properties' to fix log buffer creation.
(sieve-manage-encode)
(sieve-manage-make-process-buffer): Use
`sieve-manage--set-internal-buffer-properties'.
---
 lisp/net/sieve-manage.el | 65 +++++++++++++++++++++++++++-------------
 1 file changed, 44 insertions(+), 21 deletions(-)

diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index 5bee4f4c4ad..7721ca139b7 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -58,7 +58,7 @@
 ;;
 ;; References:
 ;;
-;; draft-martin-managesieve-02.txt,
+;; RFC5804,
 ;; "A Protocol for Remotely Managing Sieve Scripts",
 ;; by Tim Martin.
 ;;
@@ -145,6 +145,15 @@ sieve-manage-ignore-starttls
   :type 'boolean)
 
 ;; Internal variables:
+(defconst sieve-manage--coding-system 'utf-8-unix
+  "Use \\='utf-8-unix coding system for (network) communication.
+
+Defines the coding system used for the internal (process, log)
+buffers and the network stream created to communicate with the
+managesieve server.  Using \\='utf-8-unix encoding corresponds to
+the use of UTF-8 in RFC5804 (managesieve).  The explicit use of
+\\='-unix\\=' avoids EOL conversions (and thus keeps CRLF (\"\\r\\n\")
+sequences intact).")
 
 (defconst sieve-manage-local-variables '(sieve-manage-server
 					 sieve-manage-port
@@ -154,8 +163,6 @@ sieve-manage-local-variables
 					 sieve-manage-client-eol
 					 sieve-manage-server-eol
 					 sieve-manage-capability))
-(defconst sieve-manage-coding-system-for-read 'binary)
-(defconst sieve-manage-coding-system-for-write 'binary)
 (defvar sieve-manage-stream nil)
 (defvar sieve-manage-auth nil)
 (defvar sieve-manage-server nil)
@@ -167,18 +174,37 @@ sieve-manage-process
 (defvar sieve-manage-capability nil)
 
 ;; Internal utility functions
+(defun sieve-manage--set-internal-buffer-properties (buffer)
+  "Set BUFFER properties for internally used buffers.
+
+Used for process and log buffers, this function makes sure that
+those buffers keep received and sent data intact by:
+- setting the coding system to 'sieve-manage--coding-system',
+- setting `after-change-functions' to nil to avoid those
+  functions messing with buffer content.
+Also disables undo (to save a bit of memory and improve
+performance).
+
+Returns BUFFER."
+  (with-current-buffer buffer
+    (set-buffer-file-coding-system sieve-manage--coding-system)
+    (setq-local after-change-functions nil)
+    (buffer-disable-undo)
+    (current-buffer)))
+
 (defun sieve-manage--append-to-log (&rest args)
-  "Append ARGS to sieve-manage log buffer.
+  "Append ARGS to `sieve-manage-log' buffer.
 
 ARGS can be a string or a list of strings.
-The buffer to use for logging is specifified via
-`sieve-manage-log'. If it is nil, logging is disabled."
+The buffer to use for logging is specifified via `sieve-manage-log'.
+If it is nil, logging is disabled.
+
+When the `sieve-manage-log' buffer doesn't exist, it gets created (and
+configured with some initial settings)."
   (when sieve-manage-log
     (with-current-buffer (or (get-buffer sieve-manage-log)
-                             (with-current-buffer
-                                 (get-buffer-create sieve-manage-log)
-                               (set-buffer-multibyte nil)
-                               (buffer-disable-undo)))
+                             (sieve-manage--set-internal-buffer-properties
+                              (get-buffer-create sieve-manage-log)))
       (goto-char (point-max))
       (apply #'insert args))))
 
@@ -202,9 +228,9 @@ sieve-manage--error
     (sieve-manage--append-to-log msg "\n")
     (error msg)))
 
-(defun sieve-manage-encode (utf8-string)
-  "Convert UTF8-STRING to managesieve protocol octets."
-  (encode-coding-string utf8-string 'raw-text t))
+(defun sieve-manage-encode (str)
+  "Convert STR to managesieve protocol octets."
+  (encode-coding-string str sieve-manage--coding-system t))
 
 (defun sieve-manage-decode (octets &optional buffer)
   "Convert managesieve protocol OCTETS to utf-8 string.
@@ -216,13 +242,11 @@ sieve-manage-decode
 
 (defun sieve-manage-make-process-buffer ()
   (with-current-buffer
-      (generate-new-buffer (format " *sieve %s:%s*"
-                                   sieve-manage-server
-                                   sieve-manage-port))
+      (sieve-manage--set-internal-buffer-properties
+       (generate-new-buffer (format " *sieve %s:%s*"
+                                    sieve-manage-server
+                                    sieve-manage-port)))
     (mapc #'make-local-variable sieve-manage-local-variables)
-    (set-buffer-multibyte nil)
-    (setq-local after-change-functions nil)
-    (buffer-disable-undo)
     (current-buffer)))
 
 (defun sieve-manage-erase (&optional p buffer)
@@ -244,8 +268,7 @@ sieve-manage-open-server
                  (open-network-stream
                   "SIEVE" buffer server port
                   :type stream
-                  ;; eol type unix is required to preserve "\r\n"
-                  :coding 'raw-text-unix
+                  :coding sieve-manage--coding-system
                   :capability-command "CAPABILITY\r\n"
                   :end-of-command "^\\(OK\\|NO\\).*\n"
                   :success "^OK.*\n"
-- 
2.39.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #5: 0002-Handle-BYE-in-sieve-manage-server-responses.patch --]
[-- Type: text/x-diff, Size: 5769 bytes --]

From 84d6b0cb6559b27b506556aa32894cc6d2752508 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <emacs@tetzco.de>
Date: Thu, 19 Jan 2023 03:52:43 +0100
Subject: [PATCH 2/4] Handle BYE in sieve-manage server responses

* lisp/net/sieve-manage.el
(sieve-manage-regex-oknobye): New function.
(sieve-manage-parse-oknobye): Renamed from `sieve-manage-parse-okno'.
(sieve-manage-open-server)
(sieve-sasl-auth)
(sieve-manage-listscripts)
(sieve-manage-putscript)
(sieve-manage-deletescript)
(sieve-manage-getscript)
(sieve-manage-setactive)
(sieve-manage-is-okno)
(sieve-manage-parse-oknobye)
(sieve-manage-parse-parse-string)
(sieve-manage-parse-parse-crlf): Use `sieve-manage-regex-oknobye' to
handle BYE in addition to OK and NO.
---
 lisp/net/sieve-manage.el | 37 ++++++++++++++++++++-----------------
 1 file changed, 20 insertions(+), 17 deletions(-)

diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index 7721ca139b7..b1a77219c61 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -270,8 +270,8 @@ sieve-manage-open-server
                   :type stream
                   :coding sieve-manage--coding-system
                   :capability-command "CAPABILITY\r\n"
-                  :end-of-command "^\\(OK\\|NO\\).*\n"
-                  :success "^OK.*\n"
+                  :end-of-command "^\\(OK\\|NO\\|BYE\\).*\r\n"
+                  :success "^OK.*\r\n"
                   :return-list t
                   :starttls-function
                   (lambda (capabilities)
@@ -330,7 +330,7 @@ sieve-sasl-auth
                                  (setq rsp nil)
                                (goto-char (match-end 0))
                                rsp))
-                           (setq rsp (sieve-manage-is-okno))))
+                           (setq rsp (sieve-manage-is-oknobye))))
             (accept-process-output sieve-manage-process 1)
             (goto-char (point-min)))
           (sieve-manage-erase)
@@ -499,19 +499,19 @@ sieve-manage-listscripts
 (defun sieve-manage-havespace (name size &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "HAVESPACE \"%s\" %s" name size))
-    (sieve-manage-parse-okno)))
+    (sieve-manage-parse-oknobye)))
 
 (defun sieve-manage-putscript (name content &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
 			       (length (sieve-manage-encode content))
 			       sieve-manage-client-eol content))
-    (sieve-manage-parse-okno)))
+    (sieve-manage-parse-oknobye)))
 
 (defun sieve-manage-deletescript (name &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "DELETESCRIPT \"%s\"" name))
-    (sieve-manage-parse-okno)))
+    (sieve-manage-parse-oknobye)))
 
 (defun sieve-manage-getscript (name output-buffer &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
@@ -519,17 +519,22 @@ sieve-manage-getscript
     (sieve-manage-decode (sieve-manage-parse-string)
                          output-buffer)
     (sieve-manage-parse-crlf)
-    (sieve-manage-parse-okno)))
+    (sieve-manage-parse-oknobye)))
 
 (defun sieve-manage-setactive (name &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "SETACTIVE \"%s\"" name))
-    (sieve-manage-parse-okno)))
+    (sieve-manage-parse-oknobye)))
 
 ;; Protocol parsing routines
+(defun sieve-manage-regexp-oknobye ()
+  "Return regexp for managesieve 'response-oknobye'."
+  (concat
+   "^\\(OK\\|NO\\|BYE\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
+   sieve-manage-server-eol))
 
 (defun sieve-manage-wait-for-answer ()
-  (let ((pattern "^\\(OK\\|NO\\).*\n")
+  (let ((pattern (sieve-manage-regexp-oknobye))
         pos)
     (while (not pos)
       (setq pos (search-forward-regexp pattern nil t))
@@ -547,10 +552,8 @@ sieve-manage-ok-p
 (defun sieve-manage-no-p (rsp)
   (string= (downcase (or (car-safe rsp) "")) "no"))
 
-(defun sieve-manage-is-okno ()
-  (when (looking-at (concat
-		     "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
-		     sieve-manage-server-eol))
+(defun sieve-manage-is-oknobye ()
+  (when (looking-at (sieve-manage-regexp-oknobye))
     (let ((status (match-string 1))
 	  (resp-code (match-string 3))
 	  (response (match-string 5)))
@@ -559,12 +562,12 @@ sieve-manage-is-okno
 	(setq response (sieve-manage-is-string)))
       (list status resp-code response))))
 
-(defun sieve-manage-parse-okno ()
+(defun sieve-manage-parse-oknobye ()
   (let (rsp)
     (while (null rsp)
       (accept-process-output (get-buffer-process (current-buffer)) 1)
       (goto-char (point-min))
-      (setq rsp (sieve-manage-is-okno)))
+      (setq rsp (sieve-manage-is-oknobye)))
     (sieve-manage-erase)
     rsp))
 
@@ -598,7 +601,7 @@ sieve-manage-parse-string
       (accept-process-output (get-buffer-process (current-buffer)) 1)
       (goto-char (point-min))
       (unless (setq rsp (sieve-manage-is-string))
-        (when (sieve-manage-no-p (sieve-manage-is-okno))
+        (when (sieve-manage-no-p (sieve-manage-is-oknobye))
           ;; simple `error' is enough since `sieve-manage-erase'
           ;; already adds the server response to the log
           (error (sieve-manage-erase)))))
@@ -612,7 +615,7 @@ sieve-manage-parse-crlf
 (defun sieve-manage-parse-listscripts ()
   (let (tmp rsp data)
     (while (null rsp)
-      (while (null (or (setq rsp (sieve-manage-is-okno))
+      (while (null (or (setq rsp (sieve-manage-is-oknobye))
                        (setq tmp (sieve-manage-decode
                                   (sieve-manage-is-string)))))
 	(accept-process-output (get-buffer-process (current-buffer)) 1)
-- 
2.39.0


[-- Attachment #6: 0003-Add-test-lisp-net-sieve-manage-tests.el.patch --]
[-- Type: text/x-diff, Size: 5141 bytes --]

From 07ace6346d66862fc84cb3fd1764f6b676558430 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <emacs@tetzco.de>
Date: Tue, 22 Mar 2022 20:48:09 +0100
Subject: [PATCH 3/4] Add test/lisp/net/sieve-manage-tests.el

* test/lisp/net/sieve-manage-tests.el:
(mk-literal-s2c)
(mk-rsp-oknobye)
(mk-rsp-getscript-ok)
(managesieve-getscript): Test helper functions.
(ert/managesieve-getscript-multibyte): Test `sieve-manage-getscript'
with script names containing multibyte characters.
---
 test/lisp/net/sieve-manage-tests.el | 106 ++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)
 create mode 100644 test/lisp/net/sieve-manage-tests.el

diff --git a/test/lisp/net/sieve-manage-tests.el b/test/lisp/net/sieve-manage-tests.el
new file mode 100644
index 00000000000..d4c616b3364
--- /dev/null
+++ b/test/lisp/net/sieve-manage-tests.el
@@ -0,0 +1,106 @@
+;;; sieve-manage-tests.el --- tests for sieve-manage.el       -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Kai Tetzlaff <emacs@tetzco.de>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+
+;;; Code:
+
+(require 'ert)
+(require 'sieve-manage)
+
+(defvar sieve-script-multibyte-unix
+  "if body :matches \"ä\" { stop; }\n"
+  "Simple multibyte sieve script.")
+
+(defun mk-literal-s2c (string)
+  "Encode STRING to managesieve 'literal-s2c'."
+  (let ((data (sieve-manage-encode string)))
+    (concat (format "{%d}\r\n" (length data))
+            data)))
+
+(defun mk-rsp-oknobye (type &optional resp-code string)
+  "Encode TYPE, RESP-CODE and STRING to managesieve 'response-oknobye'."
+  (when (memq type '(OK NO BYE))
+    (concat
+     (mapconcat #'identity
+                (delq nil
+                      (list (symbol-name type)
+                            (when resp-code
+                              (format "(%s)" resp-code))
+                            (when string
+                              (format "\"%s\""
+                                      (sieve-manage-encode string)))))
+                " ")
+     "\r\n")))
+;; (mk-rsp-oknobye 'OK nil "Getscript completed.")
+;; (mk-rsp-oknobye 'NO "TRYLATER" "Server is busy.")
+
+(defun mk-rsp-getscript-ok (script)
+  "Encode SCRIPT to managesieve 'response-getscript'."
+    (concat (mk-literal-s2c script)
+            "\r\n"
+            (mk-rsp-oknobye 'OK "Getscript completed.")))
+;; (mk-rsp-getscript-ok sieve-script-multibyte-unix)
+
+(defun managesieve-getscript (script)
+  "Simulate managesieve getscript response to test
+`sieve-manage-getscript' function."
+  (let* ((script-name "test.sieve")
+         ;; `sieve-manage-server' and `sieve-manage-port' are used in
+         ;; `sieve-manage-make-process-buffer'
+         (sieve-manage-server)
+         (sieve-manage-port "sieve")
+         (sieve-buffer (sieve-manage-make-process-buffer))
+         (output-buffer (generate-new-buffer script-name)))
+    ;; use cl-letf to mock some functions in call chain of
+    ;; sieve-manage-getscript
+    (cl-letf (((symbol-function 'sieve-manage-send)
+               ;; simulate sieve server response with a single
+               ;; multibyte character `ä`
+               (lambda (_)
+                 (with-current-buffer sieve-buffer
+                   (goto-char (point-min))
+                   (insert (mk-rsp-getscript-ok script)))))
+              ((symbol-function 'accept-process-output)
+               (lambda (&optional _ _ _ _) nil))
+              ((symbol-function 'get-buffer-process) (lambda (_) nil)))
+      ;; extract sieve script from sieve-buffer and put it into
+      ;; output-buffer
+      (sieve-manage-getscript script-name output-buffer sieve-buffer)
+      ;; extract script from output-buffer and return it as a string
+      (let ((script (with-current-buffer output-buffer
+                      (set-buffer-modified-p nil)
+                      (buffer-string))))
+        ;; cleanup
+        (kill-buffer sieve-buffer)
+        (kill-buffer output-buffer)
+        (when (get-buffer sieve-manage-log)
+          (kill-buffer sieve-manage-log))
+        ;; return script
+        script))))
+
+(ert-deftest ert/managesieve-getscript-multibyte ()
+  (should (string= sieve-script-multibyte-unix
+                   (managesieve-getscript sieve-script-multibyte-unix))))
+
+
+;;; sieve-manage-tests.el ends here
-- 
2.39.0


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #7: 0004-Some-minor-fixes-in-lisp-net-sieve.el.patch --]
[-- Type: text/x-diff, Size: 3759 bytes --]

From 4dde8d5a544c94ebc3f6001e46dca484d3632902 Mon Sep 17 00:00:00 2001
From: Kai Tetzlaff <emacs@tetzco.de>
Date: Thu, 19 Jan 2023 04:06:22 +0100
Subject: [PATCH 4/4] Some minor fixes in lisp/net/sieve.el

* lisp/net/sieve.el
(sieve-next-line)
(sieve-prev-line): Handle situations where point in `sieve-buffer'
is either before or after the list of server side scripts.
(sieve-server-script-list): New variable.
(sieve-refresh-scriptlist)
(sieve-upload): Use `sieve-server-script-list' to make sure that local
list of server side scripts in`sieve-buffer' is up-to-date after
uploading a (new) script.
(sieve-edit-script): Make sure that the newly created user facing
sieve-manage buffer is using 'utf-8-unix encoding.
---
 lisp/net/sieve.el | 40 +++++++++++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 13 deletions(-)

diff --git a/lisp/net/sieve.el b/lisp/net/sieve.el
index fbd07dee27c..1e5aae7825c 100644
--- a/lisp/net/sieve.el
+++ b/lisp/net/sieve.el
@@ -95,6 +95,8 @@ sieve-template
 
 (defvar sieve-manage-buffer nil)
 (defvar sieve-buffer-header-end nil)
+(defvar sieve-server-script-list nil
+  "Current list of server-side sieve scripts.")
 (defvar sieve-buffer-script-name nil
   "The real script name of the buffer.")
 (make-local-variable 'sieve-buffer-script-name)
@@ -204,6 +206,8 @@ sieve-edit-script
     (if (not (string-equal name sieve-new-script))
 	(let ((newbuf (generate-new-buffer name))
 	      err)
+          (with-current-buffer newbuf
+            (set-buffer-file-coding-system sieve-manage--coding-system))
 	  (setq err (sieve-manage-getscript name newbuf sieve-manage-buffer))
 	  (switch-to-buffer newbuf)
 	  (if (sieve-manage-ok-p err)
@@ -235,23 +239,28 @@ sieve-next-line
   (interactive)
   (unless arg
     (setq arg 1))
-  (if (save-excursion
-	(forward-line arg)
-	(sieve-script-at-point))
-      (sieve-change-region
-	(forward-line arg))
-    (message "End of list")))
+  (if (sieve-script-at-point)
+      (if (save-excursion
+	    (forward-line arg)
+	    (sieve-script-at-point))
+          (sieve-change-region
+	    (forward-line arg))
+        (message "End of list"))
+    (goto-char (next-overlay-change sieve-buffer-header-end))))
 
 (defun sieve-prev-line (&optional arg)
   (interactive)
   (unless arg
     (setq arg -1))
-  (if (save-excursion
-	(forward-line arg)
-	(sieve-script-at-point))
-      (sieve-change-region
-	(forward-line arg))
-    (message "Beginning of list")))
+  (if (sieve-script-at-point)
+      (if (save-excursion
+	    (forward-line arg)
+	    (sieve-script-at-point))
+          (sieve-change-region
+	    (forward-line arg))
+        (message "Beginning of list"))
+    (goto-char (previous-overlay-change (point-max)))
+    (beginning-of-line)))
 
 (defun sieve-help ()
   "Display help for various sieve commands."
@@ -319,6 +328,9 @@ sieve-refresh-scriptlist
     (let* ((scripts (sieve-manage-listscripts sieve-manage-buffer))
            (count (length scripts))
            (keys (substitute-command-keys "\\[sieve-edit-script]")))
+      (setq sieve-server-script-list
+            (mapcar (lambda (elt) (if (consp elt) (cdr elt) elt))
+                    scripts))
       (insert
        (if (null scripts)
            (format
@@ -361,7 +373,9 @@ sieve-upload
         (if (not (sieve-manage-ok-p err))
             (message "Sieve upload failed: %s" (nth 2 err))
           (message "Sieve upload done.  Use %s to manage scripts."
-                   (substitute-command-keys "\\[sieve-manage]"))))
+                   (substitute-command-keys "\\[sieve-manage]"))
+          (when (not (member script-name sieve-server-script-list))
+            (sieve-refresh-scriptlist))))
       (set-buffer-modified-p nil))))
 
 ;;;###autoload
-- 
2.39.0


  reply	other threads:[~2023-01-19 12:38 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-02-25  9:04 bug#54154: 29.0.50; [PATCH] `sieve-manage-getscript' fails if script contains multibyte characters Kai Tetzlaff
2022-02-25 12:19 ` Lars Ingebrigtsen
2022-02-25 13:10   ` Lars Ingebrigtsen
2022-02-25 16:00     ` Kai Tetzlaff
2022-02-26 15:07       ` Lars Ingebrigtsen
2022-02-28 12:27         ` Kai Tetzlaff
2022-09-06 11:34           ` Lars Ingebrigtsen
2022-02-28 12:35         ` Kai Tetzlaff
2022-02-28 13:06           ` Lars Ingebrigtsen
2022-02-28 13:08           ` Lars Ingebrigtsen
2022-02-28 13:03         ` Kai Tetzlaff
     [not found] ` <87bkmwi0ut.fsf@tetzco.de>
     [not found]   ` <87fsc8i2c5.fsf@tetzco.de>
     [not found]     ` <87bkmw8b02.fsf@tetzco.de>
2023-01-18 18:28       ` bug#54154: Emacs commit ae963e80a79f5a9184daabfc8197f211a39b136d (sieve-manage) Herbert J. Skuhra
2023-01-18 19:17         ` Eli Zaretskii
2023-01-18 23:22           ` Herbert J. Skuhra
2023-01-19  4:06             ` Kai Tetzlaff
2023-01-19  7:45               ` Eli Zaretskii
2023-01-19 12:38                 ` Kai Tetzlaff [this message]
2023-01-19 14:08                   ` Eli Zaretskii
2023-01-19 15:59                     ` Kai Tetzlaff
2023-01-19 17:41                       ` Eli Zaretskii
2023-01-19 21:33                         ` Kai Tetzlaff
2023-01-20  6:54                         ` Kai Tetzlaff
2023-01-22  2:12                         ` Kai Tetzlaff
2023-01-23  0:59                         ` Kai Tetzlaff
2023-01-23 12:47                           ` Herbert J. Skuhra
2023-01-23 13:01                             ` Kai Tetzlaff
2023-01-23 13:36                               ` Herbert J. Skuhra
2023-01-23 13:57                                 ` Kai Tetzlaff
2023-01-23 14:27                                   ` Andreas Schwab
2023-01-23 17:07                                     ` Kai Tetzlaff
2023-01-23 17:53                                       ` Andreas Schwab
2023-01-23 13:40                               ` Eli Zaretskii
2023-01-23 16:22                                 ` Kai Tetzlaff
2023-01-23 16:49                                   ` Eli Zaretskii
2023-01-23 17:12                                     ` Kai Tetzlaff
2023-01-19 13:22                 ` Kai Tetzlaff
2023-01-19 14:16                 ` Kai Tetzlaff
2023-01-19  4:50             ` bug#54154: [update] " Kai Tetzlaff

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=873586d7ii.fsf@tetzco.de \
    --to=emacs+bug@tetzco.de \
    --cc=54154@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    --cc=herbert@gojira.at \
    --cc=larsi@gnus.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).