Merge from emacs-24, up to 2012-04-10T02:06:19Z!larsi@gnus.org
[bpt/emacs.git] / lisp / gnus / sieve-manage.el
CommitLineData
53964682 1;;; sieve-manage.el --- Implementation of the managesieve protocol in elisp
e84b4b86 2
acaf905b 3;; Copyright (C) 2001-2012 Free Software Foundation, Inc.
23f87bed
MB
4
5;; Author: Simon Josefsson <simon@josefsson.org>
6
7;; This file is part of GNU Emacs.
8
5e809f55 9;; GNU Emacs is free software: you can redistribute it and/or modify
23f87bed 10;; it under the terms of the GNU General Public License as published by
5e809f55
GM
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
23f87bed
MB
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
5e809f55 16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23f87bed
MB
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
5e809f55 20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23f87bed
MB
21
22;;; Commentary:
23
24;; This library provides an elisp API for the managesieve network
25;; protocol.
26;;
01c52d31
MB
27;; It uses the SASL library for authentication, which means it
28;; supports DIGEST-MD5, CRAM-MD5, SCRAM-MD5, NTLM, PLAIN and LOGIN
29;; methods. STARTTLS is not well tested, but should be easy to get to
30;; work if someone wants.
23f87bed
MB
31;;
32;; The API should be fairly obvious for anyone familiar with the
33;; managesieve protocol, interface functions include:
34;;
35;; `sieve-manage-open'
36;; open connection to managesieve server, returning a buffer to be
37;; used by all other API functions.
38;;
39;; `sieve-manage-opened'
40;; check if a server is open or not
41;;
42;; `sieve-manage-close'
43;; close a server connection.
44;;
23f87bed
MB
45;; `sieve-manage-listscripts'
46;; `sieve-manage-deletescript'
47;; `sieve-manage-getscript'
48;; performs managesieve protocol actions
49;;
50;; and that's it. Example of a managesieve session in *scratch*:
51;;
fd9ba500
JD
52;; (with-current-buffer (sieve-manage-open "mail.example.com")
53;; (sieve-manage-authenticate)
54;; (sieve-manage-listscripts))
23f87bed 55;;
fd9ba500 56;; => ((active . "main") "vacation")
23f87bed
MB
57;;
58;; References:
59;;
60;; draft-martin-managesieve-02.txt,
61;; "A Protocol for Remotely Managing Sieve Scripts",
62;; by Tim Martin.
63;;
64;; Release history:
65;;
66;; 2001-10-31 Committed to Oort Gnus.
67;; 2002-07-27 Added DELETESCRIPT. Suggested by Ned Ludd.
01c52d31 68;; 2002-08-03 Use SASL library.
23f87bed
MB
69
70;;; Code:
71
f0b7f5a8 72;; For Emacs <22.2 and XEmacs.
1d1df709
GM
73(eval-and-compile
74 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
87035689
MB
75
76(if (locate-library "password-cache")
77 (require 'password-cache)
78 (require 'password))
79
01c52d31 80(eval-when-compile
1d8ff0c6 81 (require 'cl) ; caddr
01c52d31
MB
82 (require 'sasl)
83 (require 'starttls))
8abf1b22
GM
84(autoload 'sasl-find-mechanism "sasl")
85(autoload 'starttls-open-stream "starttls")
b8e0f0cd 86(autoload 'auth-source-search "auth-source")
23f87bed
MB
87
88;; User customizable variables:
89
90(defgroup sieve-manage nil
91 "Low-level Managesieve protocol issues."
92 :group 'mail
93 :prefix "sieve-")
94
95(defcustom sieve-manage-log "*sieve-manage-log*"
96 "Name of buffer for managesieve session trace."
d0859c9a
MB
97 :type 'string
98 :group 'sieve-manage)
23f87bed 99
23f87bed
MB
100(defcustom sieve-manage-server-eol "\r\n"
101 "The EOL string sent from the server."
d0859c9a
MB
102 :type 'string
103 :group 'sieve-manage)
23f87bed
MB
104
105(defcustom sieve-manage-client-eol "\r\n"
106 "The EOL string we send to the server."
d0859c9a
MB
107 :type 'string
108 :group 'sieve-manage)
23f87bed
MB
109
110(defcustom sieve-manage-streams '(network starttls shell)
d0859c9a
MB
111 "Priority of streams to consider when opening connection to server."
112 :group 'sieve-manage)
23f87bed
MB
113
114(defcustom sieve-manage-stream-alist
115 '((network sieve-manage-network-p sieve-manage-network-open)
116 (shell sieve-manage-shell-p sieve-manage-shell-open)
117 (starttls sieve-manage-starttls-p sieve-manage-starttls-open))
118 "Definition of network streams.
119
120\(NAME CHECK OPEN)
121
122NAME names the stream, CHECK is a function returning non-nil if the
123server support the stream and OPEN is a function for opening the
d0859c9a
MB
124stream."
125 :group 'sieve-manage)
23f87bed 126
01c52d31
MB
127(defcustom sieve-manage-authenticators '(digest-md5
128 cram-md5
129 scram-md5
130 ntlm
131 plain
132 login)
d0859c9a
MB
133 "Priority of authenticators to consider when authenticating to server."
134 :group 'sieve-manage)
23f87bed
MB
135
136(defcustom sieve-manage-authenticator-alist
137 '((cram-md5 sieve-manage-cram-md5-p sieve-manage-cram-md5-auth)
01c52d31
MB
138 (digest-md5 sieve-manage-digest-md5-p sieve-manage-digest-md5-auth)
139 (scram-md5 sieve-manage-scram-md5-p sieve-manage-scram-md5-auth)
140 (ntlm sieve-manage-ntlm-p sieve-manage-ntlm-auth)
141 (plain sieve-manage-plain-p sieve-manage-plain-auth)
142 (login sieve-manage-login-p sieve-manage-login-auth))
23f87bed
MB
143 "Definition of authenticators.
144
145\(NAME CHECK AUTHENTICATE)
146
147NAME names the authenticator. CHECK is a function returning non-nil if
148the server support the authenticator and AUTHENTICATE is a function
d0859c9a
MB
149for doing the actual authentication."
150 :group 'sieve-manage)
23f87bed
MB
151
152(defcustom sieve-manage-default-port 2000
6b7df8d3 153 "Default port number or service name for managesieve protocol."
d0859c9a
MB
154 :type 'integer
155 :group 'sieve-manage)
23f87bed 156
562f5ce5
G
157(defcustom sieve-manage-default-stream 'network
158 "Default stream type to use for `sieve-manage'.
159Must be a name of a stream in `sieve-manage-stream-alist'."
2bed3f04 160 :version "24.1"
562f5ce5
G
161 :type 'symbol
162 :group 'sieve-manage)
163
23f87bed
MB
164;; Internal variables:
165
166(defconst sieve-manage-local-variables '(sieve-manage-server
167 sieve-manage-port
168 sieve-manage-auth
169 sieve-manage-stream
23f87bed
MB
170 sieve-manage-process
171 sieve-manage-client-eol
172 sieve-manage-server-eol
173 sieve-manage-capability))
23f87bed
MB
174(defconst sieve-manage-coding-system-for-read 'binary)
175(defconst sieve-manage-coding-system-for-write 'binary)
176(defvar sieve-manage-stream nil)
177(defvar sieve-manage-auth nil)
178(defvar sieve-manage-server nil)
179(defvar sieve-manage-port nil)
23f87bed
MB
180(defvar sieve-manage-state 'closed
181 "Managesieve state.
182Valid states are `closed', `initial', `nonauth', and `auth'.")
183(defvar sieve-manage-process nil)
184(defvar sieve-manage-capability nil)
185
186;; Internal utility functions
187
765d4319 188(defmacro sieve-manage-disable-multibyte ()
23f87bed 189 "Enable multibyte in the current buffer."
765d4319
KY
190 (unless (featurep 'xemacs)
191 '(set-buffer-multibyte nil)))
23f87bed 192
23f87bed
MB
193(defun sieve-manage-erase (&optional p buffer)
194 (let ((buffer (or buffer (current-buffer))))
195 (and sieve-manage-log
196 (with-current-buffer (get-buffer-create sieve-manage-log)
197 (sieve-manage-disable-multibyte)
198 (buffer-disable-undo)
199 (goto-char (point-max))
200 (insert-buffer-substring buffer (with-current-buffer buffer
201 (point-min))
202 (or p (with-current-buffer buffer
203 (point-max)))))))
204 (delete-region (point-min) (or p (point-max))))
205
206(defun sieve-manage-open-1 (buffer)
207 (with-current-buffer buffer
208 (sieve-manage-erase)
209 (setq sieve-manage-state 'initial
210 sieve-manage-process
211 (condition-case ()
212 (funcall (nth 2 (assq sieve-manage-stream
213 sieve-manage-stream-alist))
214 "sieve" buffer sieve-manage-server sieve-manage-port)
215 ((error quit) nil)))
216 (when sieve-manage-process
217 (while (and (eq sieve-manage-state 'initial)
218 (memq (process-status sieve-manage-process) '(open run)))
219 (message "Waiting for response from %s..." sieve-manage-server)
220 (accept-process-output sieve-manage-process 1))
221 (message "Waiting for response from %s...done" sieve-manage-server)
222 (and (memq (process-status sieve-manage-process) '(open run))
223 sieve-manage-process))))
224
225;; Streams
226
227(defun sieve-manage-network-p (buffer)
228 t)
229
230(defun sieve-manage-network-open (name buffer server port)
231 (let* ((port (or port sieve-manage-default-port))
232 (coding-system-for-read sieve-manage-coding-system-for-read)
233 (coding-system-for-write sieve-manage-coding-system-for-write)
234 (process (open-network-stream name buffer server port)))
235 (when process
236 (while (and (memq (process-status process) '(open run))
237 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
238 (goto-char (point-min))
239 (not (sieve-manage-parse-greeting-1)))
240 (accept-process-output process 1)
241 (sit-for 1))
242 (sieve-manage-erase nil buffer)
243 (when (memq (process-status process) '(open run))
244 process))))
245
e3e955fe 246(defun sieve-manage-starttls-p (buffer)
23f87bed
MB
247 (condition-case ()
248 (progn
249 (require 'starttls)
250 (call-process "starttls"))
251 (error nil)))
252
e3e955fe 253(defun sieve-manage-starttls-open (name buffer server port)
23f87bed
MB
254 (let* ((port (or port sieve-manage-default-port))
255 (coding-system-for-read sieve-manage-coding-system-for-read)
256 (coding-system-for-write sieve-manage-coding-system-for-write)
257 (process (starttls-open-stream name buffer server port))
258 done)
259 (when process
260 (while (and (memq (process-status process) '(open run))
261 (set-buffer buffer) ;; XXX "blue moon" nntp.el bug
262 (goto-char (point-min))
263 (not (sieve-manage-parse-greeting-1)))
264 (accept-process-output process 1)
265 (sit-for 1))
266 (sieve-manage-erase nil buffer)
267 (sieve-manage-send "STARTTLS")
268 (starttls-negotiate process))
269 (when (memq (process-status process) '(open run))
270 process)))
271
272;; Authenticators
01c52d31
MB
273(defun sieve-sasl-auth (buffer mech)
274 "Login to server using the SASL MECH method."
275 (message "sieve: Authenticating using %s..." mech)
1d8e1f78 276 (with-current-buffer buffer
b8e0f0cd
G
277 (let* ((auth-info (auth-source-search :host sieve-manage-server
278 :port "sieve"
a5057546
G
279 :max 1
280 :create t))
281 (user-name (or (plist-get (nth 0 auth-info) :user) ""))
282 (user-password (or (plist-get (nth 0 auth-info) :secret) ""))
b8e0f0cd
G
283 (user-password (if (functionp user-password)
284 (funcall user-password)
285 user-password))
1d8e1f78 286 (client (sasl-make-client (sasl-find-mechanism (list mech))
b8e0f0cd 287 user-name "sieve" sieve-manage-server))
1d8e1f78
JD
288 (sasl-read-passphrase
289 ;; We *need* to copy the password, because sasl will modify it
290 ;; somehow.
b8e0f0cd 291 `(lambda (prompt) ,(copy-sequence user-password)))
1d8e1f78
JD
292 (step (sasl-next-step client nil))
293 (tag (sieve-manage-send
294 (concat
295 "AUTHENTICATE \""
296 mech
297 "\""
298 (and (sasl-step-data step)
299 (concat
300 " \""
301 (base64-encode-string
302 (sasl-step-data step)
303 'no-line-break)
304 "\"")))))
305 data rsp)
306 (catch 'done
307 (while t
308 (setq rsp nil)
309 (goto-char (point-min))
310 (while (null (or (progn
311 (setq rsp (sieve-manage-is-string))
312 (if (not (and rsp (looking-at
313 sieve-manage-server-eol)))
314 (setq rsp nil)
315 (goto-char (match-end 0))
316 rsp))
317 (setq rsp (sieve-manage-is-okno))))
318 (accept-process-output sieve-manage-process 1)
319 (goto-char (point-min)))
320 (sieve-manage-erase)
321 (when (sieve-manage-ok-p rsp)
322 (when (and (cadr rsp)
323 (string-match "^SASL \"\\([^\"]+\\)\"" (cadr rsp)))
324 (sasl-step-set-data
325 step (base64-decode-string (match-string 1 (cadr rsp)))))
326 (if (and (setq step (sasl-next-step client step))
327 (setq data (sasl-step-data step)))
328 ;; We got data for server but it's finished
329 (error "Server not ready for SASL data: %s" data)
330 ;; The authentication process is finished.
331 (throw 'done t)))
332 (unless (stringp rsp)
333 (error "Server aborted SASL authentication: %s" (caddr rsp)))
334 (sasl-step-set-data step (base64-decode-string rsp))
335 (setq step (sasl-next-step client step))
336 (sieve-manage-send
337 (if (sasl-step-data step)
338 (concat "\""
339 (base64-encode-string (sasl-step-data step)
340 'no-line-break)
341 "\"")
342 ""))))
343 (message "sieve: Login using %s...done" mech))))
01c52d31
MB
344
345(defun sieve-manage-cram-md5-p (buffer)
346 (sieve-manage-capability "SASL" "CRAM-MD5" buffer))
347
348(defun sieve-manage-cram-md5-auth (buffer)
349 "Login to managesieve server using the CRAM-MD5 SASL method."
350 (sieve-sasl-auth buffer "CRAM-MD5"))
351
352(defun sieve-manage-digest-md5-p (buffer)
353 (sieve-manage-capability "SASL" "DIGEST-MD5" buffer))
354
355(defun sieve-manage-digest-md5-auth (buffer)
356 "Login to managesieve server using the DIGEST-MD5 SASL method."
357 (sieve-sasl-auth buffer "DIGEST-MD5"))
358
359(defun sieve-manage-scram-md5-p (buffer)
360 (sieve-manage-capability "SASL" "SCRAM-MD5" buffer))
361
362(defun sieve-manage-scram-md5-auth (buffer)
363 "Login to managesieve server using the SCRAM-MD5 SASL method."
364 (sieve-sasl-auth buffer "SCRAM-MD5"))
365
366(defun sieve-manage-ntlm-p (buffer)
367 (sieve-manage-capability "SASL" "NTLM" buffer))
368
369(defun sieve-manage-ntlm-auth (buffer)
370 "Login to managesieve server using the NTLM SASL method."
371 (sieve-sasl-auth buffer "NTLM"))
372
23f87bed
MB
373(defun sieve-manage-plain-p (buffer)
374 (sieve-manage-capability "SASL" "PLAIN" buffer))
375
376(defun sieve-manage-plain-auth (buffer)
377 "Login to managesieve server using the PLAIN SASL method."
01c52d31 378 (sieve-sasl-auth buffer "PLAIN"))
23f87bed 379
01c52d31
MB
380(defun sieve-manage-login-p (buffer)
381 (sieve-manage-capability "SASL" "LOGIN" buffer))
23f87bed 382
01c52d31
MB
383(defun sieve-manage-login-auth (buffer)
384 "Login to managesieve server using the LOGIN SASL method."
385 (sieve-sasl-auth buffer "LOGIN"))
23f87bed
MB
386
387;; Managesieve API
388
389(defun sieve-manage-open (server &optional port stream auth buffer)
390 "Open a network connection to a managesieve SERVER (string).
56fd9faa
JB
391Optional argument PORT is port number (integer) on remote server.
392Optional argument STREAM is any of `sieve-manage-streams' (a symbol).
393Optional argument AUTH indicates authenticator to use, see
394`sieve-manage-authenticators' for available authenticators.
395If nil, chooses the best stream the server is capable of.
396Optional argument BUFFER is buffer (buffer, or string naming buffer)
23f87bed 397to work in."
c4c15f02
GM
398 (or port (setq port sieve-manage-default-port))
399 (setq buffer (or buffer (format " *sieve* %s:%s" server port)))
23f87bed 400 (with-current-buffer (get-buffer-create buffer)
6ab3804a 401 (mapc 'make-local-variable sieve-manage-local-variables)
23f87bed
MB
402 (sieve-manage-disable-multibyte)
403 (buffer-disable-undo)
404 (setq sieve-manage-server (or server sieve-manage-server))
c4c15f02 405 (setq sieve-manage-port port)
23f87bed
MB
406 (setq sieve-manage-stream (or stream sieve-manage-stream))
407 (message "sieve: Connecting to %s..." sieve-manage-server)
408 (if (let ((sieve-manage-stream
409 (or sieve-manage-stream sieve-manage-default-stream)))
410 (sieve-manage-open-1 buffer))
411 ;; Choose stream.
412 (let (stream-changed)
413 (message "sieve: Connecting to %s...done" sieve-manage-server)
414 (when (null sieve-manage-stream)
415 (let ((streams sieve-manage-streams))
416 (while (setq stream (pop streams))
417 (if (funcall (nth 1 (assq stream
418 sieve-manage-stream-alist)) buffer)
419 (setq stream-changed
420 (not (eq (or sieve-manage-stream
421 sieve-manage-default-stream)
422 stream))
423 sieve-manage-stream stream
424 streams nil)))
425 (unless sieve-manage-stream
426 (error "Couldn't figure out a stream for server"))))
427 (when stream-changed
428 (message "sieve: Reconnecting with stream `%s'..."
429 sieve-manage-stream)
430 (sieve-manage-close buffer)
431 (if (sieve-manage-open-1 buffer)
432 (message "sieve: Reconnecting with stream `%s'...done"
433 sieve-manage-stream)
434 (message "sieve: Reconnecting with stream `%s'...failed"
435 sieve-manage-stream))
436 (setq sieve-manage-capability nil))
437 (if (sieve-manage-opened buffer)
438 ;; Choose authenticator
439 (when (and (null sieve-manage-auth)
440 (not (eq sieve-manage-state 'auth)))
441 (let ((auths sieve-manage-authenticators))
442 (while (setq auth (pop auths))
443 (if (funcall (nth 1 (assq
444 auth
445 sieve-manage-authenticator-alist))
446 buffer)
447 (setq sieve-manage-auth auth
448 auths nil)))
449 (unless sieve-manage-auth
450 (error "Couldn't figure out authenticator for server"))))))
451 (message "sieve: Connecting to %s...failed" sieve-manage-server))
452 (when (sieve-manage-opened buffer)
453 (sieve-manage-erase)
454 buffer)))
455
fd9ba500
JD
456(defun sieve-manage-authenticate (&optional buffer)
457 "Authenticate on server in BUFFER.
458Return `sieve-manage-state' value."
459 (with-current-buffer (or buffer (current-buffer))
460 (if (eq sieve-manage-state 'nonauth)
461 (when (funcall (nth 2 (assq sieve-manage-auth
462 sieve-manage-authenticator-alist))
463 (current-buffer))
464 (setq sieve-manage-state 'auth))
465 sieve-manage-state)))
466
23f87bed
MB
467(defun sieve-manage-opened (&optional buffer)
468 "Return non-nil if connection to managesieve server in BUFFER is open.
469If BUFFER is nil then the current buffer is used."
470 (and (setq buffer (get-buffer (or buffer (current-buffer))))
471 (buffer-live-p buffer)
472 (with-current-buffer buffer
473 (and sieve-manage-process
474 (memq (process-status sieve-manage-process) '(open run))))))
475
476(defun sieve-manage-close (&optional buffer)
477 "Close connection to managesieve server in BUFFER.
478If BUFFER is nil, the current buffer is used."
479 (with-current-buffer (or buffer (current-buffer))
480 (when (sieve-manage-opened)
481 (sieve-manage-send "LOGOUT")
482 (sit-for 1))
483 (when (and sieve-manage-process
484 (memq (process-status sieve-manage-process) '(open run)))
485 (delete-process sieve-manage-process))
486 (setq sieve-manage-process nil)
487 (sieve-manage-erase)
488 t))
489
23f87bed 490(defun sieve-manage-capability (&optional name value buffer)
6f7e2ffd
JD
491 "Check if capability NAME of server BUFFER match VALUE.
492If it does, return the server value of NAME. If not returns nil.
493If VALUE is nil, do not check VALUE and return server value.
494If NAME is nil, return the full server list of capabilities."
23f87bed
MB
495 (with-current-buffer (or buffer (current-buffer))
496 (if (null name)
497 sieve-manage-capability
6f7e2ffd
JD
498 (let ((server-value (cadr (assoc name sieve-manage-capability))))
499 (when (or (null value)
500 (and server-value
501 (string-match value server-value)))
502 server-value)))))
23f87bed
MB
503
504(defun sieve-manage-listscripts (&optional buffer)
505 (with-current-buffer (or buffer (current-buffer))
506 (sieve-manage-send "LISTSCRIPTS")
507 (sieve-manage-parse-listscripts)))
508
509(defun sieve-manage-havespace (name size &optional buffer)
510 (with-current-buffer (or buffer (current-buffer))
511 (sieve-manage-send (format "HAVESPACE \"%s\" %s" name size))
512 (sieve-manage-parse-okno)))
513
23f87bed
MB
514(defun sieve-manage-putscript (name content &optional buffer)
515 (with-current-buffer (or buffer (current-buffer))
516 (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
2136a8a7
SM
517 ;; Here we assume that the coding-system will
518 ;; replace each char with a single byte.
519 ;; This is always the case if `content' is
520 ;; a unibyte string.
521 (length content)
23f87bed
MB
522 sieve-manage-client-eol content))
523 (sieve-manage-parse-okno)))
524
525(defun sieve-manage-deletescript (name &optional buffer)
526 (with-current-buffer (or buffer (current-buffer))
527 (sieve-manage-send (format "DELETESCRIPT \"%s\"" name))
528 (sieve-manage-parse-okno)))
529
530(defun sieve-manage-getscript (name output-buffer &optional buffer)
531 (with-current-buffer (or buffer (current-buffer))
532 (sieve-manage-send (format "GETSCRIPT \"%s\"" name))
533 (let ((script (sieve-manage-parse-string)))
534 (sieve-manage-parse-crlf)
535 (with-current-buffer output-buffer
536 (insert script))
537 (sieve-manage-parse-okno))))
538
539(defun sieve-manage-setactive (name &optional buffer)
540 (with-current-buffer (or buffer (current-buffer))
541 (sieve-manage-send (format "SETACTIVE \"%s\"" name))
542 (sieve-manage-parse-okno)))
543
544;; Protocol parsing routines
545
546(defun sieve-manage-ok-p (rsp)
547 (string= (downcase (or (car-safe rsp) "")) "ok"))
548
549(defsubst sieve-manage-forward ()
550 (or (eobp) (forward-char)))
551
552(defun sieve-manage-is-okno ()
553 (when (looking-at (concat
554 "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
555 sieve-manage-server-eol))
556 (let ((status (match-string 1))
557 (resp-code (match-string 3))
558 (response (match-string 5)))
559 (when response
560 (goto-char (match-beginning 5))
561 (setq response (sieve-manage-is-string)))
562 (list status resp-code response))))
563
564(defun sieve-manage-parse-okno ()
565 (let (rsp)
566 (while (null rsp)
567 (accept-process-output (get-buffer-process (current-buffer)) 1)
568 (goto-char (point-min))
569 (setq rsp (sieve-manage-is-okno)))
570 (sieve-manage-erase)
571 rsp))
572
573(defun sieve-manage-parse-capability-1 ()
574 "Accept a managesieve greeting."
575 (let (str)
576 (while (setq str (sieve-manage-is-string))
577 (if (eq (char-after) ? )
578 (progn
579 (sieve-manage-forward)
580 (push (list str (sieve-manage-is-string))
581 sieve-manage-capability))
582 (push (list str) sieve-manage-capability))
583 (forward-line)))
889e4d55 584 (when (re-search-forward (concat "^OK.*" sieve-manage-server-eol) nil t)
23f87bed
MB
585 (setq sieve-manage-state 'nonauth)))
586
587(defalias 'sieve-manage-parse-greeting-1 'sieve-manage-parse-capability-1)
588
589(defun sieve-manage-is-string ()
590 (cond ((looking-at "\"\\([^\"]+\\)\"")
591 (prog1
592 (match-string 1)
593 (goto-char (match-end 0))))
64763fe3 594 ((looking-at (concat "{\\([0-9]+\\+?\\)}" sieve-manage-server-eol))
23f87bed
MB
595 (let ((pos (match-end 0))
596 (len (string-to-number (match-string 1))))
597 (if (< (point-max) (+ pos len))
598 nil
599 (goto-char (+ pos len))
600 (buffer-substring pos (+ pos len)))))))
601
602(defun sieve-manage-parse-string ()
603 (let (rsp)
604 (while (null rsp)
605 (accept-process-output (get-buffer-process (current-buffer)) 1)
606 (goto-char (point-min))
607 (setq rsp (sieve-manage-is-string)))
608 (sieve-manage-erase (point))
609 rsp))
610
611(defun sieve-manage-parse-crlf ()
612 (when (looking-at sieve-manage-server-eol)
613 (sieve-manage-erase (match-end 0))))
614
615(defun sieve-manage-parse-listscripts ()
616 (let (tmp rsp data)
617 (while (null rsp)
618 (while (null (or (setq rsp (sieve-manage-is-okno))
619 (setq tmp (sieve-manage-is-string))))
620 (accept-process-output (get-buffer-process (current-buffer)) 1)
621 (goto-char (point-min)))
622 (when tmp
623 (while (not (looking-at (concat "\\( ACTIVE\\)?"
624 sieve-manage-server-eol)))
625 (accept-process-output (get-buffer-process (current-buffer)) 1)
626 (goto-char (point-min)))
627 (if (match-string 1)
628 (push (cons 'active tmp) data)
629 (push tmp data))
630 (goto-char (match-end 0))
631 (setq tmp nil)))
632 (sieve-manage-erase)
633 (if (sieve-manage-ok-p rsp)
634 data
635 rsp)))
636
637(defun sieve-manage-send (cmdstr)
638 (setq cmdstr (concat cmdstr sieve-manage-client-eol))
639 (and sieve-manage-log
640 (with-current-buffer (get-buffer-create sieve-manage-log)
641 (sieve-manage-disable-multibyte)
642 (buffer-disable-undo)
643 (goto-char (point-max))
644 (insert cmdstr)))
645 (process-send-string sieve-manage-process cmdstr))
646
647(provide 'sieve-manage)
648
23f87bed 649;; sieve-manage.el ends here