1 ;;; elmo-imap4.el -- IMAP4 Interface for ELMO.
3 ;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
28 ;; Origin of IMAP parser part is imap.el, included in Gnus.
30 ;; Copyright (C) 1998, 1999, 2000
31 ;; Free Software Foundation, Inc.
32 ;; Author: Simon Josefsson <jas@pdc.kth.se>
48 ;; silence byte compiler.
56 (defun-maybe sasl-cram-md5 (username passphrase challenge))
57 (defun-maybe sasl-digest-md5-digest-response
58 (digest-challenge username passwd serv-type host &optional realm))
59 (defun-maybe starttls-negotiate (a))
60 (defun-maybe elmo-generic-list-folder-unread (spec msgdb unread-marks))
61 (defsubst-maybe utf7-decode-string (string &optional imap) string))
63 (defvar elmo-imap4-use-lock t
64 "USE IMAP4 with locking process.")
66 ;;; internal variables
68 (defvar elmo-imap4-seq-prefix "elmo-imap4")
69 (defvar elmo-imap4-seqno 0)
70 (defvar elmo-imap4-use-uid t
71 "Use UID as message number.")
73 (defvar elmo-imap4-current-response nil)
74 (defvar elmo-imap4-status nil)
75 (defvar elmo-imap4-reached-tag "elmo-imap40")
77 ;;; buffer local variables
79 (defvar elmo-imap4-extra-namespace-alist
80 '(("^{.*/nntp}.*$" . ".")) ; Default is for UW's remote nntp mailbox...
81 "Extra namespace alist. A list of cons cell like: (REGEXP . DELIMITER) ")
82 (defvar elmo-imap4-default-hierarchy-delimiter "/")
84 (defvar elmo-imap4-server-capability nil)
85 (defvar elmo-imap4-server-namespace nil)
87 (defvar elmo-imap4-parsing nil) ; indicates parsing.
89 (defvar elmo-imap4-fetch-callback nil)
90 (defvar elmo-imap4-fetch-callback-data nil)
91 (defvar elmo-imap4-status-callback nil)
92 (defvar elmo-imap4-status-callback-data nil)
94 (defvar elmo-imap4-server-diff-async-callback nil)
95 (defvar elmo-imap4-server-diff-async-callback-data nil)
97 ;;; progress...(no use?)
98 (defvar elmo-imap4-count-progress nil)
99 (defvar elmo-imap4-count-progress-message nil)
100 (defvar elmo-imap4-progress-count nil)
102 ;;; XXX Temporal implementation
103 (defvar elmo-imap4-current-msgdb nil)
105 (defvar elmo-imap4-local-variables
107 elmo-imap4-current-response
110 elmo-imap4-reached-tag
111 elmo-imap4-count-progress
112 elmo-imap4-count-progress-message
113 elmo-imap4-progress-count
114 elmo-imap4-fetch-callback
115 elmo-imap4-fetch-callback-data
116 elmo-imap4-status-callback
117 elmo-imap4-status-callback-data
118 elmo-imap4-current-msgdb))
120 (defvar elmo-imap4-authenticator-alist
121 '((login elmo-imap4-auth-login)
122 (cram-md5 elmo-imap4-auth-cram-md5)
123 (digest-md5 elmo-imap4-auth-digest-md5)
124 (plain elmo-imap4-login))
125 "Definition of authenticators.")
129 (defconst elmo-imap4-quoted-specials-list '(?\\ ?\"))
131 (defconst elmo-imap4-non-atom-char-regex
133 (concat "[^" "]!#$&'+,./0-9:;<=>?@A-Z[^_`a-z|}~-" "]")))
135 (defconst elmo-imap4-non-text-char-regex
138 "]\x01-\x09\x0b\x0c\x0e-\x1f\x7f !\"#$%&'()*+,./0-9:;<=>?@A-Z[\\^_`a-z{|}~-"
141 (defconst elmo-imap4-literal-threshold 1024
142 "Limitation of characters that can be used in a quoted string.")
145 (defvar elmo-imap4-debug nil
146 "Non-nil forces IMAP4 folder as debug mode.
147 Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"")
149 (defvar elmo-imap4-debug-inhibit-logging nil)
154 (luna-define-class elmo-imap4-session (elmo-network-session)
155 (capability current-mailbox read-only))
156 (luna-define-internal-accessors 'elmo-imap4-session))
160 (defsubst elmo-imap4-spec-mailbox (spec)
163 (defsubst elmo-imap4-spec-username (spec)
166 (defsubst elmo-imap4-spec-auth (spec)
169 (defsubst elmo-imap4-spec-hostname (spec)
172 (defsubst elmo-imap4-spec-port (spec)
175 (defsubst elmo-imap4-spec-stream-type (spec)
181 (defsubst elmo-imap4-debug (message &rest args)
183 (with-current-buffer (get-buffer-create "*IMAP4 DEBUG*")
184 (goto-char (point-max))
185 (if elmo-imap4-debug-inhibit-logging
186 (insert "NO LOGGING\n")
187 (insert (apply 'format message args) "\n")))))
191 (defmacro elmo-imap4-response-continue-req-p (response)
192 "Returns non-nil if RESPONSE is '+' response."
193 (` (assq 'continue-req (, response))))
195 (defmacro elmo-imap4-response-ok-p (response)
196 "Returns non-nil if RESPONSE is an 'OK' response."
197 (` (assq 'ok (, response))))
199 (defmacro elmo-imap4-response-bye-p (response)
200 "Returns non-nil if RESPONSE is an 'BYE' response."
201 (` (assq 'bye (, response))))
203 (defmacro elmo-imap4-response-value (response symbol)
204 "Get value of the SYMBOL from RESPONSE."
205 (` (nth 1 (assq (, symbol) (, response)))))
207 (defsubst elmo-imap4-response-value-all (response symbol)
208 "Get all value of the SYMBOL from RESPONSE."
211 (if (eq (car (car response)) symbol)
212 (setq matched (nconc matched (nth 1 (car response)))))
213 (setq response (cdr response)))
216 (defmacro elmo-imap4-response-error-text (response)
217 "Returns text of NO, BAD, BYE response."
218 (` (nth 1 (or (elmo-imap4-response-value (, response) 'no)
219 (elmo-imap4-response-value (, response) 'bad)
220 (elmo-imap4-response-value (, response) 'bye)))))
222 (defmacro elmo-imap4-response-bodydetail-text (response)
223 "Returns text of BODY[section]<partial>"
224 (` (nth 3 (assq 'bodydetail (, response)))))
226 ;;; Session commands.
228 ; (defun elmo-imap4-send-command-wait (session command)
229 ; "Send COMMAND to the SESSION and wait for response.
230 ; Returns RESPONSE (parsed lisp object) of IMAP session."
231 ; (elmo-imap4-read-response session
232 ; (elmo-imap4-send-command
236 (defun elmo-imap4-send-command-wait (session command)
237 "Send COMMAND to the SESSION.
238 Returns RESPONSE (parsed lisp object) of IMAP session.
239 If response is not `OK', causes error with IMAP response text."
240 (elmo-imap4-accept-ok session
241 (elmo-imap4-send-command
245 (defun elmo-imap4-send-command (session command)
246 "Send COMMAND to the SESSION.
247 Returns a TAG string which is assigned to the COMAND."
248 (let* ((command-args (if (listp command)
251 (process (elmo-network-session-process-internal session))
252 cmdstr tag token kind)
253 (with-current-buffer (process-buffer process)
254 (setq tag (concat elmo-imap4-seq-prefix
256 (setq elmo-imap4-seqno (+ 1 elmo-imap4-seqno)))))
257 (setq cmdstr (concat tag " "))
258 ;; (erase-buffer) No need.
259 (goto-char (point-min))
260 (if (elmo-imap4-response-bye-p elmo-imap4-current-response)
261 (signal 'elmo-imap4-bye-error
262 (list (elmo-imap4-response-error-text
263 elmo-imap4-current-response))))
264 (setq elmo-imap4-current-response nil)
265 (if elmo-imap4-parsing
266 (error "IMAP process is running. Please wait (or plug again.)"))
267 (setq elmo-imap4-parsing t)
268 (elmo-imap4-debug "<-(%s)- %s" tag command)
269 (while (setq token (car command-args))
270 (cond ((stringp token) ; formatted
271 (setq cmdstr (concat cmdstr token)))
272 ((listp token) ; unformatted
273 (setq kind (car token))
274 (cond ((eq kind 'atom)
275 (setq cmdstr (concat cmdstr (nth 1 token))))
279 (elmo-imap4-format-quoted (nth 1 token)))))
281 (setq cmdstr (concat cmdstr
282 (format "{%d}" (nth 2 token))))
283 (process-send-string process cmdstr)
284 (process-send-string process "\r\n")
286 (elmo-imap4-accept-continue-req session)
287 (cond ((stringp (nth 1 token))
288 (setq cmdstr (nth 1 token)))
289 ((bufferp (nth 1 token))
290 (with-current-buffer (nth 1 token)
294 (+ (point-min) (nth 2 token)))))
296 (error "Wrong argument for literal"))))
298 (error "Unknown token kind %s" kind))))
300 (error "Invalid argument")))
301 (setq command-args (cdr command-args)))
303 (process-send-string process cmdstr))
304 (process-send-string process "\r\n")
307 (defun elmo-imap4-send-string (session string)
308 "Send STRING to the SESSION."
309 (with-current-buffer (process-buffer
310 (elmo-network-session-process-internal session))
311 (setq elmo-imap4-current-response nil)
312 (goto-char (point-min))
313 (elmo-imap4-debug "<-- %s" string)
314 (process-send-string (elmo-network-session-process-internal session)
316 (process-send-string (elmo-network-session-process-internal session)
319 (defun elmo-imap4-read-response (session tag)
320 "Read parsed response from SESSION.
321 TAG is the tag of the command"
322 (with-current-buffer (process-buffer
323 (elmo-network-session-process-internal session))
324 (while (not (or (string= tag elmo-imap4-reached-tag)
325 (elmo-imap4-response-bye-p elmo-imap4-current-response)))
326 (when (memq (process-status
327 (elmo-network-session-process-internal session))
329 (accept-process-output (elmo-network-session-process-internal session)
331 (elmo-imap4-debug "=>%s" (prin1-to-string elmo-imap4-current-response))
332 (setq elmo-imap4-parsing nil)
333 elmo-imap4-current-response))
335 (defsubst elmo-imap4-read-untagged (process)
336 (with-current-buffer (process-buffer process)
337 (while (not elmo-imap4-current-response)
338 (accept-process-output process 1))
339 (elmo-imap4-debug "=>%s" (prin1-to-string elmo-imap4-current-response))
340 elmo-imap4-current-response))
342 (defun elmo-imap4-read-continue-req (session)
343 "Returns a text following to continue-req in SESSION.
344 If response is not `+' response, returns nil."
345 (elmo-imap4-response-value
346 (elmo-imap4-read-untagged
347 (elmo-network-session-process-internal session))
350 (defun elmo-imap4-accept-continue-req (session)
351 "Returns non-nil if `+' (continue-req) response is arrived in SESSION.
352 If response is not `+' response, cause an error."
355 (elmo-imap4-read-untagged
356 (elmo-network-session-process-internal session)))
357 (or (elmo-imap4-response-continue-req-p response)
358 (error "IMAP error: %s"
359 (or (elmo-imap4-response-error-text response)
360 "No continut-req from server.")))))
362 (defun elmo-imap4-read-ok (session tag)
363 "Returns non-nil if `OK' response of the command with TAG is arrived
364 in SESSION. If response is not `OK' response, returns nil."
365 (elmo-imap4-response-ok-p
366 (elmo-imap4-read-response session tag)))
368 (defun elmo-imap4-accept-ok (session tag)
369 "Accept only `OK' response from SESSION.
370 If response is not `OK' response, causes error with IMAP response text."
371 (let ((response (elmo-imap4-read-response session tag)))
372 (if (elmo-imap4-response-ok-p response)
374 (if (elmo-imap4-response-bye-p response)
375 (signal 'elmo-imap4-bye-error
376 (list (elmo-imap4-response-error-text response)))
377 (error "IMAP error: %s"
378 (or (elmo-imap4-response-error-text response)
379 "No `OK' response from server."))))))
382 (defun elmo-imap4-session-check (session)
383 (elmo-imap4-send-command-wait session "check"))
385 (defun elmo-imap4-atom-p (string)
386 "Return t if STRING is an atom defined in rfc2060."
387 (if (string= string "")
390 (not (string-match elmo-imap4-non-atom-char-regex string)))))
392 (defun elmo-imap4-quotable-p (string)
393 "Return t if STRING can be formatted as a quoted defined in rfc2060."
395 (not (string-match elmo-imap4-non-text-char-regex string))))
397 (defun elmo-imap4-nil (string)
398 "Return a list represents the special atom \"NIL\" defined in rfc2060, \
400 Otherwise return nil."
404 (defun elmo-imap4-atom (string)
405 "Return a list represents STRING as an atom defined in rfc2060.
406 Return nil if STRING is not an atom. See `elmo-imap4-atom-p'."
407 (if (elmo-imap4-atom-p string)
408 (list 'atom string)))
410 (defun elmo-imap4-quoted (string)
411 "Return a list represents STRING as a quoted defined in rfc2060.
412 Return nil if STRING can not be formatted as a quoted. See `elmo-imap4-quotable-p'."
413 (if (elmo-imap4-quotable-p string)
414 (list 'quoted string)))
416 (defun elmo-imap4-literal-1 (string-or-buffer length)
417 "Internal function for `elmo-imap4-literal' and `elmo-imap4-buffer-literal'.
418 Return a list represents STRING-OR-BUFFER as a literal defined in rfc2060.
419 STRING-OR-BUFFER must be an encoded string or a single-byte string or a single-byte buffer.
420 LENGTH must be the number of octets for STRING-OR-BUFFER."
421 (list 'literal string-or-buffer length))
423 (defun elmo-imap4-literal (string)
424 "Return a list represents STRING as a literal defined in rfc2060.
425 STRING must be an encoded or a single-byte string."
426 (elmo-imap4-literal-1 string (length string)))
428 (defun elmo-imap4-buffer-literal (buffer)
429 "Return a list represents BUFFER as a literal defined in rfc2060.
430 BUFFER must be a single-byte buffer."
431 (elmo-imap4-literal-1 buffer (with-current-buffer buffer
434 (defun elmo-imap4-string-1 (string length)
435 "Internal function for `elmo-imap4-string' and `elmo-imap4-buffer-string'.
436 Return a list represents STRING as a string defined in rfc2060.
437 STRING must be an encoded or a single-byte string.
438 LENGTH must be the number of octets for STRING."
439 (or (elmo-imap4-quoted string)
440 (elmo-imap4-literal-1 string length)))
442 (defun elmo-imap4-string (string)
443 "Return a list represents STRING as a string defined in rfc2060.
444 STRING must be an encoded or a single-byte string."
445 (let ((length (length string)))
446 (if (< elmo-imap4-literal-threshold length)
447 (elmo-imap4-literal-1 string length)
448 (elmo-imap4-string-1 string length))))
450 (defun elmo-imap4-buffer-string (buffer)
451 "Return a list represents BUFFER as a string defined in rfc2060.
452 BUFFER must be a single-byte buffer."
453 (let ((length (with-current-buffer buffer
455 (if (< elmo-imap4-literal-threshold length)
456 (elmo-imap4-literal-1 buffer length)
457 (elmo-imap4-string-1 (with-current-buffer buffer
461 (defun elmo-imap4-astring-1 (string length)
462 "Internal function for `elmo-imap4-astring' and `elmo-imap4-buffer-astring'.
463 Return a list represents STRING as an astring defined in rfc2060.
464 STRING must be an encoded or a single-byte string.
465 LENGTH must be the number of octets for STRING."
466 (or (elmo-imap4-atom string)
467 (elmo-imap4-string-1 string length)))
469 (defun elmo-imap4-astring (string)
470 "Return a list represents STRING as an astring defined in rfc2060.
471 STRING must be an encoded or a single-byte string."
472 (let ((length (length string)))
473 (if (< elmo-imap4-literal-threshold length)
474 (elmo-imap4-literal-1 string length)
475 (elmo-imap4-astring-1 string length))))
477 (defun elmo-imap4-buffer-astring (buffer)
478 "Return a list represents BUFFER as an astring defined in rfc2060.
479 BUFFER must be a single-byte buffer."
480 (let ((length (with-current-buffer buffer
482 (if (< elmo-imap4-literal-threshold length)
483 (elmo-imap4-literal-1 buffer length)
484 (elmo-imap4-astring-1 (with-current-buffer buffer
488 (defun elmo-imap4-nstring (string)
489 "Return a list represents STRING as a nstring defined in rfc2060.
490 STRING must be an encoded or a single-byte string."
491 (or (elmo-imap4-nil string)
492 (elmo-imap4-string string)))
494 (defun elmo-imap4-buffer-nstring (buffer)
495 "Return a list represents BUFFER as a nstring defined in rfc2060.
496 BUFFER must be a single-byte buffer."
497 (or (elmo-imap4-nil buffer)
498 (elmo-imap4-buffer-string buffer)))
500 (defalias 'elmo-imap4-mailbox 'elmo-imap4-astring)
501 (defalias 'elmo-imap4-field-body 'elmo-imap4-astring)
502 (defalias 'elmo-imap4-userid 'elmo-imap4-astring)
503 (defalias 'elmo-imap4-password 'elmo-imap4-astring)
505 (defun elmo-imap4-format-quoted (string)
506 "Return STRING in a form of the quoted-string defined in rfc2060."
508 (std11-wrap-as-quoted-pairs string elmo-imap4-quoted-specials-list)
511 (defsubst elmo-imap4-response-get-selectable-mailbox-list (response)
515 (if (and (eq 'list (car entry))
516 (not (member "\\NoSelect" (nth 1 (nth 1 entry)))))
517 (car (nth 1 entry))))
521 (defun elmo-imap4-list-folders (spec &optional hierarchy)
522 (let* ((root (elmo-imap4-spec-mailbox spec))
523 (session (elmo-imap4-get-session spec))
526 (elmo-string-matched-assoc
528 (with-current-buffer (elmo-network-session-buffer session)
529 elmo-imap4-server-namespace)))
530 elmo-imap4-default-hierarchy-delimiter))
531 result append-serv type)
534 (not (string= root ""))
535 (not (string-match (concat "\\(.*\\)"
539 (setq root (concat root delim)))
540 (setq result (elmo-imap4-response-get-selectable-mailbox-list
541 (elmo-imap4-send-command-wait
543 (list "list " (elmo-imap4-mailbox root) " *"))))
544 (unless (string= (elmo-imap4-spec-username spec)
545 elmo-default-imap4-user)
546 (setq append-serv (concat ":" (elmo-imap4-spec-username spec))))
547 (unless (string= (elmo-imap4-spec-hostname spec)
548 elmo-default-imap4-server)
549 (setq append-serv (concat append-serv "@" (elmo-imap4-spec-hostname
551 (unless (eq (elmo-imap4-spec-port spec)
552 elmo-default-imap4-port)
553 (setq append-serv (concat append-serv ":"
555 (elmo-imap4-spec-port spec)))))
556 (setq type (elmo-imap4-spec-stream-type spec))
557 (unless (eq (elmo-network-stream-type-symbol type)
558 elmo-default-imap4-stream-type)
560 (setq append-serv (concat append-serv
561 (elmo-network-stream-type-spec-string
563 (mapcar (lambda (fld)
564 (concat "%" (elmo-imap4-decode-folder-string fld)
566 (eval append-serv))))
569 (defun elmo-imap4-folder-exists-p (spec)
570 (let ((session (elmo-imap4-get-session spec))
573 (elmo-imap4-read-response
575 (elmo-imap4-send-command
577 (list "status " (elmo-imap4-mailbox (elmo-imap4-spec-mailbox spec))
579 (when (elmo-imap4-response-bye-p response)
580 (signal 'elmo-imap4-bye-error
581 (list (elmo-imap4-response-error-text response))))
582 (elmo-imap4-response-ok-p response)))
584 (defun elmo-imap4-folder-creatable-p (spec)
587 (defun elmo-imap4-create-folder-maybe (spec dummy)
588 (unless (elmo-imap4-folder-exists-p spec)
589 (elmo-imap4-create-folder spec)))
591 (defun elmo-imap4-create-folder (spec)
592 (elmo-imap4-send-command-wait
593 (elmo-imap4-get-session spec)
594 (list "create " (elmo-imap4-mailbox
595 (elmo-imap4-spec-mailbox spec)))))
597 (defun elmo-imap4-delete-folder (spec)
598 (let ((session (elmo-imap4-get-session spec))
600 (when (elmo-imap4-spec-mailbox spec)
601 (when (setq msgs (elmo-imap4-list-folder spec))
602 (elmo-imap4-delete-msgs spec msgs))
603 ;; (elmo-imap4-send-command-wait session "close")
604 (elmo-imap4-send-command-wait
607 (elmo-imap4-mailbox (elmo-imap4-spec-mailbox spec)))))))
609 (defun elmo-imap4-rename-folder (old-spec new-spec)
610 ;;(elmo-imap4-send-command-wait session "close")
611 (elmo-imap4-send-command-wait
612 (elmo-imap4-get-session old-spec)
615 (elmo-imap4-spec-mailbox old-spec))
618 (elmo-imap4-spec-mailbox new-spec)))))
620 (defun elmo-imap4-max-of-folder (spec)
621 (let ((session (elmo-imap4-get-session spec))
623 (with-current-buffer (elmo-network-session-buffer session)
624 (setq elmo-imap4-status-callback nil)
625 (setq elmo-imap4-status-callback-data nil))
626 (setq status (elmo-imap4-response-value
627 (elmo-imap4-send-command-wait
631 (elmo-imap4-spec-mailbox spec))
632 " (uidnext messages)"))
635 (- (elmo-imap4-response-value status 'uidnext) 1)
636 (elmo-imap4-response-value status 'messages))))
638 ; (when (and response (string-match
639 ; "\\* STATUS [^(]* \\(([^)]*)\\)" response))
640 ; (setq response (read (downcase (elmo-match-string 1 response))))
641 ; (cons (- (cadr (memq 'uidnext response)) 1)
642 ; (cadr (memq 'messages response)))))))
644 (defun elmo-imap4-get-session (spec &optional if-exists)
645 (elmo-network-get-session
648 (elmo-imap4-spec-hostname spec)
649 (elmo-imap4-spec-port spec)
650 (elmo-imap4-spec-username spec)
651 (elmo-imap4-spec-auth spec)
652 (elmo-imap4-spec-stream-type spec)
655 (defun elmo-imap4-commit (spec)
656 (if (elmo-imap4-plugged-p spec)
657 (let ((session (elmo-imap4-get-session spec 'if-exists)))
660 (elmo-imap4-session-current-mailbox-internal session)
661 (elmo-imap4-spec-mailbox spec))
662 (if elmo-imap4-use-select-to-update-status
663 (elmo-imap4-session-select-mailbox
665 (elmo-imap4-spec-mailbox spec)
667 (elmo-imap4-session-check session)))))))
669 (defun elmo-imap4-session-select-mailbox (session mailbox &optional force)
672 (elmo-imap4-session-current-mailbox-internal session)
677 (elmo-imap4-read-response
679 (elmo-imap4-send-command
683 (elmo-imap4-mailbox mailbox)))))
684 (if (elmo-imap4-response-ok-p response)
686 (elmo-imap4-session-set-current-mailbox-internal session mailbox)
687 (elmo-imap4-session-set-read-only-internal
689 (nth 1 (assq 'read-only (assq 'ok response)))))
690 (elmo-imap4-session-set-current-mailbox-internal session nil)
692 (elmo-imap4-response-error-text response)
693 (format "Select %s failed" mailbox))))))))
695 (defun elmo-imap4-check-validity (spec validity-file)
697 ; (elmo-imap4-send-command-wait
698 ; (elmo-imap4-get-session spec)
700 ; (elmo-imap4-mailbox
701 ; (elmo-imap4-spec-mailbox spec))
702 ; " (uidvalidity)")))
705 (defun elmo-imap4-sync-validity (spec validity-file)
709 (defun elmo-imap4-list (spec flag)
710 (let ((session (elmo-imap4-get-session spec)))
711 (elmo-imap4-session-select-mailbox session
712 (elmo-imap4-spec-mailbox spec))
713 (elmo-imap4-response-value
714 (elmo-imap4-send-command-wait
716 (format (if elmo-imap4-use-uid "uid search %s"
720 (defun elmo-imap4-list-folder (spec)
721 (let ((killed (and elmo-use-killed-list
722 (elmo-msgdb-killed-list-load
723 (elmo-msgdb-expand-path nil spec))))
725 (setq numbers (elmo-imap4-list spec "all"))
728 (mapcar (lambda (number)
729 (unless (memq number killed) number))
733 (defun elmo-imap4-list-folder-unread (spec msgdb unread-marks)
734 (if (elmo-imap4-use-flag-p spec)
735 (elmo-imap4-list spec "unseen")
736 (elmo-generic-list-folder-unread spec msgdb unread-marks)))
738 (defun elmo-imap4-list-folder-important (spec msgdb)
739 (and (elmo-imap4-use-flag-p spec)
740 (elmo-imap4-list spec "flagged")))
742 (defmacro elmo-imap4-detect-search-charset (string)
745 (detect-mime-charset-region (point-min) (point-max)))))
747 (defun elmo-imap4-search-internal-primitive (spec session filter from-msgs)
748 (let ((search-key (elmo-filter-key filter))
749 (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to"))
752 ((string= "last" search-key)
753 (let ((numbers (or from-msgs (elmo-imap4-list-folder spec))))
754 (nthcdr (max (- (length numbers)
755 (string-to-int (elmo-filter-value filter)))
758 ((string= "first" search-key)
759 (let* ((numbers (or from-msgs (elmo-imap4-list-folder spec)))
760 (rest (nthcdr (string-to-int (elmo-filter-value filter) )
762 (mapcar '(lambda (x) (delete x numbers)) rest)
764 ((or (string= "since" search-key)
765 (string= "before" search-key))
766 (setq search-key (concat "sent" search-key))
767 (elmo-imap4-response-value
768 (elmo-imap4-send-command-wait session
770 (if elmo-imap4-use-uid
771 "uid search%s%s%s %s"
775 (if elmo-imap4-use-uid "uid ")
778 (elmo-imap4-make-number-set-list
782 (if (eq (elmo-filter-type filter)
786 (elmo-date-get-description
787 (elmo-date-get-datevec
788 (elmo-filter-value filter)))))
792 (if (eq (length (elmo-filter-value filter)) 0)
793 (setq charset 'us-ascii)
794 (elmo-imap4-detect-search-charset
795 (elmo-filter-value filter))))
796 (elmo-imap4-response-value
797 (elmo-imap4-send-command-wait session
799 (if elmo-imap4-use-uid "uid ")
803 (symbol-name charset))
807 (if elmo-imap4-use-uid "uid ")
810 (elmo-imap4-make-number-set-list
814 (if (eq (elmo-filter-type filter)
819 (elmo-filter-key filter)
823 (elmo-filter-key filter))
825 (encode-mime-charset-string
826 (elmo-filter-value filter) charset))))
829 (defun elmo-imap4-search-internal (spec session condition from-msgs)
833 (setq result (elmo-imap4-search-internal-primitive
834 spec session condition from-msgs)))
835 ((eq (car condition) 'and)
836 (setq result (elmo-imap4-search-internal spec session (nth 1 condition)
838 result (elmo-list-filter result
839 (elmo-imap4-search-internal
840 spec session (nth 2 condition)
842 ((eq (car condition) 'or)
843 (setq result (elmo-imap4-search-internal
844 spec session (nth 1 condition) from-msgs)
845 result (elmo-uniq-list
847 (elmo-imap4-search-internal
848 spec session (nth 2 condition) from-msgs)))
849 result (sort result '<))))))
852 (defun elmo-imap4-search (spec condition &optional from-msgs)
854 (let ((session (elmo-imap4-get-session spec)))
855 (elmo-imap4-session-select-mailbox
857 (elmo-imap4-spec-mailbox spec))
858 (elmo-imap4-search-internal spec session condition from-msgs))))
860 (defun elmo-imap4-use-flag-p (spec)
861 (not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
862 (elmo-imap4-spec-mailbox spec))))
866 ;; Emacs can parse dot symbol.
867 (defvar elmo-imap4-rfc822-size "RFC822\.SIZE")
868 (defvar elmo-imap4-rfc822-text "RFC822\.TEXT")
869 (defvar elmo-imap4-rfc822-header "RFC822\.HEADER")
870 (defvar elmo-imap4-rfc822-size "RFC822\.SIZE")
871 (defvar elmo-imap4-header-fields "HEADER\.FIELDS")
872 (defmacro elmo-imap4-replace-dot-symbols ()) ;; noop
873 (defalias 'elmo-imap4-fetch-read 'read)
874 (defalias 'elmo-imap4-read 'read)
878 ;; Cannot parse dot symbol.
879 (defvar elmo-imap4-rfc822-size "RFC822_SIZE")
880 (defvar elmo-imap4-header-fields "HEADER_FIELDS")
881 (defvar elmo-imap4-rfc822-size "RFC822_SIZE")
882 (defvar elmo-imap4-rfc822-text "RFC822_TEXT")
883 (defvar elmo-imap4-rfc822-header "RFC822_HEADER")
884 (defvar elmo-imap4-header-fields "HEADER_FIELDS")
885 (defun elmo-imap4-fetch-read (buffer)
886 (with-current-buffer buffer
889 (when (re-search-forward "[[ ]" nil t)
890 (goto-char (match-beginning 0))
891 (setq token (buffer-substring beg (point)))
892 (cond ((string= token "RFC822.SIZE")
893 (intern elmo-imap4-rfc822-size))
894 ((string= token "RFC822.HEADER")
895 (intern elmo-imap4-rfc822-header))
896 ((string= token "RFC822.TEXT")
897 (intern elmo-imap4-rfc822-text))
898 ((string= token "HEADER\.FIELDS")
899 (intern elmo-imap4-header-fields))
901 (elmo-imap4-read (current-buffer))))))))
902 ;; Nemacs's `read' is different.
903 (defun elmo-imap4-read (obj)
906 (or (bobp) (forward-char -1)))))))
908 (defun elmo-imap4-add-to-cont-list (cont-list msg)
909 (let ((elist cont-list)
912 (while (and elist (not found))
913 (setq entity (car elist))
916 (eq (+ 1 (cdr entity)) msg))
919 ((and (integerp entity)
920 (eq (+ 1 entity) msg))
921 (setcar elist (cons entity msg))
923 ((or (and (integerp entity) (eq entity msg))
925 (<= (car entity) msg)
926 (<= msg (cdr entity)))) ; included
927 (setq found t))); noop
928 (setq elist (cdr elist)))
930 (setq ret-val (append cont-list (list msg))))
933 (defun elmo-imap4-make-number-set-list (msg-list &optional chop-length)
934 "Make RFC2060's message set specifier from MSG-LIST.
935 Returns a list of (NUMBER . SET-STRING).
936 SET-STRING is the message set specifier described in RFC2060.
937 NUMBER is contained message number in SET-STRING.
938 Every SET-STRING does not contain number of messages longer than CHOP-LENGTH.
939 If CHOP-LENGTH is not specified, message set is not chopped."
940 (let (count cont-list set-list)
941 (setq msg-list (sort (copy-sequence msg-list) '<))
946 (setq chop-length (length msg-list)))
947 (while (and (not (null msg-list))
948 (< count chop-length))
950 (elmo-imap4-add-to-cont-list
951 cont-list (car msg-list)))
953 (setq msg-list (cdr msg-list)))
961 (format "%s:%s" (car x) (cdr x)))
967 (nreverse set-list)))
971 ;; read-mark -> "\\Seen"
972 ;; important -> "\\Flagged"
974 ;; (delete -> \\Deleted)
975 (defun elmo-imap4-mark-set-on-msgs (spec msgs mark &optional unmark no-expunge)
976 "SET flag of MSGS as MARK.
977 If optional argument UNMARK is non-nil, unmark."
978 (let ((session (elmo-imap4-get-session spec))
980 (elmo-imap4-session-select-mailbox session
981 (elmo-imap4-spec-mailbox spec))
982 (setq set-list (elmo-imap4-make-number-set-list msgs))
984 (with-current-buffer (elmo-network-session-buffer session)
985 (setq elmo-imap4-fetch-callback nil)
986 (setq elmo-imap4-fetch-callback-data nil))
987 (elmo-imap4-send-command-wait
990 (if elmo-imap4-use-uid
991 "uid store %s %sflags.silent (%s)"
992 "store %s %sflags.silent (%s)")
997 (elmo-imap4-send-command-wait session "expunge")))
1000 (defun elmo-imap4-mark-as-important (spec msgs)
1001 (and (elmo-imap4-use-flag-p spec)
1002 (elmo-imap4-mark-set-on-msgs spec msgs "\\Flagged" nil 'no-expunge)))
1004 (defun elmo-imap4-mark-as-read (spec msgs)
1005 (and (elmo-imap4-use-flag-p spec)
1006 (elmo-imap4-mark-set-on-msgs spec msgs "\\Seen" nil 'no-expunge)))
1008 (defun elmo-imap4-unmark-important (spec msgs)
1009 (and (elmo-imap4-use-flag-p spec)
1010 (elmo-imap4-mark-set-on-msgs spec msgs "\\Flagged" 'unmark
1013 (defun elmo-imap4-mark-as-unread (spec msgs)
1014 (and (elmo-imap4-use-flag-p spec)
1015 (elmo-imap4-mark-set-on-msgs spec msgs "\\Seen" 'unmark 'no-expunge)))
1017 (defun elmo-imap4-delete-msgs (spec msgs)
1018 (elmo-imap4-mark-set-on-msgs spec msgs "\\Deleted"))
1020 (defun elmo-imap4-delete-msgs-no-expunge (spec msgs)
1021 (elmo-imap4-mark-set-on-msgs spec msgs "\\Deleted" nil 'no-expunge))
1023 (defun elmo-imap4-msgdb-create-as-numlist (spec numlist new-mark already-mark
1024 seen-mark important-mark
1026 "Create msgdb for SPEC for NUMLIST."
1027 (elmo-imap4-msgdb-create spec numlist new-mark already-mark
1028 seen-mark important-mark seen-list t))
1030 ;; Current buffer is process buffer.
1031 (defun elmo-imap4-fetch-callback (element app-data)
1032 (funcall elmo-imap4-fetch-callback
1034 (insert (or (elmo-imap4-response-bodydetail-text element)
1037 (goto-char (point-min))
1038 (while (search-forward "\r\n" nil t)
1039 (replace-match "\n"))
1040 (elmo-msgdb-create-overview-from-buffer
1041 (elmo-imap4-response-value element 'uid)
1042 (elmo-imap4-response-value element 'rfc822size)))
1043 (elmo-imap4-response-value element 'flags)
1048 ;; 0: new-mark 1: already-mark 2: seen-mark 3: important-mark
1049 ;; 4: seen-list 5: as-number
1050 (defun elmo-imap4-fetch-callback-1 (entity flags app-data)
1051 "A msgdb entity callback function."
1052 (let ((seen (member (car entity) (nth 4 app-data)))
1054 (if (member "\\Flagged" flags)
1055 (elmo-msgdb-global-mark-set (car entity) (nth 3 app-data)))
1056 (setq mark (or (elmo-msgdb-global-mark-get (car entity))
1057 (if (elmo-cache-exists-p (car entity)) ;; XXX
1058 (if (or (member "\\Seen" flags) seen)
1061 (if (or (member "\\Seen" flags) seen)
1062 (if elmo-imap4-use-cache
1064 (nth 0 app-data)))))
1065 (setq elmo-imap4-current-msgdb
1067 elmo-imap4-current-msgdb
1069 (list (cons (elmo-msgdb-overview-entity-get-number entity)
1073 (list (elmo-msgdb-overview-entity-get-number entity)
1076 (defun elmo-imap4-msgdb-create (spec numlist &rest args)
1077 "Create msgdb for SPEC."
1079 (let ((session (elmo-imap4-get-session spec))
1082 '("Subject" "From" "To" "Cc" "Date"
1083 "Message-Id" "References" "In-Reply-To")
1084 elmo-msgdb-extra-fields))
1086 (length (length numlist))
1088 (setq rfc2060 (memq 'imap4rev1
1089 (elmo-imap4-session-capability-internal
1091 (message "Getting overview...")
1092 (elmo-imap4-session-select-mailbox session
1093 (elmo-imap4-spec-mailbox spec))
1094 (setq set-list (elmo-imap4-make-number-set-list
1096 elmo-imap4-overview-fetch-chop-length))
1098 (with-current-buffer (elmo-network-session-buffer session)
1099 (setq elmo-imap4-current-msgdb nil
1100 elmo-imap4-fetch-callback 'elmo-imap4-fetch-callback-1
1101 elmo-imap4-fetch-callback-data args)
1103 (elmo-imap4-send-command-wait
1105 ;; get overview entity from IMAP4
1106 (format "%sfetch %s (%s rfc822.size flags)"
1107 (if elmo-imap4-use-uid "uid " "")
1108 (cdr (car set-list))
1110 (format "body.peek[header.fields %s]" headers)
1111 (format "%s" headers))))
1112 (when (> length elmo-display-progress-threshold)
1113 (setq total (+ total (car (car set-list))))
1114 (elmo-display-progress
1115 'elmo-imap4-msgdb-create "Getting overview..."
1116 (/ (* total 100) length)))
1117 (setq set-list (cdr set-list)))
1118 (message "Getting overview...done.")
1119 elmo-imap4-current-msgdb))))
1121 (defun elmo-imap4-parse-capability (string)
1122 (if (string-match "^\\*\\(.*\\)$" string)
1124 (concat "(" (downcase (elmo-match-string 1 string)) ")"))))
1126 ;; Current buffer is process buffer.
1127 (defun elmo-imap4-auth-login (session)
1128 (let ((tag (elmo-imap4-send-command session "authenticate login"))
1129 (elmo-imap4-debug-inhibit-logging t))
1130 (or (elmo-imap4-read-continue-req session)
1131 (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
1132 (elmo-imap4-send-string session
1133 (elmo-base64-encode-string
1134 (elmo-network-session-user-internal session)))
1135 (or (elmo-imap4-read-continue-req session)
1136 (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
1137 (elmo-imap4-send-string session
1138 (elmo-base64-encode-string
1140 (elmo-network-session-password-key session))))
1141 (or (elmo-imap4-read-ok session tag)
1142 (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
1143 (setq elmo-imap4-status 'auth)))
1145 (defun elmo-imap4-auth-cram-md5 (session)
1146 (let ((tag (elmo-imap4-send-command session "authenticate cram-md5"))
1147 (elmo-imap4-debug-inhibit-logging t)
1149 (or (setq response (elmo-imap4-read-continue-req session))
1150 (signal 'elmo-authenticate-error
1151 '(elmo-imap4-auth-cram-md5)))
1152 (elmo-imap4-send-string
1154 (elmo-base64-encode-string
1155 (sasl-cram-md5 (elmo-network-session-user-internal session)
1157 (elmo-network-session-password-key session))
1158 (elmo-base64-decode-string response))))
1159 (or (elmo-imap4-read-ok session tag)
1160 (signal 'elmo-authenticate-error '(elmo-imap4-auth-cram-md5)))))
1162 (defun elmo-imap4-auth-digest-md5 (session)
1163 (let ((tag (elmo-imap4-send-command session "authenticate digest-md5"))
1164 (elmo-imap4-debug-inhibit-logging t)
1166 (or (setq response (elmo-imap4-read-continue-req session))
1167 (signal 'elmo-authenticate-error '(elmo-imap4-auth-digest-md5)))
1168 (elmo-imap4-send-string
1170 (elmo-base64-encode-string
1171 (sasl-digest-md5-digest-response
1172 (elmo-base64-decode-string response)
1173 (elmo-network-session-user-internal session)
1174 (elmo-get-passwd (elmo-network-session-password-key session))
1176 (elmo-network-session-password-key session))
1178 (or (setq response (elmo-imap4-read-continue-req session))
1179 (signal 'elmo-authenticate-error '(elmo-imap4-auth-digest-md5)))
1180 (elmo-imap4-send-string session "")
1181 (or (elmo-imap4-read-ok session tag)
1182 (signal 'elmo-authenticate-error '(elmo-imap4-auth-digest-md5)))))
1184 (defun elmo-imap4-login (session)
1185 (let ((elmo-imap4-debug-inhibit-logging t))
1189 (elmo-imap4-send-command
1192 (elmo-imap4-userid (elmo-network-session-user-internal session))
1194 (elmo-imap4-password
1195 (elmo-get-passwd (elmo-network-session-password-key session))))))
1196 (signal 'elmo-authenticate-error '(login)))))
1199 elmo-network-initialize-session-buffer :after ((session
1200 elmo-imap4-session) buffer)
1201 (with-current-buffer buffer
1202 (mapcar 'make-variable-buffer-local elmo-imap4-local-variables)
1203 (setq elmo-imap4-seqno 0)
1204 (setq elmo-imap4-status 'initial)))
1206 (luna-define-method elmo-network-initialize-session ((session
1207 elmo-imap4-session))
1208 (let ((process (elmo-network-session-process-internal session))
1210 (with-current-buffer (process-buffer process)
1211 ;; Skip garbage output from process before greeting.
1212 (while (and (memq (process-status process) '(open run))
1213 (goto-char (point-max))
1215 (not (elmo-imap4-parse-greeting)))
1216 (accept-process-output process 1))
1217 (set-process-filter process 'elmo-imap4-arrival-filter)
1218 (set-process-sentinel process 'elmo-imap4-sentinel)
1219 ;; (while (and (memq (process-status process) '(open run))
1220 ;; (eq elmo-imap4-status 'initial))
1221 ;; (message "Waiting for server response...")
1222 ;; (accept-process-output process 1))
1224 (unless (memq elmo-imap4-status '(nonauth auth))
1225 (signal 'elmo-open-error
1226 (list 'elmo-network-initialize-session)))
1227 (elmo-imap4-session-set-capability-internal
1229 (elmo-imap4-response-value
1230 (elmo-imap4-send-command-wait session "capability")
1232 (when (eq (elmo-network-stream-type-symbol
1233 (elmo-network-session-stream-type-internal session))
1235 (or (memq 'starttls capability)
1236 (signal 'elmo-open-error
1237 '(elmo-network-initialize-session)))
1238 (elmo-imap4-send-command-wait session "starttls")
1239 (starttls-negotiate process)))))
1241 (luna-define-method elmo-network-authenticate-session ((session
1242 elmo-imap4-session))
1243 (with-current-buffer (process-buffer
1244 (elmo-network-session-process-internal session))
1245 (unless (eq elmo-imap4-status 'auth)
1246 (unless (or (not (elmo-network-session-auth-internal session))
1247 (eq (elmo-network-session-auth-internal session) 'plain)
1250 (elmo-network-session-auth-internal
1252 (elmo-imap4-session-capability-internal session))
1254 (elmo-network-session-auth-internal session)
1255 elmo-imap4-authenticator-alist)))
1256 (if (or elmo-imap4-force-login
1259 "There's no %s capability in server. continue?"
1260 (elmo-network-session-auth-internal session))))
1261 (elmo-network-session-set-auth-internal session nil)
1262 (signal 'elmo-open-error
1263 '(elmo-network-initialize-session))))
1264 (let ((authenticator
1265 (if (elmo-network-session-auth-internal session)
1267 (elmo-network-session-auth-internal session)
1268 elmo-imap4-authenticator-alist))
1269 'elmo-imap4-login)))
1270 (funcall authenticator session)))))
1272 (luna-define-method elmo-network-setup-session ((session
1273 elmo-imap4-session))
1274 (with-current-buffer (elmo-network-session-buffer session)
1275 (when (memq 'namespace (elmo-imap4-session-capability-internal session))
1276 (setq elmo-imap4-server-namespace
1277 (elmo-imap4-response-value
1278 (elmo-imap4-send-command-wait session "namespace")
1281 (defun elmo-imap4-setup-send-buffer (string)
1282 (let ((tmp-buf (get-buffer-create " *elmo-imap4-setup-send-buffer*")))
1285 (set-buffer tmp-buf)
1287 (elmo-set-buffer-multibyte nil)
1289 (goto-char (point-min))
1290 (if (eq (re-search-forward "^$" nil t)
1293 (goto-char (point-min))
1294 (while (search-forward "\n" nil t)
1295 (replace-match "\r\n"))))
1298 (defun elmo-imap4-read-part (folder msg part)
1299 (let* ((spec (elmo-folder-get-spec folder))
1300 (session (elmo-imap4-get-session spec)))
1301 (elmo-imap4-session-select-mailbox session
1302 (elmo-imap4-spec-mailbox spec))
1303 (with-current-buffer (elmo-network-session-buffer session)
1304 (setq elmo-imap4-fetch-callback nil)
1305 (setq elmo-imap4-fetch-callback-data nil))
1307 (elmo-imap4-response-bodydetail-text
1308 (elmo-imap4-response-value-all
1309 (elmo-imap4-send-command-wait session
1311 (if elmo-imap4-use-uid
1312 "uid fetch %s body.peek[%s]"
1313 "fetch %s body.peek[%s]")
1317 (defun elmo-imap4-prefetch-msg (spec msg outbuf)
1318 (elmo-imap4-read-msg spec msg outbuf 'unseen))
1320 (defun elmo-imap4-read-msg (spec msg outbuf
1321 &optional leave-seen-flag-untouched)
1322 (let ((session (elmo-imap4-get-session spec))
1324 (elmo-imap4-session-select-mailbox session
1325 (elmo-imap4-spec-mailbox spec))
1326 (with-current-buffer (elmo-network-session-buffer session)
1327 (setq elmo-imap4-fetch-callback nil)
1328 (setq elmo-imap4-fetch-callback-data nil))
1330 (elmo-imap4-send-command-wait session
1332 (if elmo-imap4-use-uid
1333 "uid fetch %s rfc822%s"
1334 "fetch %s rfc822%s")
1336 (if leave-seen-flag-untouched
1338 (and (setq response (elmo-imap4-response-value
1339 (elmo-imap4-response-value-all
1342 (with-current-buffer outbuf
1345 (elmo-delete-cr-get-content-type)))))
1347 (defun elmo-imap4-setup-send-buffer-from-file (file)
1348 (let ((tmp-buf (get-buffer-create
1349 " *elmo-imap4-setup-send-buffer-from-file*")))
1352 (set-buffer tmp-buf)
1354 (as-binary-input-file
1355 (insert-file-contents file))
1356 (goto-char (point-min))
1357 (if (eq (re-search-forward "^$" nil t)
1360 (goto-char (point-min))
1361 (while (search-forward "\n" nil t)
1362 (replace-match "\r\n"))))
1365 (defun elmo-imap4-delete-msgids (spec msgids)
1366 "If actual message-id is matched, then delete it."
1367 (let ((message-ids msgids)
1369 (num (length msgids)))
1372 (message "Deleting message...%d/%d" i num)
1373 (elmo-imap4-delete-msg-by-id spec (car message-ids))
1374 (setq message-ids (cdr message-ids)))
1375 (elmo-imap4-send-command-wait (elmo-imap4-get-session spec) "expunge")))
1377 (defun elmo-imap4-delete-msg-by-id (spec msgid)
1378 (let ((session (elmo-imap4-get-session spec)))
1379 (elmo-imap4-session-select-mailbox session
1380 (elmo-imap4-spec-mailbox spec))
1381 (elmo-imap4-delete-msgs-no-expunge
1383 (elmo-imap4-response-value
1384 (elmo-imap4-send-command-wait session
1386 (if elmo-imap4-use-uid
1387 "uid search header message-id "
1388 "search header message-id ")
1389 (elmo-imap4-field-body msgid)))
1392 (defun elmo-imap4-append-msg-by-id (spec msgid)
1393 (let ((session (elmo-imap4-get-session spec))
1395 (elmo-imap4-session-select-mailbox session
1396 (elmo-imap4-spec-mailbox spec))
1397 (setq send-buf (elmo-imap4-setup-send-buffer-from-file
1398 (elmo-cache-get-path msgid)))
1400 (elmo-imap4-send-command-wait
1404 (elmo-imap4-mailbox (elmo-imap4-spec-mailbox spec))
1406 (elmo-imap4-buffer-literal send-buf)))
1407 (kill-buffer send-buf)))
1410 (defun elmo-imap4-append-msg (spec string &optional msg no-see)
1411 (let ((session (elmo-imap4-get-session spec))
1413 (elmo-imap4-session-select-mailbox session
1414 (elmo-imap4-spec-mailbox spec))
1415 (setq send-buf (elmo-imap4-setup-send-buffer string))
1417 (elmo-imap4-send-command-wait
1421 (elmo-imap4-mailbox (elmo-imap4-spec-mailbox spec))
1422 (if no-see " " " (\\Seen) ")
1423 (elmo-imap4-buffer-literal send-buf)))
1424 (kill-buffer send-buf)))
1427 (defun elmo-imap4-copy-msgs (dst-spec
1428 msgs src-spec &optional expunge-it same-number)
1429 "Equivalence of hostname, username is assumed."
1430 (let ((session (elmo-imap4-get-session src-spec)))
1431 (elmo-imap4-session-select-mailbox session
1432 (elmo-imap4-spec-mailbox src-spec))
1434 (elmo-imap4-send-command-wait session
1437 (if elmo-imap4-use-uid
1442 (elmo-imap4-spec-mailbox dst-spec))))
1443 (setq msgs (cdr msgs)))
1445 (elmo-imap4-send-command-wait session "expunge"))
1448 (defun elmo-imap4-server-diff-async-callback-1 (status data)
1449 (funcall elmo-imap4-server-diff-async-callback
1450 (cons (elmo-imap4-response-value status 'unseen)
1451 (elmo-imap4-response-value status 'messages))
1454 (defun elmo-imap4-server-diff-async (spec)
1455 (let ((session (elmo-imap4-get-session spec)))
1457 ;; (elmo-imap4-commit spec)
1458 (with-current-buffer (elmo-network-session-buffer session)
1459 (setq elmo-imap4-status-callback
1460 'elmo-imap4-server-diff-async-callback-1)
1461 (setq elmo-imap4-status-callback-data
1462 elmo-imap4-server-diff-async-callback-data))
1463 (elmo-imap4-send-command session
1467 (elmo-imap4-spec-mailbox spec))
1468 " (unseen messages)"))))
1470 (defun elmo-imap4-server-diff (spec)
1472 (let ((session (elmo-imap4-get-session spec))
1475 ; (elmo-imap4-commit spec)
1476 (with-current-buffer (elmo-network-session-buffer session)
1477 (setq elmo-imap4-status-callback nil)
1478 (setq elmo-imap4-status-callback-data nil))
1480 (elmo-imap4-send-command-wait session
1484 (elmo-imap4-spec-mailbox spec))
1485 " (unseen messages)")))
1486 (setq response (elmo-imap4-response-value response 'status))
1487 (cons (elmo-imap4-response-value response 'unseen)
1488 (elmo-imap4-response-value response 'messages))))
1490 (defun elmo-imap4-use-cache-p (spec number)
1491 elmo-imap4-use-cache)
1493 (defun elmo-imap4-local-file-p (spec number)
1496 (defun elmo-imap4-port-label (spec)
1498 (if (elmo-imap4-spec-stream-type spec)
1499 (concat "!" (symbol-name
1500 (elmo-network-stream-type-symbol
1501 (elmo-imap4-spec-stream-type spec)))))))
1504 (defsubst elmo-imap4-portinfo (spec)
1505 (list (elmo-imap4-spec-hostname spec) (elmo-imap4-spec-port spec)))
1507 (defun elmo-imap4-plugged-p (spec)
1508 (apply 'elmo-plugged-p
1509 (append (elmo-imap4-portinfo spec)
1510 (list nil (quote (elmo-imap4-port-label spec))))))
1512 (defun elmo-imap4-set-plugged (spec plugged add)
1513 (apply 'elmo-set-plugged plugged
1514 (append (elmo-imap4-portinfo spec)
1515 (list nil nil (quote (elmo-imap4-port-label spec)) add))))
1517 (defalias 'elmo-imap4-sync-number-alist 'elmo-generic-sync-number-alist)
1521 (defvar elmo-imap4-server-eol "\r\n"
1522 "The EOL string sent from the server.")
1524 (defvar elmo-imap4-client-eol "\r\n"
1525 "The EOL string we send to the server.")
1527 (defvar elmo-imap4-status nil)
1528 (defvar elmo-imap4-reached-tag nil)
1530 (defun elmo-imap4-find-next-line ()
1531 "Return point at end of current line, taking into account literals.
1532 Return nil if no complete line has arrived."
1533 (when (re-search-forward (concat elmo-imap4-server-eol "\\|{\\([0-9]+\\)}"
1534 elmo-imap4-server-eol)
1536 (if (match-string 1)
1537 (if (< (point-max) (+ (point) (string-to-number (match-string 1))))
1539 (goto-char (+ (point) (string-to-number (match-string 1))))
1540 (elmo-imap4-find-next-line))
1543 (defun elmo-imap4-sentinel (process string)
1544 (delete-process process))
1546 (defun elmo-imap4-arrival-filter (proc string)
1547 "IMAP process filter."
1548 (with-current-buffer (process-buffer proc)
1549 (elmo-imap4-debug "-> %s" string)
1550 (goto-char (point-max))
1553 (goto-char (point-min))
1554 (while (setq end (elmo-imap4-find-next-line))
1556 (narrow-to-region (point-min) end)
1557 (delete-backward-char (length elmo-imap4-server-eol))
1558 (goto-char (point-min))
1560 (cond ((eq elmo-imap4-status 'initial)
1561 (setq elmo-imap4-current-response
1563 (list 'greeting (elmo-imap4-parse-greeting)))))
1564 ((or (eq elmo-imap4-status 'auth)
1565 (eq elmo-imap4-status 'nonauth)
1566 (eq elmo-imap4-status 'selected)
1567 (eq elmo-imap4-status 'examine))
1568 (setq elmo-imap4-current-response
1570 (elmo-imap4-parse-response)
1571 elmo-imap4-current-response)))
1573 (message "Unknown state %s in arrival filter"
1574 elmo-imap4-status))))
1575 (delete-region (point-min) (point-max)))))))
1579 (defsubst elmo-imap4-forward ()
1580 (or (eobp) (forward-char 1)))
1582 (defsubst elmo-imap4-parse-number ()
1583 (when (looking-at "[0-9]+")
1585 (string-to-number (match-string 0))
1586 (goto-char (match-end 0)))))
1588 (defsubst elmo-imap4-parse-literal ()
1589 (when (looking-at "{\\([0-9]+\\)}\r\n")
1590 (let ((pos (match-end 0))
1591 (len (string-to-number (match-string 1))))
1592 (if (< (point-max) (+ pos len))
1594 (goto-char (+ pos len))
1595 (buffer-substring pos (+ pos len))))))
1596 ;(list ' pos (+ pos len))))))
1598 (defsubst elmo-imap4-parse-string ()
1599 (cond ((eq (char-after (point)) ?\")
1601 (let ((p (point)) (name ""))
1602 (skip-chars-forward "^\"\\\\")
1603 (setq name (buffer-substring p (point)))
1604 (while (eq (char-after (point)) ?\\)
1605 (setq p (1+ (point)))
1607 (skip-chars-forward "^\"\\\\")
1608 (setq name (concat name (buffer-substring p (point)))))
1611 ((eq (char-after (point)) ?{)
1612 (elmo-imap4-parse-literal))))
1614 (defsubst elmo-imap4-parse-nil ()
1615 (if (looking-at "NIL")
1616 (goto-char (match-end 0))))
1618 (defsubst elmo-imap4-parse-nstring ()
1619 (or (elmo-imap4-parse-string)
1620 (and (elmo-imap4-parse-nil)
1623 (defsubst elmo-imap4-parse-astring ()
1624 (or (elmo-imap4-parse-string)
1625 (buffer-substring (point)
1626 (if (re-search-forward "[(){ \r\n%*\"\\]" nil t)
1627 (goto-char (1- (match-end 0)))
1631 (defsubst elmo-imap4-parse-address ()
1633 (when (eq (char-after (point)) ?\()
1634 (elmo-imap4-forward)
1635 (setq address (vector (prog1 (elmo-imap4-parse-nstring)
1636 (elmo-imap4-forward))
1637 (prog1 (elmo-imap4-parse-nstring)
1638 (elmo-imap4-forward))
1639 (prog1 (elmo-imap4-parse-nstring)
1640 (elmo-imap4-forward))
1641 (elmo-imap4-parse-nstring)))
1642 (when (eq (char-after (point)) ?\))
1643 (elmo-imap4-forward)
1646 (defsubst elmo-imap4-parse-address-list ()
1647 (if (eq (char-after (point)) ?\()
1648 (let (address addresses)
1649 (elmo-imap4-forward)
1650 (while (and (not (eq (char-after (point)) ?\)))
1651 ;; next line for MS Exchange bug
1652 (progn (and (eq (char-after (point)) ? ) (elmo-imap4-forward)) t)
1653 (setq address (elmo-imap4-parse-address)))
1654 (setq addresses (cons address addresses)))
1655 (when (eq (char-after (point)) ?\))
1656 (elmo-imap4-forward)
1657 (nreverse addresses)))
1658 (assert (elmo-imap4-parse-nil))))
1660 (defsubst elmo-imap4-parse-mailbox ()
1661 (let ((mailbox (elmo-imap4-parse-astring)))
1662 (if (string-equal "INBOX" (upcase mailbox))
1666 (defun elmo-imap4-parse-greeting ()
1667 "Parse a IMAP greeting."
1668 (cond ((looking-at "\\* OK ")
1669 (setq elmo-imap4-status 'nonauth))
1670 ((looking-at "\\* PREAUTH ")
1671 (setq elmo-imap4-status 'auth))
1672 ((looking-at "\\* BYE ")
1673 (setq elmo-imap4-status 'closed))))
1675 (defun elmo-imap4-parse-response ()
1676 "Parse a IMAP command response."
1678 (case (setq token (elmo-imap4-read (current-buffer)))
1680 (skip-chars-forward " ")
1681 (list 'continue-req (buffer-substring (point) (point-max)))))
1682 (* (case (prog1 (setq token (elmo-imap4-read (current-buffer)))
1683 (elmo-imap4-forward))
1684 (OK (elmo-imap4-parse-resp-text-code))
1685 (NO (elmo-imap4-parse-resp-text-code))
1686 (BAD (elmo-imap4-parse-resp-text-code))
1687 (BYE (elmo-imap4-parse-bye))
1689 (elmo-imap4-parse-flag-list)))
1690 (LIST (list 'list (elmo-imap4-parse-data-list)))
1691 (LSUB (list 'lsub (elmo-imap4-parse-data-list)))
1694 (elmo-imap4-read (concat "("
1695 (buffer-substring (point) (point-max))
1697 (STATUS (elmo-imap4-parse-status))
1699 (NAMESPACE (elmo-imap4-parse-namespace))
1700 (CAPABILITY (list 'capability
1702 (concat "(" (downcase (buffer-substring
1703 (point) (point-max)))
1705 (ACL (elmo-imap4-parse-acl))
1706 (t (case (prog1 (elmo-imap4-read (current-buffer))
1707 (elmo-imap4-forward))
1708 (EXISTS (list 'exists token))
1709 (RECENT (list 'recent token))
1710 (EXPUNGE (list 'expunge token))
1711 (FETCH (elmo-imap4-parse-fetch token))
1712 (t (list 'garbage (buffer-string)))))))
1713 (t (if (not (string-match elmo-imap4-seq-prefix (symbol-name token)))
1714 (list 'garbage (buffer-string))
1715 (case (prog1 (elmo-imap4-read (current-buffer))
1716 (elmo-imap4-forward))
1718 (setq elmo-imap4-parsing nil)
1719 (elmo-imap4-debug "*%s* OK arrived" token)
1720 (setq elmo-imap4-reached-tag token)
1721 (list 'ok (elmo-imap4-parse-resp-text-code))))
1723 (setq elmo-imap4-parsing nil)
1724 (elmo-imap4-debug "*%s* NO arrived" token)
1725 (setq elmo-imap4-reached-tag token)
1727 (when (eq (char-after (point)) ?\[)
1728 (setq code (buffer-substring (point)
1729 (search-forward "]")))
1730 (elmo-imap4-forward))
1731 (setq text (buffer-substring (point) (point-max)))
1732 (list 'no (list code text)))))
1734 (setq elmo-imap4-parsing nil)
1735 (elmo-imap4-debug "*%s* BAD arrived" token)
1736 (setq elmo-imap4-reached-tag token)
1738 (when (eq (char-after (point)) ?\[)
1739 (setq code (buffer-substring (point)
1740 (search-forward "]")))
1741 (elmo-imap4-forward))
1742 (setq text (buffer-substring (point) (point-max)))
1743 (list 'bad (list code text)))))
1744 (t (list 'garbage (buffer-string)))))))))
1746 (defun elmo-imap4-parse-bye ()
1748 (when (eq (char-after (point)) ?\[)
1749 (setq code (buffer-substring (point)
1750 (search-forward "]")))
1751 (elmo-imap4-forward))
1752 (setq text (buffer-substring (point) (point-max)))
1753 (list 'bye (list code text))))
1755 (defun elmo-imap4-parse-text ()
1756 (goto-char (point-min))
1757 (when (search-forward "[" nil t)
1758 (search-forward "]")
1759 (elmo-imap4-forward))
1760 (list 'text (buffer-substring (point) (point-max))))
1762 (defun elmo-imap4-parse-resp-text-code ()
1763 (when (eq (char-after (point)) ?\[)
1764 (elmo-imap4-forward)
1765 (cond ((search-forward "PERMANENTFLAGS " nil t)
1766 (list 'permanentflags (elmo-imap4-parse-flag-list)))
1767 ((search-forward "UIDNEXT " nil t)
1768 (list 'uidnext (elmo-imap4-read (current-buffer))))
1769 ((search-forward "UNSEEN " nil t)
1770 (list 'unseen (elmo-imap4-read (current-buffer))))
1771 ((looking-at "UIDVALIDITY \\([0-9]+\\)")
1772 (list 'uidvalidity (match-string 1)))
1773 ((search-forward "READ-ONLY" nil t)
1774 (list 'read-only t))
1775 ((search-forward "READ-WRITE" nil t)
1776 (list 'read-write t))
1777 ((search-forward "NEWNAME " nil t)
1778 (let (oldname newname)
1779 (setq oldname (elmo-imap4-parse-string))
1780 (elmo-imap4-forward)
1781 (setq newname (elmo-imap4-parse-string))
1782 (list 'newname newname oldname)))
1783 ((search-forward "TRYCREATE" nil t)
1784 (list 'trycreate t))
1785 ((looking-at "APPENDUID \\([0-9]+\\) \\([0-9]+\\)")
1787 (list (match-string 1)
1788 (string-to-number (match-string 2)))))
1789 ((looking-at "COPYUID \\([0-9]+\\) \\([0-9,:]+\\) \\([0-9,:]+\\)")
1790 (list 'copyuid (list (match-string 1)
1793 ((search-forward "ALERT] " nil t)
1794 (message "IMAP server information: %s"
1795 (buffer-substring (point) (point-max))))
1796 (t (list 'unknown)))))
1798 (defun elmo-imap4-parse-data-list ()
1799 (let (flags delimiter mailbox)
1800 (setq flags (elmo-imap4-parse-flag-list))
1801 (when (looking-at " NIL\\| \"\\\\?\\(.\\)\"")
1802 (setq delimiter (match-string 1))
1803 (goto-char (1+ (match-end 0)))
1804 (when (setq mailbox (elmo-imap4-parse-mailbox))
1805 (list mailbox flags delimiter)))))
1807 (defsubst elmo-imap4-parse-header-list ()
1808 (when (eq (char-after (point)) ?\()
1810 (while (not (eq (char-after (point)) ?\)))
1811 (elmo-imap4-forward)
1812 (push (elmo-imap4-parse-astring) strlist))
1813 (elmo-imap4-forward)
1814 (nreverse strlist))))
1816 (defsubst elmo-imap4-parse-fetch-body-section ()
1818 (buffer-substring (point)
1820 (progn (re-search-forward "[] ]" nil t)
1822 (if (eq (char-before) ? )
1824 (mapconcat 'identity
1825 (cons section (elmo-imap4-parse-header-list)) " ")
1826 (search-forward "]" nil t))
1829 (defun elmo-imap4-parse-fetch (response)
1830 (when (eq (char-after (point)) ?\()
1832 (while (not (eq (char-after (point)) ?\)))
1833 (elmo-imap4-forward)
1834 (let ((token (elmo-imap4-fetch-read (current-buffer))))
1835 (elmo-imap4-forward)
1837 (cond ((eq token 'UID)
1838 (list 'uid (condition-case nil
1839 (elmo-imap4-read (current-buffer))
1842 (list 'flags (elmo-imap4-parse-flag-list)))
1843 ((eq token 'ENVELOPE)
1844 (list 'envelope (elmo-imap4-parse-envelope)))
1845 ((eq token 'INTERNALDATE)
1846 (list 'internaldate (elmo-imap4-parse-string)))
1848 (list 'rfc822 (elmo-imap4-parse-nstring)))
1849 ((eq token (intern elmo-imap4-rfc822-header))
1850 (list 'rfc822header (elmo-imap4-parse-nstring)))
1851 ((eq token (intern elmo-imap4-rfc822-text))
1852 (list 'rfc822text (elmo-imap4-parse-nstring)))
1853 ((eq token (intern elmo-imap4-rfc822-size))
1854 (list 'rfc822size (elmo-imap4-read (current-buffer))))
1856 (if (eq (char-before) ?\[)
1859 (upcase (elmo-imap4-parse-fetch-body-section))
1861 (eq (char-after (point)) ?<)
1862 (buffer-substring (1+ (point))
1864 (search-forward ">" nil t)
1866 (progn (elmo-imap4-forward)
1867 (elmo-imap4-parse-nstring)))
1868 (list 'body (elmo-imap4-parse-body))))
1869 ((eq token 'BODYSTRUCTURE)
1870 (list 'bodystructure (elmo-imap4-parse-body)))))
1871 (setq list (cons element list))))
1872 (and elmo-imap4-fetch-callback
1873 (elmo-imap4-fetch-callback
1875 elmo-imap4-fetch-callback-data))
1876 (list 'fetch list))))
1878 (defun elmo-imap4-parse-status ()
1879 (let ((mailbox (elmo-imap4-parse-mailbox))
1881 (when (and mailbox (search-forward "(" nil t))
1882 (while (not (eq (char-after (point)) ?\)))
1885 (let ((token (elmo-imap4-read (current-buffer))))
1886 (cond ((eq token 'MESSAGES)
1887 (list 'messages (elmo-imap4-read (current-buffer))))
1889 (list 'recent (elmo-imap4-read (current-buffer))))
1890 ((eq token 'UIDNEXT)
1891 (list 'uidnext (elmo-imap4-read (current-buffer))))
1892 ((eq token 'UIDVALIDITY)
1893 (and (looking-at " \\([0-9]+\\)")
1894 (prog1 (list 'uidvalidity (match-string 1))
1895 (goto-char (match-end 1)))))
1897 (list 'unseen (elmo-imap4-read (current-buffer))))
1900 "Unknown status data %s in mailbox %s ignored"
1903 (and elmo-imap4-status-callback
1904 (funcall elmo-imap4-status-callback
1906 elmo-imap4-status-callback-data))
1907 (list 'status status)))
1910 (defmacro elmo-imap4-value (value)
1911 (` (if (eq (, value) 'NIL) nil
1914 (defmacro elmo-imap4-nth (pos list)
1915 (` (let ((value (nth (, pos) (, list))))
1916 (elmo-imap4-value value))))
1918 (defun elmo-imap4-parse-namespace ()
1921 (copy-sequence elmo-imap4-extra-namespace-alist)
1922 (elmo-imap4-parse-namespace-subr
1923 (elmo-imap4-read (concat "(" (buffer-substring
1924 (point) (point-max))
1927 (defun elmo-imap4-parse-namespace-subr (ns)
1928 (let (prefix delim namespace-alist default-delim)
1929 ;; 0: personal, 1: other, 2: shared
1931 (setq namespace-alist
1932 (nconc namespace-alist
1936 (setq prefix (elmo-imap4-nth 0 namespace)
1937 delim (elmo-imap4-nth 1 namespace))
1938 (if (and prefix delim
1940 (concat (regexp-quote delim) "\\'")
1942 (setq prefix (substring prefix 0
1943 (match-beginning 0))))
1944 (if (eq (length prefix) 0)
1945 (progn (setq default-delim delim) nil)
1948 (if (string= (downcase prefix) "inbox")
1949 "[Ii][Nn][Bb][Oo][Xx]"
1950 (regexp-quote prefix))
1953 (elmo-imap4-nth i ns))))))
1955 (setq namespace-alist
1956 (nconc namespace-alist
1957 (list (cons "^.*$" default-delim)))))
1960 (defun elmo-imap4-parse-acl ()
1961 (let ((mailbox (elmo-imap4-parse-mailbox))
1962 identifier rights acl)
1963 (while (eq (char-after (point)) ?\ )
1964 (elmo-imap4-forward)
1965 (setq identifier (elmo-imap4-parse-astring))
1966 (elmo-imap4-forward)
1967 (setq rights (elmo-imap4-parse-astring))
1968 (setq acl (append acl (list (cons identifier rights)))))
1969 (list 'acl acl mailbox)))
1971 (defun elmo-imap4-parse-flag-list ()
1972 (let ((str (buffer-substring (+ (point) 1)
1973 (progn (search-forward ")" nil t)
1975 (unless (eq (length str) 0)
1976 (split-string str))))
1978 (defun elmo-imap4-parse-envelope ()
1979 (when (eq (char-after (point)) ?\()
1980 (elmo-imap4-forward)
1981 (vector (prog1 (elmo-imap4-parse-nstring);; date
1982 (elmo-imap4-forward))
1983 (prog1 (elmo-imap4-parse-nstring);; subject
1984 (elmo-imap4-forward))
1985 (prog1 (elmo-imap4-parse-address-list);; from
1986 (elmo-imap4-forward))
1987 (prog1 (elmo-imap4-parse-address-list);; sender
1988 (elmo-imap4-forward))
1989 (prog1 (elmo-imap4-parse-address-list);; reply-to
1990 (elmo-imap4-forward))
1991 (prog1 (elmo-imap4-parse-address-list);; to
1992 (elmo-imap4-forward))
1993 (prog1 (elmo-imap4-parse-address-list);; cc
1994 (elmo-imap4-forward))
1995 (prog1 (elmo-imap4-parse-address-list);; bcc
1996 (elmo-imap4-forward))
1997 (prog1 (elmo-imap4-parse-nstring);; in-reply-to
1998 (elmo-imap4-forward))
1999 (prog1 (elmo-imap4-parse-nstring);; message-id
2000 (elmo-imap4-forward)))))
2002 (defsubst elmo-imap4-parse-string-list ()
2003 (cond ((eq (char-after (point)) ?\();; body-fld-param
2005 (elmo-imap4-forward)
2006 (while (setq str (elmo-imap4-parse-string))
2008 (elmo-imap4-forward))
2009 (nreverse strlist)))
2010 ((elmo-imap4-parse-nil)
2013 (defun elmo-imap4-parse-body-extension ()
2014 (if (eq (char-after (point)) ?\()
2016 (elmo-imap4-forward)
2017 (push (elmo-imap4-parse-body-extension) b-e)
2018 (while (eq (char-after (point)) ?\ )
2019 (elmo-imap4-forward)
2020 (push (elmo-imap4-parse-body-extension) b-e))
2021 (assert (eq (char-after (point)) ?\)))
2022 (elmo-imap4-forward)
2024 (or (elmo-imap4-parse-number)
2025 (elmo-imap4-parse-nstring))))
2027 (defsubst elmo-imap4-parse-body-ext ()
2029 (when (eq (char-after (point)) ?\ );; body-fld-dsp
2030 (elmo-imap4-forward)
2032 (if (eq (char-after (point)) ?\()
2034 (elmo-imap4-forward)
2035 (push (elmo-imap4-parse-string) dsp)
2036 (elmo-imap4-forward)
2037 (push (elmo-imap4-parse-string-list) dsp)
2038 (elmo-imap4-forward))
2039 (assert (elmo-imap4-parse-nil)))
2040 (push (nreverse dsp) ext))
2041 (when (eq (char-after (point)) ?\ );; body-fld-lang
2042 (elmo-imap4-forward)
2043 (if (eq (char-after (point)) ?\()
2044 (push (elmo-imap4-parse-string-list) ext)
2045 (push (elmo-imap4-parse-nstring) ext))
2046 (while (eq (char-after (point)) ?\ );; body-extension
2047 (elmo-imap4-forward)
2048 (setq ext (append (elmo-imap4-parse-body-extension) ext)))))
2051 (defun elmo-imap4-parse-body ()
2053 (when (eq (char-after (point)) ?\()
2054 (elmo-imap4-forward)
2055 (if (eq (char-after (point)) ?\()
2057 (while (and (eq (char-after (point)) ?\()
2058 (setq subbody (elmo-imap4-parse-body)))
2059 (push subbody body))
2060 (elmo-imap4-forward)
2061 (push (elmo-imap4-parse-string) body);; media-subtype
2062 (when (eq (char-after (point)) ?\ );; body-ext-mpart:
2063 (elmo-imap4-forward)
2064 (if (eq (char-after (point)) ?\();; body-fld-param
2065 (push (elmo-imap4-parse-string-list) body)
2066 (push (and (elmo-imap4-parse-nil) nil) body))
2068 (append (elmo-imap4-parse-body-ext) body)));; body-ext-...
2069 (assert (eq (char-after (point)) ?\)))
2070 (elmo-imap4-forward)
2073 (push (elmo-imap4-parse-string) body);; media-type
2074 (elmo-imap4-forward)
2075 (push (elmo-imap4-parse-string) body);; media-subtype
2076 (elmo-imap4-forward)
2077 ;; next line for Sun SIMS bug
2078 (and (eq (char-after (point)) ? ) (elmo-imap4-forward))
2079 (if (eq (char-after (point)) ?\();; body-fld-param
2080 (push (elmo-imap4-parse-string-list) body)
2081 (push (and (elmo-imap4-parse-nil) nil) body))
2082 (elmo-imap4-forward)
2083 (push (elmo-imap4-parse-nstring) body);; body-fld-id
2084 (elmo-imap4-forward)
2085 (push (elmo-imap4-parse-nstring) body);; body-fld-desc
2086 (elmo-imap4-forward)
2087 (push (elmo-imap4-parse-string) body);; body-fld-enc
2088 (elmo-imap4-forward)
2089 (push (elmo-imap4-parse-number) body);; body-fld-octets
2091 ;; ok, we're done parsing the required parts, what comes now is one
2094 ;; envelope (then we're parsing body-type-msg)
2095 ;; body-fld-lines (then we're parsing body-type-text)
2096 ;; body-ext-1part (then we're parsing body-type-basic)
2098 ;; the problem is that the two first are in turn optionally followed
2099 ;; by the third. So we parse the first two here (if there are any)...
2101 (when (eq (char-after (point)) ?\ )
2102 (elmo-imap4-forward)
2104 (cond ((eq (char-after (point)) ?\();; body-type-msg:
2105 (push (elmo-imap4-parse-envelope) body);; envelope
2106 (elmo-imap4-forward)
2107 (push (elmo-imap4-parse-body) body);; body
2108 (elmo-imap4-forward)
2109 (push (elmo-imap4-parse-number) body));; body-fld-lines
2110 ((setq lines (elmo-imap4-parse-number));; body-type-text:
2111 (push lines body));; body-fld-lines
2113 (backward-char)))));; no match...
2115 ;; ...and then parse the third one here...
2117 (when (eq (char-after (point)) ?\ );; body-ext-1part:
2118 (elmo-imap4-forward)
2119 (push (elmo-imap4-parse-nstring) body);; body-fld-md5
2121 (append (elmo-imap4-parse-body-ext) body)));; body-ext-1part..
2123 (assert (eq (char-after (point)) ?\)))
2124 (elmo-imap4-forward)
2127 (provide 'elmo-imap4)
2129 ;;; elmo-imap4.el ends here