Add MIME security button.
[gnus] / lisp / gnus-art.el
index 058ca5c..3436814 100644 (file)
@@ -659,10 +659,12 @@ used."
 
 (defcustom gnus-mime-action-alist
   '(("save to file" . gnus-mime-save-part)
+    ("save and strip" . gnus-mime-save-part-and-strip)
     ("display as text" . gnus-mime-inline-part)
     ("view the part" . gnus-mime-view-part)
     ("pipe to command" . gnus-mime-pipe-part)
     ("toggle display" . gnus-article-press-button)
+    ("toggle display" . gnus-article-view-part-as-charset)
     ("view as type" . gnus-mime-view-part-as-type)
     ("internalize type" . gnus-mime-internalize-part)
     ("externalize type" . gnus-mime-externalize-part))
@@ -964,6 +966,17 @@ See the manual for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
+(defvar gnus-article-encrypt-protocol-alist
+  '(("PGP" . mml2015-self-encrypt)))
+
+;; Set to nil if more than one protocol added to
+;; gnus-article-encrypt-protocol-alist.
+(defcustom gnus-article-encrypt-protocol "PGP"
+  "The protocol used for encrypt articles.
+It is a string, such as \"PGP\". If nil, ask user."
+  :type 'string
+  :group 'mime-security)
+
 ;;; Internal variables
 
 (defvar article-goto-body-goes-to-point-min-p nil)
@@ -2922,7 +2935,9 @@ If ALL-HEADERS is non-nil, no headers are hidden."
   '((gnus-article-press-button "\r" "Toggle Display")
     (gnus-mime-view-part "v" "View Interactively...")
     (gnus-mime-view-part-as-type "t" "View As Type...")
+    (gnus-mime-view-part-as-charset "C" "View As charset...")
     (gnus-mime-save-part "o" "Save...")
+    (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
     (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
     (gnus-mime-inline-part "i" "View As Text, In This Buffer")
     (gnus-mime-internalize-part "E" "View Internally")
@@ -2968,11 +2983,73 @@ If ALL-HEADERS is non-nil, no headers are hidden."
     (let ((handles (or handles gnus-article-mime-handles))
          (mail-parse-charset gnus-newsgroup-charset)
          (mail-parse-ignored-charsets 
-          (save-excursion (set-buffer gnus-summary-buffer)
-                          gnus-newsgroup-ignored-charsets)))
-      (if (stringp (car handles))
-         (gnus-mime-view-all-parts (cdr handles))
-       (mapcar 'mm-display-part handles)))))
+          (with-current-buffer gnus-summary-buffer
+            gnus-newsgroup-ignored-charsets)))
+      (mm-remove-parts handles)
+      (goto-char (point-min))
+      (or (search-forward "\n\n") (goto-char (point-max)))
+      (let (buffer-read-only)
+       (delete-region (point) (point-max)))
+      (mm-display-parts handles))))
+
+(defun gnus-mime-save-part-and-strip ()
+  "Save the MIME part under point then replace it with an external body."
+  (interactive)
+  (gnus-article-check-buffer)
+  (let* ((data (get-text-property (point) 'gnus-data)) 
+        (file (mm-save-part data))
+        param)
+    (when file
+      (with-current-buffer (mm-handle-buffer data)
+       (erase-buffer)
+       (insert "Content-Type: " (mm-handle-media-type data))
+       (mml-insert-parameter-string (cdr (mm-handle-type data))
+                                    '(charset))
+       (insert "\n")
+       (insert "Content-ID: " (message-make-message-id) "\n")
+       (insert "Content-Transfer-Encoding: binary\n")
+       (insert "\n"))
+      (setcdr data
+             (cdr (mm-make-handle nil 
+                                  `("message/external-body"
+                                    (access-type . "LOCAL-FILE")
+                                    (name . ,file)))))
+      (set-buffer gnus-summary-buffer)
+      (gnus-article-edit-article
+       `(lambda () 
+          (erase-buffer)
+          (let ((mail-parse-charset (or gnus-article-charset 
+                                        ',gnus-newsgroup-charset))
+                (mail-parse-ignored-charsets 
+                 (or gnus-article-ignored-charsets
+                     ',gnus-newsgroup-ignored-charsets))
+                (mbl mml-buffer-list))
+            (setq mml-buffer-list nil)
+            (insert-buffer gnus-original-article-buffer)
+            (mime-to-mml gnus-article-mime-handles)
+            (setq gnus-article-mime-handles nil)
+            (make-local-hook 'kill-buffer-hook)
+            (let ((mbl1 mml-buffer-list))
+              (setq mml-buffer-list mbl)
+              (set (make-local-variable 'mml-buffer-list) mbl1))
+            (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
+       `(lambda (no-highlight)
+         (let ((mail-parse-charset (or gnus-article-charset
+                                       ',gnus-newsgroup-charset))
+               (message-options message-options)
+               (message-options-set-recipient)
+               (mail-parse-ignored-charsets  
+                (or gnus-article-ignored-charsets
+                    ',gnus-newsgroup-ignored-charsets)))
+          (mml-to-mime)
+          (mml-destroy-buffers)
+          (remove-hook 'kill-buffer-hook 
+                       'mml-destroy-buffers t)
+          (kill-local-variable 'mml-buffer-list))
+         (gnus-summary-edit-article-done
+          ,(or (mail-header-references gnus-current-headers) "")
+          ,(gnus-group-read-only-p) 
+          ,gnus-summary-buffer no-highlight))))))
 
 (defun gnus-mime-save-part ()
   "Save the MIME part under point."
@@ -3054,7 +3131,7 @@ If ALL-HEADERS is non-nil, no headers are hidden."
         contents charset
         (b (point))
         buffer-read-only)
-    (if (mm-handle-undisplayer handle)
+    (if (and (not arg) (mm-handle-undisplayer handle))
        (mm-remove-part handle)
       (setq contents (mm-get-part handle))
       (cond
@@ -3063,6 +3140,8 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                           (mm-handle-type handle) 'charset)
                          gnus-newsgroup-charset)))
        ((numberp arg)
+       (if (mm-handle-undisplayer handle)
+           (mm-remove-part handle))
        (setq charset
              (or (cdr (assq arg 
                             gnus-summary-show-article-charset-alist))
@@ -3077,6 +3156,23 @@ If ALL-HEADERS is non-nil, no headers are hidden."
                          contents))
       (goto-char b))))
 
+(defun gnus-mime-view-part-as-charset (&optional handle arg)
+  "Insert the MIME part under point into the current buffer."
+  (interactive (list nil current-prefix-arg))
+  (gnus-article-check-buffer)
+  (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
+        contents charset
+        (b (point))
+        buffer-read-only)
+    (if (mm-handle-undisplayer handle)
+       (mm-remove-part handle))
+    (let ((gnus-newsgroup-charset
+          (or (cdr (assq arg 
+                         gnus-summary-show-article-charset-alist))
+              (read-coding-system "Charset: ")))
+         (gnus-newsgroup-ignored-charsets 'gnus-all))
+      (gnus-article-press-button))))
+
 (defun gnus-mime-externalize-part (&optional handle)
   "View the MIME part under point with an external viewer."
   (interactive)
@@ -3147,6 +3243,11 @@ In no internal viewer is available, use an external viewer."
   (interactive "p")
   (gnus-article-part-wrapper n 'gnus-mime-copy-part))
 
+(defun gnus-article-view-part-as-charset (n)
+  "Copy MIME part N, which is the numerical prefix."
+  (interactive "p")
+  (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
+
 (defun gnus-article-externalize-part (n)
   "View MIME part N externally, which is the numerical prefix."
   (interactive "p")
@@ -3252,6 +3353,8 @@ In no internal viewer is available, use an external viewer."
                                    'name)
             (mail-content-type-get (mm-handle-disposition handle)
                                    'filename)
+            (mail-content-type-get (mm-handle-type handle)
+                                   'url)
             ""))
        (gnus-tmp-type (mm-handle-media-type handle))
        (gnus-tmp-description
@@ -3388,10 +3491,12 @@ In no internal viewer is available, use an external viewer."
    ((equal (car handle) "multipart/signed")
     (or (memq 'signed gnus-article-wash-types)
        (push 'signed gnus-article-wash-types))
+    (gnus-insert-mime-security-button handle)
     (gnus-mime-display-mixed (cdr handle)))
    ((equal (car handle) "multipart/encrypted")
     (or (memq 'encrypted gnus-article-wash-types)
        (push 'encrypted gnus-article-wash-types))
+    (gnus-insert-mime-security-button handle)
     (gnus-mime-display-mixed (cdr handle)))
    (t
     (gnus-mime-display-mixed (cdr handle)))))
@@ -4805,6 +4910,151 @@ For example:
    (t
     (error "%S is not a valid value" val))))
 
+(defun gnus-article-encrypt-body (protocol &optional n)
+  "Encrypt the article body."
+  (interactive 
+   (list
+    (or gnus-article-encrypt-protocol
+       (completing-read "Encrypt protocol: "
+                        gnus-article-encrypt-protocol-alist 
+                        nil t))
+    current-prefix-arg))
+  (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
+    (unless func
+      (error (format "Can't find the encrypt protocol %s" protocol)))
+    (if (equal gnus-newsgroup-name "nndraft:drafts")
+       (error "Can't encrypt the article in group nndraft:drafts."))
+    (if (equal gnus-newsgroup-name "nndraft:queue")
+       (error "Don't encrypt the article in group nndraft:queue."))
+    (gnus-summary-iterate n
+      (save-excursion
+       (set-buffer gnus-summary-buffer)
+       (let ((mail-parse-charset gnus-newsgroup-charset)
+             (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
+             (summary-buffer gnus-summary-buffer)
+             references point)
+         (gnus-set-global-variables)
+         (when (gnus-group-read-only-p)
+           (error "The current newsgroup does not support article encrypt"))
+         (gnus-summary-show-article t)
+         (setq references
+             (or (mail-header-references gnus-current-headers) ""))
+         (set-buffer gnus-article-buffer)
+         (let* ((buffer-read-only nil)
+                (headers
+                 (mapcar (lambda (field)
+                           (and (save-restriction 
+                                  (message-narrow-to-head)
+                                  (goto-char (point-min))
+                                  (search-forward field nil t))
+                                (prog2
+                                    (message-narrow-to-field)
+                                    (buffer-substring (point-min) (point-max))
+                                  (delete-region (point-min) (point-max))
+                                  (widen))))
+                         '("Content-Type:" "Content-Transfer-Encoding:"
+                           "Content-Disposition:"))))
+           (message-narrow-to-head)
+           (message-remove-header "MIME-Version")
+           (goto-char (point-max))
+           (setq point (point))
+           (insert (apply 'concat headers))
+           (widen)
+           (narrow-to-region point (point-max))
+           (let ((message-options message-options))
+             (message-options-set 'message-sender user-mail-address)
+             (message-options-set 'message-recipients user-mail-address)
+             (message-options-set 'message-sign-encrypt 'not)
+             (funcall func))
+           (goto-char (point-min))
+           (insert "MIME-Version: 1.0\n")
+           (widen)
+           (gnus-summary-edit-article-done
+            references nil summary-buffer t))
+         (when gnus-keep-backlog
+           (gnus-backlog-remove-article
+            (car gnus-article-current) (cdr gnus-article-current)))
+         (save-excursion
+           (when (get-buffer gnus-original-article-buffer)
+             (set-buffer gnus-original-article-buffer)
+             (setq gnus-original-article nil)))
+         (when gnus-use-cache
+           (gnus-cache-update-article
+            (car gnus-article-current) (cdr gnus-article-current))))))))
+
+(defvar gnus-mime-security-button-line-format "%{%([[%t:%i]]%)%}\n"
+  "The following specs can be used:
+%t  The security MIME type
+%i  Additional info")
+
+(defvar gnus-mime-security-button-line-format-alist
+  '((?t gnus-tmp-type ?s)
+    (?i gnus-tmp-info ?s)))
+
+(defvar gnus-mime-security-button-map
+  (let ((map (make-sparse-keymap)))
+    (set-keymap-parent map gnus-article-mode-map)
+    (define-key map gnus-mouse-2 'gnus-article-push-button)
+    (define-key map "\r" 'gnus-article-press-button)
+    map))
+
+(defvar gnus-mime-security-details-buffer nil)
+
+(defun gnus-mime-security-show-details (handle)
+  (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
+    (if details
+       (progn
+         (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
+             (with-current-buffer gnus-mime-security-details-buffer
+               (erase-buffer)
+               t)
+           (setq gnus-mime-security-details-buffer
+                 (gnus-get-buffer-create "*MIME Security Details*")))
+         (with-current-buffer gnus-mime-security-details-buffer
+           (insert details))
+         (pop-to-buffer gnus-mime-security-details-buffer))
+      (gnus-message 5 "No details."))))
+
+(defun gnus-insert-mime-security-button (handle &optional displayed)
+  (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
+        (gnus-tmp-type
+         (concat 
+          (or (nth 2 (assoc protocol mm-verify-function-alist))
+              (nth 2 (assoc protocol mm-decrypt-function-alist))
+              "Unknown")
+          (if (equal (car handle) "multipart/signed")
+              " Signed" " Encrypted")))
+        (gnus-tmp-info
+         (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
+             "Undecided"))
+        b e)
+    (unless (bolp)
+      (insert "\n"))
+    (setq b (point))
+    (gnus-eval-format
+     gnus-mime-security-button-line-format 
+     gnus-mime-security-button-line-format-alist
+     `(local-map ,gnus-mime-security-button-map
+                keymap ,gnus-mime-security-button-map
+                gnus-callback gnus-mime-security-show-details
+                article-type annotation
+                gnus-data ,handle))
+    (setq e (point))
+    (widget-convert-button
+     'link b e
+     :mime-handle handle
+     :action 'gnus-widget-press-button
+     :button-keymap gnus-mime-security-button-map
+     :help-echo
+     (lambda (widget/window &optional overlay pos)
+       ;; Needed to properly clear the message due to a bug in
+       ;; wid-edit (XEmacs only).
+       (if (boundp 'help-echo-owns-message)
+          (setq help-echo-owns-message t))
+       (format
+       "%S: show detail"
+       (aref gnus-mouse-2 0))))))
+
 (gnus-ems-redefine)
 
 (provide 'gnus-art)