(article-verify-x-pgp-sig): Autoload "mm-uu".
[elisp/gnus.git-] / lisp / gnus-art.el
index ac0821a..89fc99f 100644 (file)
@@ -1013,6 +1013,25 @@ See the manual for details."
   :group 'gnus-article-treat
   :type gnus-article-treat-custom)
 
+(defcustom gnus-treat-x-pgp-sig nil
+  "Verify X-PGP-Sig.
+Valid values are nil, t, `head', `last', an integer or a predicate.
+See the manual for details."
+  :group 'gnus-article-treat
+  :group 'mime-security
+  :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)
@@ -1023,6 +1042,7 @@ See the manual for details."
 (defvar gnus-treatment-function-alist
   '((gnus-treat-decode-article-as-default-mime-charset
      gnus-article-decode-article-as-default-mime-charset)
+    (gnus-treat-x-pgp-sig gnus-article-verify-x-pgp-sig)
     (gnus-treat-strip-banner gnus-article-strip-banner)
     (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body)
     (gnus-treat-buttonize gnus-article-add-buttons)
@@ -2736,6 +2756,78 @@ If variable `gnus-use-long-file-name' is non-nil, it is
         (expand-file-name "news" (gnus-newsgroup-directory-form newsgroup)))
        gnus-article-save-directory)))
 
+(autoload 'mm-uu-pgp-signed-test "mm-uu")
+
+(defun article-verify-x-pgp-sig ()
+  "Verify X-PGP-Sig."
+  (interactive)
+  (let ((sig (with-current-buffer gnus-original-article-buffer
+              (gnus-fetch-field "X-PGP-Sig")))
+       items info headers)
+    (when (and sig (mm-uu-pgp-signed-test))
+      (with-temp-buffer
+       (insert-buffer gnus-original-article-buffer)
+       (setq items (split-string sig))
+       (message-narrow-to-head)
+       (let ((inhibit-point-motion-hooks t)
+             (case-fold-search t))
+         ;; Don't verify multiple headers.
+         (setq headers (mapconcat (lambda (header)
+                                    (concat header ": " 
+                                            (mail-fetch-field header) "\n"))
+                                  (split-string (nth 1 items) ",") "")))
+       (delete-region (point-min) (point-max))
+       (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
+       (insert "X-Signed-Headers: " (nth 1 items) "\n")
+       (insert headers)
+       (widen)
+       (forward-line)
+       (while (not (eobp))
+         (if (looking-at "^-")
+             (insert "- "))
+         (forward-line))
+       (insert "\n-----BEGIN PGP SIGNATURE-----\n")
+       (insert "Version: " (car items) "\n\n")
+       (insert (mapconcat 'identity (cddr items) "\n"))
+       (insert "\n-----END PGP SIGNATURE-----\n")
+       (let ((mm-security-handle (list (format "multipart/signed"))))
+         (mml2015-clean-buffer)
+         (let ((coding-system-for-write (or gnus-newsgroup-charset
+                                            'iso-8859-1)))
+           (funcall (mml2015-clear-verify-function)))
+         (setq info 
+               (or (mm-handle-multipart-ctl-parameter 
+                    mm-security-handle 'gnus-details)
+                   (mm-handle-multipart-ctl-parameter 
+                    mm-security-handle 'gnus-info)))))
+      (when info
+       (let (buffer-read-only bface eface)
+         (save-restriction
+           (message-narrow-to-head)
+           (goto-char (point-max))
+           (forward-line -1)
+           (setq bface (get-text-property (gnus-point-at-bol) 'face)
+                 eface (get-text-property (1- (gnus-point-at-eol)) 'face))
+           (message-remove-header "X-Gnus-PGP-Verify")
+           (if (re-search-forward "^X-PGP-Sig:" nil t)
+               (forward-line)
+             (goto-char (point-max)))
+           (narrow-to-region (point) (point))
+           (insert "X-Gnus-PGP-Verify: " info "\n")
+           (goto-char (point-min))
+           (forward-line)
+           (while (not (eobp))
+             (if (not (looking-at "^[ \t]"))
+                 (insert " "))
+             (forward-line))
+           ;; Do highlighting.
+           (goto-char (point-min))
+           (when (looking-at "\\([^:]+\\): *")
+             (put-text-property (match-beginning 1) (1+ (match-end 1))
+                                'face bface)
+             (put-text-property (match-end 0) (point-max)
+                                'face eface))))))))
+
 (eval-and-compile
   (mapcar
    (lambda (func)
@@ -2756,6 +2848,7 @@ If variable `gnus-use-long-file-name' is non-nil, it is
                    (call-interactively ',afunc)
                  (apply ',afunc args))))))))
    '(article-hide-headers
+     article-verify-x-pgp-sig
      article-hide-boring-headers
      article-toggle-headers
      article-treat-overstrike
@@ -3320,9 +3413,9 @@ value of the variable `gnus-show-mime' is non-nil."
 (defun gnus-mime-button-menu (event)
   "Construct a context-sensitive menu of MIME commands."
   (interactive "e")
-  (save-excursion
+  (save-window-excursion
     (let ((pos (event-start event)))
-      (set-buffer (window-buffer (posn-window pos)))
+      (select-window (posn-window pos))
       (goto-char (posn-point pos))
       (gnus-article-check-buffer)
       (let ((response (x-popup-menu
@@ -3341,11 +3434,14 @@ value of the variable `gnus-show-mime' is non-nil."
     (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."
@@ -3841,16 +3937,22 @@ In no internal viewer is available, use an external viewer."
         (not gnus-mime-display-multipart-as-mixed))
     ;;;!!!We should find the start part, but we just default
     ;;;!!!to the first part.
-    (gnus-mime-display-part (cadr handle)))
-   ;; Other multiparts are handled like multipart/mixed.
+    ;;(gnus-mime-display-part (cadr handle))
+    ;;;!!! Most multipart/related is an HTML message plus images.
+    ;;;!!! Unfortunately we are unable to let W3 display those 
+    ;;;!!! included images, so we just display it as a mixed multipart.
+    (gnus-mime-display-mixed (cdr handle)))
    ((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)))
+   ;; Other multiparts are handled like multipart/mixed.
    (t
     (gnus-mime-display-mixed (cdr handle)))))
 
@@ -3962,6 +4064,7 @@ In no internal viewer is available, use an external viewer."
        (unless (setq not-pref (cadr (member preferred ihandles)))
          (setq not-pref (car ihandles)))
        (when (or ibegend
+                 (not preferred)
                  (not (gnus-unbuttonized-mime-type-p
                        "multipart/alternative")))
          (gnus-add-text-properties
@@ -5446,6 +5549,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))))))
+
 
 ;;; @ for mime-view
 ;;;