Update.
[elisp/wanderlust.git] / wl / wl-mime.el
1 ;;; wl-mime.el -- SEMI implementations of MIME processing on Wanderlust.
2
3 ;; Copyright 1998,1999,2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7 ;; Time-stamp: <2000-04-20 17:43:19 teranisi>
8
9 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
10
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15 ;;
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20 ;;
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25 ;;
26
27 ;;; Commentary:
28 ;; 
29
30 ;;; Code:
31 ;; 
32
33 (require 'mime-view)
34 (require 'mime-edit)
35 (require 'mime-play)
36 (require 'mmelmo)
37
38 (eval-when-compile
39   (defun-maybe Meadow-version ())
40   (mapcar
41    (function
42     (lambda (symbol)
43       (unless (boundp symbol)
44         (set (make-local-variable symbol) nil))))
45    '(xemacs-betaname
46      xemacs-codename
47      enable-multibyte-characters
48      mule-version)))
49
50 ;;; Draft
51
52 (defalias 'wl-draft-editor-mode 'mime-edit-mode)
53
54 (defalias 'wl-draft-decode-message-in-buffer 
55   'mime-edit-decode-message-in-buffer)
56
57 (defun wl-draft-yank-current-message-entity ()
58   "Yank currently displayed message entity. 
59 By setting following-method as yank-content."
60   (let ((wl-draft-buffer (current-buffer))
61         (mime-view-following-method-alist 
62          (list (cons 'mmelmo-original-mode 
63                      (function wl-draft-yank-to-draft-buffer))))
64         (mime-preview-following-method-alist 
65          (list (cons 'mmelmo-original-mode
66                      (function wl-draft-yank-to-draft-buffer)))))
67     (if (get-buffer (wl-current-message-buffer))
68         (save-excursion
69           (save-restriction
70             (set-buffer (wl-current-message-buffer))
71             (widen)
72             (mime-preview-follow-current-entity))))))
73
74 (defalias 'wl-draft-enclose-digest-region 'mime-edit-enclose-digest-region)
75
76 ;; SEMI 1.13.5 or later.
77 ;; (mime-display-message 
78 ;;  MESSAGE &optional 
79 ;;  PREVIEW-BUFFER MOTHER DEFAULT-KEYMAP-OR-FUNCTION ORIGINAL-MAJOR-MODE)
80 ;; SEMI 1.13.4 or earlier.
81 ;; (mime-display-message 
82 ;;  MESSAGE &optional 
83 ;;  PREVIEW-BUFFER MOTHER DEFAULT-KEYMAP-OR-FUNCTION)
84 (static-if (or (and mime-user-interface-product
85                     (eq (nth 0 (aref mime-user-interface-product 1)) 1)
86                     (>= (nth 1 (aref mime-user-interface-product 1)) 14))
87                (and mime-user-interface-product
88                     (eq (nth 0 (aref mime-user-interface-product 1)) 1)
89                     (eq (nth 1 (aref mime-user-interface-product 1)) 13)
90                     (>= (nth 2 (aref mime-user-interface-product 1)) 5)))
91     ;; Has original-major-mode optional argument.
92     (defalias 'wl-mime-display-message 'mime-display-message)
93   (defmacro wl-mime-display-message (message &optional
94                                              preview-buffer mother
95                                              default-keymap-or-function
96                                              original-major-mode)
97     (` (mime-display-message (, message) (, preview-buffer) (, mother)
98                              (, default-keymap-or-function))))
99   ;; User agent field of XEmacs has problem on SEMI 1.13.4 or earlier.
100   (setq mime-edit-user-agent-value
101         (concat 
102          (mime-product-name mime-user-interface-product) "/"
103          (mapconcat 
104           #'number-to-string
105           (mime-product-version mime-user-interface-product) ".")
106          " (" (mime-product-code-name mime-user-interface-product)
107          ") " (mime-product-name mime-library-product)
108          "/" (mapconcat #'number-to-string
109                         (mime-product-version mime-library-product) ".")
110          " (" (mime-product-code-name mime-library-product) ") "
111          (if (featurep 'xemacs)
112              (concat 
113               (if (featurep 'mule) "MULE")
114               " XEmacs"
115               (if (string-match "\\s +\\((\\|\\\"\\)" emacs-version)
116                   (concat "/" (substring emacs-version 0
117                                          (match-beginning 0))
118                           (if (and (boundp 'xemacs-betaname)
119                                    ;; It does not exist in XEmacs
120                                    ;; versions prior to 20.3.
121                                    xemacs-betaname)
122                               (concat " " xemacs-betaname)
123                             "")
124                           " (" xemacs-codename ") ("
125                           system-configuration ")")
126                 " (" emacs-version ")"))
127            (let ((ver (if (string-match "\\.[0-9]+$" emacs-version)
128                           (substring emacs-version 0 (match-beginning 0))
129                         emacs-version)))
130              (if (featurep 'mule)
131                  (if (boundp 'enable-multibyte-characters)
132                      (concat "Emacs/" ver
133                              " (" system-configuration ")"
134                              (if enable-multibyte-characters
135                                  (concat " MULE/" mule-version)
136                                " (with unibyte mode)")
137                              (if (featurep 'meadow)
138                                  (let ((mver (Meadow-version)))
139                                    (if (string-match "^Meadow-" mver)
140                                        (concat " Meadow/"
141                                                (substring mver
142                                                           (match-end 0)))))))
143                    (concat "MULE/" mule-version
144                            " (based on Emacs " ver ")"))
145                (concat "Emacs/" ver " (" system-configuration ")")))))))
146
147 ;; FLIM 1.12.7
148 ;; (mime-read-field FIELD-NAME &optional ENTITY)
149 ;; FLIM 1.13.2 or later
150 ;; (mime-entity-read-field ENTITY FIELD-NAME)
151 (static-if (fboundp 'mime-entity-read-field)
152     (defalias 'wl-mime-entity-read-field 'mime-entity-read-field)
153   (defmacro wl-mime-entity-read-field (entity field-name)
154     (` (mime-read-field (, field-name) (, entity)))))
155
156 (defun wl-draft-preview-message ()
157   (interactive)
158   (let ((mime-display-header-hook 'wl-highlight-headers)
159         mime-view-ignored-field-list ; all header.
160         (mime-edit-translate-buffer-hook (append
161                                           (list 'wl-draft-config-exec)
162                                           mime-edit-translate-buffer-hook)))
163     (mime-edit-preview-message)
164     (let ((buffer-read-only nil))
165       (when wl-highlight-body-too 
166         (wl-highlight-body))
167       (run-hooks 'wl-draft-preview-message-hook))))
168
169 (defalias 'wl-draft-caesar-region  'mule-caesar-region)
170
171 (defalias 'wl-draft-insert-message 'mime-edit-insert-message)
172
173 (defalias 'wl-draft-insert-mail 'mime-edit-insert-mail)
174
175 ;;; Message
176
177 (defun wl-message-decode-mode (outbuf inbuf)
178   (let ((mime-view-content-header-filter-hook 'wl-highlight-headers)
179         (mime-display-header-hook 'wl-highlight-headers))
180     (mime-view-mode nil nil nil inbuf outbuf)))
181
182 (defun wl-message-decode-with-all-header (outbuf inbuf)
183   (let ((mime-view-ignored-field-regexp "^:$")
184         (mime-view-content-header-filter-hook 'wl-highlight-headers)
185         (mime-display-header-hook 'wl-highlight-headers)
186         mime-view-ignored-field-list)
187     (mime-view-mode nil nil nil inbuf outbuf)))
188
189 (defun wl-message-delete-mime-out-buf ()
190   (let (mime-out-buf mime-out-win)
191     (if (setq mime-out-buf (get-buffer mime-echo-buffer-name))
192         (if (setq mime-out-win (get-buffer-window mime-out-buf))
193             (delete-window mime-out-win)))))
194
195 (defun wl-message-request-partial (folder number)
196   (elmo-set-work-buf
197    (elmo-read-msg-no-cache folder number (current-buffer))
198    (mime-parse-buffer nil))); 'mime-buffer-entity)))
199
200 (defalias 'wl-message-read            'mime-preview-scroll-up-entity)
201 (defalias 'wl-message-next-content    'mime-preview-move-to-next)
202 (defalias 'wl-message-prev-content    'mime-preview-move-to-previous)
203 (defalias 'wl-message-play-content    'mime-preview-play-current-entity)
204 (defalias 'wl-message-extract-content 'mime-preview-extract-current-entity)
205 (defalias 'wl-message-quit            'mime-preview-quit)
206 (defalias 'wl-message-button-dispatcher 'mime-button-dispatcher)
207
208 ;;; Summary
209 (defun wl-summary-burst-subr (children target number)
210   ;; returns new number.
211   (let (content-type message-entity granch)
212     (while children
213       (setq content-type (mime-entity-content-type (car children)))
214       (if (eq (cdr (assq 'type content-type)) 'multipart)
215           (setq number (wl-summary-burst-subr 
216                         (mime-entity-children (car children))
217                         target
218                         number))
219         (when (and (eq (cdr (assq 'type content-type)) 'message)
220                    (eq (cdr (assq 'subtype content-type)) 'rfc822))
221           (message (format "Bursting...%s" (setq number (+ 1 number))))
222           (setq message-entity
223                 (car (mime-entity-children (car children))))
224           (save-restriction
225             (narrow-to-region (mime-entity-point-min message-entity)
226                               (mime-entity-point-max message-entity))
227             (elmo-append-msg target
228                              ;;(mime-entity-content (car children))))
229                              (buffer-substring (point-min) (point-max))
230                              (std11-field-body "Message-ID")))))
231       (setq children (cdr children)))
232     number))
233
234 (defun wl-summary-burst ()
235   (interactive)
236   (let ((raw-buf (wl-message-get-original-buffer))
237         children message-entity content-type target)
238     (save-excursion
239       (setq target wl-summary-buffer-folder-name)
240       (while (not (elmo-folder-writable-p target))
241         (setq target 
242               (wl-summary-read-folder wl-default-folder "to extract to")))
243       (wl-summary-set-message-buffer-or-redisplay)
244       (save-excursion
245         (set-buffer (get-buffer wl-message-buf-name))
246         (setq message-entity (get-text-property (point-min) 'mime-view-entity)))
247       (set-buffer raw-buf)
248       (setq children (mime-entity-children message-entity))
249       (when children
250         (message "Bursting...")
251         (wl-summary-burst-subr children target 0)
252         (message "Bursting...done."))
253       (if (elmo-folder-plugged-p target)
254           (elmo-commit target)))
255     (wl-summary-sync-update3)))
256
257 ;; internal variable.
258 (defvar wl-mime-save-dir nil "Last saved directory.")
259 ;;; Yet another save method.
260 (defun wl-mime-save-content (entity situation)
261   (let ((filename (read-file-name "Save to file: "
262                                   (expand-file-name 
263                                    (or (mime-entity-safe-filename entity)
264                                        ".")
265                                    (or wl-mime-save-dir
266                                        wl-tmp-dir)))))
267     (while (file-directory-p filename)
268       (setq filename (read-file-name "Please set filename (not directory): "
269                                      filename)))
270     (if (file-exists-p filename)
271         (or (yes-or-no-p (format "File %s exists. Save anyway? " filename))
272             (error "Not saved")))
273     (setq wl-mime-save-dir (file-name-directory filename))
274     (mime-write-entity-content entity filename)))
275
276 ;;; Yet another combine method.
277 (defun wl-mime-combine-message/partial-pieces (entity situation)
278   "Internal method for wl to combine message/partial messages
279 automatically."
280   (interactive)
281   (let* ((msgdb (save-excursion 
282                   (set-buffer wl-message-buffer-cur-summary-buffer)
283                   wl-summary-buffer-msgdb))
284          (mime-display-header-hook 'wl-highlight-headers)
285          (folder wl-message-buffer-cur-folder)
286          (id (or (cdr (assoc "id" situation)) ""))
287          (mother (current-buffer))
288          subject-id overviews
289          (root-dir (expand-file-name
290                     (concat "m-prts-" (user-login-name))
291                     temporary-file-directory))
292          full-file)
293     (setq root-dir (concat root-dir "/" (replace-as-filename id)))
294     (setq full-file (concat root-dir "/FULL"))
295     (if (or (file-exists-p full-file)
296             (not (y-or-n-p "Merge partials?")))
297         (with-current-buffer mother
298           (mime-store-message/partial-piece entity situation))
299       (setq subject-id 
300             (eword-decode-string
301              (decode-mime-charset-string 
302               (wl-mime-entity-read-field entity 'Subject)
303               wl-summary-buffer-mime-charset)))
304       (if (string-match "[0-9\n]+" subject-id)
305           (setq subject-id (substring subject-id 0 (match-beginning 0))))
306       (setq overviews (elmo-msgdb-get-overview msgdb))
307       (catch 'tag
308         (while overviews
309           (when (string-match
310                  (regexp-quote subject-id)
311                  (elmo-msgdb-overview-entity-get-subject (car overviews)))
312             (let* ((message
313                     ;; request message at the cursor in Subject buffer.
314                     (wl-message-request-partial
315                      folder
316                      (elmo-msgdb-overview-entity-get-number (car overviews))))
317                    (situation (mime-entity-situation message))
318                    (the-id (or (cdr (assoc "id" situation)) "")))
319               (when (string= (downcase the-id) 
320                              (downcase id))
321                 (with-current-buffer mother
322                   (mime-store-message/partial-piece message situation))
323                 (if (file-exists-p full-file)
324                     (throw 'tag nil)))))
325           (setq overviews (cdr overviews)))
326         (message "Not all partials found.")))))
327
328 ;;; Setup methods.
329 (defun wl-mime-setup ()
330   (set-alist 'mime-preview-quitting-method-alist
331              'mmelmo-original-mode 'wl-message-exit)
332   (set-alist 'mime-view-over-to-previous-method-alist
333              'mmelmo-original-mode 'wl-message-exit)
334   (set-alist 'mime-view-over-to-next-method-alist 
335              'mmelmo-original-mode 'wl-message-exit)
336   (set-alist 'mime-preview-over-to-previous-method-alist
337              'mmelmo-original-mode 'wl-message-exit)
338   (set-alist 'mime-preview-over-to-next-method-alist
339              'mmelmo-original-mode 'wl-message-exit)
340   (add-hook 'wl-summary-redisplay-hook 'wl-message-delete-mime-out-buf)
341   (add-hook 'wl-message-exit-hook 'wl-message-delete-mime-out-buf)
342
343   (ctree-set-calist-strictly
344    'mime-acting-condition
345    '((type . message) (subtype . partial)
346      (method .  wl-mime-combine-message/partial-pieces)
347      (request-partial-message-method . wl-message-request-partial)
348      (major-mode . mmelmo-original-mode)))
349   (ctree-set-calist-strictly
350    'mime-acting-condition
351    '((mode . "extract")
352      (major-mode . mmelmo-original-mode)
353      (method . wl-mime-save-content)))
354   (set-alist 'mime-preview-following-method-alist 
355              'mmelmo-original-mode
356              (function wl-message-follow-current-entity))
357   (set-alist 'mime-view-following-method-alist 
358              'mmelmo-original-mode
359              (function wl-message-follow-current-entity))
360   (set-alist 'mime-edit-message-inserter-alist
361              'wl-draft-mode (function wl-draft-insert-current-message))
362   (set-alist 'mime-edit-mail-inserter-alist
363              'wl-draft-mode (function wl-draft-insert-get-message))
364   (set-alist 'mime-edit-split-message-sender-alist
365              'wl-draft-mode 
366              (cdr (assq 'mail-mode mime-edit-split-message-sender-alist)))
367   (set-alist 'mime-raw-representation-type-alist
368              'mmelmo-original-mode 'binary)
369   ;; Sort and highlight header fields.
370   (setq mmelmo-sort-field-list wl-message-sort-field-list)
371   (add-hook 'mmelmo-header-inserted-hook 'wl-highlight-headers)
372   (add-hook 'mmelmo-entity-content-inserted-hook 'wl-highlight-body))
373   
374
375 (provide 'wl-mime)
376
377 ;;; wl-mime.el ends here