`mime::preview/article-buffer' -> `mime-raw-buffer'.
[elisp/semi.git] / mime-view.el
1 ;;; mime-view.el --- interactive MIME viewer for GNU Emacs
2
3 ;; Copyright (C) 1995,1996,1997 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Created: 1994/7/13
7 ;;      Renamed: 1994/8/31 from tm-body.el
8 ;;      Renamed: 1997/02/19 from tm-view.el
9 ;; Version: $Revision: 0.73 $
10 ;; Keywords: MIME, multimedia, mail, news
11
12 ;; This file is part of SEMI (SEMI is Emacs MIME Interfaces).
13
14 ;; This program is free software; you can redistribute it and/or
15 ;; modify it under the terms of the GNU General Public License as
16 ;; published by the Free Software Foundation; either version 2, or (at
17 ;; your option) any later version.
18
19 ;; This program is distributed in the hope that it will be useful, but
20 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22 ;; General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
28
29 ;;; Code:
30
31 (require 'cl)
32 (require 'std11)
33 (require 'mel)
34 (require 'eword-decode)
35 (require 'mime-parse)
36 (require 'mime-text)
37
38
39 ;;; @ version
40 ;;;
41
42 (defconst mime-view-RCS-ID
43   "$Id: mime-view.el,v 0.73 1997-03-18 14:56:16 morioka Exp $")
44
45 (defconst mime-view-version (get-version-string mime-view-RCS-ID))
46
47
48 ;;; @ variables
49 ;;;
50
51 (defvar mime/content-decoding-condition
52   '(((type . "text/plain")
53      (method "tm-plain" nil 'file 'type 'encoding 'mode 'name)
54      (mode "play" "print")
55      )
56     ((type . "text/html")
57      (method "tm-html" nil 'file 'type 'encoding 'mode 'name)
58      (mode . "play")
59      )
60     ((type . "text/x-rot13-47")
61      (method . mime-article/decode-caesar)
62      (mode . "play")
63      )
64     ((type . "audio/basic")
65      (method "tm-au"    nil 'file 'type 'encoding 'mode 'name)
66      (mode . "play")
67      )
68     
69     ((type . "image/jpeg")
70      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
71      (mode "play" "print")
72      )
73     ((type . "image/gif")
74      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
75      (mode "play" "print")
76      )
77     ((type . "image/tiff")
78      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
79      (mode "play" "print")
80      )
81     ((type . "image/x-tiff")
82      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
83      (mode "play" "print")
84      )
85     ((type . "image/x-xbm")
86      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
87      (mode "play" "print")
88      )
89     ((type . "image/x-pic")
90      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
91      (mode "play" "print")
92      )
93     ((type . "image/x-mag")
94      (method "tm-image" nil 'file 'type 'encoding 'mode 'name)
95      (mode "play" "print")
96      )
97     
98     ((type . "video/mpeg")
99      (method "tm-mpeg"  nil 'file 'type 'encoding 'mode 'name)
100      (mode . "play")
101      )
102     
103     ((type . "application/postscript")
104      (method "tm-ps" nil 'file 'type 'encoding 'mode 'name)
105      (mode "play" "print")
106      )
107     ((type . "application/octet-stream")
108      (method "tm-file"  nil 'file 'type 'encoding 'mode 'name)
109      (mode "play" "print")
110      )
111     
112     ;;((type . "message/external-body")
113     ;; (method "xterm" nil
114     ;;         "-e" "showexternal"
115     ;;         'file '"access-type" '"name" '"site" '"directory"))
116     ((type . "message/external-body")
117      ("access-type" . "anon-ftp")
118      (method . mime-article/decode-message/external-ftp)
119      )
120     ((type . "message/rfc822")
121      (method . mime-article/view-message/rfc822)
122      (mode . "play")
123      )
124     ((type . "message/partial")
125      (method . mime-article/decode-message/partial)
126      (mode . "play")
127      )
128     
129     ((method "metamail" t "-m" "tm" "-x" "-d" "-z" "-e" 'file)
130      (mode . "play")
131      )
132     ((method "tm-file"  nil 'file 'type 'encoding 'mode 'name)
133      (mode . "extract")
134      )
135     ))
136
137 (defvar mime-view-childrens-header-showing-Content-Type-list
138   '("message/rfc822" "message/news"))
139
140 (defvar mime-view-visible-media-type-list
141   '("text/plain" nil "text/richtext" "text/enriched"
142     "text/rfc822-headers"
143     "text/x-latex" "application/x-latex"
144     "message/delivery-status"
145     "application/pgp" "text/x-pgp"
146     "application/octet-stream"
147     "application/x-selection" "application/x-comment")
148   "*List of media-types to be able to display in MIME-View buffer.
149 Each elements are string of TYPE/SUBTYPE, e.g. \"text/plain\".")
150
151 (defvar mime-view-content-button-visible-ctype-list
152   '("application/pgp"))
153
154 (defvar mime-view-uuencode-encoding-name-list '("x-uue" "x-uuencode"))
155
156 (defvar mime-view-ignored-field-list
157   '(".*Received" ".*Path" ".*Id" "References"
158     "Replied" "Errors-To"
159     "Lines" "Sender" ".*Host" "Xref"
160     "Content-Type" "Precedence"
161     "Status" "X-VM-.*")
162   "All fields that match this list will be hidden in MIME preview buffer.
163 Each elements are regexp of field-name. [mime-view.el]")
164
165 (defvar mime-view-ignored-field-regexp
166   (concat "^"
167           (apply (function regexp-or) mime-view-ignored-field-list)
168           ":"))
169
170 (defvar mime-view-visible-field-list '("Dnas.*" "Message-Id")
171   "All fields that match this list will be displayed in MIME preview buffer.
172 Each elements are regexp of field-name.")
173
174 (defvar mime-view-redisplay nil)
175
176 (defvar mime-view-announcement-for-message/partial
177   (if (and (>= emacs-major-version 19) window-system)
178       "\
179 \[[ This is message/partial style split message. ]]
180 \[[ Please press `v' key in this buffer          ]]
181 \[[ or click here by mouse button-2.             ]]"
182     "\
183 \[[ This is message/partial style split message. ]]
184 \[[ Please press `v' key in this buffer.         ]]"
185     ))
186
187
188 ;;; @@ predicate functions
189 ;;;
190
191 (defun mime-view-header-visible-p (rcnum cinfo)
192   "Return non-nil if header of current entity is visible."
193   (or (null rcnum)
194       (member (mime::content-info/type
195                (mime-article/rcnum-to-cinfo (cdr rcnum) cinfo))
196               mime-view-childrens-header-showing-Content-Type-list)
197       ))
198
199 (defun mime-view-body-visible-p (rcnum cinfo &optional ctype)
200   (let (ccinfo)
201     (or ctype
202         (setq ctype
203               (mime::content-info/type
204                (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
205                ))
206         )
207     (and (member ctype mime-view-visible-media-type-list)
208          (if (string-equal ctype "application/octet-stream")
209              (progn
210                (or ccinfo
211                    (setq ccinfo (mime-article/rcnum-to-cinfo rcnum cinfo))
212                    )
213                (member (mime::content-info/encoding ccinfo)
214                        '(nil "7bit" "8bit"))
215                )
216            t))
217     ))
218
219
220 ;;; @@ entity button
221 ;;;
222
223 (defun mime-view-insert-entity-button (rcnum cinfo ctype params subj encoding)
224   "Insert entity-button."
225   (save-restriction
226     (narrow-to-region (point)(point))
227     (let ((access-type (assoc "access-type" params))
228           (num (or (cdr (assoc "x-part-number" params))
229                    (if (consp rcnum)
230                        (mapconcat (function
231                                    (lambda (num)
232                                      (format "%s" (1+ num))
233                                      ))
234                                   (reverse rcnum) ".")
235                      "0"))
236                ))
237       (cond (access-type
238              (let ((server (assoc "server" params)))
239                (setq access-type (cdr access-type))
240                (if server
241                    (insert (format "[%s %s ([%s] %s)]\n" num subj
242                                    access-type (cdr server)))
243                  (let ((site (cdr (assoc "site" params)))
244                        (dir (cdr (assoc "directory" params)))
245                        )
246                    (insert (format "[%s %s ([%s] %s:%s)]\n" num subj
247                                    access-type site dir))
248                    )))
249              )
250             (t
251              (let ((charset (cdr (assoc "charset" params))))
252                (insert (concat "[" num " " subj))
253                (let ((rest
254                       (concat " <" ctype
255                               (if charset
256                                   (concat "; " charset)
257                                 (if encoding (concat " (" encoding ")"))
258                                 )
259                               ">]\n")))
260                  (if (>= (+ (current-column)(length rest))(window-width))
261                      (insert "\n\t")
262                    )
263                  (insert rest)
264                  ))))
265       )
266     (mime-add-button (point-min)(1- (point-max))
267                      (function mime-view-play-current-entity))
268     ))
269
270 (defun mime-view-entity-button-function
271   (rcnum cinfo ctype params subj encoding)
272   "Insert entity button conditionally.
273 Please redefine this function if you want to change default setting."
274   (or (null rcnum)
275       (string= ctype "application/x-selection")
276       (and (string= ctype "application/octet-stream")
277            (string= (mime::content-info/type
278                      (mime-article/rcnum-to-cinfo (cdr rcnum) cinfo))
279                     "multipart/encrypted"))
280       (mime-view-insert-entity-button rcnum cinfo ctype params subj encoding)
281       ))
282
283
284 ;;; @@ content header filter
285 ;;;
286
287 (defsubst mime-view-cut-header ()
288   (goto-char (point-min))
289   (while (re-search-forward mime-view-ignored-field-regexp nil t)
290     (let* ((beg (match-beginning 0))
291            (end (match-end 0))
292            (name (buffer-substring beg end))
293            )
294       (or (member-if (function
295                       (lambda (regexp)
296                         (string-match regexp name)
297                         )) mime-view-visible-field-list)
298           (delete-region beg
299                          (if (re-search-forward "^\\([^ \t]\\|$\\)" nil t)
300                              (match-beginning 0)
301                            (point-max)))
302           ))))
303
304 (defun mime-view-default-content-header-filter ()
305   (mime-view-cut-header)
306   (eword-decode-header)
307   )
308
309 (defvar mime-view-content-header-filter-alist nil)
310
311
312 ;;; @@ content filter
313 ;;;
314
315 (defvar mime-view-content-filter-alist
316   '(("text/enriched" . mime-preview/filter-for-text/enriched)
317     ("text/richtext" . mime-preview/filter-for-text/richtext)
318     (t . mime-preview/filter-for-text/plain)
319     ))
320
321
322 ;;; @@ entity separator
323 ;;;
324
325 (defun mime-view-entity-separator-function (rcnum cinfo ctype params subj)
326   "Insert entity separator conditionally.
327 Please redefine this function if you want to change default setting."
328   (or (mime-view-header-visible-p rcnum cinfo)
329       (mime-view-body-visible-p rcnum cinfo ctype)
330       (progn
331         (goto-char (point-max))
332         (insert "\n")
333         )))
334
335
336 ;;; @@ buffer local variables
337 ;;;
338
339 ;;; @@@ in raw buffer
340 ;;;
341
342 (defvar mime::article/content-info
343   "Information about structure of message.
344 Please use reference function `mime::content-info/SLOT-NAME' to
345 reference slot of content-info.  Their argument is only content-info.
346
347 Following is a list of slots of the structure:
348
349 rcnum           reversed content-number (list)
350 point-min       beginning point of region in raw-buffer
351 point-max       end point of region in raw-buffer
352 type            media-type/subtype (string or nil)
353 parameters      parameter of Content-Type field (association list)
354 encoding        Content-Transfer-Encoding (string or nil)
355 children        entities included in this entity (list of content-infos)
356
357 If a entity includes other entities in its body, such as multipart or
358 message/rfc822, content-infos of other entities are included in
359 `children', so content-info become a tree.")
360 (make-variable-buffer-local 'mime::article/content-info)
361
362 (defvar mime-view-buffer nil
363   "MIME View buffer corresponding with the (raw) buffer.")
364 (make-variable-buffer-local 'mime-view-buffer)
365
366
367 ;;; @@@ in view buffer
368 ;;;
369
370 (make-variable-buffer-local 'mime::preview/mother-buffer)
371
372 (defvar mime-raw-buffer nil
373   "Raw buffer corresponding with the (MIME-View) buffer.")
374 (make-variable-buffer-local 'mime-raw-buffer)
375
376 (make-variable-buffer-local 'mime::preview/original-major-mode)
377 (make-variable-buffer-local 'mime::preview/original-window-configuration)
378
379
380 ;;; @@ quitting method
381 ;;;
382
383 (defvar mime-view-quitting-method-alist
384   '((mime-show-message-mode
385      . mime-view-quitting-method-for-mime-show-message-mode))
386   "Alist of major-mode vs. quitting-method of mime-view.")
387
388 (defvar mime-view-over-to-previous-method-alist nil)
389 (defvar mime-view-over-to-next-method-alist nil)
390
391 (defvar mime-view-show-summary-method nil
392   "Alist of major-mode vs. show-summary-method.")
393
394
395 ;;; @@ following method
396 ;;;
397
398 (defvar mime-view-following-method-alist nil
399   "Alist of major-mode vs. following-method of mime-view.")
400
401 (defvar mime-view-following-required-fields-list
402   '("From"))
403
404
405 ;;; @@ X-Face
406 ;;;
407
408 ;; hack from Gnus 5.0.4.
409
410 (defvar mime-view-x-face-to-pbm-command
411   "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm")
412
413 (defvar mime-view-x-face-command
414   (concat mime-view-x-face-to-pbm-command
415           " | xv -quit -")
416   "String to be executed to display an X-Face field.
417 The command will be executed in a sub-shell asynchronously.
418 The compressed face will be piped to this command.")
419
420 (defun mime-view-x-face-function ()
421   "Function to display X-Face field. You can redefine to customize."
422   ;; 1995/10/12 (c.f. tm-eng:130)
423   ;;    fixed by Eric Ding <ericding@San-Jose.ate.slb.com>
424   (save-restriction
425     (narrow-to-region (point-min) (re-search-forward "^$" nil t))
426     ;; end
427     (goto-char (point-min))
428     (if (re-search-forward "^X-Face:[ \t]*" nil t)
429         (let ((beg (match-end 0))
430               (end (std11-field-end))
431               )
432           (call-process-region beg end "sh" nil 0 nil
433                                "-c" mime-view-x-face-command)
434           ))))
435
436
437 ;;; @@ utility
438 ;;;
439
440 (defun mime-preview/get-original-major-mode ()
441   (if mime::preview/mother-buffer
442       (save-excursion
443         (set-buffer mime::preview/mother-buffer)
444         (mime-preview/get-original-major-mode)
445         )
446     mime::preview/original-major-mode))
447
448
449 ;;; @ buffer setup
450 ;;;
451
452 (defun mime-view-setup-buffer (&optional ctl encoding ibuf obuf)
453   (if ibuf
454       (progn
455         (get-buffer ibuf)
456         (set-buffer ibuf)
457         ))
458   (or mime-view-redisplay
459       (setq mime::article/content-info (mime-parse-message ctl encoding))
460       )
461   (setq mime-view-buffer (mime-view-make-preview-buffer obuf))
462   )
463
464 (defun mime-view-make-preview-buffer (&optional obuf)
465   (let* ((cinfo mime::article/content-info)
466          (pcl (mime/flatten-content-info cinfo))
467          (the-buf (current-buffer))
468          (mode major-mode)
469          )
470     (or obuf
471         (setq obuf (concat "*Preview-" (buffer-name the-buf) "*")))
472     (set-buffer (get-buffer-create obuf))
473     (setq buffer-read-only nil)
474     (widen)
475     (erase-buffer)
476     (setq mime-raw-buffer the-buf)
477     (setq mime::preview/original-major-mode mode)
478     (setq major-mode 'mime-view-mode)
479     (setq mode-name "MIME-View")
480     (while pcl
481       (mime-view-display-entity (car pcl) cinfo the-buf obuf)
482       (setq pcl (cdr pcl))
483       )
484     (set-buffer-modified-p nil)
485     (setq buffer-read-only t)
486     (set-buffer the-buf)
487     obuf))
488
489 (defun mime-view-display-entity (content cinfo ibuf obuf)
490   "Display entity from content-info CONTENT."
491   (let* ((beg (mime::content-info/point-min content))
492          (end (mime::content-info/point-max content))
493          (ctype (mime::content-info/type content))
494          (params (mime::content-info/parameters content))
495          (encoding (mime::content-info/encoding content))
496          (rcnum (mime::content-info/rcnum content))
497          he e nb ne subj)
498     (set-buffer ibuf)
499     (goto-char beg)
500     (setq he (if (re-search-forward "^$" nil t)
501                  (1+ (match-end 0))
502                end))
503     (if (> he end)
504         (setq he end)
505       )
506     (save-restriction
507       (narrow-to-region beg end)
508       (setq subj
509             (eword-decode-string
510              (mime-article/get-subject params encoding)))
511       )
512     (set-buffer obuf)
513     (setq nb (point))
514     (narrow-to-region nb nb)
515     (mime-view-entity-button-function rcnum cinfo ctype params subj encoding)
516     (if (mime-view-header-visible-p rcnum cinfo)
517         (mime-preview/display-header beg he)
518       )
519     (if (and (null rcnum)
520              (member
521               ctype mime-view-content-button-visible-ctype-list))
522         (save-excursion
523           (goto-char (point-max))
524           (mime-view-insert-entity-button
525            rcnum cinfo ctype params subj encoding)
526           ))
527     (cond ((mime-view-body-visible-p rcnum cinfo ctype)
528            (mime-preview/display-body he end
529                                       rcnum cinfo ctype params subj encoding)
530            )
531           ((equal ctype "message/partial")
532            (mime-preview/display-message/partial)
533            )
534           ((and (null rcnum)
535                 (null (mime::content-info/children cinfo))
536                 )
537            (goto-char (point-max))
538            (mime-view-insert-entity-button
539             rcnum cinfo ctype params subj encoding)
540            ))
541     (mime-view-entity-separator-function rcnum cinfo ctype params subj)
542     (setq ne (point-max))
543     (widen)
544     (put-text-property nb ne 'mime-view-raw-buffer ibuf)
545     (put-text-property nb ne 'mime-view-cinfo content)
546     (goto-char ne)
547     ))
548
549 (defun mime-preview/display-header (beg end)
550   (save-restriction
551     (narrow-to-region (point)(point))
552     (insert-buffer-substring mime-raw-buffer beg end)
553     (let ((f (cdr (assq mime::preview/original-major-mode
554                         mime-view-content-header-filter-alist))))
555       (if (functionp f)
556           (funcall f)
557         (mime-view-default-content-header-filter)
558         ))
559     (run-hooks 'mime-view-content-header-filter-hook)
560     ))
561
562 (defun mime-preview/display-body (beg end
563                                       rcnum cinfo ctype params subj encoding)
564   (save-restriction
565     (narrow-to-region (point-max)(point-max))
566     (insert-buffer-substring mime-raw-buffer beg end)
567     (let ((f (cdr (or (assoc ctype mime-view-content-filter-alist)
568                       (assq t mime-view-content-filter-alist)))))
569       (and (functionp f)
570            (funcall f ctype params encoding)
571            )
572       )))
573
574 (defun mime-preview/display-message/partial ()
575   (save-restriction
576     (goto-char (point-max))
577     (if (not (search-backward "\n\n" nil t))
578         (insert "\n")
579       )
580     (let ((be (point-max)))
581       (narrow-to-region be be)
582       (insert mime-view-announcement-for-message/partial)
583       (mime-add-button (point-min)(point-max)
584                        (function mime-view-play-current-entity))
585       )))
586
587 (defun mime-article/get-uu-filename (param &optional encoding)
588   (if (member (or encoding
589                   (cdr (assq 'encoding param))
590                   )
591               mime-view-uuencode-encoding-name-list)
592       (save-excursion
593         (or (if (re-search-forward "^begin [0-9]+ " nil t)
594                 (if (looking-at ".+$")
595                     (buffer-substring (match-beginning 0)(match-end 0))
596                   ))
597             ""))
598     ))
599
600 (defun mime-article/get-subject (param &optional encoding)
601   (or (std11-find-field-body '("Content-Description" "Subject"))
602       (let (ret)
603         (if (or (and (setq ret (mime/Content-Disposition))
604                      (setq ret (assoc "filename" (cdr ret)))
605                      )
606                 (setq ret (assoc "name" param))
607                 (setq ret (assoc "x-name" param))
608                 )
609             (std11-strip-quoted-string (cdr ret))
610           ))
611       (mime-article/get-uu-filename param encoding)
612       ""))
613
614
615 ;;; @ content information
616 ;;;
617
618 (defun mime-article/point-content-number (p &optional cinfo)
619   (or cinfo
620       (setq cinfo mime::article/content-info)
621       )
622   (let ((b (mime::content-info/point-min cinfo))
623         (e (mime::content-info/point-max cinfo))
624         (c (mime::content-info/children cinfo))
625         )
626     (if (and (<= b p)(<= p e))
627         (or (let (co ret (sn 0))
628               (catch 'tag
629                 (while c
630                   (setq co (car c))
631                   (setq ret (mime-article/point-content-number p co))
632                   (cond ((eq ret t) (throw 'tag (list sn)))
633                         (ret (throw 'tag (cons sn ret)))
634                         )
635                   (setq c (cdr c))
636                   (setq sn (1+ sn))
637                   )))
638             t))))
639
640 (defun mime-article/rcnum-to-cinfo (rcnum &optional cinfo)
641   (or cinfo
642       (setq cinfo mime::article/content-info)
643       )
644   (find-if (function
645             (lambda (ci)
646               (equal (mime::content-info/rcnum ci) rcnum)
647               ))
648            (mime/flatten-content-info cinfo)
649            ))
650
651 (defun mime-article/cnum-to-cinfo (cn &optional cinfo)
652   (or cinfo
653       (setq cinfo mime::article/content-info)
654       )
655   (if (eq cn t)
656       cinfo
657     (let ((sn (car cn)))
658       (if (null sn)
659           cinfo
660         (let ((rc (nth sn (mime::content-info/children cinfo))))
661           (if rc
662               (mime-article/cnum-to-cinfo (cdr cn) rc)
663             ))
664         ))))
665
666 (defun mime/flatten-content-info (&optional cinfo)
667   (or cinfo
668       (setq cinfo mime::article/content-info)
669       )
670   (let ((dest (list cinfo))
671         (rcl (mime::content-info/children cinfo))
672         )
673     (while rcl
674       (setq dest (nconc dest (mime/flatten-content-info (car rcl))))
675       (setq rcl (cdr rcl))
676       )
677     dest))
678
679
680 ;;; @ MIME viewer mode
681 ;;;
682
683 (defconst mime-view-menu-title "MIME-View")
684 (defconst mime-view-menu-list
685   '((up          "Move to upper content"      mime-view-move-to-upper)
686     (previous    "Move to previous content"   mime-view-move-to-previous)
687     (next        "Move to next content"       mime-view-move-to-next)
688     (scroll-down "Scroll to previous content" mime-view-scroll-down-entity)
689     (scroll-up   "Scroll to next content"     mime-view-scroll-up-entity)
690     (play        "Play Content"               mime-view-play-current-entity)
691     (extract     "Extract Content"            mime-view-extract-current-entity)
692     (print       "Print"                      mime-view-print-current-entity)
693     (x-face      "Show X Face"                mime-view-display-x-face)
694     )
695   "Menu for MIME Viewer")
696
697 (cond (running-xemacs
698        (defvar mime-view-xemacs-popup-menu
699          (cons mime-view-menu-title
700                (mapcar (function
701                         (lambda (item)
702                           (vector (nth 1 item)(nth 2 item) t)
703                           ))
704                        mime-view-menu-list)))
705        (defun mime-view-xemacs-popup-menu (event)
706          "Popup the menu in the MIME Viewer buffer"
707          (interactive "e")
708          (select-window (event-window event))
709          (set-buffer (event-buffer event))
710          (popup-menu 'mime-view-xemacs-popup-menu))
711        (defvar mouse-button-2 'button2)
712        )
713       (t
714        (defvar mouse-button-2 [mouse-2])
715        ))
716
717 (defun mime-view-define-keymap (&optional default)
718   (let ((mime-view-mode-map (if (keymapp default)
719                                 (copy-keymap default)
720                               (make-sparse-keymap)
721                               )))
722     (define-key mime-view-mode-map
723       "u"        (function mime-view-move-to-upper))
724     (define-key mime-view-mode-map
725       "p"        (function mime-view-move-to-previous))
726     (define-key mime-view-mode-map
727       "n"        (function mime-view-move-to-next))
728     (define-key mime-view-mode-map
729       "\e\t"     (function mime-view-move-to-previous))
730     (define-key mime-view-mode-map
731       "\t"       (function mime-view-move-to-next))
732     (define-key mime-view-mode-map
733       " "        (function mime-view-scroll-up-entity))
734     (define-key mime-view-mode-map
735       "\M- "     (function mime-view-scroll-down-entity))
736     (define-key mime-view-mode-map
737       "\177"     (function mime-view-scroll-down-entity))
738     (define-key mime-view-mode-map
739       "\C-m"     (function mime-view-next-line-content))
740     (define-key mime-view-mode-map
741       "\C-\M-m"  (function mime-view-previous-line-content))
742     (define-key mime-view-mode-map
743       "v"        (function mime-view-play-current-entity))
744     (define-key mime-view-mode-map
745       "e"        (function mime-view-extract-current-entity))
746     (define-key mime-view-mode-map
747       "\C-c\C-p" (function mime-view-print-current-entity))
748     (define-key mime-view-mode-map
749       "a"        (function mime-view-follow-current-entity))
750     (define-key mime-view-mode-map
751       "q"        (function mime-view-quit))
752     (define-key mime-view-mode-map
753       "h"        (function mime-view-show-summary))
754     (define-key mime-view-mode-map
755       "\C-c\C-x" (function mime-view-kill-buffer))
756     (define-key mime-view-mode-map
757       "<"        (function beginning-of-buffer))
758     (define-key mime-view-mode-map
759       ">"        (function end-of-buffer))
760     (define-key mime-view-mode-map
761       "?"        (function describe-mode))
762     (if (functionp default)
763         (setq mime-view-mode-map
764               (append mime-view-mode-map (list (cons t default)))
765               ))
766     (if mouse-button-2
767         (define-key mime-view-mode-map
768           mouse-button-2 (function mime-button-dispatcher))
769       )
770     (cond (running-xemacs
771            (define-key mime-view-mode-map
772              mouse-button-3 (function mime-view-xemacs-popup-menu))
773            )
774           ((>= emacs-major-version 19)
775            (define-key mime-view-mode-map [menu-bar mime-view]
776              (cons mime-view-menu-title
777                    (make-sparse-keymap mime-view-menu-title)))
778            (mapcar (function
779                     (lambda (item)
780                       (define-key mime-view-mode-map
781                         (vector 'menu-bar 'mime-view (car item))
782                         (cons (nth 1 item)(nth 2 item))
783                         )
784                       ))
785                    (reverse mime-view-menu-list)
786                    )
787            ))
788     (use-local-map mime-view-mode-map)
789     (run-hooks 'mime-view-define-keymap-hook)
790     ))
791
792 (defun mime-view-mode (&optional mother ctl encoding ibuf obuf
793                                  default-keymap-or-function)
794   "Major mode for viewing MIME message.
795
796 Here is a list of the standard keys for mime-view-mode.
797
798 key             feature
799 ---             -------
800
801 u               Move to upper content
802 p or M-TAB      Move to previous content
803 n or TAB        Move to next content
804 SPC             Scroll up or move to next content
805 M-SPC or DEL    Scroll down or move to previous content
806 RET             Move to next line
807 M-RET           Move to previous line
808 v               Decode current content as `play mode'
809 e               Decode current content as `extract mode'
810 C-c C-p         Decode current content as `print mode'
811 a               Followup to current content.
812 x               Display X-Face
813 q               Quit
814 button-2        Move to point under the mouse cursor
815                 and decode current content as `play mode'
816 "
817   (interactive)
818   (let ((buf (get-buffer mime/output-buffer-name)))
819     (if buf
820         (save-excursion
821           (set-buffer buf)
822           (erase-buffer)
823           )))
824   (let ((ret (mime-view-setup-buffer ctl encoding ibuf obuf))
825         (win-conf (current-window-configuration))
826         )
827     (prog1
828         (switch-to-buffer ret)
829       (setq mime::preview/original-window-configuration win-conf)
830       (if mother
831           (progn
832             (setq mime::preview/mother-buffer mother)
833             ))
834       (mime-view-define-keymap default-keymap-or-function)
835       (let ((point (next-single-property-change (point-min) 'mime-view-cinfo)))
836         (if point
837             (goto-char point)
838           (goto-char (point-min))
839           (search-forward "\n\n" nil t)
840           ))
841       (run-hooks 'mime-view-mode-hook)
842       )))
843
844 (autoload 'mime-view-play-current-entity "mime-play" "Play current entity." t)
845
846 (defun mime-view-extract-current-entity ()
847   "Extract current entity into file (maybe).
848 It decodes current entity to call internal or external method as
849 \"extract\" mode.  The method is selected from variable
850 `mime/content-decoding-condition'."
851   (interactive)
852   (mime-view-play-current-entity "extract")
853   )
854
855 (defun mime-view-print-current-entity ()
856   "Print current entity (maybe).
857 It decodes current entity to call internal or external method as
858 \"print\" mode.  The method is selected from variable
859 `mime/content-decoding-condition'."
860   (interactive)
861   (mime-view-play-current-entity "print")
862   )
863
864 (defun mime-view-follow-current-entity ()
865   "Write follow message to current entity.
866 It calls following-method selected from variable
867 `mime-view-following-method-alist'."
868   (interactive)
869   (let ((root-cinfo (get-text-property (point-min) 'mime-view-cinfo))
870         cinfo)
871     (while (null (setq cinfo (get-text-property (point) 'mime-view-cinfo)))
872       (backward-char)
873       )
874     (let* ((p-beg (previous-single-property-change (point) 'mime-view-cinfo))
875            p-end
876            (rcnum (mime::content-info/rcnum cinfo))
877            (len (length rcnum))
878            rc)
879       (cond ((null p-beg)
880              (setq p-beg
881                    (if (eq (next-single-property-change (point-min)
882                                                         'mime-view-cinfo)
883                            (point))
884                        (point)
885                      (point-min)))
886              )
887             ((eq (next-single-property-change p-beg 'mime-view-cinfo)
888                  (point))
889              (setq p-beg (point))
890              ))
891       (setq p-end (next-single-property-change p-beg 'mime-view-cinfo))
892       (cond ((null p-end)
893              (setq p-end (point-max))
894              )
895             ((null rcnum)
896              (setq p-end (point-max))
897              )
898             (t
899              (save-excursion
900                (goto-char p-end)
901                (catch 'tag
902                  (let (e)
903                    (while (setq e
904                                 (next-single-property-change
905                                  (point) 'mime-view-cinfo))
906                      (goto-char e)
907                      (let ((rc (mime::content-info/rcnum
908                                 (get-text-property (point)
909                                                    'mime-view-cinfo))))
910                        (or (equal rcnum (nthcdr (- (length rc) len) rc))
911                            (throw 'tag nil)
912                            ))
913                      (setq p-end e)
914                      ))
915                  (setq p-end (point-max))
916                  ))
917              ))
918       (let* ((mode (mime-preview/get-original-major-mode))
919              (new-name (format "%s-%s" (buffer-name) (reverse rcnum)))
920              new-buf
921              (the-buf (current-buffer))
922              (a-buf mime-raw-buffer)
923              fields)
924         (save-excursion
925           (set-buffer (setq new-buf (get-buffer-create new-name)))
926           (erase-buffer)
927           (insert-buffer-substring the-buf p-beg p-end)
928           (goto-char (point-min))
929           (if (mime-view-header-visible-p rcnum root-cinfo)
930               (delete-region (goto-char (point-min))
931                              (if (re-search-forward "^$" nil t)
932                                  (match-end 0)
933                                (point-min)))
934             )
935           (goto-char (point-min))
936           (insert "\n")
937           (goto-char (point-min))
938           (let ((rcnum (mime::content-info/rcnum cinfo)) ci str)
939             (while (progn
940                      (setq str
941                            (save-excursion
942                              (set-buffer a-buf)
943                              (setq ci (mime-article/rcnum-to-cinfo rcnum))
944                              (save-restriction
945                                (narrow-to-region
946                                 (mime::content-info/point-min ci)
947                                 (mime::content-info/point-max ci)
948                                 )
949                                (std11-header-string-except
950                                 (concat "^"
951                                         (apply (function regexp-or) fields)
952                                         ":") ""))))
953                      (if (string= (mime::content-info/type ci)
954                                   "message/rfc822")
955                          nil
956                        (if str
957                            (insert str)
958                          )
959                        rcnum))
960               (setq fields (std11-collect-field-names)
961                     rcnum (cdr rcnum))
962               )
963             )
964           (let ((rest mime-view-following-required-fields-list))
965             (while rest
966               (let ((field-name (car rest)))
967                 (or (std11-field-body field-name)
968                     (insert
969                      (format
970                       (concat field-name
971                               ": "
972                               (save-excursion
973                                 (set-buffer the-buf)
974                                 (set-buffer mime::preview/mother-buffer)
975                                 (set-buffer mime-raw-buffer)
976                                 (std11-field-body field-name)
977                                 )
978                               "\n")))
979                     ))
980               (setq rest (cdr rest))
981               ))
982           (eword-decode-header)
983           )
984         (let ((f (cdr (assq mode mime-view-following-method-alist))))
985           (if (functionp f)
986               (funcall f new-buf)
987             (message
988              (format
989               "Sorry, following method for %s is not implemented yet."
990               mode))
991             ))
992         ))))
993
994 (defun mime-view-display-x-face ()
995   (interactive)
996   (save-window-excursion
997     (set-buffer mime-raw-buffer)
998     (mime-view-x-face-function)
999     ))
1000
1001 (defun mime-view-move-to-upper ()
1002   "Move to upper entity.
1003 If there is no upper entity, call function `mime-view-quit'."
1004   (interactive)
1005   (let (cinfo)
1006     (while (null (setq cinfo (get-text-property (point) 'mime-view-cinfo)))
1007       (backward-char)
1008       )
1009     (let ((r (mime-article/rcnum-to-cinfo
1010               (cdr (mime::content-info/rcnum cinfo))
1011               (get-text-property 1 'mime-view-cinfo)))
1012           point)
1013       (catch 'tag
1014         (while (setq point (previous-single-property-change
1015                             (point) 'mime-view-cinfo))
1016           (goto-char point)
1017           (if (eq r (get-text-property (point) 'mime-view-cinfo))
1018               (throw 'tag t)
1019             )
1020           )
1021         (mime-view-quit)
1022         ))))
1023
1024 (defun mime-view-move-to-previous ()
1025   "Move to previous entity.
1026 If there is no previous entity, it calls function registered in
1027 variable `mime-view-over-to-previous-method-alist'."
1028   (interactive)
1029   (while (null (get-text-property (point) 'mime-view-cinfo))
1030     (backward-char)
1031     )
1032   (let ((point (previous-single-property-change (point) 'mime-view-cinfo)))
1033     (if point
1034         (goto-char point)
1035       (let ((f (assq mime::preview/original-major-mode
1036                      mime-view-over-to-previous-method-alist)))
1037         (if f
1038             (funcall (cdr f))
1039           ))
1040       )))
1041
1042 (defun mime-view-move-to-next ()
1043   "Move to next entity.
1044 If there is no previous entity, it calls function registered in
1045 variable `mime-view-over-to-next-method-alist'."
1046   (interactive)
1047   (let ((point (next-single-property-change (point) 'mime-view-cinfo)))
1048     (if point
1049         (goto-char point)
1050       (let ((f (assq mime::preview/original-major-mode
1051                      mime-view-over-to-next-method-alist)))
1052         (if f
1053             (funcall (cdr f))
1054           ))
1055       )))
1056
1057 (defun mime-view-scroll-up-entity (&optional h)
1058   "Scroll up current entity.
1059 If reached to (point-max), it calls function registered in variable
1060 `mime-view-over-to-next-method-alist'."
1061   (interactive)
1062   (or h
1063       (setq h (1- (window-height)))
1064       )
1065   (if (= (point) (point-max))
1066       (let ((f (assq mime::preview/original-major-mode
1067                      mime-view-over-to-next-method-alist)))
1068         (if f
1069             (funcall (cdr f))
1070           ))
1071     (let ((point
1072            (or (next-single-property-change (point) 'mime-view-cinfo)
1073                (point-max))))
1074       (forward-line h)
1075       (if (> (point) point)
1076           (goto-char point)
1077         )
1078       )))
1079
1080 (defun mime-view-scroll-down-entity (&optional h)
1081   "Scroll down current entity.
1082 If reached to (point-min), it calls function registered in variable
1083 `mime-view-over-to-previous-method-alist'."
1084   (interactive)
1085   (or h
1086       (setq h (1- (window-height)))
1087       )
1088   (if (= (point) (point-min))
1089       (let ((f (assq mime::preview/original-major-mode
1090                      mime-view-over-to-previous-method-alist)))
1091         (if f
1092             (funcall (cdr f))
1093           ))
1094     (let (point)
1095       (save-excursion
1096         (catch 'tag
1097           (while (> (point) 1)
1098             (if (setq point
1099                       (previous-single-property-change (point)
1100                                                        'mime-view-cinfo))
1101                 (throw 'tag t)
1102               )
1103             (backward-char)
1104             )
1105           (setq point (point-min))
1106           ))
1107       (forward-line (- h))
1108       (if (< (point) point)
1109           (goto-char point)
1110         ))))
1111
1112 (defun mime-view-next-line-content ()
1113   (interactive)
1114   (mime-view-scroll-up-entity 1)
1115   )
1116
1117 (defun mime-view-previous-line-content ()
1118   (interactive)
1119   (mime-view-scroll-down-entity 1)
1120   )
1121
1122 (defun mime-view-quit ()
1123   "Quit from MIME-View buffer.
1124 It calls function registered in variable
1125 `mime-view-quitting-method-alist'."
1126   (interactive)
1127   (let ((r (assq mime::preview/original-major-mode
1128                  mime-view-quitting-method-alist)))
1129     (if r
1130         (funcall (cdr r))
1131       )))
1132
1133 (defun mime-view-show-summary ()
1134   "Show summary.
1135 It calls function registered in variable
1136 `mime-view-show-summary-method'."
1137   (interactive)
1138   (let ((r (assq mime::preview/original-major-mode
1139                  mime-view-show-summary-method)))
1140     (if r
1141         (funcall (cdr r))
1142       )))
1143
1144 (defun mime-view-kill-buffer ()
1145   (interactive)
1146   (kill-buffer (current-buffer))
1147   )
1148
1149
1150 ;;; @ end
1151 ;;;
1152
1153 (provide 'mime-view)
1154
1155 (run-hooks 'mime-view-load-hook)
1156
1157 ;;; mime-view.el ends here