* wl-summary.el (wl-summary-insert-summary): Renamed dummy argument.
[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            (cons (elmo-imap4-response-value status 'unseen)
1074                  (elmo-imap4-response-value status 'messages))
1075            data))
1076
1077 (defun elmo-imap4-server-diff-async (folder)
1078   (let ((session (elmo-imap4-get-session folder)))
1079     ;; We should `check' folder to obtain newest information here.
1080     ;; But since there's no asynchronous check mechanism in elmo yet,
1081     ;; checking is not done here.
1082     (with-current-buffer (elmo-network-session-buffer session)
1083       (setq elmo-imap4-status-callback
1084             'elmo-imap4-server-diff-async-callback-1)
1085       (setq elmo-imap4-status-callback-data
1086             elmo-imap4-server-diff-async-callback-data))
1087     (elmo-imap4-send-command session
1088                              (list
1089                               "status "
1090                               (elmo-imap4-mailbox
1091                                (elmo-imap4-folder-mailbox-internal folder))
1092                               " (unseen messages)"))))
1093
1094 (luna-define-method elmo-server-diff-async ((folder elmo-imap4-folder))
1095   (let ((session (elmo-imap4-get-session folder)))
1096     ;; commit.
1097     ;; (elmo-imap4-commit spec)
1098     (with-current-buffer (elmo-network-session-buffer session)
1099       (setq elmo-imap4-status-callback
1100             'elmo-imap4-server-diff-async-callback-1)
1101       (setq elmo-imap4-status-callback-data
1102             elmo-imap4-server-diff-async-callback-data))
1103     (elmo-imap4-send-command session
1104                              (list
1105                               "status "
1106                               (elmo-imap4-mailbox
1107                                (elmo-imap4-folder-mailbox-internal folder))
1108                               " (unseen messages)"))))
1109
1110 ;;; IMAP parser.
1111
1112 (defvar elmo-imap4-server-eol "\r\n"
1113   "The EOL string sent from the server.")
1114
1115 (defvar elmo-imap4-client-eol "\r\n"
1116   "The EOL string we send to the server.")
1117
1118 (defvar elmo-imap4-display-literal-progress nil)
1119
1120 (defun elmo-imap4-find-next-line ()
1121   "Return point at end of current line, taking into account literals.
1122 Return nil if no complete line has arrived."
1123   (when (re-search-forward (concat elmo-imap4-server-eol "\\|{\\([0-9]+\\)}"
1124                                    elmo-imap4-server-eol)
1125                            nil t)
1126     (if (match-string 1)
1127         (if (< (point-max) (+ (point) (string-to-number (match-string 1))))
1128             (progn
1129               (if (and elmo-imap4-display-literal-progress
1130                        (> (string-to-number (match-string 1))
1131                           (min elmo-display-retrieval-progress-threshold 100)))
1132                   (elmo-display-progress
1133                    'elmo-imap4-display-literal-progress
1134                    (format "Retrieving (%d/%d bytes)..."
1135                            (- (point-max) (point))
1136                            (string-to-number (match-string 1)))
1137                    (/ (- (point-max) (point))
1138                       (/ (string-to-number (match-string 1)) 100))))
1139               nil)
1140           (goto-char (+ (point) (string-to-number (match-string 1))))
1141           (elmo-imap4-find-next-line))
1142       (point))))
1143
1144 (defun elmo-imap4-sentinel (process string)
1145   (delete-process process))
1146
1147 (defun elmo-imap4-arrival-filter (proc string)
1148   "IMAP process filter."
1149   (when (buffer-live-p (process-buffer proc))
1150   (with-current-buffer (process-buffer proc)
1151     (elmo-imap4-debug "-> %s" string)
1152     (goto-char (point-max))
1153     (insert string)
1154     (let (end)
1155       (goto-char (point-min))
1156       (while (setq end (elmo-imap4-find-next-line))
1157         (save-restriction
1158           (narrow-to-region (point-min) end)
1159           (delete-backward-char (length elmo-imap4-server-eol))
1160           (goto-char (point-min))
1161           (unwind-protect
1162               (cond ((eq elmo-imap4-status 'initial)
1163                      (setq elmo-imap4-current-response
1164                            (list
1165                             (list 'greeting (elmo-imap4-parse-greeting)))))
1166                     ((or (eq elmo-imap4-status 'auth)
1167                          (eq elmo-imap4-status 'nonauth)
1168                          (eq elmo-imap4-status 'selected)
1169                          (eq elmo-imap4-status 'examine))
1170                      (setq elmo-imap4-current-response
1171                            (cons
1172                             (elmo-imap4-parse-response)
1173                             elmo-imap4-current-response)))
1174                     (t
1175                      (message "Unknown state %s in arrival filter"
1176                               elmo-imap4-status))))
1177           (delete-region (point-min) (point-max))))))))
1178
1179 ;; IMAP parser.
1180
1181 (defsubst elmo-imap4-forward ()
1182   (or (eobp) (forward-char 1)))
1183
1184 (defsubst elmo-imap4-parse-number ()
1185   (when (looking-at "[0-9]+")
1186     (prog1
1187         (string-to-number (match-string 0))
1188       (goto-char (match-end 0)))))
1189
1190 (defsubst elmo-imap4-parse-literal ()
1191   (when (looking-at "{\\([0-9]+\\)}\r\n")
1192     (let ((pos (match-end 0))
1193           (len (string-to-number (match-string 1))))
1194       (if (< (point-max) (+ pos len))
1195           nil
1196         (goto-char (+ pos len))
1197         (buffer-substring pos (+ pos len))))))
1198 ;;;     (list ' pos (+ pos len))))))
1199
1200 (defsubst elmo-imap4-parse-string ()
1201   (cond ((eq (char-after (point)) ?\")
1202          (forward-char 1)
1203          (let ((p (point)) (name ""))
1204            (skip-chars-forward "^\"\\\\")
1205            (setq name (buffer-substring p (point)))
1206            (while (eq (char-after (point)) ?\\)
1207              (setq p (1+ (point)))
1208              (forward-char 2)
1209              (skip-chars-forward "^\"\\\\")
1210              (setq name (concat name (buffer-substring p (point)))))
1211            (forward-char 1)
1212            name))
1213         ((eq (char-after (point)) ?{)
1214          (elmo-imap4-parse-literal))))
1215
1216 (defsubst elmo-imap4-parse-nil ()
1217   (if (looking-at "NIL")
1218       (goto-char (match-end 0))))
1219
1220 (defsubst elmo-imap4-parse-nstring ()
1221   (or (elmo-imap4-parse-string)
1222       (and (elmo-imap4-parse-nil)
1223            nil)))
1224
1225 (defsubst elmo-imap4-parse-astring ()
1226   (or (elmo-imap4-parse-string)
1227       (buffer-substring (point)
1228                         (if (re-search-forward "[(){ \r\n%*\"\\]" nil t)
1229                             (goto-char (1- (match-end 0)))
1230                           (end-of-line)
1231                           (point)))))
1232
1233 (defsubst elmo-imap4-parse-address ()
1234   (let (address)
1235     (when (eq (char-after (point)) ?\()
1236       (elmo-imap4-forward)
1237       (setq address (vector (prog1 (elmo-imap4-parse-nstring)
1238                               (elmo-imap4-forward))
1239                             (prog1 (elmo-imap4-parse-nstring)
1240                               (elmo-imap4-forward))
1241                             (prog1 (elmo-imap4-parse-nstring)
1242                               (elmo-imap4-forward))
1243                             (elmo-imap4-parse-nstring)))
1244       (when (eq (char-after (point)) ?\))
1245         (elmo-imap4-forward)
1246         address))))
1247
1248 (defsubst elmo-imap4-parse-address-list ()
1249   (if (eq (char-after (point)) ?\()
1250       (let (address addresses)
1251         (elmo-imap4-forward)
1252         (while (and (not (eq (char-after (point)) ?\)))
1253                     ;; next line for MS Exchange bug
1254                     (progn (and (eq (char-after (point)) ? ) (elmo-imap4-forward)) t)
1255                     (setq address (elmo-imap4-parse-address)))
1256           (setq addresses (cons address addresses)))
1257         (when (eq (char-after (point)) ?\))
1258           (elmo-imap4-forward)
1259           (nreverse addresses)))
1260     (assert (elmo-imap4-parse-nil))))
1261
1262 (defsubst elmo-imap4-parse-mailbox ()
1263   (let ((mailbox (elmo-imap4-parse-astring)))
1264     (if (string-equal "INBOX" (upcase mailbox))
1265         "INBOX"
1266       mailbox)))
1267
1268 (defun elmo-imap4-parse-greeting ()
1269   "Parse a IMAP greeting."
1270   (cond ((looking-at "\\* OK ")
1271          (setq elmo-imap4-status 'nonauth))
1272         ((looking-at "\\* PREAUTH ")
1273          (setq elmo-imap4-status 'auth))
1274         ((looking-at "\\* BYE ")
1275          (setq elmo-imap4-status 'closed))))
1276
1277 (defun elmo-imap4-parse-response ()
1278   "Parse a IMAP command response."
1279   (let (token)
1280     (case (setq token (elmo-read (current-buffer)))
1281       (+ (progn
1282            (skip-chars-forward " ")
1283            (list 'continue-req (buffer-substring (point) (point-max)))))
1284       (* (case (prog1 (setq token (elmo-read (current-buffer)))
1285                  (elmo-imap4-forward))
1286            (OK         (elmo-imap4-parse-resp-text-code))
1287            (NO         (elmo-imap4-parse-resp-text-code))
1288            (BAD        (elmo-imap4-parse-resp-text-code))
1289            (BYE        (elmo-imap4-parse-bye))
1290            (FLAGS      (list 'flags
1291                              (elmo-imap4-parse-flag-list)))
1292            (LIST       (list 'list (elmo-imap4-parse-data-list)))
1293            (LSUB       (list 'lsub (elmo-imap4-parse-data-list)))
1294            (SEARCH     (list
1295                         'search
1296                         (elmo-read (concat "("
1297                                       (buffer-substring (point) (point-max))
1298                                       ")"))))
1299            (STATUS     (elmo-imap4-parse-status))
1300            ;; Added
1301            (NAMESPACE  (elmo-imap4-parse-namespace))
1302            (CAPABILITY (list 'capability
1303                              (elmo-read
1304                               (concat "(" (downcase (buffer-substring
1305                                                      (point) (point-max)))
1306                                       ")"))))
1307            (ACL (elmo-imap4-parse-acl))
1308            (t       (case (prog1 (elmo-read (current-buffer))
1309                             (elmo-imap4-forward))
1310                       (EXISTS  (list 'exists token))
1311                       (RECENT  (list 'recent token))
1312                       (EXPUNGE (list 'expunge token))
1313                       (FETCH   (elmo-imap4-parse-fetch token))
1314                       (t       (list 'garbage (buffer-string)))))))
1315       (t (if (not (string-match elmo-imap4-seq-prefix (symbol-name token)))
1316              (list 'garbage (buffer-string))
1317            (case (prog1 (elmo-read (current-buffer))
1318                    (elmo-imap4-forward))
1319              (OK  (progn
1320                     (setq elmo-imap4-parsing nil)
1321                     (setq token (symbol-name token))
1322                     (elmo-unintern token)
1323                     (elmo-imap4-debug "*%s* OK arrived" token)
1324                     (setq elmo-imap4-reached-tag token)
1325                     (list 'ok (elmo-imap4-parse-resp-text-code))))
1326              (NO  (progn
1327                     (setq elmo-imap4-parsing nil)
1328                     (setq token (symbol-name token))
1329                     (elmo-unintern token)
1330                     (elmo-imap4-debug "*%s* NO arrived" token)
1331                     (setq elmo-imap4-reached-tag token)
1332                     (let (code text)
1333                       (when (eq (char-after (point)) ?\[)
1334                         (setq code (buffer-substring (point)
1335                                                      (search-forward "]")))
1336                         (elmo-imap4-forward))
1337                       (setq text (buffer-substring (point) (point-max)))
1338                       (list 'no (list code text)))))
1339              (BAD (progn
1340                     (setq elmo-imap4-parsing nil)
1341                     (elmo-imap4-debug "*%s* BAD arrived" token)
1342                     (setq token (symbol-name token))
1343                     (elmo-unintern token)
1344                     (setq elmo-imap4-reached-tag token)
1345                     (let (code text)
1346                       (when (eq (char-after (point)) ?\[)
1347                         (setq code (buffer-substring (point)
1348                                                      (search-forward "]")))
1349                         (elmo-imap4-forward))
1350                       (setq text (buffer-substring (point) (point-max)))
1351                       (list 'bad (list code text)))))
1352              (t   (list 'garbage (buffer-string)))))))))
1353
1354 (defun elmo-imap4-parse-bye ()
1355   (let (code text)
1356     (when (eq (char-after (point)) ?\[)
1357       (setq code (buffer-substring (point)
1358                                    (search-forward "]")))
1359       (elmo-imap4-forward))
1360     (setq text (buffer-substring (point) (point-max)))
1361     (list 'bye (list code text))))
1362
1363 (defun elmo-imap4-parse-text ()
1364   (goto-char (point-min))
1365   (when (search-forward "[" nil t)
1366     (search-forward "]")
1367     (elmo-imap4-forward))
1368   (list 'text (buffer-substring (point) (point-max))))
1369
1370 (defun elmo-imap4-parse-resp-text-code ()
1371   (when (eq (char-after (point)) ?\[)
1372     (elmo-imap4-forward)
1373     (cond ((search-forward "PERMANENTFLAGS " nil t)
1374            (list 'permanentflags (elmo-imap4-parse-flag-list)))
1375           ((search-forward "UIDNEXT " nil t)
1376            (list 'uidnext (elmo-read (current-buffer))))
1377           ((search-forward "UNSEEN " nil t)
1378            (list 'unseen (elmo-read (current-buffer))))
1379           ((looking-at "UIDVALIDITY \\([0-9]+\\)")
1380            (list 'uidvalidity (match-string 1)))
1381           ((search-forward "READ-ONLY" nil t)
1382            (list 'read-only t))
1383           ((search-forward "READ-WRITE" nil t)
1384            (list 'read-write t))
1385           ((search-forward "NEWNAME " nil t)
1386            (let (oldname newname)
1387              (setq oldname (elmo-imap4-parse-string))
1388              (elmo-imap4-forward)
1389              (setq newname (elmo-imap4-parse-string))
1390              (list 'newname newname oldname)))
1391           ((search-forward "TRYCREATE" nil t)
1392            (list 'trycreate t))
1393           ((looking-at "APPENDUID \\([0-9]+\\) \\([0-9]+\\)")
1394            (list 'appenduid
1395                  (list (match-string 1)
1396                        (string-to-number (match-string 2)))))
1397           ((looking-at "COPYUID \\([0-9]+\\) \\([0-9,:]+\\) \\([0-9,:]+\\)")
1398            (list 'copyuid (list (match-string 1)
1399                                 (match-string 2)
1400                                 (match-string 3))))
1401           ((search-forward "ALERT] " nil t)
1402            (message "IMAP server information: %s"
1403                     (buffer-substring (point) (point-max))))
1404           (t (list 'unknown)))))
1405
1406 (defun elmo-imap4-parse-data-list ()
1407   (let (flags delimiter mailbox)
1408     (setq flags (elmo-imap4-parse-flag-list))
1409     (when (looking-at " NIL\\| \"\\\\?\\(.\\)\"")
1410       (setq delimiter (match-string 1))
1411       (goto-char (1+ (match-end 0)))
1412       (when (setq mailbox (elmo-imap4-parse-mailbox))
1413         (list mailbox flags delimiter)))))
1414
1415 (defsubst elmo-imap4-parse-header-list ()
1416   (when (eq (char-after (point)) ?\()
1417     (let (strlist)
1418       (while (not (eq (char-after (point)) ?\)))
1419         (elmo-imap4-forward)
1420         (push (elmo-imap4-parse-astring) strlist))
1421       (elmo-imap4-forward)
1422       (nreverse strlist))))
1423
1424 (defsubst elmo-imap4-parse-fetch-body-section ()
1425   (let ((section
1426          (buffer-substring (point)
1427                            (1-
1428                             (progn (re-search-forward "[] ]" nil t)
1429                                    (point))))))
1430     (if (eq (char-before) ? )
1431         (prog1
1432             (mapconcat 'identity
1433                        (cons section (elmo-imap4-parse-header-list)) " ")
1434           (search-forward "]" nil t))
1435       section)))
1436
1437 (defun elmo-imap4-parse-fetch (response)
1438   (when (eq (char-after (point)) ?\()
1439     (let (element list)
1440       (while (not (eq (char-after (point)) ?\)))
1441         (elmo-imap4-forward)
1442         (let ((token (elmo-imap4-fetch-read (current-buffer))))
1443           (elmo-imap4-forward)
1444           (setq element
1445                 (cond ((eq token 'UID)
1446                        (list 'uid (condition-case nil
1447                                       (elmo-read (current-buffer))
1448                                     (error nil))))
1449                       ((eq token 'FLAGS)
1450                        (list 'flags (elmo-imap4-parse-flag-list)))
1451                       ((eq token 'ENVELOPE)
1452                        (list 'envelope (elmo-imap4-parse-envelope)))
1453                       ((eq token 'INTERNALDATE)
1454                        (list 'internaldate (elmo-imap4-parse-string)))
1455                       ((eq token 'RFC822)
1456                        (list 'rfc822 (elmo-imap4-parse-nstring)))
1457                       ((eq token (intern elmo-imap4-rfc822-header))
1458                        (list 'rfc822header (elmo-imap4-parse-nstring)))
1459                       ((eq token (intern elmo-imap4-rfc822-text))
1460                        (list 'rfc822text (elmo-imap4-parse-nstring)))
1461                       ((eq token (intern elmo-imap4-rfc822-size))
1462                        (list 'rfc822size (elmo-read (current-buffer))))
1463                       ((eq token 'BODY)
1464                        (if (eq (char-before) ?\[)
1465                            (list
1466                             'bodydetail
1467                             (upcase (elmo-imap4-parse-fetch-body-section))
1468                             (and
1469                              (eq (char-after (point)) ?<)
1470                              (buffer-substring (1+ (point))
1471                                                (progn
1472                                                  (search-forward ">" nil t)
1473                                                  (point))))
1474                             (progn (elmo-imap4-forward)
1475                                    (elmo-imap4-parse-nstring)))
1476                          (list 'body (elmo-imap4-parse-body))))
1477                       ((eq token 'BODYSTRUCTURE)
1478                        (list 'bodystructure (elmo-imap4-parse-body)))))
1479           (setq list (cons element list))))
1480       (and elmo-imap4-fetch-callback
1481            (funcall elmo-imap4-fetch-callback
1482                     list elmo-imap4-fetch-callback-data))
1483       (list 'fetch list))))
1484
1485 (defun elmo-imap4-parse-status ()
1486   (let ((mailbox (elmo-imap4-parse-mailbox))
1487         status)
1488     (when (and mailbox (search-forward "(" nil t))
1489       (while (not (eq (char-after (point)) ?\)))
1490         (setq status
1491               (cons
1492                (let ((token (elmo-read (current-buffer))))
1493                  (cond ((eq token 'MESSAGES)
1494                         (list 'messages (elmo-read (current-buffer))))
1495                        ((eq token 'RECENT)
1496                         (list 'recent (elmo-read (current-buffer))))
1497                        ((eq token 'UIDNEXT)
1498                         (list 'uidnext (elmo-read (current-buffer))))
1499                        ((eq token 'UIDVALIDITY)
1500                         (and (looking-at " \\([0-9]+\\)")
1501                              (prog1 (list 'uidvalidity (match-string 1))
1502                                (goto-char (match-end 1)))))
1503                        ((eq token 'UNSEEN)
1504                         (list 'unseen (elmo-read (current-buffer))))
1505                        (t
1506                         (message
1507                          "Unknown status data %s in mailbox %s ignored"
1508                          token mailbox))))
1509                status))))
1510     (and elmo-imap4-status-callback
1511          (funcall elmo-imap4-status-callback
1512                   status
1513                   elmo-imap4-status-callback-data))
1514     (list 'status status)))
1515
1516
1517 (defmacro elmo-imap4-value (value)
1518   (` (if (eq (, value) 'NIL) nil
1519        (, value))))
1520
1521 (defmacro elmo-imap4-nth (pos list)
1522   (` (let ((value (nth (, pos) (, list))))
1523        (elmo-imap4-value value))))
1524
1525 (defun elmo-imap4-parse-namespace ()
1526   (list 'namespace
1527         (nconc
1528          (copy-sequence elmo-imap4-extra-namespace-alist)
1529          (elmo-imap4-parse-namespace-subr
1530           (elmo-read (concat "(" (buffer-substring
1531                                   (point) (point-max))
1532                              ")"))))))
1533
1534 (defun elmo-imap4-parse-namespace-subr (ns)
1535   (let (prefix delim namespace-alist default-delim)
1536     ;; 0: personal, 1: other, 2: shared
1537     (dotimes (i 3)
1538       (setq namespace-alist
1539             (nconc namespace-alist
1540                    (delq nil
1541                          (mapcar
1542                           (lambda (namespace)
1543                             (setq prefix (elmo-imap4-nth 0 namespace)
1544                                   delim (elmo-imap4-nth 1 namespace))
1545                             (if (and prefix delim
1546                                      (string-match
1547                                       (concat (regexp-quote delim) "\\'")
1548                                       prefix))
1549                                 (setq prefix (substring prefix 0
1550                                                         (match-beginning 0))))
1551                             (if (eq (length prefix) 0)
1552                                 (progn (setq default-delim delim) nil)
1553                               (cons
1554                                (concat "^"
1555                                        (if (string= (downcase prefix) "inbox")
1556                                            "[Ii][Nn][Bb][Oo][Xx]"
1557                                          (regexp-quote prefix))
1558                                        ".*$")
1559                                delim)))
1560                           (elmo-imap4-nth i ns))))))
1561     (if default-delim
1562         (setq namespace-alist
1563               (nconc namespace-alist
1564                      (list (cons "^.*$" default-delim)))))
1565     namespace-alist))
1566
1567 (defun elmo-imap4-parse-acl ()
1568   (let ((mailbox (elmo-imap4-parse-mailbox))
1569         identifier rights acl)
1570     (while (eq (char-after (point)) ?\ )
1571       (elmo-imap4-forward)
1572       (setq identifier (elmo-imap4-parse-astring))
1573       (elmo-imap4-forward)
1574       (setq rights (elmo-imap4-parse-astring))
1575       (setq acl (append acl (list (cons identifier rights)))))
1576     (list 'acl acl mailbox)))
1577
1578 (defun elmo-imap4-parse-flag-list ()
1579   (let ((str (buffer-substring (+ (point) 1)
1580                                (progn (search-forward ")" nil t)
1581                                       (- (point) 1)))))
1582     (unless (eq (length str) 0)
1583       (split-string str))))
1584
1585 (defun elmo-imap4-parse-envelope ()
1586   (when (eq (char-after (point)) ?\()
1587     (elmo-imap4-forward)
1588     (vector (prog1 (elmo-imap4-parse-nstring);; date
1589               (elmo-imap4-forward))
1590             (prog1 (elmo-imap4-parse-nstring);; subject
1591               (elmo-imap4-forward))
1592             (prog1 (elmo-imap4-parse-address-list);; from
1593               (elmo-imap4-forward))
1594             (prog1 (elmo-imap4-parse-address-list);; sender
1595               (elmo-imap4-forward))
1596             (prog1 (elmo-imap4-parse-address-list);; reply-to
1597               (elmo-imap4-forward))
1598             (prog1 (elmo-imap4-parse-address-list);; to
1599               (elmo-imap4-forward))
1600             (prog1 (elmo-imap4-parse-address-list);; cc
1601               (elmo-imap4-forward))
1602             (prog1 (elmo-imap4-parse-address-list);; bcc
1603               (elmo-imap4-forward))
1604             (prog1 (elmo-imap4-parse-nstring);; in-reply-to
1605               (elmo-imap4-forward))
1606             (prog1 (elmo-imap4-parse-nstring);; message-id
1607               (elmo-imap4-forward)))))
1608
1609 (defsubst elmo-imap4-parse-string-list ()
1610   (cond ((eq (char-after (point)) ?\();; body-fld-param
1611          (let (strlist str)
1612            (elmo-imap4-forward)
1613            (while (setq str (elmo-imap4-parse-string))
1614              (push str strlist)
1615              (elmo-imap4-forward))
1616            (nreverse strlist)))
1617         ((elmo-imap4-parse-nil)
1618          nil)))
1619
1620 (defun elmo-imap4-parse-body-extension ()
1621   (if (eq (char-after (point)) ?\()
1622       (let (b-e)
1623         (elmo-imap4-forward)
1624         (push (elmo-imap4-parse-body-extension) b-e)
1625         (while (eq (char-after (point)) ?\ )
1626           (elmo-imap4-forward)
1627           (push (elmo-imap4-parse-body-extension) b-e))
1628         (assert (eq (char-after (point)) ?\)))
1629         (elmo-imap4-forward)
1630         (nreverse b-e))
1631     (or (elmo-imap4-parse-number)
1632         (elmo-imap4-parse-nstring))))
1633
1634 (defsubst elmo-imap4-parse-body-ext ()
1635   (let (ext)
1636     (when (eq (char-after (point)) ?\ );; body-fld-dsp
1637       (elmo-imap4-forward)
1638       (let (dsp)
1639         (if (eq (char-after (point)) ?\()
1640             (progn
1641               (elmo-imap4-forward)
1642               (push (elmo-imap4-parse-string) dsp)
1643               (elmo-imap4-forward)
1644               (push (elmo-imap4-parse-string-list) dsp)
1645               (elmo-imap4-forward))
1646           (assert (elmo-imap4-parse-nil)))
1647         (push (nreverse dsp) ext))
1648       (when (eq (char-after (point)) ?\ );; body-fld-lang
1649         (elmo-imap4-forward)
1650         (if (eq (char-after (point)) ?\()
1651             (push (elmo-imap4-parse-string-list) ext)
1652           (push (elmo-imap4-parse-nstring) ext))
1653         (while (eq (char-after (point)) ?\ );; body-extension
1654           (elmo-imap4-forward)
1655           (setq ext (append (elmo-imap4-parse-body-extension) ext)))))
1656     ext))
1657
1658 (defun elmo-imap4-parse-body ()
1659   (let (body)
1660     (when (eq (char-after (point)) ?\()
1661       (elmo-imap4-forward)
1662       (if (eq (char-after (point)) ?\()
1663           (let (subbody)
1664             (while (and (eq (char-after (point)) ?\()
1665                         (setq subbody (elmo-imap4-parse-body)))
1666               (push subbody body))
1667             (elmo-imap4-forward)
1668             (push (elmo-imap4-parse-string) body);; media-subtype
1669             (when (eq (char-after (point)) ?\ );; body-ext-mpart:
1670               (elmo-imap4-forward)
1671               (if (eq (char-after (point)) ?\();; body-fld-param
1672                   (push (elmo-imap4-parse-string-list) body)
1673                 (push (and (elmo-imap4-parse-nil) nil) body))
1674               (setq body
1675                     (append (elmo-imap4-parse-body-ext) body)));; body-ext-...
1676             (assert (eq (char-after (point)) ?\)))
1677             (elmo-imap4-forward)
1678             (nreverse body))
1679
1680         (push (elmo-imap4-parse-string) body);; media-type
1681         (elmo-imap4-forward)
1682         (push (elmo-imap4-parse-string) body);; media-subtype
1683         (elmo-imap4-forward)
1684         ;; next line for Sun SIMS bug
1685         (and (eq (char-after (point)) ? ) (elmo-imap4-forward))
1686         (if (eq (char-after (point)) ?\();; body-fld-param
1687             (push (elmo-imap4-parse-string-list) body)
1688           (push (and (elmo-imap4-parse-nil) nil) body))
1689         (elmo-imap4-forward)
1690         (push (elmo-imap4-parse-nstring) body);; body-fld-id
1691         (elmo-imap4-forward)
1692         (push (elmo-imap4-parse-nstring) body);; body-fld-desc
1693         (elmo-imap4-forward)
1694         (push (elmo-imap4-parse-string) body);; body-fld-enc
1695         (elmo-imap4-forward)
1696         (push (elmo-imap4-parse-number) body);; body-fld-octets
1697
1698         ;; ok, we're done parsing the required parts, what comes now is one
1699         ;; of three things:
1700         ;;
1701         ;; envelope       (then we're parsing body-type-msg)
1702         ;; body-fld-lines (then we're parsing body-type-text)
1703         ;; body-ext-1part (then we're parsing body-type-basic)
1704         ;;
1705         ;; the problem is that the two first are in turn optionally followed
1706         ;; by the third.  So we parse the first two here (if there are any)...
1707
1708         (when (eq (char-after (point)) ?\ )
1709           (elmo-imap4-forward)
1710           (let (lines)
1711             (cond ((eq (char-after (point)) ?\();; body-type-msg:
1712                    (push (elmo-imap4-parse-envelope) body);; envelope
1713                    (elmo-imap4-forward)
1714                    (push (elmo-imap4-parse-body) body);; body
1715                    (elmo-imap4-forward)
1716                    (push (elmo-imap4-parse-number) body));; body-fld-lines
1717                   ((setq lines (elmo-imap4-parse-number));; body-type-text:
1718                    (push lines body));; body-fld-lines
1719                   (t
1720                    (backward-char)))));; no match...
1721
1722         ;; ...and then parse the third one here...
1723
1724         (when (eq (char-after (point)) ?\ );; body-ext-1part:
1725           (elmo-imap4-forward)
1726           (push (elmo-imap4-parse-nstring) body);; body-fld-md5
1727           (setq body
1728                 (append (elmo-imap4-parse-body-ext) body)));; body-ext-1part..
1729
1730         (assert (eq (char-after (point)) ?\)))
1731         (elmo-imap4-forward)
1732         (nreverse body)))))
1733
1734 (luna-define-method elmo-folder-initialize :around ((folder
1735                                                      elmo-imap4-folder)
1736                                                     name)
1737   (let ((default-user        elmo-imap4-default-user)
1738         (default-server      elmo-imap4-default-server)
1739         (default-port        elmo-imap4-default-port)
1740         (elmo-network-stream-type-alist
1741          (if elmo-imap4-stream-type-alist
1742              (append elmo-imap4-stream-type-alist
1743                      elmo-network-stream-type-alist)
1744            elmo-network-stream-type-alist))
1745         parse)
1746     (when (string-match "\\(.*\\)@\\(.*\\)" default-server)
1747       ;; case: imap4-default-server is specified like
1748       ;; "hoge%imap.server@gateway".
1749       (setq default-user (elmo-match-string 1 default-server))
1750       (setq default-server (elmo-match-string 2 default-server)))
1751     (setq name (luna-call-next-method))
1752     ;; mailbox
1753     (setq parse (elmo-parse-token name ":"))
1754     (elmo-imap4-folder-set-mailbox-internal folder
1755                                             (elmo-imap4-encode-folder-string
1756                                              (if (eq (length (car parse)) 0)
1757                                                  elmo-imap4-default-mailbox
1758                                                (car parse))))
1759     ;; user
1760     (setq parse (elmo-parse-prefixed-element ?: (cdr parse) "/"))
1761     (elmo-net-folder-set-user-internal folder
1762                                        (if (eq (length (car parse)) 0)
1763                                            default-user
1764                                          (car parse)))
1765     ;; auth
1766     (setq parse (elmo-parse-prefixed-element ?/ (cdr parse)))
1767     (elmo-net-folder-set-auth-internal
1768      folder
1769      (if (eq (length (car parse)) 0)
1770          (or elmo-imap4-default-authenticate-type 'clear)
1771        (intern (car parse))))
1772     (unless (elmo-net-folder-server-internal folder)
1773       (elmo-net-folder-set-server-internal folder default-server))
1774     (unless (elmo-net-folder-port-internal folder)
1775       (elmo-net-folder-set-port-internal folder default-port))
1776     (unless (elmo-net-folder-stream-type-internal folder)
1777       (elmo-net-folder-set-stream-type-internal
1778        folder
1779        (elmo-get-network-stream-type elmo-imap4-default-stream-type)))
1780     folder))
1781   
1782 ;;; ELMO IMAP4 folder
1783 (luna-define-method elmo-folder-expand-msgdb-path ((folder
1784                                                     elmo-imap4-folder))
1785   (convert-standard-filename
1786    (let ((mailbox (elmo-imap4-folder-mailbox-internal folder)))
1787      (if (string= "inbox" (downcase mailbox))
1788          (setq mailbox "inbox"))
1789      (if (eq (string-to-char mailbox) ?/)
1790          (setq mailbox (substring mailbox 1 (length mailbox))))
1791      (expand-file-name
1792       mailbox
1793       (expand-file-name
1794        (or (elmo-net-folder-user-internal folder) "nobody")
1795        (expand-file-name (or (elmo-net-folder-server-internal folder)
1796                              "nowhere")
1797                          (expand-file-name
1798                           "imap"
1799                           elmo-msgdb-dir)))))))
1800
1801 (luna-define-method elmo-folder-status-plugged ((folder
1802                                                  elmo-imap4-folder))
1803   (elmo-imap4-folder-status-plugged folder))
1804
1805 (defun elmo-imap4-folder-status-plugged (folder)
1806   (let ((session (elmo-imap4-get-session folder))
1807         (killed (elmo-msgdb-killed-list-load
1808                  (elmo-folder-msgdb-path folder)))
1809         status)
1810     (with-current-buffer (elmo-network-session-buffer session)
1811       (setq elmo-imap4-status-callback nil)
1812       (setq elmo-imap4-status-callback-data nil))
1813     (setq status (elmo-imap4-response-value
1814                   (elmo-imap4-send-command-wait
1815                    session
1816                    (list "status "
1817                          (elmo-imap4-mailbox
1818                           (elmo-imap4-folder-mailbox-internal folder))
1819                          " (uidnext messages)"))
1820                   'status))
1821     (cons
1822      (- (elmo-imap4-response-value status 'uidnext) 1)
1823      (if killed
1824          (-
1825           (elmo-imap4-response-value status 'messages)
1826           (elmo-msgdb-killed-list-length killed))
1827        (elmo-imap4-response-value status 'messages)))))
1828
1829 (luna-define-method elmo-folder-list-messages-plugged ((folder
1830                                                         elmo-imap4-folder)
1831                                                        &optional nohide)
1832   (elmo-imap4-list folder
1833                    (let ((max (elmo-msgdb-max-of-killed
1834                                (elmo-folder-killed-list-internal folder))))
1835                      (if (or nohide
1836                              (null (eq max 0)))
1837                          (format "uid %d:*" (1+ max))
1838                        "all"))))
1839
1840 (luna-define-method elmo-folder-list-unreads-plugged
1841   ((folder elmo-imap4-folder))
1842   (elmo-imap4-list folder "unseen"))
1843
1844 (luna-define-method elmo-folder-list-importants-plugged
1845   ((folder elmo-imap4-folder))
1846   (elmo-imap4-list folder "flagged"))
1847
1848 (luna-define-method elmo-folder-use-flag-p ((folder elmo-imap4-folder))
1849   (not (string-match elmo-imap4-disuse-server-flag-mailbox-regexp
1850                      (elmo-imap4-folder-mailbox-internal folder))))
1851
1852 (luna-define-method elmo-folder-list-subfolders ((folder elmo-imap4-folder)
1853                                                  &optional one-level)
1854   (let* ((root (elmo-imap4-folder-mailbox-internal folder))
1855          (session (elmo-imap4-get-session folder))
1856          (prefix (elmo-folder-prefix-internal folder))
1857          (delim (or
1858                  (cdr
1859                   (elmo-string-matched-assoc
1860                    root
1861                    (with-current-buffer (elmo-network-session-buffer session)
1862                      elmo-imap4-server-namespace)))
1863                  elmo-imap4-default-hierarchy-delimiter))
1864          result append-serv type)
1865     (setq result (elmo-imap4-response-get-selectable-mailbox-list
1866                   (elmo-imap4-send-command-wait
1867                    session
1868                    (list "list " (elmo-imap4-mailbox root) " *"))))
1869     (unless (string= (elmo-net-folder-user-internal folder)
1870                      elmo-imap4-default-user)
1871       (setq append-serv (concat ":" (elmo-net-folder-user-internal folder))))
1872     (unless (eq (elmo-net-folder-auth-internal folder)
1873                 (or elmo-imap4-default-authenticate-type 'clear))
1874       (setq append-serv
1875             (concat append-serv "/"
1876                     (symbol-name (elmo-net-folder-auth-internal folder)))))
1877     (unless (string= (elmo-net-folder-server-internal folder)
1878                      elmo-imap4-default-server)
1879       (setq append-serv (concat append-serv "@"
1880                                 (elmo-net-folder-server-internal folder))))
1881     (unless (eq (elmo-net-folder-port-internal folder) elmo-imap4-default-port)
1882       (setq append-serv (concat append-serv ":"
1883                                 (int-to-string
1884                                  (elmo-net-folder-port-internal folder)))))
1885     (setq type (elmo-net-folder-stream-type-internal folder))
1886     (unless (eq (elmo-network-stream-type-symbol type)
1887                 elmo-imap4-default-stream-type)
1888       (if type
1889           (setq append-serv (concat append-serv
1890                                     (elmo-network-stream-type-spec-string
1891                                      type)))))
1892     (if one-level
1893         (let ((re-delim (regexp-quote delim))
1894               folder ret has-child-p)
1895           ;; Append delimiter
1896           (when (and root
1897                      (not (string= root ""))
1898                      (not (string-match
1899                            (concat "\\(.*\\)" re-delim "\\'")
1900                            root)))
1901             (setq root (concat root delim)))
1902           (while (setq folder (car result))
1903             (when (string-match
1904                    (concat "^\\(" (regexp-quote root) "[^" re-delim "]" "+\\)"
1905                            re-delim)
1906                    folder)
1907               (setq folder (match-string 1 folder)))
1908             (setq has-child-p nil
1909                   result (delq
1910                           nil
1911                           (mapcar (lambda (fld)
1912                                     (if (string-match
1913                                          (concat "^" (regexp-quote folder)
1914                                                  "\\(" re-delim "\\|\\'\\)")
1915                                          fld)
1916                                         (progn (setq has-child-p t) nil)
1917                                       fld))
1918                                   (cdr result)))
1919                   folder (concat prefix
1920                                  (elmo-imap4-decode-folder-string folder)
1921                                  (and append-serv
1922                                       (eval append-serv)))
1923                   ret (append ret (if has-child-p
1924                                       (list (list folder))
1925                                     (list folder)))))
1926           ret)
1927       (mapcar (lambda (fld)
1928                 (concat prefix (elmo-imap4-decode-folder-string fld)
1929                         (and append-serv
1930                              (eval append-serv))))
1931               result))))
1932
1933 (luna-define-method elmo-folder-exists-p-plugged ((folder elmo-imap4-folder))
1934   (let ((session (elmo-imap4-get-session folder)))
1935     (if (string=
1936          (elmo-imap4-session-current-mailbox-internal session)
1937          (elmo-imap4-folder-mailbox-internal folder))
1938         t
1939       (elmo-imap4-session-select-mailbox
1940        session
1941        (elmo-imap4-folder-mailbox-internal folder)
1942        'force 'no-error))))
1943
1944 (luna-define-method elmo-folder-writable-p ((folder elmo-imap4-folder))
1945   t)
1946
1947 (luna-define-method elmo-folder-delete ((folder elmo-imap4-folder))
1948   (let ((session (elmo-imap4-get-session folder))
1949         msgs)
1950     (when (elmo-imap4-folder-mailbox-internal folder)
1951       (when (setq msgs (elmo-folder-list-messages folder))
1952         (elmo-folder-delete-messages folder msgs))
1953       (elmo-imap4-send-command-wait session "close")
1954       (elmo-imap4-send-command-wait
1955        session
1956        (list "delete "
1957              (elmo-imap4-mailbox
1958               (elmo-imap4-folder-mailbox-internal folder)))))))
1959
1960 (luna-define-method elmo-folder-rename-internal ((folder elmo-imap4-folder)
1961                                                  new-folder)
1962   (let ((session (elmo-imap4-get-session folder)))
1963     ;; make sure the folder is selected.
1964     (elmo-imap4-session-select-mailbox session
1965                                        (elmo-imap4-folder-mailbox-internal
1966                                         folder))
1967     (elmo-imap4-send-command-wait session "close")
1968     (elmo-imap4-send-command-wait
1969      session
1970      (list "rename "
1971            (elmo-imap4-mailbox
1972             (elmo-imap4-folder-mailbox-internal folder))
1973            " "
1974            (elmo-imap4-mailbox
1975             (elmo-imap4-folder-mailbox-internal new-folder))))))
1976
1977 (defun elmo-imap4-copy-messages (src-folder dst-folder numbers)
1978   (let ((session (elmo-imap4-get-session src-folder))
1979         (set-list (elmo-imap4-make-number-set-list numbers)))
1980     (elmo-imap4-session-select-mailbox session
1981                                        (elmo-imap4-folder-mailbox-internal
1982                                         src-folder))
1983     (when set-list
1984       (if (elmo-imap4-send-command-wait session
1985                                         (list
1986                                          (format
1987                                           (if elmo-imap4-use-uid
1988                                               "uid copy %s "
1989                                             "copy %s ")
1990                                           (cdr (car set-list)))
1991                                          (elmo-imap4-mailbox
1992                                           (elmo-imap4-folder-mailbox-internal
1993                                            dst-folder))))
1994           numbers))))
1995
1996 (defun elmo-imap4-set-flag (folder numbers flag &optional remove)
1997   "Set flag on messages.
1998 FOLDER is the ELMO folder structure.
1999 NUMBERS is the message numbers to be flagged.
2000 FLAG is the flag name.
2001 If optional argument REMOVE is non-nil, remove FLAG."
2002   (let ((session (elmo-imap4-get-session folder))
2003         set-list)
2004     (elmo-imap4-session-select-mailbox session
2005                                        (elmo-imap4-folder-mailbox-internal
2006                                         folder))
2007     (setq set-list (elmo-imap4-make-number-set-list numbers))
2008     (when set-list
2009       (with-current-buffer (elmo-network-session-buffer session)
2010         (setq elmo-imap4-fetch-callback nil)
2011         (setq elmo-imap4-fetch-callback-data nil))
2012       (elmo-imap4-send-command-wait
2013        session
2014        (format
2015         (if elmo-imap4-use-uid
2016             "uid store %s %sflags.silent (%s)"
2017           "store %s %sflags.silent (%s)")
2018         (cdr (car set-list))
2019         (if remove "-" "+")
2020         flag)))))
2021
2022 (luna-define-method elmo-folder-delete-messages-plugged
2023   ((folder elmo-imap4-folder) numbers)
2024   (let ((session (elmo-imap4-get-session folder)))
2025     (elmo-imap4-set-flag folder numbers "\\Deleted")
2026     (elmo-imap4-send-command-wait session "expunge")))
2027
2028 (defmacro elmo-imap4-detect-search-charset (string)
2029   (` (with-temp-buffer
2030        (insert (, string))
2031        (detect-mime-charset-region (point-min) (point-max)))))
2032
2033 (defun elmo-imap4-search-internal-primitive (folder session filter from-msgs)
2034   (let ((search-key (elmo-filter-key filter))
2035         (imap-search-keys '("bcc" "body" "cc" "from" "subject" "to"))
2036         charset)
2037     (cond
2038      ((string= "last" search-key)
2039       (let ((numbers (or from-msgs (elmo-folder-list-messages folder))))
2040         (nthcdr (max (- (length numbers)
2041                         (string-to-int (elmo-filter-value filter)))
2042                      0)
2043                 numbers)))
2044      ((string= "first" search-key)
2045       (let* ((numbers (or from-msgs (elmo-folder-list-messages folder)))
2046              (rest (nthcdr (string-to-int (elmo-filter-value filter) )
2047                            numbers)))
2048         (mapcar '(lambda (x) (delete x numbers)) rest)
2049         numbers))
2050      ((or (string= "since" search-key)
2051           (string= "before" search-key))
2052       (setq search-key (concat "sent" search-key))
2053       (elmo-imap4-response-value
2054        (elmo-imap4-send-command-wait session
2055                                      (format
2056                                       (if elmo-imap4-use-uid
2057                                           "uid search %s%s%s %s"
2058                                         "search %s%s%s %s")
2059                                       (if from-msgs
2060                                           (concat
2061                                            (if elmo-imap4-use-uid "uid ")
2062                                            (cdr
2063                                             (car
2064                                              (elmo-imap4-make-number-set-list
2065                                               from-msgs)))
2066                                            " ")
2067                                         "")
2068                                       (if (eq (elmo-filter-type filter)
2069                                               'unmatch)
2070                                           "not " "")
2071                                       search-key
2072                                       (elmo-date-get-description
2073                                        (elmo-date-get-datevec
2074                                         (elmo-filter-value filter)))))
2075        'search))
2076      (t
2077       (setq charset
2078             (if (eq (length (elmo-filter-value filter)) 0)
2079                 (setq charset 'us-ascii)
2080               (elmo-imap4-detect-search-charset
2081                (elmo-filter-value filter))))
2082       (elmo-imap4-response-value
2083        (elmo-imap4-send-command-wait session
2084                                      (list
2085                                       (if elmo-imap4-use-uid "uid ")
2086                                       "search "
2087                                       "CHARSET "
2088                                       (elmo-imap4-astring
2089                                        (symbol-name charset))
2090                                       " "
2091                                       (if from-msgs
2092                                           (concat
2093                                            (if elmo-imap4-use-uid "uid ")
2094                                            (cdr
2095                                             (car
2096                                              (elmo-imap4-make-number-set-list
2097                                               from-msgs)))
2098                                            " ")
2099                                         "")
2100                                       (if (eq (elmo-filter-type filter)
2101                                               'unmatch)
2102                                           "not " "")
2103                                       (format "%s%s "
2104                                               (if (member
2105                                                    (elmo-filter-key filter)
2106                                                    imap-search-keys)
2107                                                   ""
2108                                                 "header ")
2109                                               (elmo-filter-key filter))
2110                                       (elmo-imap4-astring
2111                                        (encode-mime-charset-string
2112                                         (elmo-filter-value filter) charset))))
2113        'search)))))
2114
2115 (defun elmo-imap4-search-internal (folder session condition from-msgs)
2116   (let (result)
2117     (cond
2118      ((vectorp condition)
2119       (setq result (elmo-imap4-search-internal-primitive
2120                     folder session condition from-msgs)))
2121      ((eq (car condition) 'and)
2122       (setq result (elmo-imap4-search-internal folder session (nth 1 condition)
2123                                                from-msgs)
2124             result (elmo-list-filter result
2125                                      (elmo-imap4-search-internal
2126                                       folder session (nth 2 condition)
2127                                       from-msgs))))
2128      ((eq (car condition) 'or)
2129       (setq result (elmo-imap4-search-internal
2130                     folder session (nth 1 condition) from-msgs)
2131             result (elmo-uniq-list
2132                     (nconc result
2133                            (elmo-imap4-search-internal
2134                             folder session (nth 2 condition) from-msgs)))
2135             result (sort result '<))))))
2136
2137 (luna-define-method elmo-folder-search ((folder elmo-imap4-folder)
2138                                         condition &optional numbers)
2139   (save-excursion
2140     (let ((session (elmo-imap4-get-session folder)))
2141       (elmo-imap4-session-select-mailbox
2142        session
2143        (elmo-imap4-folder-mailbox-internal folder))
2144       (elmo-imap4-search-internal folder session condition numbers))))
2145
2146 (luna-define-method elmo-folder-msgdb-create-plugged
2147   ((folder elmo-imap4-folder) numbers &rest args)
2148   (when numbers
2149     (let ((session (elmo-imap4-get-session folder))
2150           (headers
2151            (append
2152             '("Subject" "From" "To" "Cc" "Date"
2153               "Message-Id" "References" "In-Reply-To")
2154             elmo-msgdb-extra-fields))
2155           (total 0)
2156           (length (length numbers))
2157           rfc2060 set-list)
2158       (setq rfc2060 (memq 'imap4rev1
2159                           (elmo-imap4-session-capability-internal
2160                            session)))
2161       (message "Getting overview...")
2162       (elmo-imap4-session-select-mailbox
2163        session (elmo-imap4-folder-mailbox-internal folder))
2164       (setq set-list (elmo-imap4-make-number-set-list
2165                       numbers
2166                       elmo-imap4-overview-fetch-chop-length))
2167       ;; Setup callback.
2168       (with-current-buffer (elmo-network-session-buffer session)
2169         (setq elmo-imap4-current-msgdb nil
2170               elmo-imap4-seen-messages nil
2171               elmo-imap4-fetch-callback 'elmo-imap4-fetch-callback-1
2172               elmo-imap4-fetch-callback-data (cons args
2173                                                    (elmo-folder-use-flag-p
2174                                                     folder)))
2175         (while set-list
2176           (elmo-imap4-send-command-wait
2177            session
2178            ;; get overview entity from IMAP4
2179            (format "%sfetch %s (%s rfc822.size flags)"
2180                    (if elmo-imap4-use-uid "uid " "")
2181                    (cdr (car set-list))
2182                    (if rfc2060
2183                        (format "body.peek[header.fields %s]" headers)
2184                      (format "%s" headers))))
2185           (when (> length elmo-display-progress-threshold)
2186             (setq total (+ total (car (car set-list))))
2187             (elmo-display-progress
2188              'elmo-imap4-msgdb-create "Getting overview..."
2189              (/ (* total 100) length)))
2190           (setq set-list (cdr set-list)))
2191         (message "Getting overview...done")
2192         (when elmo-imap4-seen-messages
2193           (elmo-imap4-set-flag folder elmo-imap4-seen-messages "\\Seen"))
2194         elmo-imap4-current-msgdb))))
2195
2196 (luna-define-method elmo-folder-unmark-important-plugged
2197   ((folder elmo-imap4-folder) numbers)
2198   (elmo-imap4-set-flag folder numbers "\\Flagged" 'remove))
2199
2200 (luna-define-method elmo-folder-mark-as-important-plugged
2201   ((folder elmo-imap4-folder) numbers)
2202   (elmo-imap4-set-flag folder numbers "\\Flagged"))
2203
2204 (luna-define-method elmo-folder-unmark-read-plugged
2205   ((folder elmo-imap4-folder) numbers)
2206   (elmo-imap4-set-flag folder numbers "\\Seen" 'remove))
2207
2208 (luna-define-method elmo-folder-mark-as-read-plugged
2209   ((folder elmo-imap4-folder) numbers)
2210   (elmo-imap4-set-flag folder numbers "\\Seen"))
2211
2212 (luna-define-method elmo-message-use-cache-p ((folder elmo-imap4-folder)
2213                                               number)
2214   elmo-imap4-use-cache)
2215
2216 (luna-define-method elmo-folder-message-appendable-p ((folder elmo-imap4-folder))
2217   (if (elmo-folder-plugged-p folder)
2218       (not (elmo-imap4-session-read-only-internal
2219             (elmo-imap4-get-session folder)))
2220     elmo-enable-disconnected-operation)) ; offline refile.
2221
2222 (luna-define-method elmo-folder-check-plugged ((folder elmo-imap4-folder))
2223   (let ((session (elmo-imap4-get-session folder 'if-exists)))
2224     (when session
2225       (if (string=
2226            (elmo-imap4-session-current-mailbox-internal session)
2227            (elmo-imap4-folder-mailbox-internal folder))
2228           (if elmo-imap4-use-select-to-update-status
2229               (elmo-imap4-session-select-mailbox
2230                session
2231                (elmo-imap4-folder-mailbox-internal folder)
2232                'force)
2233             (elmo-imap4-session-check session))))))
2234
2235 (defsubst elmo-imap4-folder-diff-plugged (folder)
2236   (let ((session (elmo-imap4-get-session folder))
2237         messages
2238         response killed)
2239 ;;; (elmo-imap4-commit spec)
2240     (with-current-buffer (elmo-network-session-buffer session)
2241       (setq elmo-imap4-status-callback nil)
2242       (setq elmo-imap4-status-callback-data nil))
2243     (setq response
2244           (elmo-imap4-send-command-wait session
2245                                         (list
2246                                          "status "
2247                                          (elmo-imap4-mailbox
2248                                           (elmo-imap4-folder-mailbox-internal
2249                                            folder))
2250                                          " (unseen messages)")))
2251     (setq response (elmo-imap4-response-value response 'status))
2252     (setq messages (elmo-imap4-response-value response 'messages))
2253     (setq killed (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
2254     (if killed
2255         (setq messages (- messages
2256                           (elmo-msgdb-killed-list-length
2257                            killed))))
2258     (cons (elmo-imap4-response-value response 'unseen)
2259           messages)))
2260
2261 (luna-define-method elmo-folder-diff-plugged ((folder elmo-imap4-folder))
2262   (elmo-imap4-folder-diff-plugged folder))
2263
2264 (luna-define-method elmo-folder-diff-async ((folder elmo-imap4-folder)
2265                                             &optional number-alist)
2266   (setq elmo-imap4-server-diff-async-callback
2267         elmo-folder-diff-async-callback)
2268   (setq elmo-imap4-server-diff-async-callback-data
2269         elmo-folder-diff-async-callback-data)
2270   (elmo-imap4-server-diff-async folder))
2271
2272 (luna-define-method elmo-folder-open :around ((folder elmo-imap4-folder)
2273                                               &optional load-msgdb)
2274   (if (elmo-folder-plugged-p folder)
2275       (let (session mailbox msgdb response tag)
2276         (condition-case err
2277             (progn
2278               (setq session (elmo-imap4-get-session folder)
2279                     mailbox (elmo-imap4-folder-mailbox-internal folder)
2280                     tag (elmo-imap4-send-command session
2281                                                  (list "select "
2282                                                        (elmo-imap4-mailbox
2283                                                         mailbox))))
2284               (if load-msgdb
2285                   (setq msgdb (elmo-msgdb-load folder)))
2286               (elmo-folder-set-killed-list-internal
2287                folder
2288                (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder)))
2289               (setq response (elmo-imap4-read-response session tag)))
2290           (quit
2291            (if response
2292                (elmo-imap4-session-set-current-mailbox-internal
2293                 session mailbox)
2294              (and session
2295                   (elmo-imap4-session-set-current-mailbox-internal
2296                    session nil))))
2297           (error
2298            (if response
2299                (elmo-imap4-session-set-current-mailbox-internal
2300                 session mailbox)
2301              (and session
2302                   (elmo-imap4-session-set-current-mailbox-internal
2303                    session nil)))))
2304         (if load-msgdb
2305             (elmo-folder-set-msgdb-internal
2306              folder
2307              (or msgdb (elmo-msgdb-load folder)))))
2308     (luna-call-next-method)))
2309
2310 ;; elmo-folder-open-internal: do nothing.
2311
2312 (luna-define-method elmo-find-fetch-strategy
2313   ((folder elmo-imap4-folder) entity &optional ignore-cache)
2314   (let ((number (elmo-msgdb-overview-entity-get-number entity))
2315         cache-file size message-id)
2316     (setq size (elmo-msgdb-overview-entity-get-size entity))
2317     (setq message-id (elmo-msgdb-overview-entity-get-id entity))
2318     (setq cache-file (elmo-file-cache-get message-id))
2319     (if (or ignore-cache
2320             (null (elmo-file-cache-status cache-file)))
2321         (if (and elmo-message-fetch-threshold
2322                  (integerp size)
2323                  (>= size elmo-message-fetch-threshold)
2324                  (or (not elmo-message-fetch-confirm)
2325                      (not (prog1 (y-or-n-p
2326                                   (format
2327                                    "Fetch entire message at once? (%dbytes)"
2328                                    size))
2329                             (message "")))))
2330             ;; Fetch message as imap message.
2331             (elmo-make-fetch-strategy 'section
2332                                       nil
2333                                       (elmo-message-use-cache-p
2334                                        folder number)
2335                                       (elmo-file-cache-path
2336                                        cache-file))
2337           ;; Don't use existing cache and fetch entire message at once.
2338           (elmo-make-fetch-strategy 'entire nil
2339                                     (elmo-message-use-cache-p
2340                                      folder number)
2341                                     (elmo-file-cache-path cache-file)))
2342       ;; Cache found and use it.
2343       (if (not ignore-cache)
2344           (if (eq (elmo-file-cache-status cache-file) 'section)
2345               ;; Fetch message with imap message.
2346               (elmo-make-fetch-strategy 'section
2347                                         t
2348                                         (elmo-message-use-cache-p
2349                                          folder number)
2350                                         (elmo-file-cache-path
2351                                          cache-file))
2352             (elmo-make-fetch-strategy 'entire
2353                                       t
2354                                       (elmo-message-use-cache-p
2355                                        folder number)
2356                                       (elmo-file-cache-path
2357                                        cache-file)))))))
2358
2359 (luna-define-method elmo-folder-create ((folder elmo-imap4-folder))
2360   (elmo-imap4-send-command-wait
2361    (elmo-imap4-get-session folder)
2362    (list "create "
2363          (elmo-imap4-mailbox
2364           (elmo-imap4-folder-mailbox-internal folder)))))
2365
2366 (luna-define-method elmo-folder-append-buffer
2367   ((folder elmo-imap4-folder) unread &optional number)
2368   (if (elmo-folder-plugged-p folder)
2369       (let ((session (elmo-imap4-get-session folder))
2370             send-buffer result)
2371         (elmo-imap4-session-select-mailbox session
2372                                            (elmo-imap4-folder-mailbox-internal
2373                                             folder))
2374         (setq send-buffer (elmo-imap4-setup-send-buffer))
2375         (unwind-protect
2376             (setq result
2377                   (elmo-imap4-send-command-wait
2378                    session
2379                    (list
2380                     "append "
2381                     (elmo-imap4-mailbox (elmo-imap4-folder-mailbox-internal
2382                                          folder))
2383                     (if unread " " " (\\Seen) ")
2384                     (elmo-imap4-buffer-literal send-buffer))))
2385           (kill-buffer send-buffer))
2386         result)
2387     ;; Unplugged
2388     (if elmo-enable-disconnected-operation
2389         (elmo-folder-append-buffer-dop folder unread number)
2390       (error "Unplugged"))))
2391
2392 (eval-when-compile
2393   (defmacro elmo-imap4-identical-system-p (folder1 folder2)
2394     "Return t if FOLDER1 and FOLDER2 are in the same IMAP4 system."
2395     (` (and (string= (elmo-net-folder-server-internal (, folder1))
2396                      (elmo-net-folder-server-internal (, folder2)))
2397             (eq (elmo-net-folder-port-internal (, folder1))
2398                 (elmo-net-folder-port-internal (, folder2)))
2399             (string= (elmo-net-folder-user-internal (, folder1))
2400                      (elmo-net-folder-user-internal (, folder2)))))))
2401
2402 (luna-define-method elmo-folder-append-messages :around
2403   ((folder elmo-imap4-folder) src-folder numbers unread-marks
2404    &optional same-number)
2405   (if (and (eq (elmo-folder-type-internal src-folder) 'imap4)
2406            (elmo-imap4-identical-system-p folder src-folder)
2407            (elmo-folder-plugged-p folder))
2408       ;; Plugged
2409       (prog1
2410           (elmo-imap4-copy-messages src-folder folder numbers)
2411         (elmo-progress-notify 'elmo-folder-move-messages (length numbers)))
2412     (luna-call-next-method)))
2413
2414 (luna-define-method elmo-message-deletable-p ((folder elmo-imap4-folder)
2415                                               number)
2416   (if (elmo-folder-plugged-p folder)
2417       (not (elmo-imap4-session-read-only-internal
2418             (elmo-imap4-get-session folder)))
2419     elmo-enable-disconnected-operation)) ; offline refile.
2420
2421 ;(luna-define-method elmo-message-fetch-unplugged
2422 ;  ((folder elmo-imap4-folder)
2423 ;   number strategy  &optional section outbuf unseen)
2424 ;  (error "%d%s is not cached." number (if section
2425 ;                                         (format "(%s)" section)
2426 ;                                       "")))
2427
2428 (defsubst elmo-imap4-message-fetch (folder number strategy
2429                                            section outbuf unseen)
2430   (let ((session (elmo-imap4-get-session folder))
2431         response)
2432     (elmo-imap4-session-select-mailbox session
2433                                        (elmo-imap4-folder-mailbox-internal
2434                                         folder))
2435     (with-current-buffer (elmo-network-session-buffer session)
2436       (setq elmo-imap4-fetch-callback nil)
2437       (setq elmo-imap4-fetch-callback-data nil))
2438     (unless elmo-inhibit-display-retrieval-progress
2439       (setq elmo-imap4-display-literal-progress t))
2440     (unwind-protect
2441         (setq response
2442               (elmo-imap4-send-command-wait session
2443                                             (format
2444                                              (if elmo-imap4-use-uid
2445                                                  "uid fetch %s body%s[%s]"
2446                                                "fetch %s body%s[%s]")
2447                                              number
2448                                              (if unseen ".peek" "")
2449                                              (or section "")
2450                                              )))
2451       (setq elmo-imap4-display-literal-progress nil))
2452     (unless elmo-inhibit-display-retrieval-progress
2453       (elmo-display-progress 'elmo-imap4-display-literal-progress
2454                              "" 100)  ; remove progress bar.
2455       (message "Retrieving...done."))
2456     (if (setq response (elmo-imap4-response-bodydetail-text
2457                         (elmo-imap4-response-value-all
2458                          response 'fetch)))
2459         (with-current-buffer outbuf
2460           (erase-buffer)
2461           (insert response)))))
2462
2463 (luna-define-method elmo-message-fetch-plugged ((folder elmo-imap4-folder)
2464                                                 number strategy
2465                                                 &optional section
2466                                                 outbuf unseen)
2467   (elmo-imap4-message-fetch folder number strategy section outbuf unseen))
2468
2469 (luna-define-method elmo-message-fetch-field ((folder elmo-imap4-folder)
2470                                               number field)
2471   (let ((session (elmo-imap4-get-session folder)))
2472     (elmo-imap4-session-select-mailbox session
2473                                        (elmo-imap4-folder-mailbox-internal
2474                                         folder))
2475     (with-current-buffer (elmo-network-session-buffer session)
2476       (setq elmo-imap4-fetch-callback nil)
2477       (setq elmo-imap4-fetch-callback-data nil))
2478     (with-temp-buffer
2479       (insert
2480        (elmo-imap4-response-bodydetail-text
2481         (elmo-imap4-response-value
2482          (elmo-imap4-send-command-wait session
2483                                        (concat
2484                                         (if elmo-imap4-use-uid
2485                                             "uid ")
2486                                         (format
2487                                          "fetch %s (body.peek[header.fields (%s)])"
2488                                          number field)))
2489          'fetch)))
2490       (elmo-delete-cr-buffer)
2491       (goto-char (point-min))
2492       (std11-field-body (symbol-name field)))))
2493
2494
2495
2496 (require 'product)
2497 (product-provide (provide 'elmo-imap4) (require 'elmo-version))
2498
2499 ;;; elmo-imap4.el ends here