(mime-acting-condition): Set up `mime-method-to-detect' for
[elisp/semi.git] / mime-view.el
1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
2
3 ;; Copyright (C) 1995,1996,1997,1998 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Created: 1994/07/13
7 ;;      Renamed: 1994/08/31 from tm-body.el
8 ;;      Renamed: 1997/02/19 from tm-view.el
9 ;; Keywords: MIME, multimedia, mail, news
10
11 ;; This file is part of SEMI (Sophisticated Emacs MIME Interfaces).
12
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License as
15 ;; published by the Free Software Foundation; either version 2, or (at
16 ;; your option) any later version.
17
18 ;; This program is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Code:
29
30 (require 'std11)
31 (require 'mel)
32 (require 'eword-decode)
33 (require 'mime-parse)
34 (require 'semi-def)
35 (require 'calist)
36 (require 'alist)
37 (require 'mailcap)
38
39
40 ;;; @ version
41 ;;;
42
43 (defconst mime-view-version-string
44   `,(concat (car mime-module-version) " MIME-View "
45             (mapconcat #'number-to-string (cddr mime-module-version) ".")
46             " (" (cadr mime-module-version) ")"))
47
48
49 ;;; @ variables
50 ;;;
51
52 (defgroup mime-view nil
53   "MIME view mode"
54   :group 'mime)
55
56 (defcustom mime-view-find-every-acting-situation t
57   "*Find every available acting-situation if non-nil."
58   :group 'mime-view
59   :type 'boolean)
60
61 (defcustom mime-acting-situation-examples-file "~/.mime-example"
62   "*File name of example about acting-situation demonstrated by user."
63   :group 'mime-view
64   :type 'file)
65
66
67 ;;; @ in raw-buffer (representation space)
68 ;;;
69
70 (defvar mime-raw-message-info nil
71   "Information about structure of message.
72 Please use reference function `mime-entity-SLOT' to get value of SLOT.
73
74 Following is a list of slots of the structure:
75
76 buffer                  buffer includes this entity (buffer).
77 node-id                 node-id (list of integers)
78 header-start            minimum point of header in raw-buffer
79 header-end              maximum point of header in raw-buffer
80 body-start              minimum point of body in raw-buffer
81 body-end                maximum point of body in raw-buffer
82 content-type            content-type (content-type)
83 content-disposition     content-disposition (content-disposition)
84 encoding                Content-Transfer-Encoding (string or nil)
85 children                entities included in this entity (list of entity)
86
87 If an entity includes other entities in its body, such as multipart or
88 message/rfc822, `mime-entity' structures of them are included in
89 `children', so the `mime-entity' structure become a tree.")
90 (make-variable-buffer-local 'mime-raw-message-info)
91
92
93 (defvar mime-preview-buffer nil
94   "MIME-preview buffer corresponding with the (raw) buffer.")
95 (make-variable-buffer-local 'mime-preview-buffer)
96
97
98 (defvar mime-raw-representation-type nil
99   "Representation-type of mime-raw-buffer.
100 It must be nil, `binary' or `cooked'.
101 If it is nil, `mime-raw-representation-type-alist' is used as default
102 value.
103 Notice that this variable is usually used as buffer local variable in
104 raw-buffer.")
105
106 (make-variable-buffer-local 'mime-raw-representation-type)
107
108 (defvar mime-raw-representation-type-alist
109   '((mime-show-message-mode     . binary)
110     (mime-temp-message-mode     . binary)
111     (t                          . cooked)
112     )
113   "Alist of major-mode vs. representation-type of mime-raw-buffer.
114 Each element looks like (SYMBOL . REPRESENTATION-TYPE).  SYMBOL is
115 major-mode or t.  t means default.  REPRESENTATION-TYPE must be
116 `binary' or `cooked'.
117 This value is overridden by buffer local variable
118 `mime-raw-representation-type' if it is not nil.")
119
120
121 (defsubst mime-raw-find-entity-from-node-id (entity-node-id
122                                              &optional message-info)
123   "Return entity from ENTITY-NODE-ID in mime-raw-buffer.
124 If optional argument MESSAGE-INFO is not specified,
125 `mime-raw-message-info' is used."
126   (mime-raw-find-entity-from-number (reverse entity-node-id) message-info))
127
128 (defun mime-raw-find-entity-from-number (entity-number &optional message-info)
129   "Return entity from ENTITY-NUMBER in mime-raw-buffer.
130 If optional argument MESSAGE-INFO is not specified,
131 `mime-raw-message-info' is used."
132   (or message-info
133       (setq message-info mime-raw-message-info))
134   (if (eq entity-number t)
135       message-info
136     (let ((sn (car entity-number)))
137       (if (null sn)
138           message-info
139         (let ((rc (nth sn (mime-entity-children message-info))))
140           (if rc
141               (mime-raw-find-entity-from-number (cdr entity-number) rc)
142             ))
143         ))))
144
145 (defun mime-raw-find-entity-from-point (point &optional message-info)
146   "Return entity from POINT in mime-raw-buffer.
147 If optional argument MESSAGE-INFO is not specified,
148 `mime-raw-message-info' is used."
149   (or message-info
150       (setq message-info mime-raw-message-info))
151   (if (and (<= (mime-entity-point-min message-info) point)
152            (<= point (mime-entity-point-max message-info)))
153       (let ((children (mime-entity-children message-info)))
154         (catch 'tag
155           (while children
156             (let ((ret
157                    (mime-raw-find-entity-from-point point (car children))))
158               (if ret
159                   (throw 'tag ret)
160                 ))
161             (setq children (cdr children)))
162           message-info))))
163
164
165 ;;; @ in preview-buffer (presentation space)
166 ;;;
167
168 (defvar mime-mother-buffer nil
169   "Mother buffer corresponding with the (MIME-preview) buffer.
170 If current MIME-preview buffer is generated by other buffer, such as
171 message/partial, it is called `mother-buffer'.")
172 (make-variable-buffer-local 'mime-mother-buffer)
173
174 (defvar mime-raw-buffer nil
175   "Raw buffer corresponding with the (MIME-preview) buffer.")
176 (make-variable-buffer-local 'mime-raw-buffer)
177
178 (defvar mime-preview-original-window-configuration nil
179   "Window-configuration before mime-view-mode is called.")
180 (make-variable-buffer-local 'mime-preview-original-window-configuration)
181
182 (defun mime-preview-original-major-mode (&optional recursive)
183   "Return major-mode of original buffer.
184 If optional argument RECURSIVE is non-nil and current buffer has
185 mime-mother-buffer, it returns original major-mode of the
186 mother-buffer."
187   (if (and recursive mime-mother-buffer)
188       (save-excursion
189         (set-buffer mime-mother-buffer)
190         (mime-preview-original-major-mode recursive)
191         )
192     (save-excursion
193       (set-buffer
194        (mime-entity-buffer
195         (get-text-property (point-min) 'mime-view-entity)))
196       major-mode)))
197
198
199 ;;; @ entity information
200 ;;;
201
202 (defsubst mime-entity-parent (entity &optional message-info)
203   "Return mother entity of ENTITY.
204 If optional argument MESSAGE-INFO is not specified,
205 `mime-raw-message-info' in buffer of ENTITY is used."
206   (mime-raw-find-entity-from-node-id
207    (cdr (mime-entity-node-id entity))
208    (or message-info
209        (save-excursion
210          (set-buffer (mime-entity-buffer entity))
211          mime-raw-message-info))))
212
213 (defun mime-entity-situation (entity)
214   "Return situation of ENTITY."
215   (append (or (mime-entity-content-type entity)
216               (make-mime-content-type 'text 'plain))
217           (let ((d (mime-entity-content-disposition entity)))
218             (cons (cons 'disposition-type
219                         (mime-content-disposition-type d))
220                   (mapcar (function
221                            (lambda (param)
222                              (let ((name (car param)))
223                                (cons (cond ((string= name "filename")
224                                             'filename)
225                                            ((string= name "creation-date")
226                                             'creation-date)
227                                            ((string= name "modification-date")
228                                             'modification-date)
229                                            ((string= name "read-date")
230                                             'read-date)
231                                            ((string= name "size")
232                                             'size)
233                                            (t (cons 'disposition (car param))))
234                                      (cdr param)))))
235                           (mime-content-disposition-parameters d))
236                   ))
237           (list (cons 'encoding (mime-entity-encoding entity))
238                 (cons 'major-mode
239                       (save-excursion
240                         (set-buffer (mime-entity-buffer entity))
241                         major-mode)))
242           ))
243
244
245 (defvar mime-view-uuencode-encoding-name-list '("x-uue" "x-uuencode"))
246
247 (defun mime-raw-get-uu-filename ()
248   (save-excursion
249     (if (re-search-forward "^begin [0-9]+ " nil t)
250         (if (looking-at ".+$")
251             (buffer-substring (match-beginning 0)(match-end 0))
252           ))))
253
254 (defun mime-raw-get-subject (entity)
255   (or (std11-find-field-body '("Content-Description" "Subject"))
256       (let ((ret (mime-entity-content-disposition entity)))
257         (and ret
258              (setq ret (mime-content-disposition-filename ret))
259              (std11-strip-quoted-string ret)
260              ))
261       (let ((ret (mime-entity-content-type entity)))
262         (and ret
263              (setq ret
264                    (cdr
265                     (let ((param (mime-content-type-parameters ret)))
266                       (or (assoc "name" param)
267                           (assoc "x-name" param))
268                       )))
269              (std11-strip-quoted-string ret)
270              ))
271       (if (member (mime-entity-encoding entity)
272                   mime-view-uuencode-encoding-name-list)
273           (mime-raw-get-uu-filename))
274       ""))
275
276
277 (defsubst mime-raw-point-to-entity-node-id (point &optional message-info)
278   "Return entity-node-id from POINT in mime-raw-buffer.
279 If optional argument MESSAGE-INFO is not specified,
280 `mime-raw-message-info' is used."
281   (mime-entity-node-id (mime-raw-find-entity-from-point point message-info)))
282
283 (defsubst mime-raw-point-to-entity-number (point &optional message-info)
284   "Return entity-number from POINT in mime-raw-buffer.
285 If optional argument MESSAGE-INFO is not specified,
286 `mime-raw-message-info' is used."
287   (mime-entity-number (mime-raw-find-entity-from-point point message-info)))
288
289 (defun mime-raw-flatten-message-info (&optional message-info)
290   "Return list of entity in mime-raw-buffer.
291 If optional argument MESSAGE-INFO is not specified,
292 `mime-raw-message-info' is used."
293   (or message-info
294       (setq message-info mime-raw-message-info))
295   (let ((dest (list message-info))
296         (rcl (mime-entity-children message-info)))
297     (while rcl
298       (setq dest (nconc dest (mime-raw-flatten-message-info (car rcl))))
299       (setq rcl (cdr rcl)))
300     dest))
301
302
303 ;;; @ presentation of preview
304 ;;;
305
306 ;;; @@ entity-button
307 ;;;
308
309 ;;; @@@ predicate function
310 ;;;
311
312 (defun mime-view-entity-button-visible-p (entity)
313   "Return non-nil if header of ENTITY is visible.
314 Please redefine this function if you want to change default setting."
315   (let ((media-type (mime-entity-media-type entity))
316         (media-subtype (mime-entity-media-subtype entity)))
317     (or (not (eq media-type 'application))
318         (and (not (eq media-subtype 'x-selection))
319              (or (not (eq media-subtype 'octet-stream))
320                  (let ((mother-entity (mime-entity-parent entity)))
321                    (or (not (eq (mime-entity-media-type mother-entity)
322                                 'multipart))
323                        (not (eq (mime-entity-media-subtype mother-entity)
324                                 'encrypted)))
325                    )
326                  )))))
327
328 ;;; @@@ entity button generator
329 ;;;
330
331 (defun mime-view-insert-entity-button (entity subject)
332   "Insert entity-button of ENTITY."
333   (let ((entity-node-id (mime-entity-node-id entity))
334         (params (mime-entity-parameters entity)))
335     (mime-insert-button
336      (let ((access-type (assoc "access-type" params))
337            (num (or (cdr (assoc "x-part-number" params))
338                     (if (consp entity-node-id)
339                         (mapconcat (function
340                                     (lambda (num)
341                                       (format "%s" (1+ num))
342                                       ))
343                                    (reverse entity-node-id) ".")
344                       "0"))
345                 ))
346        (cond (access-type
347               (let ((server (assoc "server" params)))
348                 (setq access-type (cdr access-type))
349                 (if server
350                     (format "%s %s ([%s] %s)"
351                             num subject access-type (cdr server))
352                 (let ((site (cdr (assoc "site" params)))
353                       (dir (cdr (assoc "directory" params)))
354                       )
355                   (format "%s %s ([%s] %s:%s)"
356                           num subject access-type site dir)
357                   )))
358             )
359            (t
360             (let ((media-type (mime-entity-media-type entity))
361                   (media-subtype (mime-entity-media-subtype entity))
362                   (charset (cdr (assoc "charset" params)))
363                   (encoding (mime-entity-encoding entity)))
364               (concat
365                num " " subject
366                (let ((rest
367                       (format " <%s/%s%s%s>"
368                               media-type media-subtype
369                               (if charset
370                                   (concat "; " charset)
371                                 "")
372                               (if encoding
373                                   (concat " (" encoding ")")
374                                 ""))))
375                  (if (>= (+ (current-column)(length rest))(window-width))
376                      "\n\t")
377                  rest)))
378             )))
379      (function mime-preview-play-current-entity))
380     ))
381
382
383 ;;; @@ entity-header
384 ;;;
385
386 ;;; @@@ entity header filter
387 ;;;
388
389 (defvar mime-view-content-header-filter-alist nil)
390
391 (defun mime-view-default-content-header-filter ()
392   (mime-view-cut-header)
393   (eword-decode-header)
394   )
395
396 ;;; @@@ entity field cutter
397 ;;;
398
399 (defvar mime-view-ignored-field-list
400   '(".*Received" ".*Path" ".*Id" "References"
401     "Replied" "Errors-To"
402     "Lines" "Sender" ".*Host" "Xref"
403     "Content-Type" "Precedence"
404     "Status" "X-VM-.*")
405   "All fields that match this list will be hidden in MIME preview buffer.
406 Each elements are regexp of field-name.")
407
408 (defvar mime-view-ignored-field-regexp
409   (concat "^"
410           (apply (function regexp-or) mime-view-ignored-field-list)
411           ":"))
412
413 (defvar mime-view-visible-field-list '("Dnas.*" "Message-Id")
414   "All fields that match this list will be displayed in MIME preview buffer.
415 Each elements are regexp of field-name.")
416
417 (defun mime-view-cut-header ()
418   (goto-char (point-min))
419   (while (re-search-forward mime-view-ignored-field-regexp nil t)
420     (let* ((beg (match-beginning 0))
421            (end (match-end 0))
422            (name (buffer-substring beg end))
423            )
424       (catch 'visible
425         (let ((rest mime-view-visible-field-list))
426           (while rest
427             (if (string-match (car rest) name)
428                 (throw 'visible nil)
429               )
430             (setq rest (cdr rest))))
431         (delete-region beg
432                        (save-excursion
433                          (if (re-search-forward "^\\([^ \t]\\|$\\)" nil t)
434                              (match-beginning 0)
435                            (point-max))))
436         ))))
437
438
439 ;;; @@ entity-body
440 ;;;
441
442 ;;; @@@ predicate function
443 ;;;
444
445 (defun mime-calist::field-match-method-as-default-rule (calist
446                                                         field-type field-value)
447   (let ((s-field (assq field-type calist)))
448     (cond ((null s-field)
449            (cons (cons field-type field-value) calist)
450            )
451           (t calist))))
452
453 (define-calist-field-match-method
454   'header #'mime-calist::field-match-method-as-default-rule)
455
456 (define-calist-field-match-method
457   'body #'mime-calist::field-match-method-as-default-rule)
458
459
460 (defvar mime-preview-condition nil
461   "Condition-tree about how to display entity.")
462
463 (ctree-set-calist-strictly
464  'mime-preview-condition '((type . application)(subtype . octet-stream)
465                            (encoding . nil)
466                            (body . visible)))
467 (ctree-set-calist-strictly
468  'mime-preview-condition '((type . application)(subtype . octet-stream)
469                            (encoding . "7bit")
470                            (body . visible)))
471 (ctree-set-calist-strictly
472  'mime-preview-condition '((type . application)(subtype . octet-stream)
473                            (encoding . "8bit")
474                            (body . visible)))
475
476 (ctree-set-calist-strictly
477  'mime-preview-condition '((type . application)(subtype . pgp)
478                            (body . visible)))
479
480 (ctree-set-calist-strictly
481  'mime-preview-condition '((type . application)(subtype . x-latex)
482                            (body . visible)))
483
484 (ctree-set-calist-strictly
485  'mime-preview-condition '((type . application)(subtype . x-selection)
486                            (body . visible)))
487
488 (ctree-set-calist-strictly
489  'mime-preview-condition '((type . application)(subtype . x-comment)
490                            (body . visible)))
491
492 (ctree-set-calist-strictly
493  'mime-preview-condition '((type . message)(subtype . delivery-status)
494                            (body . visible)))
495
496 (ctree-set-calist-strictly
497  'mime-preview-condition
498  '((body . visible)
499    (body-presentation-method . mime-preview-text/plain)))
500
501 (ctree-set-calist-strictly
502  'mime-preview-condition
503  '((type . nil)
504    (body . visible)
505    (body-presentation-method . mime-preview-text/plain)))
506
507 (ctree-set-calist-strictly
508  'mime-preview-condition
509  '((type . text)(subtype . enriched)
510    (body . visible)
511    (body-presentation-method . mime-preview-text/enriched)))
512
513 (ctree-set-calist-strictly
514  'mime-preview-condition
515  '((type . text)(subtype . richtext)
516    (body . visible)
517    (body-presentation-method . mime-preview-text/richtext)))
518
519 (ctree-set-calist-strictly
520  'mime-preview-condition
521  '((type . text)(subtype . t)
522    (body . visible)
523    (body-presentation-method . mime-preview-text/plain)))
524
525 (ctree-set-calist-strictly
526  'mime-preview-condition
527  '((type . multipart)(subtype . alternative)
528    (body . visible)
529    (body-presentation-method . mime-preview-multipart/alternative)))
530
531 (ctree-set-calist-strictly
532  'mime-preview-condition '((type . message)(subtype . partial)
533                            (body-presentation-method
534                             . mime-preview-message/partial-button)))
535
536 (ctree-set-calist-strictly
537  'mime-preview-condition '((type . message)(subtype . rfc822)
538                            (body-presentation-method . nil)
539                            (childrens-situation (header . visible)
540                                                 (entity-button . invisible))))
541
542 (ctree-set-calist-strictly
543  'mime-preview-condition '((type . message)(subtype . news)
544                            (body-presentation-method . nil)
545                            (childrens-situation (header . visible)
546                                                 (entity-button . invisible))))
547
548
549 ;;; @@@ entity presentation
550 ;;;
551
552 (autoload 'mime-preview-text/plain "mime-text")
553 (autoload 'mime-preview-text/enriched "mime-text")
554 (autoload 'mime-preview-text/richtext "mime-text")
555
556 (defvar mime-view-announcement-for-message/partial
557   (if (and (>= emacs-major-version 19) window-system)
558       "\
559 \[[ This is message/partial style split message. ]]
560 \[[ Please press `v' key in this buffer          ]]
561 \[[ or click here by mouse button-2.             ]]"
562     "\
563 \[[ This is message/partial style split message. ]]
564 \[[ Please press `v' key in this buffer.         ]]"
565     ))
566
567 (defun mime-preview-message/partial-button (&optional entity situation)
568   (save-restriction
569     (goto-char (point-max))
570     (if (not (search-backward "\n\n" nil t))
571         (insert "\n")
572       )
573     (goto-char (point-max))
574     (narrow-to-region (point-max)(point-max))
575     (insert mime-view-announcement-for-message/partial)
576     (mime-add-button (point-min)(point-max)
577                      #'mime-preview-play-current-entity)
578     ))
579
580 (defun mime-preview-multipart/mixed (entity situation)
581   (let ((children (mime-entity-children entity))
582         (default-situation
583           (cdr (assq 'childrens-situation situation))))
584     (while children
585       (mime-view-display-entity (car children)
586                                 (save-excursion
587                                   (set-buffer (mime-entity-buffer entity))
588                                   mime-raw-message-info)
589                                 (current-buffer)
590                                 default-situation)
591       (setq children (cdr children))
592       )))
593
594 (defcustom mime-view-type-subtype-score-alist
595   '(((text . enriched) . 3)
596     ((text . richtext) . 2)
597     ((text . plain)    . 1)
598     (t . 0))
599   "Alist MEDIA-TYPE vs corresponding score.
600 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t.  t means default."
601   :group 'mime-view
602   :type '(repeat (cons (choice :tag "Media-Type"
603                                (item :tag "Type/Subtype"
604                                      (cons symbol symbol))
605                                (item :tag "Type" symbol)
606                                (item :tag "Default" t))
607                        integer)))
608
609 (defun mime-preview-multipart/alternative (entity situation)
610   (let* ((children (mime-entity-children entity))
611          (default-situation
612            (cdr (assq 'childrens-situation situation)))
613          (i 0)
614          (p 0)
615          (max-score 0)
616          (situations
617           (mapcar (function
618                    (lambda (child)
619                      (let ((situation
620                             (or (ctree-match-calist
621                                  mime-preview-condition
622                                  (append (mime-entity-situation child)
623                                          default-situation))
624                                 default-situation)))
625                        (if (cdr (assq 'body-presentation-method situation))
626                            (let ((score
627                                   (cdr
628                                    (or (assoc
629                                         (cons
630                                          (cdr (assq 'type situation))
631                                          (cdr (assq 'subtype situation)))
632                                         mime-view-type-subtype-score-alist)
633                                        (assq
634                                         (cdr (assq 'type situation))
635                                         mime-view-type-subtype-score-alist)
636                                        (assq
637                                         t
638                                         mime-view-type-subtype-score-alist)
639                                        ))))
640                              (if (> score max-score)
641                                  (setq p i
642                                        max-score score)
643                                )))
644                        (setq i (1+ i))
645                        situation)
646                      ))
647                   children)))
648     (setq i 0)
649     (while children
650       (let ((child (car children))
651             (situation (car situations)))
652         (mime-view-display-entity child
653                                   (save-excursion
654                                     (set-buffer (mime-entity-buffer child))
655                                     mime-raw-message-info)
656                                   (current-buffer)
657                                   default-situation
658                                   (if (= i p)
659                                       situation
660                                     (del-alist 'body-presentation-method
661                                                (copy-alist situation))))
662         )
663       (setq children (cdr children)
664             situations (cdr situations)
665             i (1+ i))
666       )))
667
668
669 ;;; @ acting-condition
670 ;;;
671
672 (defvar mime-acting-condition nil
673   "Condition-tree about how to process entity.")
674
675 (if (file-readable-p mailcap-file)
676     (let ((entries (mailcap-parse-file)))
677       (while entries
678         (let ((entry (car entries))
679               view print shared)
680           (while entry
681             (let* ((field (car entry))
682                    (field-type (car field)))
683               (cond ((eq field-type 'view)  (setq view field))
684                     ((eq field-type 'print) (setq print field))
685                     ((memq field-type '(compose composetyped edit)))
686                     (t (setq shared (cons field shared))))
687               )
688             (setq entry (cdr entry))
689             )
690           (setq shared (nreverse shared))
691           (ctree-set-calist-with-default
692            'mime-acting-condition
693            (append shared (list '(mode . "play")(cons 'method (cdr view)))))
694           (if print
695               (ctree-set-calist-with-default
696                'mime-acting-condition
697                (append shared
698                        (list '(mode . "print")(cons 'method (cdr view))))
699                ))
700           )
701         (setq entries (cdr entries))
702         )))
703
704 (ctree-set-calist-strictly
705  'mime-acting-condition
706  '((type . application)(subtype . octet-stream)
707    (mode . "play")
708    (method . mime-method-to-detect)
709    ))
710
711 (ctree-set-calist-with-default
712  'mime-acting-condition
713  '((mode . "extract")
714    (method . mime-method-to-save)))
715
716 (ctree-set-calist-strictly
717  'mime-acting-condition
718  '((type . text)(subtype . x-rot13-47)(mode . "play")
719    (method . mime-method-to-display-caesar)
720    ))
721 (ctree-set-calist-strictly
722  'mime-acting-condition
723  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
724    (method . mime-method-to-display-caesar)
725    ))
726
727 (ctree-set-calist-strictly
728  'mime-acting-condition
729  '((type . message)(subtype . rfc822)(mode . "play")
730    (method . mime-method-to-display-message/rfc822)
731    ))
732 (ctree-set-calist-strictly
733  'mime-acting-condition
734  '((type . message)(subtype . partial)(mode . "play")
735    (method . mime-method-to-store-message/partial)
736    ))
737
738 (ctree-set-calist-strictly
739  'mime-acting-condition
740  '((type . message)(subtype . external-body)
741    ("access-type" . "anon-ftp")
742    (method . mime-method-to-display-message/external-ftp)
743    ))
744
745 (ctree-set-calist-strictly
746  'mime-acting-condition
747  '((type . application)(subtype . octet-stream)
748    (method . mime-method-to-save)
749    ))
750
751
752 ;;; @ quitting method
753 ;;;
754
755 (defvar mime-preview-quitting-method-alist
756   '((mime-show-message-mode
757      . mime-preview-quitting-method-for-mime-show-message-mode))
758   "Alist of major-mode vs. quitting-method of mime-view.")
759
760 (defvar mime-view-over-to-previous-method-alist nil)
761 (defvar mime-view-over-to-next-method-alist nil)
762
763
764 ;;; @ following method
765 ;;;
766
767 (defvar mime-view-following-method-alist nil
768   "Alist of major-mode vs. following-method of mime-view.")
769
770 (defvar mime-view-following-required-fields-list
771   '("From"))
772
773
774 ;;; @ X-Face
775 ;;;
776
777 ;; hack from Gnus 5.0.4.
778
779 (defvar mime-view-x-face-to-pbm-command
780   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
781
782 (defvar mime-view-x-face-command
783   (concat mime-view-x-face-to-pbm-command
784           " | xv -quit -")
785   "String to be executed to display an X-Face field.
786 The command will be executed in a sub-shell asynchronously.
787 The compressed face will be piped to this command.")
788
789 (defun mime-view-x-face-function ()
790   "Function to display X-Face field. You can redefine to customize."
791   ;; 1995/10/12 (c.f. tm-eng:130)
792   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
793   (save-restriction
794     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
795     ;; end
796     (goto-char (point-min))
797     (if (re-search-forward "^X-Face:[ \t]*" nil t)
798         (let ((beg (match-end 0))
799               (end (std11-field-end))
800               )
801           (call-process-region beg end "sh" nil 0 nil
802                                "-c" mime-view-x-face-command)
803           ))))
804
805
806 ;;; @ buffer setup
807 ;;;
808
809 (defun mime-view-display-entity (entity message-info obuf
810                                         default-situation
811                                         &optional situation)
812   (let* ((raw-buffer (mime-entity-buffer entity))
813          (start (mime-entity-point-min entity))
814          (end (mime-entity-point-max entity))
815          original-major-mode end-of-header e nb ne subj)
816     (set-buffer raw-buffer)
817     (setq original-major-mode major-mode)
818     (goto-char start)
819     (setq end-of-header (if (re-search-forward "^$" nil t)
820                             (1+ (match-end 0))
821                           end))
822     (if (> end-of-header end)
823         (setq end-of-header end)
824       )
825     (save-restriction
826       (narrow-to-region start end)
827       (setq subj (eword-decode-string (mime-raw-get-subject entity)))
828       )
829     (or situation
830         (setq situation
831               (or (ctree-match-calist mime-preview-condition
832                                       (append (mime-entity-situation entity)
833                                               default-situation))
834                   default-situation)))
835     (let ((button-is-invisible
836            (eq (cdr (assq 'entity-button situation)) 'invisible))
837           (header-is-visible
838            (eq (cdr (assq 'header situation)) 'visible))
839           (body-presentation-method
840            (cdr (assq 'body-presentation-method situation)))
841           (children (mime-entity-children entity)))
842       (set-buffer obuf)
843       (setq nb (point))
844       (narrow-to-region nb nb)
845       (or button-is-invisible
846           (if (mime-view-entity-button-visible-p entity)
847               (mime-view-insert-entity-button entity subj)
848             ))
849       (if header-is-visible
850           (save-restriction
851             (narrow-to-region (point)(point))
852             (insert-buffer-substring raw-buffer start end-of-header)
853             (let ((f (cdr (assq original-major-mode
854                                 mime-view-content-header-filter-alist))))
855               (if (functionp f)
856                   (funcall f)
857                 (mime-view-default-content-header-filter)
858                 ))
859             (run-hooks 'mime-view-content-header-filter-hook)
860             ))
861       (cond ((eq body-presentation-method 'with-filter)
862              (let ((body-filter (cdr (assq 'body-filter situation))))
863                (save-restriction
864                  (narrow-to-region (point-max)(point-max))
865                  (insert-buffer-substring raw-buffer end-of-header end)
866                  (funcall body-filter situation)
867                  )))
868             (children)
869             ((functionp body-presentation-method)
870              (funcall body-presentation-method entity situation)
871              )
872             (t
873              (when button-is-invisible
874                (goto-char (point-max))
875                (mime-view-insert-entity-button entity subj)
876                )
877              (or header-is-visible
878                  (progn
879                    (goto-char (point-max))
880                    (insert "\n")
881                    ))
882              ))
883       (setq ne (point-max))
884       (widen)
885       (put-text-property nb ne 'mime-view-entity entity)
886       (goto-char ne)
887       (if children
888           (if (functionp body-presentation-method)
889               (funcall body-presentation-method entity situation)
890             (mime-preview-multipart/mixed entity situation)
891             ))
892       )))
893
894
895 ;;; @ MIME viewer mode
896 ;;;
897
898 (defconst mime-view-menu-title "MIME-View")
899 (defconst mime-view-menu-list
900   '((up          "Move to upper entity"    mime-preview-move-to-upper)
901     (previous    "Move to previous entity" mime-preview-move-to-previous)
902     (next        "Move to next entity"     mime-preview-move-to-next)
903     (scroll-down "Scroll-down"             mime-preview-scroll-down-entity)
904     (scroll-up   "Scroll-up"               mime-preview-scroll-up-entity)
905     (play        "Play current entity"     mime-preview-play-current-entity)
906     (extract     "Extract current entity"  mime-preview-extract-current-entity)
907     (print       "Print current entity"    mime-preview-print-current-entity)
908     (x-face      "Show X Face"             mime-preview-display-x-face)
909     )
910   "Menu for MIME Viewer")
911
912 (cond (running-xemacs
913        (defvar mime-view-xemacs-popup-menu
914          (cons mime-view-menu-title
915                (mapcar (function
916                         (lambda (item)
917                           (vector (nth 1 item)(nth 2 item) t)
918                           ))
919                        mime-view-menu-list)))
920        (defun mime-view-xemacs-popup-menu (event)
921          "Popup the menu in the MIME Viewer buffer"
922          (interactive "e")
923          (select-window (event-window event))
924          (set-buffer (event-buffer event))
925          (popup-menu 'mime-view-xemacs-popup-menu))
926        (defvar mouse-button-2 'button2)
927        )
928       (t
929        (defvar mouse-button-2 [mouse-2])
930        ))
931
932 (defun mime-view-define-keymap (&optional default)
933   (let ((mime-view-mode-map (if (keymapp default)
934                                 (copy-keymap default)
935                               (make-sparse-keymap)
936                               )))
937     (define-key mime-view-mode-map
938       "u"        (function mime-preview-move-to-upper))
939     (define-key mime-view-mode-map
940       "p"        (function mime-preview-move-to-previous))
941     (define-key mime-view-mode-map
942       "n"        (function mime-preview-move-to-next))
943     (define-key mime-view-mode-map
944       "\e\t"     (function mime-preview-move-to-previous))
945     (define-key mime-view-mode-map
946       "\t"       (function mime-preview-move-to-next))
947     (define-key mime-view-mode-map
948       " "        (function mime-preview-scroll-up-entity))
949     (define-key mime-view-mode-map
950       "\M- "     (function mime-preview-scroll-down-entity))
951     (define-key mime-view-mode-map
952       "\177"     (function mime-preview-scroll-down-entity))
953     (define-key mime-view-mode-map
954       "\C-m"     (function mime-preview-next-line-entity))
955     (define-key mime-view-mode-map
956       "\C-\M-m"  (function mime-preview-previous-line-entity))
957     (define-key mime-view-mode-map
958       "v"        (function mime-preview-play-current-entity))
959     (define-key mime-view-mode-map
960       "e"        (function mime-preview-extract-current-entity))
961     (define-key mime-view-mode-map
962       "\C-c\C-p" (function mime-preview-print-current-entity))
963     (define-key mime-view-mode-map
964       "a"        (function mime-preview-follow-current-entity))
965     (define-key mime-view-mode-map
966       "q"        (function mime-preview-quit))
967     (define-key mime-view-mode-map
968       "\C-c\C-x" (function mime-preview-kill-buffer))
969     ;; (define-key mime-view-mode-map
970     ;;   "<"        (function beginning-of-buffer))
971     ;; (define-key mime-view-mode-map
972     ;;   ">"        (function end-of-buffer))
973     (define-key mime-view-mode-map
974       "?"        (function describe-mode))
975     (define-key mime-view-mode-map
976       [tab] (function mime-preview-move-to-next))
977     (define-key mime-view-mode-map
978       [delete] (function mime-preview-scroll-down-entity))
979     (define-key mime-view-mode-map
980       [backspace] (function mime-preview-scroll-down-entity))
981     (if (functionp default)
982         (cond (running-xemacs
983                (set-keymap-default-binding mime-view-mode-map default)
984                )
985               (t
986                (setq mime-view-mode-map
987                      (append mime-view-mode-map (list (cons t default))))
988                )))
989     (if mouse-button-2
990         (define-key mime-view-mode-map
991           mouse-button-2 (function mime-button-dispatcher))
992       )
993     (cond (running-xemacs
994            (define-key mime-view-mode-map
995              mouse-button-3 (function mime-view-xemacs-popup-menu))
996            )
997           ((>= emacs-major-version 19)
998            (define-key mime-view-mode-map [menu-bar mime-view]
999              (cons mime-view-menu-title
1000                    (make-sparse-keymap mime-view-menu-title)))
1001            (mapcar (function
1002                     (lambda (item)
1003                       (define-key mime-view-mode-map
1004                         (vector 'menu-bar 'mime-view (car item))
1005                         (cons (nth 1 item)(nth 2 item))
1006                         )
1007                       ))
1008                    (reverse mime-view-menu-list)
1009                    )
1010            ))
1011     (use-local-map mime-view-mode-map)
1012     (run-hooks 'mime-view-define-keymap-hook)
1013     ))
1014
1015 (defsubst mime-maybe-hide-echo-buffer ()
1016   "Clear mime-echo buffer and delete window for it."
1017   (let ((buf (get-buffer mime-echo-buffer-name)))
1018     (if buf
1019         (save-excursion
1020           (set-buffer buf)
1021           (erase-buffer)
1022           (let ((win (get-buffer-window buf)))
1023             (if win
1024                 (delete-window win)
1025               ))
1026           (bury-buffer buf)
1027           ))))
1028
1029 (defvar mime-view-redisplay nil)
1030
1031 (defun mime-view-display-message (message &optional preview-buffer
1032                                           mother default-keymap-or-function)
1033   (mime-maybe-hide-echo-buffer)
1034   (let ((win-conf (current-window-configuration))
1035         (raw-buffer (mime-entity-buffer message)))
1036     (or preview-buffer
1037         (setq preview-buffer
1038               (concat "*Preview-" (buffer-name raw-buffer) "*")))
1039     (set-buffer raw-buffer)
1040     (setq mime-raw-message-info (mime-parse-message))
1041     (setq mime-preview-buffer preview-buffer)
1042     (let ((inhibit-read-only t))
1043       (switch-to-buffer preview-buffer)
1044       (widen)
1045       (erase-buffer)
1046       (setq mime-raw-buffer raw-buffer)
1047       (if mother
1048           (setq mime-mother-buffer mother)
1049         )
1050       (setq mime-preview-original-window-configuration win-conf)
1051       (setq major-mode 'mime-view-mode)
1052       (setq mode-name "MIME-View")
1053       (mime-view-display-entity message message
1054                                 preview-buffer
1055                                 '((entity-button . invisible)
1056                                   (header . visible)
1057                                   ))
1058       (mime-view-define-keymap default-keymap-or-function)
1059       (let ((point
1060              (next-single-property-change (point-min) 'mime-view-entity)))
1061         (if point
1062             (goto-char point)
1063           (goto-char (point-min))
1064           (search-forward "\n\n" nil t)
1065           ))
1066       (run-hooks 'mime-view-mode-hook)
1067       ))
1068   (set-buffer-modified-p nil)
1069   (setq buffer-read-only t)
1070   )
1071
1072 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1073                                    default-keymap-or-function)
1074   (interactive)
1075   (mime-view-display-message
1076    (save-excursion
1077      (if raw-buffer (set-buffer raw-buffer))
1078      (mime-parse-message)
1079      )
1080    preview-buffer mother default-keymap-or-function))
1081
1082 (defun mime-view-mode (&optional mother ctl encoding
1083                                  raw-buffer preview-buffer
1084                                  default-keymap-or-function)
1085   "Major mode for viewing MIME message.
1086
1087 Here is a list of the standard keys for mime-view-mode.
1088
1089 key             feature
1090 ---             -------
1091
1092 u               Move to upper content
1093 p or M-TAB      Move to previous content
1094 n or TAB        Move to next content
1095 SPC             Scroll up or move to next content
1096 M-SPC or DEL    Scroll down or move to previous content
1097 RET             Move to next line
1098 M-RET           Move to previous line
1099 v               Decode current content as `play mode'
1100 e               Decode current content as `extract mode'
1101 C-c C-p         Decode current content as `print mode'
1102 a               Followup to current content.
1103 x               Display X-Face
1104 q               Quit
1105 button-2        Move to point under the mouse cursor
1106                 and decode current content as `play mode'
1107 "
1108   (interactive)
1109   (mime-view-display-message
1110    (save-excursion
1111      (if raw-buffer (set-buffer raw-buffer))
1112      (or mime-view-redisplay
1113          (mime-parse-message ctl encoding))
1114      )
1115    preview-buffer mother default-keymap-or-function))
1116
1117
1118 ;;; @@ playing
1119 ;;;
1120
1121 (autoload 'mime-preview-play-current-entity "mime-play"
1122   "Play current entity." t)
1123
1124 (defun mime-preview-extract-current-entity ()
1125   "Extract current entity into file (maybe).
1126 It decodes current entity to call internal or external method as
1127 \"extract\" mode.  The method is selected from variable
1128 `mime-acting-condition'."
1129   (interactive)
1130   (mime-preview-play-current-entity "extract")
1131   )
1132
1133 (defun mime-preview-print-current-entity ()
1134   "Print current entity (maybe).
1135 It decodes current entity to call internal or external method as
1136 \"print\" mode.  The method is selected from variable
1137 `mime-acting-condition'."
1138   (interactive)
1139   (mime-preview-play-current-entity "print")
1140   )
1141
1142
1143 ;;; @@ following
1144 ;;;
1145
1146 (defun mime-preview-follow-current-entity ()
1147   "Write follow message to current entity.
1148 It calls following-method selected from variable
1149 `mime-view-following-method-alist'."
1150   (interactive)
1151   (let (entity)
1152     (while (null (setq entity
1153                        (get-text-property (point) 'mime-view-entity)))
1154       (backward-char)
1155       )
1156     (let* ((p-beg
1157             (previous-single-property-change (point) 'mime-view-entity))
1158            p-end
1159            (entity-node-id (mime-entity-node-id entity))
1160            (len (length entity-node-id))
1161            )
1162       (cond ((null p-beg)
1163              (setq p-beg
1164                    (if (eq (next-single-property-change (point-min)
1165                                                         'mime-view-entity)
1166                            (point))
1167                        (point)
1168                      (point-min)))
1169              )
1170             ((eq (next-single-property-change p-beg 'mime-view-entity)
1171                  (point))
1172              (setq p-beg (point))
1173              ))
1174       (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1175       (cond ((null p-end)
1176              (setq p-end (point-max))
1177              )
1178             ((null entity-node-id)
1179              (setq p-end (point-max))
1180              )
1181             (t
1182              (save-excursion
1183                (goto-char p-end)
1184                (catch 'tag
1185                  (let (e)
1186                    (while (setq e
1187                                 (next-single-property-change
1188                                  (point) 'mime-view-entity))
1189                      (goto-char e)
1190                      (let ((rc (mime-entity-node-id
1191                                 (get-text-property (point)
1192                                                    'mime-view-entity))))
1193                        (or (equal entity-node-id
1194                                   (nthcdr (- (length rc) len) rc))
1195                            (throw 'tag nil)
1196                            ))
1197                      (setq p-end e)
1198                      ))
1199                  (setq p-end (point-max))
1200                  ))
1201              ))
1202       (let* ((mode (mime-preview-original-major-mode 'recursive))
1203              (new-name
1204               (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1205              new-buf
1206              (the-buf (current-buffer))
1207              (a-buf mime-raw-buffer)
1208              fields)
1209         (save-excursion
1210           (set-buffer (setq new-buf (get-buffer-create new-name)))
1211           (erase-buffer)
1212           (insert-buffer-substring the-buf p-beg p-end)
1213           (goto-char (point-min))
1214           (let ((entity-node-id (mime-entity-node-id entity)) ci str)
1215             (while (progn
1216                      (setq
1217                       str
1218                       (save-excursion
1219                         (set-buffer a-buf)
1220                         (setq
1221                          ci
1222                          (mime-raw-find-entity-from-node-id entity-node-id))
1223                         (save-restriction
1224                           (narrow-to-region
1225                            (mime-entity-point-min ci)
1226                            (mime-entity-point-max ci)
1227                            )
1228                           (std11-header-string-except
1229                            (concat "^"
1230                                    (apply (function regexp-or) fields)
1231                                    ":") ""))))
1232                      (if (and
1233                           (eq (mime-entity-media-type ci) 'message)
1234                           (eq (mime-entity-media-subtype ci) 'rfc822))
1235                          nil
1236                        (if str
1237                            (insert str)
1238                          )
1239                        entity-node-id))
1240               (setq fields (std11-collect-field-names)
1241                     entity-node-id (cdr entity-node-id))
1242               )
1243             )
1244           (let ((rest mime-view-following-required-fields-list))
1245             (while rest
1246               (let ((field-name (car rest)))
1247                 (or (std11-field-body field-name)
1248                     (insert
1249                      (format
1250                       (concat field-name
1251                               ": "
1252                               (save-excursion
1253                                 (set-buffer the-buf)
1254                                 (set-buffer mime-mother-buffer)
1255                                 (set-buffer mime-raw-buffer)
1256                                 (std11-field-body field-name)
1257                                 )
1258                               "\n")))
1259                     ))
1260               (setq rest (cdr rest))
1261               ))
1262           (eword-decode-header)
1263           )
1264         (let ((f (cdr (assq mode mime-view-following-method-alist))))
1265           (if (functionp f)
1266               (funcall f new-buf)
1267             (message
1268              (format
1269               "Sorry, following method for %s is not implemented yet."
1270               mode))
1271             ))
1272         ))))
1273
1274
1275 ;;; @@ X-Face
1276 ;;;
1277
1278 (defun mime-preview-display-x-face ()
1279   (interactive)
1280   (save-window-excursion
1281     (set-buffer mime-raw-buffer)
1282     (mime-view-x-face-function)
1283     ))
1284
1285
1286 ;;; @@ moving
1287 ;;;
1288
1289 (defun mime-preview-move-to-upper ()
1290   "Move to upper entity.
1291 If there is no upper entity, call function `mime-preview-quit'."
1292   (interactive)
1293   (let (cinfo)
1294     (while (null (setq cinfo
1295                        (get-text-property (point) 'mime-view-entity)))
1296       (backward-char)
1297       )
1298     (let ((r (mime-raw-find-entity-from-node-id
1299               (cdr (mime-entity-node-id cinfo))
1300               (get-text-property 1 'mime-view-entity)))
1301           point)
1302       (catch 'tag
1303         (while (setq point (previous-single-property-change
1304                             (point) 'mime-view-entity))
1305           (goto-char point)
1306           (if (eq r (get-text-property (point) 'mime-view-entity))
1307               (throw 'tag t)
1308             )
1309           )
1310         (mime-preview-quit)
1311         ))))
1312
1313 (defun mime-preview-move-to-previous ()
1314   "Move to previous entity.
1315 If there is no previous entity, it calls function registered in
1316 variable `mime-view-over-to-previous-method-alist'."
1317   (interactive)
1318   (while (null (get-text-property (point) 'mime-view-entity))
1319     (backward-char)
1320     )
1321   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1322     (if point
1323         (if (get-text-property (1- point) 'mime-view-entity)
1324             (goto-char point)
1325           (goto-char (1- point))
1326           (mime-preview-move-to-previous)
1327           )
1328       (let ((f (assq (mime-preview-original-major-mode)
1329                      mime-view-over-to-previous-method-alist)))
1330         (if f
1331             (funcall (cdr f))
1332           ))
1333       )))
1334
1335 (defun mime-preview-move-to-next ()
1336   "Move to next entity.
1337 If there is no previous entity, it calls function registered in
1338 variable `mime-view-over-to-next-method-alist'."
1339   (interactive)
1340   (while (null (get-text-property (point) 'mime-view-entity))
1341     (forward-char)
1342     )
1343   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1344     (if point
1345         (progn
1346           (goto-char point)
1347           (if (null (get-text-property point 'mime-view-entity))
1348               (mime-preview-move-to-next)
1349             ))
1350       (let ((f (assq (mime-preview-original-major-mode)
1351                      mime-view-over-to-next-method-alist)))
1352         (if f
1353             (funcall (cdr f))
1354           ))
1355       )))
1356
1357 (defun mime-preview-scroll-up-entity (&optional h)
1358   "Scroll up current entity.
1359 If reached to (point-max), it calls function registered in variable
1360 `mime-view-over-to-next-method-alist'."
1361   (interactive)
1362   (or h
1363       (setq h (1- (window-height)))
1364       )
1365   (if (= (point) (point-max))
1366       (let ((f (assq (mime-preview-original-major-mode)
1367                      mime-view-over-to-next-method-alist)))
1368         (if f
1369             (funcall (cdr f))
1370           ))
1371     (let ((point
1372            (or (next-single-property-change (point) 'mime-view-entity)
1373                (point-max))))
1374       (forward-line h)
1375       (if (> (point) point)
1376           (goto-char point)
1377         )
1378       )))
1379
1380 (defun mime-preview-scroll-down-entity (&optional h)
1381   "Scroll down current entity.
1382 If reached to (point-min), it calls function registered in variable
1383 `mime-view-over-to-previous-method-alist'."
1384   (interactive)
1385   (or h
1386       (setq h (1- (window-height)))
1387       )
1388   (if (= (point) (point-min))
1389       (let ((f (assq (mime-preview-original-major-mode)
1390                      mime-view-over-to-previous-method-alist)))
1391         (if f
1392             (funcall (cdr f))
1393           ))
1394     (let ((point
1395            (or (previous-single-property-change (point) 'mime-view-entity)
1396                (point-min))))
1397       (forward-line (- h))
1398       (if (< (point) point)
1399           (goto-char point)
1400         ))))
1401
1402 (defun mime-preview-next-line-entity ()
1403   (interactive)
1404   (mime-preview-scroll-up-entity 1)
1405   )
1406
1407 (defun mime-preview-previous-line-entity ()
1408   (interactive)
1409   (mime-preview-scroll-down-entity 1)
1410   )
1411
1412
1413 ;;; @@ quitting
1414 ;;;
1415
1416 (defun mime-preview-quit ()
1417   "Quit from MIME-preview buffer.
1418 It calls function registered in variable
1419 `mime-preview-quitting-method-alist'."
1420   (interactive)
1421   (let ((r (assq (mime-preview-original-major-mode)
1422                  mime-preview-quitting-method-alist)))
1423     (if r
1424         (funcall (cdr r))
1425       )))
1426
1427 (defun mime-preview-kill-buffer ()
1428   (interactive)
1429   (kill-buffer (current-buffer))
1430   )
1431
1432
1433 ;;; @ end
1434 ;;;
1435
1436 (provide 'mime-view)
1437
1438 (run-hooks 'mime-view-load-hook)
1439
1440 ;;; mime-view.el ends here