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