2047f620131f056fc938e4fe9de029df3f3f45ee
[elisp/wanderlust.git] / elmo / elmo.el
1 ;;; elmo.el --- Elisp Library for Message Orchestration.
2
3 ;; Copyright (C) 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14 ;;
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24 ;;
25
26 ;;; Commentary:
27 ;;
28
29 ;;; Code:
30 ;;
31
32 (require 'luna)
33
34 (require 'elmo-version)                 ; reduce recursive-load-depth
35 (require 'elmo-vars)
36 (require 'elmo-util)
37 (require 'elmo-msgdb)
38
39 (eval-when-compile (require 'cl))
40
41 (if (or (featurep 'dbm)
42         (featurep 'gnudbm)
43         (featurep 'berkdb)
44         (featurep 'berkeley-db))
45     (require 'elmo-database))
46
47 (defcustom elmo-message-fetch-threshold 30000
48   "Fetch threshold."
49   :type 'integer
50   :group 'elmo)
51
52 (defcustom elmo-message-fetch-confirm t
53   "If non-nil, confirm fetching if message size is larger than
54 `elmo-message-fetch-threshold'.
55 Otherwise, entire fetching of the message is aborted without confirmation."
56   :type 'boolean
57   :group 'elmo)
58
59 (defcustom elmo-folder-update-threshold 500
60   "Update threshold."
61   :type 'integer
62   :group 'elmo)
63
64 (defcustom elmo-folder-update-confirm t
65   "Confirm if update number exceeds `elmo-folder-update-threshold'."
66   :type 'boolean
67   :group 'elmo)
68
69 (defvar elmo-message-displaying nil
70   "A global switch to indicate message is displaying or not.")
71
72 ;;; internal
73 (defvar elmo-folder-type-alist nil)
74
75 (defvar elmo-newsgroups-hashtb nil)
76
77 (elmo-define-error 'elmo-error "Error" 'error)
78 (elmo-define-error 'elmo-open-error "Cannot open" 'elmo-error)
79 (elmo-define-error 'elmo-authenticate-error "Login failed" 'elmo-open-error)
80 (elmo-define-error 'elmo-imap4-bye-error "IMAP4 session was terminated" 'elmo-open-error)
81
82 (defun elmo-define-folder (prefix backend)
83   "Define a folder.
84 If a folder name begins with PREFIX, use BACKEND."
85   (let ((pair (assq prefix elmo-folder-type-alist)))
86     (if pair
87         (progn
88           (setcar pair prefix)
89           (setcdr pair backend))
90       (setq elmo-folder-type-alist (cons (cons prefix backend)
91                                          elmo-folder-type-alist)))))
92
93 (defmacro elmo-folder-type (name)
94   "Get folder type from NAME string."
95   (` (and (stringp (, name))
96           (cdr (assoc (string-to-char (, name)) elmo-folder-type-alist)))))
97
98 ;;; ELMO folder
99 ;; A elmo folder provides uniformed (orchestrated) access
100 ;; to the internet messages.
101 (eval-and-compile
102   (luna-define-class elmo-folder () (type   ; folder type symbol.
103                                      name   ; orignal folder name string.
104                                      prefix ; prefix for folder name
105                                      path   ; directory path for msgdb.
106                                      msgdb  ; msgdb (may be nil).
107                                      killed-list  ; killed list.
108                                      persistent   ; non-nil if persistent.
109                                      message-modified ; message is modified.
110                                      mark-modified    ; mark is modified.
111                                      process-duplicates  ; read or hide
112                                      biff   ; folder for biff
113                                      ))
114   (luna-define-internal-accessors 'elmo-folder))
115
116 (luna-define-generic elmo-folder-initialize (folder name)
117   ;; Initialize a FOLDER structure with NAME."
118   )
119
120 (defmacro elmo-folder-send (folder message &rest args)
121   "Let FOLDER receive the MESSAGE with ARGS."
122   (` (luna-send (, folder) (, message) (, folder) (,@ args))))
123
124 ;;;###autoload
125 (defun elmo-make-folder (name &optional non-persistent)
126   "Make an ELMO folder structure specified by NAME.
127 If optional argument NON-PERSISTENT is non-nil, folder is treated as
128  non-persistent."
129   (let ((type (elmo-folder-type name))
130         prefix split class folder original)
131     (setq original (elmo-string name))
132     (if type
133         (progn
134           (setq prefix (substring name 0 1))
135           (setq name (substring name 1)))
136       (setq type (intern (car (setq split (split-string name ":")))))
137       (if (> (length split) 2)
138           (setq name (substring name (+ 1 (length (car split)))))
139         (error "Error in folder name `%s'" original))
140       (setq prefix (concat (car split) ":")))
141     (setq class (format "elmo-%s" (symbol-name type)))
142     (require (intern class))
143     (setq folder (luna-make-entity (intern (concat class "-folder"))
144                                    :type   type
145                                    :prefix prefix
146                                    :name original
147                                    :persistent (not non-persistent)))
148     (save-match-data
149       (elmo-folder-send folder 'elmo-folder-initialize name))))
150
151 (luna-define-generic elmo-folder-msgdb (folder)
152   "Return the msgdb of FOLDER (on-demand loading).")
153
154 (luna-define-method elmo-folder-msgdb ((folder elmo-folder))
155   (or (elmo-folder-msgdb-internal folder)
156       (elmo-folder-set-msgdb-internal folder
157                                       (elmo-msgdb-load folder))))
158
159 (luna-define-generic elmo-folder-open (folder &optional load-msgdb)
160   "Open and setup (load saved status) FOLDER.
161 If optional LOAD-MSGDB is non-nil, msgdb is loaded.
162 (otherwise, msgdb is loaded on-demand)")
163
164 (luna-define-generic elmo-folder-open-internal (folder)
165   "Open FOLDER (without loading saved folder status).")
166
167 (luna-define-generic elmo-folder-check (folder)
168   "Check the FOLDER to obtain newest information at the next list operation.")
169
170 (luna-define-generic elmo-folder-clear (folder &optional keep-killed)
171   "Clear FOLDER to the initial state.
172 If optional KEEP-KILLED is non-nil, killed-list is not cleared.")
173
174 (luna-define-generic elmo-folder-commit (folder)
175   "Save current status of FOLDER.")
176
177 (luna-define-generic elmo-folder-close (folder)
178   "Close, save and clearnup FOLDER.")
179
180 (luna-define-generic elmo-folder-close-internal (folder)
181   "Close FOLDER (without saving folder status).")
182
183 (luna-define-generic elmo-folder-plugged-p (folder)
184   "Returns t if FOLDER is plugged.")
185
186 (luna-define-generic elmo-folder-set-plugged (folder plugged &optional add)
187   "Set FOLDER as plugged.")
188
189 (luna-define-generic elmo-net-port-info (folder)
190   "Get port information of FOLDER.")
191
192 (luna-define-generic elmo-folder-use-flag-p (folder)
193   "Returns t if FOLDER treats unread/important flag itself.")
194
195 (luna-define-generic elmo-folder-diff (folder &optional numbers)
196   "Get diff of FOLDER.
197 If optional NUMBERS is set, it is used as current NUMBERS.
198 Otherwise, saved status for folder is used for comparison.
199 Return value is cons cell or list:
200  - a cons cell (NEWS . MESSAGES)
201  - a list (RECENT UNSEEN MESSAGES) ; RECENT means NEWS, UNSEEN means UNREAD.")
202
203 (luna-define-generic elmo-folder-status (folder)
204   "Returns a cons cell of (MAX-NUMBER . MESSAGES) in the FOLDER.")
205
206 (luna-define-generic elmo-folder-reserve-status-p (folder)
207   "If non-nil, the folder should not close folder after `elmo-folder-status'.")
208
209 (defun elmo-folder-list-messages (folder &optional visible-only in-msgdb)
210   "Return a list of message numbers contained in FOLDER.
211 If optional VISIBLE-ONLY is non-nil, killed messages are not listed.
212 If second optional IN-MSGDB is non-nil, only messages in the msgdb are listed."
213   (let ((list (if in-msgdb
214                   t
215                 (elmo-folder-list-messages-internal folder visible-only))))
216     (elmo-living-messages
217      (if (listp list)
218          list
219        ;; Use current list.
220        (mapcar
221         'car
222         (elmo-msgdb-get-number-alist (elmo-folder-msgdb folder))))
223      (elmo-folder-killed-list-internal folder))))
224
225 (luna-define-generic elmo-folder-list-unreads (folder)
226   "Return a list of unread message numbers contained in FOLDER.")
227 (luna-define-generic elmo-folder-list-importants (folder)
228   "Return a list of important message numbers contained in FOLDER.")
229 (luna-define-generic elmo-folder-list-answereds (folder)
230   "Return a list of answered message numbers contained in FOLDER.")
231
232 (luna-define-method elmo-folder-list-unreads ((folder elmo-folder))
233   (delq nil
234         (mapcar
235          (lambda (x)
236            (if (member (cadr x) (elmo-msgdb-unread-marks))
237                (car x)))
238          (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))))
239
240 ;; TODO: Should reconsider the structure of global mark.
241 (defun elmo-folder-list-messages-with-global-mark (folder mark)
242   (let (entity msgs)
243     (dolist (mark-pair (or elmo-msgdb-global-mark-alist
244                            (setq elmo-msgdb-global-mark-alist
245                                  (elmo-object-load
246                                   (expand-file-name
247                                    elmo-msgdb-global-mark-filename
248                                    elmo-msgdb-directory)))))
249       (if (and (string= mark (cdr mark-pair))
250                (setq entity
251                      (elmo-msgdb-overview-get-entity (car mark-pair)
252                                                      (elmo-folder-msgdb
253                                                       folder))))
254           (setq msgs (cons (elmo-msgdb-overview-entity-get-number entity)
255                            msgs))))
256     msgs))
257
258 (luna-define-method elmo-folder-list-importants ((folder elmo-folder))
259   (elmo-folder-list-messages-mark-match folder
260                                         (regexp-quote
261                                          elmo-msgdb-important-mark)))
262
263 (luna-define-method elmo-folder-list-answereds ((folder elmo-folder))
264   (delq nil
265         (mapcar
266          (function
267           (lambda (x)
268             (if (member (cadr x) (elmo-msgdb-answered-marks))
269                 (car x))))
270          (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))))
271
272 (luna-define-generic elmo-folder-list-messages-internal (folder &optional
273                                                                 visible-only)
274   ;; Return a list of message numbers contained in FOLDER.
275   ;; Return t if the message list is not available.
276   )
277
278 (luna-define-generic elmo-folder-list-subfolders (folder &optional one-level)
279   "Returns a list of subfolders contained in FOLDER.
280 If optional argument ONE-LEVEL is non-nil, only children of FOLDER is returned.
281 \(a folder which have children is returned as a list\)
282 Otherwise, all descendent folders are returned.")
283
284 (luna-define-generic elmo-folder-have-subfolder-p (folder)
285   "Return non-nil when FOLDER has subfolders.")
286
287 (luna-define-generic elmo-folder-exists-p (folder)
288   "Returns non-nil when FOLDER exists.")
289
290 (luna-define-generic elmo-folder-creatable-p (folder)
291   "Returns non-nil when FOLDER is creatable.")
292
293 (luna-define-generic elmo-folder-writable-p (folder)
294   "Returns non-nil when FOLDER is writable.")
295
296 (luna-define-generic elmo-folder-persistent-p (folder)
297   "Return non-nil when FOLDER is persistent.")
298
299 (luna-define-generic elmo-folder-create (folder)
300   "Create a FOLDER.")
301
302 (luna-define-generic elmo-message-deletable-p (folder number)
303   "Returns non-nil when the message in the FOLDER with NUMBER is deletable.")
304
305 (luna-define-generic elmo-folder-delete (folder)
306   "Delete FOLDER completely.")
307
308 (luna-define-generic elmo-folder-rename (folder new-name)
309   "Rename FOLDER to NEW-NAME (string).")
310
311 (luna-define-generic elmo-folder-delete-messages (folder numbers)
312   "Delete messages.
313 FOLDER is the ELMO folder structure.
314 NUMBERS is a list of message numbers to be deleted.")
315
316 (luna-define-generic elmo-folder-search (folder condition &optional numbers)
317   "Search and return list of message numbers.
318 FOLDER is the ELMO folder structure.
319 CONDITION is a condition structure for searching.
320 If optional argument NUMBERS is specified and is a list of message numbers,
321 messages are searched from the list.")
322
323 (luna-define-generic elmo-message-match-condition (folder number
324                                                           condition
325                                                           numbers)
326   "Return non-nil when the message in the FOLDER with NUMBER is matched.
327 CONDITION is a condition structure for testing.
328 NUMBERS is a list of message numbers,
329 use to be test for \"last\" and \"first\" predicates.")
330
331 (luna-define-generic elmo-folder-msgdb-create (folder numbers id-mark-table)
332   "Create a message database (implemented in each backends).
333 FOLDER is the ELMO folder structure.
334 NUMBERS is a list of message numbers to create msgdb.
335 ID-MARK-TABLE is a hashtable of message-id and its status mark.")
336
337 (luna-define-generic elmo-folder-unmark-important (folder numbers)
338   "Un-mark messages as important.
339 FOLDER is the ELMO folder structure.
340 NUMBERS is a list of message numbers to be processed.")
341
342 (luna-define-generic elmo-folder-mark-as-important (folder numbers)
343   "Mark messages as important.
344 FOLDER is the ELMO folder structure.
345 NUMBERS is a list of message numbers to be processed.")
346
347 (luna-define-generic elmo-folder-unmark-read (folder numbers)
348   "Un-mark messages as read.
349 FOLDER is the ELMO folder structure.
350 NUMBERS is a list of message numbers to be processed.")
351
352 (luna-define-generic elmo-folder-mark-as-read (folder numbers
353                                                       &optional ignore-flags)
354   "Mark messages as read.
355 FOLDER is the ELMO folder structure.
356 NUMBERS is a list of message numbers to be processed.")
357
358 (luna-define-generic elmo-folder-unmark-answered (folder numbers)
359   "Un-mark messages as answered.
360 FOLDER is the ELMO folder structure.
361 NUMBERS is a list of message numbers to be processed.")
362
363 (luna-define-generic elmo-folder-mark-as-answered (folder numbers)
364   "Mark messages as answered.
365 FOLDER is the ELMO folder structure.
366 NUMBERS is a list of message numbers to be processed.")
367
368 (luna-define-generic elmo-folder-append-buffer (folder unread &optional number)
369   "Append current buffer as a new message.
370 FOLDER is the destination folder(ELMO folder structure).
371 If UNREAD is non-nil, message is appended as unread.
372 If optional argument NUMBER is specified, the new message number is set
373 (if possible).")
374
375 (luna-define-generic elmo-folder-append-messages (folder
376                                                   src-folder
377                                                   numbers
378                                                   &optional
379                                                   same-number)
380   "Append messages from folder.
381 FOLDER is the ELMO folder structure.
382 Caller should make sure FOLDER is `writable'.
383 (Can be checked with `elmo-folder-writable-p').
384 SRC-FOLDER is the source ELMO folder structure.
385 NUMBERS is the message numbers to be appended in the SRC-FOLDER.
386 If second optional argument SAME-NUMBER is specified,
387 message number is preserved (if possible).")
388
389 (luna-define-generic elmo-folder-pack-numbers (folder)
390   "Pack message numbers of FOLDER.")
391
392 (luna-define-generic elmo-folder-update-number (folder)
393   "Update number of FOLDER.")
394
395 (luna-define-generic elmo-folder-diff-async (folder)
396   "Get diff of FOLDER asynchronously.")
397
398 (luna-define-generic elmo-folder-expand-msgdb-path (folder)
399   "Expand path for FOLDER.")
400
401 (luna-define-generic elmo-folder-get-primitive-list (folder)
402   "Get primitive folder structure list contained in FOLDER.")
403
404 (luna-define-generic elmo-folder-contains-type (folder type)
405   "Returns t if FOLDER contains TYPE.")
406
407 (luna-define-generic elmo-folder-local-p (folder)
408   "Returns t if FOLDER is local.")
409
410 (luna-define-generic elmo-folder-message-file-p (folder)
411   "Returns t if all messages in the FOLDER are files.")
412
413 ;;; Message methods.
414 (luna-define-generic elmo-message-use-cache-p (folder number)
415   "Returns t if the message in the FOLDER with NUMBER uses cache.")
416
417 (luna-define-generic elmo-message-file-name (folder number)
418   "Return the file name of a message specified by FOLDER and NUMBER.")
419
420 ;;; For archive
421
422 ;;; Use original file
423 (luna-define-generic elmo-folder-message-file-number-p (folder)
424   "Return t if the file name in the FOLDER is the message number.")
425
426 (luna-define-generic elmo-folder-message-file-directory (folder)
427   "Return the directory of the message files of FOLDER.")
428
429 ;;; Use temporary file
430 (luna-define-generic elmo-folder-message-make-temp-file-p (folder)
431   "Return t if the messages in the FOLDER makes local temporary file.")
432
433 (luna-define-generic elmo-folder-message-make-temp-files (folder
434                                                           numbers
435                                                           &optional
436                                                           start-number)
437   "Make a new temporary files from the messages in the FOLDER with NUMBERS.
438 If START-NUMBER is specified, temporary files begin from the number.
439 Otherwise, same number is used for temporary files.
440 Return newly created temporary directory name which contains temporary files.")
441
442 (luna-define-generic elmo-message-file-p (folder number)
443   "Return t if message in the FOLDER with NUMBER is a file.")
444
445 (luna-define-generic elmo-find-fetch-strategy
446   (folder entity &optional ignore-cache)
447 ;; Returns the message fetching strategy suitable for the message.
448 ;; FOLDER is the ELMO folder structure.
449 ;; ENTITY is the overview entity of the message in the folder.
450 ;; If optional argument IGNORE-CACHE is non-nil, cache is ignored.
451 ;; Returned value is a elmo-fetch-strategy object.
452 ;; If return value is nil, message should not be nil.
453   )
454
455 (defmacro elmo-make-fetch-strategy (entireness
456                                     &optional
457                                     use-cache
458                                     save-cache
459                                     cache-path)
460 ;; Make elmo-message-fetching strategy.
461 ;; ENTIRENESS is 'entire or 'section.
462 ;; 'entire means fetch message entirely at once.
463 ;; 'section means fetch message section by section.
464 ;; If optional USE-CACHE is non-nil, existing cache is used and otherwise,
465 ;; existing cache is thrown away.
466 ;; If SAVE-CACHE is non-nil, fetched message is saved.
467 ;; CACHE-PATH is the cache path to be used as a message cache file.
468   (` (vector (, entireness)
469              (, use-cache) (, save-cache) (, cache-path))))
470
471 (defmacro elmo-fetch-strategy-entireness (strategy)
472   ;; Return entireness of STRATEGY.
473   (` (aref (, strategy) 0)))
474
475 (defmacro elmo-fetch-strategy-use-cache (strategy)
476   ;; Return use-cache of STRATEGY.
477   (` (aref (, strategy) 1)))
478
479 (defmacro elmo-fetch-strategy-save-cache (strategy)
480   ;; Return save-cache of STRATEGY.
481   (` (aref (, strategy) 2)))
482
483 (defmacro elmo-fetch-strategy-cache-path (strategy)
484   ;;  Return cache-path of STRATEGY.
485   (` (aref (, strategy) 3)))
486
487 (luna-define-method elmo-find-fetch-strategy
488   ((folder elmo-folder) entity &optional ignore-cache)
489   (let (cache-file size message-id number)
490     (setq size (elmo-msgdb-overview-entity-get-size entity))
491     (setq message-id (elmo-msgdb-overview-entity-get-id entity))
492     (setq number (elmo-msgdb-overview-entity-get-number entity))
493     (setq cache-file (elmo-file-cache-get message-id))
494     (setq ignore-cache (or ignore-cache
495                            (null (elmo-message-use-cache-p folder number))))
496     (if (or ignore-cache
497             (null (elmo-file-cache-status cache-file)))
498         ;; No cache or ignore-cache.
499         (if (and (not (elmo-folder-local-p folder))
500                  elmo-message-fetch-threshold
501                  (integerp size)
502                  (>= size elmo-message-fetch-threshold)
503                  (or (not elmo-message-fetch-confirm)
504                      (not (prog1 (y-or-n-p
505                                   (format "Fetch entire message(%dbytes)? "
506                                           size))
507                             (message "")))))
508             ;; Don't fetch message at all.
509             nil
510           ;; Don't use existing cache and fetch entire message at once.
511           (elmo-make-fetch-strategy
512            'entire nil
513            (elmo-message-use-cache-p folder number)
514            (elmo-file-cache-path cache-file)))
515       ;; Cache exists.
516       (if (not ignore-cache)
517           (elmo-make-fetch-strategy
518            'entire
519            ;; ...But ignore current section cache and re-fetch
520            ;; if section cache.
521            (not (eq (elmo-file-cache-status cache-file) 'section))
522            ;; Save cache.
523            (elmo-message-use-cache-p folder number)
524            (elmo-file-cache-path cache-file))))))
525
526 (luna-define-method elmo-folder-list-messages-internal
527   ((folder elmo-folder) &optional visible-only)
528   t)
529
530 (defun elmo-folder-encache (folder numbers &optional unread)
531   "Encache messages in the FOLDER with NUMBERS.
532 If UNREAD is non-nil, messages are not marked as read."
533   (dolist (number numbers)
534     (elmo-message-encache folder number unread)))
535
536 (luna-define-generic elmo-message-encache (folder number &optional read)
537   "Encache message in the FOLDER with NUMBER.
538 If READ is non-nil, message is marked as read.")
539
540 (luna-define-method elmo-message-encache ((folder elmo-folder) number
541                                           &optional read)
542   (elmo-message-fetch
543    folder number
544    (elmo-make-fetch-strategy 'entire
545                              nil ;use-cache
546                              t   ;save-cache
547                              (elmo-file-cache-get-path
548                               (elmo-message-field
549                                folder number 'message-id)))
550    nil nil (not read)))
551
552 (luna-define-generic elmo-message-fetch (folder number strategy
553                                                 &optional
554                                                 section
555                                                 outbuf
556                                                 unread)
557   "Fetch a message and return as a string.
558 FOLDER is the ELMO folder structure.
559 NUMBER is the number of the message in the FOLDER.
560 STRATEGY is the message fetching strategy.
561 If optional argument SECTION is specified, only the SECTION of the message
562 is fetched (if possible).
563 If second optional argument OUTBUF is specified, fetched message is
564 inserted to the buffer and returns t if fetch was ended successfully.
565 If third optional argument UNREAD is non-nil, message is not marked as read.
566 Returns non-nil if fetching was succeed.")
567
568 (luna-define-generic elmo-message-fetch-with-cache-process (folder
569                                                             number strategy
570                                                             &optional
571                                                             section
572                                                             unread)
573   "Fetch a message into current buffer with cache process.
574 FOLDER is the ELMO folder structure.
575 NUMBER is the number of the message in the FOLDER.
576 STRATEGY is the message fetching strategy.
577 If optional argument SECTION is specified, only the SECTION of the message
578 is fetched (if possible).
579 If second optional argument UNREAD is non-nil, message is not marked as read.
580 Returns non-nil if fetching was succeed.")
581
582 (luna-define-generic elmo-message-fetch-internal (folder number strategy
583                                                          &optional
584                                                          section
585                                                          unread)
586   "Fetch a message into current buffer.
587 FOLDER is the ELMO folder structure.
588 NUMBER is the number of the message in the FOLDER.
589 STRATEGY is the message fetching strategy.
590 If optional argument SECTION is specified, only the SECTION of the message
591 is fetched (if possible).
592 If second optional argument UNREAD is non-nil, message is not marked as read.
593 Returns non-nil if fetching was succeed.")
594
595 (luna-define-generic elmo-message-fetch-field (folder number field)
596   "Fetch a message field value.
597 FOLDER is the ELMO folder structure.
598 NUMBER is the number of the message in the FOLDER.
599 FIELD is a symbol of the field name.")
600
601 (luna-define-generic elmo-message-folder (folder number)
602   "Get primitive folder of the message.")
603
604 (luna-define-generic elmo-folder-process-crosspost (folder
605                                                     &optional
606                                                     number-alist)
607   "Process crosspost for FOLDER.
608 If NUMBER-ALIST is set, it is used as number-alist.
609 Return a cons cell of (NUMBER-CROSSPOSTS . NEW-MARK-ALIST).")
610
611 (luna-define-generic elmo-folder-append-msgdb (folder append-msgdb)
612   "Append  APPEND-MSGDB to the current msgdb of the folder.")
613
614 (luna-define-generic elmo-folder-newsgroups (folder)
615   "Return list of newsgroup name of FOLDER.")
616
617 (luna-define-generic elmo-folder-search-requires-msgdb-p (folder condition)
618   "Return non-nil if searching in FOLDER by CONDITION requires msgdb fetch.")
619
620 (defun elmo-folder-search-requires-msgdb-p-internal (folder condition)
621   (if (listp condition)
622       (or (elmo-folder-search-requires-msgdb-p-internal
623            folder (nth 1 condition))
624           (elmo-folder-search-requires-msgdb-p-internal
625            folder (nth 2 condition)))
626     (and (not (string= (elmo-filter-key condition) "last"))
627          (not (string= (elmo-filter-key condition) "first")))))
628
629 (luna-define-method elmo-folder-search-requires-msgdb-p ((folder elmo-folder)
630                                                          condition)
631   (elmo-folder-search-requires-msgdb-p-internal folder condition))
632
633 (luna-define-method elmo-folder-newsgroups ((folder elmo-folder))
634   nil)
635
636 (luna-define-method elmo-folder-open ((folder elmo-folder)
637                                       &optional load-msgdb)
638   (elmo-generic-folder-open folder load-msgdb))
639
640 (defun elmo-generic-folder-open (folder load-msgdb)
641   (let ((inhibit-quit t))
642     (if load-msgdb (elmo-folder-msgdb folder))
643     (elmo-folder-set-killed-list-internal
644      folder
645      (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))))
646   (elmo-folder-open-internal folder))
647
648 (luna-define-method elmo-folder-open-internal ((folder elmo-folder))
649   nil ; default is do nothing.
650   )
651
652 (luna-define-method elmo-folder-check ((folder elmo-folder))
653   nil) ; default is noop.
654
655 (luna-define-method elmo-folder-commit ((folder elmo-folder))
656   (elmo-generic-folder-commit folder))
657
658 (defun elmo-generic-folder-commit (folder)
659   (when (elmo-folder-persistent-p folder)
660     (when (elmo-folder-message-modified-internal folder)
661       (elmo-msgdb-overview-save
662        (elmo-folder-msgdb-path folder)
663        (elmo-msgdb-get-overview (elmo-folder-msgdb folder)))
664       (elmo-msgdb-number-save
665        (elmo-folder-msgdb-path folder)
666        (elmo-msgdb-get-number-alist (elmo-folder-msgdb folder)))
667       (elmo-folder-set-info-max-by-numdb
668        folder
669        (elmo-msgdb-get-number-alist
670         (elmo-folder-msgdb folder)))
671       (elmo-folder-set-message-modified-internal folder nil)
672       (elmo-msgdb-killed-list-save
673        (elmo-folder-msgdb-path folder)
674        (elmo-folder-killed-list-internal folder)))
675     (when (elmo-folder-mark-modified-internal folder)
676       (elmo-msgdb-mark-save
677        (elmo-folder-msgdb-path folder)
678        (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))
679       (elmo-folder-set-mark-modified-internal folder nil))))
680
681 (luna-define-method elmo-folder-close-internal ((folder elmo-folder))
682   ;; do nothing.
683   )
684
685 (luna-define-method elmo-folder-close ((folder elmo-folder))
686   (elmo-generic-folder-close folder)
687   (elmo-folder-close-internal folder))
688
689 (defun elmo-generic-folder-close (folder)
690   (elmo-folder-commit folder)
691   (elmo-folder-set-msgdb-internal folder nil)
692   (elmo-folder-set-killed-list-internal folder nil))
693
694 (luna-define-method elmo-folder-plugged-p ((folder elmo-folder))
695   t) ; default is plugged.
696
697 (luna-define-method elmo-folder-set-plugged ((folder elmo-folder) plugged
698                                              &optional add)
699   nil) ; default is do nothing.
700
701 (luna-define-method elmo-folder-use-flag-p ((folder elmo-folder))
702   nil) ; default is no flag.
703
704 (luna-define-method elmo-folder-persistent-p ((folder elmo-folder))
705   (elmo-folder-persistent-internal folder))
706
707 (luna-define-method elmo-folder-creatable-p ((folder elmo-folder))
708   nil) ; default is not creatable.
709
710 (luna-define-method elmo-folder-writable-p ((folder elmo-folder))
711   nil) ; default is not writable.
712
713 (luna-define-method elmo-folder-delete ((folder elmo-folder))
714   (elmo-msgdb-delete-path folder))
715
716 (luna-define-method elmo-folder-rename ((folder elmo-folder) new-name)
717   (let* ((new-folder (elmo-make-folder new-name)))
718     (unless (eq (elmo-folder-type-internal folder)
719                 (elmo-folder-type-internal new-folder))
720       (error "Not same folder type"))
721     (if (or (file-exists-p (elmo-folder-msgdb-path new-folder))
722             (elmo-folder-exists-p new-folder))
723         (error "Already exists folder: %s" new-name))
724     (elmo-folder-send folder 'elmo-folder-rename-internal new-folder)
725     (elmo-msgdb-rename-path folder new-folder)))
726
727 (defsubst elmo-folder-search-fast (folder condition numbers)
728   (when (and numbers
729              (vectorp condition)
730              (member (elmo-filter-key condition) '("first" "last" "mark")))
731     (if (string= (elmo-filter-key condition) "mark")
732         (let ((msgdb (elmo-folder-msgdb folder)))
733           ;; msgdb should be synchronized at this point.
734           (cond
735            ((string= (elmo-filter-value condition) "unread")
736             (elmo-folder-list-unreads folder))
737            ((string= (elmo-filter-value condition) "important")
738             (elmo-folder-list-importants folder))
739            ((string= (elmo-filter-value condition) "answered")
740             (elmo-folder-list-answereds folder))
741            ((string= (elmo-filter-value condition) "any")
742             (nconc (elmo-folder-list-unreads folder)
743                    (elmo-folder-list-importants folder)
744                    (elmo-folder-list-answereds folder)))))
745       (let ((len (length numbers))
746             (lastp (string= (elmo-filter-key condition) "last"))
747             (value (string-to-number (elmo-filter-value condition))))
748         (when (eq (elmo-filter-type condition) 'unmatch)
749           (setq lastp (not lastp)
750                 value  (- len value)))
751         (if lastp
752             (nthcdr (max (- len value) 0) numbers)
753           (when (> value 0)
754             (let ((last (nthcdr (1- value) numbers)))
755               (when last
756                 (setcdr last nil))
757               numbers)))))))
758
759 (luna-define-method elmo-folder-search ((folder elmo-folder)
760                                         condition
761                                         &optional numbers)
762   (let ((numbers (or numbers (elmo-folder-list-messages folder))))
763     (or (elmo-folder-search-fast folder condition numbers)
764         (let ((msgdb (elmo-folder-msgdb folder))
765               (len (length numbers))
766               matched)
767           (when (> len elmo-display-progress-threshold)
768             (elmo-progress-set 'elmo-folder-search len "Searching..."))
769           (unwind-protect
770               (dolist (number numbers)
771                 (let (result)
772                   (setq result (elmo-msgdb-match-condition
773                                 msgdb
774                                 condition
775                                 number
776                                 numbers))
777                   (when (elmo-filter-condition-p result)
778                     (setq result (elmo-message-match-condition
779                                   folder
780                                   number
781                                   condition
782                                   numbers)))
783                   (when result
784                     (setq matched (cons number matched))))
785                 (elmo-progress-notify 'elmo-folder-search))
786             (elmo-progress-clear 'elmo-folder-search))
787           (nreverse matched)))))
788
789 (luna-define-method elmo-message-match-condition ((folder elmo-folder)
790                                                   number condition
791                                                   numbers)
792   (let ((filename (cond
793                    ((elmo-message-file-name folder number))
794                    ((let* ((cache (elmo-file-cache-get
795                                    (elmo-message-field folder number
796                                                        'message-id)))
797                            (cache-path (elmo-file-cache-path cache)))
798                       (when (and cache-path
799                                  (not (elmo-cache-path-section-p cache-path)))
800                         cache-path))))))
801     (when (and filename
802                (file-readable-p filename))
803       (with-temp-buffer
804         (insert-file-contents-as-binary filename)
805         (elmo-set-buffer-multibyte default-enable-multibyte-characters)
806         ;; Should consider charset?
807         (decode-mime-charset-region (point-min) (point-max) elmo-mime-charset)
808         (elmo-buffer-field-condition-match condition number numbers)))))
809
810 (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder))
811   nil) ; default is noop.
812
813 (luna-define-method elmo-folder-update-number ((folder elmo-folder))
814   nil) ; default is noop.
815
816 (luna-define-method elmo-folder-message-file-p ((folder elmo-folder))
817   nil) ; default is not file.
818
819 (luna-define-method elmo-folder-message-file-number-p ((folder elmo-folder))
820   nil) ; default is not number.
821
822 (luna-define-method elmo-folder-message-make-temp-file-p ((folder elmo-folder))
823   nil) ; default is not make temp file.
824
825 (luna-define-method elmo-message-file-name ((folder elmo-folder)
826                                                    number)
827   nil) ; default is no name.
828
829 (luna-define-method elmo-folder-local-p ((folder elmo-folder))
830   t)   ; default is local.
831
832 (luna-define-method elmo-folder-have-subfolder-p ((folder elmo-folder))
833   t)
834
835 ;;; Folder info
836 ;; Folder info is a message number information cache (hashtable)
837 (defsubst elmo-folder-get-info (folder &optional hashtb)
838   "Return FOLDER info from HASHTB (default is `elmo-folder-info-hashtb')."
839   (elmo-get-hash-val (elmo-folder-name-internal folder)
840                      (or hashtb elmo-folder-info-hashtb)))
841
842 (defun elmo-folder-set-info-hashtb (folder max numbers &optional new unread)
843   "Set FOLDER info (means MAX, NUMBERS, NEW and UNREAD)."
844   (let ((info (elmo-folder-get-info folder)))
845     (when info
846       (or new     (setq new     (nth 0 info)))
847       (or unread  (setq unread  (nth 1 info)))
848       (or numbers (setq numbers (nth 2 info)))
849       (or max     (setq max     (nth 3 info))))
850     (elmo-set-hash-val (elmo-folder-name-internal folder)
851                        (list new unread numbers max)
852                        elmo-folder-info-hashtb)))
853
854 (defun elmo-folder-set-info-max-by-numdb (folder msgdb-number)
855   "Set FOLDER info by MSGDB-NUMBER in msgdb."
856   (let ((num-db (sort (mapcar 'car msgdb-number) '<)))
857     (elmo-folder-set-info-hashtb
858      folder
859      (or (nth (max 0 (1- (length num-db))) num-db) 0)
860      nil ;;(length num-db)
861      )))
862
863 (defun elmo-folder-get-info-max (folder)
864   "Return max number of FODLER from folder info."
865   (nth 3 (elmo-folder-get-info folder)))
866
867 (defun elmo-folder-get-info-length (folder)
868   "Return length of FODLER from folder info."
869   (nth 2 (elmo-folder-get-info folder)))
870
871 (defun elmo-folder-get-info-unread (folder)
872   "Return unread of FODLER from folder info."
873   (nth 1 (elmo-folder-get-info folder)))
874
875 (defun elmo-folder-info-make-hashtb (info-alist hashtb)
876   "Setup folder info hashtable by INFO-ALIST on HASHTB."
877   (let* ((hashtb (or hashtb
878                      (elmo-make-hash (length info-alist)))))
879     (mapcar
880      (lambda (x)
881        (let ((info (cadr x)))
882          (and (intern-soft (car x) hashtb)
883               (elmo-set-hash-val (car x)
884                                  (list (nth 2 info)   ;; new
885                                        (nth 3 info)   ;; unread
886                                        (nth 1 info)   ;; length
887                                        (nth 0 info))  ;; max
888                                  hashtb))))
889      info-alist)
890     (setq elmo-folder-info-hashtb hashtb)))
891
892 (defsubst elmo-diff-new (diff)
893   (car diff))
894
895 (defsubst elmo-diff-unread (diff)
896   (when (consp (cdr diff))
897     (nth 1 diff)))
898
899 (defsubst elmo-diff-all (diff)
900   (if (consp (cdr diff))
901       (nth 2 diff)
902     (cdr diff)))
903
904 (defsubst elmo-strict-folder-diff (folder)
905   "Return folder diff information strictly from FOLDER."
906   (let* ((dir (elmo-folder-msgdb-path folder))
907          (nalist (elmo-msgdb-get-number-alist (elmo-folder-msgdb folder)))
908          (in-db (sort (mapcar 'car nalist) '<))
909          (in-folder  (elmo-folder-list-messages folder))
910          append-list delete-list diff)
911     (cons (if (equal in-folder in-db)
912               0
913             (setq diff (elmo-list-diff
914                         in-folder in-db
915                         nil
916                         ))
917             (setq append-list (car diff))
918             (setq delete-list (cadr diff))
919             (if append-list
920                 (length append-list)
921               (if delete-list
922                   (- 0 (length delete-list))
923                 0)))
924           (length in-folder))))
925
926 (luna-define-method elmo-folder-diff ((folder elmo-folder)
927                                       &optional numbers)
928   (elmo-generic-folder-diff folder numbers))
929
930 (defun elmo-generic-folder-diff (folder numbers)
931   (if (elmo-string-match-member (elmo-folder-name-internal folder)
932                                 elmo-strict-diff-folder-list)
933       (elmo-strict-folder-diff folder)
934     (let ((cached-in-db-max (elmo-folder-get-info-max folder))
935           (in-folder (elmo-folder-status folder))
936           (in-db t)
937           unsync messages
938           in-db-max)
939       (if numbers
940           (setq in-db-max (or (nth (max 0 (1- (length numbers))) numbers)
941                               0))
942         (if (not cached-in-db-max)
943             (let ((number-list (mapcar 'car
944                                        (elmo-msgdb-number-load
945                                         (elmo-folder-msgdb-path folder)))))
946               ;; No info-cache.
947               (setq in-db (sort number-list '<))
948               (setq in-db-max (or (nth (max 0 (1- (length in-db))) in-db)
949                                   0))
950               (elmo-folder-set-info-hashtb folder in-db-max nil))
951           (setq in-db-max cached-in-db-max)))
952       (setq unsync (if (and in-db
953                             (car in-folder))
954                        (- (car in-folder) in-db-max)
955                      (if (and in-folder
956                               (null in-db))
957                          (cdr in-folder)
958                        (if (null (car in-folder))
959                            nil))))
960       (setq messages (cdr in-folder))
961       (if (and unsync messages (> unsync messages))
962           (setq unsync messages))
963       (cons (or unsync 0) (or messages 0)))))
964
965 (defvar elmo-folder-diff-async-callback nil)
966 (defvar elmo-folder-diff-async-callback-data nil)
967
968 (luna-define-method elmo-folder-diff-async ((folder elmo-folder))
969   (and elmo-folder-diff-async-callback
970        (funcall elmo-folder-diff-async-callback
971                 folder
972                 (elmo-folder-diff folder))))
973
974 (luna-define-method elmo-folder-get-primitive-list ((folder elmo-folder))
975   (list folder))
976
977 (luna-define-method elmo-folder-contains-type ((folder elmo-folder) type)
978   (eq (elmo-folder-type-internal folder) type))
979
980 (luna-define-method elmo-folder-append-messages ((folder elmo-folder)
981                                                  src-folder
982                                                  numbers
983                                                  &optional
984                                                  same-number)
985   (elmo-generic-folder-append-messages folder src-folder numbers
986                                        same-number))
987
988 (defun elmo-generic-folder-append-messages (folder src-folder numbers
989                                                    same-number)
990   (let (unseen seen-list succeed-numbers failure cache)
991     (with-temp-buffer
992       (set-buffer-multibyte nil)
993       (while numbers
994         (setq failure nil)
995         (condition-case nil
996             (setq cache (elmo-file-cache-get
997                          (elmo-message-field src-folder
998                                              (car numbers)
999                                              'message-id))
1000                   failure
1001                   (not
1002                    (and
1003                     (elmo-message-fetch
1004                      src-folder (car numbers)
1005                      (if (elmo-folder-plugged-p src-folder)
1006                          (elmo-make-fetch-strategy
1007                           'entire 'maybe nil
1008                           (and cache (elmo-file-cache-path cache)))
1009                        (or (and elmo-enable-disconnected-operation
1010                                 cache
1011                                 (eq (elmo-file-cache-status cache) 'entire)
1012                                 (elmo-make-fetch-strategy
1013                                  'entire t nil
1014                                  (elmo-file-cache-path cache)))
1015                            (error "Unplugged")))
1016                      nil (current-buffer)
1017                      'unread)
1018                     (> (buffer-size) 0)
1019                     (elmo-folder-append-buffer
1020                      folder
1021                      (setq unseen (member (elmo-message-mark
1022                                            src-folder (car numbers))
1023                                           (elmo-msgdb-unread-marks)))
1024                      (if same-number (car numbers))))))
1025           (error (setq failure t)))
1026         ;; FETCH & APPEND finished
1027         (unless failure
1028           (unless unseen
1029             (setq seen-list (cons (elmo-message-field
1030                                    src-folder (car numbers)
1031                                    'message-id)
1032                                   seen-list)))
1033           (setq succeed-numbers (cons (car numbers) succeed-numbers)))
1034         (elmo-progress-notify 'elmo-folder-move-messages)
1035         (setq numbers (cdr numbers)))
1036       (if (and seen-list (elmo-folder-persistent-p folder))
1037           (elmo-msgdb-seen-save (elmo-folder-msgdb-path folder)
1038                                 (nconc (elmo-msgdb-seen-load
1039                                         (elmo-folder-msgdb-path folder))
1040                                        seen-list)))
1041       succeed-numbers)))
1042
1043 ;; Arguments should be reduced.
1044 (defun elmo-folder-move-messages (src-folder msgs dst-folder
1045                                              &optional msgdb
1046                                              no-delete-info
1047                                              no-delete
1048                                              same-number
1049                                              save-unread)
1050   (save-excursion
1051     (let* ((messages msgs)
1052            (elmo-inhibit-display-retrieval-progress t)
1053            (len (length msgs))
1054            succeeds i result)
1055       (if (eq dst-folder 'null)
1056           (setq succeeds messages)
1057         (unless (elmo-folder-writable-p dst-folder)
1058           (error "move: %d is not writable"
1059                  (elmo-folder-name-internal dst-folder)))
1060         (when messages
1061           ;; src is already opened.
1062           (elmo-folder-open-internal dst-folder)
1063           (unless (setq succeeds (elmo-folder-append-messages dst-folder
1064                                                               src-folder
1065                                                               messages
1066                                                               same-number))
1067             (error "move: append message to %s failed"
1068                    (elmo-folder-name-internal dst-folder)))
1069           (elmo-folder-close dst-folder))
1070         (when (and (elmo-folder-persistent-p dst-folder)
1071                    save-unread)
1072           ;; Save to seen list.
1073           (let* ((dir (elmo-folder-msgdb-path dst-folder))
1074                  (seen-list (elmo-msgdb-seen-load dir)))
1075             (setq seen-list
1076                   (elmo-msgdb-add-msgs-to-seen-list
1077                    msgs (elmo-folder-msgdb src-folder)
1078                    seen-list))
1079             (elmo-msgdb-seen-save dir seen-list))))
1080       (if (and (not no-delete) succeeds)
1081           (progn
1082             (if (not no-delete-info)
1083                 (message "Cleaning up src folder..."))
1084             (if (and (elmo-folder-delete-messages src-folder succeeds)
1085                      (elmo-msgdb-delete-msgs
1086                       (elmo-folder-msgdb src-folder) succeeds))
1087                 (setq result t)
1088               (message "move: delete messages from %s failed."
1089                        (elmo-folder-name-internal src-folder))
1090               (setq result nil))
1091             (if (and result
1092                      (not no-delete-info))
1093                 (message "Cleaning up src folder...done"))
1094             result)
1095         (if no-delete
1096             (progn
1097               (message "Copying messages...done")
1098               t)
1099           (if (eq len 0)
1100               (message "No message was moved.")
1101             (message "Moving messages failed.")
1102             nil ; failure
1103             ))))))
1104
1105 (defun elmo-folder-msgdb-path (folder)
1106   "Return the msgdb path for FOLDER."
1107   (or (elmo-folder-path-internal folder)
1108       (elmo-folder-set-path-internal
1109        folder
1110        (elmo-folder-expand-msgdb-path folder))))
1111
1112 (defun elmo-message-set-cached (folder number cached)
1113   "Set cache status of the message mark.
1114 FOLDER is the ELMO folder structure.
1115 NUMBER is a number of the message.
1116 If CACHED is t, message mark is set as cached."
1117   (elmo-msgdb-set-cached 
1118    (elmo-folder-msgdb folder) number cached))
1119
1120 (defun elmo-message-mark (folder number)
1121   "Get mark of the message.
1122 FOLDER is the ELMO folder structure.
1123 NUMBER is a number of the message."
1124   (elmo-msgdb-get-mark (elmo-folder-msgdb folder) number))
1125
1126 (defun elmo-folder-list-messages-mark-match (folder mark-regexp)
1127   "List messages in the FOLDER which have a mark that matches MARK-REGEXP"
1128   (let ((case-fold-search nil)
1129         matched)
1130     (if mark-regexp
1131         (dolist (elem (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))
1132           (if (string-match mark-regexp (cadr elem))
1133               (setq matched (cons (car elem) matched)))))
1134     matched))
1135
1136 (defun elmo-message-field (folder number field)
1137   "Get message field value in the msgdb.
1138 FOLDER is the ELMO folder structure.
1139 NUMBER is a number of the message.
1140 FIELD is a symbol of the field."
1141   (elmo-msgdb-get-field (elmo-folder-msgdb folder) number field))
1142
1143 (defun elmo-message-set-mark (folder number mark)
1144   ;; Set mark for the message in the FOLDER with NUMBER as MARK.
1145   (elmo-msgdb-set-mark
1146    (elmo-folder-msgdb folder)
1147    number mark))
1148
1149 (luna-define-method elmo-message-use-cache-p ((folder elmo-folder) number)
1150   nil) ; default is not use cache.
1151
1152 (luna-define-method elmo-message-folder ((folder elmo-folder) number)
1153   folder) ; default is folder
1154
1155 (luna-define-method elmo-folder-unmark-important ((folder elmo-folder)
1156                                                   numbers)
1157   (when (elmo-folder-msgdb-internal folder)
1158     (dolist (number numbers)
1159       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1160                                folder
1161                                number
1162                                'important))))
1163
1164 (luna-define-method elmo-folder-mark-as-important ((folder elmo-folder)
1165                                                    numbers)
1166   (when (elmo-folder-msgdb-internal folder)
1167     (dolist (number numbers)
1168       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1169                              folder
1170                              number
1171                              'important))))
1172
1173 (luna-define-method elmo-folder-unmark-read ((folder elmo-folder) numbers)
1174   (when (elmo-folder-msgdb-internal folder)
1175     (dolist (number numbers)
1176       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1177                                folder
1178                                number
1179                                'read))))
1180
1181 (luna-define-method elmo-folder-mark-as-read ((folder elmo-folder)
1182                                               numbers
1183                                               &optional ignore-flag)
1184   (when (elmo-folder-msgdb-internal folder)
1185     (dolist (number numbers)
1186       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1187                              folder
1188                              number
1189                              'read))))
1190
1191 (luna-define-method elmo-folder-unmark-answered ((folder elmo-folder) numbers)
1192   (when (elmo-folder-msgdb-internal folder)
1193     (dolist (number numbers)
1194       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1195                                folder
1196                                number
1197                                'answered))))
1198
1199 (luna-define-method elmo-folder-mark-as-answered ((folder elmo-folder) numbers)
1200   (when (elmo-folder-msgdb-internal folder)
1201     (dolist (number numbers)
1202       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1203                              folder
1204                              number
1205                              'answered))))
1206
1207 (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder)
1208                                                    &optional
1209                                                    number-alist)
1210   ;; Do nothing.
1211   )
1212
1213 (defun elmo-generic-folder-append-msgdb (folder append-msgdb)
1214   (if append-msgdb
1215       (let* ((number-alist (elmo-msgdb-get-number-alist append-msgdb))
1216              (all-alist (copy-sequence (append
1217                                         (elmo-msgdb-get-number-alist
1218                                          (elmo-folder-msgdb folder))
1219                                         number-alist)))
1220              (cur number-alist)
1221              pair overview
1222              to-be-deleted
1223              mark-alist)
1224         (elmo-folder-set-msgdb-internal folder
1225                                         (elmo-msgdb-append
1226                                          (elmo-folder-msgdb folder)
1227                                          append-msgdb))
1228         (while cur
1229           (setq all-alist (delq (car cur) all-alist))
1230           ;; same message id exists.
1231           (if (setq pair (rassoc (cdr (car cur)) all-alist))
1232               (setq to-be-deleted (nconc to-be-deleted (list (car pair)))))
1233           (setq cur (cdr cur)))
1234         (cond ((eq (elmo-folder-process-duplicates-internal folder)
1235                    'hide)
1236                ;; Hide duplicates.
1237                (setq overview (elmo-delete-if
1238                                (lambda (x)
1239                                  (memq (elmo-msgdb-overview-entity-get-number
1240                                         x)
1241                                        to-be-deleted))
1242                                (elmo-msgdb-get-overview append-msgdb)))
1243                ;; Should be mark as read.
1244                (elmo-folder-mark-as-read folder to-be-deleted)
1245                (elmo-msgdb-set-overview append-msgdb overview))
1246               ((eq (elmo-folder-process-duplicates-internal folder)
1247                    'read)
1248                ;; Mark as read duplicates.
1249                (elmo-folder-mark-as-read folder to-be-deleted))
1250               (t
1251                ;; Do nothing.
1252                (setq to-be-deleted nil)))
1253         (length to-be-deleted))
1254     0))
1255
1256 (luna-define-method elmo-folder-append-msgdb ((folder elmo-folder)
1257                                               append-msgdb)
1258   (elmo-generic-folder-append-msgdb folder append-msgdb))
1259
1260 (defun elmo-folder-confirm-appends (appends)
1261   (let ((len (length appends))
1262         in)
1263     (if (and elmo-folder-update-threshold
1264              (> len elmo-folder-update-threshold)
1265              elmo-folder-update-confirm)
1266         (if (y-or-n-p (format "Too many messages(%d).  Update all? " len))
1267             appends
1268           (setq in elmo-folder-update-threshold)
1269           (catch 'end
1270             (while t
1271               (setq in (read-from-minibuffer "Update number: "
1272                                              (int-to-string in))
1273                     in (string-to-int in))
1274               (if (< len in)
1275                   (throw 'end len))
1276               (if (y-or-n-p (format "%d messages are not appeared.  OK? "
1277                                     (max (- len in) 0)))
1278                   (throw 'end in))))
1279           (nthcdr (max (- len in) 0) appends))
1280       (if (and elmo-folder-update-threshold
1281                (> len elmo-folder-update-threshold)
1282                (not elmo-folder-update-confirm))
1283           (nthcdr (max (- len elmo-folder-update-threshold) 0) appends)
1284         appends))))
1285
1286 (luna-define-method elmo-message-fetch ((folder elmo-folder)
1287                                         number strategy
1288                                         &optional
1289                                         section
1290                                         outbuf
1291                                         unread)
1292   (if outbuf
1293       (with-current-buffer outbuf
1294         (erase-buffer)
1295         (elmo-message-fetch-with-cache-process folder number
1296                                                strategy section unread))
1297     (with-temp-buffer
1298       (elmo-message-fetch-with-cache-process folder number
1299                                              strategy section unread)
1300       (buffer-string))))
1301
1302 (luna-define-method elmo-message-fetch-with-cache-process ((folder elmo-folder)
1303                                                            number strategy
1304                                                            &optional
1305                                                            section unread)
1306   (let ((cache-path (elmo-fetch-strategy-cache-path strategy))
1307         (method-priorities
1308          (cond ((eq (elmo-fetch-strategy-use-cache strategy) 'maybe)
1309                 '(entity cache))
1310                ((elmo-fetch-strategy-use-cache strategy)
1311                 '(cache entity))
1312                (t
1313                 '(entity))))
1314         result err)
1315     (while (and method-priorities
1316                 (null result))
1317       (setq result
1318             (case (car method-priorities)
1319               (cache
1320                (elmo-file-cache-load cache-path section))
1321               (entity
1322                (when (and (condition-case error
1323                               (elmo-message-fetch-internal folder number
1324                                                            strategy
1325                                                            section
1326                                                            unread)
1327                             (error (setq err error) nil))
1328                           (> (buffer-size) 0))
1329                  (elmo-delete-cr-buffer)
1330                  (when (and (elmo-fetch-strategy-save-cache strategy)
1331                             cache-path)
1332                    (elmo-file-cache-save cache-path section))
1333                  t)))
1334             method-priorities (cdr method-priorities)))
1335     (or result
1336         (and err (signal (car err) (cdr err))))))
1337
1338 (luna-define-method elmo-folder-clear ((folder elmo-folder)
1339                                        &optional keep-killed)
1340   (unless keep-killed
1341     (elmo-folder-set-killed-list-internal folder nil))
1342   (elmo-folder-set-msgdb-internal folder (elmo-msgdb-clear)))
1343
1344 (defun elmo-folder-synchronize (folder
1345                                 &optional ignore-msgdb
1346                                 no-check)
1347   "Synchronize the folder data to the newest status.
1348 FOLDER is the ELMO folder structure.
1349 If optional IGNORE-MSGDB is non-nil, current msgdb is thrown away except
1350 read mark status. If IGNORE-MSGDB is 'visible-only, only visible messages
1351 \(the messages which are not in the killed-list\) are thrown away and
1352 synchronized.
1353 If NO-CHECK is non-nil, rechecking folder is skipped.
1354
1355 Return a list of a cross-posted message number.
1356 If update process is interrupted, return nil."
1357   (let ((killed-list (elmo-folder-killed-list-internal folder))
1358         (before-append t)
1359         number-alist mark-alist
1360         old-msgdb diff diff-2 delete-list new-list new-msgdb mark
1361         seen-list crossed after-append)
1362     (setq old-msgdb (elmo-folder-msgdb folder))
1363     ;; Load seen-list.
1364     (setq seen-list (elmo-msgdb-seen-load (elmo-folder-msgdb-path folder)))
1365     (setq number-alist (elmo-msgdb-get-number-alist
1366                         (elmo-folder-msgdb folder)))
1367     (setq mark-alist (elmo-msgdb-get-mark-alist
1368                       (elmo-folder-msgdb folder)))
1369     (if ignore-msgdb
1370         (progn
1371           (setq seen-list (nconc (elmo-msgdb-seen-list
1372                                   (elmo-folder-msgdb folder))
1373                                  seen-list))
1374           (elmo-folder-clear folder (eq ignore-msgdb 'visible-only))))
1375     (unless no-check (elmo-folder-check folder))
1376     (condition-case nil
1377         (progn
1378           (message "Checking folder diff...")
1379           ;; TODO: killed list is loaded in elmo-folder-open and
1380           ;; list-messages use internal killed-list-folder.
1381           (setq diff (elmo-list-diff (elmo-folder-list-messages
1382                                       folder
1383                                       (eq 'visible-only ignore-msgdb))
1384                                      (unless ignore-msgdb
1385                                        (sort (mapcar
1386                                               'car
1387                                               number-alist)
1388                                              '<))))
1389           (message "Checking folder diff...done")
1390           (setq new-list (elmo-folder-confirm-appends (car diff)))
1391           ;; Set killed list.
1392           (when (and (not (eq (length (car diff))
1393                               (length new-list)))
1394                      (setq diff-2 (elmo-list-diff (car diff) new-list)))
1395             (elmo-msgdb-append-to-killed-list folder (car diff-2)))
1396           (setq delete-list (cadr diff))
1397           (if (or (equal diff '(nil nil))
1398                   (equal diff '(nil))
1399                   (and (eq (length (car diff)) 0)
1400                        (eq (length (cadr diff)) 0)))
1401               (progn
1402                 (elmo-folder-update-number folder)
1403                 (elmo-folder-process-crosspost folder)
1404                 0 ; no updates.
1405                 )
1406             (if delete-list (elmo-msgdb-delete-msgs
1407                              (elmo-folder-msgdb folder) delete-list))
1408             (when new-list
1409               (setq new-msgdb (elmo-folder-msgdb-create
1410                                folder new-list seen-list))
1411               (elmo-msgdb-change-mark (elmo-folder-msgdb folder)
1412                                       elmo-msgdb-new-mark
1413                                       elmo-msgdb-unread-uncached-mark)
1414               ;; Clear seen-list.
1415               (if (elmo-folder-persistent-p folder)
1416                   (setq seen-list (elmo-msgdb-seen-save
1417                                    (elmo-folder-msgdb-path folder) nil)))
1418               (setq before-append nil)
1419               (setq crossed (elmo-folder-append-msgdb folder new-msgdb))
1420               ;; process crosspost.
1421               ;; Return a cons cell of (NUMBER-CROSSPOSTS . NEW-MARK-ALIST).
1422               (elmo-folder-process-crosspost folder)
1423               (elmo-folder-set-message-modified-internal folder t)
1424               (elmo-folder-set-mark-modified-internal folder t))
1425             ;; return value.
1426             (or crossed 0)))
1427       (quit
1428        ;; Resume to the original status.
1429        (if before-append
1430            (elmo-folder-set-msgdb-internal folder old-msgdb))
1431        (elmo-folder-set-killed-list-internal folder killed-list)
1432        nil))))
1433
1434 (defun elmo-folder-messages (folder)
1435   "Return number of messages in the FOLDER."
1436   (length
1437    (elmo-msgdb-get-number-alist
1438     (elmo-folder-msgdb folder))))
1439
1440 (defun elmo-msgdb-load (folder &optional silent)
1441   (unless silent
1442     (message "Loading msgdb for %s..." (elmo-folder-name-internal folder)))
1443   (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder))))
1444     (elmo-folder-set-info-max-by-numdb folder
1445                                        (elmo-msgdb-get-number-alist msgdb))
1446     
1447     (unless silent
1448       (message "Loading msgdb for %s...done"
1449                (elmo-folder-name-internal folder)))
1450     msgdb))
1451   
1452 (defun elmo-msgdb-delete-path (folder)
1453   (let ((path (elmo-folder-msgdb-path folder)))
1454     (if (file-directory-p path)
1455         (elmo-delete-directory path t))))
1456
1457 (defun elmo-msgdb-rename-path (old-folder new-folder)
1458   (let* ((old (directory-file-name (elmo-folder-msgdb-path old-folder)))
1459          (new (directory-file-name (elmo-folder-msgdb-path new-folder)))
1460          (new-dir (directory-file-name (file-name-directory new))))
1461     (if (not (file-directory-p old))
1462         ()
1463       (if (file-exists-p new)
1464           (error "Already exists directory: %s" new)
1465         (if (not (file-exists-p new-dir))
1466             (elmo-make-directory new-dir))
1467         (rename-file old new)))))
1468
1469 (defun elmo-setup-subscribed-newsgroups (groups)
1470   "Setup subscribed newsgroups.
1471 GROUPS is a list of newsgroup name string.
1472 Return a hashtable for newsgroups."
1473   (let ((hashtb (or elmo-newsgroups-hashtb
1474                     (setq elmo-newsgroups-hashtb
1475                           (elmo-make-hash (length groups))))))
1476     (dolist (group groups)
1477       (or (elmo-get-hash-val group hashtb)
1478           (elmo-set-hash-val group nil hashtb)))
1479     (setq elmo-newsgroups-hashtb hashtb)))
1480
1481 (defvar elmo-crosspost-message-alist-modified nil)
1482 (defun elmo-crosspost-message-alist-load ()
1483   "Load crosspost message alist."
1484   (setq elmo-crosspost-message-alist (elmo-crosspost-alist-load))
1485   (setq elmo-crosspost-message-alist-modified nil))
1486
1487 (defun elmo-crosspost-message-alist-save ()
1488   "Save crosspost message alist."
1489   (when elmo-crosspost-message-alist-modified
1490     (let ((alist elmo-crosspost-message-alist)
1491           newsgroups)
1492       (while alist
1493         (setq newsgroups
1494               (elmo-delete-if
1495                '(lambda (x)
1496                   (not (intern-soft x elmo-newsgroups-hashtb)))
1497                (nth 1 (car alist))))
1498         (if newsgroups
1499             (setcar (cdar alist) newsgroups)
1500           (setq elmo-crosspost-message-alist
1501                 (delete (car alist) elmo-crosspost-message-alist)))
1502         (setq alist (cdr alist)))
1503       (elmo-crosspost-alist-save elmo-crosspost-message-alist)
1504       (setq elmo-crosspost-message-alist-modified nil))))
1505
1506 (defun elmo-folder-make-temporary-directory (folder)
1507   ;; Make a temporary directory for FOLDER.
1508   (let ((temp-dir (make-temp-name
1509                    (concat
1510                     (file-name-as-directory (elmo-folder-msgdb-path folder))
1511                     "elmo"))))
1512     (elmo-make-directory temp-dir)
1513     temp-dir))
1514
1515 (defun elmo-init ()
1516   "Initialize ELMO module."
1517   (elmo-crosspost-message-alist-load)
1518   (elmo-resque-obsolete-variables)
1519   (elmo-dop-queue-load))
1520
1521 (defun elmo-quit ()
1522   "Quit and cleanup ELMO."
1523   (elmo-crosspost-message-alist-save)
1524   (elmo-dop-queue-save)
1525   ;; Not implemented yet.
1526   (let ((types elmo-folder-type-alist)
1527         class)
1528     (while types
1529       (setq class
1530             (luna-find-class
1531              (intern (format "elmo-%s-folder" (symbol-name (cdr (car types)))))))
1532       ;; Call all folder's `elmo-quit' method.
1533       (if class
1534           (dolist (func (luna-class-find-functions class 'elmo-quit))
1535             (funcall func nil)))
1536       (setq types (cdr types)))))
1537
1538
1539 ;;; Define folders.
1540 (elmo-define-folder ?% 'imap4)
1541 (elmo-define-folder ?-  'nntp)
1542 (elmo-define-folder ?\+ 'localdir)
1543 (elmo-define-folder ?\* 'multi)
1544 (elmo-define-folder ?\/ 'filter)
1545 (elmo-define-folder ?\$ 'archive)
1546 (elmo-define-folder ?&  'pop3)
1547 (elmo-define-folder ?=  'localnews)
1548 (elmo-define-folder ?|  'pipe)
1549 (elmo-define-folder ?.  'maildir)
1550 (elmo-define-folder ?'  'internal)
1551 (elmo-define-folder ?\[  'nmz)
1552 (elmo-define-folder ?@  'shimbun)
1553
1554 ;;; Obsolete variables.
1555 (elmo-define-obsolete-variable 'elmo-default-imap4-mailbox
1556                                'elmo-imap4-default-mailbox)
1557 (elmo-define-obsolete-variable 'elmo-default-imap4-server
1558                                'elmo-imap4-default-server)
1559 (elmo-define-obsolete-variable 'elmo-default-imap4-authenticate-type
1560                                'elmo-imap4-default-authenticate-type)
1561 (elmo-define-obsolete-variable 'elmo-default-imap4-user
1562                                'elmo-imap4-default-user)
1563 (elmo-define-obsolete-variable 'elmo-default-imap4-port
1564                                'elmo-imap4-default-port)
1565 (elmo-define-obsolete-variable 'elmo-default-imap4-stream-type
1566                                'elmo-imap4-default-stream-type)
1567 (elmo-define-obsolete-variable 'elmo-default-nntp-server
1568                                'elmo-nntp-default-server)
1569 (elmo-define-obsolete-variable 'elmo-default-nntp-user
1570                                'elmo-nntp-default-user)
1571 (elmo-define-obsolete-variable 'elmo-default-nntp-port
1572                                'elmo-nntp-default-port)
1573 (elmo-define-obsolete-variable 'elmo-default-nntp-stream-type
1574                                'elmo-nntp-default-stream-type)
1575 (elmo-define-obsolete-variable 'elmo-default-pop3-server
1576                                'elmo-pop3-default-server)
1577 (elmo-define-obsolete-variable 'elmo-default-pop3-user
1578                                'elmo-pop3-default-user)
1579 (elmo-define-obsolete-variable 'elmo-default-pop3-authenticate-type
1580                                'elmo-pop3-default-authenticate-type)
1581 (elmo-define-obsolete-variable 'elmo-default-pop3-port
1582                                'elmo-pop3-default-port)
1583 (elmo-define-obsolete-variable 'elmo-default-pop3-stream-type
1584                                'elmo-pop3-default-stream-type)
1585 (elmo-define-obsolete-variable 'elmo-cache-dirname
1586                                'elmo-cache-directory)
1587 (elmo-define-obsolete-variable 'elmo-msgdb-dir
1588                                'elmo-msgdb-directory)
1589
1590 ;; Obsolete functions.
1591 ;; 2001-12-11: *-dir -> *-directory
1592 (defalias 'elmo-folder-make-temp-dir 'elmo-folder-make-temporary-directory)
1593 (make-obsolete 'elmo-folder-make-temp-dir
1594                'elmo-folder-make-temporary-directory)
1595
1596
1597 ;; autoloads
1598 (autoload 'elmo-dop-queue-flush "elmo-dop")
1599
1600 (require 'product)
1601 (product-provide (provide 'elmo) (require 'elmo-version))
1602
1603 ;;; elmo.el ends here