f8140ee44e467cb9bed8600b8c168931ff1f5690
[elisp/wanderlust.git] / elmo / elmo-imap4.el
1 ;;; elmo-imap4.el --- IMAP4 Interface for ELMO.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4 ;; Copyright (C) 1999,2000      Kenichi OKADA <okada@opaopa.org>
5 ;; Copyright (C) 2000           OKAZAKI Tetsurou <okazaki@be.to>
6 ;; Copyright (C) 2000           Daiki Ueno <ueno@unixuser.org>
7
8 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
9 ;;      Kenichi OKADA <okada@opaopa.org>
10 ;;      OKAZAKI Tetsurou <okazaki@be.to>
11 ;;      Daiki Ueno <ueno@unixuser.org>
12 ;; Keywords: mail, net news
13
14 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
15
16 ;; This program is free software; you can redistribute it and/or modify
17 ;; it under the terms of the GNU General Public License as published by
18 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; any later version.
20 ;;
21 ;; This program is distributed in the hope that it will be useful,
22 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24 ;; GNU General Public License for more details.
25 ;;
26 ;; You should have received a copy of the GNU General Public License
27 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
28 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
29 ;; Boston, MA 02111-1307, USA.
30 ;;
31
32 ;;; Commentary:
33 ;;
34 ;; Origin of IMAP parser part is imap.el, included in Gnus.
35 ;;
36 ;;    Copyright (C) 1998, 1999, 2000
37 ;;    Free Software Foundation, Inc.
38 ;;    Author: Simon Josefsson <jas@pdc.kth.se>
39 ;;
40
41 (require 'elmo-vars)
42 (require 'elmo-util)
43 (require 'elmo-date)
44 (require 'elmo-msgdb)
45 (require 'elmo-cache)
46 (require 'elmo)
47 (require 'elmo-net)
48 (require 'utf7)
49 (require 'elmo-mime)
50
51 ;;; Code:
52 (eval-when-compile (require 'cl))
53
54 (defvar elmo-imap4-disuse-server-flag-mailbox-regexp "^#mh" ; UW imapd
55   "Regexp to match IMAP4 mailbox names whose message flags on server should be ignored.
56 \(Except `\\Deleted' flag\).")
57
58 (defvar elmo-imap4-overview-fetch-chop-length 200
59   "*Number of overviews to fetch in one request.")
60
61 ;; c.f. rfc2683 3.2.1.5 Long Command Lines
62 ;;
63 ;; "A client should limit the length of the command lines it generates
64 ;;  to approximately 1000 octets (including all quoted strings but not
65 ;;  including literals). If the client is unable to group things into
66 ;;  ranges so that the command line is within that length, it should
67 ;;  split the request into multiple commands. The client should use
68 ;;  literals instead of long quoted strings, in order to keep the command
69 ;;  length down.
70 ;;  For its part, a server should allow for a command line of at least
71 ;;  8000 octets. This provides plenty of leeway for accepting reasonable
72 ;;  length commands from clients. The server should send a BAD response
73 ;;  to a command that does not end within the server's maximum accepted
74 ;;  command length. "
75
76 ;; To limit command line length, chop number set.
77 (defvar elmo-imap4-number-set-chop-length 1000
78   "*Number of messages to specify as a number-set argument for one request.")
79
80 (defvar elmo-imap4-force-login nil
81   "*Non-nil forces to try 'login' if there is no 'auth' capability in imapd.")
82
83 (defvar elmo-imap4-use-select-to-update-status nil
84   "*Some imapd have to send select command to update status.
85 \(ex. UW imapd 4.5-BETA?\).  For these imapd, you must set this variable t.")
86
87 (defvar elmo-imap4-use-modified-utf7 nil
88   "*Use mofidied UTF-7 (rfc2060) encoding for IMAP4 folder name.")
89
90 (defvar elmo-imap4-use-cache t
91   "Use cache in imap4 folder.")
92
93 (defvar elmo-imap4-extra-namespace-alist
94   '(("^\\({.*/nntp}\\).*$" . ".")) ; Default is for UW's remote nntp mailbox...
95   "Extra namespace alist.
96 A list of cons cell like: (REGEXP . DELIMITER).
97 REGEXP should have a grouping for namespace prefix.")
98 ;;
99 ;;; internal variables
100 ;;
101 (defvar elmo-imap4-seq-prefix "elmo-imap4")
102 (defvar elmo-imap4-seqno 0)
103 (defvar elmo-imap4-use-uid t
104   "Use UID as message number.")
105
106 (defvar elmo-imap4-current-response nil)
107 (defvar elmo-imap4-status nil)
108 (defvar elmo-imap4-reached-tag "elmo-imap40")
109
110 ;;; buffer local variables
111 (defvar elmo-imap4-default-hierarchy-delimiter "/")
112
113 (defvar elmo-imap4-server-capability nil)
114 (defvar elmo-imap4-server-namespace nil)
115
116 (defvar elmo-imap4-parsing nil) ; indicates parsing.
117
118 (defvar elmo-imap4-fetch-callback nil)
119 (defvar elmo-imap4-fetch-callback-data nil)
120 (defvar elmo-imap4-status-callback nil)
121 (defvar elmo-imap4-status-callback-data nil)
122
123 (defvar elmo-imap4-server-diff-async-callback nil)
124 (defvar elmo-imap4-server-diff-async-callback-data nil)
125
126 ;;; progress...(no use?)
127 (defvar elmo-imap4-count-progress nil)
128 (defvar elmo-imap4-count-progress-message nil)
129 (defvar elmo-imap4-progress-count nil)
130
131 ;;; XXX Temporal implementation
132 (defvar elmo-imap4-current-msgdb nil)
133 (defvar elmo-imap4-seen-messages nil)
134
135 (defvar elmo-imap4-local-variables
136   '(elmo-imap4-status
137     elmo-imap4-current-response
138     elmo-imap4-seqno
139     elmo-imap4-parsing
140     elmo-imap4-reached-tag
141     elmo-imap4-count-progress
142     elmo-imap4-count-progress-message
143     elmo-imap4-progress-count
144     elmo-imap4-fetch-callback
145     elmo-imap4-fetch-callback-data
146     elmo-imap4-status-callback
147     elmo-imap4-status-callback-data
148     elmo-imap4-current-msgdb
149     elmo-imap4-seen-messages))
150
151 ;;;;
152
153 (defconst elmo-imap4-quoted-specials-list '(?\\ ?\"))
154
155 (defconst elmo-imap4-non-atom-char-regex
156   (eval-when-compile
157     (concat "[^" "]!#$&'+,./0-9:;<=>?@A-Z[^_`a-z|}~-" "]")))
158
159 (defconst elmo-imap4-non-text-char-regex
160   (eval-when-compile
161     (concat "[^"
162             "]\x01-\x09\x0b\x0c\x0e-\x1f\x7f !\"#$%&'()*+,./0-9:;<=>?@A-Z[\\^_`a-z{|}~-"
163             "]")))
164
165 (defconst elmo-imap4-literal-threshold 1024
166  "Limitation of characters that can be used in a quoted string.")
167
168 ;; For debugging.
169 (defvar elmo-imap4-debug nil
170   "Non-nil forces IMAP4 folder as debug mode.
171 Debug information is inserted in the buffer \"*IMAP4 DEBUG*\"")
172
173 (defvar elmo-imap4-debug-inhibit-logging nil)
174
175 ;;; ELMO IMAP4 folder
176 (eval-and-compile
177   (luna-define-class elmo-imap4-folder (elmo-net-folder)
178                      (mailbox))
179   (luna-define-internal-accessors 'elmo-imap4-folder))
180
181 ;;; Session
182 (eval-and-compile
183   (luna-define-class elmo-imap4-session (elmo-network-session)
184                      (capability current-mailbox read-only))
185   (luna-define-internal-accessors 'elmo-imap4-session))
186
187 ;;; MIME-ELMO-IMAP Location
188 (eval-and-compile
189   (luna-define-class mime-elmo-imap-location
190                      (mime-imap-location)
191                      (folder number rawbuf strategy))
192   (luna-define-internal-accessors 'mime-elmo-imap-location))
193
194 ;;; Debug
195 (defmacro elmo-imap4-debug (message &rest args)
196   (` (if elmo-imap4-debug
197          (elmo-imap4-debug-1 (, message) (,@ args)))))
198
199 (defun elmo-imap4-debug-1 (message &rest args)
200   (with-current-buffer (get-buffer-create "*IMAP4 DEBUG*")
201     (goto-char (point-max))
202     (if elmo-imap4-debug-inhibit-logging
203         (insert "NO LOGGING\n")
204       (insert (apply 'format message args) "\n"))))
205
206 (defsubst elmo-imap4-decode-folder-string (string)
207   (if elmo-imap4-use-modified-utf7
208       (utf7-decode-string string 'imap)
209     string))
210
211 (defsubst elmo-imap4-encode-folder-string (string)
212   (if elmo-imap4-use-modified-utf7
213       (utf7-encode-string string 'imap)
214     string))
215
216 ;;; Response
217
218 (defmacro elmo-imap4-response-continue-req-p (response)
219   "Returns non-nil if RESPONSE is '+' response."
220   (` (assq 'continue-req (, response))))
221
222 (defmacro elmo-imap4-response-ok-p (response)
223   "Returns non-nil if RESPONSE is an 'OK' response."
224   (` (assq 'ok (, response))))
225
226 (defmacro elmo-imap4-response-bye-p (response)
227   "Returns non-nil if RESPONSE is an 'BYE' response."
228   (` (assq 'bye (, response))))
229
230 (defmacro elmo-imap4-response-garbage-p (response)
231   "Returns non-nil if RESPONSE is an 'garbage' response."
232   (` (assq 'garbage (, response))))
233
234 (defmacro elmo-imap4-response-value (response symbol)
235   "Get value of the SYMBOL from RESPONSE."
236   (` (nth 1 (assq (, symbol) (, response)))))
237
238 (defsubst elmo-imap4-response-value-all (response symbol)
239   "Get all value of the SYMBOL from RESPONSE."
240   (let (matched)
241     (while response
242       (if (eq (car (car response)) symbol)
243           (setq matched (nconc matched (nth 1 (car response)))))
244       (setq response (cdr response)))
245     matched))
246
247 (defmacro elmo-imap4-response-error-text (response)
248   "Returns text of NO, BAD, BYE response."
249   (` (nth 1 (or (elmo-imap4-response-value (, response) 'no)
250                 (elmo-imap4-response-value (, response) 'bad)
251                 (elmo-imap4-response-value (, response) 'bye)))))
252
253 (defmacro elmo-imap4-response-bodydetail-text (response)
254   "Returns text of BODY[section]<partial>."
255   (` (nth 3 (assq 'bodydetail (, response)))))
256
257 ;;; Session commands.
258
259 ; (defun elmo-imap4-send-command-wait (session command)
260 ;   "Send COMMAND to the SESSION and wait for response.
261 ; Returns RESPONSE (parsed lisp object) of IMAP session."
262 ;   (elmo-imap4-read-response session
263 ;                           (elmo-imap4-send-command
264 ;                            session
265 ;                            command)))
266
267 (defun elmo-imap4-send-command-wait (session command)
268   "Send COMMAND to the SESSION.
269 Returns RESPONSE (parsed lisp object) of IMAP session.
270 If response is not `OK', causes error with IMAP response text."
271   (elmo-imap4-accept-ok session
272                         (elmo-imap4-send-command
273                          session
274                          command)))
275
276 (defun elmo-imap4-send-command (session command)
277   "Send COMMAND to the SESSION.
278 Returns a TAG string which is assigned to the COMMAND."
279   (let* ((command-args (if (listp command)
280                            command
281                          (list command)))
282          (process (elmo-network-session-process-internal session))
283          cmdstr tag token kind)
284     (with-current-buffer (process-buffer process)
285       (setq tag (concat elmo-imap4-seq-prefix
286                         (number-to-string
287                          (setq elmo-imap4-seqno (+ 1 elmo-imap4-seqno)))))
288       (setq cmdstr (concat tag " "))
289       ;; (erase-buffer) No need.
290       (goto-char (point-min))
291       (when (elmo-imap4-response-bye-p elmo-imap4-current-response)
292         (elmo-imap4-process-bye session))
293       (setq elmo-imap4-current-response nil)
294       (when elmo-imap4-parsing
295         (message "Waiting for IMAP response...")
296         (accept-process-output (elmo-network-session-process-internal
297                                 session))
298         (message "Waiting for IMAP response...done"))
299       (setq elmo-imap4-parsing t)
300       (elmo-imap4-debug "<-(%s)- %s" tag command)
301       (while (setq token (car command-args))
302         (cond ((stringp token)   ; formatted
303                (setq cmdstr (concat cmdstr token)))
304               ((listp token)     ; unformatted
305                (setq kind (car token))
306                (cond ((eq kind 'atom)
307                       (setq cmdstr (concat cmdstr (nth 1 token))))
308                      ((eq kind 'quoted)
309                       (setq cmdstr (concat
310                                     cmdstr
311                                     (elmo-imap4-format-quoted (nth 1 token)))))
312                      ((eq kind 'literal)
313                       (setq cmdstr (concat cmdstr
314                                            (format "{%d}" (nth 2 token))))
315                       (process-send-string process cmdstr)
316                       (process-send-string process "\r\n")
317                       (setq cmdstr nil)
318                       (elmo-imap4-accept-continue-req session)
319                       (cond ((stringp (nth 1 token))
320                              (setq cmdstr (nth 1 token)))
321                             ((bufferp (nth 1 token))
322                              (with-current-buffer (nth 1 token)
323                                (process-send-region
324                                 process
325                                 (point-min)
326                                 (+ (point-min) (nth 2 token)))))
327                             (t
328                              (error "Wrong argument for literal"))))
329                      (t
330                       (error "Unknown token kind %s" kind))))
331               (t
332                (error "Invalid argument")))
333         (setq command-args (cdr command-args)))
334       (if cmdstr
335           (process-send-string process cmdstr))
336       (process-send-string process "\r\n")
337       tag)))
338
339 (defun elmo-imap4-send-string (session string)
340   "Send STRING to the SESSION."
341   (with-current-buffer (process-buffer
342                         (elmo-network-session-process-internal session))
343     (setq elmo-imap4-current-response nil)
344     (goto-char (point-min))
345     (elmo-imap4-debug "<-- %s" string)
346     (process-send-string (elmo-network-session-process-internal session)
347                          string)
348     (process-send-string (elmo-network-session-process-internal session)
349                          "\r\n")))
350
351 (defun elmo-imap4-read-response (session tag)
352   "Read parsed response from SESSION.
353 TAG is the tag of the command"
354   (with-current-buffer (process-buffer
355                         (elmo-network-session-process-internal session))
356     (while (not (or (string= tag elmo-imap4-reached-tag)
357                     (elmo-imap4-response-bye-p elmo-imap4-current-response)
358                     (when (elmo-imap4-response-garbage-p
359                            elmo-imap4-current-response)
360                       (message "Garbage response: %s"
361                                (elmo-imap4-response-value
362                                 elmo-imap4-current-response
363                                 'garbage))
364                       t)))
365       (when (memq (process-status
366                    (elmo-network-session-process-internal session))
367                   '(open run))
368         (accept-process-output (elmo-network-session-process-internal session)
369                                1)))
370     (elmo-imap4-debug "=>%s" (prin1-to-string elmo-imap4-current-response))
371     (setq elmo-imap4-parsing nil)
372     elmo-imap4-current-response))
373
374 (defsubst elmo-imap4-read-untagged (process)
375   (with-current-buffer (process-buffer process)
376     (while (not elmo-imap4-current-response)
377       (accept-process-output process 1))
378     (elmo-imap4-debug "=>%s" (prin1-to-string elmo-imap4-current-response))
379     elmo-imap4-current-response))
380
381 (defun elmo-imap4-read-continue-req (session)
382   "Returns a text following to continue-req in SESSION.
383 If response is not `+' response, returns nil."
384   (elmo-imap4-response-value
385    (elmo-imap4-read-untagged
386     (elmo-network-session-process-internal session))
387    'continue-req))
388
389 (defun elmo-imap4-process-bye (session)
390   (with-current-buffer (elmo-network-session-buffer session)
391     (let ((r elmo-imap4-current-response))
392       (setq elmo-imap4-current-response nil)
393       (elmo-network-close-session session)
394       (signal 'elmo-imap4-bye-error
395               (list (concat (elmo-imap4-response-error-text r))
396                     "Try Again")))))
397
398 (defun elmo-imap4-accept-continue-req (session)
399   "Returns non-nil if `+' (continue-req) response is arrived in SESSION.
400 If response is not `+' response, cause an error."
401   (let (response)
402     (setq response
403           (elmo-imap4-read-untagged
404            (elmo-network-session-process-internal session)))
405     (or (elmo-imap4-response-continue-req-p response)
406         (error "IMAP error: %s"
407                (or (elmo-imap4-response-error-text response)
408                    "No continut-req from server.")))))
409
410 (defun elmo-imap4-read-ok (session tag)
411   "Returns non-nil if `OK' response of the command with TAG is arrived
412 in SESSION. If response is not `OK' response, returns nil."
413   (elmo-imap4-response-ok-p
414    (elmo-imap4-read-response session tag)))
415
416 (defun elmo-imap4-accept-ok (session tag)
417   "Accept only `OK' response from SESSION.
418 If response is not `OK' response, causes error with IMAP response text."
419   (let ((response (elmo-imap4-read-response session tag)))
420     (if (elmo-imap4-response-ok-p response)
421         response
422       (if (elmo-imap4-response-bye-p response)
423           (elmo-imap4-process-bye session)
424         (error "IMAP error: %s"
425                (or (elmo-imap4-response-error-text response)
426                    "No `OK' response from server."))))))
427
428 ;;; MIME-ELMO-IMAP Location
429 (luna-define-method mime-imap-location-section-body ((location
430                                                       mime-elmo-imap-location)
431                                                      section)
432   (if (and (stringp section)
433            (string= section "HEADER"))
434       ;; Even in the section mode, header fields should be saved to the
435       ;; raw buffer .
436       (with-current-buffer (mime-elmo-imap-location-rawbuf-internal location)
437         (erase-buffer)
438         (elmo-message-fetch
439          (mime-elmo-imap-location-folder-internal location)
440          (mime-elmo-imap-location-number-internal location)
441          (mime-elmo-imap-location-strategy-internal location)
442          section
443          (current-buffer)
444          'unseen)
445         (buffer-string))
446     (elmo-message-fetch
447      (mime-elmo-imap-location-folder-internal location)
448      (mime-elmo-imap-location-number-internal location)
449      (mime-elmo-imap-location-strategy-internal location)
450      section
451      nil 'unseen)))
452
453
454 (luna-define-method mime-imap-location-bodystructure
455   ((location mime-elmo-imap-location))
456   (elmo-imap4-fetch-bodystructure
457    (mime-elmo-imap-location-folder-internal location)
458    (mime-elmo-imap-location-number-internal location)
459    (mime-elmo-imap-location-strategy-internal location)))
460
461 (luna-define-method mime-imap-location-fetch-entity-p
462   ((location mime-elmo-imap-location) entity)
463   (or (not elmo-message-displaying) ; Fetching entity to save or force display.
464       ;; cache exists
465       (file-exists-p
466        (expand-file-name
467         (mmimap-entity-section (mime-entity-node-id-internal entity))
468         (elmo-fetch-strategy-cache-path
469          (mime-elmo-imap-location-strategy-internal location))))
470       ;; not too large to fetch.
471       (> elmo-message-fetch-threshold
472          (or (mime-imap-entity-size-internal entity) 0))))
473
474 ;;;
475
476 (defun elmo-imap4-session-check (session)
477   (with-current-buffer (elmo-network-session-buffer session)
478     (setq elmo-imap4-fetch-callback nil)
479     (setq elmo-imap4-fetch-callback-data nil))
480   (elmo-imap4-send-command session "check"))
481
482 (defun elmo-imap4-atom-p (string)
483   "Return t if STRING is an atom defined in rfc2060."
484   (if (string= string "")
485       nil
486     (save-match-data
487       (not (string-match elmo-imap4-non-atom-char-regex string)))))
488
489 (defun elmo-imap4-quotable-p (string)
490   "Return t if STRING can be formatted as a quoted defined in rfc2060."
491   (save-match-data
492     (not (string-match elmo-imap4-non-text-char-regex string))))
493
494 (defun elmo-imap4-nil (string)
495   "Return a list represents the special atom \"NIL\" defined in rfc2060, \
496 if STRING is nil.
497 Otherwise return nil."
498   (if (eq string nil)
499       (list 'atom "NIL")))
500
501 (defun elmo-imap4-atom (string)
502   "Return a list represents STRING as an atom defined in rfc2060.
503 Return nil if STRING is not an atom.  See `elmo-imap4-atom-p'."
504   (if (elmo-imap4-atom-p string)
505       (list 'atom string)))
506
507 (defun elmo-imap4-quoted (string)
508   "Return a list represents STRING as a quoted defined in rfc2060.
509 Return nil if STRING can not be formatted as a quoted.  See `elmo-imap4-quotable-p'."
510   (if (elmo-imap4-quotable-p string)
511       (list 'quoted string)))
512
513 (defun elmo-imap4-literal-1 (string-or-buffer length)
514   "Internal function for `elmo-imap4-literal' and `elmo-imap4-buffer-literal'.
515 Return a list represents STRING-OR-BUFFER as a literal defined in rfc2060.
516 STRING-OR-BUFFER must be an encoded string or a single-byte string or a single-byte buffer.
517 LENGTH must be the number of octets for STRING-OR-BUFFER."
518   (list 'literal string-or-buffer length))
519
520 (defun elmo-imap4-literal (string)
521   "Return a list represents STRING as a literal defined in rfc2060.
522 STRING must be an encoded or a single-byte string."
523   (elmo-imap4-literal-1 string (length string)))
524
525 (defun elmo-imap4-buffer-literal (buffer)
526   "Return a list represents BUFFER as a literal defined in rfc2060.
527 BUFFER must be a single-byte buffer."
528   (elmo-imap4-literal-1 buffer (with-current-buffer buffer
529                                  (buffer-size))))
530
531 (defun elmo-imap4-string-1 (string length)
532   "Internal function for `elmo-imap4-string' and `elmo-imap4-buffer-string'.
533 Return a list represents STRING as a string defined in rfc2060.
534 STRING must be an encoded or a single-byte string.
535 LENGTH must be the number of octets for STRING."
536   (or (elmo-imap4-quoted string)
537       (elmo-imap4-literal-1 string length)))
538
539 (defun elmo-imap4-string (string)
540   "Return a list represents STRING as a string defined in rfc2060.
541 STRING must be an encoded or a single-byte string."
542   (let ((length (length string)))
543     (if (< elmo-imap4-literal-threshold length)
544         (elmo-imap4-literal-1 string length)
545       (elmo-imap4-string-1 string length))))
546
547 (defun elmo-imap4-buffer-string (buffer)
548   "Return a list represents BUFFER as a string defined in rfc2060.
549 BUFFER must be a single-byte buffer."
550   (let ((length (with-current-buffer buffer
551                   (buffer-size))))
552     (if (< elmo-imap4-literal-threshold length)
553         (elmo-imap4-literal-1 buffer length)
554       (elmo-imap4-string-1 (with-current-buffer buffer
555                              (buffer-string))
556                            length))))
557
558 (defun elmo-imap4-astring-1 (string length)
559   "Internal function for `elmo-imap4-astring' and `elmo-imap4-buffer-astring'.
560 Return a list represents STRING as an astring defined in rfc2060.
561 STRING must be an encoded or a single-byte string.
562 LENGTH must be the number of octets for STRING."
563   (or (elmo-imap4-atom string)
564       (elmo-imap4-string-1 string length)))
565
566 (defun elmo-imap4-astring (string)
567   "Return a list represents STRING as an astring defined in rfc2060.
568 STRING must be an encoded or a single-byte string."
569   (let ((length (length string)))
570     (if (< elmo-imap4-literal-threshold length)
571         (elmo-imap4-literal-1 string length)
572       (elmo-imap4-astring-1 string length))))
573
574 (defun elmo-imap4-buffer-astring (buffer)
575   "Return a list represents BUFFER as an astring defined in rfc2060.
576 BUFFER must be a single-byte buffer."
577   (let ((length (with-current-buffer buffer
578                   (buffer-size))))
579     (if (< elmo-imap4-literal-threshold length)
580         (elmo-imap4-literal-1 buffer length)
581       (elmo-imap4-astring-1 (with-current-buffer buffer
582                               (buffer-string))
583                             length))))
584
585 (defun elmo-imap4-nstring (string)
586   "Return a list represents STRING as a nstring defined in rfc2060.
587 STRING must be an encoded or a single-byte string."
588    (or (elmo-imap4-nil string)
589        (elmo-imap4-string string)))
590
591 (defun elmo-imap4-buffer-nstring (buffer)
592   "Return a list represents BUFFER as a nstring defined in rfc2060.
593 BUFFER must be a single-byte buffer."
594    (or (elmo-imap4-nil buffer)
595        (elmo-imap4-buffer-string buffer)))
596
597 (defalias 'elmo-imap4-mailbox 'elmo-imap4-astring)
598 (defalias 'elmo-imap4-field-body 'elmo-imap4-astring)
599 (defalias 'elmo-imap4-userid 'elmo-imap4-astring)
600 (defalias 'elmo-imap4-password 'elmo-imap4-astring)
601
602 (defun elmo-imap4-format-quoted (string)
603   "Return STRING in a form of the quoted-string defined in rfc2060."
604   (concat "\""
605           (std11-wrap-as-quoted-pairs string elmo-imap4-quoted-specials-list)
606           "\""))
607
608 (defsubst elmo-imap4-response-get-selectable-mailbox-list (response)
609   (delq nil
610         (mapcar
611          (lambda (entry)
612            (if (and (eq 'list (car entry))
613                     (not (elmo-string-member-ignore-case "\\Noselect" (nth 1 (nth 1 entry)))))
614                (car (nth 1 entry))))
615          response)))
616
617 (defun elmo-imap4-fetch-bodystructure (folder number strategy)
618   "Fetch BODYSTRUCTURE for the message in the FOLDER with NUMBER using STRATEGY."
619   (if (elmo-fetch-strategy-use-cache strategy)
620       (elmo-object-load
621        (elmo-file-cache-expand-path
622         (elmo-fetch-strategy-cache-path strategy)
623         "bodystructure"))
624     (let ((session (elmo-imap4-get-session folder))
625           bodystructure)
626       (elmo-imap4-session-select-mailbox
627        session
628        (elmo-imap4-folder-mailbox-internal folder))
629       (with-current-buffer (elmo-network-session-buffer session)
630         (setq elmo-imap4-fetch-callback nil)
631         (setq elmo-imap4-fetch-callback-data nil))
632       (prog1 (setq bodystructure
633                    (elmo-imap4-response-value
634                     (elmo-imap4-response-value
635                      (elmo-imap4-send-command-wait
636                       session
637                       (format
638                        (if elmo-imap4-use-uid
639                            "uid fetch %s bodystructure"
640                          "fetch %s bodystructure")
641                        number))
642                      'fetch)
643                     'bodystructure))
644         (when (elmo-fetch-strategy-save-cache strategy)
645           (elmo-file-cache-delete
646            (elmo-fetch-strategy-cache-path strategy))
647           (elmo-object-save
648            (elmo-file-cache-expand-path
649             (elmo-fetch-strategy-cache-path strategy)
650             "bodystructure")
651            bodystructure))))))
652
653 ;;; Backend methods.
654 (luna-define-method elmo-create-folder-plugged ((folder elmo-imap4-folder))
655   (elmo-imap4-send-command-wait
656    (elmo-imap4-get-session folder)
657    (list "create " (elmo-imap4-mailbox
658                     (elmo-imap4-folder-mailbox-internal folder)))))
659
660 (defun elmo-imap4-get-session (folder &optional if-exists)
661   (elmo-network-get-session 'elmo-imap4-session
662                             (concat
663                              (if (elmo-folder-biff-internal folder)
664                                  "BIFF-")
665                              "IMAP")
666                             folder if-exists))
667
668 (defun elmo-imap4-session-select-mailbox (session mailbox
669                                                   &optional force no-error)
670   "Select MAILBOX in SESSION.
671 If optional argument FORCE is non-nil, select mailbox even if current mailbox
672 is same as MAILBOX.
673 If second optional argument NO-ERROR is non-nil, don't cause an error when
674 selecting folder was failed.
675 If NO-ERROR is 'notify-bye, only BYE response is reported as error.
676 Returns response value if selecting folder succeed. "
677   (when (or force
678             (not (string=
679                   (elmo-imap4-session-current-mailbox-internal session)
680                   mailbox)))
681     (let (response result)
682       (unwind-protect
683           (setq response
684                 (elmo-imap4-read-response
685                  session
686                  (elmo-imap4-send-command
687                   session
688                   (list
689                    "select "
690                    (elmo-imap4-mailbox mailbox)))))
691         (if (setq result (elmo-imap4-response-ok-p response))
692             (progn
693               (elmo-imap4-session-set-current-mailbox-internal session mailbox)
694               (elmo-imap4-session-set-read-only-internal
695                session
696                (nth 1 (assq 'read-only (assq 'ok response)))))
697           (elmo-imap4-session-set-current-mailbox-internal session nil)
698           (if (and (eq no-error 'notify-bye)
699                    (elmo-imap4-response-bye-p response))
700               (elmo-imap4-process-bye session)
701             (unless no-error
702               (error "%s"
703                      (or (elmo-imap4-response-error-text response)
704                          (format "Select %s failed" mailbox)))))))
705       (and result response))))
706
707 (defun elmo-imap4-check-validity (spec validity-file)
708 ;;; Not used.
709 ;;;(elmo-imap4-send-command-wait
710 ;;;(elmo-imap4-get-session spec)
711 ;;;(list "status "
712 ;;;      (elmo-imap4-mailbox
713 ;;;       (elmo-imap4-spec-mailbox spec))
714 ;;;      " (uidvalidity)")))
715   )
716
717 (defun elmo-imap4-sync-validity  (spec validity-file)
718   ;; Not used.
719   )
720
721 (defun elmo-imap4-list (folder flag)
722   (let ((session (elmo-imap4-get-session folder)))
723     (elmo-imap4-session-select-mailbox
724      session
725      (elmo-imap4-folder-mailbox-internal folder))
726     (elmo-imap4-response-value
727      (elmo-imap4-send-command-wait
728       session
729       (format (if elmo-imap4-use-uid "uid search %s"
730                 "search %s") flag))
731      'search)))
732
733 (defvar elmo-imap4-rfc822-size "RFC822\.SIZE")
734 (defvar elmo-imap4-rfc822-text "RFC822\.TEXT")
735 (defvar elmo-imap4-rfc822-header "RFC822\.HEADER")
736 (defvar elmo-imap4-header-fields "HEADER\.FIELDS")
737
738 (defun elmo-imap4-make-number-set-list (msg-list &optional chop-length)
739   "Make RFC2060's message set specifier from MSG-LIST.
740 Returns a list of (NUMBER . SET-STRING).
741 SET-STRING is the message set specifier described in RFC2060.
742 NUMBER is contained message number in SET-STRING.
743 Every SET-STRING does not contain number of messages longer than CHOP-LENGTH.
744 If CHOP-LENGTH is not specified, message set is not chopped."
745   (let (count cont-list set-list)
746     (setq msg-list (sort (copy-sequence msg-list) '<))
747     (while msg-list
748       (setq cont-list nil)
749       (setq count 0)
750       (unless chop-length
751         (setq chop-length (length msg-list)))
752       (while (and (not (null msg-list))
753                   (< count chop-length))
754         (setq cont-list
755               (elmo-number-set-append
756                cont-list (car msg-list)))
757         (incf count)
758         (setq msg-list (cdr msg-list)))
759       (setq set-list
760             (cons
761              (cons
762               count
763               (mapconcat
764                (lambda (x)
765                  (cond ((consp x)
766                         (format "%s:%s" (car x) (cdr x)))
767                        ((integerp x)
768                         (int-to-string x))))
769                cont-list
770                ","))
771              set-list)))
772     (nreverse set-list)))
773
774 ;;
775 ;; app-data:
776 ;; cons of flag-table and folder structure
777 (defsubst elmo-imap4-fetch-callback-1-subr (entity flags app-data)
778   "A msgdb entity callback function."
779   (let ((use-flag (elmo-folder-use-flag-p (cdr app-data)))
780         (flag-table (car app-data))
781         (msg-id (elmo-message-entity-field entity 'message-id))
782         saved-flags flag-list)
783 ;;    (when (elmo-string-member-ignore-case "\\Flagged" flags)
784 ;;      (elmo-msgdb-global-mark-set msg-id elmo-msgdb-important-mark))
785     (setq saved-flags (elmo-flag-table-get flag-table msg-id)
786           flag-list
787           (if use-flag
788               (append
789                (and (elmo-string-member-ignore-case "\\Recent" flags)
790                     '(new))
791                (and (elmo-string-member-ignore-case "\\Flagged" flags)
792                     '(important))
793                (and (not (elmo-string-member-ignore-case "\\Seen" flags))
794                     '(unread))
795                (and (elmo-string-member-ignore-case "\\Answered" flags)
796                     '(answered))
797                (and (elmo-file-cache-exists-p msg-id)
798                     '(cached)))
799             saved-flags))
800     (when (and (or (memq 'important flag-list)
801                    (memq 'answered flag-list))
802                (memq 'unread flag-list))
803       (setq elmo-imap4-seen-messages
804             (cons (elmo-message-entity-number entity)
805                   elmo-imap4-seen-messages)))
806     (elmo-msgdb-append-entity elmo-imap4-current-msgdb
807                               entity
808                               flag-list)))
809
810 ;; Current buffer is process buffer.
811 (defun elmo-imap4-fetch-callback-1 (element app-data)
812   (let ((handler (elmo-msgdb-message-entity-handler elmo-imap4-current-msgdb)))
813     (elmo-imap4-fetch-callback-1-subr
814      (with-temp-buffer
815        (insert (or (elmo-imap4-response-bodydetail-text element)
816                    ""))
817        ;; Delete CR.
818        (goto-char (point-min))
819        (while (search-forward "\r\n" nil t)
820          (replace-match "\n"))
821        (elmo-msgdb-create-message-entity-from-buffer
822         handler
823         (elmo-imap4-response-value element 'uid)
824         :size (elmo-imap4-response-value element 'rfc822size)))
825      (elmo-imap4-response-value element 'flags)
826      app-data)))
827
828 (defun elmo-imap4-parse-capability (string)
829   (if (string-match "^\\*\\(.*\\)$" string)
830       (read
831        (concat "(" (downcase (elmo-match-string 1 string)) ")"))))
832
833 (defun elmo-imap4-clear-login (session)
834   (let ((elmo-imap4-debug-inhibit-logging t))
835     (or
836      (elmo-imap4-read-ok
837       session
838       (elmo-imap4-send-command
839        session
840        (list "login "
841              (elmo-imap4-userid (elmo-network-session-user-internal session))
842              " "
843              (elmo-imap4-password
844               (elmo-get-passwd (elmo-network-session-password-key session))))))
845      (signal 'elmo-authenticate-error '(elmo-imap4-clear-login)))))
846
847 (defun elmo-imap4-auth-login (session)
848   (let ((tag (elmo-imap4-send-command session "authenticate login"))
849         (elmo-imap4-debug-inhibit-logging t))
850     (or (elmo-imap4-read-continue-req session)
851         (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
852     (elmo-imap4-send-string session
853                             (elmo-base64-encode-string
854                              (elmo-network-session-user-internal session)))
855     (or (elmo-imap4-read-continue-req session)
856         (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
857     (elmo-imap4-send-string session
858                             (elmo-base64-encode-string
859                              (elmo-get-passwd
860                               (elmo-network-session-password-key session))))
861     (or (elmo-imap4-read-ok session tag)
862         (signal 'elmo-authenticate-error '(elmo-imap4-auth-login)))
863     (setq elmo-imap4-status 'auth)))
864
865 (luna-define-method
866   elmo-network-initialize-session-buffer :after ((session
867                                                   elmo-imap4-session) buffer)
868   (with-current-buffer buffer
869     (mapcar 'make-variable-buffer-local elmo-imap4-local-variables)
870     (setq elmo-imap4-seqno 0)
871     (setq elmo-imap4-status 'initial)))
872
873 (luna-define-method elmo-network-initialize-session ((session
874                                                       elmo-imap4-session))
875   (let ((process (elmo-network-session-process-internal session)))
876     (with-current-buffer (process-buffer process)
877       ;; Skip garbage output from process before greeting.
878       (while (and (memq (process-status process) '(open run))
879                   (goto-char (point-max))
880                   (forward-line -1)
881                   (not (elmo-imap4-parse-greeting)))
882         (accept-process-output process 1))
883       (set-process-filter process 'elmo-imap4-arrival-filter)
884       (set-process-sentinel process 'elmo-imap4-sentinel)
885 ;;;   (while (and (memq (process-status process) '(open run))
886 ;;;               (eq elmo-imap4-status 'initial))
887 ;;;     (message "Waiting for server response...")
888 ;;;     (accept-process-output process 1))
889 ;;;   (message "")
890       (unless (memq elmo-imap4-status '(nonauth auth))
891         (signal 'elmo-open-error
892                 (list 'elmo-network-initialize-session)))
893       (elmo-imap4-session-set-capability-internal
894        session
895        (elmo-imap4-response-value
896         (elmo-imap4-send-command-wait session "capability")
897         'capability))
898       (when (eq (elmo-network-stream-type-symbol
899                  (elmo-network-session-stream-type-internal session))
900                 'starttls)
901         (or (memq 'starttls
902                   (elmo-imap4-session-capability-internal session))
903             (signal 'elmo-open-error
904                     '(elmo-imap4-starttls-error)))
905         (elmo-imap4-send-command-wait session "starttls")
906         (starttls-negotiate process)
907         (elmo-imap4-session-set-capability-internal
908          session
909          (elmo-imap4-response-value
910           (elmo-imap4-send-command-wait session "capability")
911           'capability))))))
912
913 (luna-define-method elmo-network-authenticate-session ((session
914                                                         elmo-imap4-session))
915   (with-current-buffer (process-buffer
916                         (elmo-network-session-process-internal session))
917     (let* ((auth (elmo-network-session-auth-internal session))
918            (auth (if (listp auth) auth (list auth))))
919       (unless (or (eq elmo-imap4-status 'auth)
920                   (null auth))
921         (cond
922          ((eq 'clear (car auth))
923           (elmo-imap4-clear-login session))
924          ((eq 'login (car auth))
925           (elmo-imap4-auth-login session))
926          (t
927           (let* ((elmo-imap4-debug-inhibit-logging t)
928                  (sasl-mechanisms
929                   (delq nil
930                         (mapcar
931                          '(lambda (cap)
932                             (if (string-match "^auth=\\(.*\\)$"
933                                               (symbol-name cap))
934                                 (match-string 1 (upcase (symbol-name cap)))))
935                          (elmo-imap4-session-capability-internal session))))
936                  (mechanism
937                   (sasl-find-mechanism
938                    (delq nil
939                          (mapcar '(lambda (cap) (upcase (symbol-name cap)))
940                                  (if (listp auth)
941                                      auth
942                                    (list auth)))))) ;)
943                  client name step response tag
944                  sasl-read-passphrase)
945             (unless mechanism
946               (if (or elmo-imap4-force-login
947                       (y-or-n-p
948                        (format
949                         "There's no %s capability in server. continue?"
950                         (elmo-list-to-string
951                          (elmo-network-session-auth-internal session)))))
952                   (setq mechanism (sasl-find-mechanism
953                                    sasl-mechanisms))
954                 (signal 'elmo-authenticate-error
955                         '(elmo-imap4-auth-no-mechanisms))))
956             (setq client
957                   (sasl-make-client
958                    mechanism
959                    (elmo-network-session-user-internal session)
960                    "imap"
961                    (elmo-network-session-server-internal session)))
962 ;;;         (if elmo-imap4-auth-user-realm
963 ;;;             (sasl-client-set-property client 'realm elmo-imap4-auth-user-realm))
964             (setq name (sasl-mechanism-name mechanism)
965                   step (sasl-next-step client nil))
966             (elmo-network-session-set-auth-internal
967              session
968              (intern (downcase name)))
969             (setq sasl-read-passphrase
970                   (function
971                    (lambda (prompt)
972                      (elmo-get-passwd
973                       (elmo-network-session-password-key session)))))
974             (setq tag
975                   (elmo-imap4-send-command
976                    session
977                    (concat "AUTHENTICATE " name
978                            (and (sasl-step-data step)
979                                 (concat
980                                  " "
981                                  (elmo-base64-encode-string
982                                   (sasl-step-data step)
983                                   'no-lin-break))))))
984             (catch 'done
985               (while t
986                 (setq response
987                       (elmo-imap4-read-untagged
988                        (elmo-network-session-process-internal session)))
989                 (if (elmo-imap4-response-ok-p response)
990                     (if (sasl-next-step client step)
991                         ;; Bogus server?
992                         (signal 'elmo-authenticate-error
993                                 (list (intern
994                                        (concat "elmo-imap4-auth-"
995                                                (downcase name)))))
996                       ;; The authentication process is finished.
997                       (throw 'done nil)))
998                 (unless (elmo-imap4-response-continue-req-p response)
999                   ;; response is NO or BAD.
1000                   (signal 'elmo-authenticate-error
1001                           (list (intern
1002                                  (concat "elmo-imap4-auth-"
1003                                          (downcase name))))))
1004                 (sasl-step-set-data
1005                  step
1006                  (elmo-base64-decode-string
1007                   (elmo-imap4-response-value response 'continue-req)))
1008                 (setq step (sasl-next-step client step))
1009                 (setq tag
1010                       (elmo-imap4-send-string
1011                        session
1012                        (if (sasl-step-data step)
1013                            (elmo-base64-encode-string (sasl-step-data step)
1014                                                       'no-line-break)
1015                          ""))))))))))))
1016
1017 (luna-define-method elmo-network-setup-session ((session
1018                                                  elmo-imap4-session))
1019   (with-current-buffer (elmo-network-session-buffer session)
1020     (when (memq 'namespace (elmo-imap4-session-capability-internal session))
1021       (setq elmo-imap4-server-namespace
1022             (elmo-imap4-response-value
1023              (elmo-imap4-send-command-wait session "namespace")
1024              'namespace)))))
1025
1026 (defun elmo-imap4-setup-send-buffer (&optional string)
1027   (let ((send-buf (get-buffer-create " *elmo-imap4-setup-send-buffer*"))
1028         (source-buf (unless string (current-buffer))))
1029     (save-excursion
1030       (save-match-data
1031         (set-buffer send-buf)
1032         (erase-buffer)
1033         (elmo-set-buffer-multibyte nil)
1034         (if string
1035             (insert string)
1036           (with-current-buffer source-buf
1037             (copy-to-buffer send-buf (point-min) (point-max))))
1038         (goto-char (point-min))
1039         (if (eq (re-search-forward "^$" nil t)
1040                 (point-max))
1041             (insert "\n"))
1042         (goto-char (point-min))
1043         (while (search-forward "\n" nil t)
1044           (replace-match "\r\n"))))
1045     send-buf))
1046
1047 (defun elmo-imap4-setup-send-buffer-from-file (file)
1048   (let ((tmp-buf (get-buffer-create
1049                   " *elmo-imap4-setup-send-buffer-from-file*")))
1050     (save-excursion
1051       (save-match-data
1052         (set-buffer tmp-buf)
1053         (erase-buffer)
1054         (as-binary-input-file
1055          (insert-file-contents file))
1056         (goto-char (point-min))
1057         (if (eq (re-search-forward "^$" nil t)
1058                 (point-max))
1059             (insert "\n"))
1060         (goto-char (point-min))
1061         (while (search-forward "\n" nil t)
1062           (replace-match "\r\n"))))
1063     tmp-buf))
1064
1065 (luna-define-method elmo-delete-message-safe ((folder elmo-imap4-folder)
1066                                               number msgid)
1067   (let ((session (elmo-imap4-get-session folder))
1068         candidates)
1069     (elmo-imap4-session-select-mailbox
1070      session
1071      (elmo-imap4-folder-mailbox-internal folder))
1072     (setq candidates
1073           (elmo-imap4-response-value
1074            (elmo-imap4-send-command-wait session
1075                                          (list
1076                                           (if elmo-imap4-use-uid
1077                                               "uid search header message-id "
1078                                             "search header message-id ")
1079                                           (elmo-imap4-field-body msgid)))
1080            'search))
1081     (if (memq number candidates)
1082         (elmo-folder-delete-messages folder (list number)))))
1083
1084 (defun elmo-imap4-server-diff-async-callback-1 (status data)
1085   (funcall elmo-imap4-server-diff-async-callback
1086            (list (elmo-imap4-response-value status 'recent)
1087                  (elmo-imap4-response-value status 'unseen)
1088                  (elmo-imap4-response-value status 'messages))
1089            data))
1090
1091 (defun elmo-imap4-server-diff-async (folder)
1092   (let ((session (elmo-imap4-get-session folder)))
1093     ;; We should `check' folder to obtain newest information here.
1094     ;; But since there's no asynchronous check mechanism in elmo yet,
1095     ;; checking is not done here.
1096     (with-current-buffer (elmo-network-session-buffer session)
1097       (setq elmo-imap4-status-callback
1098             'elmo-imap4-server-diff-async-callback-1)
1099       (setq elmo-imap4-status-callback-data
1100             elmo-imap4-server-diff-async-callback-data))
1101     (elmo-imap4-send-command session
1102                              (list
1103                               "status "
1104                               (elmo-imap4-mailbox
1105                                (elmo-imap4-folder-mailbox-internal folder))
1106                               " (recent unseen messages)"))))
1107
1108 (luna-define-method elmo-server-diff-async ((folder elmo-imap4-folder))
1109   (let ((session (elmo-imap4-get-session folder)))
1110     ;; commit.
1111     ;; (elmo-imap4-commit spec)
1112     (with-current-buffer (elmo-network-session-buffer session)
1113       (setq elmo-imap4-status-callback
1114             'elmo-imap4-server-diff-async-callback-1)
1115       (setq elmo-imap4-status-callback-data
1116             elmo-imap4-server-diff-async-callback-data))
1117     (elmo-imap4-send-command session
1118                              (list
1119                               "status "
1120                               (elmo-imap4-mailbox
1121                                (elmo-imap4-folder-mailbox-internal folder))
1122                               " (recent unseen messages)"))))
1123
1124 ;;; IMAP parser.
1125
1126 (defvar elmo-imap4-server-eol "\r\n"
1127   "The EOL string sent from the server.")
1128
1129 (defvar elmo-imap4-client-eol "\r\n"
1130   "The EOL string we send to the server.")
1131
1132 (defvar elmo-imap4-display-literal-progress nil)
1133
1134 (defun elmo-imap4-find-next-line ()
1135   "Return point at end of current line, taking into account literals.
1136 Return nil if no complete line has arrived."
1137   (when (re-search-forward (concat elmo-imap4-server-eol "\\|{\\([0-9]+\\)}"
1138                                    elmo-imap4-server-eol)
1139                            nil t)
1140     (if (match-string 1)
1141         (if (< (point-max) (+ (point) (string-to-number (match-string 1))))
1142             (progn
1143               (if (and elmo-imap4-display-literal-progress
1144                        (> (string-to-number (match-string 1))
1145                           (min elmo-display-retrieval-progress-threshold 100)))
1146                   (elmo-display-progress
1147                    'elmo-imap4-display-literal-progress
1148                    (format "Retrieving (%d/%d bytes)..."
1149                            (- (point-max) (point))
1150                            (string-to-number (match-string 1)))
1151                    (/ (- (point-max) (point))
1152                       (/ (string-to-number (match-string 1)) 100))))
1153               nil)
1154           (goto-char (+ (point) (string-to-number (match-string 1))))
1155           (elmo-imap4-find-next-line))
1156       (point))))
1157
1158 (defun elmo-imap4-sentinel (process string)
1159   (delete-process process))
1160
1161 (defun elmo-imap4-arrival-filter (proc string)
1162   "IMAP process filter."
1163   (when (buffer-live-p (process-buffer proc))
1164   (with-current-buffer (process-buffer proc)
1165     (elmo-imap4-debug "-> %s" string)
1166     (goto-char (point-max))
1167     (insert string)
1168     (let (end)
1169       (goto-char (point-min))
1170       (while (setq end (elmo-imap4-find-next-line))
1171         (save-restriction
1172           (narrow-to-region (point-min) end)
1173           (delete-backward-char (length elmo-imap4-server-eol))
1174           (goto-char (point-min))
1175           (unwind-protect
1176               (cond ((eq elmo-imap4-status 'initial)
1177                      (setq elmo-imap4-current-response
1178                            (list
1179                             (list 'greeting (elmo-imap4-parse-greeting)))))
1180                     ((or (eq elmo-imap4-status 'auth)
1181                          (eq elmo-imap4-status 'nonauth)
1182                          (eq elmo-imap4-status 'selected)
1183                          (eq elmo-imap4-status 'examine))
1184                      (setq elmo-imap4-current-response
1185                            (cons
1186                             (elmo-imap4-parse-response)
1187                             elmo-imap4-current-response)))
1188                     (t
1189                      (message "Unknown state %s in arrival filter"
1190                               elmo-imap4-status))))
1191           (delete-region (point-min) (point-max))))))))
1192
1193 ;; IMAP parser.
1194
1195 (defsubst elmo-imap4-forward ()
1196   (or (eobp) (forward-char 1)))
1197
1198 (defsubst elmo-imap4-parse-number ()
1199   (when (looking-at "[0-9]+")
1200     (prog1
1201         (string-to-number (match-string 0))
1202       (goto-char (match-end 0)))))
1203
1204 (defsubst elmo-imap4-parse-literal ()
1205   (when (looking-at "{\\([0-9]+\\)}\r\n")
1206     (let ((pos (match-end 0))
1207           (len (string-to-number (match-string 1))))
1208       (if (< (point-max) (+ pos len))
1209           nil
1210         (goto-char (+ pos len))
1211         (buffer-substring pos (+ pos len))))))
1212 ;;;     (list ' pos (+ pos len))))))
1213
1214 (defsubst elmo-imap4-parse-string ()
1215   (cond ((eq (char-after (point)) ?\")
1216          (forward-char 1)
1217          (let ((p (point)) (name ""))
1218            (skip-chars-forward "^\"\\\\")
1219            (setq name (buffer-substring p (point)))
1220            (while (eq (char-after (point)) ?\\)
1221              (setq p (1+ (point)))
1222              (forward-char 2)
1223              (skip-chars-forward "^\"\\\\")
1224              (setq name (concat name (buffer-substring p (point)))))
1225            (forward-char 1)
1226            name))
1227         ((eq (char-after (point)) ?{)
1228          (elmo-imap4-parse-literal))))
1229
1230 (defsubst elmo-imap4-parse-nil ()
1231   (if (looking-at "NIL")
1232       (goto-char (match-end 0))))
1233
1234 (defsubst elmo-imap4-parse-nstring ()
1235   (or (elmo-imap4-parse-string)
1236       (and (elmo-imap4-parse-nil)
1237            nil)))
1238
1239 (defsubst elmo-imap4-parse-astring ()
1240   (or (elmo-imap4-parse-string)
1241       (buffer-substring (point)
1242                         (if (re-search-forward "[(){ \r\n%*\"\\]" nil t)
1243                             (goto-char (1- (match-end 0)))
1244                           (end-of-line)
1245                           (point)))))
1246
1247 (defsubst elmo-imap4-parse-address ()
1248   (let (address)
1249     (when (eq (char-after (point)) ?\()
1250       (elmo-imap4-forward)
1251       (setq address (vector (prog1 (elmo-imap4-parse-nstring)
1252                               (elmo-imap4-forward))
1253                             (prog1 (elmo-imap4-parse-nstring)
1254                               (elmo-imap4-forward))
1255                             (prog1 (elmo-imap4-parse-nstring)
1256                               (elmo-imap4-forward))
1257                             (elmo-imap4-parse-nstring)))
1258       (when (eq (char-after (point)) ?\))
1259         (elmo-imap4-forward)
1260         address))))
1261
1262 (defsubst elmo-imap4-parse-address-list ()
1263   (if (eq (char-after (point)) ?\()
1264       (let (address addresses)
1265         (elmo-imap4-forward)
1266         (while (and (not (eq (char-after (point)) ?\)))
1267                     ;; next line for MS Exchange bug
1268                     (progn (and (eq (char-after (point)) ? ) (elmo-imap4-forward)) t)
1269                     (setq address (elmo-imap4-parse-address)))
1270           (setq addresses (cons address addresses)))
1271         (when (eq (char-after (point)) ?\))
1272           (elmo-imap4-forward)
1273           (nreverse addresses)))
1274     (assert (elmo-imap4-parse-nil))))
1275
1276 (defsubst elmo-imap4-parse-mailbox ()
1277   (let ((mailbox (elmo-imap4-parse-astring)))
1278     (if (string-equal "INBOX" (upcase mailbox))
1279         "INBOX"
1280       mailbox)))
1281
1282 (defun elmo-imap4-parse-greeting ()
1283   "Parse a IMAP greeting."
1284   (cond ((looking-at "\\* OK ")
1285          (setq elmo-imap4-status 'nonauth))
1286         ((looking-at "\\* PREAUTH ")
1287          (setq elmo-imap4-status 'auth))
1288         ((looking-at "\\* BYE ")
1289          (setq elmo-imap4-status 'closed))))
1290
1291 (defun elmo-imap4-parse-response ()
1292   "Parse a IMAP command response."
1293   (let (token)
1294     (case (setq token (read (current-buffer)))
1295       (+ (progn
1296            (skip-chars-forward " ")
1297            (list 'continue-req (buffer-substring (point) (point-max)))))
1298       (* (case (prog1 (setq token (read (current-buffer)))
1299                  (elmo-imap4-forward))
1300            (OK         (elmo-imap4-parse-resp-text-code))
1301            (NO         (elmo-imap4-parse-resp-text-code))
1302            (BAD        (elmo-imap4-parse-resp-text-code))
1303            (BYE        (elmo-imap4-parse-bye))
1304            (FLAGS      (list 'flags
1305                              (elmo-imap4-parse-flag-list)))
1306            (LIST       (list 'list (elmo-imap4-parse-data-list)))
1307            (LSUB       (list 'lsub (elmo-imap4-parse-data-list)))
1308            (SEARCH     (list
1309                         'search
1310                         (read (concat "("
1311                                       (buffer-substring (point) (point-max))
1312                                       ")"))))
1313            (STATUS     (elmo-imap4-parse-status))
1314            ;; Added
1315            (NAMESPACE  (elmo-imap4-parse-namespace))
1316            (CAPABILITY (list 'capability
1317                              (read
1318                               (concat "(" (downcase (buffer-substring
1319                                                      (point) (point-max)))
1320                                       ")"))))
1321            (ACL (elmo-imap4-parse-acl))
1322            (t       (case (prog1 (read (current-buffer))
1323                             (elmo-imap4-forward))
1324                       (EXISTS  (list 'exists token))
1325                       (RECENT  (list 'recent token))
1326                       (EXPUNGE (list 'expunge token))
1327                       (FETCH   (elmo-imap4-parse-fetch token))
1328                       (t       (list 'garbage (buffer-string)))))))
1329       (t (if (not (string-match elmo-imap4-seq-prefix (symbol-name token)))
1330              (list 'garbage (buffer-string))
1331            (case (prog1 (read (current-buffer))
1332                    (elmo-imap4-forward))
1333              (OK  (progn
1334                     (setq elmo-imap4-parsing nil)
1335                     (setq token (symbol-name token))
1336                     (elmo-unintern token)
1337                     (elmo-imap4-debug "*%s* OK arrived" token)
1338                     (setq elmo-imap4-reached-tag token)
1339                     (list 'ok (elmo-imap4-parse-resp-text-code))))
1340              (NO  (progn
1341                     (setq elmo-imap4-parsing nil)
1342                     (setq token (symbol-name token))
1343                     (elmo-unintern token)
1344                     (elmo-imap4-debug "*%s* NO arrived" token)
1345                     (setq elmo-imap4-reached-tag token)
1346                     (let (code text)
1347                       (when (eq (char-after (point)) ?\[)
1348                         (setq code (buffer-substring (point)
1349                                                      (search-forward "]")))
1350                         (elmo-imap4-forward))
1351                       (setq text (buffer-substring (point) (point-max)))
1352                       (list 'no (list code text)))))
1353              (BAD (progn
1354                     (setq elmo-imap4-parsing nil)
1355                     (elmo-imap4-debug "*%s* BAD arrived" token)
1356                     (setq token (symbol-name token))
1357                     (elmo-unintern token)
1358                     (setq elmo-imap4-reached-tag token)
1359                     (let (code text)
1360                       (when (eq (char-after (point)) ?\[)
1361                         (setq code (buffer-substring (point)
1362                                                      (search-forward "]")))
1363                         (elmo-imap4-forward))
1364                       (setq text (buffer-substring (point) (point-max)))
1365                       (list 'bad (list code text)))))
1366              (t   (list 'garbage (buffer-string)))))))))
1367
1368 (defun elmo-imap4-parse-bye ()
1369   (let (code text)
1370     (when (eq (char-after (point)) ?\[)
1371       (setq code (buffer-substring (point)
1372                                    (search-forward "]")))
1373       (elmo-imap4-forward))
1374     (setq text (buffer-substring (point) (point-max)))
1375     (list 'bye (list code text))))
1376
1377 (defun elmo-imap4-parse-text ()
1378   (goto-char (point-min))
1379   (when (search-forward "[" nil t)
1380     (search-forward "]")
1381     (elmo-imap4-forward))
1382   (list 'text (buffer-substring (point) (point-max))))
1383
1384 (defun elmo-imap4-parse-resp-text-code ()
1385   (when (eq (char-after (point)) ?\[)
1386     (elmo-imap4-forward)
1387     (cond ((search-forward "PERMANENTFLAGS " nil t)
1388            (list 'permanentflags (elmo-imap4-parse-flag-list)))
1389           ((search-forward "UIDNEXT " nil t)
1390            (list 'uidnext (read (current-buffer))))
1391           ((search-forward "UNSEEN " nil t)
1392            (list 'unseen (read (current-buffer))))
1393           ((looking-at "UIDVALIDITY \\([0-9]+\\)")
1394            (list 'uidvalidity (match-string 1)))
1395           ((search-forward "READ-ONLY" nil t)
1396            (list 'read-only t))
1397           ((search-forward "READ-WRITE" nil t)
1398            (list 'read-write t))
1399           ((search-forward "NEWNAME " nil t)
1400            (let (oldname newname)
1401              (setq oldname (elmo-imap4-parse-string))
1402              (elmo-imap4-forward)
1403              (setq newname (elmo-imap4-parse-string))
1404              (list 'newname newname oldname)))
1405           ((search-forward "TRYCREATE" nil t)
1406            (list 'trycreate t))
1407           ((looking-at "APPENDUID \\([0-9]+\\) \\([0-9]+\\)")
1408            (list 'appenduid
1409                  (list (match-string 1)
1410                        (string-to-number (match-string 2)))))
1411           ((looking-at "COPYUID \\([0-9]+\\) \\([0-9,:]+\\) \\([0-9,:]+\\)")
1412            (list 'copyuid (list (match-string 1)
1413                                 (match-string 2)
1414                                 (match-string 3))))
1415           ((search-forward "ALERT] " nil t)
1416            (message "IMAP server information: %s"
1417                     (buffer-substring (point) (point-max))))
1418           (t (list 'unknown)))))
1419
1420 (defun elmo-imap4-parse-data-list ()
1421   (let (flags delimiter mailbox)
1422     (setq flags (elmo-imap4-parse-flag-list))
1423     (when (looking-at " NIL\\| \"\\\\?\\(.\\)\"")
1424       (setq delimiter (match-string 1))
1425       (goto-char (1+ (match-end 0)))
1426       (when (setq mailbox (elmo-imap4-parse-mailbox))
1427         (list mailbox flags delimiter)))))
1428
1429 (defsubst elmo-imap4-parse-header-list ()
1430   (when (eq (char-after (point)) ?\()
1431     (let (strlist)
1432       (while (not (eq (char-after (point)) ?\)))
1433         (elmo-imap4-forward)
1434         (push (elmo-imap4-parse-astring) strlist))
1435       (elmo-imap4-forward)
1436       (nreverse strlist))))
1437
1438 (defsubst elmo-imap4-parse-fetch-body-section ()
1439   (let ((section
1440          (buffer-substring (point)
1441                            (1-
1442                             (progn (re-search-forward "[] ]" nil t)
1443                                    (point))))))
1444     (if (eq (char-before) ? )
1445         (prog1
1446             (mapconcat 'identity
1447                        (cons section (elmo-imap4-parse-header-list)) " ")
1448           (search-forward "]" nil t))
1449       section)))
1450
1451 (defun elmo-imap4-parse-fetch (response)
1452   (when (eq (char-after (point)) ?\()
1453     (let (element list)
1454       (while (not (eq (char-after (point)) ?\)))
1455         (elmo-imap4-forward)
1456         (let ((token (read (current-buffer))))
1457           (elmo-imap4-forward)
1458           (setq element
1459                 (cond ((eq token 'UID)
1460                        (list 'uid (condition-case nil
1461                                       (read (current-buffer))
1462                                     (error nil))))
1463                       ((eq token 'FLAGS)
1464                        (list 'flags (elmo-imap4-parse-flag-list)))
1465                       ((eq token 'ENVELOPE)
1466                        (list 'envelope (elmo-imap4-parse-envelope)))
1467                       ((eq token 'INTERNALDATE)
1468                        (list 'internaldate (elmo-imap4-parse-string)))
1469                       ((eq token 'RFC822)
1470                        (list 'rfc822 (elmo-imap4-parse-nstring)))
1471                       ((eq token (intern elmo-imap4-rfc822-header))
1472                        (list 'rfc822header (elmo-imap4-parse-nstring)))
1473                       ((eq token (intern elmo-imap4-rfc822-text))
1474                        (list 'rfc822text (elmo-imap4-parse-nstring)))
1475                       ((eq token (intern elmo-imap4-rfc822-size))
1476                        (list 'rfc822size (read (current-buffer))))
1477                       ((eq token 'BODY)
1478                        (if (eq (char-before) ?\[)
1479                            (list
1480                             'bodydetail
1481                             (upcase (elmo-imap4-parse-fetch-body-section))
1482                             (and
1483                              (eq (char-after (point)) ?<)
1484                              (buffer-substring (1+ (point))
1485                                                (progn
1486                                                  (search-forward ">" nil t)
1487                                                  (point))))
1488                             (progn (elmo-imap4-forward)
1489                                    (elmo-imap4-parse-nstring)))
1490                          (list 'body (elmo-imap4-parse-body))))
1491                       ((eq token 'BODYSTRUCTURE)
1492                        (list 'bodystructure (elmo-imap4-parse-body)))))
1493           (setq list (cons element list))))
1494       (and elmo-imap4-fetch-callback
1495            (funcall elmo-imap4-fetch-callback
1496                     list elmo-imap4-fetch-callback-data))
1497       (list 'fetch list))))
1498
1499 (defun elmo-imap4-parse-status ()
1500   (let ((mailbox (elmo-imap4-parse-mailbox))
1501         status)
1502     (when (and mailbox (search-forward "(" nil t))
1503       (while (not (eq (char-after (point)) ?\)))
1504         (setq status
1505               (cons
1506                (let ((token (read (current-buffer))))
1507                  (cond ((eq token 'MESSAGES)
1508                         (list 'messages (read (current-buffer))))
1509                        ((eq token 'RECENT)
1510                         (list 'recent (read (current-buffer))))
1511                        ((eq token 'UIDNEXT)
1512                         (list 'uidnext (read (current-buffer))))
1513                        ((eq token 'UIDVALIDITY)
1514                         (and (looking-at " \\([0-9]+\\)")
1515                              (prog1 (list 'uidvalidity (match-string 1))
1516                                (goto-char (match-end 1)))))
1517                        ((eq token 'UNSEEN)
1518                         (list 'unseen (read (current-buffer))))
1519                        (t
1520                         (message
1521                          "Unknown status data %s in mailbox %s ignored"
1522                          token mailbox))))
1523                status))
1524         (skip-chars-forward " ")))
1525     (and elmo-imap4-status-callback
1526          (funcall elmo-imap4-status-callback
1527                   status
1528                   elmo-imap4-status-callback-data))
1529     (list 'status status)))
1530
1531
1532 (defmacro elmo-imap4-value (value)
1533   (` (if (eq (, value) 'NIL) nil
1534        (, value))))
1535
1536 (defmacro elmo-imap4-nth (pos list)
1537   (` (let ((value (nth (, pos) (, list))))
1538        (elmo-imap4-value value))))
1539
1540 (defun elmo-imap4-parse-namespace ()
1541   (list 'namespace
1542         (nconc
1543          (copy-sequence elmo-imap4-extra-namespace-alist)
1544          (elmo-imap4-parse-namespace-subr
1545           (read (concat "(" (buffer-substring
1546                              (point) (point-max))
1547                         ")"))))))
1548
1549 (defun elmo-imap4-parse-namespace-subr (ns)
1550   (let (prefix delim namespace-alist default-delim)
1551     ;; 0: personal, 1: other, 2: shared
1552     (dotimes (i 3)
1553       (setq namespace-alist
1554             (nconc namespace-alist
1555                    (delq nil
1556                          (mapcar
1557                           (lambda (namespace)
1558                             (setq prefix (elmo-imap4-nth 0 namespace)
1559                                   delim (elmo-imap4-nth 1 namespace))
1560                             (if (and prefix delim
1561                                      (string-match
1562                                       (concat (regexp-quote delim) "\\'")
1563                                       prefix))
1564                                 (setq prefix (substring prefix 0
1565                                                         (match-beginning 0))))
1566                             (if (eq (length prefix) 0)
1567                                 (progn (setq default-delim delim) nil)
1568                               (cons
1569                                (concat "^\\("
1570                                        (if (string= (downcase prefix) "inbox")
1571                                            "[Ii][Nn][Bb][Oo][Xx]"
1572                                          (regexp-quote prefix))
1573                                        "\\).*$")
1574                                delim)))
1575                           (elmo-imap4-nth i ns))))))
1576     (if default-delim
1577         (setq namespace-alist
1578               (nconc namespace-alist
1579                      (list (cons "^.*$" default-delim)))))
1580     namespace-alist))
1581
1582 (defun elmo-imap4-parse-acl ()
1583   (let ((mailbox (elmo-imap4-parse-mailbox))
1584         identifier rights acl)
1585     (while (eq (char-after (point)) ?\ )
1586       (elmo-imap4-forward)
1587       (setq identifier (elmo-imap4-parse-astring))
1588       (elmo-imap4-forward)
1589       (setq rights (elmo-imap4-parse-astring))
1590       (setq acl (append acl (list (cons identifier rights)))))
1591     (list 'acl acl mailbox)))
1592
1593 (defun elmo-imap4-parse-flag-list ()
1594   (let ((str (buffer-substring (+ (point) 1)
1595                                (progn (search-forward ")" nil t)
1596                                       (- (point) 1)))))
1597     (unless (eq (length str) 0)
1598       (split-string str))))
1599
1600 (defun elmo-imap4-parse-envelope ()
1601   (when (eq (char-after (point)) ?\()
1602     (elmo-imap4-forward)
1603     (vector (prog1 (elmo-imap4-parse-nstring);; date
1604               (elmo-imap4-forward))
1605             (prog1 (elmo-imap4-parse-nstring);; subject
1606               (elmo-imap4-forward))
1607             (prog1 (elmo-imap4-parse-address-list);; from
1608               (elmo-imap4-forward))
1609             (prog1 (elmo-imap4-parse-address-list);; sender
1610               (elmo-imap4-forward))
1611             (prog1 (elmo-imap4-parse-address-list);; reply-to
1612               (elmo-imap4-forward))
1613             (prog1 (elmo-imap4-parse-address-list);; to
1614               (elmo-imap4-forward))
1615             (prog1 (elmo-imap4-parse-address-list);; cc
1616               (elmo-imap4-forward))
1617             (prog1 (elmo-imap4-parse-address-list);; bcc
1618               (elmo-imap4-forward))
1619             (prog1 (elmo-imap4-parse-nstring);; in-reply-to
1620               (elmo-imap4-forward))
1621             (prog1 (elmo-imap4-parse-nstring);; message-id
1622               (elmo-imap4-forward)))))
1623
1624 (defsubst elmo-imap4-parse-string-list ()
1625   (cond ((eq (char-after (point)) ?\();; body-fld-param
1626          (let (strlist str)
1627            (elmo-imap4-forward)
1628            (while (setq str (elmo-imap4-parse-string))
1629              (push str strlist)
1630              (elmo-imap4-forward))
1631            (nreverse strlist)))
1632         ((elmo-imap4-parse-nil)
1633          nil)))
1634
1635 (defun elmo-imap4-parse-body-extension ()
1636   (if (eq (char-after (point)) ?\()
1637       (let (b-e)
1638         (elmo-imap4-forward)
1639         (push (elmo-imap4-parse-body-extension) b-e)
1640         (while (eq (char-after (point)) ?\ )
1641           (elmo-imap4-forward)
1642           (push (elmo-imap4-parse-body-extension) b-e))
1643         (assert (eq (char-after (point)) ?\)))
1644         (elmo-imap4-forward)
1645         (nreverse b-e))
1646     (or (elmo-imap4-parse-number)
1647         (elmo-imap4-parse-nstring))))
1648
1649 (defsubst elmo-imap4-parse-body-ext ()
1650   (let (ext)
1651     (when (eq (char-after (point)) ?\ );; body-fld-dsp
1652       (elmo-imap4-forward)
1653       (let (dsp)
1654         (if (eq (char-after (point)) ?\()
1655             (progn
1656               (elmo-imap4-forward)
1657               (push (elmo-imap4-parse-string) dsp)
1658               (elmo-imap4-forward)
1659               (push (elmo-imap4-parse-string-list) dsp)
1660               (elmo-imap4-forward))
1661           (assert (elmo-imap4-parse-nil)))
1662         (push (nreverse dsp) ext))
1663       (when (eq (char-after (point)) ?\ );; body-fld-lang
1664         (elmo-imap4-forward)
1665         (if (eq (char-after (point)) ?\()
1666             (push (elmo-imap4-parse-string-list) ext)
1667           (push (elmo-imap4-parse-nstring) ext))
1668         (while (eq (char-after (point)) ?\ );; body-extension
1669           (elmo-imap4-forward)
1670           (setq ext (append (elmo-imap4-parse-body-extension) ext)))))
1671     ext))
1672
1673 (defun elmo-imap4-parse-body ()
1674   (let (body)
1675     (when (eq (char-after (point)) ?\()
1676       (elmo-imap4-forward)
1677       (if (eq (char-after (point)) ?\()
1678           (let (subbody)
1679             (while (and (eq (char-after (point)) ?\()
1680                         (setq subbody (elmo-imap4-parse-body)))
1681               (push subbody body))
1682             (elmo-imap4-forward)
1683             (push (elmo-imap4-parse-string) body);; media-subtype
1684             (when (eq (char-after (point)) ?\ );; body-ext-mpart:
1685               (elmo-imap4-forward)
1686               (if (eq (char-after (point)) ?\();; body-fld-param
1687                   (push (elmo-imap4-parse-string-list) body)
1688                 (push (and (elmo-imap4-parse-nil) nil) body))
1689               (setq body
1690                     (append (elmo-imap4-parse-body-ext) body)));; body-ext-...
1691             (assert (eq (char-after (point)) ?\)))
1692             (elmo-imap4-forward)
1693             (nreverse body))
1694
1695         (push (elmo-imap4-parse-string) body);; media-type
1696         (elmo-imap4-forward)
1697         (push (elmo-imap4-parse-string) body);; media-subtype
1698         (elmo-imap4-forward)
1699         ;; next line for Sun SIMS bug
1700         (and (eq (char-after (point)) ? ) (elmo-imap4-forward))
1701         (if (eq (char-after (point)) ?\();; body-fld-param
1702             (push (elmo-imap4-parse-string-list) body)
1703           (push (and (elmo-imap4-parse-nil) nil) body))
1704         (elmo-imap4-forward)
1705         (push (elmo-imap4-parse-nstring) body);; body-fld-id
1706         (elmo-imap4-forward)
1707         (push (elmo-imap4-parse-nstring) body);; body-fld-desc
1708         (elmo-imap4-forward)
1709         (push (elmo-imap4-parse-string) body);; body-fld-enc
1710         (elmo-imap4-forward)
1711         (push (elmo-imap4-parse-number) body);; body-fld-octets
1712
1713         ;; ok, we're done parsing the required parts, what comes now is one
1714         ;; of three things:
1715         ;;
1716         ;; envelope       (then we're parsing body-type-msg)
1717         ;; body-fld-lines (then we're parsing body-type-text)
1718         ;; body-ext-1part (then we're parsing body-type-basic)
1719         ;;
1720         ;; the problem is that the two first are in turn optionally followed
1721         ;; by the third.  So we parse the first two here (if there are any)...
1722
1723         (when (eq (char-after (point)) ?\ )
1724           (elmo-imap4-forward)
1725           (let (lines)
1726             (cond ((eq (char-after (point)) ?\();; body-type-msg:
1727                    (push (elmo-imap4-parse-envelope) body);; envelope
1728                    (elmo-imap4-forward)
1729                    (push (elmo-imap4-parse-body) body);; body
1730                    (elmo-imap4-forward)
1731                    (push (elmo-imap4-parse-number) body));; body-fld-lines
1732                   ((setq lines (elmo-imap4-parse-number));; body-type-text:
1733                    (push lines body));; body-fld-lines
1734                   (t
1735                    (backward-char)))));; no match...
1736
1737         ;; ...and then parse the third one here...
1738
1739         (when (eq (char-after (point)) ?\ );; body-ext-1part:
1740           (elmo-imap4-forward)
1741           (push (elmo-imap4-parse-nstring) body);; body-fld-md5
1742           (setq body
1743                 (append (elmo-imap4-parse-body-ext) body)));; body-ext-1part..
1744
1745         (assert (eq (char-after (point)) ?\)))
1746         (elmo-imap4-forward)
1747         (nreverse body)))))
1748
1749 (luna-define-method elmo-folder-initialize :around ((folder
1750                                                      elmo-imap4-folder)
1751                                                     name)
1752   (let ((default-user   elmo-imap4-default-user)
1753         (default-server elmo-imap4-default-server)
1754         (default-port   elmo-imap4-default-port)
1755         (elmo-network-stream-type-alist
1756          (if elmo-imap4-stream-type-alist
1757              (append elmo-imap4-stream-type-alist
1758                      elmo-network-stream-type-alist)
1759            elmo-network-stream-type-alist))
1760         parse)
1761     (when (string-match "\\(.*\\)@\\(.*\\)" default-server)
1762       ;; case: imap4-default-server is specified like
1763       ;; "hoge%imap.server@gateway".
1764       (setq default-user (elmo-match-string 1 default-server))
1765       (setq default-server (elmo-match-string 2 default-server)))
1766     (setq name (luna-call-next-method))
1767     ;; mailbox
1768     (setq parse (elmo-parse-token name ":"))
1769     (elmo-imap4-folder-set-mailbox-internal folder
1770                                             (elmo-imap4-encode-folder-string
1771                                              (car parse)))
1772     ;; user
1773     (setq parse (elmo-parse-prefixed-element ?: (cdr parse) "/"))
1774     (elmo-net-folder-set-user-internal folder
1775                                        (if (eq (length (car parse)) 0)
1776                                            default-user
1777                                          (car parse)))
1778     ;; auth
1779     (setq parse (elmo-parse-prefixed-element ?/ (cdr parse)))
1780     (elmo-net-folder-set-auth-internal
1781      folder
1782      (if (eq (length (car parse)) 0)
1783          (or elmo-imap4-default-authenticate-type 'clear)
1784        (intern (car parse))))
1785     (unless (elmo-net-folder-server-internal folder)
1786       (elmo-net-folder-set-server-internal folder default-server))
1787     (unless (elmo-net-folder-port-internal folder)
1788       (elmo-net-folder-set-port-internal folder default-port))
1789     (unless (elmo-net-folder-stream-type-internal folder)
1790       (elmo-net-folder-set-stream-type-internal
1791        folder
1792        (elmo-get-network-stream-type elmo-imap4-default-stream-type)))
1793     folder))
1794
1795 ;;; ELMO IMAP4 folder
1796 (luna-define-method elmo-folder-expand-msgdb-path ((folder
1797                                                     elmo-imap4-folder))
1798   (convert-standard-filename
1799    (let ((mailbox (elmo-imap4-folder-mailbox-internal folder)))
1800      (if (string= "inbox" (downcase mailbox))
1801          (setq mailbox "inbox"))
1802      (if (eq (string-to-char mailbox) ?/)
1803          (setq mailbox (substring mailbox 1 (length mailbox))))
1804      ;; don't use expand-file-name (e.g. %~/something)
1805      (concat
1806       (expand-file-name
1807        (or (elmo-net-folder-user-internal folder) "nobody")
1808        (expand-file-name (or (elmo-net-folder-server-internal folder)
1809                              "nowhere")
1810                          (expand-file-name
1811                           "imap"
1812                           elmo-msgdb-directory)))
1813       "/" mailbox))))
1814
1815 (luna-define-method elmo-folder-status-plugged ((folder
1816                                                  elmo-imap4-folder))
1817   (elmo-imap4-folder-status-plugged folder))
1818
1819 (defun elmo-imap4-folder-status-plugged (folder)
1820   (let ((session (elmo-imap4-get-session folder))
1821         (killed (elmo-msgdb-killed-list-load
1822                  (elmo-folder-msgdb-path folder)))
1823         status)
1824     (with-current-buffer (elmo-network-session-buffer session)
1825       (setq elmo-imap4-status-callback nil)
1826       (setq elmo-imap4-status-callback-data nil))
1827     (setq status (elmo-imap4-response-value
1828                   (elmo-imap4-send-command-wait
1829                    session
1830                    (list "status "
1831                          (elmo-imap4-mailbox
1832                           (elmo-imap4-folder-mailbox-internal folder))
1833                          " (uidnext messages)"))
1834                   'status))
1835     (cons
1836      (- (elmo-imap4-response-value status 'uidnext) 1)
1837      (if killed
1838          (-
1839           (elmo-imap4-response-value status 'messages)
1840           (elmo-msgdb-killed-list-length killed))
1841        (elmo-imap4-response-value status 'messages)))))
1842
1843 (luna-define-method elmo-folder-list-messages-plugged ((folder
1844                                                         elmo-imap4-folder)
1845                                                        &optional
1846                                                        enable-killed)
1847   (elmo-imap4-list folder
1848                    (let ((killed
1849                           (elmo-folder-killed-list-internal
1850                            folder)))
1851                      (if (and killed
1852                               (eq (length killed) 1)
1853                               (consp (car killed))
1854                               (eq (car (car killed)) 1))
1855                          (format "uid %d:*" (cdr (car killed)))
1856                        "all"))))
1857
1858 (luna-define-method elmo-folder-list-unreads-plugged
1859   ((folder elmo-imap4-folder))
1860   (elmo-imap4-list folder "unseen"))
1861
1862 (luna-define-method elmo-folder-list-importants-plugged
1863   ((folder elmo-imap4-folder))
1864   (elmo-imap4-list folder "flagged"))
1865
1866 (luna-define-method elmo-folder-list-answereds-plugged
1867   ((folder elmo-imap4-folder))
1868   (elmo-imap4-list folder "answered"))
1869
1870 (defun elmo-imap4-folder-list-any-plugged (folder)
1871   (elmo-imap4-list folder "or answered or unseen flagged"))
1872
1873 (defun elmo-imap4-folder-list-digest-plugged (folder)
1874   (elmo-imap4-list folder "or unseen flagged"))
1875
1876 (luna-define-method elmo-folder-use-flag-p ((folder elmo-imap4-folder))
1877   (not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
1878                      (elmo-imap4-folder-mailbox-internal folder))))
1879
1880 (luna-define-method elmo-folder-list-subfolders ((folder elmo-imap4-folder)
1881                                                  &optional one-level)
1882   (let* ((root (elmo-imap4-folder-mailbox-internal folder))
1883          (session (elmo-imap4-get-session folder))
1884          (prefix (elmo-folder-prefix-internal folder))
1885          (namespace-assoc
1886                   (elmo-string-matched-assoc
1887                    root
1888                    (with-current-buffer (elmo-network-session-buffer session)
1889                      elmo-imap4-server-namespace)))
1890          (delim (or (cdr namespace-assoc)
1891                  elmo-imap4-default-hierarchy-delimiter))
1892          ;; Append delimiter when root with namespace.
1893          (root (if (and namespace-assoc
1894                         (match-end 1)
1895                         (string= (substring root (match-end 1))
1896                                  ""))
1897                    (concat root delim)
1898                  root))
1899          result append-serv type)
1900     (setq result (elmo-imap4-response-get-selectable-mailbox-list
1901                   (elmo-imap4-send-command-wait
1902                    session
1903                    (list "list " (elmo-imap4-mailbox root) " *"))))
1904     (when (or (not (string= (elmo-net-folder-user-internal folder)
1905                             elmo-imap4-default-user))
1906               (not (eq (elmo-net-folder-auth-internal folder)
1907                        (or elmo-imap4-default-authenticate-type 'clear))))
1908       (setq append-serv (concat ":" (elmo-net-folder-user-internal folder))))
1909     (unless (eq (elmo-net-folder-auth-internal folder)
1910                 (or elmo-imap4-default-authenticate-type 'clear))
1911       (setq append-serv
1912             (concat append-serv "/"
1913                     (symbol-name (elmo-net-folder-auth-internal folder)))))
1914     (unless (string= (elmo-net-folder-server-internal folder)
1915                      elmo-imap4-default-server)
1916       (setq append-serv (concat append-serv "@"
1917                                 (elmo-net-folder-server-internal folder))))
1918     (unless (eq (elmo-net-folder-port-internal folder) elmo-imap4-default-port)
1919       (setq append-serv (concat append-serv ":"
1920                                 (int-to-string
1921                                  (elmo-net-folder-port-internal folder)))))
1922     (setq type (elmo-net-folder-stream-type-internal folder))
1923     (unless (eq (elmo-network-stream-type-symbol type)
1924                 elmo-imap4-default-stream-type)
1925       (if type
1926           (setq append-serv (concat append-serv
1927                                     (elmo-network-stream-type-spec-string
1928                                      type)))))
1929     (if one-level
1930         (let ((re-delim (regexp-quote delim))
1931               (case-fold-search nil)
1932               folder ret has-child-p)
1933           ;; Append delimiter
1934           (when (and root
1935                      (not (string= root ""))
1936                      (not (string-match
1937                            (concat "\\(.*\\)" re-delim "\\'")
1938                            root)))
1939             (setq root (concat root delim)))
1940           (while (setq folder (car result))
1941             (when (string-match
1942                    (concat "^\\(" (regexp-quote root) "[^" re-delim "]" "+\\)"
1943                            re-delim)
1944                    folder)
1945               (setq folder (match-string 1 folder)))
1946             (setq has-child-p nil
1947                   result (delq
1948                           nil
1949                           (mapcar (lambda (fld)
1950                                     (if (string-match
1951                                          (concat "^" (regexp-quote folder)
1952                                                  "\\(" re-delim "\\|\\'\\)")
1953                                          fld)
1954                                         (progn (setq has-child-p t) nil)
1955                                       fld))
1956                                   (cdr result)))
1957                   folder (concat prefix
1958                                  (elmo-imap4-decode-folder-string folder)
1959                                  (and append-serv
1960                                       (eval append-serv)))
1961                   ret (append ret (if has-child-p
1962                                       (list (list folder))
1963                                     (list folder)))))
1964           ret)
1965       (mapcar (lambda (fld)
1966                 (concat prefix (elmo-imap4-decode-folder-string fld)
1967                         (and append-serv
1968                              (eval append-serv))))
1969               result))))
1970
1971 (luna-define-method elmo-folder-exists-p-plugged ((folder elmo-imap4-folder))
1972   (let ((session (elmo-imap4-get-session folder)))
1973     (if (string=
1974          (elmo-imap4-session-current-mailbox-internal session)
1975          (elmo-imap4-folder-mailbox-internal folder))
1976         t
1977       (elmo-imap4-session-select-mailbox
1978        session
1979        (elmo-imap4-folder-mailbox-internal folder)
1980        'force 'notify-bye))))
1981
1982 (luna-define-method elmo-folder-creatable-p ((folder elmo-imap4-folder))
1983   t)
1984
1985 (luna-define-method elmo-folder-writable-p ((folder elmo-imap4-folder))
1986   t)
1987
1988 (luna-define-method elmo-folder-delete ((folder elmo-imap4-folder))
1989   (let ((msgs (and (elmo-folder-exists-p folder)
1990                    (elmo-folder-list-messages folder))))
1991     (when (yes-or-no-p (format "%sDelete msgdb and substance of \"%s\"? "
1992                                (if (> (length msgs) 0)
1993                                    (format "%d msg(s) exists. " (length msgs))
1994                                  "")
1995                                (elmo-folder-name-internal folder)))
1996       (let ((session (elmo-imap4-get-session folder)))
1997         (when (elmo-imap4-folder-mailbox-internal folder)
1998           (when msgs (elmo-folder-delete-messages folder msgs))
1999           (elmo-imap4-send-command-wait session "close")
2000           (elmo-imap4-send-command-wait
2001            session
2002            (list "delete "
2003                  (elmo-imap4-mailbox
2004                   (elmo-imap4-folder-mailbox-internal folder))))))
2005       (elmo-msgdb-delete-path folder)
2006       t)))
2007
2008 (luna-define-method elmo-folder-rename-internal ((folder elmo-imap4-folder)
2009                                                  new-folder)
2010   (let ((session (elmo-imap4-get-session folder)))
2011     ;; make sure the folder is selected.
2012     (elmo-imap4-session-select-mailbox session
2013                                        (elmo-imap4-folder-mailbox-internal
2014                                         folder))
2015     (elmo-imap4-send-command-wait session "close")
2016     (elmo-imap4-send-command-wait
2017      session
2018      (list "rename "
2019            (elmo-imap4-mailbox
2020             (elmo-imap4-folder-mailbox-internal folder))
2021            " "
2022            (elmo-imap4-mailbox
2023             (elmo-imap4-folder-mailbox-internal new-folder))))
2024     (elmo-imap4-session-set-current-mailbox-internal
2025      session (elmo-imap4-folder-mailbox-internal new-folder))))
2026
2027 (defun elmo-imap4-copy-messages (src-folder dst-folder numbers)
2028   (let ((session (elmo-imap4-get-session src-folder))
2029         (set-list (elmo-imap4-make-number-set-list
2030                    numbers
2031                    elmo-imap4-number-set-chop-length))
2032         succeeds)
2033     (elmo-imap4-session-select-mailbox session
2034                                        (elmo-imap4-folder-mailbox-internal
2035                                         src-folder))
2036     (while set-list
2037       (if (elmo-imap4-send-command-wait session
2038                                         (list
2039                                          (format
2040                                           (if elmo-imap4-use-uid
2041                                               "uid copy %s "
2042                                             "copy %s ")
2043                                           (cdr (car set-list)))
2044                                          (elmo-imap4-mailbox
2045                                           (elmo-imap4-folder-mailbox-internal
2046                                            dst-folder))))
2047           (setq succeeds (append succeeds numbers)))
2048       (setq set-list (cdr set-list)))
2049     succeeds))
2050
2051 (defun elmo-imap4-set-flag (folder numbers flag &optional remove)
2052   "Set flag on messages.
2053 FOLDER is the ELMO folder structure.
2054 NUMBERS is the message numbers to be flagged.
2055 FLAG is the flag name.
2056 If optional argument REMOVE is non-nil, remove FLAG."
2057   (let ((session (elmo-imap4-get-session folder))
2058         response set-list)
2059     (elmo-imap4-session-select-mailbox session
2060                                        (elmo-imap4-folder-mailbox-internal
2061                                         folder))
2062     (setq set-list (elmo-imap4-make-number-set-list
2063                     numbers
2064                     elmo-imap4-number-set-chop-length))
2065     (while set-list
2066       (with-current-buffer (elmo-network-session-buffer session)
2067         (setq elmo-imap4-fetch-callback nil)
2068         (setq elmo-imap4-fetch-callback-data nil))
2069       (unless (elmo-imap4-response-ok-p
2070                (elmo-imap4-send-command-wait
2071                 session
2072                 (format
2073                  (if elmo-imap4-use-uid
2074                      "uid store %s %sflags.silent (%s)"
2075                    "store %s %sflags.silent (%s)")
2076                  (cdr (car set-list))
2077                  (if remove "-" "+")
2078                  flag)))
2079         (setq response 'fail))
2080       (setq set-list (cdr set-list)))
2081     (not (eq response 'fail))))
2082
2083 (luna-define-method elmo-folder-delete-messages-plugged
2084   ((folder elmo-imap4-folder) numbers)
2085   (let ((session (elmo-imap4-get-session folder)))
2086     (elmo-imap4-session-select-mailbox
2087      session
2088      (elmo-imap4-folder-mailbox-internal folder))
2089     (unless (elmo-imap4-set-flag folder numbers "\\Deleted")
2090       (error "Failed to set deleted flag"))
2091     (elmo-imap4-send-command session "expunge")))
2092
2093 (defmacro elmo-imap4-detect-search-charset (string)
2094   (` (with-temp-buffer
2095        (insert (, string))
2096        (detect-mime-charset-region (point-min) (point-max)))))
2097
2098 (defun elmo-imap4-search-internal-primitive (folder session filter from-msgs)
2099   (let ((search-key (elmo-filter-key filter))
2100         (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to"
2101                             "larger" "smaller" "flag"))
2102         (total 0)
2103         (length (length from-msgs))
2104         charset set-list end results)
2105     (message "Searching...")
2106     (cond
2107      ((string= "last" search-key)
2108       (let ((numbers (or from-msgs (elmo-folder-list-messages folder))))
2109         (nthcdr (max (- (length numbers)
2110                         (string-to-int (elmo-filter-value filter)))
2111                      0)
2112                 numbers)))
2113      ((string= "first" search-key)
2114       (let* ((numbers (or from-msgs (elmo-folder-list-messages folder)))
2115              (rest (nthcdr (string-to-int (elmo-filter-value filter) )
2116                            numbers)))
2117         (mapcar '(lambda (x) (delete x numbers)) rest)
2118         numbers))
2119      ((string= "flag" search-key)
2120       (cond
2121        ((string= "unread" (elmo-filter-value filter))
2122         (elmo-folder-list-unreads folder))
2123        ((string= "important" (elmo-filter-value filter))
2124         (elmo-folder-list-importants folder))
2125        ((string= "answered" (elmo-filter-value filter))
2126         (elmo-folder-list-answereds folder))
2127        ((string= "digest" (elmo-filter-value filter))
2128         (elmo-imap4-folder-list-digest-plugged folder))
2129        ((string= "any" (elmo-filter-value filter))
2130         (elmo-imap4-folder-list-any-plugged folder))))
2131      ((or (string= "since" search-key)
2132           (string= "before" search-key))
2133       (setq search-key (concat "sent" search-key)
2134             set-list (elmo-imap4-make-number-set-list
2135                       from-msgs
2136                       elmo-imap4-number-set-chop-length)
2137             end nil)
2138       (while (not end)
2139         (setq results
2140               (append
2141                results
2142                (elmo-imap4-response-value
2143                 (elmo-imap4-send-command-wait
2144                  session
2145                  (format
2146                   (if elmo-imap4-use-uid
2147                       "uid search %s%s%s %s"
2148                     "search %s%s%s %s")
2149                   (if from-msgs
2150                       (concat
2151                        (if elmo-imap4-use-uid "uid ")
2152                        (cdr (car set-list))
2153                        " ")
2154                     "")
2155                   (if (eq (elmo-filter-type filter)
2156                           'unmatch)
2157                       "not " "")
2158                   search-key
2159                   (elmo-date-get-description
2160                    (elmo-date-get-datevec
2161                     (elmo-filter-value filter)))))
2162                 'search)))
2163         (when (> length elmo-display-progress-threshold)
2164           (setq total (+ total (car (car set-list))))
2165           (elmo-display-progress
2166            'elmo-imap4-search "Searching..."
2167            (/ (* total 100) length)))
2168         (setq set-list (cdr set-list)
2169               end (null set-list)))
2170       results)
2171      (t
2172       (setq charset
2173             (if (eq (length (elmo-filter-value filter)) 0)
2174                 (setq charset 'us-ascii)
2175               (elmo-imap4-detect-search-charset
2176                (elmo-filter-value filter)))
2177             set-list (elmo-imap4-make-number-set-list
2178                       from-msgs
2179                       elmo-imap4-number-set-chop-length)
2180             end nil)
2181       (while (not end)
2182         (setq results
2183               (append
2184                results
2185                (elmo-imap4-response-value
2186                 (elmo-imap4-send-command-wait
2187                  session
2188                  (list
2189                   (if elmo-imap4-use-uid "uid ")
2190                   "search "
2191                   "CHARSET "
2192                   (elmo-imap4-astring
2193                    (symbol-name charset))
2194                   " "
2195                   (if from-msgs
2196                       (concat
2197                        (if elmo-imap4-use-uid "uid ")
2198                        (cdr (car set-list))
2199                        " ")
2200                     "")
2201                   (if (eq (elmo-filter-type filter)
2202                           'unmatch)
2203                       "not " "")
2204                   (format "%s%s "
2205                           (if (member
2206                                (elmo-filter-key filter)
2207                                imap-search-keys)
2208                               ""
2209                             "header ")
2210                           (elmo-filter-key filter))
2211                   (elmo-imap4-astring
2212                    (encode-mime-charset-string
2213                     (elmo-filter-value filter) charset))))
2214                 'search)))
2215         (when (> length elmo-display-progress-threshold)
2216           (setq total (+ total (car (car set-list))))
2217           (elmo-display-progress
2218            'elmo-imap4-search "Searching..."
2219            (/ (* total 100) length)))
2220         (setq set-list (cdr set-list)
2221               end (null set-list)))
2222       results))))
2223
2224 (defun elmo-imap4-search-internal (folder session condition from-msgs)
2225   (let (result)
2226     (cond
2227      ((vectorp condition)
2228       (setq result (elmo-imap4-search-internal-primitive
2229                     folder session condition from-msgs)))
2230      ((eq (car condition) 'and)
2231       (setq result (elmo-imap4-search-internal folder session (nth 1 condition)
2232                                                from-msgs)
2233             result (elmo-list-filter result
2234                                      (elmo-imap4-search-internal
2235                                       folder session (nth 2 condition)
2236                                       from-msgs))))
2237      ((eq (car condition) 'or)
2238       (setq result (elmo-imap4-search-internal
2239                     folder session (nth 1 condition) from-msgs)
2240             result (elmo-uniq-list
2241                     (nconc result
2242                            (elmo-imap4-search-internal
2243                             folder session (nth 2 condition) from-msgs)))
2244             result (sort result '<))))))
2245
2246 (luna-define-method elmo-folder-search :around ((folder elmo-imap4-folder)
2247                                                 condition &optional numbers)
2248   (if (elmo-folder-plugged-p folder)
2249       (save-excursion
2250         (let ((session (elmo-imap4-get-session folder)))
2251           (elmo-imap4-session-select-mailbox
2252            session
2253            (elmo-imap4-folder-mailbox-internal folder))
2254           (elmo-imap4-search-internal folder session condition numbers)))
2255     (luna-call-next-method)))
2256
2257 (luna-define-method elmo-folder-msgdb-create-plugged
2258   ((folder elmo-imap4-folder) numbers flag-table)
2259   (when numbers
2260     (let ((session (elmo-imap4-get-session folder))
2261           (headers
2262            (append
2263             '("Subject" "From" "To" "Cc" "Date"
2264               "Message-Id" "References" "In-Reply-To")
2265             elmo-msgdb-extra-fields))
2266           (total 0)
2267           (length (length numbers))
2268           print-length print-depth
2269           rfc2060 set-list)
2270       (setq rfc2060 (memq 'imap4rev1
2271                           (elmo-imap4-session-capability-internal
2272                            session)))
2273       (message "Getting overview...")
2274       (elmo-imap4-session-select-mailbox
2275        session (elmo-imap4-folder-mailbox-internal folder))
2276       (setq set-list (elmo-imap4-make-number-set-list
2277                       numbers
2278                       elmo-imap4-overview-fetch-chop-length))
2279       ;; Setup callback.
2280       (with-current-buffer (elmo-network-session-buffer session)
2281         (setq elmo-imap4-current-msgdb (elmo-make-msgdb)
2282               elmo-imap4-seen-messages nil
2283               elmo-imap4-fetch-callback 'elmo-imap4-fetch-callback-1
2284               elmo-imap4-fetch-callback-data (cons flag-table folder))
2285         (while set-list
2286           (elmo-imap4-send-command-wait
2287            session
2288            ;; get overview entity from IMAP4
2289            (format "%sfetch %s (%s rfc822.size flags)"
2290                    (if elmo-imap4-use-uid "uid " "")
2291                    (cdr (car set-list))
2292                    (if rfc2060
2293                        (format "body.peek[header.fields %s]" headers)
2294                      (format "%s" headers))))
2295           (when (> length elmo-display-progress-threshold)
2296             (setq total (+ total (car (car set-list))))
2297             (elmo-display-progress
2298              'elmo-imap4-msgdb-create "Getting overview..."
2299              (/ (* total 100) length)))
2300           (setq set-list (cdr set-list)))
2301         (message "Getting overview...done")
2302         (when elmo-imap4-seen-messages
2303           (elmo-imap4-set-flag folder elmo-imap4-seen-messages "\\Seen"))
2304         ;; cannot setup the global flag while retrieval.
2305         (dolist (number (elmo-msgdb-list-messages elmo-imap4-current-msgdb))
2306           (elmo-global-flags-set (elmo-msgdb-flags elmo-imap4-current-msgdb
2307                                                    number)
2308                                  folder number
2309                                  (elmo-message-entity-field
2310                                   (elmo-msgdb-message-entity
2311                                    elmo-imap4-current-msgdb number)
2312                                   'message-id)))
2313         elmo-imap4-current-msgdb))))
2314
2315 (luna-define-method elmo-folder-unflag-important-plugged
2316   ((folder elmo-imap4-folder) numbers)
2317   (elmo-imap4-set-flag folder numbers "\\Flagged" 'remove))
2318
2319 (luna-define-method elmo-folder-flag-as-important-plugged
2320   ((folder elmo-imap4-folder) numbers)
2321   (elmo-imap4-set-flag folder numbers "\\Flagged"))
2322
2323 (luna-define-method elmo-folder-unflag-read-plugged
2324   ((folder elmo-imap4-folder) numbers)
2325   (elmo-imap4-set-flag folder numbers "\\Seen" 'remove))
2326
2327 (luna-define-method elmo-folder-flag-as-read-plugged
2328   ((folder elmo-imap4-folder) numbers)
2329   (elmo-imap4-set-flag folder numbers "\\Seen"))
2330
2331 (luna-define-method elmo-folder-unflag-answered-plugged
2332   ((folder elmo-imap4-folder) numbers)
2333   (elmo-imap4-set-flag folder numbers "\\Answered" 'remove))
2334
2335 (luna-define-method elmo-folder-flag-as-answered-plugged
2336   ((folder elmo-imap4-folder) numbers)
2337   (elmo-imap4-set-flag folder numbers "\\Answered"))
2338
2339 (luna-define-method elmo-message-use-cache-p ((folder elmo-imap4-folder)
2340                                               number)
2341   elmo-imap4-use-cache)
2342
2343 (luna-define-method elmo-folder-message-appendable-p ((folder elmo-imap4-folder))
2344   (if (elmo-folder-plugged-p folder)
2345       (not (elmo-imap4-session-read-only-internal
2346             (elmo-imap4-get-session folder)))
2347     elmo-enable-disconnected-operation)) ; offline refile.
2348
2349 (luna-define-method elmo-folder-check-plugged ((folder elmo-imap4-folder))
2350   (let ((session (elmo-imap4-get-session folder 'if-exists)))
2351     (when session
2352       (if (string=
2353            (elmo-imap4-session-current-mailbox-internal session)
2354            (elmo-imap4-folder-mailbox-internal folder))
2355           (if elmo-imap4-use-select-to-update-status
2356               (elmo-imap4-session-select-mailbox
2357                session
2358                (elmo-imap4-folder-mailbox-internal folder)
2359                'force)
2360             (elmo-imap4-session-check session))))))
2361
2362 (defsubst elmo-imap4-folder-diff-plugged (folder)
2363   (let ((session (elmo-imap4-get-session folder))
2364         messages new unread response killed uidnext)
2365 ;;; (elmo-imap4-commit spec)
2366     (with-current-buffer (elmo-network-session-buffer session)
2367       (setq elmo-imap4-status-callback nil)
2368       (setq elmo-imap4-status-callback-data nil))
2369     (if elmo-imap4-use-select-to-update-status
2370         (elmo-imap4-session-select-mailbox
2371          session
2372          (elmo-imap4-folder-mailbox-internal folder)))
2373     (setq response
2374           (elmo-imap4-send-command-wait session
2375                                         (list
2376                                          "status "
2377                                          (elmo-imap4-mailbox
2378                                           (elmo-imap4-folder-mailbox-internal
2379                                            folder))
2380                                          " (recent unseen messages uidnext)")))
2381     (setq response (elmo-imap4-response-value response 'status))
2382     (setq messages (elmo-imap4-response-value response 'messages))
2383     (setq uidnext (elmo-imap4-response-value response 'uidnext))
2384     (setq killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
2385     ;; 
2386     (when killed
2387       (when (and (consp (car killed))
2388                  (eq (car (car killed)) 1))
2389         (setq messages (- uidnext (cdr (car killed)) 1)))
2390       (setq messages (- messages
2391                         (elmo-msgdb-killed-list-length (cdr killed)))))
2392     (setq new (elmo-imap4-response-value response 'recent)
2393           unread (elmo-imap4-response-value response 'unseen))
2394     (if (< unread new) (setq new unread))
2395     (list new unread messages)))
2396
2397 (luna-define-method elmo-folder-diff-plugged ((folder elmo-imap4-folder))
2398   (elmo-imap4-folder-diff-plugged folder))
2399
2400 (luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder))
2401   (setq elmo-imap4-server-diff-async-callback
2402         elmo-folder-diff-async-callback)
2403   (setq elmo-imap4-server-diff-async-callback-data
2404         elmo-folder-diff-async-callback-data)
2405   (elmo-imap4-server-diff-async folder))
2406
2407 (luna-define-method elmo-folder-open :around ((folder elmo-imap4-folder)
2408                                               &optional load-msgdb)
2409   (if (elmo-folder-plugged-p folder)
2410       (let (session mailbox msgdb result response tag)
2411         (condition-case err
2412             (progn
2413               (setq session (elmo-imap4-get-session folder)
2414                     mailbox (elmo-imap4-folder-mailbox-internal folder)
2415                     tag (elmo-imap4-send-command session
2416                                                  (list "select "
2417                                                        (elmo-imap4-mailbox
2418                                                         mailbox))))
2419               (message "Selecting %s..."
2420                        (elmo-folder-name-internal folder))
2421               (if load-msgdb
2422                   (setq msgdb (elmo-folder-msgdb-load folder 'silent)))
2423               (elmo-folder-set-killed-list-internal
2424                folder
2425                (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
2426               (if (setq result (elmo-imap4-response-ok-p
2427                                 (setq response
2428                                       (elmo-imap4-read-response session tag))))
2429                   (progn
2430                     (elmo-imap4-session-set-current-mailbox-internal
2431                      session mailbox)
2432                     (elmo-imap4-session-set-read-only-internal
2433                      session
2434                      (nth 1 (assq 'read-only (assq 'ok response)))))
2435                 (elmo-imap4-session-set-current-mailbox-internal session nil)
2436                 (if (elmo-imap4-response-bye-p response)
2437                     (elmo-imap4-process-bye session)
2438                   (error "%s"
2439                          (or (elmo-imap4-response-error-text response)
2440                              (format "Select %s failed" mailbox)))))
2441               (message "Selecting %s...done"
2442                        (elmo-folder-name-internal folder))
2443               (elmo-folder-set-msgdb-internal
2444                folder msgdb))
2445           (quit
2446            (if (elmo-imap4-response-ok-p response)
2447                (elmo-imap4-session-set-current-mailbox-internal
2448                 session mailbox)
2449              (and session
2450                   (elmo-imap4-session-set-current-mailbox-internal
2451                    session nil))))
2452           (error
2453            (if (elmo-imap4-response-ok-p response)
2454                (elmo-imap4-session-set-current-mailbox-internal
2455                 session mailbox)
2456              (and session
2457                   (elmo-imap4-session-set-current-mailbox-internal
2458                    session nil))))))
2459     (luna-call-next-method)))
2460
2461 ;; elmo-folder-open-internal: do nothing.
2462
2463 (luna-define-method elmo-find-fetch-strategy
2464   ((folder elmo-imap4-folder) entity &optional ignore-cache)
2465   (let ((number (elmo-message-entity-number entity))
2466         cache-file size message-id)
2467     (setq size (elmo-message-entity-field entity 'size))
2468     (setq message-id (elmo-message-entity-field entity 'message-id))
2469     (setq cache-file (elmo-file-cache-get message-id))
2470     (if (or ignore-cache
2471             (null (elmo-file-cache-status cache-file)))
2472         (if (and elmo-message-fetch-threshold
2473                  (integerp size)
2474                  (>= size elmo-message-fetch-threshold)
2475                  (or (not elmo-message-fetch-confirm)
2476                      (not (prog1 (y-or-n-p
2477                                   (format
2478                                    "Fetch entire message at once? (%dbytes)"
2479                                    size))
2480                             (message "")))))
2481             ;; Fetch message as imap message.
2482             (elmo-make-fetch-strategy 'section
2483                                       nil
2484                                       (elmo-message-use-cache-p
2485                                        folder number)
2486                                       (elmo-file-cache-path
2487                                        cache-file))
2488           ;; Don't use existing cache and fetch entire message at once.
2489           (elmo-make-fetch-strategy 'entire nil
2490                                     (elmo-message-use-cache-p
2491                                      folder number)
2492                                     (elmo-file-cache-path cache-file)))
2493       ;; Cache found and use it.
2494       (if (not ignore-cache)
2495           (if (eq (elmo-file-cache-status cache-file) 'section)
2496               ;; Fetch message with imap message.
2497               (elmo-make-fetch-strategy 'section
2498                                         t
2499                                         (elmo-message-use-cache-p
2500                                          folder number)
2501                                         (elmo-file-cache-path
2502                                          cache-file))
2503             (elmo-make-fetch-strategy 'entire
2504                                       t
2505                                       (elmo-message-use-cache-p
2506                                        folder number)
2507                                       (elmo-file-cache-path
2508                                        cache-file)))))))
2509
2510 (luna-define-method elmo-folder-create-plugged ((folder elmo-imap4-folder))
2511   (elmo-imap4-send-command-wait
2512    (elmo-imap4-get-session folder)
2513    (list "create "
2514          (elmo-imap4-mailbox
2515           (elmo-imap4-folder-mailbox-internal folder)))))
2516
2517 (luna-define-method elmo-folder-append-buffer
2518   ((folder elmo-imap4-folder) &optional flags number)
2519   (if (elmo-folder-plugged-p folder)
2520       (let ((session (elmo-imap4-get-session folder))
2521             send-buffer result)
2522         (elmo-imap4-session-select-mailbox session
2523                                            (elmo-imap4-folder-mailbox-internal
2524                                             folder))
2525         (setq send-buffer (elmo-imap4-setup-send-buffer))
2526         (unwind-protect
2527             (setq result
2528                   (elmo-imap4-send-command-wait
2529                    session
2530                    (list
2531                     "append "
2532                     (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal
2533                                          folder))
2534                     (if (and flags (elmo-folder-use-flag-p folder))
2535                         (concat " ("
2536                                 (mapconcat
2537                                  'identity
2538                                  (append
2539                                   (and (memq 'important flags)
2540                                        '("\\Flagged"))
2541                                   (and (not (memq 'unread flags))
2542                                        '("\\Seen"))
2543                                   (and (memq 'answered flags)
2544                                        '("\\Answered")))
2545                                  " ")
2546                                 ") ")
2547                       " () ")
2548                     (elmo-imap4-buffer-literal send-buffer))))
2549           (kill-buffer send-buffer))
2550         result)
2551     ;; Unplugged
2552     (if elmo-enable-disconnected-operation
2553         (elmo-folder-append-buffer-dop folder flags number)
2554       (error "Unplugged"))))
2555
2556 (eval-when-compile
2557   (defmacro elmo-imap4-identical-system-p (folder1 folder2)
2558     "Return t if FOLDER1 and FOLDER2 are in the same IMAP4 system."
2559     (` (and (string= (elmo-net-folder-server-internal (, folder1))
2560                      (elmo-net-folder-server-internal (, folder2)))
2561             (eq (elmo-net-folder-port-internal (, folder1))
2562                 (elmo-net-folder-port-internal (, folder2)))
2563             (string= (elmo-net-folder-user-internal (, folder1))
2564                      (elmo-net-folder-user-internal (, folder2)))))))
2565
2566 (luna-define-method elmo-folder-append-messages :around
2567   ((folder elmo-imap4-folder) src-folder numbers &optional same-number)
2568   (if (and (eq (elmo-folder-type-internal src-folder) 'imap4)
2569            (elmo-imap4-identical-system-p folder src-folder)
2570            (elmo-folder-plugged-p folder))
2571       ;; Plugged
2572       (prog1
2573           (elmo-imap4-copy-messages src-folder folder numbers)
2574         (elmo-progress-notify 'elmo-folder-move-messages (length numbers)))
2575     (luna-call-next-method)))
2576
2577 (luna-define-method elmo-message-deletable-p ((folder elmo-imap4-folder)
2578                                               number)
2579   (if (elmo-folder-plugged-p folder)
2580       (not (elmo-imap4-session-read-only-internal
2581             (elmo-imap4-get-session folder)))
2582     elmo-enable-disconnected-operation)) ; offline refile.
2583
2584 ;(luna-define-method elmo-message-fetch-unplugged
2585 ;  ((folder elmo-imap4-folder)
2586 ;   number strategy  &optional section outbuf unseen)
2587 ;  (error "%d%s is not cached." number (if section
2588 ;                                         (format "(%s)" section)
2589 ;                                       "")))
2590
2591 (defsubst elmo-imap4-message-fetch (folder number strategy
2592                                            section outbuf unseen)
2593   (let ((session (elmo-imap4-get-session folder))
2594         response)
2595     (elmo-imap4-session-select-mailbox session
2596                                        (elmo-imap4-folder-mailbox-internal
2597                                         folder))
2598     (with-current-buffer (elmo-network-session-buffer session)
2599       (setq elmo-imap4-fetch-callback nil)
2600       (setq elmo-imap4-fetch-callback-data nil))
2601     (unless elmo-inhibit-display-retrieval-progress
2602       (setq elmo-imap4-display-literal-progress t))
2603     (unwind-protect
2604         (setq response
2605               (elmo-imap4-send-command-wait session
2606                                             (format
2607                                              (if elmo-imap4-use-uid
2608                                                  "uid fetch %s body%s[%s]"
2609                                                "fetch %s body%s[%s]")
2610                                              number
2611                                              (if unseen ".peek" "")
2612                                              (or section "")
2613                                              )))
2614       (setq elmo-imap4-display-literal-progress nil))
2615     (unless elmo-inhibit-display-retrieval-progress
2616       (elmo-display-progress 'elmo-imap4-display-literal-progress
2617                              "Retrieving..." 100)  ; remove progress bar.
2618       (message "Retrieving...done"))
2619     (if (setq response (elmo-imap4-response-bodydetail-text
2620                         (elmo-imap4-response-value-all
2621                          response 'fetch)))
2622         (with-current-buffer outbuf
2623           (erase-buffer)
2624           (insert response)
2625           t))))
2626
2627 (luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder)
2628                                                 number strategy
2629                                                 &optional section
2630                                                 outbuf unseen)
2631   (elmo-imap4-message-fetch folder number strategy section outbuf unseen))
2632
2633 (luna-define-method elmo-message-fetch-field ((folder elmo-imap4-folder)
2634                                               number field)
2635   (let ((session (elmo-imap4-get-session folder)))
2636     (elmo-imap4-session-select-mailbox session
2637                                        (elmo-imap4-folder-mailbox-internal
2638                                         folder))
2639     (with-current-buffer (elmo-network-session-buffer session)
2640       (setq elmo-imap4-fetch-callback nil)
2641       (setq elmo-imap4-fetch-callback-data nil))
2642     (with-temp-buffer
2643       (insert
2644        (elmo-imap4-response-bodydetail-text
2645         (elmo-imap4-response-value
2646          (elmo-imap4-send-command-wait session
2647                                        (concat
2648                                         (if elmo-imap4-use-uid
2649                                             "uid ")
2650                                         (format
2651                                          "fetch %s (body.peek[header.fields (%s)])"
2652                                          number field)))
2653          'fetch)))
2654       (elmo-delete-cr-buffer)
2655       (goto-char (point-min))
2656       (std11-field-body (symbol-name field)))))
2657
2658 (luna-define-method elmo-folder-search-requires-msgdb-p ((folder
2659                                                           elmo-imap4-folder)
2660                                                          condition)
2661   nil)
2662
2663 (autoload 'elmo-global-flags-set "elmo-flag")
2664
2665 (require 'product)
2666 (product-provide (provide 'elmo-imap4) (require 'elmo-version))
2667
2668 ;;; elmo-imap4.el ends here