aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Tetzlaff2022-02-28 11:08:07 +0100
committerLars Ingebrigtsen2022-09-06 13:33:48 +0200
commitae963e80a79f5a9184daabfc8197f211a39b136d (patch)
treebea2e9347427e3be97f663c6eadd2164aa7f5535
parent46963d0bc9058caeb8241abe34b1552bd83e097d (diff)
downloademacs-ae963e80a79f5a9184daabfc8197f211a39b136d.tar.gz
emacs-ae963e80a79f5a9184daabfc8197f211a39b136d.zip
Fix (mostly multibyte) issues in sieve-manage.el (Bug#54154)
The managesieve protocol (s. RFC5804) requires support for (a sightly restricted variant of) UTF-8 in script content and script names. This commit fixes/improves the handling of multibyte characters. In addition, `sieve-manage-getscript' now properly handles NO responses from the server instead of inflooping. There are also some logging improvements. * lisp/net/sieve-manage.el (sieve-manage--append-to-log): (sieve-manage--message): (sieve-manage--error): (sieve-manage-encode): (sieve-manage-decode): (sieve-manage-no-p): New functions. (sieve-manage-make-process-buffer): Switch process buffer to unibyte. (sieve-manage-open-server): Add `:coding 'raw-text-unix` to `open-network-stream' call. Use unix EOLs in order to keep matching CRLF (aka "\r\n") intact. (sieve-manage-send): Make sure that UTF-8 multibyte characters are properly encoded before sending data to the server. (sieve-manage-getscript): (sieve-manage-putscript): Use the changes above to fix down/uploading scripts containing UTF-8 multibyte characters. (sieve-manage-listscripts): (sieve-manage-havespace) (sieve-manage-getscript) (sieve-manage-putscript): (sieve-manage-deletescript): (sieve-manage-setactive): Use the changes above to fix handling of script names which contain UTF-8 multibyte characters. (sieve-manage-parse-string): (sieve-manage-getscript): Add handling of server responses with type NO. Abort `sieve-manage-getscript' and show error message in message area. (sieve-manage-erase): (sieve-manage-drop-next-answer): (sieve-manage-parse-crlf): Return erased/dropped data (instead of nil). (sieve-sasl-auth): (sieve-manage-getscript): (sieve-manage-erase): (sieve-manage-open-server): (sieve-manage-open): (sieve-manage-send): Improve logging.
-rw-r--r--lisp/net/sieve-manage.el125
1 files changed, 86 insertions, 39 deletions
diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index a39e35a53a1..381e1fcd4f8 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -167,7 +167,52 @@ Valid states are `closed', `initial', `nonauth', and `auth'.")
167(defvar sieve-manage-capability nil) 167(defvar sieve-manage-capability nil)
168 168
169;; Internal utility functions 169;; Internal utility functions
170(autoload 'mm-enable-multibyte "mm-util") 170(defun sieve-manage--append-to-log (&rest args)
171 "Append ARGS to sieve-manage log buffer.
172
173ARGS can be a string or a list of strings.
174The buffer to use for logging is specifified via
175`sieve-manage-log'. If it is nil, logging is disabled."
176 (when sieve-manage-log
177 (with-current-buffer (or (get-buffer sieve-manage-log)
178 (with-current-buffer
179 (get-buffer-create sieve-manage-log)
180 (set-buffer-multibyte nil)
181 (buffer-disable-undo)))
182 (goto-char (point-max))
183 (apply #'insert args))))
184
185(defun sieve-manage--message (format-string &rest args)
186 "Wrapper around `message' which also logs to sieve manage log.
187
188See `sieve-manage--append-to-log'."
189 (let ((ret (apply #'message
190 (concat "sieve-manage: " format-string)
191 args)))
192 (sieve-manage--append-to-log ret "\n")
193 ret))
194
195(defun sieve-manage--error (format-string &rest args)
196 "Wrapper around `error' which also logs to sieve manage log.
197
198See `sieve-manage--append-to-log'."
199 (let ((msg (apply #'format
200 (concat "sieve-manage/ERROR: " format-string)
201 args)))
202 (sieve-manage--append-to-log msg "\n")
203 (error msg)))
204
205(defun sieve-manage-encode (utf8-string)
206 "Convert UTF8-STRING to managesieve protocol octets."
207 (encode-coding-string utf8-string 'raw-text t))
208
209(defun sieve-manage-decode (octets &optional buffer)
210 "Convert managesieve protocol OCTETS to utf-8 string.
211
212If optional BUFFER is non-nil, insert decoded string into BUFFER."
213 (when octets
214 ;; eol type unix is required to preserve "\r\n"
215 (decode-coding-string octets 'utf-8-unix t buffer)))
171 216
172(defun sieve-manage-make-process-buffer () 217(defun sieve-manage-make-process-buffer ()
173 (with-current-buffer 218 (with-current-buffer
@@ -175,22 +220,19 @@ Valid states are `closed', `initial', `nonauth', and `auth'.")
175 sieve-manage-server 220 sieve-manage-server
176 sieve-manage-port)) 221 sieve-manage-port))
177 (mapc #'make-local-variable sieve-manage-local-variables) 222 (mapc #'make-local-variable sieve-manage-local-variables)
178 (mm-enable-multibyte) 223 (set-buffer-multibyte nil)
224 (setq-local after-change-functions nil)
179 (buffer-disable-undo) 225 (buffer-disable-undo)
180 (current-buffer))) 226 (current-buffer)))
181 227
182(defun sieve-manage-erase (&optional p buffer) 228(defun sieve-manage-erase (&optional p buffer)
183 (let ((buffer (or buffer (current-buffer)))) 229 (with-current-buffer (or buffer (current-buffer))
184 (and sieve-manage-log 230 (let* ((start (point-min))
185 (with-current-buffer (get-buffer-create sieve-manage-log) 231 (end (or p (point-max)))
186 (mm-enable-multibyte) 232 (logdata (buffer-substring-no-properties start end)))
187 (buffer-disable-undo) 233 (sieve-manage--append-to-log logdata)
188 (goto-char (point-max)) 234 (delete-region start end)
189 (insert-buffer-substring buffer (with-current-buffer buffer 235 logdata)))
190 (point-min))
191 (or p (with-current-buffer buffer
192 (point-max)))))))
193 (delete-region (point-min) (or p (point-max))))
194 236
195(defun sieve-manage-open-server (server port &optional stream buffer) 237(defun sieve-manage-open-server (server port &optional stream buffer)
196 "Open network connection to SERVER on PORT. 238 "Open network connection to SERVER on PORT.
@@ -202,6 +244,8 @@ Return the buffer associated with the connection."
202 (open-network-stream 244 (open-network-stream
203 "SIEVE" buffer server port 245 "SIEVE" buffer server port
204 :type stream 246 :type stream
247 ;; eol type unix is required to preserve "\r\n"
248 :coding 'raw-text-unix
205 :capability-command "CAPABILITY\r\n" 249 :capability-command "CAPABILITY\r\n"
206 :end-of-command "^\\(OK\\|NO\\).*\n" 250 :end-of-command "^\\(OK\\|NO\\).*\n"
207 :success "^OK.*\n" 251 :success "^OK.*\n"
@@ -224,7 +268,7 @@ Return the buffer associated with the connection."
224;; Authenticators 268;; Authenticators
225(defun sieve-sasl-auth (buffer mech) 269(defun sieve-sasl-auth (buffer mech)
226 "Login to server using the SASL MECH method." 270 "Login to server using the SASL MECH method."
227 (message "sieve: Authenticating using %s..." mech) 271 (sieve-manage--message "Authenticating using %s..." mech)
228 (with-current-buffer buffer 272 (with-current-buffer buffer
229 (let* ((auth-info (auth-source-search :host sieve-manage-server 273 (let* ((auth-info (auth-source-search :host sieve-manage-server
230 :port "sieve" 274 :port "sieve"
@@ -275,11 +319,15 @@ Return the buffer associated with the connection."
275 (if (and (setq step (sasl-next-step client step)) 319 (if (and (setq step (sasl-next-step client step))
276 (setq data (sasl-step-data step))) 320 (setq data (sasl-step-data step)))
277 ;; We got data for server but it's finished 321 ;; We got data for server but it's finished
278 (error "Server not ready for SASL data: %s" data) 322 (sieve-manage--error
323 "Server not ready for SASL data: %s" data)
279 ;; The authentication process is finished. 324 ;; The authentication process is finished.
325 (sieve-manage--message "Logged in as %s using %s"
326 user-name mech)
280 (throw 'done t))) 327 (throw 'done t)))
281 (unless (stringp rsp) 328 (unless (stringp rsp)
282 (error "Server aborted SASL authentication: %s" (caddr rsp))) 329 (sieve-manage--error
330 "Server aborted SASL authentication: %s" (caddr rsp)))
283 (sasl-step-set-data step (base64-decode-string rsp)) 331 (sasl-step-set-data step (base64-decode-string rsp))
284 (setq step (sasl-next-step client step)) 332 (setq step (sasl-next-step client step))
285 (sieve-manage-send 333 (sieve-manage-send
@@ -288,8 +336,7 @@ Return the buffer associated with the connection."
288 (base64-encode-string (sasl-step-data step) 336 (base64-encode-string (sasl-step-data step)
289 'no-line-break) 337 'no-line-break)
290 "\"") 338 "\"")
291 "")))) 339 "")))))))
292 (message "sieve: Login using %s...done" mech))))
293 340
294(defun sieve-manage-cram-md5-p (buffer) 341(defun sieve-manage-cram-md5-p (buffer)
295 (sieve-manage-capability "SASL" "CRAM-MD5" buffer)) 342 (sieve-manage-capability "SASL" "CRAM-MD5" buffer))
@@ -353,7 +400,7 @@ to work in."
353 sieve-manage-default-stream) 400 sieve-manage-default-stream)
354 sieve-manage-auth (or auth 401 sieve-manage-auth (or auth
355 sieve-manage-auth)) 402 sieve-manage-auth))
356 (message "sieve: Connecting to %s..." sieve-manage-server) 403 (sieve-manage--message "Connecting to %s..." sieve-manage-server)
357 (sieve-manage-open-server sieve-manage-server 404 (sieve-manage-open-server sieve-manage-server
358 sieve-manage-port 405 sieve-manage-port
359 sieve-manage-stream 406 sieve-manage-stream
@@ -368,7 +415,8 @@ to work in."
368 (setq sieve-manage-auth auth) 415 (setq sieve-manage-auth auth)
369 (cl-return))) 416 (cl-return)))
370 (unless sieve-manage-auth 417 (unless sieve-manage-auth
371 (error "Couldn't figure out authenticator for server"))) 418 (sieve-manage--error
419 "Couldn't figure out authenticator for server")))
372 (sieve-manage-erase) 420 (sieve-manage-erase)
373 (current-buffer)))) 421 (current-buffer))))
374 422
@@ -433,11 +481,7 @@ If NAME is nil, return the full server list of capabilities."
433(defun sieve-manage-putscript (name content &optional buffer) 481(defun sieve-manage-putscript (name content &optional buffer)
434 (with-current-buffer (or buffer (current-buffer)) 482 (with-current-buffer (or buffer (current-buffer))
435 (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name 483 (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
436 ;; Here we assume that the coding-system will 484 (length (sieve-manage-encode content))
437 ;; replace each char with a single byte.
438 ;; This is always the case if `content' is
439 ;; a unibyte string.
440 (length content)
441 sieve-manage-client-eol content)) 485 sieve-manage-client-eol content))
442 (sieve-manage-parse-okno))) 486 (sieve-manage-parse-okno)))
443 487
@@ -449,11 +493,10 @@ If NAME is nil, return the full server list of capabilities."
449(defun sieve-manage-getscript (name output-buffer &optional buffer) 493(defun sieve-manage-getscript (name output-buffer &optional buffer)
450 (with-current-buffer (or buffer (current-buffer)) 494 (with-current-buffer (or buffer (current-buffer))
451 (sieve-manage-send (format "GETSCRIPT \"%s\"" name)) 495 (sieve-manage-send (format "GETSCRIPT \"%s\"" name))
452 (let ((script (sieve-manage-parse-string))) 496 (sieve-manage-decode (sieve-manage-parse-string)
453 (sieve-manage-parse-crlf) 497 output-buffer)
454 (with-current-buffer output-buffer 498 (sieve-manage-parse-crlf)
455 (insert script)) 499 (sieve-manage-parse-okno)))
456 (sieve-manage-parse-okno))))
457 500
458(defun sieve-manage-setactive (name &optional buffer) 501(defun sieve-manage-setactive (name &optional buffer)
459 (with-current-buffer (or buffer (current-buffer)) 502 (with-current-buffer (or buffer (current-buffer))
@@ -478,6 +521,9 @@ If NAME is nil, return the full server list of capabilities."
478(defun sieve-manage-ok-p (rsp) 521(defun sieve-manage-ok-p (rsp)
479 (string= (downcase (or (car-safe rsp) "")) "ok")) 522 (string= (downcase (or (car-safe rsp) "")) "ok"))
480 523
524(defun sieve-manage-no-p (rsp)
525 (string= (downcase (or (car-safe rsp) "")) "no"))
526
481(defun sieve-manage-is-okno () 527(defun sieve-manage-is-okno ()
482 (when (looking-at (concat 528 (when (looking-at (concat
483 "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?" 529 "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
@@ -528,7 +574,11 @@ to local variable `sieve-manage-capability'."
528 (while (null rsp) 574 (while (null rsp)
529 (accept-process-output (get-buffer-process (current-buffer)) 1) 575 (accept-process-output (get-buffer-process (current-buffer)) 1)
530 (goto-char (point-min)) 576 (goto-char (point-min))
531 (setq rsp (sieve-manage-is-string))) 577 (unless (setq rsp (sieve-manage-is-string))
578 (when (sieve-manage-no-p (sieve-manage-is-okno))
579 ;; simple `error' is enough since `sieve-manage-erase'
580 ;; already adds the server response to the log
581 (error (sieve-manage-erase)))))
532 (sieve-manage-erase (point)) 582 (sieve-manage-erase (point))
533 rsp)) 583 rsp))
534 584
@@ -540,7 +590,8 @@ to local variable `sieve-manage-capability'."
540 (let (tmp rsp data) 590 (let (tmp rsp data)
541 (while (null rsp) 591 (while (null rsp)
542 (while (null (or (setq rsp (sieve-manage-is-okno)) 592 (while (null (or (setq rsp (sieve-manage-is-okno))
543 (setq tmp (sieve-manage-is-string)))) 593 (setq tmp (sieve-manage-decode
594 (sieve-manage-is-string)))))
544 (accept-process-output (get-buffer-process (current-buffer)) 1) 595 (accept-process-output (get-buffer-process (current-buffer)) 1)
545 (goto-char (point-min))) 596 (goto-char (point-min)))
546 (when tmp 597 (when tmp
@@ -559,13 +610,9 @@ to local variable `sieve-manage-capability'."
559 rsp))) 610 rsp)))
560 611
561(defun sieve-manage-send (cmdstr) 612(defun sieve-manage-send (cmdstr)
562 (setq cmdstr (concat cmdstr sieve-manage-client-eol)) 613 (setq cmdstr (sieve-manage-encode
563 (and sieve-manage-log 614 (concat cmdstr sieve-manage-client-eol)))
564 (with-current-buffer (get-buffer-create sieve-manage-log) 615 (sieve-manage--append-to-log cmdstr)
565 (mm-enable-multibyte)
566 (buffer-disable-undo)
567 (goto-char (point-max))
568 (insert cmdstr)))
569 (process-send-string sieve-manage-process cmdstr)) 616 (process-send-string sieve-manage-process cmdstr))
570 617
571(provide 'sieve-manage) 618(provide 'sieve-manage)