;;; gnus-art.el --- article mode commands for Gnus
;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;; 2005, 2006, 2007 Free Software Foundation, Inc.
+;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: news
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING. If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;; Code:
+;; For Emacs < 22.2.
+(eval-and-compile
+ (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
(eval-when-compile
- (require 'cl)
- (defvar tool-bar-map)
- (defvar w3m-minor-mode-map))
+ (require 'cl))
+(defvar tool-bar-map)
+(defvar w3m-minor-mode-map)
(require 'gnus)
;; Avoid the "Recursive load suspected" error in Emacs 21.1.
"*All headers that do not match this regexp will be hidden.
This variable can also be a list of regexp of headers to remain visible.
If this variable is non-nil, `gnus-ignored-headers' will be ignored."
- :type '(repeat :value-to-internal (lambda (widget value)
- (custom-split-regexp-maybe value))
- :match (lambda (widget value)
- (or (stringp value)
- (widget-editable-list-match widget value)))
- regexp)
+ :type '(choice
+ (repeat :value-to-internal (lambda (widget value)
+ (custom-split-regexp-maybe value))
+ :match (lambda (widget value)
+ (or (stringp value)
+ (widget-editable-list-match widget value)))
+ regexp)
+ (const :tag "Use gnus-ignored-headers" nil)
+ regexp)
:group 'gnus-article-hiding)
(defcustom gnus-sorted-header-list
If the match is a string, it is used as a regexp match on the
article. If the match is a symbol, that symbol will be funcalled
-from the buffer of the article to be saved with the newsgroup as
-the parameter. If it is a list, it will be evaled in the same
-buffer.
+from the buffer of the article to be saved with the newsgroup as the
+parameter. If it is a list, it will be evaled in the same buffer.
-If this form or function returns a string, this string will be
-used as a possible file name; and if it returns a non-nil list,
-that list will be used as possible file names."
+If this form or function returns a string, this string will be used as a
+possible file name; and if it returns a non-nil list, that list will be
+used as possible file names."
:group 'gnus-article-saving
:type '(repeat (choice (list :value (fun) function)
(cons :value ("" "") regexp (repeat string))
(defcustom gnus-copy-article-ignored-headers nil
"List of headers to be removed when copying an article.
Each element is a regular expression."
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:type '(repeat regexp)
:group 'gnus-article-various)
Currently, `pbm' is used for X-Face images and `png' is used for Face
images in Emacs. Only the `:face' property is effective on the `xface'
image type in XEmacs if it is built with the libcompface library."
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:group 'gnus-article-headers
:type '(repeat (cons :format "%v" (symbol :tag "Image type") plist)))
When 0, point will be placed on the same part as before. When
positive (negative), move point forward (backwards) this many
parts. When nil, redisplay article."
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:group 'gnus-article-mime
:type '(choice (const nil :tag "Redisplay article.")
(const 1 :tag "Next part.")
:type gnus-article-treat-head-custom)
(put 'gnus-treat-buttonize-head 'highlight t)
-(defcustom gnus-treat-emphasize
- (and (or window-system
- (featurep 'xemacs))
- 50000)
+(defcustom gnus-treat-emphasize 50000
"Emphasize text.
Valid values are nil, t, `head', `first', `last', an integer or a
predicate. See Info node `(gnus)Customizing Articles'."
If it is t, all long headers are unfolded.
This variable has no effect if `gnus-treat-unfold-headers' is nil."
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:group 'gnus-article-treat
:type '(choice (const nil)
(const :tag "all" t)
(put 'gnus-treat-newsgroups-picon 'highlight t)
(defcustom gnus-treat-body-boundary
- (if (and (eq window-system 'x)
- (or gnus-treat-newsgroups-picon
- gnus-treat-mail-picon
- gnus-treat-from-picon))
- 'head nil)
+ (if (or gnus-treat-newsgroups-picon
+ gnus-treat-mail-picon
+ gnus-treat-from-picon)
+ ;; If there's much decoration, the user might prefer a boundery.
+ 'head
+ nil)
"Draw a boundary at the end of the headers.
Valid values are nil and `head'.
See Info node `(gnus)Customizing Articles' for details."
(defvar gnus-save-article-buffer nil)
-(defvar gnus-article-mode-line-format-alist
- (nconc '((?w (gnus-article-wash-status) ?s)
- (?m (gnus-article-mime-part-status) ?s))
- gnus-summary-mode-line-format-alist))
-
(defvar gnus-number-of-articles-to-be-saved nil)
(defvar gnus-inhibit-hiding nil)
;;; Macros for dealing with the article buffer.
(defmacro gnus-with-article-headers (&rest forms)
- `(save-excursion
- (set-buffer gnus-article-buffer)
+ `(with-current-buffer gnus-article-buffer
(save-restriction
(let ((inhibit-read-only t)
(inhibit-point-motion-hooks t)
(put 'gnus-with-article-headers 'edebug-form-spec '(body))
(defmacro gnus-with-article-buffer (&rest forms)
- `(save-excursion
- (set-buffer gnus-article-buffer)
+ `(with-current-buffer gnus-article-buffer
(let ((inhibit-read-only t))
,@forms)))
(mail-header-fold-field)
(goto-char (point-max))))))
-(defcustom gnus-article-truncate-lines default-truncate-lines
+(defcustom gnus-article-truncate-lines (default-value 'truncate-lines)
"Value of `truncate-lines' in Gnus Article buffer.
Valid values are nil, t, `head', `first', `last', an integer or a
predicate. See Info node `(gnus)Customizing Articles'."
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:group 'gnus-article
;; :link '(custom-manual "(gnus)Customizing Articles")
:type 'boolean)
"Toggle whether to fold or truncate long lines in article the buffer.
If ARG is non-nil and not a number, toggle
`gnus-article-truncate-lines' too. If ARG is a number, truncate
-long lines iff arg is positive."
+long lines if and only if arg is positive."
(interactive "P")
(cond
((and (numberp arg) (> arg 0))
(forward-line 1)
(point))))))
-(eval-when-compile
- (defvar gnus-face-properties-alist))
+(defvar gnus-face-properties-alist)
-(defun article-display-face ()
+(defun article-display-face (&optional force)
"Display any Face headers in the header."
- (interactive)
+ (interactive (list 'force))
(let ((wash-face-p buffer-read-only))
(gnus-with-article-headers
;; When displaying parts, this function can be called several times on
;; read-only.
(if (and wash-face-p (memq 'face gnus-article-wash-types))
(gnus-delete-images 'face)
- (let (face faces from)
+ (let ((from (message-fetch-field "from"))
+ face faces)
(save-current-buffer
(when (and wash-face-p
(gnus-buffer-live-p gnus-original-article-buffer)
(set-buffer gnus-original-article-buffer))
(save-restriction
(mail-narrow-to-head)
- (while (gnus-article-goto-header "Face")
- (push (mail-header-field-value) faces))))
+ (when (or force
+ ;; Check whether this face is censored.
+ (not (and gnus-article-x-face-too-ugly
+ (or from
+ (setq from (message-fetch-field "from")))
+ (string-match gnus-article-x-face-too-ugly
+ from))))
+ (while (gnus-article-goto-header "Face")
+ (push (mail-header-field-value) faces)))))
(when faces
(goto-char (point-min))
- (let ((from (gnus-article-goto-header "from"))
- png image)
- (unless from
+ (let (png image)
+ (unless (setq from (gnus-article-goto-header "from"))
(insert "From:")
(setq from (point))
- (insert "[no `from' set]\n"))
+ (insert " [no `from' set]\n"))
(while faces
(when (setq png (gnus-convert-face-to-png (pop faces)))
(setq image
;; instead.
(gnus-delete-images 'xface)
;; Display X-Faces.
- (let (x-faces from face)
+ (let ((from (message-fetch-field "from"))
+ x-faces face)
(save-current-buffer
(when (and wash-face-p
(gnus-buffer-live-p gnus-original-article-buffer)
(set-buffer gnus-original-article-buffer))
(save-restriction
(mail-narrow-to-head)
- (while (gnus-article-goto-header "X-Face")
- (push (mail-header-field-value) x-faces))
- (setq from (message-fetch-field "from"))))
- ;; Sending multiple EOFs to xv doesn't work, so we only do a
- ;; single external face.
- (when (stringp gnus-article-x-face-command)
- (setq x-faces (list (car x-faces))))
- (when (and x-faces
- gnus-article-x-face-command
- (or force
- ;; Check whether this face is censored.
- (not gnus-article-x-face-too-ugly)
- (and from
- (not (string-match gnus-article-x-face-too-ugly
- from)))))
- (while (setq face (pop x-faces))
- ;; We display the face.
- (cond ((stringp gnus-article-x-face-command)
- ;; The command is a string, so we interpret the command
- ;; as a, well, command, and fork it off.
- (let ((process-connection-type nil))
- (gnus-set-process-query-on-exit-flag
- (start-process
- "article-x-face" nil shell-file-name
- shell-command-switch gnus-article-x-face-command)
- nil)
- (with-temp-buffer
- (insert face)
- (process-send-region "article-x-face"
- (point-min) (point-max)))
- (process-send-eof "article-x-face")))
- ((functionp gnus-article-x-face-command)
- ;; The command is a lisp function, so we call it.
- (funcall gnus-article-x-face-command face))
- (t
- (error "%s is not a function"
- gnus-article-x-face-command))))))))))
+ (and gnus-article-x-face-command
+ (or force
+ ;; Check whether this face is censored.
+ (not (and gnus-article-x-face-too-ugly
+ (or from
+ (setq from (message-fetch-field "from")))
+ (string-match gnus-article-x-face-too-ugly
+ from))))
+ (while (gnus-article-goto-header "X-Face")
+ (push (mail-header-field-value) x-faces)))))
+ (when x-faces
+ ;; We display the face.
+ (cond ((functionp gnus-article-x-face-command)
+ ;; The command is a lisp function, so we call it.
+ (mapc gnus-article-x-face-command x-faces))
+ ((stringp gnus-article-x-face-command)
+ ;; The command is a string, so we interpret the command
+ ;; as a, well, command, and fork it off.
+ (let ((process-connection-type nil))
+ (gnus-set-process-query-on-exit-flag
+ (start-process
+ "article-x-face" nil shell-file-name
+ shell-command-switch gnus-article-x-face-command)
+ nil)
+ ;; Sending multiple EOFs to xv doesn't work,
+ ;; so we only do a single external face.
+ (with-temp-buffer
+ (insert (car x-faces))
+ (process-send-region "article-x-face"
+ (point-min) (point-max)))
+ (process-send-eof "article-x-face")))
+ (t
+ (error "`%s' set to `%s' is not a function"
+ gnus-article-x-face-command
+ 'gnus-article-x-face-command)))))))))
(defun article-decode-mime-words ()
"Decode all MIME-encoded words in the article."
(goto-char (setq end start)))))
(defun article-decode-group-name ()
- "Decode group names in `Newsgroups:'."
+ "Decode group names in Newsgroups, Followup-To and Xref headers."
(let ((inhibit-point-motion-hooks t)
(inhibit-read-only t)
- (method (gnus-find-method-for-group gnus-newsgroup-name)))
+ (method (gnus-find-method-for-group gnus-newsgroup-name))
+ regexp)
(when (and (or gnus-group-name-charset-method-alist
gnus-group-name-charset-group-alist)
(gnus-buffer-live-p gnus-original-article-buffer))
(save-restriction
(article-narrow-to-head)
- (with-current-buffer gnus-original-article-buffer
- (goto-char (point-min)))
- (while (re-search-forward
- "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
- (replace-match (save-match-data
- (gnus-decode-newsgroups
- ;; XXX how to use data in article buffer?
- (with-current-buffer gnus-original-article-buffer
- (re-search-forward
- "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
- nil t)
- (match-string 1))
- gnus-newsgroup-name method))
- t t nil 1))
- (goto-char (point-min))
- (with-current-buffer gnus-original-article-buffer
- (goto-char (point-min)))
- (while (re-search-forward
- "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
- (replace-match (save-match-data
- (gnus-decode-newsgroups
- ;; XXX how to use data in article buffer?
- (with-current-buffer gnus-original-article-buffer
- (re-search-forward
- "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
- nil t)
- (match-string 1))
- gnus-newsgroup-name method))
- t t nil 1))))))
+ (dolist (header '("Newsgroups" "Followup-To" "Xref"))
+ (with-current-buffer gnus-original-article-buffer
+ (goto-char (point-min)))
+ (setq regexp (concat "^" header
+ ":\\([^\n]*\\(?:\n[\t ]+[^\n]+\\)*\\)\n"))
+ (while (re-search-forward regexp nil t)
+ (replace-match (save-match-data
+ (gnus-decode-newsgroups
+ ;; XXX how to use data in article buffer?
+ (with-current-buffer gnus-original-article-buffer
+ (re-search-forward regexp nil t)
+ (match-string 1))
+ gnus-newsgroup-name method))
+ t t nil 1))
+ (goto-char (point-min)))))))
(autoload 'idna-to-unicode "idna")
(t
(apply (car func) (cdr func))))))))))
+;; External.
+(declare-function w3-region "ext:w3-display" (st nd))
+
(defun gnus-article-wash-html-with-w3 ()
"Wash the current buffer with w3."
(mm-setup-w3)
(w3-region (point-min) (point-max))
(error))))
+;; External.
+(declare-function w3m-region "ext:w3m" (start end &optional url charset))
+
(defun gnus-article-wash-html-with-w3m ()
"Wash the current buffer with emacs-w3m."
(mm-setup-w3m)
;; Put the mark meaning this part was rendered by emacs-w3m.
'mm-inline-text-html-with-w3m t))))
-(eval-when-compile (defvar charset)) ;; Bound by `article-wash-html'.
+(defvar charset) ;; Bound by `article-wash-html'.
(defun gnus-article-wash-html-with-w3m-standalone ()
"Wash the current buffer with w3m."
on each file, if it is `ask' ask once when exiting from the
summary buffer."
:group 'gnus-article
- :version "23.0" ;; No Gnus
+ :version "23.1" ;; No Gnus
:type '(choice (const :tag "Don't delete" nil)
(const :tag "Don't ask" t)
(const :tag "Ask" ask)
(or how
(setq how gnus-article-browse-delete-temp)))
(when (and (eq how 'ask)
- (y-or-n-p (format
- "Delete all %s temporary HTML file(s)? "
- (length gnus-article-browse-html-temp-list)))
+ (gnus-y-or-n-p (format
+ "Delete all %s temporary HTML file(s)? "
+ (length gnus-article-browse-html-temp-list)))
(setq how t)))
(dolist (file gnus-article-browse-html-temp-list)
(when (and (file-exists-p file)
(setq gnus-article-browse-html-temp-list nil))
gnus-article-browse-html-temp-list)
-(defun gnus-article-browse-html-parts (list)
+(defun gnus-article-browse-html-parts (list &optional header)
"View all \"text/html\" parts from LIST.
-Recurse into multiparts."
+Recurse into multiparts. The optional HEADER that should be a decoded
+message header will be added to the bodies of the \"text/html\" parts."
;; Internal function used by `gnus-article-browse-html-article'.
- (let ((showed))
+ (let (type file charset tmp-file showed)
;; Find and show the html-parts.
(dolist (handle list)
;; If HTML, show it:
- (when (listp handle)
- (cond ((and (bufferp (car handle))
- (string-match "text/html" (car (mm-handle-type handle))))
- (let ((tmp-file (mm-make-temp-file
- ;; Do we need to care for 8.3 filenames?
- "mm-" nil ".html")))
- (mm-save-part-to-file handle tmp-file)
- (add-to-list 'gnus-article-browse-html-temp-list tmp-file)
- (add-hook 'gnus-summary-prepare-exit-hook
- 'gnus-article-browse-delete-temp-files)
- (add-hook 'gnus-exit-gnus-hook
- (lambda ()
- (gnus-article-browse-delete-temp-files t)))
- ;; FIXME: Warn if there's an <img> tag?
- (browse-url-of-file tmp-file)
- (setq showed t)))
- ;; If multipart, recurse
- ((and (stringp (car handle))
- (string-match "^multipart/" (car handle))
- (setq showed
- (or showed
- (gnus-article-browse-html-parts handle))))))))
+ (cond ((not (listp handle)))
+ ((or (equal (car (setq type (mm-handle-type handle))) "text/html")
+ (and (equal (car type) "message/external-body")
+ (or header
+ (setq file (or (mail-content-type-get type 'name)
+ (mail-content-type-get
+ (mm-handle-disposition handle)
+ 'filename))))
+ (or (mm-handle-cache handle)
+ (condition-case code
+ (progn (mm-extern-cache-contents handle) t)
+ (error
+ (gnus-message 3 "%s" (error-message-string code))
+ (when (>= gnus-verbose 3) (sit-for 2))
+ nil)))
+ (progn
+ (setq handle (mm-handle-cache handle)
+ type (mm-handle-type handle))
+ (equal (car type) "text/html"))))
+ (when (or (setq charset (mail-content-type-get type 'charset))
+ header
+ (not file))
+ (setq tmp-file (mm-make-temp-file
+ ;; Do we need to care for 8.3 filenames?
+ "mm-" nil ".html")))
+ ;; Add a meta html tag to specify charset and a header.
+ (cond
+ (header
+ (let (title eheader body hcharset coding)
+ (with-temp-buffer
+ (mm-enable-multibyte)
+ (setq case-fold-search t)
+ (insert header "\n")
+ (setq title (message-fetch-field "subject"))
+ (goto-char (point-min))
+ (while (re-search-forward "\\(<\\)\\|\\(>\\)\\|&" nil t)
+ (replace-match (cond ((match-beginning 1) "<")
+ ((match-beginning 2) ">")
+ (t "&"))))
+ (goto-char (point-min))
+ (insert "<pre>\n")
+ (goto-char (point-max))
+ (insert "</pre>\n<hr>\n")
+ ;; We have to examine charset one by one since
+ ;; charset specified in parts might be different.
+ (if (eq charset 'gnus-decoded)
+ (setq charset 'utf-8
+ eheader (mm-encode-coding-string (buffer-string)
+ charset)
+ title (when title
+ (mm-encode-coding-string title charset))
+ body (mm-encode-coding-string (mm-get-part handle)
+ charset))
+ (setq hcharset (mm-find-mime-charset-region (point-min)
+ (point-max)))
+ (cond ((= (length hcharset) 1)
+ (setq hcharset (car hcharset)
+ coding (mm-charset-to-coding-system
+ hcharset)))
+ ((> (length hcharset) 1)
+ (setq hcharset 'utf-8
+ coding hcharset)))
+ (if coding
+ (if charset
+ (progn
+ (setq body
+ (mm-charset-to-coding-system charset))
+ (if (eq coding body)
+ (setq eheader (mm-encode-coding-string
+ (buffer-string) coding)
+ title (when title
+ (mm-encode-coding-string
+ title coding))
+ body (mm-get-part handle))
+ (setq charset 'utf-8
+ eheader (mm-encode-coding-string
+ (buffer-string) charset)
+ title (when title
+ (mm-encode-coding-string
+ title charset))
+ body (mm-encode-coding-string
+ (mm-decode-coding-string
+ (mm-get-part handle) body)
+ charset))))
+ (setq charset hcharset
+ eheader (mm-encode-coding-string
+ (buffer-string) coding)
+ title (when title
+ (mm-encode-coding-string
+ title coding))
+ body (mm-get-part handle)))
+ (setq eheader (mm-string-as-unibyte (buffer-string))
+ body (mm-get-part handle))))
+ (erase-buffer)
+ (mm-disable-multibyte)
+ (insert body)
+ (when charset
+ (mm-add-meta-html-tag handle charset))
+ (when title
+ (goto-char (point-min))
+ (unless (search-forward "<title>" nil t)
+ (re-search-forward "<head>\\s-*" nil t)
+ (insert "<title>" title "</title>\n")))
+ (goto-char (point-min))
+ (or (re-search-forward
+ "<body\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t)
+ (re-search-forward
+ "</head\\(?:\\s-+[^>]+\\|\\s-*\\)>\\s-*" nil t))
+ (insert eheader)
+ (mm-write-region (point-min) (point-max)
+ tmp-file nil nil nil 'binary t))))
+ (charset
+ (mm-with-unibyte-buffer
+ (insert (if (eq charset 'gnus-decoded)
+ (mm-encode-coding-string
+ (mm-get-part handle)
+ (setq charset 'utf-8))
+ (mm-get-part handle)))
+ (if (or (mm-add-meta-html-tag handle charset)
+ (not file))
+ (mm-write-region (point-min) (point-max)
+ tmp-file nil nil nil 'binary t)
+ (setq tmp-file nil))))
+ (tmp-file
+ (mm-save-part-to-file handle tmp-file)))
+ (when tmp-file
+ (add-to-list 'gnus-article-browse-html-temp-list tmp-file))
+ (add-hook 'gnus-summary-prepare-exit-hook
+ 'gnus-article-browse-delete-temp-files)
+ (add-hook 'gnus-exit-gnus-hook
+ (lambda ()
+ (gnus-article-browse-delete-temp-files t)))
+ ;; FIXME: Warn if there's an <img> tag?
+ (browse-url-of-file (or tmp-file (expand-file-name file)))
+ (setq showed t))
+ ;; If multipart, recurse
+ ((equal (mm-handle-media-supertype handle) "multipart")
+ (when (gnus-article-browse-html-parts handle header)
+ (setq showed t)))
+ ((equal (mm-handle-media-type handle) "message/rfc822")
+ (mm-with-multibyte-buffer
+ (mm-insert-part handle)
+ (setq handle (mm-dissect-buffer t t))
+ (when (and (bufferp (car handle))
+ (stringp (car (mm-handle-type handle))))
+ (setq handle (list handle)))
+ (when header
+ (article-decode-encoded-words)
+ (let ((gnus-visible-headers
+ (or (get 'gnus-visible-headers 'standard-value)
+ gnus-visible-headers)))
+ (article-hide-headers))
+ (goto-char (point-min))
+ (search-forward "\n\n" nil 'move)
+ (skip-chars-backward "\t\n ")
+ (setq header (buffer-substring (point-min) (point)))))
+ (when (prog1
+ (gnus-article-browse-html-parts handle header)
+ (mm-destroy-parts handle))
+ (setq showed t)))))
showed))
-;; FIXME: Documentation in texi/gnus.texi missing.
-(defun gnus-article-browse-html-article ()
+(defun gnus-article-browse-html-article (&optional arg)
"View \"text/html\" parts of the current article with a WWW browser.
+The message header is added to the beginning of every html part unless
+the prefix argument ARG is given.
Warning: Spammers use links to images in HTML articles to verify
whether you have read the message. As
-`gnus-article-browse-html-article' passes the unmodified HTML
-content to the browser without eliminating these \"web bugs\" you
-should only use it for mails from trusted senders."
+`gnus-article-browse-html-article' passes the HTML content to the
+browser without eliminating these \"web bugs\" you should only
+use it for mails from trusted senders.
+
+If you always want to display HTML parts in the browser, set
+`mm-text-html-renderer' to nil."
;; Cf. `mm-w3m-safe-url-regexp'
- (interactive)
- (save-window-excursion
- ;; Open raw article and select the buffer
- (gnus-summary-show-article t)
- (gnus-summary-select-article-buffer)
- (let ((parts (mm-dissect-buffer t t)))
+ (interactive "P")
+ (if arg
+ (gnus-summary-show-article)
+ (let ((gnus-visible-headers (or (get 'gnus-visible-headers 'standard-value)
+ gnus-visible-headers))
+ ;; As we insert a <hr>, there's no need for the body boundary.
+ (gnus-treat-body-boundary nil))
+ (gnus-summary-show-article)))
+ (with-current-buffer gnus-article-buffer
+ (let ((header (unless arg
+ (save-restriction
+ (widen)
+ (buffer-substring-no-properties
+ (goto-char (point-min))
+ (if (search-forward "\n\n" nil t)
+ (match-beginning 0)
+ (goto-char (point-max))
+ (skip-chars-backward "\t\n ")
+ (point))))))
+ parts)
+ (set-buffer gnus-original-article-buffer)
+ (setq parts (mm-dissect-buffer t t))
;; If singlepart, enforce a list.
(when (and (bufferp (car parts))
(stringp (car (mm-handle-type parts))))
(setq parts (list parts)))
;; Process the list
- (unless (gnus-article-browse-html-parts parts)
+ (unless (gnus-article-browse-html-parts parts header)
(gnus-error 3 "Mail doesn't contain a \"text/html\" part!"))
- (gnus-summary-show-article))))
+ (mm-destroy-parts parts)
+ (unless arg
+ (gnus-summary-show-article)))))
(defun article-hide-list-identifiers ()
"Remove list identifies from the Subject header.
gnus-newsgroup-name 'highlight-words t)))
gnus-emphasis-alist)))))
-(eval-when-compile
- (defvar gnus-summary-article-menu)
- (defvar gnus-summary-post-menu))
+(defvar gnus-summary-article-menu)
+(defvar gnus-summary-post-menu)
;;; Saving functions.
(or (symbol-value (get gnus-default-article-saver :headers))
gnus-saved-headers gnus-visible-headers))
(gnus-article-buffer save-buffer))
- (save-excursion
- (set-buffer save-buffer)
+ (with-current-buffer save-buffer
(article-hide-headers 1 t))))
(save-window-excursion
(if (not gnus-default-article-saver)
(defun article-verify-x-pgp-sig ()
"Verify X-PGP-Sig."
+ ;; <ftp://ftp.isc.org/pub/pgpcontrol/FORMAT>
(interactive)
(if (gnus-buffer-live-p gnus-original-article-buffer)
(let ((sig (with-current-buffer gnus-original-article-buffer
(canlock-verify gnus-original-article-buffer)))
(eval-and-compile
- (mapcar
+ (mapc
(lambda (func)
(let (afunc gfunc)
(if (consp func)
`(lambda (&optional interactive &rest args)
,(documentation afunc t)
(interactive (list t))
- (save-excursion
- (set-buffer gnus-article-buffer)
+ (with-current-buffer gnus-article-buffer
(if interactive
(call-interactively ',afunc)
(apply ',afunc args))))))))
article-emphasize
article-treat-dumbquotes
article-normalize-headers
-;; (article-show-all . gnus-article-show-all-headers)
+ ;;(article-show-all . gnus-article-show-all-headers)
)))
\f
;;;
"F" gnus-article-followup-with-original
"\C-hk" gnus-article-describe-key
"\C-hc" gnus-article-describe-key-briefly
+ "\C-hb" gnus-article-describe-bindings
"\C-d" gnus-article-read-summary-keys
"\M-*" gnus-article-read-summary-keys
(substitute-key-definition
'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
+(gnus-define-keys (gnus-article-send-map "S" gnus-article-mode-map)
+ "W" gnus-article-wide-reply-with-original)
+(if (featurep 'xemacs)
+ (set-keymap-default-binding gnus-article-send-map
+ 'gnus-article-read-summary-send-keys)
+ (define-key gnus-article-send-map [t] 'gnus-article-read-summary-send-keys))
+
(defun gnus-article-make-menu-bar ()
(unless (boundp 'gnus-article-commands-menu)
(gnus-summary-make-menu-bar))
(gnus-set-global-variables)))
(gnus-article-setup-highlight-words)
;; Init original article buffer.
- (save-excursion
- (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
+ (with-current-buffer (gnus-get-buffer-create gnus-original-article-buffer)
(mm-enable-multibyte)
(setq major-mode 'gnus-original-article-mode)
(make-local-variable 'gnus-original-article))
nil)
(error "Action aborted"))
t)))
- (save-excursion
- (set-buffer name)
+ (with-current-buffer name
(set (make-local-variable 'gnus-article-edit-mode) nil)
(when gnus-article-mime-handles
(mm-destroy-parts gnus-article-mime-handles)
(unless (eq major-mode 'gnus-article-mode)
(gnus-article-mode))
(current-buffer))
- (save-excursion
- (set-buffer (gnus-get-buffer-create name))
+ (with-current-buffer (gnus-get-buffer-create name)
(gnus-article-mode)
(make-local-variable 'gnus-summary-buffer)
(setq gnus-summary-buffer
(when article-window
(set-window-start
article-window
- (save-excursion
- (set-buffer gnus-article-buffer)
+ (with-current-buffer gnus-article-buffer
(goto-char (point-min))
(if (not line)
(point-min)
(if (or (eq result 'pseudo)
(eq result 'nneething))
(progn
- (save-excursion
- (set-buffer summary-buffer)
+ (with-current-buffer summary-buffer
(push article gnus-newsgroup-history)
(setq gnus-last-article gnus-current-article
gnus-current-article 0
(not (eq article gnus-current-article)))
;; Seems like a new article has been selected.
;; `gnus-current-article' must be an article number.
- (save-excursion
- (set-buffer summary-buffer)
+ (with-current-buffer summary-buffer
(push article gnus-newsgroup-history)
(setq gnus-last-article gnus-current-article
gnus-current-article article
(funcall gnus-display-mime-function))
(gnus-run-hooks 'gnus-article-prepare-hook)))
+;;;
+;;; Gnus Sticky Article Mode
+;;;
+
+(define-derived-mode gnus-sticky-article-mode gnus-article-mode "StickyArticle"
+ "Mode for sticky articles."
+ ;; Release bindings that won't work.
+ (substitute-key-definition 'gnus-article-read-summary-keys 'undefined
+ gnus-sticky-article-mode-map)
+ (substitute-key-definition 'gnus-article-refer-article 'undefined
+ gnus-sticky-article-mode-map)
+ (dolist (k '("e" "h" "s" "F" "R"))
+ (define-key gnus-sticky-article-mode-map k nil))
+ (define-key gnus-sticky-article-mode-map "k" 'gnus-kill-sticky-article-buffer)
+ (define-key gnus-sticky-article-mode-map "q" 'bury-buffer)
+ (define-key gnus-sticky-article-mode-map "\C-hc" 'describe-key-briefly)
+ (define-key gnus-sticky-article-mode-map "\C-hk" 'describe-key))
+
+(defun gnus-sticky-article (arg)
+ "Make the current article sticky.
+If a prefix ARG is given, ask for a name for this sticky article buffer."
+ (interactive "P")
+ (gnus-summary-show-thread)
+ (gnus-summary-select-article nil nil 'pseudo)
+ (let (new-art-buf-name)
+ (gnus-eval-in-buffer-window gnus-article-buffer
+ (setq new-art-buf-name
+ (concat
+ "*Sticky Article: "
+ (if arg
+ (read-from-minibuffer "Sticky article buffer name: ")
+ (gnus-with-article-headers
+ (gnus-article-goto-header "subject")
+ (setq new-art-buf-name
+ (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position)))
+ (goto-char (point-min))
+ (gnus-article-goto-header "from")
+ (setq new-art-buf-name
+ (concat
+ new-art-buf-name ", "
+ (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))))
+ (goto-char (point-min))
+ (gnus-article-goto-header "date")
+ (setq new-art-buf-name
+ (concat
+ new-art-buf-name ", "
+ (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position))))))
+ "*"))
+ (if (and (gnus-buffer-live-p new-art-buf-name)
+ (with-current-buffer new-art-buf-name
+ (eq major-mode 'gnus-sticky-article-mode)))
+ (switch-to-buffer new-art-buf-name)
+ (setq new-art-buf-name (rename-buffer new-art-buf-name t)))
+ (gnus-sticky-article-mode))
+ (setq gnus-article-buffer new-art-buf-name))
+ (gnus-summary-recenter)
+ (gnus-summary-position-point))
+
+(defun gnus-kill-sticky-article-buffer (&optional buffer)
+ "Kill the given sticky article BUFFER.
+If none is given, assume the current buffer and kill it if it has
+`gnus-sticky-article-mode'."
+ (interactive)
+ (unless buffer
+ (setq buffer (current-buffer)))
+ (with-current-buffer buffer
+ (when (eq major-mode 'gnus-sticky-article-mode)
+ (gnus-kill-buffer buffer))))
+
+(defun gnus-kill-sticky-article-buffers (arg)
+ "Kill all sticky article buffers.
+If a prefix ARG is given, ask for confirmation."
+ (interactive "P")
+ (dolist (buf (gnus-buffers))
+ (with-current-buffer buf
+ (when (eq major-mode 'gnus-sticky-article-mode)
+ (if (not arg)
+ (gnus-kill-buffer buf)
+ (when (yes-or-no-p (concat "Kill buffer " (buffer-name buf) "? "))
+ (gnus-kill-buffer buf)))))))
+
;;;
;;; Gnus MIME viewing functions
;;;
(gnus-summary-show-article)
(when (and current-id (integerp gnus-auto-select-part))
(gnus-article-jump-to-part
- (+ current-id gnus-auto-select-part)))))
+ (if (text-property-any (point-min) (point-max)
+ 'gnus-part (+ current-id gnus-auto-select-part))
+ (+ current-id gnus-auto-select-part)
+ (with-current-buffer gnus-article-buffer
+ (length gnus-article-mime-handle-alist)))))))
(defun gnus-mime-replace-part (file)
"Replace MIME part under point with an external body."
;; Useful if file has already been saved to disk
(interactive
(list
- (mm-with-multibyte
- (read-file-name "Replace MIME part with file: "
- (or mm-default-directory default-directory)
- nil nil))))
+ (read-file-name "Replace MIME part with file: "
+ (or mm-default-directory default-directory)
+ nil nil)))
(gnus-mime-save-part-and-strip file))
(defun gnus-mime-save-part-and-strip (&optional file)