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