Add missing :version tags to new defcustoms and defgroups
[gnus] / lisp / sieve-manage.el
1 ;;; sieve-manage.el --- Implementation of the managesieve protocol in elisp
2
3 ;; Copyright (C) 2001-2012  Free Software Foundation, Inc.
4
5 ;; Author: Simon Josefsson <simon@josefsson.org>
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
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
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
21
22 ;;; Commentary:
23
24 ;; This library provides an elisp API for the managesieve network
25 ;; protocol.
26 ;;
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.
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 ;;
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 ;;
52 ;; (with-current-buffer (sieve-manage-open "mail.example.com")
53 ;;   (sieve-manage-authenticate)
54 ;;   (sieve-manage-listscripts))
55 ;;
56 ;; => ((active . "main") "vacation")
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.
68 ;; 2002-08-03 Use SASL library.
69
70 ;;; Code:
71
72 ;; For Emacs <22.2 and XEmacs.
73 (eval-and-compile
74   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
75
76 (if (locate-library "password-cache")
77     (require 'password-cache)
78   (require 'password))
79
80 (eval-when-compile
81   (require 'cl)                         ; caddr
82   (require 'sasl)
83   (require 'starttls))
84 (autoload 'sasl-find-mechanism "sasl")
85 (autoload 'starttls-open-stream "starttls")
86 (autoload 'auth-source-search "auth-source")
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."
97   :type 'string
98   :group 'sieve-manage)
99
100 (defcustom sieve-manage-server-eol "\r\n"
101   "The EOL string sent from the server."
102   :type 'string
103   :group 'sieve-manage)
104
105 (defcustom sieve-manage-client-eol "\r\n"
106   "The EOL string we send to the server."
107   :type 'string
108   :group 'sieve-manage)
109
110 (defcustom sieve-manage-streams '(network starttls shell)
111   "Priority of streams to consider when opening connection to server."
112   :group 'sieve-manage)
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
122 NAME names the stream, CHECK is a function returning non-nil if the
123 server support the stream and OPEN is a function for opening the
124 stream."
125   :group 'sieve-manage)
126
127 (defcustom sieve-manage-authenticators '(digest-md5
128                                          cram-md5
129                                          scram-md5
130                                          ntlm
131                                          plain
132                                          login)
133   "Priority of authenticators to consider when authenticating to server."
134   :group 'sieve-manage)
135
136 (defcustom sieve-manage-authenticator-alist
137   '((cram-md5   sieve-manage-cram-md5-p       sieve-manage-cram-md5-auth)
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))
143   "Definition of authenticators.
144
145 \(NAME CHECK AUTHENTICATE)
146
147 NAME names the authenticator.  CHECK is a function returning non-nil if
148 the server support the authenticator and AUTHENTICATE is a function
149 for doing the actual authentication."
150   :group 'sieve-manage)
151
152 (defcustom sieve-manage-default-port 2000
153   "Default port number or service name for managesieve protocol."
154   :type 'integer
155   :group 'sieve-manage)
156
157 (defcustom sieve-manage-default-stream 'network
158   "Default stream type to use for `sieve-manage'.
159 Must be a name of a stream in `sieve-manage-stream-alist'."
160   :version "24.1"
161   :type 'symbol
162   :group 'sieve-manage)
163
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
170                                          sieve-manage-process
171                                          sieve-manage-client-eol
172                                          sieve-manage-server-eol
173                                          sieve-manage-capability))
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)
180 (defvar sieve-manage-state 'closed
181   "Managesieve state.
182 Valid 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
188 (defmacro sieve-manage-disable-multibyte ()
189   "Enable multibyte in the current buffer."
190   (unless (featurep 'xemacs)
191     '(set-buffer-multibyte nil)))
192
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
246 (defun sieve-manage-starttls-p (buffer)
247   (condition-case ()
248       (progn
249         (require 'starttls)
250         (call-process "starttls"))
251     (error nil)))
252
253 (defun sieve-manage-starttls-open (name buffer server port)
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
273 (defun sieve-sasl-auth (buffer mech)
274   "Login to server using the SASL MECH method."
275   (message "sieve: Authenticating using %s..." mech)
276   (with-current-buffer buffer
277     (let* ((auth-info (auth-source-search :host sieve-manage-server
278                                           :port "sieve"
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) ""))
283            (user-password (if (functionp user-password)
284                               (funcall user-password)
285                             user-password))
286            (client (sasl-make-client (sasl-find-mechanism (list mech))
287                                      user-name "sieve" sieve-manage-server))
288            (sasl-read-passphrase
289             ;; We *need* to copy the password, because sasl will modify it
290             ;; somehow.
291             `(lambda (prompt) ,(copy-sequence user-password)))
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))))
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
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."
378   (sieve-sasl-auth buffer "PLAIN"))
379
380 (defun sieve-manage-login-p (buffer)
381   (sieve-manage-capability "SASL" "LOGIN" buffer))
382
383 (defun sieve-manage-login-auth (buffer)
384   "Login to managesieve server using the LOGIN SASL method."
385   (sieve-sasl-auth buffer "LOGIN"))
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).
391 Optional argument PORT is port number (integer) on remote server.
392 Optional argument STREAM is any of `sieve-manage-streams' (a symbol).
393 Optional argument AUTH indicates authenticator to use, see
394 `sieve-manage-authenticators' for available authenticators.
395 If nil, chooses the best stream the server is capable of.
396 Optional argument BUFFER is buffer (buffer, or string naming buffer)
397 to work in."
398   (or port (setq port sieve-manage-default-port))
399   (setq buffer (or buffer (format " *sieve* %s:%s" server port)))
400   (with-current-buffer (get-buffer-create buffer)
401     (mapc 'make-local-variable sieve-manage-local-variables)
402     (sieve-manage-disable-multibyte)
403     (buffer-disable-undo)
404     (setq sieve-manage-server (or server sieve-manage-server))
405     (setq sieve-manage-port port)
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
456 (defun sieve-manage-authenticate (&optional buffer)
457   "Authenticate on server in BUFFER.
458 Return `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
467 (defun sieve-manage-opened (&optional buffer)
468   "Return non-nil if connection to managesieve server in BUFFER is open.
469 If 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.
478 If 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
490 (defun sieve-manage-capability (&optional name value buffer)
491   "Check if capability NAME of server BUFFER match VALUE.
492 If it does, return the server value of NAME. If not returns nil.
493 If VALUE is nil, do not check VALUE and return server value.
494 If NAME is nil, return the full server list of capabilities."
495   (with-current-buffer (or buffer (current-buffer))
496     (if (null name)
497         sieve-manage-capability
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)))))
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
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
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)
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)))
584   (when (re-search-forward (concat "^OK.*" sieve-manage-server-eol) nil t)
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))))
594         ((looking-at (concat "{\\([0-9]+\\+?\\)}" sieve-manage-server-eol))
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
649 ;; sieve-manage.el ends here