117184ce6570f2e64b482a7e617d2d9ebdac9028
[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                                                      &optional ignore-flags)
349   "Un-mark messages as read.
350 FOLDER is the ELMO folder structure.
351 NUMBERS is a list of message numbers to be processed.
352 If IGNORE-FLAGS is non-nil, folder flags are not updated.")
353
354 (luna-define-generic elmo-folder-mark-as-read (folder numbers
355                                                       &optional ignore-flags)
356   "Mark messages as read.
357 FOLDER is the ELMO folder structure.
358 NUMBERS is a list of message numbers to be processed.
359 If IGNORE-FLAGS is non-nil, folder flags are not updated.")
360
361 (luna-define-generic elmo-folder-unmark-answered (folder numbers)
362   "Un-mark messages as answered.
363 FOLDER is the ELMO folder structure.
364 NUMBERS is a list of message numbers to be processed.")
365
366 (luna-define-generic elmo-folder-mark-as-answered (folder numbers)
367   "Mark messages as answered.
368 FOLDER is the ELMO folder structure.
369 NUMBERS is a list of message numbers to be processed.")
370
371 (luna-define-generic elmo-folder-append-buffer (folder unread &optional number)
372   "Append current buffer as a new message.
373 FOLDER is the destination folder(ELMO folder structure).
374 If UNREAD is non-nil, message is appended as unread.
375 If optional argument NUMBER is specified, the new message number is set
376 (if possible).")
377
378 (luna-define-generic elmo-folder-append-messages (folder
379                                                   src-folder
380                                                   numbers
381                                                   &optional
382                                                   same-number)
383   "Append messages from folder.
384 FOLDER is the ELMO folder structure.
385 Caller should make sure FOLDER is `writable'.
386 (Can be checked with `elmo-folder-writable-p').
387 SRC-FOLDER is the source ELMO folder structure.
388 NUMBERS is the message numbers to be appended in the SRC-FOLDER.
389 If second optional argument SAME-NUMBER is specified,
390 message number is preserved (if possible).")
391
392 (luna-define-generic elmo-folder-pack-numbers (folder)
393   "Pack message numbers of FOLDER.")
394
395 (luna-define-generic elmo-folder-update-number (folder)
396   "Update number of FOLDER.")
397
398 (luna-define-generic elmo-folder-diff-async (folder)
399   "Get diff of FOLDER asynchronously.")
400
401 (luna-define-generic elmo-folder-expand-msgdb-path (folder)
402   "Expand path for FOLDER.")
403
404 (luna-define-generic elmo-folder-get-primitive-list (folder)
405   "Get primitive folder structure list contained in FOLDER.")
406
407 (luna-define-generic elmo-folder-contains-type (folder type)
408   "Returns t if FOLDER contains TYPE.")
409
410 (luna-define-generic elmo-folder-local-p (folder)
411   "Returns t if FOLDER is local.")
412
413 (luna-define-generic elmo-folder-message-file-p (folder)
414   "Returns t if all messages in the FOLDER are files.")
415
416 ;;; Message methods.
417 (luna-define-generic elmo-message-use-cache-p (folder number)
418   "Returns t if the message in the FOLDER with NUMBER uses cache.")
419
420 (luna-define-generic elmo-message-file-name (folder number)
421   "Return the file name of a message specified by FOLDER and NUMBER.")
422
423 ;;; For archive
424
425 ;;; Use original file
426 (luna-define-generic elmo-folder-message-file-number-p (folder)
427   "Return t if the file name in the FOLDER is the message number.")
428
429 (luna-define-generic elmo-folder-message-file-directory (folder)
430   "Return the directory of the message files of FOLDER.")
431
432 ;;; Use temporary file
433 (luna-define-generic elmo-folder-message-make-temp-file-p (folder)
434   "Return t if the messages in the FOLDER makes local temporary file.")
435
436 (luna-define-generic elmo-folder-message-make-temp-files (folder
437                                                           numbers
438                                                           &optional
439                                                           start-number)
440   "Make a new temporary files from the messages in the FOLDER with NUMBERS.
441 If START-NUMBER is specified, temporary files begin from the number.
442 Otherwise, same number is used for temporary files.
443 Return newly created temporary directory name which contains temporary files.")
444
445 (luna-define-generic elmo-message-file-p (folder number)
446   "Return t if message in the FOLDER with NUMBER is a file.")
447
448 (luna-define-generic elmo-find-fetch-strategy
449   (folder entity &optional ignore-cache)
450 ;; Returns the message fetching strategy suitable for the message.
451 ;; FOLDER is the ELMO folder structure.
452 ;; ENTITY is the overview entity of the message in the folder.
453 ;; If optional argument IGNORE-CACHE is non-nil, cache is ignored.
454 ;; Returned value is a elmo-fetch-strategy object.
455 ;; If return value is nil, message should not be nil.
456   )
457
458 (defmacro elmo-make-fetch-strategy (entireness
459                                     &optional
460                                     use-cache
461                                     save-cache
462                                     cache-path)
463 ;; Make elmo-message-fetching strategy.
464 ;; ENTIRENESS is 'entire or 'section.
465 ;; 'entire means fetch message entirely at once.
466 ;; 'section means fetch message section by section.
467 ;; If optional USE-CACHE is non-nil, existing cache is used and otherwise,
468 ;; existing cache is thrown away.
469 ;; If SAVE-CACHE is non-nil, fetched message is saved.
470 ;; CACHE-PATH is the cache path to be used as a message cache file.
471   (` (vector (, entireness)
472              (, use-cache) (, save-cache) (, cache-path))))
473
474 (defmacro elmo-fetch-strategy-entireness (strategy)
475   ;; Return entireness of STRATEGY.
476   (` (aref (, strategy) 0)))
477
478 (defmacro elmo-fetch-strategy-use-cache (strategy)
479   ;; Return use-cache of STRATEGY.
480   (` (aref (, strategy) 1)))
481
482 (defmacro elmo-fetch-strategy-save-cache (strategy)
483   ;; Return save-cache of STRATEGY.
484   (` (aref (, strategy) 2)))
485
486 (defmacro elmo-fetch-strategy-cache-path (strategy)
487   ;;  Return cache-path of STRATEGY.
488   (` (aref (, strategy) 3)))
489
490 (luna-define-method elmo-find-fetch-strategy
491   ((folder elmo-folder) entity &optional ignore-cache)
492   (let (cache-file size message-id number)
493     (setq size (elmo-msgdb-overview-entity-get-size entity))
494     (setq message-id (elmo-msgdb-overview-entity-get-id entity))
495     (setq number (elmo-msgdb-overview-entity-get-number entity))
496     (setq cache-file (elmo-file-cache-get message-id))
497     (setq ignore-cache (or ignore-cache
498                            (null (elmo-message-use-cache-p folder number))))
499     (if (or ignore-cache
500             (null (elmo-file-cache-status cache-file)))
501         ;; No cache or ignore-cache.
502         (if (and (not (elmo-folder-local-p folder))
503                  elmo-message-fetch-threshold
504                  (integerp size)
505                  (>= size elmo-message-fetch-threshold)
506                  (or (not elmo-message-fetch-confirm)
507                      (not (prog1 (y-or-n-p
508                                   (format "Fetch entire message(%dbytes)? "
509                                           size))
510                             (message "")))))
511             ;; Don't fetch message at all.
512             nil
513           ;; Don't use existing cache and fetch entire message at once.
514           (elmo-make-fetch-strategy
515            'entire nil
516            (elmo-message-use-cache-p folder number)
517            (elmo-file-cache-path cache-file)))
518       ;; Cache exists.
519       (if (not ignore-cache)
520           (elmo-make-fetch-strategy
521            'entire
522            ;; ...But ignore current section cache and re-fetch
523            ;; if section cache.
524            (not (eq (elmo-file-cache-status cache-file) 'section))
525            ;; Save cache.
526            (elmo-message-use-cache-p folder number)
527            (elmo-file-cache-path cache-file))))))
528
529 (luna-define-method elmo-folder-list-messages-internal
530   ((folder elmo-folder) &optional visible-only)
531   t)
532
533 (defun elmo-folder-encache (folder numbers &optional unread)
534   "Encache messages in the FOLDER with NUMBERS.
535 If UNREAD is non-nil, messages are not marked as read."
536   (dolist (number numbers)
537     (elmo-message-encache folder number unread)))
538
539 (luna-define-generic elmo-message-encache (folder number &optional read)
540   "Encache message in the FOLDER with NUMBER.
541 If READ is non-nil, message is marked as read.")
542
543 (luna-define-method elmo-message-encache ((folder elmo-folder) number
544                                           &optional read)
545   (elmo-message-fetch
546    folder number
547    (elmo-make-fetch-strategy 'entire
548                              nil ;use-cache
549                              t   ;save-cache
550                              (elmo-file-cache-get-path
551                               (elmo-message-field
552                                folder number 'message-id)))
553    nil nil (not read)))
554
555 (luna-define-generic elmo-message-fetch (folder number strategy
556                                                 &optional
557                                                 section
558                                                 outbuf
559                                                 unread)
560   "Fetch a message and return as a string.
561 FOLDER is the ELMO folder structure.
562 NUMBER is the number of the message in the FOLDER.
563 STRATEGY is the message fetching strategy.
564 If optional argument SECTION is specified, only the SECTION of the message
565 is fetched (if possible).
566 If second optional argument OUTBUF is specified, fetched message is
567 inserted to the buffer and returns t if fetch was ended successfully.
568 If third optional argument UNREAD is non-nil, message is not marked as read.
569 Returns non-nil if fetching was succeed.")
570
571 (luna-define-generic elmo-message-fetch-with-cache-process (folder
572                                                             number strategy
573                                                             &optional
574                                                             section
575                                                             unread)
576   "Fetch a message into current buffer with cache process.
577 FOLDER is the ELMO folder structure.
578 NUMBER is the number of the message in the FOLDER.
579 STRATEGY is the message fetching strategy.
580 If optional argument SECTION is specified, only the SECTION of the message
581 is fetched (if possible).
582 If second optional argument UNREAD is non-nil, message is not marked as read.
583 Returns non-nil if fetching was succeed.")
584
585 (luna-define-generic elmo-message-fetch-internal (folder number strategy
586                                                          &optional
587                                                          section
588                                                          unread)
589   "Fetch a message into current buffer.
590 FOLDER is the ELMO folder structure.
591 NUMBER is the number of the message in the FOLDER.
592 STRATEGY is the message fetching strategy.
593 If optional argument SECTION is specified, only the SECTION of the message
594 is fetched (if possible).
595 If second optional argument UNREAD is non-nil, message is not marked as read.
596 Returns non-nil if fetching was succeed.")
597
598 (luna-define-generic elmo-message-fetch-field (folder number field)
599   "Fetch a message field value.
600 FOLDER is the ELMO folder structure.
601 NUMBER is the number of the message in the FOLDER.
602 FIELD is a symbol of the field name.")
603
604 (luna-define-generic elmo-message-folder (folder number)
605   "Get primitive folder of the message.")
606
607 (luna-define-generic elmo-folder-process-crosspost (folder
608                                                     &optional
609                                                     number-alist)
610   "Process crosspost for FOLDER.
611 If NUMBER-ALIST is set, it is used as number-alist.
612 Return a cons cell of (NUMBER-CROSSPOSTS . NEW-MARK-ALIST).")
613
614 (luna-define-generic elmo-folder-append-msgdb (folder append-msgdb)
615   "Append  APPEND-MSGDB to the current msgdb of the folder.")
616
617 (luna-define-generic elmo-folder-newsgroups (folder)
618   "Return list of newsgroup name of FOLDER.")
619
620 (luna-define-generic elmo-folder-search-requires-msgdb-p (folder condition)
621   "Return non-nil if searching in FOLDER by CONDITION requires msgdb fetch.")
622
623 (defun elmo-folder-search-requires-msgdb-p-internal (folder condition)
624   (if (listp condition)
625       (or (elmo-folder-search-requires-msgdb-p-internal
626            folder (nth 1 condition))
627           (elmo-folder-search-requires-msgdb-p-internal
628            folder (nth 2 condition)))
629     (and (not (string= (elmo-filter-key condition) "last"))
630          (not (string= (elmo-filter-key condition) "first")))))
631
632 (luna-define-method elmo-folder-search-requires-msgdb-p ((folder elmo-folder)
633                                                          condition)
634   (elmo-folder-search-requires-msgdb-p-internal folder condition))
635
636 (luna-define-method elmo-folder-newsgroups ((folder elmo-folder))
637   nil)
638
639 (luna-define-method elmo-folder-open ((folder elmo-folder)
640                                       &optional load-msgdb)
641   (elmo-generic-folder-open folder load-msgdb))
642
643 (defun elmo-generic-folder-open (folder load-msgdb)
644   (let ((inhibit-quit t))
645     (if load-msgdb (elmo-folder-msgdb folder))
646     (elmo-folder-set-killed-list-internal
647      folder
648      (elmo-msgdb-killed-list-load (elmo-folder-msgdb-path folder))))
649   (elmo-folder-open-internal folder))
650
651 (luna-define-method elmo-folder-open-internal ((folder elmo-folder))
652   nil ; default is do nothing.
653   )
654
655 (luna-define-method elmo-folder-check ((folder elmo-folder))
656   nil) ; default is noop.
657
658 (luna-define-method elmo-folder-commit ((folder elmo-folder))
659   (elmo-generic-folder-commit folder))
660
661 (defun elmo-generic-folder-commit (folder)
662   (when (elmo-folder-persistent-p folder)
663     (when (elmo-folder-message-modified-internal folder)
664       (elmo-msgdb-overview-save
665        (elmo-folder-msgdb-path folder)
666        (elmo-msgdb-get-overview (elmo-folder-msgdb folder)))
667       (elmo-msgdb-number-save
668        (elmo-folder-msgdb-path folder)
669        (elmo-msgdb-get-number-alist (elmo-folder-msgdb folder)))
670       (elmo-folder-set-info-max-by-numdb
671        folder
672        (elmo-msgdb-get-number-alist
673         (elmo-folder-msgdb folder)))
674       (elmo-folder-set-message-modified-internal folder nil)
675       (elmo-msgdb-killed-list-save
676        (elmo-folder-msgdb-path folder)
677        (elmo-folder-killed-list-internal folder)))
678     (when (elmo-folder-mark-modified-internal folder)
679       (elmo-msgdb-mark-save
680        (elmo-folder-msgdb-path folder)
681        (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))
682       (elmo-folder-set-mark-modified-internal folder nil))))
683
684 (luna-define-method elmo-folder-close-internal ((folder elmo-folder))
685   ;; do nothing.
686   )
687
688 (luna-define-method elmo-folder-close ((folder elmo-folder))
689   (elmo-generic-folder-close folder)
690   (elmo-folder-close-internal folder))
691
692 (defun elmo-generic-folder-close (folder)
693   (elmo-folder-commit folder)
694   (elmo-folder-set-msgdb-internal folder nil)
695   (elmo-folder-set-killed-list-internal folder nil))
696
697 (luna-define-method elmo-folder-plugged-p ((folder elmo-folder))
698   t) ; default is plugged.
699
700 (luna-define-method elmo-folder-set-plugged ((folder elmo-folder) plugged
701                                              &optional add)
702   nil) ; default is do nothing.
703
704 (luna-define-method elmo-folder-use-flag-p ((folder elmo-folder))
705   nil) ; default is no flag.
706
707 (luna-define-method elmo-folder-persistent-p ((folder elmo-folder))
708   (elmo-folder-persistent-internal folder))
709
710 (luna-define-method elmo-folder-creatable-p ((folder elmo-folder))
711   nil) ; default is not creatable.
712
713 (luna-define-method elmo-folder-writable-p ((folder elmo-folder))
714   nil) ; default is not writable.
715
716 (luna-define-method elmo-folder-delete ((folder elmo-folder))
717   (elmo-msgdb-delete-path folder))
718
719 (luna-define-method elmo-folder-rename ((folder elmo-folder) new-name)
720   (let* ((new-folder (elmo-make-folder new-name)))
721     (unless (eq (elmo-folder-type-internal folder)
722                 (elmo-folder-type-internal new-folder))
723       (error "Not same folder type"))
724     (if (or (file-exists-p (elmo-folder-msgdb-path new-folder))
725             (elmo-folder-exists-p new-folder))
726         (error "Already exists folder: %s" new-name))
727     (elmo-folder-send folder 'elmo-folder-rename-internal new-folder)
728     (elmo-msgdb-rename-path folder new-folder)))
729
730 (defsubst elmo-folder-search-fast (folder condition numbers)
731   (when (and numbers
732              (vectorp condition)
733              (member (elmo-filter-key condition) '("first" "last" "mark")))
734     (if (string= (elmo-filter-key condition) "mark")
735         (let ((msgdb (elmo-folder-msgdb folder)))
736           ;; msgdb should be synchronized at this point.
737           (cond
738            ((string= (elmo-filter-value condition) "unread")
739             (elmo-folder-list-unreads folder))
740            ((string= (elmo-filter-value condition) "important")
741             (elmo-folder-list-importants folder))
742            ((string= (elmo-filter-value condition) "answered")
743             (elmo-folder-list-answereds folder))
744            ((string= (elmo-filter-value condition) "any")
745             (nconc (elmo-folder-list-unreads folder)
746                    (elmo-folder-list-importants folder)
747                    (elmo-folder-list-answereds folder)))))
748       (let ((len (length numbers))
749             (lastp (string= (elmo-filter-key condition) "last"))
750             (value (string-to-number (elmo-filter-value condition))))
751         (when (eq (elmo-filter-type condition) 'unmatch)
752           (setq lastp (not lastp)
753                 value  (- len value)))
754         (if lastp
755             (nthcdr (max (- len value) 0) numbers)
756           (when (> value 0)
757             (let ((last (nthcdr (1- value) numbers)))
758               (when last
759                 (setcdr last nil))
760               numbers)))))))
761
762 (luna-define-method elmo-folder-search ((folder elmo-folder)
763                                         condition
764                                         &optional numbers)
765   (let ((numbers (or numbers (elmo-folder-list-messages folder))))
766     (or (elmo-folder-search-fast folder condition numbers)
767         (let ((msgdb (elmo-folder-msgdb folder))
768               (len (length numbers))
769               matched)
770           (when (> len elmo-display-progress-threshold)
771             (elmo-progress-set 'elmo-folder-search len "Searching..."))
772           (unwind-protect
773               (dolist (number numbers)
774                 (let (result)
775                   (setq result (elmo-msgdb-match-condition
776                                 msgdb
777                                 condition
778                                 number
779                                 numbers))
780                   (when (elmo-filter-condition-p result)
781                     (setq result (elmo-message-match-condition
782                                   folder
783                                   number
784                                   condition
785                                   numbers)))
786                   (when result
787                     (setq matched (cons number matched))))
788                 (elmo-progress-notify 'elmo-folder-search))
789             (elmo-progress-clear 'elmo-folder-search))
790           (nreverse matched)))))
791
792 (luna-define-method elmo-message-match-condition ((folder elmo-folder)
793                                                   number condition
794                                                   numbers)
795   (let ((filename (cond
796                    ((elmo-message-file-name folder number))
797                    ((let* ((cache (elmo-file-cache-get
798                                    (elmo-message-field folder number
799                                                        'message-id)))
800                            (cache-path (elmo-file-cache-path cache)))
801                       (when (and cache-path
802                                  (not (elmo-cache-path-section-p cache-path)))
803                         cache-path))))))
804     (when (and filename
805                (file-readable-p filename))
806       (with-temp-buffer
807         (insert-file-contents-as-binary filename)
808         (elmo-set-buffer-multibyte default-enable-multibyte-characters)
809         ;; Should consider charset?
810         (decode-mime-charset-region (point-min) (point-max) elmo-mime-charset)
811         (elmo-buffer-field-condition-match condition number numbers)))))
812
813 (luna-define-method elmo-folder-pack-numbers ((folder elmo-folder))
814   nil) ; default is noop.
815
816 (luna-define-method elmo-folder-update-number ((folder elmo-folder))
817   nil) ; default is noop.
818
819 (luna-define-method elmo-folder-message-file-p ((folder elmo-folder))
820   nil) ; default is not file.
821
822 (luna-define-method elmo-folder-message-file-number-p ((folder elmo-folder))
823   nil) ; default is not number.
824
825 (luna-define-method elmo-folder-message-make-temp-file-p ((folder elmo-folder))
826   nil) ; default is not make temp file.
827
828 (luna-define-method elmo-message-file-name ((folder elmo-folder)
829                                                    number)
830   nil) ; default is no name.
831
832 (luna-define-method elmo-folder-local-p ((folder elmo-folder))
833   t)   ; default is local.
834
835 (luna-define-method elmo-folder-have-subfolder-p ((folder elmo-folder))
836   t)
837
838 ;;; Folder info
839 ;; Folder info is a message number information cache (hashtable)
840 (defsubst elmo-folder-get-info (folder &optional hashtb)
841   "Return FOLDER info from HASHTB (default is `elmo-folder-info-hashtb')."
842   (elmo-get-hash-val (elmo-folder-name-internal folder)
843                      (or hashtb elmo-folder-info-hashtb)))
844
845 (defun elmo-folder-set-info-hashtb (folder max numbers &optional new unread)
846   "Set FOLDER info (means MAX, NUMBERS, NEW and UNREAD)."
847   (let ((info (elmo-folder-get-info folder)))
848     (when info
849       (or new     (setq new     (nth 0 info)))
850       (or unread  (setq unread  (nth 1 info)))
851       (or numbers (setq numbers (nth 2 info)))
852       (or max     (setq max     (nth 3 info))))
853     (elmo-set-hash-val (elmo-folder-name-internal folder)
854                        (list new unread numbers max)
855                        elmo-folder-info-hashtb)))
856
857 (defun elmo-folder-set-info-max-by-numdb (folder msgdb-number)
858   "Set FOLDER info by MSGDB-NUMBER in msgdb."
859   (let ((num-db (sort (mapcar 'car msgdb-number) '<)))
860     (elmo-folder-set-info-hashtb
861      folder
862      (or (nth (max 0 (1- (length num-db))) num-db) 0)
863      nil ;;(length num-db)
864      )))
865
866 (defun elmo-folder-get-info-max (folder)
867   "Return max number of FODLER from folder info."
868   (nth 3 (elmo-folder-get-info folder)))
869
870 (defun elmo-folder-get-info-length (folder)
871   "Return length of FODLER from folder info."
872   (nth 2 (elmo-folder-get-info folder)))
873
874 (defun elmo-folder-get-info-unread (folder)
875   "Return unread of FODLER from folder info."
876   (nth 1 (elmo-folder-get-info folder)))
877
878 (defun elmo-folder-info-make-hashtb (info-alist hashtb)
879   "Setup folder info hashtable by INFO-ALIST on HASHTB."
880   (let* ((hashtb (or hashtb
881                      (elmo-make-hash (length info-alist)))))
882     (mapcar
883      (lambda (x)
884        (let ((info (cadr x)))
885          (and (intern-soft (car x) hashtb)
886               (elmo-set-hash-val (car x)
887                                  (list (nth 2 info)   ;; new
888                                        (nth 3 info)   ;; unread
889                                        (nth 1 info)   ;; length
890                                        (nth 0 info))  ;; max
891                                  hashtb))))
892      info-alist)
893     (setq elmo-folder-info-hashtb hashtb)))
894
895 (defsubst elmo-diff-new (diff)
896   (car diff))
897
898 (defsubst elmo-diff-unread (diff)
899   (when (consp (cdr diff))
900     (nth 1 diff)))
901
902 (defsubst elmo-diff-all (diff)
903   (if (consp (cdr diff))
904       (nth 2 diff)
905     (cdr diff)))
906
907 (defsubst elmo-strict-folder-diff (folder)
908   "Return folder diff information strictly from FOLDER."
909   (let* ((dir (elmo-folder-msgdb-path folder))
910          (nalist (elmo-msgdb-get-number-alist (elmo-folder-msgdb folder)))
911          (in-db (sort (mapcar 'car nalist) '<))
912          (in-folder  (elmo-folder-list-messages folder))
913          append-list delete-list diff)
914     (cons (if (equal in-folder in-db)
915               0
916             (setq diff (elmo-list-diff
917                         in-folder in-db
918                         nil
919                         ))
920             (setq append-list (car diff))
921             (setq delete-list (cadr diff))
922             (if append-list
923                 (length append-list)
924               (if delete-list
925                   (- 0 (length delete-list))
926                 0)))
927           (length in-folder))))
928
929 (luna-define-method elmo-folder-diff ((folder elmo-folder)
930                                       &optional numbers)
931   (elmo-generic-folder-diff folder numbers))
932
933 (defun elmo-generic-folder-diff (folder numbers)
934   (if (elmo-string-match-member (elmo-folder-name-internal folder)
935                                 elmo-strict-diff-folder-list)
936       (elmo-strict-folder-diff folder)
937     (let ((cached-in-db-max (elmo-folder-get-info-max folder))
938           (in-folder (elmo-folder-status folder))
939           (in-db t)
940           unsync messages
941           in-db-max)
942       (if numbers
943           (setq in-db-max (or (nth (max 0 (1- (length numbers))) numbers)
944                               0))
945         (if (not cached-in-db-max)
946             (let ((number-list (mapcar 'car
947                                        (elmo-msgdb-number-load
948                                         (elmo-folder-msgdb-path folder)))))
949               ;; No info-cache.
950               (setq in-db (sort number-list '<))
951               (setq in-db-max (or (nth (max 0 (1- (length in-db))) in-db)
952                                   0))
953               (elmo-folder-set-info-hashtb folder in-db-max nil))
954           (setq in-db-max cached-in-db-max)))
955       (setq unsync (if (and in-db
956                             (car in-folder))
957                        (- (car in-folder) in-db-max)
958                      (if (and in-folder
959                               (null in-db))
960                          (cdr in-folder)
961                        (if (null (car in-folder))
962                            nil))))
963       (setq messages (cdr in-folder))
964       (if (and unsync messages (> unsync messages))
965           (setq unsync messages))
966       (cons (or unsync 0) (or messages 0)))))
967
968 (defvar elmo-folder-diff-async-callback nil)
969 (defvar elmo-folder-diff-async-callback-data nil)
970
971 (luna-define-method elmo-folder-diff-async ((folder elmo-folder))
972   (and elmo-folder-diff-async-callback
973        (funcall elmo-folder-diff-async-callback
974                 folder
975                 (elmo-folder-diff folder))))
976
977 (luna-define-method elmo-folder-get-primitive-list ((folder elmo-folder))
978   (list folder))
979
980 (luna-define-method elmo-folder-contains-type ((folder elmo-folder) type)
981   (eq (elmo-folder-type-internal folder) type))
982
983 (luna-define-method elmo-folder-append-messages ((folder elmo-folder)
984                                                  src-folder
985                                                  numbers
986                                                  &optional
987                                                  same-number)
988   (elmo-generic-folder-append-messages folder src-folder numbers
989                                        same-number))
990
991 (defun elmo-generic-folder-append-messages (folder src-folder numbers
992                                                    same-number)
993   (let (unseen seen-list succeed-numbers failure cache)
994     (with-temp-buffer
995       (set-buffer-multibyte nil)
996       (while numbers
997         (setq failure nil)
998         (condition-case nil
999             (setq cache (elmo-file-cache-get
1000                          (elmo-message-field src-folder
1001                                              (car numbers)
1002                                              'message-id))
1003                   failure
1004                   (not
1005                    (and
1006                     (elmo-message-fetch
1007                      src-folder (car numbers)
1008                      (if (elmo-folder-plugged-p src-folder)
1009                          (elmo-make-fetch-strategy
1010                           'entire 'maybe nil
1011                           (and cache (elmo-file-cache-path cache)))
1012                        (or (and elmo-enable-disconnected-operation
1013                                 cache
1014                                 (eq (elmo-file-cache-status cache) 'entire)
1015                                 (elmo-make-fetch-strategy
1016                                  'entire t nil
1017                                  (elmo-file-cache-path cache)))
1018                            (error "Unplugged")))
1019                      nil (current-buffer)
1020                      'unread)
1021                     (> (buffer-size) 0)
1022                     (elmo-folder-append-buffer
1023                      folder
1024                      (setq unseen (member (elmo-message-mark
1025                                            src-folder (car numbers))
1026                                           (elmo-msgdb-unread-marks)))
1027                      (if same-number (car numbers))))))
1028           (error (setq failure t)))
1029         ;; FETCH & APPEND finished
1030         (unless failure
1031           (unless unseen
1032             (setq seen-list (cons (elmo-message-field
1033                                    src-folder (car numbers)
1034                                    'message-id)
1035                                   seen-list)))
1036           (setq succeed-numbers (cons (car numbers) succeed-numbers)))
1037         (elmo-progress-notify 'elmo-folder-move-messages)
1038         (setq numbers (cdr numbers)))
1039       (if (and seen-list (elmo-folder-persistent-p folder))
1040           (elmo-msgdb-seen-save (elmo-folder-msgdb-path folder)
1041                                 (nconc (elmo-msgdb-seen-load
1042                                         (elmo-folder-msgdb-path folder))
1043                                        seen-list)))
1044       succeed-numbers)))
1045
1046 ;; Arguments should be reduced.
1047 (defun elmo-folder-move-messages (src-folder msgs dst-folder
1048                                              &optional msgdb
1049                                              no-delete-info
1050                                              no-delete
1051                                              same-number
1052                                              save-unread)
1053   (save-excursion
1054     (let* ((messages msgs)
1055            (elmo-inhibit-display-retrieval-progress t)
1056            (len (length msgs))
1057            succeeds i result)
1058       (if (eq dst-folder 'null)
1059           (setq succeeds messages)
1060         (unless (elmo-folder-writable-p dst-folder)
1061           (error "move: %d is not writable"
1062                  (elmo-folder-name-internal dst-folder)))
1063         (when messages
1064           ;; src is already opened.
1065           (elmo-folder-open-internal dst-folder)
1066           (unless (setq succeeds (elmo-folder-append-messages dst-folder
1067                                                               src-folder
1068                                                               messages
1069                                                               same-number))
1070             (error "move: append message to %s failed"
1071                    (elmo-folder-name-internal dst-folder)))
1072           (elmo-folder-close dst-folder))
1073         (when (and (elmo-folder-persistent-p dst-folder)
1074                    save-unread)
1075           ;; Save to seen list.
1076           (let* ((dir (elmo-folder-msgdb-path dst-folder))
1077                  (seen-list (elmo-msgdb-seen-load dir)))
1078             (setq seen-list
1079                   (elmo-msgdb-add-msgs-to-seen-list
1080                    msgs (elmo-folder-msgdb src-folder)
1081                    seen-list))
1082             (elmo-msgdb-seen-save dir seen-list))))
1083       (if (and (not no-delete) succeeds)
1084           (progn
1085             (if (not no-delete-info)
1086                 (message "Cleaning up src folder..."))
1087             (if (and (elmo-folder-delete-messages src-folder succeeds)
1088                      (elmo-msgdb-delete-msgs
1089                       (elmo-folder-msgdb src-folder) succeeds))
1090                 (setq result t)
1091               (message "move: delete messages from %s failed."
1092                        (elmo-folder-name-internal src-folder))
1093               (setq result nil))
1094             (if (and result
1095                      (not no-delete-info))
1096                 (message "Cleaning up src folder...done"))
1097             result)
1098         (if no-delete
1099             (progn
1100               (message "Copying messages...done")
1101               t)
1102           (if (eq len 0)
1103               (message "No message was moved.")
1104             (message "Moving messages failed.")
1105             nil ; failure
1106             ))))))
1107
1108 (defun elmo-folder-msgdb-path (folder)
1109   "Return the msgdb path for FOLDER."
1110   (or (elmo-folder-path-internal folder)
1111       (elmo-folder-set-path-internal
1112        folder
1113        (elmo-folder-expand-msgdb-path folder))))
1114
1115 (defun elmo-message-set-cached (folder number cached)
1116   "Set cache status of the message mark.
1117 FOLDER is the ELMO folder structure.
1118 NUMBER is a number of the message.
1119 If CACHED is t, message mark is set as cached."
1120   (elmo-msgdb-set-cached 
1121    (elmo-folder-msgdb folder) number cached))
1122
1123 (defun elmo-message-mark (folder number)
1124   "Get mark of the message.
1125 FOLDER is the ELMO folder structure.
1126 NUMBER is a number of the message."
1127   (elmo-msgdb-get-mark (elmo-folder-msgdb folder) number))
1128
1129 (defun elmo-folder-list-messages-mark-match (folder mark-regexp)
1130   "List messages in the FOLDER which have a mark that matches MARK-REGEXP"
1131   (let ((case-fold-search nil)
1132         matched)
1133     (if mark-regexp
1134         (dolist (elem (elmo-msgdb-get-mark-alist (elmo-folder-msgdb folder)))
1135           (if (string-match mark-regexp (cadr elem))
1136               (setq matched (cons (car elem) matched)))))
1137     matched))
1138
1139 (defun elmo-message-field (folder number field)
1140   "Get message field value in the msgdb.
1141 FOLDER is the ELMO folder structure.
1142 NUMBER is a number of the message.
1143 FIELD is a symbol of the field."
1144   (elmo-msgdb-get-field (elmo-folder-msgdb folder) number field))
1145
1146 (defun elmo-message-set-mark (folder number mark)
1147   ;; Set mark for the message in the FOLDER with NUMBER as MARK.
1148   (elmo-msgdb-set-mark
1149    (elmo-folder-msgdb folder)
1150    number mark))
1151
1152 (luna-define-method elmo-message-use-cache-p ((folder elmo-folder) number)
1153   nil) ; default is not use cache.
1154
1155 (luna-define-method elmo-message-folder ((folder elmo-folder) number)
1156   folder) ; default is folder
1157
1158 (luna-define-method elmo-folder-unmark-important ((folder elmo-folder)
1159                                                   numbers)
1160   (when (elmo-folder-msgdb-internal folder)
1161     (dolist (number numbers)
1162       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1163                                folder
1164                                number
1165                                'important))))
1166
1167 (luna-define-method elmo-folder-mark-as-important ((folder elmo-folder)
1168                                                    numbers)
1169   (when (elmo-folder-msgdb-internal folder)
1170     (dolist (number numbers)
1171       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1172                              folder
1173                              number
1174                              'important))))
1175
1176 (luna-define-method elmo-folder-unmark-read ((folder elmo-folder)
1177                                              numbers
1178                                              &optional ignore-flags)
1179   (when (elmo-folder-msgdb-internal folder)
1180     (dolist (number numbers)
1181       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1182                                folder
1183                                number
1184                                'read))))
1185
1186 (luna-define-method elmo-folder-mark-as-read ((folder elmo-folder)
1187                                               numbers
1188                                               &optional ignore-flag)
1189   (when (elmo-folder-msgdb-internal folder)
1190     (dolist (number numbers)
1191       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1192                              folder
1193                              number
1194                              'read))))
1195
1196 (luna-define-method elmo-folder-unmark-answered ((folder elmo-folder) numbers)
1197   (when (elmo-folder-msgdb-internal folder)
1198     (dolist (number numbers)
1199       (elmo-msgdb-unset-status (elmo-folder-msgdb folder)
1200                                folder
1201                                number
1202                                'answered))))
1203
1204 (luna-define-method elmo-folder-mark-as-answered ((folder elmo-folder) numbers)
1205   (when (elmo-folder-msgdb-internal folder)
1206     (dolist (number numbers)
1207       (elmo-msgdb-set-status (elmo-folder-msgdb folder)
1208                              folder
1209                              number
1210                              'answered))))
1211
1212 (luna-define-method elmo-folder-process-crosspost ((folder elmo-folder)
1213                                                    &optional
1214                                                    number-alist)
1215   ;; Do nothing.
1216   )
1217
1218 (defun elmo-generic-folder-append-msgdb (folder append-msgdb)
1219   (if append-msgdb
1220       (let* ((number-alist (elmo-msgdb-get-number-alist append-msgdb))
1221              (all-alist (copy-sequence (append
1222                                         (elmo-msgdb-get-number-alist
1223                                          (elmo-folder-msgdb folder))
1224                                         number-alist)))
1225              (cur number-alist)
1226              pair overview
1227              to-be-deleted
1228              mark-alist)
1229         (elmo-folder-set-msgdb-internal folder
1230                                         (elmo-msgdb-append
1231                                          (elmo-folder-msgdb folder)
1232                                          append-msgdb))
1233         (while cur
1234           (setq all-alist (delq (car cur) all-alist))
1235           ;; same message id exists.
1236           (if (setq pair (rassoc (cdr (car cur)) all-alist))
1237               (setq to-be-deleted (nconc to-be-deleted (list (car pair)))))
1238           (setq cur (cdr cur)))
1239         (cond ((eq (elmo-folder-process-duplicates-internal folder)
1240                    'hide)
1241                ;; Hide duplicates.
1242                (setq overview (elmo-delete-if
1243                                (lambda (x)
1244                                  (memq (elmo-msgdb-overview-entity-get-number
1245                                         x)
1246                                        to-be-deleted))
1247                                (elmo-msgdb-get-overview append-msgdb)))
1248                ;; Should be mark as read.
1249                (elmo-folder-mark-as-read folder to-be-deleted)
1250                (elmo-msgdb-set-overview append-msgdb overview))
1251               ((eq (elmo-folder-process-duplicates-internal folder)
1252                    'read)
1253                ;; Mark as read duplicates.
1254                (elmo-folder-mark-as-read folder to-be-deleted))
1255               (t
1256                ;; Do nothing.
1257                (setq to-be-deleted nil)))
1258         (length to-be-deleted))
1259     0))
1260
1261 (luna-define-method elmo-folder-append-msgdb ((folder elmo-folder)
1262                                               append-msgdb)
1263   (elmo-generic-folder-append-msgdb folder append-msgdb))
1264
1265 (defun elmo-folder-confirm-appends (appends)
1266   (let ((len (length appends))
1267         in)
1268     (if (and elmo-folder-update-threshold
1269              (> len elmo-folder-update-threshold)
1270              elmo-folder-update-confirm)
1271         (if (y-or-n-p (format "Too many messages(%d).  Update all? " len))
1272             appends
1273           (setq in elmo-folder-update-threshold)
1274           (catch 'end
1275             (while t
1276               (setq in (read-from-minibuffer "Update number: "
1277                                              (int-to-string in))
1278                     in (string-to-int in))
1279               (if (< len in)
1280                   (throw 'end len))
1281               (if (y-or-n-p (format "%d messages are not appeared.  OK? "
1282                                     (max (- len in) 0)))
1283                   (throw 'end in))))
1284           (nthcdr (max (- len in) 0) appends))
1285       (if (and elmo-folder-update-threshold
1286                (> len elmo-folder-update-threshold)
1287                (not elmo-folder-update-confirm))
1288           (nthcdr (max (- len elmo-folder-update-threshold) 0) appends)
1289         appends))))
1290
1291 (luna-define-method elmo-message-fetch ((folder elmo-folder)
1292                                         number strategy
1293                                         &optional
1294                                         section
1295                                         outbuf
1296                                         unread)
1297   (if outbuf
1298       (with-current-buffer outbuf
1299         (erase-buffer)
1300         (elmo-message-fetch-with-cache-process folder number
1301                                                strategy section unread))
1302     (with-temp-buffer
1303       (elmo-message-fetch-with-cache-process folder number
1304                                              strategy section unread)
1305       (buffer-string))))
1306
1307 (luna-define-method elmo-message-fetch-with-cache-process ((folder elmo-folder)
1308                                                            number strategy
1309                                                            &optional
1310                                                            section unread)
1311   (let ((cache-path (elmo-fetch-strategy-cache-path strategy))
1312         (method-priorities
1313          (cond ((eq (elmo-fetch-strategy-use-cache strategy) 'maybe)
1314                 '(entity cache))
1315                ((elmo-fetch-strategy-use-cache strategy)
1316                 '(cache entity))
1317                (t
1318                 '(entity))))
1319         result err)
1320     (while (and method-priorities
1321                 (null result))
1322       (setq result
1323             (case (car method-priorities)
1324               (cache
1325                (elmo-file-cache-load cache-path section))
1326               (entity
1327                (when (and (condition-case error
1328                               (elmo-message-fetch-internal folder number
1329                                                            strategy
1330                                                            section
1331                                                            unread)
1332                             (error (setq err error) nil))
1333                           (> (buffer-size) 0))
1334                  (elmo-delete-cr-buffer)
1335                  (when (and (elmo-fetch-strategy-save-cache strategy)
1336                             cache-path)
1337                    (elmo-file-cache-save cache-path section))
1338                  t)))
1339             method-priorities (cdr method-priorities)))
1340     (or result
1341         (and err (signal (car err) (cdr err))))))
1342
1343 (luna-define-method elmo-folder-clear ((folder elmo-folder)
1344                                        &optional keep-killed)
1345   (unless keep-killed
1346     (elmo-folder-set-killed-list-internal folder nil))
1347   (elmo-folder-set-msgdb-internal folder (elmo-msgdb-clear)))
1348
1349 (defun elmo-folder-synchronize (folder
1350                                 &optional ignore-msgdb
1351                                 no-check)
1352   "Synchronize the folder data to the newest status.
1353 FOLDER is the ELMO folder structure.
1354 If optional IGNORE-MSGDB is non-nil, current msgdb is thrown away except
1355 read mark status. If IGNORE-MSGDB is 'visible-only, only visible messages
1356 \(the messages which are not in the killed-list\) are thrown away and
1357 synchronized.
1358 If NO-CHECK is non-nil, rechecking folder is skipped.
1359
1360 Return a list of a cross-posted message number.
1361 If update process is interrupted, return nil."
1362   (let ((killed-list (elmo-folder-killed-list-internal folder))
1363         (before-append t)
1364         number-alist mark-alist
1365         old-msgdb diff diff-2 delete-list new-list new-msgdb mark
1366         seen-list crossed after-append)
1367     (setq old-msgdb (elmo-folder-msgdb folder))
1368     ;; Load seen-list.
1369     (setq seen-list (elmo-msgdb-seen-load (elmo-folder-msgdb-path folder)))
1370     (setq number-alist (elmo-msgdb-get-number-alist
1371                         (elmo-folder-msgdb folder)))
1372     (setq mark-alist (elmo-msgdb-get-mark-alist
1373                       (elmo-folder-msgdb folder)))
1374     (if ignore-msgdb
1375         (progn
1376           (setq seen-list (nconc (elmo-msgdb-seen-list
1377                                   (elmo-folder-msgdb folder))
1378                                  seen-list))
1379           (elmo-folder-clear folder (eq ignore-msgdb 'visible-only))))
1380     (unless no-check (elmo-folder-check folder))
1381     (condition-case nil
1382         (progn
1383           (message "Checking folder diff...")
1384           ;; TODO: killed list is loaded in elmo-folder-open and
1385           ;; list-messages use internal killed-list-folder.
1386           (setq diff (elmo-list-diff (elmo-folder-list-messages
1387                                       folder
1388                                       (eq 'visible-only ignore-msgdb))
1389                                      (unless ignore-msgdb
1390                                        (sort (mapcar
1391                                               'car
1392                                               number-alist)
1393                                              '<))))
1394           (message "Checking folder diff...done")
1395           (setq new-list (elmo-folder-confirm-appends (car diff)))
1396           ;; Set killed list.
1397           (when (and (not (eq (length (car diff))
1398                               (length new-list)))
1399                      (setq diff-2 (elmo-list-diff (car diff) new-list)))
1400             (elmo-msgdb-append-to-killed-list folder (car diff-2)))
1401           (setq delete-list (cadr diff))
1402           (if (or (equal diff '(nil nil))
1403                   (equal diff '(nil))
1404                   (and (eq (length (car diff)) 0)
1405                        (eq (length (cadr diff)) 0)))
1406               (progn
1407                 (elmo-folder-update-number folder)
1408                 (elmo-folder-process-crosspost folder)
1409                 0 ; no updates.
1410                 )
1411             (if delete-list (elmo-msgdb-delete-msgs
1412                              (elmo-folder-msgdb folder) delete-list))
1413             (when new-list
1414               (setq new-msgdb (elmo-folder-msgdb-create
1415                                folder new-list seen-list))
1416               (elmo-msgdb-change-mark (elmo-folder-msgdb folder)
1417                                       elmo-msgdb-new-mark
1418                                       elmo-msgdb-unread-uncached-mark)
1419               ;; Clear seen-list.
1420               (if (elmo-folder-persistent-p folder)
1421                   (setq seen-list (elmo-msgdb-seen-save
1422                                    (elmo-folder-msgdb-path folder) nil)))
1423               (setq before-append nil)
1424               (setq crossed (elmo-folder-append-msgdb folder new-msgdb))
1425               ;; process crosspost.
1426               ;; Return a cons cell of (NUMBER-CROSSPOSTS . NEW-MARK-ALIST).
1427               (elmo-folder-process-crosspost folder)
1428               (elmo-folder-set-message-modified-internal folder t)
1429               (elmo-folder-set-mark-modified-internal folder t))
1430             ;; return value.
1431             (or crossed 0)))
1432       (quit
1433        ;; Resume to the original status.
1434        (if before-append
1435            (elmo-folder-set-msgdb-internal folder old-msgdb))
1436        (elmo-folder-set-killed-list-internal folder killed-list)
1437        nil))))
1438
1439 (defun elmo-folder-messages (folder)
1440   "Return number of messages in the FOLDER."
1441   (length
1442    (elmo-msgdb-get-number-alist
1443     (elmo-folder-msgdb folder))))
1444
1445 (defun elmo-msgdb-load (folder &optional silent)
1446   (unless silent
1447     (message "Loading msgdb for %s..." (elmo-folder-name-internal folder)))
1448   (let ((msgdb (elmo-load-msgdb (elmo-folder-msgdb-path folder))))
1449     (elmo-folder-set-info-max-by-numdb folder
1450                                        (elmo-msgdb-get-number-alist msgdb))
1451     
1452     (unless silent
1453       (message "Loading msgdb for %s...done"
1454                (elmo-folder-name-internal folder)))
1455     msgdb))
1456   
1457 (defun elmo-msgdb-delete-path (folder)
1458   (let ((path (elmo-folder-msgdb-path folder)))
1459     (if (file-directory-p path)
1460         (elmo-delete-directory path t))))
1461
1462 (defun elmo-msgdb-rename-path (old-folder new-folder)
1463   (let* ((old (directory-file-name (elmo-folder-msgdb-path old-folder)))
1464          (new (directory-file-name (elmo-folder-msgdb-path new-folder)))
1465          (new-dir (directory-file-name (file-name-directory new))))
1466     (if (not (file-directory-p old))
1467         ()
1468       (if (file-exists-p new)
1469           (error "Already exists directory: %s" new)
1470         (if (not (file-exists-p new-dir))
1471             (elmo-make-directory new-dir))
1472         (rename-file old new)))))
1473
1474 (defun elmo-setup-subscribed-newsgroups (groups)
1475   "Setup subscribed newsgroups.
1476 GROUPS is a list of newsgroup name string.
1477 Return a hashtable for newsgroups."
1478   (let ((hashtb (or elmo-newsgroups-hashtb
1479                     (setq elmo-newsgroups-hashtb
1480                           (elmo-make-hash (length groups))))))
1481     (dolist (group groups)
1482       (or (elmo-get-hash-val group hashtb)
1483           (elmo-set-hash-val group nil hashtb)))
1484     (setq elmo-newsgroups-hashtb hashtb)))
1485
1486 (defvar elmo-crosspost-message-alist-modified nil)
1487 (defun elmo-crosspost-message-alist-load ()
1488   "Load crosspost message alist."
1489   (setq elmo-crosspost-message-alist (elmo-crosspost-alist-load))
1490   (setq elmo-crosspost-message-alist-modified nil))
1491
1492 (defun elmo-crosspost-message-alist-save ()
1493   "Save crosspost message alist."
1494   (when elmo-crosspost-message-alist-modified
1495     (let ((alist elmo-crosspost-message-alist)
1496           newsgroups)
1497       (while alist
1498         (setq newsgroups
1499               (elmo-delete-if
1500                '(lambda (x)
1501                   (not (intern-soft x elmo-newsgroups-hashtb)))
1502                (nth 1 (car alist))))
1503         (if newsgroups
1504             (setcar (cdar alist) newsgroups)
1505           (setq elmo-crosspost-message-alist
1506                 (delete (car alist) elmo-crosspost-message-alist)))
1507         (setq alist (cdr alist)))
1508       (elmo-crosspost-alist-save elmo-crosspost-message-alist)
1509       (setq elmo-crosspost-message-alist-modified nil))))
1510
1511 (defun elmo-folder-make-temporary-directory (folder)
1512   ;; Make a temporary directory for FOLDER.
1513   (let ((temp-dir (make-temp-name
1514                    (concat
1515                     (file-name-as-directory (elmo-folder-msgdb-path folder))
1516                     "elmo"))))
1517     (elmo-make-directory temp-dir)
1518     temp-dir))
1519
1520 (defun elmo-init ()
1521   "Initialize ELMO module."
1522   (elmo-crosspost-message-alist-load)
1523   (elmo-resque-obsolete-variables)
1524   (elmo-dop-queue-load))
1525
1526 (defun elmo-quit ()
1527   "Quit and cleanup ELMO."
1528   (elmo-crosspost-message-alist-save)
1529   (elmo-dop-queue-save)
1530   ;; Not implemented yet.
1531   (let ((types elmo-folder-type-alist)
1532         class)
1533     (while types
1534       (setq class
1535             (luna-find-class
1536              (intern (format "elmo-%s-folder" (symbol-name (cdr (car types)))))))
1537       ;; Call all folder's `elmo-quit' method.
1538       (if class
1539           (dolist (func (luna-class-find-functions class 'elmo-quit))
1540             (funcall func nil)))
1541       (setq types (cdr types)))))
1542
1543
1544 ;;; Define folders.
1545 (elmo-define-folder ?% 'imap4)
1546 (elmo-define-folder ?-  'nntp)
1547 (elmo-define-folder ?\+ 'localdir)
1548 (elmo-define-folder ?\* 'multi)
1549 (elmo-define-folder ?\/ 'filter)
1550 (elmo-define-folder ?\$ 'archive)
1551 (elmo-define-folder ?&  'pop3)
1552 (elmo-define-folder ?=  'localnews)
1553 (elmo-define-folder ?|  'pipe)
1554 (elmo-define-folder ?.  'maildir)
1555 (elmo-define-folder ?'  'internal)
1556 (elmo-define-folder ?\[  'nmz)
1557 (elmo-define-folder ?@  'shimbun)
1558
1559 ;;; Obsolete variables.
1560 (elmo-define-obsolete-variable 'elmo-default-imap4-mailbox
1561                                'elmo-imap4-default-mailbox)
1562 (elmo-define-obsolete-variable 'elmo-default-imap4-server
1563                                'elmo-imap4-default-server)
1564 (elmo-define-obsolete-variable 'elmo-default-imap4-authenticate-type
1565                                'elmo-imap4-default-authenticate-type)
1566 (elmo-define-obsolete-variable 'elmo-default-imap4-user
1567                                'elmo-imap4-default-user)
1568 (elmo-define-obsolete-variable 'elmo-default-imap4-port
1569                                'elmo-imap4-default-port)
1570 (elmo-define-obsolete-variable 'elmo-default-imap4-stream-type
1571                                'elmo-imap4-default-stream-type)
1572 (elmo-define-obsolete-variable 'elmo-default-nntp-server
1573                                'elmo-nntp-default-server)
1574 (elmo-define-obsolete-variable 'elmo-default-nntp-user
1575                                'elmo-nntp-default-user)
1576 (elmo-define-obsolete-variable 'elmo-default-nntp-port
1577                                'elmo-nntp-default-port)
1578 (elmo-define-obsolete-variable 'elmo-default-nntp-stream-type
1579                                'elmo-nntp-default-stream-type)
1580 (elmo-define-obsolete-variable 'elmo-default-pop3-server
1581                                'elmo-pop3-default-server)
1582 (elmo-define-obsolete-variable 'elmo-default-pop3-user
1583                                'elmo-pop3-default-user)
1584 (elmo-define-obsolete-variable 'elmo-default-pop3-authenticate-type
1585                                'elmo-pop3-default-authenticate-type)
1586 (elmo-define-obsolete-variable 'elmo-default-pop3-port
1587                                'elmo-pop3-default-port)
1588 (elmo-define-obsolete-variable 'elmo-default-pop3-stream-type
1589                                'elmo-pop3-default-stream-type)
1590 (elmo-define-obsolete-variable 'elmo-cache-dirname
1591                                'elmo-cache-directory)
1592 (elmo-define-obsolete-variable 'elmo-msgdb-dir
1593                                'elmo-msgdb-directory)
1594
1595 ;; Obsolete functions.
1596 ;; 2001-12-11: *-dir -> *-directory
1597 (defalias 'elmo-folder-make-temp-dir 'elmo-folder-make-temporary-directory)
1598 (make-obsolete 'elmo-folder-make-temp-dir
1599                'elmo-folder-make-temporary-directory)
1600
1601
1602 ;; autoloads
1603 (autoload 'elmo-dop-queue-flush "elmo-dop")
1604
1605 (require 'product)
1606 (product-provide (provide 'elmo) (require 'elmo-version))
1607
1608 ;;; elmo.el ends here