;;; mew-decode.el --- MIME syntax decoder for Mew

;; Author:  Kazu Yamamoto <Kazu@Mew.org>
;; Created: Oct  2, 1996
;; Revised: Oct 25, 1997

;;; Code:

(defconst mew-decode-version "mew-decode.el version 0.23")

(require 'mew)

(defvar mew-prog-mime-decode-switch
  (list (cons mew-b64 '("-b"))
	(cons mew-qp  '("-q"))
	(cons mew-xg  '("-g"))))

(defvar mew-prog-mime-decode-text-switch
  (list (cons mew-b64 '("-b" "-t"))
	(cons mew-qp  '("-q"))
	(cons mew-xg  '("-g" "-t"))))

(defvar mew-decode-multipart-encrypted-switch
  '(("application/pgp-encrypted"  . mew-pgp-decrypt)
;    ("application/moss-keys"      . mew-moss-decrypt)
    )
  )

(defvar mew-decode-multipart-signed-switch
  '(("application/pgp-signature"   . mew-pgp-verify)
;    ("application/moss-signature"  . mew-moss-verify)
;    ("application/pkcs7-signature" . mew-smime-verify)
    )
  )

(defvar mew-decode-multipart-protocol-prog
  (list 
   (cons "application/pgp-encrypted" mew-prog-pgp)
   (cons "application/pgp-signature" mew-prog-pgp)
   ))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; MIME decoder
;;

(defmacro mew-decode-error (error-msg)
  (` (progn (setq mew-decode-error (, error-msg)) (error ""))))

(defun mew-decode-rfc822-header ()
  ;; Called on the beginning of the header in the narrowed region
  ;; (1) Decode RFC822 fields exclusing MIME fields
  ;; (2) Delete X-Mew: fields
  ;; (3) Arrange decoded-RFC822-fields, mew-mv:, MIME fields in order
  ;; Return whether MIME or not
  (if (re-search-forward mew-eoh2 nil t)
      (beginning-of-line)
    (mew-decode-error "No end-of-header for RFC822 header"))
  (if (> (count-lines (point-min) (point)) mew-header-max-length)
      (mew-decode-error "Too long header")
    (let (key beg med cnt-hdrs back subj mimep)
      (save-restriction
	(narrow-to-region (point-min) (point))
	(goto-char (point-min))
	(while (not (eobp))
	  (if (not (looking-at mew-keyval))
	      (forward-line)
	    (setq key (capitalize (mew-match 1)))
	    (setq beg (match-beginning 0))
	    (setq med (match-end 0))
	    (forward-line)
	    (while (looking-at mew-lwsp) (forward-line))
	    (cond
	     ((equal key mew-x-mew:)
	      ;; deleting X-Mew: on the RFC822 header
	      (delete-region beg (point)))
	     ((string-match "^Content-" key)
	      ;; save MIME fields
	      (setq cnt-hdrs (concat cnt-hdrs (buffer-substring beg (point))))
	      (delete-region beg (point)))
	     (t
	      (mew-header-decode-region (member key mew-address-fields)
					med (point))
	      (cond
	       ((equal key "Subject:")
		(setq subj (buffer-substring med (1- (point)))))
	       ((equal key mew-mv:)
		;; MIME-Version:
		(setq mimep (string-match
			     mew-mv:-num
			     (mew-header-syntax
			      (buffer-substring med (point))))))
	       ))
	     ))))
      (if (not cnt-hdrs)
	  ()
	(setq back (point))
	(insert cnt-hdrs)
	(goto-char back))
      ;; the beginning of the content header
      (list mimep subj) ;; return value
      )))

(defun mew-decode-mime-header (&optional dct)
  ;; Called on the beginning of the content header in the narrowed region
  ;; Return a part syntax after moving the beginning of the content body
  (if (re-search-forward mew-eoh2)
      (beginning-of-line)
    (mew-decode-error "No end-of-header for MIME header"))
  (if (> (count-lines (point-min) (point)) mew-header-max-length)
      (mew-decode-error "Too long header")
    (let* ((case-fold-search t)
	   (len (length mew-mime-fields))
	   (vec (make-vector len nil))
	   key med attr n act value)
      (aset vec 0 (if dct dct mew-type-txt))
      (save-restriction
	(narrow-to-region (point-min) (point))
	(goto-char (point-min))
	(while (not (eobp))
	  (if (not (looking-at mew-keyval))
	      (forward-line)
	    (setq key (mew-match 1))
	    (setq med (match-end 0))
	    (forward-line)
	    (while (looking-at mew-lwsp) (forward-line))
	    (setq attr (mew-assoc-case-equal3 key mew-mime-fields 0))
	    (if (not attr)
		()
	      (setq n (nth 0 attr))
	      (setq act (nth 2 attr))
	      (setq value (buffer-substring med (1- (point))))
	      (cond
	       ((equal act 'analyze)
		(setq value (mew-header-syntax-list value))) ;; list
	       ((equal act 'extract)
		(setq value (mew-header-syntax value)))
	       ((equal act 'decode)
		(mew-header-decode-region nil med (point) t)
		;; mew-header-decode-region goes to the max point in
		;; the narrowed region. So, this must be (point).
		(setq value (buffer-substring med (1- (point)))))
	       )
	      (aset vec n value)))))
      (forward-line) 
      ;; the beginning of the content body
      (vconcat (list 'single (point) nil nil) vec))))

(defun mew-decode-mime (enc &optional text charset)
  ;; Called on the beginning of the content body in the narrow region
  ;; Decode the content body
  ;; Convert text
  (let ((beg (point))
	(switch mew-prog-mime-decode-switch)
	opt file)
    (if (null enc)
	() ;; just in case
      (if text (setq switch mew-prog-mime-decode-text-switch))
      (setq opt (cdr (mew-assoc-case-equal enc switch 0)))
      ;; opt is nil if 7bit, 8bit, and binary
      (if opt
	  (if (not (mew-which mew-prog-mime-decode exec-path))
	      (mew-decode-error (concat mew-prog-mime-decode " doesn't exist"))
	    (setq file (mew-make-temp-name))
	    (mew-flet 
	     ;; NEVER use call-process-region for privacy reasons
	     (write-region beg (point-max) file nil 'no-msg))
	    (delete-region beg (point-max))
	    ;; input is always in ASCII domain
	    (mew-plet
	     (apply (function call-process) mew-prog-mime-decode 
		    file t nil opt))
	    (if (file-exists-p file) (delete-file file)))))
    (mew-cs-decode-region beg (point-max) (mew-charset-to-cs charset))
    ))

;;
;; Kick start function
;;

(defun mew-file-size (file)
  (nth 7 (file-attributes file))
  )

(defun mew-decode (fld msg &optional edit)
  ;; in cache buffer
  (goto-char (point-min))
  (setq mew-decode-error nil)
  ;; must be set mc-flag on Mule 2 for re-search-forward
  (if (and mew-temacs-p mew-mule-p) (setq mc-flag nil))
  (mew-erase-buffer)
  (cond
   ((or (mew-folder-newsp fld) (mew-folder-imapp fld))
    (mew-piolet mew-cs-noconv-eol mew-cs-noconv
     (mew-im-call-process nil mew-prog-imcat (format "--src=%s" fld) msg)))
   (t
    (let ((file (mew-expand-folder fld msg)))
      (mew-frwlet mew-cs-noconv-eol mew-cs-noconv
	 (insert-file-contents file))
      (setq mew-cache-attribute (mew-cache-attribute-get file)))
    (if (fboundp (function set-buffer-multibyte))
	(set-buffer-multibyte t)))
   )
  ;; Illegal messages may not have end-of-header.
  ;; Truncated messages may not have end-of-header.
  (if (re-search-forward mew-eoh2 nil t)
      ()
    (setq mew-decode-error "No end-of-header(null line) in the top level")
    (goto-char (point-max))
    (if (not (bolp)) (insert "\n"))
    (insert "\n"))
  (goto-char (point-min))
  (if mew-debug
      (let ((debug-on-error t))
	(setq mew-decode-syntax
	      (mew-decode-message (mew-decode-syntax-rfc822-head) edit)))
    (condition-case nil
	(setq mew-decode-syntax 
	      (mew-decode-message
	       ;; Call internalform with VIRTUAL content header
	       ;;     CT: message/rfc822 (virtual)
	       ;; 
	       ;;     Header(RFC822 header + content header)
	       ;;
	       ;;     Body(content body)
	       (mew-decode-syntax-rfc822-head) edit))
      (error
       (widen)
       (goto-char (point-min))
       ;; There is certainly end-of-header but just in case.
       (re-search-forward mew-eoh2 nil t)
       (forward-line)
       ;; after header delimiter
       (setq mew-decode-syntax (mew-decode-syntax-rfc822))
       ;; min, point - 1, point, point-max
       ))))

;;
;; the function "m":: for message
;;

(defun mew-decode-message (syntax &optional edit)
  ;; Called on the beginning of the RFC822 header in the narrowed region
  ;; hbeg is certainly the beginning of the VIRTUAL content body(i.e. min).
  ;; hend will have to set to the end of PHYSICAL content header(i.e. end)
  ;; after analyzing the physical content header and body since CD:'s 
  ;; length in the physical content header will change(no need to say
  ;; about the end of the physical content header).
  ;;
  ;;     Content-Type: Message/Rfc822    == virtual content header
  ;;
  ;;(min)Decoded RFC822 fields           == virtual content body
  ;;     MIME-Version: 1.0
  ;;(cur)MIME fields                     == physical content header
  ;;(end)
  ;;     Content-Body                    == physical content body
  ;;(max)
  (let* ((m-s (mew-decode-rfc822-header))
	 (mimep (nth 0 m-s))
	 (subj (nth 1 m-s))
	 ;; the beginning of the physical content header (cur)
	 part)
    (cond 
     (mimep ;; MIME
      (save-restriction
	(narrow-to-region (point) (point-max))
	(setq part (mew-decode-singlepart nil 'message edit))
	;; hend is always 1 char smaller than the beginning of 
	;; the physical content body
	(mew-syntax-set-key syntax 'message)
	(mew-syntax-set-end syntax (1- (mew-syntax-get-begin part)))
	(or (mew-syntax-get-cd syntax) (mew-syntax-set-cd syntax subj))
	(mew-syntax-cat syntax part) ;; return value
	))
     (t ;; RFC822
      ;; the beginning of the meaningless physical content header
      (if (not (re-search-forward mew-eoh2 nil t))
	(mew-decode-error "No end-of-header(null line) in RFC822 message"))
      (forward-line)
      ;; the beginning of the BODY(i.e. the physical content body)
      (mew-cs-decode-region (point) (point-max) mew-cs-rfc822-trans)
      (mew-syntax-set-key syntax 'message)
      (mew-syntax-set-end syntax (1- (point)))
      (or (mew-syntax-get-cd syntax) (mew-syntax-set-cd syntax subj))
      (mew-decode-syntax-rfc822 syntax)
      ;; (point-min), (point) - 1, (point), (point-max)
      )
     )
    ))

;;
;; the function "S":: for singlepart
;;

(defun mew-decode-singlepart (&optional dct parent edit)
  ;; Called on the beginning of the content header in the narrowed region
  (let* ((case-fold-search t)
	 (begin (point))
	 (syntax (mew-decode-mime-header dct))
	 ;; the beginning of the content body
	 (ctl (mew-syntax-get-ct syntax))
	 (ct (capitalize (car ctl)))
	 (cte (or (mew-syntax-get-cte syntax) mew-7bit))
	 (encap nil)
	 charset)
    (cond
     ((not (mew-member-case-equal cte mew-decode-value))
      (mew-syntax-set-ct syntax mew-type-apo))
     ;; Message/Rfc822, decoding is not required
     ((equal mew-ct-msg ct)
      (if edit
	  ()
	(if (equal parent 'message) (setq encap t))
	(save-restriction
	  (narrow-to-region (point) (point-max))
	  (setq syntax (mew-decode-message syntax)))))
     ((equal mew-ct-ext ct)
      (let* ((at (mew-syntax-get-member ctl "access-type"))
	     (func (cdr (mew-assoc-case-equal at mew-ext-include-switch 0))))
	(if (not (and func (fboundp func)))
	    ()
	  (save-excursion
	    (goto-char (point-max)) ;; phantom body
	    (funcall func ctl))
	  (delete-region begin (point))
	  ;; xxx again how about edit?
	  (setq syntax (mew-decode-singlepart)))))
     ;; Multipart, decoding is not required
     ((string-match "^Multipart/" ct)
      (let* ((proto (mew-syntax-get-member ctl "protocol"))
	     (switch mew-decode-multipart-protocol-prog) ;; save length
	     (prog (if proto (cdr (mew-assoc-case-equal proto switch 0))))
	     (existp (if prog (mew-which prog exec-path))))
	(cond 
	 ((equal mew-ct-mld ct)
	  ;; semantics into digest
	  (setq syntax (mew-decode-multipart syntax mew-type-msg edit)))
	 ((and (equal mew-ct-mls ct) existp)
	  (setq syntax (mew-decode-multipart-signed syntax edit)))
	 ((and (equal mew-ct-mle ct) existp)
	  (setq syntax (mew-decode-multipart-encrypted syntax edit)))
	 (t
	  (setq syntax (mew-decode-multipart syntax nil edit)))
	 )))
     ;; Others
     (t
      (if (and (equal parent 'message) (not (equal mew-ct-txt ct)))
	  (setq encap t))
      ;; even if cte is nil, call mew-decode-mime for charset conversion
      (cond 
       ((string-match "^Text/" ct)
	(setq charset (mew-syntax-get-member ctl "charset"))
	;;charset is "us-ascii" if it is nil.
	(mew-decode-mime cte 'text charset))
       ((mew-member-case-equal ct mew-mime-content-type-text-list)
	(mew-decode-mime cte 'text))
       (t
	(mew-decode-mime cte))
       )
      )
     )
    (if (or (not (equal mew-ct-msg ct)) edit)
	(mew-syntax-set-end syntax (point-max)))
    (if encap
	;; Mew allows text/plain and multipart/* for body.
	;; If other CT: is embedded under message, it should be
	;; encapsulated in multipart/mixed.
	(let ((head mew-encode-syntax-multi-head))
	  ;; begin for multipart syntax is important because
	  ;; the begin will be used by the parent to set hend
	  (mew-syntax-set-begin head (mew-syntax-get-begin syntax))
	  (mew-syntax-set-end head (point-max))
	  (mew-syntax-cat head syntax)) ;; return value
      syntax) ;; return value
    ))

;;
;; the function "M":: for multipart
;;

(defun mew-decode-multipart (syntax &optional dct edit)
  (let* ((case-fold-search nil) ;; boundary is case sensitive
	 (ctl (mew-syntax-get-ct syntax))
	 (boundary (regexp-quote (mew-syntax-get-member ctl "boundary")))
	 (parts []) part
	 obound ebound bregex start break)
    (if (null boundary)
	(mew-decode-error "No boundary parameter for multipart"))
    (mew-syntax-set-key syntax 'multi)
    (setq obound (concat "--" boundary))
    (setq ebound (concat "--" boundary "--"))
    (setq bregex (concat "^--" boundary "-?-?$"))
    (if (not (re-search-forward (concat "^" obound "$") nil t))
	(mew-decode-error (format "No first boundary for %s" (car ctl))))
    (forward-line)
    (setq start (point)) ;; the beginning of the part
    (catch 'multipart
      (while 1
	(if (not (re-search-forward bregex nil t))
	    (mew-decode-error (format "No last boundary for %s" (car ctl))))
	(setq break (string= (regexp-quote (mew-match 0)) ebound))
	(forward-line) ;; the beginning of the next part
	(save-excursion
	  (forward-line -1)
	  (beginning-of-line) ;; just in case
	  (forward-char -1) ;; skip the preceeding CRLF
	  ;; the end of the part
	  (save-restriction
	    (narrow-to-region start (point))
	    (goto-char (point-min))
	    ;; the beginning of the part
	    (setq part (mew-decode-singlepart dct nil edit))
	    (setq parts (vconcat parts (vector part)))
	    ))
	(setq start (point)) ;; the beginning of the part
	(if break 
	    (throw 'multipart (vconcat syntax parts)))
	))
    ))

;;
;; the function "D":: for decryption
;;

(defun mew-decode-multipart-encrypted (syntax &optional edit)
  ;; called in narrowed region
  ;;
  ;;     CT: M/E; proto; bound;
  ;;
  ;;(cur)--bound
  ;;             (the key part)
  ;;     --bound
  ;;             (the encrypted part)
  ;;     --bound--
  (let* ((case-fold-search nil) ;; boundary is case sensitive
	 (ctl (mew-syntax-get-ct syntax))
	 (boundary (regexp-quote (mew-syntax-get-member ctl "boundary")))
	 (protocol (mew-syntax-get-member ctl "protocol"))
	 (switch mew-decode-multipart-encrypted-switch) ;; save length
	 (func (cdr (mew-assoc-case-equal protocol switch 0)))
	 file1 file2 file3 syntax3
	 start result file3result privacy
	 oregex eregex)
    (if (null boundary)
	(mew-decode-error "No boundary parameter for multipart"))
    (if (null protocol)
	(mew-decode-error "No protocol parameter for security multipart"))
    (setq oregex (concat "^--" boundary "$"))
    (setq eregex (concat "^--" boundary "--$"))
    ;;
    (if (not (re-search-forward oregex))
	(mew-decode-error "No first boundary for Multipart/Encrypted"))
    (forward-line) ;; the beginning of the key part
    (setq start (point))
    ;;
    (if (not (re-search-forward oregex))
	(mew-decode-error "No second boundary for Multipart/Encrypted"))
    (beginning-of-line)
    (setq file1 (mew-save-decode-form start (1- (point))))
    (forward-line) ;; the beginning of the encrypted part
    (setq start (point)) 
    ;;
    (if (not (re-search-forward eregex nil t))
	(mew-decode-error "No third boundary for Multipart/Encrypted"))
    (beginning-of-line)
    (setq file2 (mew-save-decode-form start (1- (point))))
    ;;
    (delete-region (point-min) (point-max))
    ;; 
    ;; Call protocol function
    (setq file3result (funcall func file1 file2))
    (setq file3 (nth 0 file3result) result (nth 1 file3result))
    ;;
    (if (null (file-exists-p file3))
	(progn
	  (insert "\n") ;; CT: text/plain; charset=us-ascii
	  (insert "Multipart/Encrypted could not be decrypted.\n"))
      (mew-flet 
       (insert-file-contents file3)
       ;; because of RICH functionality of RFC1847... Gee dirty!
       (mew-decode-crlf-magic)
       ))
    ;; Analyze the decrypted part
    (goto-char (point-min))
    (setq syntax3 (mew-decode-singlepart nil nil edit))
    (setq privacy (mew-syntax-get-privacy syntax3))
    (if privacy (setq result (concat result "\n\t")))
    (mew-syntax-set-privacy syntax3
			    (cons (list mew-ct-mle protocol result) privacy))
    ;; Throw away garbage
    (if (file-exists-p file1) (delete-file file1))
    (if (file-exists-p file2) (delete-file file2))
    (if (file-exists-p file3) (delete-file file3))
    syntax3 ;; return value
    ))

;;
;; the function "V":: for verification
;;

(defun mew-decode-multipart-signed (syntax &optional edit)
  ;; called in narrowed region
  ;;
  ;;     CT: M/S; proto; bound; micalg;
  ;;
  ;;(cur)--bound
  ;;             (the signed part)
  ;;     --bound
  ;;             (the key part)
  ;;     --bound--
  (let* ((case-fold-search nil) ;; boundary is case sensitive
	 (ctl (mew-syntax-get-ct syntax))
	 (boundary (regexp-quote (mew-syntax-get-member ctl "boundary")))
	 (protocol (mew-syntax-get-member ctl "protocol"))
	 (micalg (mew-syntax-get-member ctl "micalg"))
	 (switch mew-decode-multipart-signed-switch) ;; save length
	 (func (cdr (mew-assoc-case-equal protocol switch 0)))
	 file1 file2 syntax3 end1 start2 result privacy
	 oregex eregex)
    (if (null boundary)
	(mew-decode-error "No boundary parameter for multipart"))
    (if (null protocol)
	(mew-decode-error "No protocol parameter for security multipart"))
    (setq oregex (concat "^--" boundary "$"))
    (setq eregex (concat "^--" boundary "--$"))
    ;;
    (if (not (re-search-forward oregex nil t))
	(mew-decode-error "No first boundary for Multipart/Signed"))
    (forward-line)
    ;; the beginning of the signed part
    (delete-region (point-min) (point)) ;; deleting content-header
    (goto-char (point-min)) ;; just in case
    ;;
    (if (not (re-search-forward oregex nil t))
	(mew-decode-error "No second boundary for Multipart/Signed"))
    (beginning-of-line) 
    (setq end1 (1- (point))) ;; the end of the signed part
    (forward-line) ;; the beginning of the key part
    (setq start2 (point)) 
    ;;
    (save-excursion
      (save-restriction
	(narrow-to-region (point-min) end1)
	(setq file1 (mew-save-transfer-form (point-min) end1 'retain))))
    ;; xxx make use of micalg?
    ;; xxx protocol check?
    (if (not (re-search-forward eregex))
	(mew-decode-error "No third boundary for Multipart/Signed"))
    (beginning-of-line) ;; the end of the encrypted part + 1
    (setq file2 (mew-save-decode-form start2 (1- (point))))
    ;;
    (delete-region end1 (point-max))
    ;; Now the signed part only
    ;; Call protocl function
    (setq result (funcall func file1 file2 micalg))
    ;; Analyze the signed part
    (goto-char (point-min))
    (setq syntax3 (mew-decode-singlepart nil nil edit))
    (setq privacy (mew-syntax-get-privacy syntax3))
    (if privacy (setq result (concat result "\n\t")))
    (mew-syntax-set-privacy syntax3
			    (cons (list mew-ct-mls protocol result) privacy))
    ;; Throw away garbage
    (if (file-exists-p file1) (delete-file file1))
    (if (file-exists-p file2) (delete-file file2))
    ;;
    syntax3 ;; return value
    ))

(defun mew-save-decode-form (beg end)
  (mew-flet
   (let (syntax file)
     (setq file (mew-make-temp-name))
     (save-excursion
       (save-restriction
	 (narrow-to-region beg end)
	 (goto-char (point-min))
	 (setq syntax (mew-decode-singlepart))
	 (write-region (mew-syntax-get-begin syntax)
		       (mew-syntax-get-end syntax)
		       file nil 'no-msg)
	 ))
     file
     )
   ))

(defun mew-decode-crlf-magic ()
  (let ((case-fold-search t)
	(cte mew-7bit)
	key start match)
    (save-excursion
      (goto-char (point-min))
      (catch 'header
	(while (re-search-forward 
		"^\r?$\\|^Content-Transfer-Encoding:[ \t]*" nil t)
	  (setq key (mew-match 0))
	  (setq start (match-end 0))
	  (if (string-match "^\r?$" key)
	      (progn
		(save-restriction
		  (if (string-match mew-bin cte)
		      (narrow-to-region (point-min) (1+ start))
		    (narrow-to-region (point-min) (point-max)))
		  (goto-char (point-min))
		  (while (search-forward "\r\n" nil t)
		    (replace-match "\n" nil t)))
		(throw 'header nil)))
	  (forward-line)
	  (while (looking-at mew-lwsp) (forward-line))
	  (setq match (mew-buffer-substring start (1- (point))))
	  (setq cte (mew-header-syntax match))
	  )))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;
;;; Header arrange in a message buffer
;;;

(defun mew-header-arrange (&optional nodisplay)
  ;; just in case
  (goto-char (point-min))
  (re-search-forward mew-eoh2 nil t)
  (beginning-of-line)
  (if (> (count-lines (point-min) (point)) mew-header-max-length)
      (message "Header is too long")
    (let* ((len (length mew-field-spec))
	   (visibles (make-vector len nil))
	   (hilightp (and (or window-system mew-xemacs-p)
			  mew-use-highlight-header))
	   (case-fold-search t)
	   others key beg med n i key-face val-face
	   n-spec visiblep)
      (save-restriction
	(narrow-to-region (point-min) (point))
	(goto-char (point-min))
	(while (not (eobp))
	  (if (not (looking-at mew-keyval))
	      (forward-line)
	    (setq key (mew-match 1))
	    (setq beg (match-beginning 0))
	    (setq med (match-end 0))
	    (forward-line)
	    (while (looking-at mew-lwsp) (forward-line))
	    (setq n-spec (mew-assoc-match3 key mew-field-spec 0))
	    (setq n (nth 0 n-spec))
	    (setq visiblep (nth 2 n-spec))
	    (if hilightp
		(progn
		  (setq key-face (or (nth 3 n-spec)
				     mew-highlight-header-face-key))
		  (setq val-face (or (nth 4 n-spec)
				     mew-highlight-header-face-marginal))
		  (put-text-property beg med 'face key-face)
		  (put-text-property med (point) 'face val-face)))
	    (cond
	     ((null n-spec) ;; others
	      (setq others (concat others (buffer-substring beg (point))))
	      (delete-region beg (point)))
	     (visiblep
	      (aset visibles n
		    (concat (aref visibles n)
			    (buffer-substring beg (point))))
	      (delete-region beg (point)))
	     (t ;; invisible
	      ()
	      ))
	    )))
      (and (not mew-field-other-visible) others (insert others))
      (or nodisplay (recenter 0))
      (setq i 0)
      (while (< i len)
	(if (aref visibles i) (insert (aref visibles i)))
	(setq i (1+ i)))
      (and mew-field-other-visible others (insert others))
      )
    (mew-highlight-x-face (point-min) (point))
    )
  ;; when analysis, body will be inserted later.
  ;; when non-analysis, body is already inserted.
  ;; Sigh!
  (goto-char (point-min))
  (re-search-forward mew-eoh2 nil t)
  (forward-line)
  )

(provide 'mew-decode)

;;; Copyright Notice:

;; Copyright (C) 1996, 1997, 1998 Mew developing team.
;; All rights reserved.

;; Redistribution and use in source and binary forms, with or without
;; modification, are permitted provided that the following conditions
;; are met:
;; 
;; 1. Redistributions of source code must retain the above copyright
;;    notice, this list of conditions and the following disclaimer.
;; 2. Redistributions in binary form must reproduce the above copyright
;;    notice, this list of conditions and the following disclaimer in the
;;    documentation and/or other materials provided with the distribution.
;; 3. All advertising materials mentioning features or use of this software
;;    must display the following acknowledgement:
;;       This product includes software developed by 
;;       Mew developing team and its contributors.
;; 4. Neither the name of the team nor the names of its contributors
;;    may be used to endorse or promote products derived from this software
;;    without specific prior written permission.
;; 
;; THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
;; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
;; PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
;; LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
;; CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
;; SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;; BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;; WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
;; OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
;; IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

;;; mew-decode.el ends here
