Revert last commit (I'm sorry it was my mistake).
[elisp/wanderlust.git] / elmo / elmo-dop.el
1 ;;; elmo-dop.el --- Modules for Disconnected Operations on ELMO.
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 'elmo)
33 (require 'elmo-vars)
34 (require 'elmo-msgdb)
35 (require 'elmo-util)
36 (require 'elmo-localdir)
37
38 ;; global variable.
39 (defvar elmo-dop-queue nil
40   "A list of (folder-name function-to-be-called argument-list).
41 Automatically loaded/saved.")
42
43 (defmacro elmo-make-dop-queue (fname method arguments)
44   "Make a dop queue."
45   (` (vector (, fname) (, method) (, arguments))))
46
47 (defmacro elmo-dop-queue-fname (queue)
48   "Return the folder name string of the QUEUE."
49   (` (aref (, queue) 0)))
50
51 (defmacro elmo-dop-queue-method (queue)
52   "Return the method symbol of the QUEUE."
53   (` (aref (, queue) 1)))
54
55 (defmacro elmo-dop-queue-arguments (queue)
56   "Return the arguments of the QUEUE."
57   (` (aref (, queue) 2)))
58
59 (defun elmo-dop-queue-append (folder method arguments)
60   "Append to disconnected operation queue."
61   (let ((queue (elmo-make-dop-queue (elmo-folder-name-internal folder)
62                                     method arguments)))
63     (setq elmo-dop-queue (nconc elmo-dop-queue (list queue)))))
64
65 (defvar elmo-dop-queue-merge-method-list
66   '(elmo-folder-mark-as-read
67     elmo-folder-unmark-read
68     elmo-folder-mark-as-important
69     elmo-folder-unmark-important))
70
71 (defvar elmo-dop-queue-method-name-alist
72   '((elmo-folder-append-buffer-dop-delayed . "Append")
73     (elmo-folder-delete-messages-dop-delayed . "Delete")
74     (elmo-message-encache . "Encache")
75     (elmo-folder-create-dop-delayed . "Create")
76     (elmo-folder-mark-as-read . "Read")
77     (elmo-folder-unmark-read . "Unread")
78     (elmo-folder-mark-as-important . "Important")
79     (elmo-folder-unmark-important . "Unimportant")))
80
81 (defmacro elmo-dop-queue-method-name (queue)
82   `(cdr (assq (elmo-dop-queue-method ,queue)
83               elmo-dop-queue-method-name-alist)))
84
85 (defun elmo-dop-queue-flush ()
86   "Flush disconnected operations that consern plugged folders."
87   ;; obsolete
88   (unless (or (null elmo-dop-queue)
89               (vectorp (car elmo-dop-queue)))
90     (if (y-or-n-p "\
91 Saved queue is old version(2.6). Clear all pending operations? ")
92         (progn
93           (setq elmo-dop-queue nil)
94           (message "All pending operations are cleared.")
95           (elmo-dop-queue-save))
96       (error "Please use 2.6 or earlier.")))
97   (elmo-dop-queue-merge)
98   (let ((queue-all elmo-dop-queue)
99         queue
100         (count 0)
101         len)
102     (while queue-all
103       (if (elmo-folder-plugged-p
104            (elmo-make-folder (elmo-dop-queue-fname (car queue-all))))
105           (setq queue (append queue (list (car queue-all)))))
106       (setq queue-all (cdr queue-all)))
107     (setq count (length queue))
108     (when (> count 0)
109       (if (elmo-y-or-n-p
110            (format "%d pending operation(s) exists.  Perform now? " count)
111            (not elmo-dop-flush-confirm) t)
112           (progn
113             (message "")
114             (sit-for 0)
115             (let ((queue elmo-dop-queue)
116                   (performed 0)
117                   (i 0)
118                   (num (length elmo-dop-queue))
119                   folder func failure)
120               (while queue
121                 ;; now perform pending processes.
122                 (setq failure nil)
123                 (setq i (+ 1 i))
124                 (message "Flushing queue....%d/%d." i num)
125                 (condition-case err
126                     (progn
127                       (apply (elmo-dop-queue-method (car queue))
128                              (prog1
129                                  (setq folder
130                                        (elmo-make-folder
131                                         (elmo-dop-queue-fname (car queue))))
132                                (elmo-folder-open folder)
133                                (unless (elmo-folder-plugged-p folder)
134                                  (error "Unplugged.")))
135                              (elmo-dop-queue-arguments (car queue)))
136                       (elmo-folder-close folder))
137                   (quit  (setq failure t))
138                   (error (setq failure err)))
139                 (if failure
140                     ();
141                   (setq elmo-dop-queue (delq (car queue) elmo-dop-queue))
142                   (setq performed (+ 1 performed)))
143                 (setq queue (cdr queue)))
144               (message "%d/%d operation(s) are performed successfully."
145                        performed num)
146               (sit-for 0) ;
147               (elmo-dop-queue-save)))
148         ;; when answer=NO against performing dop
149         (if (elmo-y-or-n-p "Clear these pending operations? "
150                            (not elmo-dop-flush-confirm) t)
151             (progn
152               (while queue
153                 (setq elmo-dop-queue (delq (car queue) elmo-dop-queue))
154                 (setq queue (cdr queue)))
155               (message "Pending operations are cleared.")
156               (elmo-dop-queue-save))
157           (message "")))
158       count)))
159
160 (defun elmo-dop-queue-merge ()
161   (let ((queue elmo-dop-queue)
162         new-queue match-queue que)
163     (while (setq que (car queue))
164       (if (and
165            (memq (elmo-dop-queue-method que)
166                  elmo-dop-queue-merge-method-list)
167            (setq match-queue
168                  (car (delete 
169                        nil
170                        (mapcar
171                         (lambda (nqueue)
172                           (if (and
173                                (string= (elmo-dop-queue-fname que)
174                                         (elmo-dop-queue-fname nqueue))
175                                (string= (elmo-dop-queue-method que)
176                                         (elmo-dop-queue-method nqueue)))
177                               nqueue))
178                         new-queue)))))
179           (setcar (elmo-dop-queue-arguments match-queue)
180                   (append (car (elmo-dop-queue-arguments match-queue))
181                           (car (elmo-dop-queue-arguments que))))
182         (setq new-queue (nconc new-queue (list que))))
183       (setq queue (cdr queue)) )
184     (setq elmo-dop-queue new-queue)))
185
186 ;;; dop spool folder
187 (defmacro elmo-dop-spool-folder (folder)
188   "Return a spool folder for disconnected operations
189 which is corresponded to the FOLDER."
190   (` (elmo-make-folder
191       (concat "+" (expand-file-name "spool" (elmo-folder-msgdb-path
192                                              (, folder)))))))
193
194 (defun elmo-dop-spool-folder-append-buffer (folder)
195   "Append current buffer content to the dop spool folder.
196 FOLDER is the folder structure.
197 Return a message number."
198   (setq folder (elmo-dop-spool-folder folder))
199   (unless (elmo-folder-exists-p folder)
200     (elmo-folder-create folder))
201   (let ((new-number (1+ (car (elmo-folder-status folder)))))
202     ;; dop folder is a localdir folder.
203     (write-region-as-binary (point-min) (point-max)
204                           (expand-file-name
205                            (int-to-string new-number)
206                            (elmo-localdir-folder-directory-internal folder))
207                           nil 'no-msg)
208     new-number))
209
210
211 (defun elmo-dop-spool-folder-list-messages (folder)
212   "List messages in the dop spool folder.
213 FOLDER is the folder structure."
214   (setq folder (elmo-dop-spool-folder folder))
215   (if (elmo-folder-exists-p folder)
216       (elmo-folder-list-messages folder)))
217
218 (defun elmo-dop-list-deleting-messages (folder)
219   "List messages which are on the deleting queue for the folder.
220 FOLDER is the folder structure."
221   (let (messages)
222     (dolist (queue elmo-dop-queue)
223       (if (and (string= (elmo-dop-queue-fname queue)
224                         (elmo-folder-name-internal folder))
225                (eq (elmo-dop-queue-method queue)
226                    'elmo-folder-delete-messages-dop-delayed))
227           (setq messages (nconc messages
228                                 (mapcar
229                                  'car
230                                  (car (elmo-dop-queue-arguments queue)))))))))
231
232 ;;; DOP operations.
233 (defsubst elmo-folder-append-buffer-dop (folder unread &optional number)
234   (elmo-dop-queue-append
235    folder 'elmo-folder-append-buffer-dop-delayed
236    (list unread
237          (elmo-dop-spool-folder-append-buffer
238           folder)
239          number)))
240
241 (defsubst elmo-folder-delete-messages-dop (folder numbers)
242   (let ((spool-folder (elmo-dop-spool-folder folder))
243         queue)
244     (dolist (number numbers)
245       (if (< number 0)
246           (elmo-folder-delete-messages spool-folder
247                                        (list (abs number))) ; delete from queue
248         (setq queue (cons number queue))))
249     (when queue
250       (elmo-dop-queue-append folder 'elmo-folder-delete-messages-dop-delayed
251                              (list
252                               (mapcar
253                                (lambda (number)
254                                  (cons number (elmo-message-field
255                                                folder number 'message-id)))
256                                queue))))
257     t))
258
259 (defsubst elmo-message-encache-dop (folder number &optional read)
260   (elmo-dop-queue-append folder 'elmo-message-encache (list number read)))
261
262 (defsubst elmo-folder-create-dop (folder)
263   (elmo-dop-queue-append folder 'elmo-folder-create-dop-delayed nil))
264
265 (defsubst elmo-folder-mark-as-read-dop (folder numbers)
266   (elmo-dop-queue-append folder 'elmo-folder-mark-as-read (list numbers)))
267
268 (defsubst elmo-folder-unmark-read-dop (folder numbers)
269   (elmo-dop-queue-append folder 'elmo-folder-unmark-read (list numbers)))
270
271 (defsubst elmo-folder-mark-as-important-dop (folder numbers)
272   (elmo-dop-queue-append folder 'elmo-folder-mark-as-important (list numbers)))
273
274 (defsubst elmo-folder-unmark-important-dop (folder numbers)
275   (elmo-dop-queue-append folder 'elmo-folder-unmark-important (list numbers)))
276
277 ;;; Execute as subsutitute for plugged operation.
278 (defun elmo-folder-status-dop (folder)
279   (let* ((number-alist (elmo-msgdb-number-load
280                         (elmo-folder-msgdb-path folder)))
281          (number-list (mapcar 'car number-alist))
282          (spool-folder (elmo-dop-spool-folder folder))
283          spool-length
284          (i 0)
285          max-num)
286     (setq spool-length
287           (or (car (if (elmo-folder-exists-p spool-folder)
288                        (elmo-folder-status spool-folder)))
289               0))
290     (setq max-num
291           (or (nth (max (- (length number-list) 1) 0) number-list)
292               0))
293     (cons (+ max-num spool-length) (+ (length number-list) spool-length))))
294
295 ;;; Delayed operation (executed at online status).
296 (defun elmo-folder-append-buffer-dop-delayed (folder unread number set-number)
297   (let ((spool-folder (elmo-dop-spool-folder folder))
298         failure saved dequeued)
299     (with-temp-buffer
300       (if (elmo-message-fetch spool-folder number
301                               (elmo-make-fetch-strategy 'entire)
302                               nil (current-buffer) 'unread)
303           (condition-case nil
304               (setq failure (not
305                              (elmo-folder-append-buffer
306                               folder unread set-number)))
307             (error (setq failure t)))
308         (setq dequeued t)) ; Already deletef from queue.
309       (when failure
310         ;; Append failed...
311         (setq saved (elmo-folder-append-buffer
312                      (elmo-make-folder elmo-lost+found-folder)
313                      unread set-number)))
314       (if (and (not dequeued)    ; if dequeued, no need to delete.
315                (or (not failure) ; succeed
316                    saved))       ; in lost+found
317           (elmo-folder-delete-messages spool-folder (list number)))
318       t)))
319
320 (defun elmo-folder-delete-messages-dop-delayed (folder number-alist)
321   (elmo-folder-delete-messages
322    folder
323    ;; messages are deleted only if message-id is not changed.
324    (mapcar 'car
325            (elmo-delete-if
326             (lambda (pair)
327               (not (string=
328                     (cdr pair)
329                     (elmo-message-fetch-field folder (car pair)
330                                               'message-id))))
331             number-alist))))
332
333 (defun elmo-folder-create-dop-delayed (folder)
334   (unless (elmo-folder-exists-p folder)
335     (elmo-folder-create folder)))
336
337 ;;; Util
338 (defun elmo-dop-msgdb (msgdb)
339   (list (mapcar (function
340                  (lambda (x)
341                    (elmo-msgdb-overview-entity-set-number
342                     x
343                     (* -1
344                        (elmo-msgdb-overview-entity-get-number x)))))
345                 (nth 0 msgdb))
346         (mapcar (function
347                  (lambda (x) (cons
348                               (* -1 (car x))
349                               (cdr x))))
350                 (nth 1 msgdb))
351         (mapcar (function
352                  (lambda (x) (cons
353                               (* -1 (car x))
354                               (cdr x)))) (nth 2 msgdb))))
355
356 (require 'product)
357 (product-provide (provide 'elmo-dop) (require 'elmo-version))
358
359 ;;; elmo-dop.el ends here