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