(mime-preview-over-to-previous-method-alist): Add DOC-string.
[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-preview-over-to-previous-method-alist nil
761   "Alist of major-mode vs. over-to-previous-method of mime-view.")
762
763 (defvar mime-preview-over-to-next-method-alist nil
764   "Alist of major-mode vs. over-to-next-method of mime-view.")
765
766
767 ;;; @ following method
768 ;;;
769
770 (defvar mime-view-following-method-alist nil
771   "Alist of major-mode vs. following-method of mime-view.")
772
773 (defvar mime-view-following-required-fields-list
774   '("From"))
775
776
777 ;;; @ X-Face
778 ;;;
779
780 ;; hack from Gnus 5.0.4.
781
782 (defvar mime-view-x-face-to-pbm-command
783   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
784
785 (defvar mime-view-x-face-command
786   (concat mime-view-x-face-to-pbm-command
787           " | xv -quit -")
788   "String to be executed to display an X-Face field.
789 The command will be executed in a sub-shell asynchronously.
790 The compressed face will be piped to this command.")
791
792 (defun mime-view-x-face-function ()
793   "Function to display X-Face field. You can redefine to customize."
794   ;; 1995/10/12 (c.f. tm-eng:130)
795   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
796   (save-restriction
797     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
798     ;; end
799     (goto-char (point-min))
800     (if (re-search-forward "^X-Face:[ \t]*" nil t)
801         (let ((beg (match-end 0))
802               (end (std11-field-end))
803               )
804           (call-process-region beg end "sh" nil 0 nil
805                                "-c" mime-view-x-face-command)
806           ))))
807
808
809 ;;; @ buffer setup
810 ;;;
811
812 (defun mime-view-display-entity (entity message-info obuf
813                                         default-situation
814                                         &optional situation)
815   (let* ((raw-buffer (mime-entity-buffer entity))
816          (start (mime-entity-point-min entity))
817          (end (mime-entity-point-max entity))
818          original-major-mode end-of-header e nb ne subj)
819     (set-buffer raw-buffer)
820     (setq original-major-mode major-mode)
821     (goto-char start)
822     (setq end-of-header (if (re-search-forward "^$" nil t)
823                             (1+ (match-end 0))
824                           end))
825     (if (> end-of-header end)
826         (setq end-of-header end)
827       )
828     (save-restriction
829       (narrow-to-region start end)
830       (setq subj (eword-decode-string (mime-raw-get-subject entity)))
831       )
832     (or situation
833         (setq situation
834               (or (ctree-match-calist mime-preview-condition
835                                       (append (mime-entity-situation entity)
836                                               default-situation))
837                   default-situation)))
838     (let ((button-is-invisible
839            (eq (cdr (assq 'entity-button situation)) 'invisible))
840           (header-is-visible
841            (eq (cdr (assq 'header situation)) 'visible))
842           (body-presentation-method
843            (cdr (assq 'body-presentation-method situation)))
844           (children (mime-entity-children entity)))
845       (set-buffer obuf)
846       (setq nb (point))
847       (narrow-to-region nb nb)
848       (or button-is-invisible
849           (if (mime-view-entity-button-visible-p entity)
850               (mime-view-insert-entity-button entity subj)
851             ))
852       (if header-is-visible
853           (save-restriction
854             (narrow-to-region (point)(point))
855             (insert-buffer-substring raw-buffer start end-of-header)
856             (let ((f (cdr (assq original-major-mode
857                                 mime-view-content-header-filter-alist))))
858               (if (functionp f)
859                   (funcall f)
860                 (mime-view-default-content-header-filter)
861                 ))
862             (run-hooks 'mime-view-content-header-filter-hook)
863             ))
864       (cond ((eq body-presentation-method 'with-filter)
865              (let ((body-filter (cdr (assq 'body-filter situation))))
866                (save-restriction
867                  (narrow-to-region (point-max)(point-max))
868                  (insert-buffer-substring raw-buffer end-of-header end)
869                  (funcall body-filter situation)
870                  )))
871             (children)
872             ((functionp body-presentation-method)
873              (funcall body-presentation-method entity situation)
874              )
875             (t
876              (when button-is-invisible
877                (goto-char (point-max))
878                (mime-view-insert-entity-button entity subj)
879                )
880              (or header-is-visible
881                  (progn
882                    (goto-char (point-max))
883                    (insert "\n")
884                    ))
885              ))
886       (setq ne (point-max))
887       (widen)
888       (put-text-property nb ne 'mime-view-entity entity)
889       (goto-char ne)
890       (if children
891           (if (functionp body-presentation-method)
892               (funcall body-presentation-method entity situation)
893             (mime-preview-multipart/mixed entity situation)
894             ))
895       )))
896
897
898 ;;; @ MIME viewer mode
899 ;;;
900
901 (defconst mime-view-menu-title "MIME-View")
902 (defconst mime-view-menu-list
903   '((up          "Move to upper entity"    mime-preview-move-to-upper)
904     (previous    "Move to previous entity" mime-preview-move-to-previous)
905     (next        "Move to next entity"     mime-preview-move-to-next)
906     (scroll-down "Scroll-down"             mime-preview-scroll-down-entity)
907     (scroll-up   "Scroll-up"               mime-preview-scroll-up-entity)
908     (play        "Play current entity"     mime-preview-play-current-entity)
909     (extract     "Extract current entity"  mime-preview-extract-current-entity)
910     (print       "Print current entity"    mime-preview-print-current-entity)
911     (x-face      "Show X Face"             mime-preview-display-x-face)
912     )
913   "Menu for MIME Viewer")
914
915 (cond (running-xemacs
916        (defvar mime-view-xemacs-popup-menu
917          (cons mime-view-menu-title
918                (mapcar (function
919                         (lambda (item)
920                           (vector (nth 1 item)(nth 2 item) t)
921                           ))
922                        mime-view-menu-list)))
923        (defun mime-view-xemacs-popup-menu (event)
924          "Popup the menu in the MIME Viewer buffer"
925          (interactive "e")
926          (select-window (event-window event))
927          (set-buffer (event-buffer event))
928          (popup-menu 'mime-view-xemacs-popup-menu))
929        (defvar mouse-button-2 'button2)
930        )
931       (t
932        (defvar mouse-button-2 [mouse-2])
933        ))
934
935 (defun mime-view-define-keymap (&optional default)
936   (let ((mime-view-mode-map (if (keymapp default)
937                                 (copy-keymap default)
938                               (make-sparse-keymap)
939                               )))
940     (define-key mime-view-mode-map
941       "u"        (function mime-preview-move-to-upper))
942     (define-key mime-view-mode-map
943       "p"        (function mime-preview-move-to-previous))
944     (define-key mime-view-mode-map
945       "n"        (function mime-preview-move-to-next))
946     (define-key mime-view-mode-map
947       "\e\t"     (function mime-preview-move-to-previous))
948     (define-key mime-view-mode-map
949       "\t"       (function mime-preview-move-to-next))
950     (define-key mime-view-mode-map
951       " "        (function mime-preview-scroll-up-entity))
952     (define-key mime-view-mode-map
953       "\M- "     (function mime-preview-scroll-down-entity))
954     (define-key mime-view-mode-map
955       "\177"     (function mime-preview-scroll-down-entity))
956     (define-key mime-view-mode-map
957       "\C-m"     (function mime-preview-next-line-entity))
958     (define-key mime-view-mode-map
959       "\C-\M-m"  (function mime-preview-previous-line-entity))
960     (define-key mime-view-mode-map
961       "v"        (function mime-preview-play-current-entity))
962     (define-key mime-view-mode-map
963       "e"        (function mime-preview-extract-current-entity))
964     (define-key mime-view-mode-map
965       "\C-c\C-p" (function mime-preview-print-current-entity))
966     (define-key mime-view-mode-map
967       "a"        (function mime-preview-follow-current-entity))
968     (define-key mime-view-mode-map
969       "q"        (function mime-preview-quit))
970     (define-key mime-view-mode-map
971       "\C-c\C-x" (function mime-preview-kill-buffer))
972     ;; (define-key mime-view-mode-map
973     ;;   "<"        (function beginning-of-buffer))
974     ;; (define-key mime-view-mode-map
975     ;;   ">"        (function end-of-buffer))
976     (define-key mime-view-mode-map
977       "?"        (function describe-mode))
978     (define-key mime-view-mode-map
979       [tab] (function mime-preview-move-to-next))
980     (define-key mime-view-mode-map
981       [delete] (function mime-preview-scroll-down-entity))
982     (define-key mime-view-mode-map
983       [backspace] (function mime-preview-scroll-down-entity))
984     (if (functionp default)
985         (cond (running-xemacs
986                (set-keymap-default-binding mime-view-mode-map default)
987                )
988               (t
989                (setq mime-view-mode-map
990                      (append mime-view-mode-map (list (cons t default))))
991                )))
992     (if mouse-button-2
993         (define-key mime-view-mode-map
994           mouse-button-2 (function mime-button-dispatcher))
995       )
996     (cond (running-xemacs
997            (define-key mime-view-mode-map
998              mouse-button-3 (function mime-view-xemacs-popup-menu))
999            )
1000           ((>= emacs-major-version 19)
1001            (define-key mime-view-mode-map [menu-bar mime-view]
1002              (cons mime-view-menu-title
1003                    (make-sparse-keymap mime-view-menu-title)))
1004            (mapcar (function
1005                     (lambda (item)
1006                       (define-key mime-view-mode-map
1007                         (vector 'menu-bar 'mime-view (car item))
1008                         (cons (nth 1 item)(nth 2 item))
1009                         )
1010                       ))
1011                    (reverse mime-view-menu-list)
1012                    )
1013            ))
1014     (use-local-map mime-view-mode-map)
1015     (run-hooks 'mime-view-define-keymap-hook)
1016     ))
1017
1018 (defsubst mime-maybe-hide-echo-buffer ()
1019   "Clear mime-echo buffer and delete window for it."
1020   (let ((buf (get-buffer mime-echo-buffer-name)))
1021     (if buf
1022         (save-excursion
1023           (set-buffer buf)
1024           (erase-buffer)
1025           (let ((win (get-buffer-window buf)))
1026             (if win
1027                 (delete-window win)
1028               ))
1029           (bury-buffer buf)
1030           ))))
1031
1032 (defvar mime-view-redisplay nil)
1033
1034 (defun mime-view-display-message (message &optional preview-buffer
1035                                           mother default-keymap-or-function)
1036   (mime-maybe-hide-echo-buffer)
1037   (let ((win-conf (current-window-configuration))
1038         (raw-buffer (mime-entity-buffer message)))
1039     (or preview-buffer
1040         (setq preview-buffer
1041               (concat "*Preview-" (buffer-name raw-buffer) "*")))
1042     (set-buffer raw-buffer)
1043     (setq mime-raw-message-info (mime-parse-message))
1044     (setq mime-preview-buffer preview-buffer)
1045     (let ((inhibit-read-only t))
1046       (switch-to-buffer preview-buffer)
1047       (widen)
1048       (erase-buffer)
1049       (setq mime-raw-buffer raw-buffer)
1050       (if mother
1051           (setq mime-mother-buffer mother)
1052         )
1053       (setq mime-preview-original-window-configuration win-conf)
1054       (setq major-mode 'mime-view-mode)
1055       (setq mode-name "MIME-View")
1056       (mime-view-display-entity message message
1057                                 preview-buffer
1058                                 '((entity-button . invisible)
1059                                   (header . visible)
1060                                   ))
1061       (mime-view-define-keymap default-keymap-or-function)
1062       (let ((point
1063              (next-single-property-change (point-min) 'mime-view-entity)))
1064         (if point
1065             (goto-char point)
1066           (goto-char (point-min))
1067           (search-forward "\n\n" nil t)
1068           ))
1069       (run-hooks 'mime-view-mode-hook)
1070       ))
1071   (set-buffer-modified-p nil)
1072   (setq buffer-read-only t)
1073   )
1074
1075 (defun mime-view-buffer (&optional raw-buffer preview-buffer mother
1076                                    default-keymap-or-function)
1077   (interactive)
1078   (mime-view-display-message
1079    (save-excursion
1080      (if raw-buffer (set-buffer raw-buffer))
1081      (mime-parse-message)
1082      )
1083    preview-buffer mother default-keymap-or-function))
1084
1085 (defun mime-view-mode (&optional mother ctl encoding
1086                                  raw-buffer preview-buffer
1087                                  default-keymap-or-function)
1088   "Major mode for viewing MIME message.
1089
1090 Here is a list of the standard keys for mime-view-mode.
1091
1092 key             feature
1093 ---             -------
1094
1095 u               Move to upper content
1096 p or M-TAB      Move to previous content
1097 n or TAB        Move to next content
1098 SPC             Scroll up or move to next content
1099 M-SPC or DEL    Scroll down or move to previous content
1100 RET             Move to next line
1101 M-RET           Move to previous line
1102 v               Decode current content as `play mode'
1103 e               Decode current content as `extract mode'
1104 C-c C-p         Decode current content as `print mode'
1105 a               Followup to current content.
1106 x               Display X-Face
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