* wl-mime.el (wl-mime-decrypt-application/pgp-encrypted): New
[elisp/wanderlust.git] / wl / wl-mime.el
1 ;;; wl-mime.el --- SEMI implementations of MIME processing on Wanderlust.
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 Wanderlust (Yet Another Message Interface on Emacsen).
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 'mime-view)
33 (require 'mime-edit)
34 (require 'mime-play)
35 (require 'elmo)
36
37 (eval-when-compile
38   (defalias-maybe 'Meadow-version 'ignore))
39
40 (defvar xemacs-betaname)
41 (defvar xemacs-codename)
42 (defvar enable-multibyte-characters)
43 (defvar mule-version)
44
45 ;;; Draft
46
47 (defalias 'wl-draft-editor-mode 'mime-edit-mode)
48
49 (defalias 'wl-draft-decode-message-in-buffer
50   'mime-edit-decode-message-in-buffer)
51
52 (defun wl-draft-yank-current-message-entity ()
53   "Yank currently displayed message entity.
54 By setting following-method as yank-content."
55   (let ((wl-draft-buffer (current-buffer))
56         (mime-view-following-method-alist
57          (list (cons 'wl-original-message-mode
58                      (function wl-draft-yank-to-draft-buffer))))
59         (mime-preview-following-method-alist
60          (list (cons 'wl-original-message-mode
61                      (function wl-draft-yank-to-draft-buffer)))))
62     (if (get-buffer (wl-current-message-buffer))
63         (save-excursion
64           (save-restriction
65             (set-buffer (wl-current-message-buffer))
66             (widen)
67             (mime-preview-follow-current-entity))))))
68
69 (defalias 'wl-draft-enclose-digest-region 'mime-edit-enclose-digest-region)
70
71 (defun wl-draft-preview-message ()
72   "Preview editing message."
73   (interactive)
74   (let* (recipients-message
75          (current-point (point))
76          (config-exec-flag wl-draft-config-exec-flag)
77          (parent-folder wl-draft-parent-folder)
78          (mime-display-header-hook 'wl-highlight-headers)
79          (mime-header-encode-method-alist
80           (append
81            '((wl-draft-eword-encode-address-list
82               .  (To Cc Bcc Resent-To Resent-Cc Bcc Resent-Bcc)))
83            (if (boundp 'mime-header-encode-method-alist)
84                (symbol-value 'mime-header-encode-method-alist))))
85          mime-view-ignored-field-list   ; all header.
86          (mime-edit-translate-buffer-hook
87           (append
88            (list
89             (function
90              (lambda ()
91                (let ((wl-draft-config-exec-flag config-exec-flag)
92                      (wl-draft-parent-folder parent-folder))
93                  (goto-char current-point)
94                  (run-hooks 'wl-draft-send-hook)
95                  (setq recipients-message
96                        (condition-case err
97                            (concat "Recipients: "
98                                    (mapconcat
99                                     'identity
100                                     (wl-draft-deduce-address-list
101                                      (current-buffer)
102                                      (point-min)
103                                      (save-excursion
104                                        (goto-char (point-min))
105                                        (re-search-forward
106                                         (concat
107                                          "^"
108                                          (regexp-quote mail-header-separator)
109                                          "$")
110                                         nil t)
111                                        (point)))
112                                     ", "))
113                          (error
114                           (kill-buffer (current-buffer))
115                           (signal (car err) (cdr err)))))))))
116            mime-edit-translate-buffer-hook)))
117     (mime-edit-preview-message)
118     (let ((buffer-read-only nil))
119       (when wl-highlight-body-too
120         (wl-highlight-body))
121       (run-hooks 'wl-draft-preview-message-hook))
122     (message recipients-message)))
123
124 (defalias 'wl-draft-caesar-region  'mule-caesar-region)
125
126 (defalias 'wl-draft-insert-message 'mime-edit-insert-message)
127
128 (defalias 'wl-draft-insert-mail 'mime-edit-insert-mail)
129
130 ;;; Message
131
132 (defun wl-message-decode-mode (outbuf inbuf)
133   (let ((mime-view-content-header-filter-hook 'wl-highlight-headers)
134         (mime-display-header-hook 'wl-highlight-headers))
135     (mime-view-mode nil nil nil inbuf outbuf)))
136
137 (defun wl-message-decode-with-all-header (outbuf inbuf)
138   (let ((mime-view-ignored-field-regexp "^:$")
139         (mime-view-content-header-filter-hook 'wl-highlight-headers)
140         (mime-display-header-hook 'wl-highlight-headers)
141         mime-view-ignored-field-list)
142     (mime-view-mode nil nil nil inbuf outbuf)))
143
144 (defun wl-message-delete-mime-out-buf ()
145   (let (mime-out-buf mime-out-win)
146     (if (setq mime-out-buf (get-buffer mime-echo-buffer-name))
147         (if (setq mime-out-win (get-buffer-window mime-out-buf))
148             (delete-window mime-out-win)))))
149
150 (defun wl-message-request-partial (folder number)
151   (elmo-set-work-buf
152    (elmo-message-fetch (wl-folder-get-elmo-folder folder)
153                        number
154                        (elmo-make-fetch-strategy 'entire)
155                        nil
156                        (current-buffer)
157                        'unread)
158    (mime-parse-buffer nil)))
159
160 (defalias 'wl-message-read              'mime-preview-scroll-up-entity)
161 (defalias 'wl-message-next-content      'mime-preview-move-to-next)
162 (defalias 'wl-message-prev-content      'mime-preview-move-to-previous)
163 (defalias 'wl-message-play-content      'mime-preview-play-current-entity)
164 (defalias 'wl-message-extract-content   'mime-preview-extract-current-entity)
165 (defalias 'wl-message-quit              'mime-preview-quit)
166 (defalias 'wl-message-button-dispatcher-internal
167   'mime-button-dispatcher)
168
169 ;;; Summary
170 (defun wl-summary-burst-subr (message-entity target number)
171   ;; returns new number.
172   (let (content-type entity)
173     (setq content-type (mime-entity-content-type message-entity))
174     (cond ((eq (cdr (assq 'type content-type)) 'multipart)
175            (dolist (entity (mime-entity-children message-entity))
176              (setq number (wl-summary-burst-subr
177                            entity
178                            target
179                            number))))
180           ((and (eq (cdr (assq 'type content-type)) 'message)
181                 (eq (cdr (assq 'subtype content-type)) 'rfc822))
182            (message "Bursting...%s" (setq number (+ 1 number)))
183            (setq entity
184                  (car (mime-entity-children message-entity)))
185            (with-temp-buffer
186              (insert (mime-entity-body message-entity))
187              (elmo-folder-append-buffer
188               target
189               (mime-entity-fetch-field entity "Message-ID")))))
190     number))
191
192 (defun wl-summary-burst ()
193   ""
194   (interactive)
195   (let ((raw-buf (wl-summary-get-original-buffer))
196         (view-buf wl-message-buffer)
197         children message-entity content-type target)
198     (save-excursion
199       (setq target wl-summary-buffer-elmo-folder)
200       (while (not (elmo-folder-writable-p target))
201         (setq target
202               (wl-summary-read-folder wl-default-folder "to extract to")))
203       (wl-summary-set-message-buffer-or-redisplay)
204       (with-current-buffer view-buf
205         (setq message-entity (get-text-property (point-min) 'mime-view-entity)))
206       (when message-entity
207         (message "Bursting...")
208         (with-current-buffer raw-buf
209           (wl-summary-burst-subr message-entity target 0))
210         (message "Bursting...done"))
211       (if (elmo-folder-plugged-p target)
212           (elmo-folder-check target)))
213     (wl-summary-sync-update)))
214
215 ;; internal variable.
216 (defvar wl-mime-save-directory nil "Last saved directory.")
217 ;;; Yet another save method.
218 (defun wl-mime-save-content (entity situation)
219   (let ((filename (read-file-name "Save to file: "
220                                   (expand-file-name
221                                    (or (mime-entity-safe-filename entity)
222                                        ".")
223                                    (or wl-mime-save-directory
224                                        wl-temporary-file-directory)))))
225     (while (file-directory-p filename)
226       (setq filename (read-file-name "Please set filename (not directory): "
227                                      filename)))
228     (if (and (file-exists-p filename)
229              (not (yes-or-no-p (format "File %s exists. Save anyway? "
230                                        filename))))
231         (message "Not saved")
232       (setq wl-mime-save-directory (file-name-directory filename))
233       (mime-write-entity-content entity filename))))
234
235 ;;; Yet another combine method.
236 (defun wl-mime-combine-message/partial-pieces (entity situation)
237   "Internal method for wl to combine message/partial messages automatically."
238   (interactive)
239   (let* ((msgdb (save-excursion
240                   (set-buffer wl-message-buffer-cur-summary-buffer)
241                   (wl-summary-buffer-msgdb)))
242          (mime-display-header-hook 'wl-highlight-headers)
243          (folder wl-message-buffer-cur-folder)
244          (id (or (cdr (assoc "id" situation)) ""))
245          (mother (current-buffer))
246          (summary-buf wl-message-buffer-cur-summary-buffer)
247          subject-id overviews
248          (root-dir (expand-file-name
249                     (concat "m-prts-" (user-login-name))
250                     temporary-file-directory))
251          full-file point)
252     (setq root-dir (concat root-dir "/" (replace-as-filename id)))
253     (setq full-file (concat root-dir "/FULL"))
254     (if (or (file-exists-p full-file)
255             (not (y-or-n-p "Merge partials? ")))
256         (with-current-buffer mother
257           (mime-store-message/partial-piece entity situation)
258           (setq wl-message-buffer-cur-summary-buffer summary-buf)
259           (make-variable-buffer-local 'mime-preview-over-to-next-method-alist)
260           (setq mime-preview-over-to-next-method-alist
261                 (cons (cons 'mime-show-message-mode 'wl-message-exit)
262                       mime-preview-over-to-next-method-alist))
263           (make-variable-buffer-local 'mime-preview-over-to-previous-method-alist)
264           (setq mime-preview-over-to-previous-method-alist
265                 (cons (cons 'mime-show-message-mode 'wl-message-exit)
266                       mime-preview-over-to-previous-method-alist)))
267       (setq subject-id
268             (eword-decode-string
269              (decode-mime-charset-string
270               (mime-entity-read-field entity 'Subject)
271               wl-summary-buffer-mime-charset)))
272       (if (string-match "[0-9\n]+" subject-id)
273           (setq subject-id (substring subject-id 0 (match-beginning 0))))
274       (setq overviews (elmo-msgdb-get-overview msgdb))
275       (catch 'tag
276         (while overviews
277           (when (string-match
278                  (regexp-quote subject-id)
279                  (elmo-msgdb-overview-entity-get-subject (car overviews)))
280             (let* ((message
281                     ;; request message at the cursor in Subject buffer.
282                     (wl-message-request-partial
283                      folder
284                      (elmo-msgdb-overview-entity-get-number
285                       (car overviews))))
286                    (situation (mime-entity-situation message))
287                    (the-id (or (cdr (assoc "id" situation)) "")))
288               (when (string= (downcase the-id)
289                              (downcase id))
290                 (with-current-buffer mother
291                   (mime-store-message/partial-piece message situation))
292                 (if (file-exists-p full-file)
293                     (throw 'tag nil)))))
294           (setq overviews (cdr overviews)))
295         (message "Not all partials found.")))))
296
297 (defun wl-mime-display-text/plain (entity situation)
298   (let ((beg (point)))
299     (mime-display-text/plain entity situation)
300     (wl-highlight-message beg (point-max) t t)))
301
302 (defun wl-mime-display-header (entity situation)
303   (let ((elmo-message-ignored-field-list
304          (if wl-message-buffer-all-header-flag
305              nil
306            wl-message-ignored-field-list))
307         (elmo-message-visible-field-list wl-message-visible-field-list)
308         (elmo-message-sorted-field-list wl-message-sort-field-list))
309     (elmo-mime-insert-header entity situation)
310     (wl-highlight-headers)))
311
312 (defun wl-mime-decrypt-application/pgp-encrypted (entity situation)
313   (let ((summary-buffer wl-message-buffer-cur-summary-buffer)
314         (original-buffer wl-message-buffer-original-buffer))
315     (mime-decrypt-application/pgp-encrypted entity situation)
316     (setq wl-message-buffer-cur-summary-buffer summary-buffer)
317     (setq wl-message-buffer-original-buffer original-buffer)))
318    
319
320 ;;; Setup methods.
321 (defun wl-mime-setup ()
322   (set-alist 'mime-preview-quitting-method-alist
323              'wl-original-message-mode 'wl-message-exit)
324   (set-alist 'mime-view-over-to-previous-method-alist
325              'wl-original-message-mode 'wl-message-exit)
326   (set-alist 'mime-view-over-to-next-method-alist
327              'wl-original-message-mode 'wl-message-exit)
328   (set-alist 'mime-preview-over-to-previous-method-alist
329              'wl-original-message-mode 'wl-message-exit)
330   (set-alist 'mime-preview-over-to-next-method-alist
331              'wl-original-message-mode 'wl-message-exit)
332   (add-hook 'wl-summary-redisplay-hook 'wl-message-delete-mime-out-buf)
333   (add-hook 'wl-message-exit-hook 'wl-message-delete-mime-out-buf)
334
335   (ctree-set-calist-strictly
336    'mime-preview-condition
337    '((type . text) (subtype . plain)
338      (body . visible)
339      (body-presentation-method . wl-mime-display-text/plain)
340      (major-mode . wl-original-message-mode)))
341
342   (ctree-set-calist-strictly
343    'mime-acting-condition
344    '((type . message) (subtype . partial)
345      (method .  wl-mime-combine-message/partial-pieces)
346      (request-partial-message-method . wl-message-request-partial)
347      (major-mode . wl-original-message-mode)))
348
349   (ctree-set-calist-strictly
350    'mime-acting-condition
351    '((type . application) (subtype . pgp-encrypted)
352      (method . wl-mime-decrypt-application/pgp-encrypted)
353      (major-mode . wl-original-message-mode)))
354
355   (ctree-set-calist-strictly
356    'mime-acting-condition
357    '((mode . "extract")
358      (major-mode . wl-original-message-mode)
359      (method . wl-mime-save-content)))
360   (set-alist 'mime-preview-following-method-alist
361              'wl-original-message-mode
362              (function wl-message-follow-current-entity))
363   (set-alist 'mime-view-following-method-alist
364              'wl-original-message-mode
365              (function wl-message-follow-current-entity))
366   (set-alist 'mime-edit-message-inserter-alist
367              'wl-draft-mode (function wl-draft-insert-current-message))
368   (set-alist 'mime-edit-mail-inserter-alist
369              'wl-draft-mode (function wl-draft-insert-get-message))
370   (set-alist 'mime-edit-split-message-sender-alist
371              'wl-draft-mode
372              (cdr (assq 'mail-mode mime-edit-split-message-sender-alist)))
373   (set-alist 'mime-raw-representation-type-alist
374              'wl-original-message-mode 'binary)
375   ;; Sort and highlight header fields.
376   (or wl-message-ignored-field-list
377       (setq wl-message-ignored-field-list
378             mime-view-ignored-field-list))
379   (or wl-message-visible-field-list
380       (setq wl-message-visible-field-list
381             mime-view-visible-field-list))
382   (set-alist 'mime-header-presentation-method-alist
383              'wl-original-message-mode
384              'wl-mime-display-header)
385   ;; To avoid overriding wl-draft-mode-map.
386   (when (boundp 'mime-setup-signature-key-alist)
387     (unless (assq 'wl-draft-mode mime-setup-signature-key-alist)
388       (setq mime-setup-signature-key-alist
389             (cons '(wl-draft-mode . "\C-c\C-w")
390                   mime-setup-signature-key-alist)))))
391
392 (require 'product)
393 (product-provide (provide 'wl-mime) (require 'wl-version))
394
395 ;;; wl-mime.el ends here