3fe501029a5e287632dc585fd7be491ab06f78cf
[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 WEMI (Widget based 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 or click here by mouse button-2."
561     "\
562 This is message/partial style split message.
563 Please press `v' key in this buffer."
564     ))
565
566 (defun mime-preview-message/partial-button (&optional entity situation)
567   (save-restriction
568     (goto-char (point-max))
569     (if (not (search-backward "\n\n" nil t))
570         (insert "\n")
571       )
572     (goto-char (point-max))
573     ;;(narrow-to-region (point-max)(point-max))
574     ;;(insert mime-view-announcement-for-message/partial)
575     ;; (mime-add-button (point-min)(point-max)
576     ;;                  #'mime-preview-play-current-entity)
577     (mime-insert-button mime-view-announcement-for-message/partial
578                         #'mime-preview-play-current-entity)
579     ))
580
581 (defun mime-preview-multipart/mixed (entity situation)
582   (let ((children (mime-entity-children entity))
583         (default-situation
584           (cdr (assq 'childrens-situation situation))))
585     (while children
586       (mime-view-display-entity (car children)
587                                 (save-excursion
588                                   (set-buffer (mime-entity-buffer entity))
589                                   mime-raw-message-info)
590                                 (current-buffer)
591                                 default-situation)
592       (setq children (cdr children))
593       )))
594
595 (defcustom mime-view-type-subtype-score-alist
596   '(((text . enriched) . 3)
597     ((text . richtext) . 2)
598     ((text . plain)    . 1)
599     (t . 0))
600   "Alist MEDIA-TYPE vs corresponding score.
601 MEDIA-TYPE must be (TYPE . SUBTYPE), TYPE or t.  t means default."
602   :group 'mime-view
603   :type '(repeat (cons (choice :tag "Media-Type"
604                                (item :tag "Type/Subtype"
605                                      (cons symbol symbol))
606                                (item :tag "Type" symbol)
607                                (item :tag "Default" t))
608                        integer)))
609
610 (defun mime-preview-multipart/alternative (entity situation)
611   (let* ((children (mime-entity-children entity))
612          (default-situation
613            (cdr (assq 'childrens-situation situation)))
614          (i 0)
615          (p 0)
616          (max-score 0)
617          (situations
618           (mapcar (function
619                    (lambda (child)
620                      (let ((situation
621                             (or (ctree-match-calist
622                                  mime-preview-condition
623                                  (append (mime-entity-situation child)
624                                          default-situation))
625                                 default-situation)))
626                        (if (cdr (assq 'body-presentation-method situation))
627                            (let ((score
628                                   (cdr
629                                    (or (assoc
630                                         (cons
631                                          (cdr (assq 'type situation))
632                                          (cdr (assq 'subtype situation)))
633                                         mime-view-type-subtype-score-alist)
634                                        (assq
635                                         (cdr (assq 'type situation))
636                                         mime-view-type-subtype-score-alist)
637                                        (assq
638                                         t
639                                         mime-view-type-subtype-score-alist)
640                                        ))))
641                              (if (> score max-score)
642                                  (setq p i
643                                        max-score score)
644                                )))
645                        (setq i (1+ i))
646                        situation)
647                      ))
648                   children)))
649     (setq i 0)
650     (while children
651       (let ((child (car children))
652             (situation (car situations)))
653         (mime-view-display-entity child
654                                   (save-excursion
655                                     (set-buffer (mime-entity-buffer child))
656                                     mime-raw-message-info)
657                                   (current-buffer)
658                                   default-situation
659                                   (if (= i p)
660                                       situation
661                                     (del-alist 'body-presentation-method
662                                                (copy-alist situation))))
663         )
664       (setq children (cdr children)
665             situations (cdr situations)
666             i (1+ i))
667       )))
668
669
670 ;;; @ acting-condition
671 ;;;
672
673 (defvar mime-acting-condition nil
674   "Condition-tree about how to process entity.")
675
676 (if (file-readable-p mailcap-file)
677     (let ((entries (mailcap-parse-file)))
678       (while entries
679         (let ((entry (car entries))
680               view print shared)
681           (while entry
682             (let* ((field (car entry))
683                    (field-type (car field)))
684               (cond ((eq field-type 'view)  (setq view field))
685                     ((eq field-type 'print) (setq print field))
686                     ((memq field-type '(compose composetyped edit)))
687                     (t (setq shared (cons field shared))))
688               )
689             (setq entry (cdr entry))
690             )
691           (setq shared (nreverse shared))
692           (ctree-set-calist-with-default
693            'mime-acting-condition
694            (append shared (list '(mode . "play")(cons 'method (cdr view)))))
695           (if print
696               (ctree-set-calist-with-default
697                'mime-acting-condition
698                (append shared
699                        (list '(mode . "print")(cons 'method (cdr view))))
700                ))
701           )
702         (setq entries (cdr entries))
703         )))
704
705 (ctree-set-calist-strictly
706  'mime-acting-condition
707  '((type . application)(subtype . octet-stream)
708    (mode . "play")
709    (method . mime-method-to-detect)
710    ))
711
712 (ctree-set-calist-with-default
713  'mime-acting-condition
714  '((mode . "extract")
715    (method . mime-method-to-save)))
716
717 (ctree-set-calist-strictly
718  'mime-acting-condition
719  '((type . text)(subtype . x-rot13-47)(mode . "play")
720    (method . mime-method-to-display-caesar)
721    ))
722 (ctree-set-calist-strictly
723  'mime-acting-condition
724  '((type . text)(subtype . x-rot13-47-48)(mode . "play")
725    (method . mime-method-to-display-caesar)
726    ))
727
728 (ctree-set-calist-strictly
729  'mime-acting-condition
730  '((type . message)(subtype . rfc822)(mode . "play")
731    (method . mime-method-to-display-message/rfc822)
732    ))
733 (ctree-set-calist-strictly
734  'mime-acting-condition
735  '((type . message)(subtype . partial)(mode . "play")
736    (method . mime-method-to-store-message/partial)
737    ))
738
739 (ctree-set-calist-strictly
740  'mime-acting-condition
741  '((type . message)(subtype . external-body)
742    ("access-type" . "anon-ftp")
743    (method . mime-method-to-display-message/external-ftp)
744    ))
745
746 (ctree-set-calist-strictly
747  'mime-acting-condition
748  '((type . application)(subtype . octet-stream)
749    (method . mime-method-to-save)
750    ))
751
752
753 ;;; @ quitting method
754 ;;;
755
756 (defvar mime-preview-quitting-method-alist
757   '((mime-show-message-mode
758      . mime-preview-quitting-method-for-mime-show-message-mode))
759   "Alist of major-mode vs. quitting-method of mime-view.")
760
761 (defvar mime-preview-over-to-previous-method-alist nil
762   "Alist of major-mode vs. over-to-previous-method of mime-view.")
763
764 (defvar mime-preview-over-to-next-method-alist nil
765   "Alist of major-mode vs. over-to-next-method of mime-view.")
766
767
768 ;;; @ following method
769 ;;;
770
771 (defvar mime-view-following-method-alist nil
772   "Alist of major-mode vs. following-method of mime-view.")
773
774 (defvar mime-view-following-required-fields-list
775   '("From"))
776
777
778 ;;; @ X-Face
779 ;;;
780
781 ;; hack from Gnus 5.0.4.
782
783 (defvar mime-view-x-face-to-pbm-command
784   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
785
786 (defvar mime-view-x-face-command
787   (concat mime-view-x-face-to-pbm-command
788           " | xv -quit -")
789   "String to be executed to display an X-Face field.
790 The command will be executed in a sub-shell asynchronously.
791 The compressed face will be piped to this command.")
792
793 (defun mime-view-x-face-function ()
794   "Function to display X-Face field. You can redefine to customize."
795   ;; 1995/10/12 (c.f. tm-eng:130)
796   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
797   (save-restriction
798     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
799     ;; end
800     (goto-char (point-min))
801     (if (re-search-forward "^X-Face:[ \t]*" nil t)
802         (let ((beg (match-end 0))
803               (end (std11-field-end))
804               )
805           (call-process-region beg end "sh" nil 0 nil
806                                "-c" mime-view-x-face-command)
807           ))))
808
809
810 ;;; @ buffer setup
811 ;;;
812
813 (defun mime-view-display-entity (entity message-info obuf
814                                         default-situation
815                                         &optional situation)
816   (let* ((raw-buffer (mime-entity-buffer entity))
817          (start (mime-entity-point-min entity))
818          (end (mime-entity-point-max entity))
819          original-major-mode end-of-header e nb ne subj)
820     (set-buffer raw-buffer)
821     (setq original-major-mode major-mode)
822     (goto-char start)
823     (setq end-of-header (if (re-search-forward "^$" nil t)
824                             (1+ (match-end 0))
825                           end))
826     (if (> end-of-header end)
827         (setq end-of-header end)
828       )
829     (save-restriction
830       (narrow-to-region start end)
831       (setq subj (eword-decode-string (mime-raw-get-subject entity)))
832       )
833     (or situation
834         (setq situation
835               (or (ctree-match-calist mime-preview-condition
836                                       (append (mime-entity-situation entity)
837                                               default-situation))
838                   default-situation)))
839     (let ((button-is-invisible
840            (eq (cdr (assq 'entity-button situation)) 'invisible))
841           (header-is-visible
842            (eq (cdr (assq 'header situation)) 'visible))
843           (body-presentation-method
844            (cdr (assq 'body-presentation-method situation)))
845           (children (mime-entity-children entity)))
846       (set-buffer obuf)
847       (setq nb (point))
848       (narrow-to-region nb nb)
849       (or button-is-invisible
850           (if (mime-view-entity-button-visible-p entity)
851               (mime-view-insert-entity-button entity subj)
852             ))
853       (if header-is-visible
854           (save-restriction
855             (narrow-to-region (point)(point))
856             (insert-buffer-substring raw-buffer start end-of-header)
857             (let ((f (cdr (assq original-major-mode
858                                 mime-view-content-header-filter-alist))))
859               (if (functionp f)
860                   (funcall f)
861                 (mime-view-default-content-header-filter)
862                 ))
863             (run-hooks 'mime-view-content-header-filter-hook)
864             ))
865       (cond ((eq body-presentation-method 'with-filter)
866              (let ((body-filter (cdr (assq 'body-filter situation))))
867                (save-restriction
868                  (narrow-to-region (point-max)(point-max))
869                  (insert-buffer-substring raw-buffer end-of-header end)
870                  (funcall body-filter situation)
871                  )))
872             (children)
873             ((functionp body-presentation-method)
874              (funcall body-presentation-method entity situation)
875              )
876             (t
877              (when button-is-invisible
878                (goto-char (point-max))
879                (mime-view-insert-entity-button entity subj)
880                )
881              (or header-is-visible
882                  (progn
883                    (goto-char (point-max))
884                    (insert "\n")
885                    ))
886              ))
887       (setq ne (point-max))
888       (widen)
889       (put-text-property nb ne 'mime-view-entity entity)
890       (goto-char ne)
891       (if children
892           (if (functionp body-presentation-method)
893               (funcall body-presentation-method entity situation)
894             (mime-preview-multipart/mixed entity situation)
895             ))
896       )))
897
898
899 ;;; @ MIME viewer mode
900 ;;;
901
902 (defconst mime-view-menu-title "MIME-View")
903 (defconst mime-view-menu-list
904   '((up          "Move to upper entity"    mime-preview-move-to-upper)
905     (previous    "Move to previous entity" mime-preview-move-to-previous)
906     (next        "Move to next entity"     mime-preview-move-to-next)
907     (scroll-down "Scroll-down"             mime-preview-scroll-down-entity)
908     (scroll-up   "Scroll-up"               mime-preview-scroll-up-entity)
909     (play        "Play current entity"     mime-preview-play-current-entity)
910     (extract     "Extract current entity"  mime-preview-extract-current-entity)
911     (print       "Print current entity"    mime-preview-print-current-entity)
912     (x-face      "Show X Face"             mime-preview-display-x-face)
913     )
914   "Menu for MIME Viewer")
915
916 (cond (running-xemacs
917        (defvar mime-view-xemacs-popup-menu
918          (cons mime-view-menu-title
919                (mapcar (function
920                         (lambda (item)
921                           (vector (nth 1 item)(nth 2 item) t)
922                           ))
923                        mime-view-menu-list)))
924        (defun mime-view-xemacs-popup-menu (event)
925          "Popup the menu in the MIME Viewer buffer"
926          (interactive "e")
927          (select-window (event-window event))
928          (set-buffer (event-buffer event))
929          (popup-menu 'mime-view-xemacs-popup-menu))
930        (defvar mouse-button-2 'button2)
931        )
932       (t
933        (defvar mouse-button-2 [mouse-2])
934        ))
935
936 (defun mime-view-define-keymap (&optional default)
937   (let ((mime-view-mode-map (if (keymapp default)
938                                 (copy-keymap default)
939                               (make-sparse-keymap)
940                               )))
941     (define-key mime-view-mode-map
942       "u"        (function mime-preview-move-to-upper))
943     (define-key mime-view-mode-map
944       "p"        (function mime-preview-move-to-previous))
945     (define-key mime-view-mode-map
946       "n"        (function mime-preview-move-to-next))
947     (define-key mime-view-mode-map
948       "\e\t"     (function mime-preview-move-to-previous))
949     (define-key mime-view-mode-map
950       "\t"       (function mime-preview-move-to-next))
951     (define-key mime-view-mode-map
952       " "        (function mime-preview-scroll-up-entity))
953     (define-key mime-view-mode-map
954       "\M- "     (function mime-preview-scroll-down-entity))
955     (define-key mime-view-mode-map
956       "\177"     (function mime-preview-scroll-down-entity))
957     (define-key mime-view-mode-map
958       "\C-m"     (function mime-preview-next-line-entity))
959     (define-key mime-view-mode-map
960       "\C-\M-m"  (function mime-preview-previous-line-entity))
961     (define-key mime-view-mode-map
962       "v"        (function mime-preview-play-current-entity))
963     (define-key mime-view-mode-map
964       "e"        (function mime-preview-extract-current-entity))
965     (define-key mime-view-mode-map
966       "\C-c\C-p" (function mime-preview-print-current-entity))
967     (define-key mime-view-mode-map
968       "a"        (function mime-preview-follow-current-entity))
969     (define-key mime-view-mode-map
970       "q"        (function mime-preview-quit))
971     (define-key mime-view-mode-map
972       "\C-c\C-x" (function mime-preview-kill-buffer))
973     ;; (define-key mime-view-mode-map
974     ;;   "<"        (function beginning-of-buffer))
975     ;; (define-key mime-view-mode-map
976     ;;   ">"        (function end-of-buffer))
977     (define-key mime-view-mode-map
978       "?"        (function describe-mode))
979     (define-key mime-view-mode-map
980       [tab] (function mime-preview-move-to-next))
981     (define-key mime-view-mode-map
982       [delete] (function mime-preview-scroll-down-entity))
983     (define-key mime-view-mode-map
984       [backspace] (function mime-preview-scroll-down-entity))
985     (if (functionp default)
986         (cond (running-xemacs
987                (set-keymap-default-binding mime-view-mode-map default)
988                )
989               (t
990                (setq mime-view-mode-map
991                      (append mime-view-mode-map (list (cons t default))))
992                )))
993     (if mouse-button-2
994         (define-key mime-view-mode-map
995           mouse-button-2 (function mime-button-dispatcher))
996       )
997     (cond (running-xemacs
998            (define-key mime-view-mode-map
999              mouse-button-3 (function mime-view-xemacs-popup-menu))
1000            )
1001           ((>= emacs-major-version 19)
1002            (define-key mime-view-mode-map [menu-bar mime-view]
1003              (cons mime-view-menu-title
1004                    (make-sparse-keymap mime-view-menu-title)))
1005            (mapcar (function
1006                     (lambda (item)
1007                       (define-key mime-view-mode-map
1008                         (vector 'menu-bar 'mime-view (car item))
1009                         (cons (nth 1 item)(nth 2 item))
1010                         )
1011                       ))
1012                    (reverse mime-view-menu-list)
1013                    )
1014            ))
1015     (use-local-map mime-view-mode-map)
1016     (run-hooks 'mime-view-define-keymap-hook)
1017     ))
1018
1019 (defsubst mime-maybe-hide-echo-buffer ()
1020   "Clear mime-echo buffer and delete window for it."
1021   (let ((buf (get-buffer mime-echo-buffer-name)))
1022     (if buf
1023         (save-excursion
1024           (set-buffer buf)
1025           (erase-buffer)
1026           (let ((win (get-buffer-window buf)))
1027             (if win
1028                 (delete-window win)
1029               ))
1030           (bury-buffer buf)
1031           ))))
1032
1033 (defvar mime-view-redisplay nil)
1034
1035 (defun mime-view-display-message (message &optional preview-buffer
1036                                           mother default-keymap-or-function)
1037   (mime-maybe-hide-echo-buffer)
1038   (let ((win-conf (current-window-configuration))
1039         (raw-buffer (mime-entity-buffer message)))
1040     (or preview-buffer
1041         (setq preview-buffer
1042               (concat "*Preview-" (buffer-name raw-buffer) "*")))
1043     (set-buffer raw-buffer)
1044     (setq mime-raw-message-info (mime-parse-message))
1045     (setq mime-preview-buffer preview-buffer)
1046     (let ((inhibit-read-only t))
1047       (switch-to-buffer preview-buffer)
1048       (widen)
1049       (erase-buffer)
1050       (setq mime-raw-buffer raw-buffer)
1051       (if mother
1052           (setq mime-mother-buffer mother)
1053         )
1054       (setq mime-preview-original-window-configuration win-conf)
1055       (setq major-mode 'mime-view-mode)
1056       (setq mode-name "MIME-View")
1057       (mime-view-display-entity message message
1058                                 preview-buffer
1059                                 '((entity-button . invisible)
1060                                   (header . visible)
1061                                   ))
1062       (mime-view-define-keymap default-keymap-or-function)
1063       (let ((point
1064              (next-single-property-change (point-min) 'mime-view-entity)))
1065         (if point
1066             (goto-char point)
1067           (goto-char (point-min))
1068           (search-forward "\n\n" nil t)
1069           ))
1070       (run-hooks 'mime-view-mode-hook)
1071       ))
1072   (set-buffer-modified-p nil)
1073   (setq buffer-read-only t)
1074   )
1075
1076 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1077                                    default-keymap-or-function)
1078   (interactive)
1079   (mime-view-display-message
1080    (save-excursion
1081      (if raw-buffer (set-buffer raw-buffer))
1082      (mime-parse-message)
1083      )
1084    preview-buffer mother default-keymap-or-function))
1085
1086 (defun mime-view-mode (&optional mother ctl encoding
1087                                  raw-buffer preview-buffer
1088                                  default-keymap-or-function)
1089   "Major mode for viewing MIME message.
1090
1091 Here is a list of the standard keys for mime-view-mode.
1092
1093 key             feature
1094 ---             -------
1095
1096 u               Move to upper content
1097 p or M-TAB      Move to previous content
1098 n or TAB        Move to next content
1099 SPC             Scroll up or move to next content
1100 M-SPC or DEL    Scroll down or move to previous content
1101 RET             Move to next line
1102 M-RET           Move to previous line
1103 v               Decode current content as `play mode'
1104 e               Decode current content as `extract mode'
1105 C-c C-p         Decode current content as `print mode'
1106 a               Followup to current content.
1107 q               Quit
1108 button-2        Move to point under the mouse cursor
1109                 and decode current content as `play mode'
1110 "
1111   (interactive)
1112   (mime-view-display-message
1113    (save-excursion
1114      (if raw-buffer (set-buffer raw-buffer))
1115      (or mime-view-redisplay
1116          (mime-parse-message ctl encoding))
1117      )
1118    preview-buffer mother default-keymap-or-function))
1119
1120
1121 ;;; @@ playing
1122 ;;;
1123
1124 (autoload 'mime-preview-play-current-entity "mime-play"
1125   "Play current entity." t)
1126
1127 (defun mime-preview-extract-current-entity ()
1128   "Extract current entity into file (maybe).
1129 It decodes current entity to call internal or external method as
1130 \"extract\" mode.  The method is selected from variable
1131 `mime-acting-condition'."
1132   (interactive)
1133   (mime-preview-play-current-entity "extract")
1134   )
1135
1136 (defun mime-preview-print-current-entity ()
1137   "Print current entity (maybe).
1138 It decodes current entity to call internal or external method as
1139 \"print\" mode.  The method is selected from variable
1140 `mime-acting-condition'."
1141   (interactive)
1142   (mime-preview-play-current-entity "print")
1143   )
1144
1145
1146 ;;; @@ following
1147 ;;;
1148
1149 (defun mime-preview-follow-current-entity ()
1150   "Write follow message to current entity.
1151 It calls following-method selected from variable
1152 `mime-view-following-method-alist'."
1153   (interactive)
1154   (let (entity)
1155     (while (null (setq entity
1156                        (get-text-property (point) 'mime-view-entity)))
1157       (backward-char)
1158       )
1159     (let* ((p-beg
1160             (previous-single-property-change (point) 'mime-view-entity))
1161            p-end
1162            (entity-node-id (mime-entity-node-id entity))
1163            (len (length entity-node-id))
1164            )
1165       (cond ((null p-beg)
1166              (setq p-beg
1167                    (if (eq (next-single-property-change (point-min)
1168                                                         'mime-view-entity)
1169                            (point))
1170                        (point)
1171                      (point-min)))
1172              )
1173             ((eq (next-single-property-change p-beg 'mime-view-entity)
1174                  (point))
1175              (setq p-beg (point))
1176              ))
1177       (setq p-end (next-single-property-change p-beg 'mime-view-entity))
1178       (cond ((null p-end)
1179              (setq p-end (point-max))
1180              )
1181             ((null entity-node-id)
1182              (setq p-end (point-max))
1183              )
1184             (t
1185              (save-excursion
1186                (goto-char p-end)
1187                (catch 'tag
1188                  (let (e)
1189                    (while (setq e
1190                                 (next-single-property-change
1191                                  (point) 'mime-view-entity))
1192                      (goto-char e)
1193                      (let ((rc (mime-entity-node-id
1194                                 (get-text-property (point)
1195                                                    'mime-view-entity))))
1196                        (or (equal entity-node-id
1197                                   (nthcdr (- (length rc) len) rc))
1198                            (throw 'tag nil)
1199                            ))
1200                      (setq p-end e)
1201                      ))
1202                  (setq p-end (point-max))
1203                  ))
1204              ))
1205       (let* ((mode (mime-preview-original-major-mode 'recursive))
1206              (new-name
1207               (format "%s-%s" (buffer-name) (reverse entity-node-id)))
1208              new-buf
1209              (the-buf (current-buffer))
1210              (a-buf mime-raw-buffer)
1211              fields)
1212         (save-excursion
1213           (set-buffer (setq new-buf (get-buffer-create new-name)))
1214           (erase-buffer)
1215           (insert-buffer-substring the-buf p-beg p-end)
1216           (goto-char (point-min))
1217           (let ((entity-node-id (mime-entity-node-id entity)) ci str)
1218             (while (progn
1219                      (setq
1220                       str
1221                       (save-excursion
1222                         (set-buffer a-buf)
1223                         (setq
1224                          ci
1225                          (mime-raw-find-entity-from-node-id entity-node-id))
1226                         (save-restriction
1227                           (narrow-to-region
1228                            (mime-entity-point-min ci)
1229                            (mime-entity-point-max ci)
1230                            )
1231                           (std11-header-string-except
1232                            (concat "^"
1233                                    (apply (function regexp-or) fields)
1234                                    ":") ""))))
1235                      (if (and
1236                           (eq (mime-entity-media-type ci) 'message)
1237                           (eq (mime-entity-media-subtype ci) 'rfc822))
1238                          nil
1239                        (if str
1240                            (insert str)
1241                          )
1242                        entity-node-id))
1243               (setq fields (std11-collect-field-names)
1244                     entity-node-id (cdr entity-node-id))
1245               )
1246             )
1247           (let ((rest mime-view-following-required-fields-list))
1248             (while rest
1249               (let ((field-name (car rest)))
1250                 (or (std11-field-body field-name)
1251                     (insert
1252                      (format
1253                       (concat field-name
1254                               ": "
1255                               (save-excursion
1256                                 (set-buffer the-buf)
1257                                 (set-buffer mime-mother-buffer)
1258                                 (set-buffer mime-raw-buffer)
1259                                 (std11-field-body field-name)
1260                                 )
1261                               "\n")))
1262                     ))
1263               (setq rest (cdr rest))
1264               ))
1265           (eword-decode-header)
1266           )
1267         (let ((f (cdr (assq mode mime-view-following-method-alist))))
1268           (if (functionp f)
1269               (funcall f new-buf)
1270             (message
1271              (format
1272               "Sorry, following method for %s is not implemented yet."
1273               mode))
1274             ))
1275         ))))
1276
1277
1278 ;;; @@ X-Face
1279 ;;;
1280
1281 (defun mime-preview-display-x-face ()
1282   (interactive)
1283   (save-window-excursion
1284     (set-buffer mime-raw-buffer)
1285     (mime-view-x-face-function)
1286     ))
1287
1288
1289 ;;; @@ moving
1290 ;;;
1291
1292 (defun mime-preview-move-to-upper ()
1293   "Move to upper entity.
1294 If there is no upper entity, call function `mime-preview-quit'."
1295   (interactive)
1296   (let (cinfo)
1297     (while (null (setq cinfo
1298                        (get-text-property (point) 'mime-view-entity)))
1299       (backward-char)
1300       )
1301     (let ((r (mime-raw-find-entity-from-node-id
1302               (cdr (mime-entity-node-id cinfo))
1303               (get-text-property 1 'mime-view-entity)))
1304           point)
1305       (catch 'tag
1306         (while (setq point (previous-single-property-change
1307                             (point) 'mime-view-entity))
1308           (goto-char point)
1309           (if (eq r (get-text-property (point) 'mime-view-entity))
1310               (throw 'tag t)
1311             )
1312           )
1313         (mime-preview-quit)
1314         ))))
1315
1316 (defun mime-preview-move-to-previous ()
1317   "Move to previous entity.
1318 If there is no previous entity, it calls function registered in
1319 variable `mime-preview-over-to-previous-method-alist'."
1320   (interactive)
1321   (while (null (get-text-property (point) 'mime-view-entity))
1322     (backward-char)
1323     )
1324   (let ((point (previous-single-property-change (point) 'mime-view-entity)))
1325     (if point
1326         (if (get-text-property (1- point) 'mime-view-entity)
1327             (goto-char point)
1328           (goto-char (1- point))
1329           (mime-preview-move-to-previous)
1330           )
1331       (let ((f (assq (mime-preview-original-major-mode)
1332                      mime-preview-over-to-previous-method-alist)))
1333         (if f
1334             (funcall (cdr f))
1335           ))
1336       )))
1337
1338 (defun mime-preview-move-to-next ()
1339   "Move to next entity.
1340 If there is no previous entity, it calls function registered in
1341 variable `mime-preview-over-to-next-method-alist'."
1342   (interactive)
1343   (while (null (get-text-property (point) 'mime-view-entity))
1344     (forward-char)
1345     )
1346   (let ((point (next-single-property-change (point) 'mime-view-entity)))
1347     (if point
1348         (progn
1349           (goto-char point)
1350           (if (null (get-text-property point 'mime-view-entity))
1351               (mime-preview-move-to-next)
1352             ))
1353       (let ((f (assq (mime-preview-original-major-mode)
1354                      mime-preview-over-to-next-method-alist)))
1355         (if f
1356             (funcall (cdr f))
1357           ))
1358       )))
1359
1360 (defun mime-preview-scroll-up-entity (&optional h)
1361   "Scroll up current entity.
1362 If reached to (point-max), it calls function registered in variable
1363 `mime-preview-over-to-next-method-alist'."
1364   (interactive)
1365   (or h
1366       (setq h (1- (window-height)))
1367       )
1368   (if (= (point) (point-max))
1369       (let ((f (assq (mime-preview-original-major-mode)
1370                      mime-preview-over-to-next-method-alist)))
1371         (if f
1372             (funcall (cdr f))
1373           ))
1374     (let ((point
1375            (or (next-single-property-change (point) 'mime-view-entity)
1376                (point-max))))
1377       (forward-line h)
1378       (if (> (point) point)
1379           (goto-char point)
1380         )
1381       )))
1382
1383 (defun mime-preview-scroll-down-entity (&optional h)
1384   "Scroll down current entity.
1385 If reached to (point-min), it calls function registered in variable
1386 `mime-preview-over-to-previous-method-alist'."
1387   (interactive)
1388   (or h
1389       (setq h (1- (window-height)))
1390       )
1391   (if (= (point) (point-min))
1392       (let ((f (assq (mime-preview-original-major-mode)
1393                      mime-preview-over-to-previous-method-alist)))
1394         (if f
1395             (funcall (cdr f))
1396           ))
1397     (let ((point
1398            (or (previous-single-property-change (point) 'mime-view-entity)
1399                (point-min))))
1400       (forward-line (- h))
1401       (if (< (point) point)
1402           (goto-char point)
1403         ))))
1404
1405 (defun mime-preview-next-line-entity ()
1406   (interactive)
1407   (mime-preview-scroll-up-entity 1)
1408   )
1409
1410 (defun mime-preview-previous-line-entity ()
1411   (interactive)
1412   (mime-preview-scroll-down-entity 1)
1413   )
1414
1415
1416 ;;; @@ quitting
1417 ;;;
1418
1419 (defun mime-preview-quit ()
1420   "Quit from MIME-preview buffer.
1421 It calls function registered in variable
1422 `mime-preview-quitting-method-alist'."
1423   (interactive)
1424   (let ((r (assq (mime-preview-original-major-mode)
1425                  mime-preview-quitting-method-alist)))
1426     (if r
1427         (funcall (cdr r))
1428       )))
1429
1430 (defun mime-preview-kill-buffer ()
1431   (interactive)
1432   (kill-buffer (current-buffer))
1433   )
1434
1435
1436 ;;; @ end
1437 ;;;
1438
1439 (provide 'mime-view)
1440
1441 (run-hooks 'mime-view-load-hook)
1442
1443 ;;; mime-view.el ends here