]> code.delx.au - gnu-emacs/blob - lisp/gnus/gnus-art.el
Revision: miles@gnu.org--gnu-2005/emacs--cvs-trunk--0--patch-564
[gnu-emacs] / lisp / gnus / gnus-art.el
1 ;;; gnus-art.el --- article mode commands for Gnus
2
3 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;; 2005 Free Software Foundation, Inc.
5
6 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;; Keywords: news
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25
26 ;;; Commentary:
27
28 ;;; Code:
29
30 (eval-when-compile
31 (require 'cl)
32 (defvar tool-bar-map)
33 (defvar w3m-minor-mode-map))
34
35 (require 'gnus)
36 (require 'gnus-sum)
37 (require 'gnus-spec)
38 (require 'gnus-int)
39 (require 'gnus-win)
40 (require 'mm-bodies)
41 (require 'mail-parse)
42 (require 'mm-decode)
43 (require 'mm-view)
44 (require 'wid-edit)
45 (require 'mm-uu)
46 (require 'message)
47
48 (autoload 'gnus-msg-mail "gnus-msg" nil t)
49 (autoload 'gnus-button-mailto "gnus-msg")
50 (autoload 'gnus-button-reply "gnus-msg" nil t)
51 (autoload 'parse-time-string "parse-time" nil nil)
52
53 (defgroup gnus-article nil
54 "Article display."
55 :link '(custom-manual "(gnus)Article Buffer")
56 :group 'gnus)
57
58 (defgroup gnus-article-treat nil
59 "Treating article parts."
60 :link '(custom-manual "(gnus)Article Hiding")
61 :group 'gnus-article)
62
63 (defgroup gnus-article-hiding nil
64 "Hiding article parts."
65 :link '(custom-manual "(gnus)Article Hiding")
66 :group 'gnus-article)
67
68 (defgroup gnus-article-highlight nil
69 "Article highlighting."
70 :link '(custom-manual "(gnus)Article Highlighting")
71 :group 'gnus-article
72 :group 'gnus-visual)
73
74 (defgroup gnus-article-signature nil
75 "Article signatures."
76 :link '(custom-manual "(gnus)Article Signature")
77 :group 'gnus-article)
78
79 (defgroup gnus-article-headers nil
80 "Article headers."
81 :link '(custom-manual "(gnus)Hiding Headers")
82 :group 'gnus-article)
83
84 (defgroup gnus-article-washing nil
85 "Special commands on articles."
86 :link '(custom-manual "(gnus)Article Washing")
87 :group 'gnus-article)
88
89 (defgroup gnus-article-emphasis nil
90 "Fontisizing articles."
91 :link '(custom-manual "(gnus)Article Fontisizing")
92 :group 'gnus-article)
93
94 (defgroup gnus-article-saving nil
95 "Saving articles."
96 :link '(custom-manual "(gnus)Saving Articles")
97 :group 'gnus-article)
98
99 (defgroup gnus-article-mime nil
100 "Worshiping the MIME wonder."
101 :link '(custom-manual "(gnus)Using MIME")
102 :group 'gnus-article)
103
104 (defgroup gnus-article-buttons nil
105 "Pushable buttons in the article buffer."
106 :link '(custom-manual "(gnus)Article Buttons")
107 :group 'gnus-article)
108
109 (defgroup gnus-article-various nil
110 "Other article options."
111 :link '(custom-manual "(gnus)Misc Article")
112 :group 'gnus-article)
113
114 (defcustom gnus-ignored-headers
115 (mapcar
116 (lambda (header)
117 (concat "^" header ":"))
118 '("Path" "Expires" "Date-Received" "References" "Xref" "Lines"
119 "Relay-Version" "Message-ID" "Approved" "Sender" "Received"
120 "X-UIDL" "MIME-Version" "Return-Path" "In-Reply-To"
121 "Content-Type" "Content-Transfer-Encoding" "X-WebTV-Signature"
122 "X-MimeOLE" "X-MSMail-Priority" "X-Priority" "X-Loop"
123 "X-Authentication-Warning" "X-MIME-Autoconverted" "X-Face"
124 "X-Attribution" "X-Originating-IP" "Delivered-To"
125 "NNTP-[-A-Za-z]+" "Distribution" "X-no-archive" "X-Trace"
126 "X-Complaints-To" "X-NNTP-Posting-Host" "X-Orig.*"
127 "Abuse-Reports-To" "Cache-Post-Path" "X-Article-Creation-Date"
128 "X-Poster" "X-Mail2News-Path" "X-Server-Date" "X-Cache"
129 "Originator" "X-Problems-To" "X-Auth-User" "X-Post-Time"
130 "X-Admin" "X-UID" "Resent-[-A-Za-z]+" "X-Mailing-List"
131 "Precedence" "Original-[-A-Za-z]+" "X-filename" "X-Orcpt"
132 "Old-Received" "X-Pgp" "X-Auth" "X-From-Line"
133 "X-Gnus-Article-Number" "X-Majordomo" "X-Url" "X-Sender"
134 "MBOX-Line" "Priority" "X400-[-A-Za-z]+"
135 "Status" "X-Gnus-Mail-Source" "Cancel-Lock"
136 "X-FTN" "X-EXP32-SerialNo" "Encoding" "Importance"
137 "Autoforwarded" "Original-Encoded-Information-Types" "X-Ya-Pop3"
138 "X-Face-Version" "X-Vms-To" "X-ML-NAME" "X-ML-COUNT"
139 "Mailing-List" "X-finfo" "X-md5sum" "X-md5sum-Origin"
140 "X-Sun-Charset" "X-Accept-Language" "X-Envelope-Sender"
141 "List-[A-Za-z]+" "X-Listprocessor-Version"
142 "X-Received" "X-Distribute" "X-Sequence" "X-Juno-Line-Breaks"
143 "X-Notes-Item" "X-MS-TNEF-Correlator" "x-uunet-gateway"
144 "X-Received" "Content-length" "X-precedence"
145 "X-Authenticated-User" "X-Comment" "X-Report" "X-Abuse-Info"
146 "X-HTTP-Proxy" "X-Mydeja-Info" "X-Copyright" "X-No-Markup"
147 "X-Abuse-Info" "X-From_" "X-Accept-Language" "Errors-To"
148 "X-BeenThere" "X-Mailman-Version" "List-Help" "List-Post"
149 "List-Subscribe" "List-Id" "List-Unsubscribe" "List-Archive"
150 "X-Content-length" "X-Posting-Agent" "Original-Received"
151 "X-Request-PGP" "X-Fingerprint" "X-WRIEnvto" "X-WRIEnvfrom"
152 "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
153 "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
154 "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
155 "X-Abuse-and-DMCA-Info" "X-Postfilter" "X-Gpg-.*" "X-Disclaimer"))
156 "*All headers that start with this regexp will be hidden.
157 This variable can also be a list of regexps of headers to be ignored.
158 If `gnus-visible-headers' is non-nil, this variable will be ignored."
159 :type '(choice :custom-show nil
160 regexp
161 (repeat regexp))
162 :group 'gnus-article-hiding)
163
164 (defcustom gnus-visible-headers
165 "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^[BGF]?Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Mail-Followup-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-From:\\|^X-Sent:"
166 "*All headers that do not match this regexp will be hidden.
167 This variable can also be a list of regexp of headers to remain visible.
168 If this variable is non-nil, `gnus-ignored-headers' will be ignored."
169 :type '(repeat :value-to-internal (lambda (widget value)
170 (custom-split-regexp-maybe value))
171 :match (lambda (widget value)
172 (or (stringp value)
173 (widget-editable-list-match widget value)))
174 regexp)
175 :group 'gnus-article-hiding)
176
177 (defcustom gnus-sorted-header-list
178 '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:"
179 "^Followup-To:" "^To:" "^Cc:" "^Date:" "^Organization:")
180 "*This variable is a list of regular expressions.
181 If it is non-nil, headers that match the regular expressions will
182 be placed first in the article buffer in the sequence specified by
183 this list."
184 :type '(repeat regexp)
185 :group 'gnus-article-hiding)
186
187 (defcustom gnus-boring-article-headers '(empty followup-to reply-to)
188 "Headers that are only to be displayed if they have interesting data.
189 Possible values in this list are:
190
191 'empty Headers with no content.
192 'newsgroups Newsgroup identical to Gnus group.
193 'to-address To identical to To-address.
194 'to-list To identical to To-list.
195 'cc-list CC identical to To-list.
196 'followup-to Followup-to identical to Newsgroups.
197 'reply-to Reply-to identical to From.
198 'date Date less than four days old.
199 'long-to To and/or Cc longer than 1024 characters.
200 'many-to Multiple To and/or Cc."
201 :type '(set (const :tag "Headers with no content." empty)
202 (const :tag "Newsgroups identical to Gnus group." newsgroups)
203 (const :tag "To identical to To-address." to-address)
204 (const :tag "To identical to To-list." to-list)
205 (const :tag "CC identical to To-list." cc-list)
206 (const :tag "Followup-to identical to Newsgroups." followup-to)
207 (const :tag "Reply-to identical to From." reply-to)
208 (const :tag "Date less than four days old." date)
209 (const :tag "To and/or Cc longer than 1024 characters." long-to)
210 (const :tag "Multiple To and/or Cc headers." many-to))
211 :group 'gnus-article-hiding)
212
213 (defcustom gnus-article-skip-boring nil
214 "Skip over text that is not worth reading.
215 By default, if you set this t, then Gnus will display citations and
216 signatures, but will never scroll down to show you a page consisting
217 only of boring text. Boring text is controlled by
218 `gnus-article-boring-faces'."
219 :version "22.1"
220 :type 'boolean
221 :group 'gnus-article-hiding)
222
223 (defcustom gnus-signature-separator '("^-- $" "^-- *$")
224 "Regexp matching signature separator.
225 This can also be a list of regexps. In that case, it will be checked
226 from head to tail looking for a separator. Searches will be done from
227 the end of the buffer."
228 :type '(repeat string)
229 :group 'gnus-article-signature)
230
231 (defcustom gnus-signature-limit nil
232 "Provide a limit to what is considered a signature.
233 If it is a number, no signature may not be longer (in characters) than
234 that number. If it is a floating point number, no signature may be
235 longer (in lines) than that number. If it is a function, the function
236 will be called without any parameters, and if it returns nil, there is
237 no signature in the buffer. If it is a string, it will be used as a
238 regexp. If it matches, the text in question is not a signature."
239 :type '(choice (const nil)
240 (integer :value 200)
241 (number :value 4.0)
242 (function :value fun)
243 (regexp :value ".*"))
244 :group 'gnus-article-signature)
245
246 (defcustom gnus-hidden-properties '(invisible t intangible t)
247 "Property list to use for hiding text."
248 :type 'sexp
249 :group 'gnus-article-hiding)
250
251 ;; Fixme: This isn't the right thing for mixed graphical and non-graphical
252 ;; frames in a session.
253 (defcustom gnus-article-x-face-command
254 (if (featurep 'xemacs)
255 (if (or (gnus-image-type-available-p 'xface)
256 (gnus-image-type-available-p 'pbm))
257 'gnus-display-x-face-in-from
258 "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | ee -")
259 (if (gnus-image-type-available-p 'pbm)
260 'gnus-display-x-face-in-from
261 "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | \
262 display -"))
263 "*String or function to be executed to display an X-Face header.
264 If it is a string, the command will be executed in a sub-shell
265 asynchronously. The compressed face will be piped to this command."
266 :type `(choice string
267 (function-item gnus-display-x-face-in-from)
268 function)
269 :version "21.1"
270 :group 'gnus-picon
271 :group 'gnus-article-washing)
272
273 (defcustom gnus-article-x-face-too-ugly nil
274 "Regexp matching posters whose face shouldn't be shown automatically."
275 :type '(choice regexp (const nil))
276 :group 'gnus-article-washing)
277
278 (defcustom gnus-article-banner-alist nil
279 "Banner alist for stripping.
280 For example,
281 ((egroups . \"^[ \\t\\n]*-------------------+\\\\( \\\\(e\\\\|Yahoo! \\\\)Groups Sponsor -+\\\\)?....\\n\\\\(.+\\n\\\\)+\"))"
282 :version "21.1"
283 :type '(repeat (cons symbol regexp))
284 :group 'gnus-article-washing)
285
286 (gnus-define-group-parameter
287 banner
288 :variable-document
289 "Alist of regexps (to match group names) and banner."
290 :variable-group gnus-article-washing
291 :parameter-type
292 '(choice :tag "Banner"
293 :value nil
294 (const :tag "Remove signature" signature)
295 (symbol :tag "Item in `gnus-article-banner-alist'" none)
296 regexp
297 (const :tag "None" nil))
298 :parameter-document
299 "If non-nil, specify how to remove `banners' from articles.
300
301 Symbol `signature' means to remove signatures delimited by
302 `gnus-signature-separator'. Any other symbol is used to look up a
303 regular expression to match the banner in `gnus-article-banner-alist'.
304 A string is used as a regular expression to match the banner
305 directly.")
306
307 (defcustom gnus-article-address-banner-alist nil
308 "Alist of mail addresses and banners.
309 Each element has the form (ADDRESS . BANNER), where ADDRESS is a regexp
310 to match a mail address in the From: header, BANNER is one of a symbol
311 `signature', an item in `gnus-article-banner-alist', a regexp and nil.
312 If ADDRESS matches author's mail address, it will remove things like
313 advertisements. For example:
314
315 \((\"@yoo-hoo\\\\.co\\\\.jp\\\\'\" . \"\\n_+\\nDo You Yoo-hoo!\\\\?\\n.*\\n.*\\n\"))
316 "
317 :type '(repeat
318 (cons
319 (regexp :tag "Address")
320 (choice :tag "Banner" :value nil
321 (const :tag "Remove signature" signature)
322 (symbol :tag "Item in `gnus-article-banner-alist'" none)
323 regexp
324 (const :tag "None" nil))))
325 :version "22.1"
326 :group 'gnus-article-washing)
327
328 (defmacro gnus-emphasis-custom-with-format (&rest body)
329 `(let ((format "\
330 \\(\\s-\\|^\\|\\=\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\
331 \\(\\([-,.;:!?\"]\\|\\s)\\)+\\s-\\|[?!.]\\s-\\|\\s)\\|\\s-\\)"))
332 ,@body))
333
334 (defun gnus-emphasis-custom-value-to-external (value)
335 (gnus-emphasis-custom-with-format
336 (if (consp (car value))
337 (list (format format (car (car value)) (cdr (car value)))
338 2
339 (if (nth 1 value) 2 3)
340 (nth 2 value))
341 value)))
342
343 (defun gnus-emphasis-custom-value-to-internal (value)
344 (gnus-emphasis-custom-with-format
345 (let ((regexp (concat "\\`"
346 (format (regexp-quote format)
347 "\\([^()]+\\)" "\\([^()]+\\)")
348 "\\'"))
349 pattern)
350 (if (string-match regexp (setq pattern (car value)))
351 (list (cons (match-string 1 pattern) (match-string 2 pattern))
352 (= (nth 2 value) 2)
353 (nth 3 value))
354 value))))
355
356 (defcustom gnus-emphasis-alist
357 (let ((types
358 '(("\\*" "\\*" bold nil 2)
359 ("_" "_" underline)
360 ("/" "/" italic)
361 ("_/" "/_" underline-italic)
362 ("_\\*" "\\*_" underline-bold)
363 ("\\*/" "/\\*" bold-italic)
364 ("_\\*/" "/\\*_" underline-bold-italic))))
365 (nconc
366 (gnus-emphasis-custom-with-format
367 (mapcar (lambda (spec)
368 (list (format format (car spec) (cadr spec))
369 (or (nth 3 spec) 2)
370 (or (nth 4 spec) 3)
371 (intern (format "gnus-emphasis-%s" (nth 2 spec)))))
372 types))
373 '(;; I've never seen anyone use this strikethru convention whereas I've
374 ;; several times seen it triggered by normal text. --Stef
375 ;; Miles suggests that this form is sometimes used but for italics,
376 ;; so maybe we should map it to `italic'.
377 ;; ("\\(\\s-\\|^\\)\\(-\\(\\(\\w\\|-[^-]\\)+\\)-\\)\\(\\s-\\|[?!.,;]\\)"
378 ;; 2 3 gnus-emphasis-strikethru)
379 ("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
380 2 3 gnus-emphasis-underline))))
381 "*Alist that says how to fontify certain phrases.
382 Each item looks like this:
383
384 (\"_\\\\(\\\\w+\\\\)_\" 0 1 'underline)
385
386 The first element is a regular expression to be matched. The second
387 is a number that says what regular expression grouping used to find
388 the entire emphasized word. The third is a number that says what
389 regexp grouping should be displayed and highlighted. The fourth
390 is the face used for highlighting."
391 :type
392 '(repeat
393 (menu-choice
394 :format "%[Customizing Style%]\n%v"
395 :indent 2
396 (group :tag "Default"
397 :value ("" 0 0 default)
398 :value-create
399 (lambda (widget)
400 (let ((value (widget-get
401 (cadr (widget-get (widget-get widget :parent)
402 :args))
403 :value)))
404 (if (not (eq (nth 2 value) 'default))
405 (widget-put
406 widget
407 :value
408 (gnus-emphasis-custom-value-to-external value))))
409 (widget-group-value-create widget))
410 regexp
411 (integer :format "Match group: %v")
412 (integer :format "Emphasize group: %v")
413 face)
414 (group :tag "Simple"
415 :value (("_" . "_") nil default)
416 (cons :format "%v"
417 (regexp :format "Start regexp: %v")
418 (regexp :format "End regexp: %v"))
419 (boolean :format "Show start and end patterns: %[%v%]\n"
420 :on " On " :off " Off ")
421 face)))
422 :get (lambda (symbol)
423 (mapcar 'gnus-emphasis-custom-value-to-internal
424 (default-value symbol)))
425 :set (lambda (symbol value)
426 (set-default symbol (mapcar 'gnus-emphasis-custom-value-to-external
427 value)))
428 :group 'gnus-article-emphasis)
429
430 (defcustom gnus-emphasize-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
431 "A regexp to describe whitespace which should not be emphasized.
432 Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
433 The former avoids underlining of leading and trailing whitespace,
434 and the latter avoids underlining any whitespace at all."
435 :version "21.1"
436 :group 'gnus-article-emphasis
437 :type 'regexp)
438
439 (defface gnus-emphasis-bold '((t (:bold t)))
440 "Face used for displaying strong emphasized text (*word*)."
441 :group 'gnus-article-emphasis)
442
443 (defface gnus-emphasis-italic '((t (:italic t)))
444 "Face used for displaying italic emphasized text (/word/)."
445 :group 'gnus-article-emphasis)
446
447 (defface gnus-emphasis-underline '((t (:underline t)))
448 "Face used for displaying underlined emphasized text (_word_)."
449 :group 'gnus-article-emphasis)
450
451 (defface gnus-emphasis-underline-bold '((t (:bold t :underline t)))
452 "Face used for displaying underlined bold emphasized text (_*word*_)."
453 :group 'gnus-article-emphasis)
454
455 (defface gnus-emphasis-underline-italic '((t (:italic t :underline t)))
456 "Face used for displaying underlined italic emphasized text (_/word/_)."
457 :group 'gnus-article-emphasis)
458
459 (defface gnus-emphasis-bold-italic '((t (:bold t :italic t)))
460 "Face used for displaying bold italic emphasized text (/*word*/)."
461 :group 'gnus-article-emphasis)
462
463 (defface gnus-emphasis-underline-bold-italic
464 '((t (:bold t :italic t :underline t)))
465 "Face used for displaying underlined bold italic emphasized text.
466 Example: (_/*word*/_)."
467 :group 'gnus-article-emphasis)
468
469 (defface gnus-emphasis-strikethru (if (featurep 'xemacs)
470 '((t (:strikethru t)))
471 '((t (:strike-through t))))
472 "Face used for displaying strike-through text (-word-)."
473 :group 'gnus-article-emphasis)
474
475 (defface gnus-emphasis-highlight-words
476 '((t (:background "black" :foreground "yellow")))
477 "Face used for displaying highlighted words."
478 :group 'gnus-article-emphasis)
479
480 (defcustom gnus-article-time-format "%a, %b %d %Y %T %Z"
481 "Format for display of Date headers in article bodies.
482 See `format-time-string' for the possible values.
483
484 The variable can also be function, which should return a complete Date
485 header. The function is called with one argument, the time, which can
486 be fed to `format-time-string'."
487 :type '(choice string symbol)
488 :link '(custom-manual "(gnus)Article Date")
489 :group 'gnus-article-washing)
490
491 (defcustom gnus-save-all-headers t
492 "*If non-nil, don't remove any headers before saving."
493 :group 'gnus-article-saving
494 :type 'boolean)
495
496 (defcustom gnus-prompt-before-saving 'always
497 "*This variable says how much prompting is to be done when saving articles.
498 If it is nil, no prompting will be done, and the articles will be
499 saved to the default files. If this variable is `always', each and
500 every article that is saved will be preceded by a prompt, even when
501 saving large batches of articles. If this variable is neither nil not
502 `always', there the user will be prompted once for a file name for
503 each invocation of the saving commands."
504 :group 'gnus-article-saving
505 :type '(choice (item always)
506 (item :tag "never" nil)
507 (sexp :tag "once" :format "%t\n" :value t)))
508
509 (defcustom gnus-saved-headers gnus-visible-headers
510 "Headers to keep if `gnus-save-all-headers' is nil.
511 If `gnus-save-all-headers' is non-nil, this variable will be ignored.
512 If that variable is nil, however, all headers that match this regexp
513 will be kept while the rest will be deleted before saving."
514 :group 'gnus-article-saving
515 :type 'regexp)
516
517 (defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
518 "A function to save articles in your favourite format.
519 The function must be interactively callable (in other words, it must
520 be an Emacs command).
521
522 Gnus provides the following functions:
523
524 * gnus-summary-save-in-rmail (Rmail format)
525 * gnus-summary-save-in-mail (Unix mail format)
526 * gnus-summary-save-in-folder (MH folder)
527 * gnus-summary-save-in-file (article format)
528 * gnus-summary-save-body-in-file (article body)
529 * gnus-summary-save-in-vm (use VM's folder format)
530 * gnus-summary-write-to-file (article format -- overwrite)."
531 :group 'gnus-article-saving
532 :type '(radio (function-item gnus-summary-save-in-rmail)
533 (function-item gnus-summary-save-in-mail)
534 (function-item gnus-summary-save-in-folder)
535 (function-item gnus-summary-save-in-file)
536 (function-item gnus-summary-save-body-in-file)
537 (function-item gnus-summary-save-in-vm)
538 (function-item gnus-summary-write-to-file)))
539
540 (defcustom gnus-rmail-save-name 'gnus-plain-save-name
541 "A function generating a file name to save articles in Rmail format.
542 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
543 :group 'gnus-article-saving
544 :type 'function)
545
546 (defcustom gnus-mail-save-name 'gnus-plain-save-name
547 "A function generating a file name to save articles in Unix mail format.
548 The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
549 :group 'gnus-article-saving
550 :type 'function)
551
552 (defcustom gnus-folder-save-name 'gnus-folder-save-name
553 "A function generating a file name to save articles in MH folder.
554 The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER."
555 :group 'gnus-article-saving
556 :type 'function)
557
558 (defcustom gnus-file-save-name 'gnus-numeric-save-name
559 "A function generating a file name to save articles in article format.
560 The function is called with NEWSGROUP, HEADERS, and optional
561 LAST-FILE."
562 :group 'gnus-article-saving
563 :type 'function)
564
565 (defcustom gnus-split-methods
566 '((gnus-article-archive-name)
567 (gnus-article-nndoc-name))
568 "*Variable used to suggest where articles are to be saved.
569 For instance, if you would like to save articles related to Gnus in
570 the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
571 you could set this variable to something like:
572
573 '((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
574 (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
575
576 This variable is an alist where the where the key is the match and the
577 value is a list of possible files to save in if the match is non-nil.
578
579 If the match is a string, it is used as a regexp match on the
580 article. If the match is a symbol, that symbol will be funcalled
581 from the buffer of the article to be saved with the newsgroup as the
582 parameter. If it is a list, it will be evaled in the same buffer.
583
584 If this form or function returns a string, this string will be used as
585 a possible file name; and if it returns a non-nil list, that list will
586 be used as possible file names."
587 :group 'gnus-article-saving
588 :type '(repeat (choice (list :value (fun) function)
589 (cons :value ("" "") regexp (repeat string))
590 (sexp :value nil))))
591
592 (defcustom gnus-page-delimiter "^\^L"
593 "*Regexp describing what to use as article page delimiters.
594 The default value is \"^\^L\", which is a form linefeed at the
595 beginning of a line."
596 :type 'regexp
597 :group 'gnus-article-various)
598
599 (defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m"
600 "*The format specification for the article mode line.
601 See `gnus-summary-mode-line-format' for a closer description.
602
603 The following additional specs are available:
604
605 %w The article washing status.
606 %m The number of MIME parts in the article."
607 :type 'string
608 :group 'gnus-article-various)
609
610 (defcustom gnus-article-mode-hook nil
611 "*A hook for Gnus article mode."
612 :type 'hook
613 :group 'gnus-article-various)
614
615 (when (featurep 'xemacs)
616 ;; Extracted from gnus-xmas-define in order to preserve user settings
617 (when (fboundp 'turn-off-scroll-in-place)
618 (add-hook 'gnus-article-mode-hook 'turn-off-scroll-in-place))
619 ;; Extracted from gnus-xmas-redefine in order to preserve user settings
620 (add-hook 'gnus-article-mode-hook 'gnus-xmas-article-menu-add))
621
622 (defcustom gnus-article-menu-hook nil
623 "*Hook run after the creation of the article mode menu."
624 :type 'hook
625 :group 'gnus-article-various)
626
627 (defcustom gnus-article-prepare-hook nil
628 "*A hook called after an article has been prepared in the article buffer."
629 :type 'hook
630 :group 'gnus-article-various)
631
632 (make-obsolete-variable 'gnus-article-hide-pgp-hook
633 "This variable is obsolete in Gnus 5.10.")
634
635 (defcustom gnus-article-button-face 'bold
636 "Face used for highlighting buttons in the article buffer.
637
638 An article button is a piece of text that you can activate by pressing
639 `RET' or `mouse-2' above it."
640 :type 'face
641 :group 'gnus-article-buttons)
642
643 (defcustom gnus-article-mouse-face 'highlight
644 "Face used for mouse highlighting in the article buffer.
645
646 Article buttons will be displayed in this face when the cursor is
647 above them."
648 :type 'face
649 :group 'gnus-article-buttons)
650
651 (defcustom gnus-signature-face 'gnus-signature
652 "Face used for highlighting a signature in the article buffer.
653 Obsolete; use the face `gnus-signature' for customizations instead."
654 :type 'face
655 :group 'gnus-article-highlight
656 :group 'gnus-article-signature)
657
658 (defface gnus-signature
659 '((t
660 (:italic t)))
661 "Face used for highlighting a signature in the article buffer."
662 :group 'gnus-article-highlight
663 :group 'gnus-article-signature)
664 ;; backward-compatibility alias
665 (put 'gnus-signature-face 'face-alias 'gnus-signature)
666
667 (defface gnus-header-from
668 '((((class color)
669 (background dark))
670 (:foreground "spring green"))
671 (((class color)
672 (background light))
673 (:foreground "red3"))
674 (t
675 (:italic t)))
676 "Face used for displaying from headers."
677 :group 'gnus-article-headers
678 :group 'gnus-article-highlight)
679 ;; backward-compatibility alias
680 (put 'gnus-header-from-face 'face-alias 'gnus-header-from)
681
682 (defface gnus-header-subject
683 '((((class color)
684 (background dark))
685 (:foreground "SeaGreen3"))
686 (((class color)
687 (background light))
688 (:foreground "red4"))
689 (t
690 (:bold t :italic t)))
691 "Face used for displaying subject headers."
692 :group 'gnus-article-headers
693 :group 'gnus-article-highlight)
694 ;; backward-compatibility alias
695 (put 'gnus-header-subject-face 'face-alias 'gnus-header-subject)
696
697 (defface gnus-header-newsgroups
698 '((((class color)
699 (background dark))
700 (:foreground "yellow" :italic t))
701 (((class color)
702 (background light))
703 (:foreground "MidnightBlue" :italic t))
704 (t
705 (:italic t)))
706 "Face used for displaying newsgroups headers.
707 In the default setup this face is only used for crossposted
708 articles."
709 :group 'gnus-article-headers
710 :group 'gnus-article-highlight)
711 ;; backward-compatibility alias
712 (put 'gnus-header-newsgroups-face 'face-alias 'gnus-header-newsgroups)
713
714 (defface gnus-header-name
715 '((((class color)
716 (background dark))
717 (:foreground "SeaGreen"))
718 (((class color)
719 (background light))
720 (:foreground "maroon"))
721 (t
722 (:bold t)))
723 "Face used for displaying header names."
724 :group 'gnus-article-headers
725 :group 'gnus-article-highlight)
726 ;; backward-compatibility alias
727 (put 'gnus-header-name-face 'face-alias 'gnus-header-name)
728
729 (defface gnus-header-content
730 '((((class color)
731 (background dark))
732 (:foreground "forest green" :italic t))
733 (((class color)
734 (background light))
735 (:foreground "indianred4" :italic t))
736 (t
737 (:italic t))) "Face used for displaying header content."
738 :group 'gnus-article-headers
739 :group 'gnus-article-highlight)
740 ;; backward-compatibility alias
741 (put 'gnus-header-content-face 'face-alias 'gnus-header-content)
742
743 (defcustom gnus-header-face-alist
744 '(("From" nil gnus-header-from)
745 ("Subject" nil gnus-header-subject)
746 ("Newsgroups:.*," nil gnus-header-newsgroups)
747 ("" gnus-header-name gnus-header-content))
748 "*Controls highlighting of article headers.
749
750 An alist of the form (HEADER NAME CONTENT).
751
752 HEADER is a regular expression which should match the name of a
753 header and NAME and CONTENT are either face names or nil.
754
755 The name of each header field will be displayed using the face
756 specified by the first element in the list where HEADER matches
757 the header name and NAME is non-nil. Similarly, the content will
758 be displayed by the first non-nil matching CONTENT face."
759 :group 'gnus-article-headers
760 :group 'gnus-article-highlight
761 :type '(repeat (list (regexp :tag "Header")
762 (choice :tag "Name"
763 (item :tag "skip" nil)
764 (face :value default))
765 (choice :tag "Content"
766 (item :tag "skip" nil)
767 (face :value default)))))
768
769 (defcustom gnus-article-decode-hook
770 '(article-decode-charset article-decode-encoded-words
771 article-decode-group-name article-decode-idna-rhs)
772 "*Hook run to decode charsets in articles."
773 :group 'gnus-article-headers
774 :type 'hook)
775
776 (defcustom gnus-display-mime-function 'gnus-display-mime
777 "Function to display MIME articles."
778 :group 'gnus-article-mime
779 :type 'function)
780
781 (defvar gnus-decode-header-function 'mail-decode-encoded-word-region
782 "Function used to decode headers.")
783
784 (defvar gnus-article-dumbquotes-map
785 '(("\200" "EUR")
786 ("\202" ",")
787 ("\203" "f")
788 ("\204" ",,")
789 ("\205" "...")
790 ("\213" "<")
791 ("\214" "OE")
792 ("\221" "`")
793 ("\222" "'")
794 ("\223" "``")
795 ("\224" "\"")
796 ("\225" "*")
797 ("\226" "-")
798 ("\227" "--")
799 ("\230" "~")
800 ("\231" "(TM)")
801 ("\233" ">")
802 ("\234" "oe")
803 ("\264" "'"))
804 "Table for MS-to-Latin1 translation.")
805
806 (defcustom gnus-ignored-mime-types nil
807 "List of MIME types that should be ignored by Gnus."
808 :version "21.1"
809 :group 'gnus-article-mime
810 :type '(repeat regexp))
811
812 (defcustom gnus-unbuttonized-mime-types '(".*/.*")
813 "List of MIME types that should not be given buttons when rendered inline.
814 See also `gnus-buttonized-mime-types' which may override this variable.
815 This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
816 :version "21.1"
817 :group 'gnus-article-mime
818 :type '(repeat regexp))
819
820 (defcustom gnus-buttonized-mime-types nil
821 "List of MIME types that should be given buttons when rendered inline.
822 If set, this variable overrides `gnus-unbuttonized-mime-types'.
823 To see e.g. security buttons you could set this to
824 `(\"multipart/signed\")'.
825 This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
826 :version "22.1"
827 :group 'gnus-article-mime
828 :type '(repeat regexp))
829
830 (defcustom gnus-inhibit-mime-unbuttonizing nil
831 "If non-nil, all MIME parts get buttons.
832 When nil (the default value), then some MIME parts do not get buttons,
833 as described by the variables `gnus-buttonized-mime-types' and
834 `gnus-unbuttonized-mime-types'."
835 :version "22.1"
836 :group 'gnus-article-mime
837 :type 'boolean)
838
839 (defcustom gnus-body-boundary-delimiter "_"
840 "String used to delimit header and body.
841 This variable is used by `gnus-article-treat-body-boundary' which can
842 be controlled by `gnus-treat-body-boundary'."
843 :version "22.1"
844 :group 'gnus-article-various
845 :type '(choice (item :tag "None" :value nil)
846 string))
847
848 (defcustom gnus-picon-databases '("/usr/lib/picon" "/usr/local/faces"
849 "/usr/share/picons")
850 "Defines the location of the faces database.
851 For information on obtaining this database of pretty pictures, please
852 see http://www.cs.indiana.edu/picons/ftp/index.html"
853 :version "22.1"
854 :type '(repeat directory)
855 :link '(url-link :tag "download"
856 "http://www.cs.indiana.edu/picons/ftp/index.html")
857 :link '(custom-manual "(gnus)Picons")
858 :group 'gnus-picon)
859
860 (defun gnus-picons-installed-p ()
861 "Say whether picons are installed on your machine."
862 (let ((installed nil))
863 (dolist (database gnus-picon-databases)
864 (when (file-exists-p database)
865 (setq installed t)))
866 installed))
867
868 (defcustom gnus-article-mime-part-function nil
869 "Function called with a MIME handle as the argument.
870 This is meant for people who want to do something automatic based
871 on parts -- for instance, adding Vcard info to a database."
872 :group 'gnus-article-mime
873 :type '(choice (const nil)
874 function))
875
876 (defcustom gnus-mime-multipart-functions nil
877 "An alist of MIME types to functions to display them."
878 :version "21.1"
879 :group 'gnus-article-mime
880 :type 'alist)
881
882 (defcustom gnus-article-date-lapsed-new-header nil
883 "Whether the X-Sent and Date headers can coexist.
884 When using `gnus-treat-date-lapsed', the \"X-Sent:\" header will
885 either replace the old \"Date:\" header (if this variable is nil), or
886 be added below it (otherwise)."
887 :version "21.1"
888 :group 'gnus-article-headers
889 :type 'boolean)
890
891 (defcustom gnus-article-mime-match-handle-function 'undisplayed-alternative
892 "Function called with a MIME handle as the argument.
893 This is meant for people who want to view first matched part.
894 For `undisplayed-alternative' (default), the first undisplayed
895 part or alternative part is used. For `undisplayed', the first
896 undisplayed part is used. For a function, the first part which
897 the function return t is used. For nil, the first part is
898 used."
899 :version "21.1"
900 :group 'gnus-article-mime
901 :type '(choice
902 (item :tag "first" :value nil)
903 (item :tag "undisplayed" :value undisplayed)
904 (item :tag "undisplayed or alternative"
905 :value undisplayed-alternative)
906 (function)))
907
908 (defcustom gnus-mime-action-alist
909 '(("save to file" . gnus-mime-save-part)
910 ("save and strip" . gnus-mime-save-part-and-strip)
911 ("delete part" . gnus-mime-delete-part)
912 ("display as text" . gnus-mime-inline-part)
913 ("view the part" . gnus-mime-view-part)
914 ("pipe to command" . gnus-mime-pipe-part)
915 ("toggle display" . gnus-article-press-button)
916 ("toggle display" . gnus-article-view-part-as-charset)
917 ("view as type" . gnus-mime-view-part-as-type)
918 ("view internally" . gnus-mime-view-part-internally)
919 ("view externally" . gnus-mime-view-part-externally))
920 "An alist of actions that run on the MIME attachment."
921 :group 'gnus-article-mime
922 :type '(repeat (cons (string :tag "name")
923 (function))))
924
925 ;;;
926 ;;; The treatment variables
927 ;;;
928
929 (defvar gnus-part-display-hook nil
930 "Hook called on parts that are to receive treatment.")
931
932 (defvar gnus-article-treat-custom
933 '(choice (const :tag "Off" nil)
934 (const :tag "On" t)
935 (const :tag "Header" head)
936 (const :tag "Last" last)
937 (integer :tag "Less")
938 (repeat :tag "Groups" regexp)
939 (sexp :tag "Predicate")))
940
941 (defvar gnus-article-treat-head-custom
942 '(choice (const :tag "Off" nil)
943 (const :tag "Header" head)))
944
945 (defvar gnus-article-treat-types '("text/plain")
946 "Parts to treat.")
947
948 (defvar gnus-inhibit-treatment nil
949 "Whether to inhibit treatment.")
950
951 (defcustom gnus-treat-highlight-signature '(or t (typep "text/x-vcard"))
952 "Highlight the signature.
953 Valid values are nil, t, `head', `last', an integer or a predicate.
954 See Info node `(gnus)Customizing Articles'."
955 :group 'gnus-article-treat
956 :link '(custom-manual "(gnus)Customizing Articles")
957 :type gnus-article-treat-custom)
958 (put 'gnus-treat-highlight-signature 'highlight t)
959
960 (defcustom gnus-treat-buttonize 100000
961 "Add buttons.
962 Valid values are nil, t, `head', `last', an integer or a predicate.
963 See Info node `(gnus)Customizing Articles'."
964 :group 'gnus-article-treat
965 :link '(custom-manual "(gnus)Customizing Articles")
966 :type gnus-article-treat-custom)
967 (put 'gnus-treat-buttonize 'highlight t)
968
969 (defcustom gnus-treat-buttonize-head 'head
970 "Add buttons to the head.
971 Valid values are nil, t, `head', `last', an integer or a predicate.
972 See Info node `(gnus)Customizing Articles' for details."
973 :group 'gnus-article-treat
974 :link '(custom-manual "(gnus)Customizing Articles")
975 :type gnus-article-treat-head-custom)
976 (put 'gnus-treat-buttonize-head 'highlight t)
977
978 (defcustom gnus-treat-emphasize
979 (and (or window-system
980 (featurep 'xemacs)
981 (>= (string-to-number emacs-version) 21))
982 50000)
983 "Emphasize text.
984 Valid values are nil, t, `head', `last', an integer or a predicate.
985 See Info node `(gnus)Customizing Articles' for details."
986 :group 'gnus-article-treat
987 :link '(custom-manual "(gnus)Customizing Articles")
988 :type gnus-article-treat-custom)
989 (put 'gnus-treat-emphasize 'highlight t)
990
991 (defcustom gnus-treat-strip-cr nil
992 "Remove carriage returns.
993 Valid values are nil, t, `head', `last', an integer or a predicate.
994 See Info node `(gnus)Customizing Articles' for details."
995 :version "22.1"
996 :group 'gnus-article-treat
997 :link '(custom-manual "(gnus)Customizing Articles")
998 :type gnus-article-treat-custom)
999
1000 (defcustom gnus-treat-unsplit-urls nil
1001 "Remove newlines from within URLs.
1002 Valid values are nil, t, `head', `last', an integer or a predicate.
1003 See Info node `(gnus)Customizing Articles' for details."
1004 :version "22.1"
1005 :group 'gnus-article-treat
1006 :link '(custom-manual "(gnus)Customizing Articles")
1007 :type gnus-article-treat-custom)
1008
1009 (defcustom gnus-treat-leading-whitespace nil
1010 "Remove leading whitespace in headers.
1011 Valid values are nil, t, `head', `last', an integer or a predicate.
1012 See Info node `(gnus)Customizing Articles' for details."
1013 :version "22.1"
1014 :group 'gnus-article-treat
1015 :link '(custom-manual "(gnus)Customizing Articles")
1016 :type gnus-article-treat-custom)
1017
1018 (defcustom gnus-treat-hide-headers 'head
1019 "Hide headers.
1020 Valid values are nil, t, `head', `last', an integer or a predicate.
1021 See Info node `(gnus)Customizing Articles' for details."
1022 :group 'gnus-article-treat
1023 :link '(custom-manual "(gnus)Customizing Articles")
1024 :type gnus-article-treat-head-custom)
1025
1026 (defcustom gnus-treat-hide-boring-headers nil
1027 "Hide boring headers.
1028 Valid values are nil, t, `head', `last', an integer or a predicate.
1029 See Info node `(gnus)Customizing Articles' for details."
1030 :group 'gnus-article-treat
1031 :link '(custom-manual "(gnus)Customizing Articles")
1032 :type gnus-article-treat-head-custom)
1033
1034 (defcustom gnus-treat-hide-signature nil
1035 "Hide the signature.
1036 Valid values are nil, t, `head', `last', an integer or a predicate.
1037 See Info node `(gnus)Customizing Articles' for details."
1038 :group 'gnus-article-treat
1039 :link '(custom-manual "(gnus)Customizing Articles")
1040 :type gnus-article-treat-custom)
1041
1042 (defcustom gnus-treat-fill-article nil
1043 "Fill the article.
1044 Valid values are nil, t, `head', `last', an integer or a predicate.
1045 See Info node `(gnus)Customizing Articles' for details."
1046 :group 'gnus-article-treat
1047 :link '(custom-manual "(gnus)Customizing Articles")
1048 :type gnus-article-treat-custom)
1049
1050 (defcustom gnus-treat-hide-citation nil
1051 "Hide cited text.
1052 Valid values are nil, t, `head', `last', an integer or a predicate.
1053 See Info node `(gnus)Customizing Articles' for details."
1054 :group 'gnus-article-treat
1055 :link '(custom-manual "(gnus)Customizing Articles")
1056 :type gnus-article-treat-custom)
1057
1058 (defcustom gnus-treat-hide-citation-maybe nil
1059 "Hide cited text.
1060 Valid values are nil, t, `head', `last', an integer or a predicate.
1061 See Info node `(gnus)Customizing Articles' for details."
1062 :group 'gnus-article-treat
1063 :link '(custom-manual "(gnus)Customizing Articles")
1064 :type gnus-article-treat-custom)
1065
1066 (defcustom gnus-treat-strip-list-identifiers 'head
1067 "Strip list identifiers from `gnus-list-identifiers`.
1068 Valid values are nil, t, `head', `last', an integer or a predicate.
1069 See Info node `(gnus)Customizing Articles' for details."
1070 :version "21.1"
1071 :group 'gnus-article-treat
1072 :link '(custom-manual "(gnus)Customizing Articles")
1073 :type gnus-article-treat-custom)
1074
1075 (make-obsolete-variable 'gnus-treat-strip-pgp
1076 "This option is obsolete in Gnus 5.10.")
1077
1078 (defcustom gnus-treat-strip-pem nil
1079 "Strip PEM signatures.
1080 Valid values are nil, t, `head', `last', an integer or a predicate.
1081 See Info node `(gnus)Customizing Articles' for details."
1082 :group 'gnus-article-treat
1083 :link '(custom-manual "(gnus)Customizing Articles")
1084 :type gnus-article-treat-custom)
1085
1086 (defcustom gnus-treat-strip-banner t
1087 "Strip banners from articles.
1088 The banner to be stripped is specified in the `banner' group parameter.
1089 Valid values are nil, t, `head', `last', an integer or a predicate.
1090 See Info node `(gnus)Customizing Articles' for details."
1091 :group 'gnus-article-treat
1092 :link '(custom-manual "(gnus)Customizing Articles")
1093 :type gnus-article-treat-custom)
1094
1095 (defcustom gnus-treat-highlight-headers 'head
1096 "Highlight the headers.
1097 Valid values are nil, t, `head', `last', an integer or a predicate.
1098 See Info node `(gnus)Customizing Articles' for details."
1099 :group 'gnus-article-treat
1100 :link '(custom-manual "(gnus)Customizing Articles")
1101 :type gnus-article-treat-head-custom)
1102 (put 'gnus-treat-highlight-headers 'highlight t)
1103
1104 (defcustom gnus-treat-highlight-citation t
1105 "Highlight cited text.
1106 Valid values are nil, t, `head', `last', an integer or a predicate.
1107 See Info node `(gnus)Customizing Articles' for details."
1108 :group 'gnus-article-treat
1109 :link '(custom-manual "(gnus)Customizing Articles")
1110 :type gnus-article-treat-custom)
1111 (put 'gnus-treat-highlight-citation 'highlight t)
1112
1113 (defcustom gnus-treat-date-ut nil
1114 "Display the Date in UT (GMT).
1115 Valid values are nil, t, `head', `last', an integer or a predicate.
1116 See Info node `(gnus)Customizing Articles' for details."
1117 :group 'gnus-article-treat
1118 :link '(custom-manual "(gnus)Customizing Articles")
1119 :type gnus-article-treat-head-custom)
1120
1121 (defcustom gnus-treat-date-local nil
1122 "Display the Date in the local timezone.
1123 Valid values are nil, t, `head', `last', an integer or a predicate.
1124 See Info node `(gnus)Customizing Articles' for details."
1125 :group 'gnus-article-treat
1126 :link '(custom-manual "(gnus)Customizing Articles")
1127 :type gnus-article-treat-head-custom)
1128
1129 (defcustom gnus-treat-date-english nil
1130 "Display the Date in a format that can be read aloud in English.
1131 Valid values are nil, t, `head', `last', an integer or a predicate.
1132 See Info node `(gnus)Customizing Articles' for details."
1133 :version "22.1"
1134 :group 'gnus-article-treat
1135 :link '(custom-manual "(gnus)Customizing Articles")
1136 :type gnus-article-treat-head-custom)
1137
1138 (defcustom gnus-treat-date-lapsed nil
1139 "Display the Date header in a way that says how much time has elapsed.
1140 Valid values are nil, t, `head', `last', an integer or a predicate.
1141 See Info node `(gnus)Customizing Articles' for details."
1142 :group 'gnus-article-treat
1143 :link '(custom-manual "(gnus)Customizing Articles")
1144 :type gnus-article-treat-head-custom)
1145
1146 (defcustom gnus-treat-date-original nil
1147 "Display the date in the original timezone.
1148 Valid values are nil, t, `head', `last', an integer or a predicate.
1149 See Info node `(gnus)Customizing Articles' for details."
1150 :group 'gnus-article-treat
1151 :link '(custom-manual "(gnus)Customizing Articles")
1152 :type gnus-article-treat-head-custom)
1153
1154 (defcustom gnus-treat-date-iso8601 nil
1155 "Display the date in the ISO8601 format.
1156 Valid values are nil, t, `head', `last', an integer or a predicate.
1157 See Info node `(gnus)Customizing Articles' for details."
1158 :version "21.1"
1159 :group 'gnus-article-treat
1160 :link '(custom-manual "(gnus)Customizing Articles")
1161 :type gnus-article-treat-head-custom)
1162
1163 (defcustom gnus-treat-date-user-defined nil
1164 "Display the date in a user-defined format.
1165 The format is defined by the `gnus-article-time-format' variable.
1166 Valid values are nil, t, `head', `last', an integer or a predicate.
1167 See Info node `(gnus)Customizing Articles' for details."
1168 :group 'gnus-article-treat
1169 :link '(custom-manual "(gnus)Customizing Articles")
1170 :type gnus-article-treat-head-custom)
1171
1172 (defcustom gnus-treat-strip-headers-in-body t
1173 "Strip the X-No-Archive header line from the beginning of the body.
1174 Valid values are nil, t, `head', `last', an integer or a predicate.
1175 See Info node `(gnus)Customizing Articles' for details."
1176 :version "21.1"
1177 :group 'gnus-article-treat
1178 :link '(custom-manual "(gnus)Customizing Articles")
1179 :type gnus-article-treat-custom)
1180
1181 (defcustom gnus-treat-strip-trailing-blank-lines nil
1182 "Strip trailing blank lines.
1183 Valid values are nil, t, `head', `last', an integer or a predicate.
1184 See Info node `(gnus)Customizing Articles' for details."
1185 :group 'gnus-article-treat
1186 :link '(custom-manual "(gnus)Customizing Articles")
1187 :type gnus-article-treat-custom)
1188
1189 (defcustom gnus-treat-strip-leading-blank-lines nil
1190 "Strip leading blank lines.
1191 Valid values are nil, t, `head', `last', an integer or a predicate.
1192 See Info node `(gnus)Customizing Articles' for details."
1193 :group 'gnus-article-treat
1194 :link '(custom-manual "(gnus)Customizing Articles")
1195 :type gnus-article-treat-custom)
1196
1197 (defcustom gnus-treat-strip-multiple-blank-lines nil
1198 "Strip multiple blank lines.
1199 Valid values are nil, t, `head', `last', an integer or a predicate.
1200 See Info node `(gnus)Customizing Articles' for details."
1201 :group 'gnus-article-treat
1202 :link '(custom-manual "(gnus)Customizing Articles")
1203 :type gnus-article-treat-custom)
1204
1205 (defcustom gnus-treat-unfold-headers 'head
1206 "Unfold folded header lines.
1207 Valid values are nil, t, `head', `last', an integer or a predicate.
1208 See Info node `(gnus)Customizing Articles' for details."
1209 :version "22.1"
1210 :group 'gnus-article-treat
1211 :link '(custom-manual "(gnus)Customizing Articles")
1212 :type gnus-article-treat-custom)
1213
1214 (defcustom gnus-treat-fold-headers nil
1215 "Fold headers.
1216 Valid values are nil, t, `head', `last', an integer or a predicate.
1217 See Info node `(gnus)Customizing Articles' for details."
1218 :version "22.1"
1219 :group 'gnus-article-treat
1220 :link '(custom-manual "(gnus)Customizing Articles")
1221 :type gnus-article-treat-custom)
1222
1223 (defcustom gnus-treat-fold-newsgroups 'head
1224 "Fold the Newsgroups and Followup-To headers.
1225 Valid values are nil, t, `head', `last', an integer or a predicate.
1226 See Info node `(gnus)Customizing Articles' for details."
1227 :version "22.1"
1228 :group 'gnus-article-treat
1229 :link '(custom-manual "(gnus)Customizing Articles")
1230 :type gnus-article-treat-custom)
1231
1232 (defcustom gnus-treat-overstrike t
1233 "Treat overstrike highlighting.
1234 Valid values are nil, t, `head', `last', an integer or a predicate.
1235 See Info node `(gnus)Customizing Articles' for details."
1236 :group 'gnus-article-treat
1237 :link '(custom-manual "(gnus)Customizing Articles")
1238 :type gnus-article-treat-custom)
1239 (put 'gnus-treat-overstrike 'highlight t)
1240
1241 (make-obsolete-variable 'gnus-treat-display-xface
1242 'gnus-treat-display-x-face)
1243
1244 (defcustom gnus-treat-display-x-face
1245 (and (not noninteractive)
1246 (or (and (fboundp 'image-type-available-p)
1247 (image-type-available-p 'xbm)
1248 (string-match "^0x" (shell-command-to-string "uncompface"))
1249 (executable-find "icontopbm"))
1250 (and (featurep 'xemacs)
1251 (featurep 'xface)))
1252 'head)
1253 "Display X-Face headers.
1254 Valid values are nil, t, `head', `last', an integer or a predicate.
1255 See Info node `(gnus)Customizing Articles' and Info node
1256 `(gnus)X-Face' for details."
1257 :group 'gnus-article-treat
1258 :version "21.1"
1259 :link '(custom-manual "(gnus)Customizing Articles")
1260 :link '(custom-manual "(gnus)X-Face")
1261 :type gnus-article-treat-head-custom
1262 :set (lambda (symbol value)
1263 (set-default
1264 symbol
1265 (cond ((or (boundp symbol) (get symbol 'saved-value))
1266 value)
1267 ((boundp 'gnus-treat-display-xface)
1268 (message "\
1269 ** gnus-treat-display-xface is an obsolete variable;\
1270 use gnus-treat-display-x-face instead")
1271 (default-value 'gnus-treat-display-xface))
1272 ((get 'gnus-treat-display-xface 'saved-value)
1273 (message "\
1274 ** gnus-treat-display-xface is an obsolete variable;\
1275 use gnus-treat-display-x-face instead")
1276 (eval (car (get 'gnus-treat-display-xface 'saved-value))))
1277 (t
1278 value)))))
1279 (put 'gnus-treat-display-x-face 'highlight t)
1280
1281 (defcustom gnus-treat-display-face
1282 (and (not noninteractive)
1283 (or (and (fboundp 'image-type-available-p)
1284 (image-type-available-p 'png))
1285 (and (featurep 'xemacs)
1286 (featurep 'png)))
1287 'head)
1288 "Display Face headers.
1289 Valid values are nil, t, `head', `last', an integer or a predicate.
1290 See Info node `(gnus)Customizing Articles' and Info node
1291 `(gnus)X-Face' for details."
1292 :group 'gnus-article-treat
1293 :version "22.1"
1294 :link '(custom-manual "(gnus)Customizing Articles")
1295 :link '(custom-manual "(gnus)X-Face")
1296 :type gnus-article-treat-head-custom)
1297 (put 'gnus-treat-display-face 'highlight t)
1298
1299 (defcustom gnus-treat-display-smileys
1300 (if (or (and (featurep 'xemacs)
1301 (featurep 'xpm))
1302 (and (fboundp 'image-type-available-p)
1303 (image-type-available-p 'pbm)))
1304 t nil)
1305 "Display smileys.
1306 Valid values are nil, t, `head', `last', an integer or a predicate.
1307 See Info node `(gnus)Customizing Articles' and Info node
1308 `(gnus)Smileys' for details."
1309 :group 'gnus-article-treat
1310 :version "21.1"
1311 :link '(custom-manual "(gnus)Customizing Articles")
1312 :link '(custom-manual "(gnus)Smileys")
1313 :type gnus-article-treat-custom)
1314 (put 'gnus-treat-display-smileys 'highlight t)
1315
1316 (defcustom gnus-treat-from-picon
1317 (if (and (gnus-image-type-available-p 'xpm)
1318 (gnus-picons-installed-p))
1319 'head nil)
1320 "Display picons in the From header.
1321 Valid values are nil, t, `head', `last', an integer or a predicate.
1322 See Info node `(gnus)Customizing Articles' and Info node
1323 `(gnus)Picons' for details."
1324 :version "22.1"
1325 :group 'gnus-article-treat
1326 :group 'gnus-picon
1327 :link '(custom-manual "(gnus)Customizing Articles")
1328 :link '(custom-manual "(gnus)Picons")
1329 :type gnus-article-treat-head-custom)
1330 (put 'gnus-treat-from-picon 'highlight t)
1331
1332 (defcustom gnus-treat-mail-picon
1333 (if (and (gnus-image-type-available-p 'xpm)
1334 (gnus-picons-installed-p))
1335 'head nil)
1336 "Display picons in To and Cc headers.
1337 Valid values are nil, t, `head', `last', an integer or a predicate.
1338 See Info node `(gnus)Customizing Articles' and Info node
1339 `(gnus)Picons' for details."
1340 :version "22.1"
1341 :group 'gnus-article-treat
1342 :group 'gnus-picon
1343 :link '(custom-manual "(gnus)Customizing Articles")
1344 :link '(custom-manual "(gnus)Picons")
1345 :type gnus-article-treat-head-custom)
1346 (put 'gnus-treat-mail-picon 'highlight t)
1347
1348 (defcustom gnus-treat-newsgroups-picon
1349 (if (and (gnus-image-type-available-p 'xpm)
1350 (gnus-picons-installed-p))
1351 'head nil)
1352 "Display picons in the Newsgroups and Followup-To headers.
1353 Valid values are nil, t, `head', `last', an integer or a predicate.
1354 See Info node `(gnus)Customizing Articles' and Info node
1355 `(gnus)Picons' for details."
1356 :version "22.1"
1357 :group 'gnus-article-treat
1358 :group 'gnus-picon
1359 :link '(custom-manual "(gnus)Customizing Articles")
1360 :link '(custom-manual "(gnus)Picons")
1361 :type gnus-article-treat-head-custom)
1362 (put 'gnus-treat-newsgroups-picon 'highlight t)
1363
1364 (defcustom gnus-treat-body-boundary
1365 (if (or gnus-treat-newsgroups-picon
1366 gnus-treat-mail-picon
1367 gnus-treat-from-picon)
1368 'head nil)
1369 "Draw a boundary at the end of the headers.
1370 Valid values are nil and `head'.
1371 See Info node `(gnus)Customizing Articles' for details."
1372 :version "22.1"
1373 :group 'gnus-article-treat
1374 :link '(custom-manual "(gnus)Customizing Articles")
1375 :type gnus-article-treat-head-custom)
1376
1377 (defcustom gnus-treat-capitalize-sentences nil
1378 "Capitalize sentence-starting words.
1379 Valid values are nil, t, `head', `last', an integer or a predicate.
1380 See Info node `(gnus)Customizing Articles' for details."
1381 :version "21.1"
1382 :group 'gnus-article-treat
1383 :link '(custom-manual "(gnus)Customizing Articles")
1384 :type gnus-article-treat-custom)
1385
1386 (defcustom gnus-treat-wash-html nil
1387 "Format as HTML.
1388 Valid values are nil, t, `head', `last', an integer or a predicate.
1389 See Info node `(gnus)Customizing Articles' for details."
1390 :version "22.1"
1391 :group 'gnus-article-treat
1392 :link '(custom-manual "(gnus)Customizing Articles")
1393 :type gnus-article-treat-custom)
1394
1395 (defcustom gnus-treat-fill-long-lines nil
1396 "Fill long lines.
1397 Valid values are nil, t, `head', `last', an integer or a predicate.
1398 See Info node `(gnus)Customizing Articles' for details."
1399 :group 'gnus-article-treat
1400 :link '(custom-manual "(gnus)Customizing Articles")
1401 :type gnus-article-treat-custom)
1402
1403 (defcustom gnus-treat-play-sounds nil
1404 "Play sounds.
1405 Valid values are nil, t, `head', `last', an integer or a predicate.
1406 See Info node `(gnus)Customizing Articles' for details."
1407 :version "21.1"
1408 :group 'gnus-article-treat
1409 :link '(custom-manual "(gnus)Customizing Articles")
1410 :type gnus-article-treat-custom)
1411
1412 (defcustom gnus-treat-translate nil
1413 "Translate articles from one language to another.
1414 Valid values are nil, t, `head', `last', an integer or a predicate.
1415 See Info node `(gnus)Customizing Articles' for details."
1416 :version "21.1"
1417 :group 'gnus-article-treat
1418 :link '(custom-manual "(gnus)Customizing Articles")
1419 :type gnus-article-treat-custom)
1420
1421 (defcustom gnus-treat-x-pgp-sig nil
1422 "Verify X-PGP-Sig.
1423 To automatically treat X-PGP-Sig, set it to head.
1424 Valid values are nil, t, `head', `last', an integer or a predicate.
1425 See Info node `(gnus)Customizing Articles' for details."
1426 :version "22.1"
1427 :group 'gnus-article-treat
1428 :group 'mime-security
1429 :link '(custom-manual "(gnus)Customizing Articles")
1430 :type gnus-article-treat-custom)
1431
1432 (defvar gnus-article-encrypt-protocol-alist
1433 '(("PGP" . mml2015-self-encrypt)))
1434
1435 ;; Set to nil if more than one protocol added to
1436 ;; gnus-article-encrypt-protocol-alist.
1437 (defcustom gnus-article-encrypt-protocol "PGP"
1438 "The protocol used for encrypt articles.
1439 It is a string, such as \"PGP\". If nil, ask user."
1440 :version "22.1"
1441 :type 'string
1442 :group 'mime-security)
1443
1444 (defvar gnus-article-wash-function nil
1445 "Function used for converting HTML into text.")
1446
1447 (defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
1448 (mm-coding-system-p 'utf-8)
1449 (executable-find idna-program))
1450 "Whether IDNA decoding of headers is used when viewing messages.
1451 This requires GNU Libidn, and by default only enabled if it is found."
1452 :version "22.1"
1453 :group 'gnus-article-headers
1454 :type 'boolean)
1455
1456 (defcustom gnus-article-over-scroll nil
1457 "If non-nil, allow scrolling the article buffer even when there no more text."
1458 :version "22.1"
1459 :group 'gnus-article
1460 :type 'boolean)
1461
1462 ;;; Internal variables
1463
1464 (defvar gnus-english-month-names
1465 '("January" "February" "March" "April" "May" "June" "July" "August"
1466 "September" "October" "November" "December"))
1467
1468 (defvar article-goto-body-goes-to-point-min-p nil)
1469 (defvar gnus-article-wash-types nil)
1470 (defvar gnus-article-emphasis-alist nil)
1471 (defvar gnus-article-image-alist nil)
1472
1473 (defvar gnus-article-mime-handle-alist-1 nil)
1474 (defvar gnus-treatment-function-alist
1475 '((gnus-treat-x-pgp-sig gnus-article-verify-x-pgp-sig)
1476 (gnus-treat-strip-banner gnus-article-strip-banner)
1477 (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body)
1478 (gnus-treat-highlight-signature gnus-article-highlight-signature)
1479 (gnus-treat-buttonize gnus-article-add-buttons)
1480 (gnus-treat-fill-article gnus-article-fill-cited-article)
1481 (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
1482 (gnus-treat-strip-cr gnus-article-remove-cr)
1483 (gnus-treat-unsplit-urls gnus-article-unsplit-urls)
1484 (gnus-treat-date-ut gnus-article-date-ut)
1485 (gnus-treat-date-local gnus-article-date-local)
1486 (gnus-treat-date-english gnus-article-date-english)
1487 (gnus-treat-date-original gnus-article-date-original)
1488 (gnus-treat-date-user-defined gnus-article-date-user)
1489 (gnus-treat-date-iso8601 gnus-article-date-iso8601)
1490 (gnus-treat-date-lapsed gnus-article-date-lapsed)
1491 (gnus-treat-display-x-face gnus-article-display-x-face)
1492 (gnus-treat-display-face gnus-article-display-face)
1493 (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
1494 (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
1495 (gnus-treat-hide-signature gnus-article-hide-signature)
1496 (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers)
1497 (gnus-treat-leading-whitespace gnus-article-remove-leading-whitespace)
1498 (gnus-treat-strip-pem gnus-article-hide-pem)
1499 (gnus-treat-from-picon gnus-treat-from-picon)
1500 (gnus-treat-mail-picon gnus-treat-mail-picon)
1501 (gnus-treat-newsgroups-picon gnus-treat-newsgroups-picon)
1502 (gnus-treat-highlight-headers gnus-article-highlight-headers)
1503 (gnus-treat-highlight-signature gnus-article-highlight-signature)
1504 (gnus-treat-strip-trailing-blank-lines
1505 gnus-article-remove-trailing-blank-lines)
1506 (gnus-treat-strip-leading-blank-lines
1507 gnus-article-strip-leading-blank-lines)
1508 (gnus-treat-strip-multiple-blank-lines
1509 gnus-article-strip-multiple-blank-lines)
1510 (gnus-treat-overstrike gnus-article-treat-overstrike)
1511 (gnus-treat-unfold-headers gnus-article-treat-unfold-headers)
1512 (gnus-treat-fold-headers gnus-article-treat-fold-headers)
1513 (gnus-treat-fold-newsgroups gnus-article-treat-fold-newsgroups)
1514 (gnus-treat-buttonize-head gnus-article-add-buttons-to-head)
1515 (gnus-treat-display-smileys gnus-treat-smiley)
1516 (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences)
1517 (gnus-treat-wash-html gnus-article-wash-html)
1518 (gnus-treat-emphasize gnus-article-emphasize)
1519 (gnus-treat-hide-citation gnus-article-hide-citation)
1520 (gnus-treat-hide-citation-maybe gnus-article-hide-citation-maybe)
1521 (gnus-treat-highlight-citation gnus-article-highlight-citation)
1522 (gnus-treat-body-boundary gnus-article-treat-body-boundary)
1523 (gnus-treat-play-sounds gnus-earcon-display)))
1524
1525 (defvar gnus-article-mime-handle-alist nil)
1526 (defvar article-lapsed-timer nil)
1527 (defvar gnus-article-current-summary nil)
1528
1529 (defvar gnus-article-mode-syntax-table
1530 (let ((table (copy-syntax-table text-mode-syntax-table)))
1531 ;; This causes the citation match run O(2^n).
1532 ;; (modify-syntax-entry ?- "w" table)
1533 (modify-syntax-entry ?> ")<" table)
1534 (modify-syntax-entry ?< "(>" table)
1535 ;; make M-. in article buffers work for `foo' strings
1536 (modify-syntax-entry ?' " " table)
1537 (modify-syntax-entry ?` " " table)
1538 table)
1539 "Syntax table used in article mode buffers.
1540 Initialized from `text-mode-syntax-table.")
1541
1542 (defvar gnus-save-article-buffer nil)
1543
1544 (defvar gnus-article-mode-line-format-alist
1545 (nconc '((?w (gnus-article-wash-status) ?s)
1546 (?m (gnus-article-mime-part-status) ?s))
1547 gnus-summary-mode-line-format-alist))
1548
1549 (defvar gnus-number-of-articles-to-be-saved nil)
1550
1551 (defvar gnus-inhibit-hiding nil)
1552
1553 (defvar gnus-article-edit-mode nil)
1554
1555 ;;; Macros for dealing with the article buffer.
1556
1557 (defmacro gnus-with-article-headers (&rest forms)
1558 `(save-excursion
1559 (set-buffer gnus-article-buffer)
1560 (save-restriction
1561 (let ((inhibit-read-only t)
1562 (inhibit-point-motion-hooks t)
1563 (case-fold-search t))
1564 (article-narrow-to-head)
1565 ,@forms))))
1566
1567 (put 'gnus-with-article-headers 'lisp-indent-function 0)
1568 (put 'gnus-with-article-headers 'edebug-form-spec '(body))
1569
1570 (defmacro gnus-with-article-buffer (&rest forms)
1571 `(save-excursion
1572 (set-buffer gnus-article-buffer)
1573 (let ((inhibit-read-only t))
1574 ,@forms)))
1575
1576 (put 'gnus-with-article-buffer 'lisp-indent-function 0)
1577 (put 'gnus-with-article-buffer 'edebug-form-spec '(body))
1578
1579 (defun gnus-article-goto-header (header)
1580 "Go to HEADER, which is a regular expression."
1581 (re-search-forward (concat "^\\(" header "\\):") nil t))
1582
1583 (defsubst gnus-article-hide-text (b e props)
1584 "Set text PROPS on the B to E region, extending `intangible' 1 past B."
1585 (gnus-add-text-properties-when 'article-type nil b e props)
1586 (when (memq 'intangible props)
1587 (put-text-property
1588 (max (1- b) (point-min))
1589 b 'intangible (cddr (memq 'intangible props)))))
1590
1591 (defsubst gnus-article-unhide-text (b e)
1592 "Remove hidden text properties from region between B and E."
1593 (remove-text-properties b e gnus-hidden-properties)
1594 (when (memq 'intangible gnus-hidden-properties)
1595 (put-text-property (max (1- b) (point-min))
1596 b 'intangible nil)))
1597
1598 (defun gnus-article-hide-text-type (b e type)
1599 "Hide text of TYPE between B and E."
1600 (gnus-add-wash-type type)
1601 (gnus-article-hide-text
1602 b e (cons 'article-type (cons type gnus-hidden-properties))))
1603
1604 (defun gnus-article-unhide-text-type (b e type)
1605 "Unhide text of TYPE between B and E."
1606 (gnus-delete-wash-type type)
1607 (remove-text-properties
1608 b e (cons 'article-type (cons type gnus-hidden-properties)))
1609 (when (memq 'intangible gnus-hidden-properties)
1610 (put-text-property (max (1- b) (point-min))
1611 b 'intangible nil)))
1612
1613 (defun gnus-article-hide-text-of-type (type)
1614 "Hide text of TYPE in the current buffer."
1615 (save-excursion
1616 (let ((b (point-min))
1617 (e (point-max)))
1618 (while (setq b (text-property-any b e 'article-type type))
1619 (add-text-properties b (incf b) gnus-hidden-properties)))))
1620
1621 (defun gnus-article-delete-text-of-type (type)
1622 "Delete text of TYPE in the current buffer."
1623 (save-excursion
1624 (let ((b (point-min)))
1625 (while (setq b (text-property-any b (point-max) 'article-type type))
1626 (delete-region
1627 b (or (text-property-not-all b (point-max) 'article-type type)
1628 (point-max)))))))
1629
1630 (defun gnus-article-delete-invisible-text ()
1631 "Delete all invisible text in the current buffer."
1632 (save-excursion
1633 (let ((b (point-min)))
1634 (while (setq b (text-property-any b (point-max) 'invisible t))
1635 (delete-region
1636 b (or (text-property-not-all b (point-max) 'invisible t)
1637 (point-max)))))))
1638
1639 (defun gnus-article-text-type-exists-p (type)
1640 "Say whether any text of type TYPE exists in the buffer."
1641 (text-property-any (point-min) (point-max) 'article-type type))
1642
1643 (defsubst gnus-article-header-rank ()
1644 "Give the rank of the string HEADER as given by `gnus-sorted-header-list'."
1645 (let ((list gnus-sorted-header-list)
1646 (i 1))
1647 (while list
1648 (if (looking-at (car list))
1649 (setq list nil)
1650 (setq list (cdr list))
1651 (incf i)))
1652 i))
1653
1654 (defun article-hide-headers (&optional arg delete)
1655 "Hide unwanted headers and possibly sort them as well."
1656 (interactive)
1657 ;; This function might be inhibited.
1658 (unless gnus-inhibit-hiding
1659 (let ((inhibit-read-only nil)
1660 (case-fold-search t)
1661 (max (1+ (length gnus-sorted-header-list)))
1662 (inhibit-point-motion-hooks t)
1663 (cur (current-buffer))
1664 ignored visible beg)
1665 (save-excursion
1666 ;; `gnus-ignored-headers' and `gnus-visible-headers' may be
1667 ;; group parameters, so we should go to the summary buffer.
1668 (when (prog1
1669 (condition-case nil
1670 (progn (set-buffer gnus-summary-buffer) t)
1671 (error nil))
1672 (setq ignored (when (not gnus-visible-headers)
1673 (cond ((stringp gnus-ignored-headers)
1674 gnus-ignored-headers)
1675 ((listp gnus-ignored-headers)
1676 (mapconcat 'identity
1677 gnus-ignored-headers
1678 "\\|"))))
1679 visible (cond ((stringp gnus-visible-headers)
1680 gnus-visible-headers)
1681 ((and gnus-visible-headers
1682 (listp gnus-visible-headers))
1683 (mapconcat 'identity
1684 gnus-visible-headers
1685 "\\|")))))
1686 (set-buffer cur))
1687 (save-restriction
1688 ;; First we narrow to just the headers.
1689 (article-narrow-to-head)
1690 ;; Hide any "From " lines at the beginning of (mail) articles.
1691 (while (looking-at "From ")
1692 (forward-line 1))
1693 (unless (bobp)
1694 (delete-region (point-min) (point)))
1695 ;; Then treat the rest of the header lines.
1696 ;; Then we use the two regular expressions
1697 ;; `gnus-ignored-headers' and `gnus-visible-headers' to
1698 ;; select which header lines is to remain visible in the
1699 ;; article buffer.
1700 (while (re-search-forward "^[^ \t:]*:" nil t)
1701 (beginning-of-line)
1702 ;; Mark the rank of the header.
1703 (put-text-property
1704 (point) (1+ (point)) 'message-rank
1705 (if (or (and visible (looking-at visible))
1706 (and ignored
1707 (not (looking-at ignored))))
1708 (gnus-article-header-rank)
1709 (+ 2 max)))
1710 (forward-line 1))
1711 (message-sort-headers-1)
1712 (when (setq beg (text-property-any
1713 (point-min) (point-max) 'message-rank (+ 2 max)))
1714 ;; We delete the unwanted headers.
1715 (gnus-add-wash-type 'headers)
1716 (add-text-properties (point-min) (+ 5 (point-min))
1717 '(article-type headers dummy-invisible t))
1718 (delete-region beg (point-max))))))))
1719
1720 (defun article-hide-boring-headers (&optional arg)
1721 "Toggle hiding of headers that aren't very interesting.
1722 If given a negative prefix, always show; if given a positive prefix,
1723 always hide."
1724 (interactive (gnus-article-hidden-arg))
1725 (when (and (not (gnus-article-check-hidden-text 'boring-headers arg))
1726 (not gnus-show-all-headers))
1727 (save-excursion
1728 (save-restriction
1729 (let ((inhibit-read-only t)
1730 (list gnus-boring-article-headers)
1731 (inhibit-point-motion-hooks t)
1732 elem)
1733 (article-narrow-to-head)
1734 (while list
1735 (setq elem (pop list))
1736 (goto-char (point-min))
1737 (cond
1738 ;; Hide empty headers.
1739 ((eq elem 'empty)
1740 (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
1741 (forward-line -1)
1742 (gnus-article-hide-text-type
1743 (gnus-point-at-bol)
1744 (progn
1745 (end-of-line)
1746 (if (re-search-forward "^[^ \t]" nil t)
1747 (match-beginning 0)
1748 (point-max)))
1749 'boring-headers)))
1750 ;; Hide boring Newsgroups header.
1751 ((eq elem 'newsgroups)
1752 (when (gnus-string-equal
1753 (gnus-fetch-field "newsgroups")
1754 (gnus-group-real-name
1755 (if (boundp 'gnus-newsgroup-name)
1756 gnus-newsgroup-name
1757 "")))
1758 (gnus-article-hide-header "newsgroups")))
1759 ((eq elem 'to-address)
1760 (let ((to (message-fetch-field "to"))
1761 (to-address
1762 (gnus-parameter-to-address
1763 (if (boundp 'gnus-newsgroup-name)
1764 gnus-newsgroup-name ""))))
1765 (when (and to to-address
1766 (ignore-errors
1767 (gnus-string-equal
1768 ;; only one address in To
1769 (nth 1 (mail-extract-address-components to))
1770 to-address)))
1771 (gnus-article-hide-header "to"))))
1772 ((eq elem 'to-list)
1773 (let ((to (message-fetch-field "to"))
1774 (to-list
1775 (gnus-parameter-to-list
1776 (if (boundp 'gnus-newsgroup-name)
1777 gnus-newsgroup-name ""))))
1778 (when (and to to-list
1779 (ignore-errors
1780 (gnus-string-equal
1781 ;; only one address in To
1782 (nth 1 (mail-extract-address-components to))
1783 to-list)))
1784 (gnus-article-hide-header "to"))))
1785 ((eq elem 'cc-list)
1786 (let ((cc (message-fetch-field "cc"))
1787 (to-list
1788 (gnus-parameter-to-list
1789 (if (boundp 'gnus-newsgroup-name)
1790 gnus-newsgroup-name ""))))
1791 (when (and cc to-list
1792 (ignore-errors
1793 (gnus-string-equal
1794 ;; only one address in CC
1795 (nth 1 (mail-extract-address-components cc))
1796 to-list)))
1797 (gnus-article-hide-header "cc"))))
1798 ((eq elem 'followup-to)
1799 (when (gnus-string-equal
1800 (message-fetch-field "followup-to")
1801 (message-fetch-field "newsgroups"))
1802 (gnus-article-hide-header "followup-to")))
1803 ((eq elem 'reply-to)
1804 (if (gnus-group-find-parameter
1805 gnus-newsgroup-name 'broken-reply-to)
1806 (gnus-article-hide-header "reply-to")
1807 (let ((from (message-fetch-field "from"))
1808 (reply-to (message-fetch-field "reply-to")))
1809 (when
1810 (and
1811 from reply-to
1812 (ignore-errors
1813 (equal
1814 (sort (mapcar
1815 (lambda (x) (downcase (cadr x)))
1816 (mail-extract-address-components from t))
1817 'string<)
1818 (sort (mapcar
1819 (lambda (x) (downcase (cadr x)))
1820 (mail-extract-address-components reply-to t))
1821 'string<))))
1822 (gnus-article-hide-header "reply-to")))))
1823 ((eq elem 'date)
1824 (let ((date (message-fetch-field "date")))
1825 (when (and date
1826 (< (days-between (current-time-string) date)
1827 4))
1828 (gnus-article-hide-header "date"))))
1829 ((eq elem 'long-to)
1830 (let ((to (message-fetch-field "to"))
1831 (cc (message-fetch-field "cc")))
1832 (when (> (length to) 1024)
1833 (gnus-article-hide-header "to"))
1834 (when (> (length cc) 1024)
1835 (gnus-article-hide-header "cc"))))
1836 ((eq elem 'many-to)
1837 (let ((to-count 0)
1838 (cc-count 0))
1839 (goto-char (point-min))
1840 (while (re-search-forward "^to:" nil t)
1841 (setq to-count (1+ to-count)))
1842 (when (> to-count 1)
1843 (while (> to-count 0)
1844 (goto-char (point-min))
1845 (save-restriction
1846 (re-search-forward "^to:" nil nil to-count)
1847 (forward-line -1)
1848 (narrow-to-region (point) (point-max))
1849 (gnus-article-hide-header "to"))
1850 (setq to-count (1- to-count))))
1851 (goto-char (point-min))
1852 (while (re-search-forward "^cc:" nil t)
1853 (setq cc-count (1+ cc-count)))
1854 (when (> cc-count 1)
1855 (while (> cc-count 0)
1856 (goto-char (point-min))
1857 (save-restriction
1858 (re-search-forward "^cc:" nil nil cc-count)
1859 (forward-line -1)
1860 (narrow-to-region (point) (point-max))
1861 (gnus-article-hide-header "cc"))
1862 (setq cc-count (1- cc-count)))))))))))))
1863
1864 (defun gnus-article-hide-header (header)
1865 (save-excursion
1866 (goto-char (point-min))
1867 (when (re-search-forward (concat "^" header ":") nil t)
1868 (gnus-article-hide-text-type
1869 (gnus-point-at-bol)
1870 (progn
1871 (end-of-line)
1872 (if (re-search-forward "^[^ \t]" nil t)
1873 (match-beginning 0)
1874 (point-max)))
1875 'boring-headers))))
1876
1877 (defvar gnus-article-normalized-header-length 40
1878 "Length of normalized headers.")
1879
1880 (defun article-normalize-headers ()
1881 "Make all header lines 40 characters long."
1882 (interactive)
1883 (let ((inhibit-read-only t)
1884 column)
1885 (save-excursion
1886 (save-restriction
1887 (article-narrow-to-head)
1888 (while (not (eobp))
1889 (cond
1890 ((< (setq column (- (gnus-point-at-eol) (point)))
1891 gnus-article-normalized-header-length)
1892 (end-of-line)
1893 (insert (make-string
1894 (- gnus-article-normalized-header-length column)
1895 ? )))
1896 ((> column gnus-article-normalized-header-length)
1897 (gnus-put-text-property
1898 (progn
1899 (forward-char gnus-article-normalized-header-length)
1900 (point))
1901 (gnus-point-at-eol)
1902 'invisible t))
1903 (t
1904 ;; Do nothing.
1905 ))
1906 (forward-line 1))))))
1907
1908 (defun article-treat-dumbquotes ()
1909 "Translate M****s*** sm*rtq**t*s and other symbols into proper text.
1910 Note that this function guesses whether a character is a sm*rtq**t* or
1911 not, so it should only be used interactively.
1912
1913 Sm*rtq**t*s are M****s***'s unilateral extension to the
1914 iso-8859-1 character map in an attempt to provide more quoting
1915 characters. If you see something like \\222 or \\264 where
1916 you're expecting some kind of apostrophe or quotation mark, then
1917 try this wash."
1918 (interactive)
1919 (article-translate-strings gnus-article-dumbquotes-map))
1920
1921 (defun article-translate-characters (from to)
1922 "Translate all characters in the body of the article according to FROM and TO.
1923 FROM is a string of characters to translate from; to is a string of
1924 characters to translate to."
1925 (save-excursion
1926 (when (article-goto-body)
1927 (let ((inhibit-read-only t)
1928 (x (make-string 225 ?x))
1929 (i -1))
1930 (while (< (incf i) (length x))
1931 (aset x i i))
1932 (setq i 0)
1933 (while (< i (length from))
1934 (aset x (aref from i) (aref to i))
1935 (incf i))
1936 (translate-region (point) (point-max) x)))))
1937
1938 (defun article-translate-strings (map)
1939 "Translate all string in the body of the article according to MAP.
1940 MAP is an alist where the elements are on the form (\"from\" \"to\")."
1941 (save-excursion
1942 (when (article-goto-body)
1943 (let ((inhibit-read-only t)
1944 elem)
1945 (while (setq elem (pop map))
1946 (save-excursion
1947 (while (search-forward (car elem) nil t)
1948 (replace-match (cadr elem)))))))))
1949
1950 (defun article-treat-overstrike ()
1951 "Translate overstrikes into bold text."
1952 (interactive)
1953 (save-excursion
1954 (when (article-goto-body)
1955 (let ((inhibit-read-only t))
1956 (while (search-forward "\b" nil t)
1957 (let ((next (char-after))
1958 (previous (char-after (- (point) 2))))
1959 ;; We do the boldification/underlining by hiding the
1960 ;; overstrikes and putting the proper text property
1961 ;; on the letters.
1962 (cond
1963 ((eq next previous)
1964 (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike)
1965 (put-text-property (point) (1+ (point)) 'face 'bold))
1966 ((eq next ?_)
1967 (gnus-article-hide-text-type
1968 (1- (point)) (1+ (point)) 'overstrike)
1969 (put-text-property
1970 (- (point) 2) (1- (point)) 'face 'underline))
1971 ((eq previous ?_)
1972 (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike)
1973 (put-text-property
1974 (point) (1+ (point)) 'face 'underline)))))))))
1975
1976 (defun gnus-article-treat-unfold-headers ()
1977 "Unfold folded message headers.
1978 Only the headers that fit into the current window width will be
1979 unfolded."
1980 (interactive)
1981 (gnus-with-article-headers
1982 (let (length)
1983 (while (not (eobp))
1984 (save-restriction
1985 (mail-header-narrow-to-field)
1986 (let ((header (buffer-string)))
1987 (with-temp-buffer
1988 (insert header)
1989 (goto-char (point-min))
1990 (while (re-search-forward "\n[\t ]" nil t)
1991 (replace-match " " t t)))
1992 (setq length (- (point-max) (point-min) 1)))
1993 (when (< length (window-width))
1994 (while (re-search-forward "\n[\t ]" nil t)
1995 (replace-match " " t t)))
1996 (goto-char (point-max)))))))
1997
1998 (defun gnus-article-treat-fold-headers ()
1999 "Fold message headers."
2000 (interactive)
2001 (gnus-with-article-headers
2002 (while (not (eobp))
2003 (save-restriction
2004 (mail-header-narrow-to-field)
2005 (mail-header-fold-field)
2006 (goto-char (point-max))))))
2007
2008 (defun gnus-treat-smiley ()
2009 "Toggle display of textual emoticons (\"smileys\") as small graphical icons."
2010 (interactive)
2011 (gnus-with-article-buffer
2012 (if (memq 'smiley gnus-article-wash-types)
2013 (gnus-delete-images 'smiley)
2014 (article-goto-body)
2015 (let ((images (smiley-region (point) (point-max))))
2016 (when images
2017 (gnus-add-wash-type 'smiley)
2018 (dolist (image images)
2019 (gnus-add-image 'smiley image)))))))
2020
2021 (defun gnus-article-remove-images ()
2022 "Remove all images from the article buffer."
2023 (interactive)
2024 (gnus-with-article-buffer
2025 (dolist (elem gnus-article-image-alist)
2026 (gnus-delete-images (car elem)))))
2027
2028 (defun gnus-article-treat-fold-newsgroups ()
2029 "Unfold folded message headers.
2030 Only the headers that fit into the current window width will be
2031 unfolded."
2032 (interactive)
2033 (gnus-with-article-headers
2034 (while (gnus-article-goto-header "newsgroups\\|followup-to")
2035 (save-restriction
2036 (mail-header-narrow-to-field)
2037 (while (re-search-forward ", *" nil t)
2038 (replace-match ", " t t))
2039 (mail-header-fold-field)
2040 (goto-char (point-max))))))
2041
2042 (defun gnus-article-treat-body-boundary ()
2043 "Place a boundary line at the end of the headers."
2044 (interactive)
2045 (when (and gnus-body-boundary-delimiter
2046 (> (length gnus-body-boundary-delimiter) 0))
2047 (gnus-with-article-headers
2048 (goto-char (point-max))
2049 (let ((start (point)))
2050 (insert "X-Boundary: ")
2051 (gnus-add-text-properties start (point) '(invisible t intangible t))
2052 (insert (let (str)
2053 (while (>= (1- (window-width)) (length str))
2054 (setq str (concat str gnus-body-boundary-delimiter)))
2055 (substring str 0 (1- (window-width))))
2056 "\n")
2057 (gnus-put-text-property start (point) 'gnus-decoration 'header)))))
2058
2059 (defun article-fill-long-lines ()
2060 "Fill lines that are wider than the window width."
2061 (interactive)
2062 (save-excursion
2063 (let ((inhibit-read-only t)
2064 (width (window-width (get-buffer-window (current-buffer)))))
2065 (save-restriction
2066 (article-goto-body)
2067 (let ((adaptive-fill-mode nil)) ;Why? -sm
2068 (while (not (eobp))
2069 (end-of-line)
2070 (when (>= (current-column) (min fill-column width))
2071 (narrow-to-region (min (1+ (point)) (point-max))
2072 (gnus-point-at-bol))
2073 (let ((goback (point-marker)))
2074 (fill-paragraph nil)
2075 (goto-char (marker-position goback)))
2076 (widen))
2077 (forward-line 1)))))))
2078
2079 (defun article-capitalize-sentences ()
2080 "Capitalize the first word in each sentence."
2081 (interactive)
2082 (save-excursion
2083 (let ((inhibit-read-only t)
2084 (paragraph-start "^[\n\^L]"))
2085 (article-goto-body)
2086 (while (not (eobp))
2087 (capitalize-word 1)
2088 (forward-sentence)))))
2089
2090 (defun article-remove-cr ()
2091 "Remove trailing CRs and then translate remaining CRs into LFs."
2092 (interactive)
2093 (save-excursion
2094 (let ((inhibit-read-only t))
2095 (goto-char (point-min))
2096 (while (re-search-forward "\r+$" nil t)
2097 (replace-match "" t t))
2098 (goto-char (point-min))
2099 (while (search-forward "\r" nil t)
2100 (replace-match "\n" t t)))))
2101
2102 (defun article-remove-trailing-blank-lines ()
2103 "Remove all trailing blank lines from the article."
2104 (interactive)
2105 (save-excursion
2106 (let ((inhibit-read-only t))
2107 (goto-char (point-max))
2108 (delete-region
2109 (point)
2110 (progn
2111 (while (and (not (bobp))
2112 (looking-at "^[ \t]*$")
2113 (not (gnus-annotation-in-region-p
2114 (point) (gnus-point-at-eol))))
2115 (forward-line -1))
2116 (forward-line 1)
2117 (point))))))
2118
2119 (defun article-display-face ()
2120 "Display any Face headers in the header."
2121 (interactive)
2122 (let ((wash-face-p buffer-read-only))
2123 (gnus-with-article-headers
2124 ;; When displaying parts, this function can be called several times on
2125 ;; the same article, without any intended toggle semantic (as typing `W
2126 ;; D d' would have). So face deletion must occur only when we come from
2127 ;; an interactive command, that is when the *Article* buffer is
2128 ;; read-only.
2129 (if (and wash-face-p (memq 'face gnus-article-wash-types))
2130 (gnus-delete-images 'face)
2131 (let (face faces from)
2132 (save-current-buffer
2133 (when (and wash-face-p
2134 (gnus-buffer-live-p gnus-original-article-buffer)
2135 (not (re-search-forward "^Face:[\t ]*" nil t)))
2136 (set-buffer gnus-original-article-buffer))
2137 (save-restriction
2138 (mail-narrow-to-head)
2139 (while (gnus-article-goto-header "Face")
2140 (push (mail-header-field-value) faces))))
2141 (when faces
2142 (goto-char (point-min))
2143 (let ((from (gnus-article-goto-header "from"))
2144 png image)
2145 (unless from
2146 (insert "From:")
2147 (setq from (point))
2148 (insert "[no `from' set]\n"))
2149 (while faces
2150 (when (setq png (gnus-convert-face-to-png (pop faces)))
2151 (setq image (gnus-create-image png 'png t))
2152 (goto-char from)
2153 (gnus-add-wash-type 'face)
2154 (gnus-add-image 'face image)
2155 (gnus-put-image image nil 'face))))))))))
2156
2157 (defun article-display-x-face (&optional force)
2158 "Look for an X-Face header and display it if present."
2159 (interactive (list 'force))
2160 (let ((wash-face-p buffer-read-only)) ;; When type `W f'
2161 (gnus-with-article-headers
2162 ;; Delete the old process, if any.
2163 (when (process-status "article-x-face")
2164 (delete-process "article-x-face"))
2165 ;; See the comment in `article-display-face'.
2166 (if (and wash-face-p (memq 'xface gnus-article-wash-types))
2167 ;; We have already displayed X-Faces, so we remove them
2168 ;; instead.
2169 (gnus-delete-images 'xface)
2170 ;; Display X-Faces.
2171 (let (x-faces from face)
2172 (save-current-buffer
2173 (when (and wash-face-p
2174 (gnus-buffer-live-p gnus-original-article-buffer)
2175 (not (re-search-forward "^X-Face:[\t ]*" nil t)))
2176 ;; If type `W f', use gnus-original-article-buffer,
2177 ;; otherwise use the current buffer because displaying
2178 ;; RFC822 parts calls this function too.
2179 (set-buffer gnus-original-article-buffer))
2180 (save-restriction
2181 (mail-narrow-to-head)
2182 (while (gnus-article-goto-header "X-Face")
2183 (push (mail-header-field-value) x-faces))
2184 (setq from (message-fetch-field "from"))))
2185 ;; Sending multiple EOFs to xv doesn't work, so we only do a
2186 ;; single external face.
2187 (when (stringp gnus-article-x-face-command)
2188 (setq x-faces (list (car x-faces))))
2189 (when (and x-faces
2190 gnus-article-x-face-command
2191 (or force
2192 ;; Check whether this face is censored.
2193 (not gnus-article-x-face-too-ugly)
2194 (and from
2195 (not (string-match gnus-article-x-face-too-ugly
2196 from)))))
2197 (while (setq face (pop x-faces))
2198 ;; We display the face.
2199 (cond ((stringp gnus-article-x-face-command)
2200 ;; The command is a string, so we interpret the command
2201 ;; as a, well, command, and fork it off.
2202 (let ((process-connection-type nil))
2203 (gnus-set-process-query-on-exit-flag
2204 (start-process
2205 "article-x-face" nil shell-file-name
2206 shell-command-switch gnus-article-x-face-command)
2207 nil)
2208 (with-temp-buffer
2209 (insert face)
2210 (process-send-region "article-x-face"
2211 (point-min) (point-max)))
2212 (process-send-eof "article-x-face")))
2213 ((functionp gnus-article-x-face-command)
2214 ;; The command is a lisp function, so we call it.
2215 (funcall gnus-article-x-face-command face))
2216 (t
2217 (error "%s is not a function"
2218 gnus-article-x-face-command))))))))))
2219
2220 (defun article-decode-mime-words ()
2221 "Decode all MIME-encoded words in the article."
2222 (interactive)
2223 (save-excursion
2224 (set-buffer gnus-article-buffer)
2225 (let ((inhibit-point-motion-hooks t)
2226 (inhibit-read-only t)
2227 (mail-parse-charset gnus-newsgroup-charset)
2228 (mail-parse-ignored-charsets
2229 (save-excursion (set-buffer gnus-summary-buffer)
2230 gnus-newsgroup-ignored-charsets)))
2231 (mail-decode-encoded-word-region (point-min) (point-max)))))
2232
2233 (defun article-decode-charset (&optional prompt)
2234 "Decode charset-encoded text in the article.
2235 If PROMPT (the prefix), prompt for a coding system to use."
2236 (interactive "P")
2237 (let ((inhibit-point-motion-hooks t) (case-fold-search t)
2238 (inhibit-read-only t)
2239 (mail-parse-charset gnus-newsgroup-charset)
2240 (mail-parse-ignored-charsets
2241 (save-excursion (condition-case nil
2242 (set-buffer gnus-summary-buffer)
2243 (error))
2244 gnus-newsgroup-ignored-charsets))
2245 ct cte ctl charset format)
2246 (save-excursion
2247 (save-restriction
2248 (article-narrow-to-head)
2249 (setq ct (message-fetch-field "Content-Type" t)
2250 cte (message-fetch-field "Content-Transfer-Encoding" t)
2251 ctl (and ct (ignore-errors
2252 (mail-header-parse-content-type ct)))
2253 charset (cond
2254 (prompt
2255 (mm-read-coding-system "Charset to decode: "))
2256 (ctl
2257 (mail-content-type-get ctl 'charset)))
2258 format (and ctl (mail-content-type-get ctl 'format)))
2259 (when cte
2260 (setq cte (mail-header-strip cte)))
2261 (if (and ctl (not (string-match "/" (car ctl))))
2262 (setq ctl nil))
2263 (goto-char (point-max)))
2264 (forward-line 1)
2265 (save-restriction
2266 (narrow-to-region (point) (point-max))
2267 (when (and (eq mail-parse-charset 'gnus-decoded)
2268 (eq (mm-body-7-or-8) '8bit))
2269 ;; The text code could have been decoded.
2270 (setq charset mail-parse-charset))
2271 (when (and (or (not ctl)
2272 (equal (car ctl) "text/plain"))
2273 (not format)) ;; article with format will decode later.
2274 (mm-decode-body
2275 charset (and cte (intern (downcase
2276 (gnus-strip-whitespace cte))))
2277 (car ctl)))))))
2278
2279 (defun article-decode-encoded-words ()
2280 "Remove encoded-word encoding from headers."
2281 (let ((inhibit-point-motion-hooks t)
2282 (mail-parse-charset gnus-newsgroup-charset)
2283 (mail-parse-ignored-charsets
2284 (save-excursion (condition-case nil
2285 (set-buffer gnus-summary-buffer)
2286 (error))
2287 gnus-newsgroup-ignored-charsets))
2288 (inhibit-read-only t))
2289 (save-restriction
2290 (article-narrow-to-head)
2291 (funcall gnus-decode-header-function (point-min) (point-max)))))
2292
2293 (defun article-decode-group-name ()
2294 "Decode group names in `Newsgroups:'."
2295 (let ((inhibit-point-motion-hooks t)
2296 (inhibit-read-only t)
2297 (method (gnus-find-method-for-group gnus-newsgroup-name)))
2298 (when (and (or gnus-group-name-charset-method-alist
2299 gnus-group-name-charset-group-alist)
2300 (gnus-buffer-live-p gnus-original-article-buffer))
2301 (save-restriction
2302 (article-narrow-to-head)
2303 (with-current-buffer gnus-original-article-buffer
2304 (goto-char (point-min)))
2305 (while (re-search-forward
2306 "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2307 (replace-match (save-match-data
2308 (gnus-decode-newsgroups
2309 ;; XXX how to use data in article buffer?
2310 (with-current-buffer gnus-original-article-buffer
2311 (re-search-forward
2312 "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2313 nil t)
2314 (match-string 1))
2315 gnus-newsgroup-name method))
2316 t t nil 1))
2317 (goto-char (point-min))
2318 (with-current-buffer gnus-original-article-buffer
2319 (goto-char (point-min)))
2320 (while (re-search-forward
2321 "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2322 (replace-match (save-match-data
2323 (gnus-decode-newsgroups
2324 ;; XXX how to use data in article buffer?
2325 (with-current-buffer gnus-original-article-buffer
2326 (re-search-forward
2327 "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2328 nil t)
2329 (match-string 1))
2330 gnus-newsgroup-name method))
2331 t t nil 1))))))
2332
2333 (autoload 'idna-to-unicode "idna")
2334
2335 (defun article-decode-idna-rhs ()
2336 "Decode IDNA strings in RHS in various headers in current buffer.
2337 The following headers are decoded: From:, To:, Cc:, Reply-To:,
2338 Mail-Reply-To: and Mail-Followup-To:."
2339 (when gnus-use-idna
2340 (save-restriction
2341 (let ((inhibit-point-motion-hooks t)
2342 (inhibit-read-only t))
2343 (article-narrow-to-head)
2344 (goto-char (point-min))
2345 (while (re-search-forward "@[^ \t\n\r,>]*\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
2346 (let (ace unicode)
2347 (when (save-match-data
2348 (and (setq ace (match-string 1))
2349 (save-excursion
2350 (and (re-search-backward "^[^ \t]" nil t)
2351 (looking-at "From\\|To\\|Cc\\|Reply-To\\|Mail-Reply-To\\|Mail-Followup-To")))
2352 (setq unicode (idna-to-unicode ace))))
2353 (unless (string= ace unicode)
2354 (replace-match unicode nil nil nil 1)))))))))
2355
2356 (defun article-de-quoted-unreadable (&optional force read-charset)
2357 "Translate a quoted-printable-encoded article.
2358 If FORCE, decode the article whether it is marked as quoted-printable
2359 or not.
2360 If READ-CHARSET, ask for a coding system."
2361 (interactive (list 'force current-prefix-arg))
2362 (save-excursion
2363 (let ((inhibit-read-only t) type charset)
2364 (if (gnus-buffer-live-p gnus-original-article-buffer)
2365 (with-current-buffer gnus-original-article-buffer
2366 (setq type
2367 (gnus-fetch-field "content-transfer-encoding"))
2368 (let* ((ct (gnus-fetch-field "content-type"))
2369 (ctl (and ct
2370 (ignore-errors
2371 (mail-header-parse-content-type ct)))))
2372 (setq charset (and ctl
2373 (mail-content-type-get ctl 'charset)))
2374 (if (stringp charset)
2375 (setq charset (intern (downcase charset)))))))
2376 (if read-charset
2377 (setq charset (mm-read-coding-system "Charset: " charset)))
2378 (unless charset
2379 (setq charset gnus-newsgroup-charset))
2380 (when (or force
2381 (and type (let ((case-fold-search t))
2382 (string-match "quoted-printable" type))))
2383 (article-goto-body)
2384 (quoted-printable-decode-region
2385 (point) (point-max) (mm-charset-to-coding-system charset))))))
2386
2387 (defun article-de-base64-unreadable (&optional force read-charset)
2388 "Translate a base64 article.
2389 If FORCE, decode the article whether it is marked as base64 not.
2390 If READ-CHARSET, ask for a coding system."
2391 (interactive (list 'force current-prefix-arg))
2392 (save-excursion
2393 (let ((inhibit-read-only t) type charset)
2394 (if (gnus-buffer-live-p gnus-original-article-buffer)
2395 (with-current-buffer gnus-original-article-buffer
2396 (setq type
2397 (gnus-fetch-field "content-transfer-encoding"))
2398 (let* ((ct (gnus-fetch-field "content-type"))
2399 (ctl (and ct
2400 (ignore-errors
2401 (mail-header-parse-content-type ct)))))
2402 (setq charset (and ctl
2403 (mail-content-type-get ctl 'charset)))
2404 (if (stringp charset)
2405 (setq charset (intern (downcase charset)))))))
2406 (if read-charset
2407 (setq charset (mm-read-coding-system "Charset: " charset)))
2408 (unless charset
2409 (setq charset gnus-newsgroup-charset))
2410 (when (or force
2411 (and type (let ((case-fold-search t))
2412 (string-match "base64" type))))
2413 (article-goto-body)
2414 (save-restriction
2415 (narrow-to-region (point) (point-max))
2416 (base64-decode-region (point-min) (point-max))
2417 (mm-decode-coding-region
2418 (point-min) (point-max) (mm-charset-to-coding-system charset)))))))
2419
2420 (eval-when-compile
2421 (require 'rfc1843))
2422
2423 (defun article-decode-HZ ()
2424 "Translate a HZ-encoded article."
2425 (interactive)
2426 (require 'rfc1843)
2427 (save-excursion
2428 (let ((inhibit-read-only t))
2429 (rfc1843-decode-region (point-min) (point-max)))))
2430
2431 (defun article-unsplit-urls ()
2432 "Remove the newlines that some other mailers insert into URLs."
2433 (interactive)
2434 (save-excursion
2435 (let ((inhibit-read-only t))
2436 (goto-char (point-min))
2437 (while (re-search-forward
2438 "\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
2439 (replace-match "\\1\\3" t)))
2440 (when (interactive-p)
2441 (gnus-treat-article nil))))
2442
2443
2444 (defun article-wash-html (&optional read-charset)
2445 "Format an HTML article.
2446 If READ-CHARSET, ask for a coding system."
2447 (interactive "P")
2448 (save-excursion
2449 (let ((inhibit-read-only t)
2450 charset)
2451 (when (gnus-buffer-live-p gnus-original-article-buffer)
2452 (with-current-buffer gnus-original-article-buffer
2453 (let* ((ct (gnus-fetch-field "content-type"))
2454 (ctl (and ct
2455 (ignore-errors
2456 (mail-header-parse-content-type ct)))))
2457 (setq charset (and ctl
2458 (mail-content-type-get ctl 'charset)))
2459 (when (stringp charset)
2460 (setq charset (intern (downcase charset)))))))
2461 (when read-charset
2462 (setq charset (mm-read-coding-system "Charset: " charset)))
2463 (unless charset
2464 (setq charset gnus-newsgroup-charset))
2465 (article-goto-body)
2466 (save-window-excursion
2467 (save-restriction
2468 (narrow-to-region (point) (point-max))
2469 (let* ((func (or gnus-article-wash-function mm-text-html-renderer))
2470 (entry (assq func mm-text-html-washer-alist)))
2471 (when entry
2472 (setq func (cdr entry)))
2473 (cond
2474 ((functionp func)
2475 (funcall func))
2476 (t
2477 (apply (car func) (cdr func))))))))))
2478
2479 (defun gnus-article-wash-html-with-w3 ()
2480 "Wash the current buffer with w3."
2481 (mm-setup-w3)
2482 (let ((w3-strict-width (window-width))
2483 (url-standalone-mode t)
2484 (url-gateway-unplugged t)
2485 (w3-honor-stylesheets nil))
2486 (condition-case ()
2487 (w3-region (point-min) (point-max))
2488 (error))))
2489
2490 (defun gnus-article-wash-html-with-w3m ()
2491 "Wash the current buffer with emacs-w3m."
2492 (mm-setup-w3m)
2493 (save-restriction
2494 (narrow-to-region (point) (point-max))
2495 (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
2496 w3m-force-redisplay)
2497 (w3m-region (point-min) (point-max)))
2498 (when (and mm-inline-text-html-with-w3m-keymap
2499 (boundp 'w3m-minor-mode-map)
2500 w3m-minor-mode-map)
2501 (add-text-properties
2502 (point-min) (point-max)
2503 (list 'keymap w3m-minor-mode-map
2504 ;; Put the mark meaning this part was rendered by emacs-w3m.
2505 'mm-inline-text-html-with-w3m t)))))
2506
2507 (defun article-hide-list-identifiers ()
2508 "Remove list identifies from the Subject header.
2509 The `gnus-list-identifiers' variable specifies what to do."
2510 (interactive)
2511 (let ((inhibit-point-motion-hooks t)
2512 (regexp (if (consp gnus-list-identifiers)
2513 (mapconcat 'identity gnus-list-identifiers " *\\|")
2514 gnus-list-identifiers))
2515 (inhibit-read-only t))
2516 (when regexp
2517 (save-excursion
2518 (save-restriction
2519 (article-narrow-to-head)
2520 (goto-char (point-min))
2521 (while (re-search-forward
2522 (concat "^Subject: +\\(R[Ee]: +\\)*\\(" regexp " *\\)")
2523 nil t)
2524 (delete-region (match-beginning 2) (match-end 0))
2525 (beginning-of-line))
2526 (when (re-search-forward
2527 "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t)
2528 (delete-region (match-beginning 1) (match-end 1))))))))
2529
2530 (defun article-hide-pem (&optional arg)
2531 "Toggle hiding of any PEM headers and signatures in the current article.
2532 If given a negative prefix, always show; if given a positive prefix,
2533 always hide."
2534 (interactive (gnus-article-hidden-arg))
2535 (unless (gnus-article-check-hidden-text 'pem arg)
2536 (save-excursion
2537 (let ((inhibit-read-only t) end)
2538 (goto-char (point-min))
2539 ;; Hide the horrendously ugly "header".
2540 (when (and (search-forward
2541 "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n"
2542 nil t)
2543 (setq end (1+ (match-beginning 0))))
2544 (gnus-add-wash-type 'pem)
2545 (gnus-article-hide-text-type
2546 end
2547 (if (search-forward "\n\n" nil t)
2548 (match-end 0)
2549 (point-max))
2550 'pem)
2551 ;; Hide the trailer as well
2552 (when (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n"
2553 nil t)
2554 (gnus-article-hide-text-type
2555 (match-beginning 0) (match-end 0) 'pem)))))))
2556
2557 (defun article-strip-banner ()
2558 "Strip the banners specified by the `banner' group parameter and by
2559 `gnus-article-address-banner-alist'."
2560 (interactive)
2561 (save-excursion
2562 (save-restriction
2563 (let ((inhibit-point-motion-hooks t))
2564 (when (gnus-parameter-banner gnus-newsgroup-name)
2565 (article-really-strip-banner
2566 (gnus-parameter-banner gnus-newsgroup-name)))
2567 (when gnus-article-address-banner-alist
2568 (article-really-strip-banner
2569 (let ((from (save-restriction
2570 (widen)
2571 (article-narrow-to-head)
2572 (mail-fetch-field "from"))))
2573 (when (and from
2574 (setq from
2575 (caar (mail-header-parse-addresses from))))
2576 (catch 'found
2577 (dolist (pair gnus-article-address-banner-alist)
2578 (when (string-match (car pair) from)
2579 (throw 'found (cdr pair)))))))))))))
2580
2581 (defun article-really-strip-banner (banner)
2582 "Strip the banner specified by the argument."
2583 (save-excursion
2584 (save-restriction
2585 (let ((inhibit-point-motion-hooks t)
2586 (gnus-signature-limit nil)
2587 (inhibit-read-only t))
2588 (article-goto-body)
2589 (cond
2590 ((eq banner 'signature)
2591 (when (gnus-article-narrow-to-signature)
2592 (widen)
2593 (forward-line -1)
2594 (delete-region (point) (point-max))))
2595 ((symbolp banner)
2596 (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
2597 (while (re-search-forward banner nil t)
2598 (delete-region (match-beginning 0) (match-end 0)))))
2599 ((stringp banner)
2600 (while (re-search-forward banner nil t)
2601 (delete-region (match-beginning 0) (match-end 0)))))))))
2602
2603 (defun article-babel ()
2604 "Translate article using an online translation service."
2605 (interactive)
2606 (require 'babel)
2607 (save-excursion
2608 (set-buffer gnus-article-buffer)
2609 (when (article-goto-body)
2610 (let* ((inhibit-read-only t)
2611 (start (point))
2612 (end (point-max))
2613 (orig (buffer-substring start end))
2614 (trans (babel-as-string orig)))
2615 (save-restriction
2616 (narrow-to-region start end)
2617 (delete-region start end)
2618 (insert trans))))))
2619
2620 (defun article-hide-signature (&optional arg)
2621 "Hide the signature in the current article.
2622 If given a negative prefix, always show; if given a positive prefix,
2623 always hide."
2624 (interactive (gnus-article-hidden-arg))
2625 (unless (gnus-article-check-hidden-text 'signature arg)
2626 (save-excursion
2627 (save-restriction
2628 (let ((inhibit-read-only t))
2629 (when (gnus-article-narrow-to-signature)
2630 (gnus-article-hide-text-type
2631 (point-min) (point-max) 'signature))))))
2632 (gnus-set-mode-line 'article))
2633
2634 (defun article-strip-headers-in-body ()
2635 "Strip offensive headers from bodies."
2636 (interactive)
2637 (save-excursion
2638 (article-goto-body)
2639 (let ((case-fold-search t))
2640 (when (looking-at "x-no-archive:")
2641 (gnus-delete-line)))))
2642
2643 (defun article-strip-leading-blank-lines ()
2644 "Remove all blank lines from the beginning of the article."
2645 (interactive)
2646 (save-excursion
2647 (let ((inhibit-point-motion-hooks t)
2648 (inhibit-read-only t))
2649 (when (article-goto-body)
2650 (while (and (not (eobp))
2651 (looking-at "[ \t]*$"))
2652 (gnus-delete-line))))))
2653
2654 (defun article-narrow-to-head ()
2655 "Narrow the buffer to the head of the message.
2656 Point is left at the beginning of the narrowed-to region."
2657 (narrow-to-region
2658 (goto-char (point-min))
2659 (if (search-forward "\n\n" nil 1)
2660 (1- (point))
2661 (point-max)))
2662 (goto-char (point-min)))
2663
2664 (defun article-goto-body ()
2665 "Place point at the start of the body."
2666 (goto-char (point-min))
2667 (cond
2668 ;; This variable is only bound when dealing with separate
2669 ;; MIME body parts.
2670 (article-goto-body-goes-to-point-min-p
2671 t)
2672 ((search-forward "\n\n" nil t)
2673 t)
2674 (t
2675 (goto-char (point-max))
2676 nil)))
2677
2678 (defun article-strip-multiple-blank-lines ()
2679 "Replace consecutive blank lines with one empty line."
2680 (interactive)
2681 (save-excursion
2682 (let ((inhibit-point-motion-hooks t)
2683 (inhibit-read-only t))
2684 ;; First make all blank lines empty.
2685 (article-goto-body)
2686 (while (re-search-forward "^[ \t]+$" nil t)
2687 (unless (gnus-annotation-in-region-p
2688 (match-beginning 0) (match-end 0))
2689 (replace-match "" nil t)))
2690 ;; Then replace multiple empty lines with a single empty line.
2691 (article-goto-body)
2692 (while (re-search-forward "\n\n\\(\n+\\)" nil t)
2693 (unless (gnus-annotation-in-region-p
2694 (match-beginning 0) (match-end 0))
2695 (delete-region (match-beginning 1) (match-end 1)))))))
2696
2697 (defun article-strip-leading-space ()
2698 "Remove all white space from the beginning of the lines in the article."
2699 (interactive)
2700 (save-excursion
2701 (let ((inhibit-point-motion-hooks t)
2702 (inhibit-read-only t))
2703 (article-goto-body)
2704 (while (re-search-forward "^[ \t]+" nil t)
2705 (replace-match "" t t)))))
2706
2707 (defun article-strip-trailing-space ()
2708 "Remove all white space from the end of the lines in the article."
2709 (interactive)
2710 (save-excursion
2711 (let ((inhibit-point-motion-hooks t)
2712 (inhibit-read-only t))
2713 (article-goto-body)
2714 (while (re-search-forward "[ \t]+$" nil t)
2715 (replace-match "" t t)))))
2716
2717 (defun article-strip-blank-lines ()
2718 "Strip leading, trailing and multiple blank lines."
2719 (interactive)
2720 (article-strip-leading-blank-lines)
2721 (article-remove-trailing-blank-lines)
2722 (article-strip-multiple-blank-lines))
2723
2724 (defun article-strip-all-blank-lines ()
2725 "Strip all blank lines."
2726 (interactive)
2727 (save-excursion
2728 (let ((inhibit-point-motion-hooks t)
2729 (inhibit-read-only t))
2730 (article-goto-body)
2731 (while (re-search-forward "^[ \t]*\n" nil t)
2732 (replace-match "" t t)))))
2733
2734 (defun gnus-article-narrow-to-signature ()
2735 "Narrow to the signature; return t if a signature is found, else nil."
2736 (let ((inhibit-point-motion-hooks t))
2737 (when (gnus-article-search-signature)
2738 (forward-line 1)
2739 ;; Check whether we have some limits to what we consider
2740 ;; to be a signature.
2741 (let ((limits (if (listp gnus-signature-limit) gnus-signature-limit
2742 (list gnus-signature-limit)))
2743 limit limited)
2744 (while (setq limit (pop limits))
2745 (if (or (and (integerp limit)
2746 (< (- (point-max) (point)) limit))
2747 (and (floatp limit)
2748 (< (count-lines (point) (point-max)) limit))
2749 (and (functionp limit)
2750 (funcall limit))
2751 (and (stringp limit)
2752 (not (re-search-forward limit nil t))))
2753 () ; This limit did not succeed.
2754 (setq limited t
2755 limits nil)))
2756 (unless limited
2757 (narrow-to-region (point) (point-max))
2758 t)))))
2759
2760 (defun gnus-article-search-signature ()
2761 "Search the current buffer for the signature separator.
2762 Put point at the beginning of the signature separator."
2763 (let ((cur (point)))
2764 (goto-char (point-max))
2765 (if (if (stringp gnus-signature-separator)
2766 (re-search-backward gnus-signature-separator nil t)
2767 (let ((seps gnus-signature-separator))
2768 (while (and seps
2769 (not (re-search-backward (car seps) nil t)))
2770 (pop seps))
2771 seps))
2772 t
2773 (goto-char cur)
2774 nil)))
2775
2776 (defun gnus-article-hidden-arg ()
2777 "Return the current prefix arg as a number, or 0 if no prefix."
2778 (list (if current-prefix-arg
2779 (prefix-numeric-value current-prefix-arg)
2780 0)))
2781
2782 (defun gnus-article-check-hidden-text (type arg)
2783 "Return nil if hiding is necessary.
2784 Arg can be nil or a number. nil and positive means hide, negative
2785 means show, 0 means toggle."
2786 (save-excursion
2787 (save-restriction
2788 (let ((hide (gnus-article-hidden-text-p type)))
2789 (cond
2790 ((or (null arg)
2791 (> arg 0))
2792 nil)
2793 ((< arg 0)
2794 (gnus-article-show-hidden-text type)
2795 t)
2796 (t
2797 (if (eq hide 'hidden)
2798 (progn
2799 (gnus-article-show-hidden-text type)
2800 t)
2801 nil)))))))
2802
2803 (defun gnus-article-hidden-text-p (type)
2804 "Say whether the current buffer contains hidden text of type TYPE."
2805 (let ((pos (text-property-any (point-min) (point-max) 'article-type type)))
2806 (while (and pos
2807 (not (get-text-property pos 'invisible))
2808 (not (get-text-property pos 'dummy-invisible)))
2809 (setq pos
2810 (text-property-any (1+ pos) (point-max) 'article-type type)))
2811 (if pos
2812 'hidden
2813 nil)))
2814
2815 (defun gnus-article-show-hidden-text (type &optional dummy)
2816 "Show all hidden text of type TYPE.
2817 Originally it is hide instead of DUMMY."
2818 (let ((inhibit-read-only t)
2819 (inhibit-point-motion-hooks t))
2820 (gnus-remove-text-properties-when
2821 'article-type type
2822 (point-min) (point-max)
2823 (cons 'article-type (cons type
2824 gnus-hidden-properties)))
2825 (gnus-delete-wash-type type)))
2826
2827 (defconst article-time-units
2828 `((year . ,(* 365.25 24 60 60))
2829 (week . ,(* 7 24 60 60))
2830 (day . ,(* 24 60 60))
2831 (hour . ,(* 60 60))
2832 (minute . 60)
2833 (second . 1))
2834 "Mapping from time units to seconds.")
2835
2836 (defun gnus-article-forward-header ()
2837 "Move point to the start of the next header.
2838 If the current header is a continuation header, this can be several
2839 lines forward."
2840 (let ((ended nil))
2841 (while (not ended)
2842 (forward-line 1)
2843 (if (looking-at "[ \t]+[^ \t]")
2844 (forward-line 1)
2845 (setq ended t)))))
2846
2847 (defun article-date-ut (&optional type highlight)
2848 "Convert DATE date to universal time in the current article.
2849 If TYPE is `local', convert to local time; if it is `lapsed', output
2850 how much time has lapsed since DATE. For `lapsed', the value of
2851 `gnus-article-date-lapsed-new-header' says whether the \"X-Sent:\" header
2852 should replace the \"Date:\" one, or should be added below it."
2853 (interactive (list 'ut t))
2854 (let* ((tdate-regexp "^Date:[ \t]\\|^X-Sent:[ \t]")
2855 (date-regexp (cond ((not gnus-article-date-lapsed-new-header)
2856 tdate-regexp)
2857 ((eq type 'lapsed)
2858 "^X-Sent:[ \t]")
2859 (article-lapsed-timer
2860 "^Date:[ \t]")
2861 (t
2862 tdate-regexp)))
2863 (case-fold-search t)
2864 (inhibit-read-only t)
2865 (inhibit-point-motion-hooks t)
2866 pos date bface eface)
2867 (save-excursion
2868 (save-restriction
2869 (widen)
2870 (goto-char (point-min))
2871 (while (or (setq date (get-text-property (setq pos (point))
2872 'original-date))
2873 (when (setq pos (next-single-property-change
2874 (point) 'original-date))
2875 (setq date (get-text-property pos 'original-date))
2876 t))
2877 (narrow-to-region pos (or (text-property-any pos (point-max)
2878 'original-date nil)
2879 (point-max)))
2880 (goto-char (point-min))
2881 (when (re-search-forward tdate-regexp nil t)
2882 (setq bface (get-text-property (gnus-point-at-bol) 'face)
2883 eface (get-text-property (1- (gnus-point-at-eol)) 'face)))
2884 (goto-char (point-min))
2885 (setq pos nil)
2886 ;; Delete any old Date headers.
2887 (while (re-search-forward date-regexp nil t)
2888 (if pos
2889 (delete-region (gnus-point-at-bol)
2890 (progn
2891 (gnus-article-forward-header)
2892 (point)))
2893 (delete-region (gnus-point-at-bol)
2894 (progn
2895 (gnus-article-forward-header)
2896 (forward-char -1)
2897 (point)))
2898 (setq pos (point))))
2899 (when (and (not pos)
2900 (re-search-forward tdate-regexp nil t))
2901 (forward-line 1))
2902 (gnus-goto-char pos)
2903 (insert (article-make-date-line date (or type 'ut)))
2904 (unless pos
2905 (insert "\n")
2906 (forward-line -1))
2907 ;; Do highlighting.
2908 (beginning-of-line)
2909 (when (looking-at "\\([^:]+\\): *\\(.*\\)$")
2910 (put-text-property (match-beginning 1) (1+ (match-end 1))
2911 'face bface)
2912 (put-text-property (match-beginning 2) (match-end 2)
2913 'face eface))
2914 (put-text-property (point-min) (1- (point-max)) 'original-date date)
2915 (goto-char (point-max))
2916 (widen))))))
2917
2918 (defun article-make-date-line (date type)
2919 "Return a DATE line of TYPE."
2920 (unless (memq type '(local ut original user iso8601 lapsed english))
2921 (error "Unknown conversion type: %s" type))
2922 (condition-case ()
2923 (let ((time (date-to-time date)))
2924 (cond
2925 ;; Convert to the local timezone.
2926 ((eq type 'local)
2927 (let ((tz (car (current-time-zone time))))
2928 (format "Date: %s %s%02d%02d" (current-time-string time)
2929 (if (> tz 0) "+" "-") (/ (abs tz) 3600)
2930 (/ (% (abs tz) 3600) 60))))
2931 ;; Convert to Universal Time.
2932 ((eq type 'ut)
2933 (concat "Date: "
2934 (current-time-string
2935 (let* ((e (parse-time-string date))
2936 (tm (apply 'encode-time e))
2937 (ms (car tm))
2938 (ls (- (cadr tm) (car (current-time-zone time)))))
2939 (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
2940 ((> ls 65535) (list (1+ ms) (- ls 65536)))
2941 (t (list ms ls)))))
2942 " UT"))
2943 ;; Get the original date from the article.
2944 ((eq type 'original)
2945 (concat "Date: " (if (string-match "\n+$" date)
2946 (substring date 0 (match-beginning 0))
2947 date)))
2948 ;; Let the user define the format.
2949 ((eq type 'user)
2950 (let ((format (or (condition-case nil
2951 (with-current-buffer gnus-summary-buffer
2952 gnus-article-time-format)
2953 (error nil))
2954 gnus-article-time-format)))
2955 (if (functionp format)
2956 (funcall format time)
2957 (concat "Date: " (format-time-string format time)))))
2958 ;; ISO 8601.
2959 ((eq type 'iso8601)
2960 (let ((tz (car (current-time-zone time))))
2961 (concat
2962 "Date: "
2963 (format-time-string "%Y%m%dT%H%M%S" time)
2964 (format "%s%02d%02d"
2965 (if (> tz 0) "+" "-") (/ (abs tz) 3600)
2966 (/ (% (abs tz) 3600) 60)))))
2967 ;; Do an X-Sent lapsed format.
2968 ((eq type 'lapsed)
2969 ;; If the date is seriously mangled, the timezone functions are
2970 ;; liable to bug out, so we ignore all errors.
2971 (let* ((now (current-time))
2972 (real-time (subtract-time now time))
2973 (real-sec (and real-time
2974 (+ (* (float (car real-time)) 65536)
2975 (cadr real-time))))
2976 (sec (and real-time (abs real-sec)))
2977 num prev)
2978 (cond
2979 ((null real-time)
2980 "X-Sent: Unknown")
2981 ((zerop sec)
2982 "X-Sent: Now")
2983 (t
2984 (concat
2985 "X-Sent: "
2986 ;; This is a bit convoluted, but basically we go
2987 ;; through the time units for years, weeks, etc,
2988 ;; and divide things to see whether that results
2989 ;; in positive answers.
2990 (mapconcat
2991 (lambda (unit)
2992 (if (zerop (setq num (ffloor (/ sec (cdr unit)))))
2993 ;; The (remaining) seconds are too few to
2994 ;; be divided into this time unit.
2995 ""
2996 ;; It's big enough, so we output it.
2997 (setq sec (- sec (* num (cdr unit))))
2998 (prog1
2999 (concat (if prev ", " "") (int-to-string
3000 (floor num))
3001 " " (symbol-name (car unit))
3002 (if (> num 1) "s" ""))
3003 (setq prev t))))
3004 article-time-units "")
3005 ;; If dates are odd, then it might appear like the
3006 ;; article was sent in the future.
3007 (if (> real-sec 0)
3008 " ago"
3009 " in the future"))))))
3010 ;; Display the date in proper English
3011 ((eq type 'english)
3012 (let ((dtime (decode-time time)))
3013 (concat
3014 "Date: the "
3015 (number-to-string (nth 3 dtime))
3016 (let ((digit (% (nth 3 dtime) 10)))
3017 (cond
3018 ((memq (nth 3 dtime) '(11 12 13)) "th")
3019 ((= digit 1) "st")
3020 ((= digit 2) "nd")
3021 ((= digit 3) "rd")
3022 (t "th")))
3023 " of "
3024 (nth (1- (nth 4 dtime)) gnus-english-month-names)
3025 " "
3026 (number-to-string (nth 5 dtime))
3027 " at "
3028 (format "%02d" (nth 2 dtime))
3029 ":"
3030 (format "%02d" (nth 1 dtime)))))))
3031 (error
3032 (format "Date: %s (from Gnus)" date))))
3033
3034 (defun article-date-local (&optional highlight)
3035 "Convert the current article date to the local timezone."
3036 (interactive (list t))
3037 (article-date-ut 'local highlight))
3038
3039 (defun article-date-english (&optional highlight)
3040 "Convert the current article date to something that is proper English."
3041 (interactive (list t))
3042 (article-date-ut 'english highlight))
3043
3044 (defun article-date-original (&optional highlight)
3045 "Convert the current article date to what it was originally.
3046 This is only useful if you have used some other date conversion
3047 function and want to see what the date was before converting."
3048 (interactive (list t))
3049 (article-date-ut 'original highlight))
3050
3051 (defun article-date-lapsed (&optional highlight)
3052 "Convert the current article date to time lapsed since it was sent."
3053 (interactive (list t))
3054 (article-date-ut 'lapsed highlight))
3055
3056 (defun article-update-date-lapsed ()
3057 "Function to be run from a timer to update the lapsed time line."
3058 (save-match-data
3059 (let (deactivate-mark)
3060 (save-excursion
3061 (ignore-errors
3062 (walk-windows
3063 (lambda (w)
3064 (set-buffer (window-buffer w))
3065 (when (eq major-mode 'gnus-article-mode)
3066 (let ((mark (point-marker)))
3067 (goto-char (point-min))
3068 (when (re-search-forward "^X-Sent:" nil t)
3069 (article-date-lapsed t))
3070 (goto-char (marker-position mark))
3071 (move-marker mark nil))))
3072 nil 'visible))))))
3073
3074 (defun gnus-start-date-timer (&optional n)
3075 "Start a timer to update the X-Sent header in the article buffers.
3076 The numerical prefix says how frequently (in seconds) the function
3077 is to run."
3078 (interactive "p")
3079 (unless n
3080 (setq n 1))
3081 (gnus-stop-date-timer)
3082 (setq article-lapsed-timer
3083 (nnheader-run-at-time 1 n 'article-update-date-lapsed)))
3084
3085 (defun gnus-stop-date-timer ()
3086 "Stop the X-Sent timer."
3087 (interactive)
3088 (when article-lapsed-timer
3089 (nnheader-cancel-timer article-lapsed-timer)
3090 (setq article-lapsed-timer nil)))
3091
3092 (defun article-date-user (&optional highlight)
3093 "Convert the current article date to the user-defined format.
3094 This format is defined by the `gnus-article-time-format' variable."
3095 (interactive (list t))
3096 (article-date-ut 'user highlight))
3097
3098 (defun article-date-iso8601 (&optional highlight)
3099 "Convert the current article date to ISO8601."
3100 (interactive (list t))
3101 (article-date-ut 'iso8601 highlight))
3102
3103 (defmacro gnus-article-save-original-date (&rest forms)
3104 "Save the original date as a text property and evaluate FORMS."
3105 `(let* ((case-fold-search t)
3106 (start (progn
3107 (goto-char (point-min))
3108 (when (and (re-search-forward "^date:[\t\n ]+" nil t)
3109 (not (bolp)))
3110 (match-end 0))))
3111 (date (when (and start
3112 (re-search-forward "[\t ]*\n\\([^\t ]\\|\\'\\)"
3113 nil t))
3114 (buffer-substring-no-properties start
3115 (match-beginning 0)))))
3116 (goto-char (point-max))
3117 (skip-chars-backward "\n")
3118 (put-text-property (point-min) (point) 'original-date date)
3119 ,@forms
3120 (goto-char (point-max))
3121 (skip-chars-backward "\n")
3122 (put-text-property (point-min) (point) 'original-date date)))
3123
3124 ;; (defun article-show-all ()
3125 ;; "Show all hidden text in the article buffer."
3126 ;; (interactive)
3127 ;; (save-excursion
3128 ;; (let ((inhibit-read-only t))
3129 ;; (gnus-article-unhide-text (point-min) (point-max)))))
3130
3131 (defun article-remove-leading-whitespace ()
3132 "Remove excessive whitespace from all headers."
3133 (interactive)
3134 (save-excursion
3135 (save-restriction
3136 (let ((inhibit-read-only t))
3137 (article-narrow-to-head)
3138 (goto-char (point-min))
3139 (while (re-search-forward "^[^ :]+: \\([ \t]+\\)" nil t)
3140 (delete-region (match-beginning 1) (match-end 1)))))))
3141
3142 (defun article-emphasize (&optional arg)
3143 "Emphasize text according to `gnus-emphasis-alist'."
3144 (interactive (gnus-article-hidden-arg))
3145 (unless (gnus-article-check-hidden-text 'emphasis arg)
3146 (save-excursion
3147 (let ((alist (or
3148 (condition-case nil
3149 (with-current-buffer gnus-summary-buffer
3150 gnus-article-emphasis-alist)
3151 (error))
3152 gnus-emphasis-alist))
3153 (inhibit-read-only t)
3154 (props (append '(article-type emphasis)
3155 gnus-hidden-properties))
3156 regexp elem beg invisible visible face)
3157 (article-goto-body)
3158 (setq beg (point))
3159 (while (setq elem (pop alist))
3160 (goto-char beg)
3161 (setq regexp (car elem)
3162 invisible (nth 1 elem)
3163 visible (nth 2 elem)
3164 face (nth 3 elem))
3165 (while (re-search-forward regexp nil t)
3166 (when (and (match-beginning visible) (match-beginning invisible))
3167 (gnus-article-hide-text
3168 (match-beginning invisible) (match-end invisible) props)
3169 (gnus-article-unhide-text-type
3170 (match-beginning visible) (match-end visible) 'emphasis)
3171 (gnus-put-overlay-excluding-newlines
3172 (match-beginning visible) (match-end visible) 'face face)
3173 (gnus-add-wash-type 'emphasis)
3174 (goto-char (match-end invisible)))))))))
3175
3176 (defun gnus-article-setup-highlight-words (&optional highlight-words)
3177 "Setup newsgroup emphasis alist."
3178 (unless gnus-article-emphasis-alist
3179 (let ((name (and gnus-newsgroup-name
3180 (gnus-group-real-name gnus-newsgroup-name))))
3181 (make-local-variable 'gnus-article-emphasis-alist)
3182 (setq gnus-article-emphasis-alist
3183 (nconc
3184 (let ((alist gnus-group-highlight-words-alist) elem highlight)
3185 (while (setq elem (pop alist))
3186 (when (and name (string-match (car elem) name))
3187 (setq alist nil
3188 highlight (copy-sequence (cdr elem)))))
3189 highlight)
3190 (copy-sequence highlight-words)
3191 (if gnus-newsgroup-name
3192 (copy-sequence (gnus-group-find-parameter
3193 gnus-newsgroup-name 'highlight-words t)))
3194 gnus-emphasis-alist)))))
3195
3196 (eval-when-compile
3197 (defvar gnus-summary-article-menu)
3198 (defvar gnus-summary-post-menu))
3199
3200 ;;; Saving functions.
3201
3202 (defun gnus-article-save (save-buffer file &optional num)
3203 "Save the currently selected article."
3204 (unless gnus-save-all-headers
3205 ;; Remove headers according to `gnus-saved-headers'.
3206 (let ((gnus-visible-headers
3207 (or gnus-saved-headers gnus-visible-headers))
3208 (gnus-article-buffer save-buffer))
3209 (save-excursion
3210 (set-buffer save-buffer)
3211 (article-hide-headers 1 t))))
3212 (save-window-excursion
3213 (if (not gnus-default-article-saver)
3214 (error "No default saver is defined")
3215 ;; !!! Magic! The saving functions all save
3216 ;; `gnus-save-article-buffer' (or so they think), but we
3217 ;; bind that variable to our save-buffer.
3218 (set-buffer gnus-article-buffer)
3219 (let* ((gnus-save-article-buffer save-buffer)
3220 (filename
3221 (cond
3222 ((not gnus-prompt-before-saving) 'default)
3223 ((eq gnus-prompt-before-saving 'always) nil)
3224 (t file)))
3225 (gnus-number-of-articles-to-be-saved
3226 (when (eq gnus-prompt-before-saving t)
3227 num))) ; Magic
3228 (set-buffer gnus-article-current-summary)
3229 (funcall gnus-default-article-saver filename)))))
3230
3231 (defun gnus-read-save-file-name (prompt &optional filename
3232 function group headers variable)
3233 (let ((default-name
3234 (funcall function group headers (symbol-value variable)))
3235 result)
3236 (setq result
3237 (expand-file-name
3238 (cond
3239 ((eq filename 'default)
3240 default-name)
3241 ((eq filename t)
3242 default-name)
3243 (filename filename)
3244 (t
3245 (let* ((split-name (gnus-get-split-value gnus-split-methods))
3246 (prompt
3247 (format prompt
3248 (if (and gnus-number-of-articles-to-be-saved
3249 (> gnus-number-of-articles-to-be-saved 1))
3250 (format "these %d articles"
3251 gnus-number-of-articles-to-be-saved)
3252 "this article")))
3253 (file
3254 ;; Let the split methods have their say.
3255 (cond
3256 ;; No split name was found.
3257 ((null split-name)
3258 (read-file-name
3259 (concat prompt " (default "
3260 (file-name-nondirectory default-name) "): ")
3261 (file-name-directory default-name)
3262 default-name))
3263 ;; A single group name is returned.
3264 ((stringp split-name)
3265 (setq default-name
3266 (funcall function split-name headers
3267 (symbol-value variable)))
3268 (read-file-name
3269 (concat prompt " (default "
3270 (file-name-nondirectory default-name) "): ")
3271 (file-name-directory default-name)
3272 default-name))
3273 ;; A single split name was found
3274 ((= 1 (length split-name))
3275 (let* ((name (expand-file-name
3276 (car split-name)
3277 gnus-article-save-directory))
3278 (dir (cond ((file-directory-p name)
3279 (file-name-as-directory name))
3280 ((file-exists-p name) name)
3281 (t gnus-article-save-directory))))
3282 (read-file-name
3283 (concat prompt " (default " name "): ")
3284 dir name)))
3285 ;; A list of splits was found.
3286 (t
3287 (setq split-name (nreverse split-name))
3288 (let (result)
3289 (let ((file-name-history
3290 (nconc split-name file-name-history)))
3291 (setq result
3292 (expand-file-name
3293 (read-file-name
3294 (concat prompt " (`M-p' for defaults): ")
3295 gnus-article-save-directory
3296 (car split-name))
3297 gnus-article-save-directory)))
3298 (car (push result file-name-history)))))))
3299 ;; Create the directory.
3300 (gnus-make-directory (file-name-directory file))
3301 ;; If we have read a directory, we append the default file name.
3302 (when (file-directory-p file)
3303 (setq file (expand-file-name (file-name-nondirectory
3304 default-name)
3305 (file-name-as-directory file))))
3306 ;; Possibly translate some characters.
3307 (nnheader-translate-file-chars file))))))
3308 (gnus-make-directory (file-name-directory result))
3309 (set variable result)))
3310
3311 (defun gnus-article-archive-name (group)
3312 "Return the first instance of an \"Archive-name\" in the current buffer."
3313 (let ((case-fold-search t))
3314 (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
3315 (nnheader-concat gnus-article-save-directory
3316 (match-string 1)))))
3317
3318 (defun gnus-article-nndoc-name (group)
3319 "If GROUP is an nndoc group, return the name of the parent group."
3320 (when (eq (car (gnus-find-method-for-group group)) 'nndoc)
3321 (gnus-group-get-parameter group 'save-article-group)))
3322
3323 (defun gnus-summary-save-in-rmail (&optional filename)
3324 "Append this article to Rmail file.
3325 Optional argument FILENAME specifies file name.
3326 Directory to save to is default to `gnus-article-save-directory'."
3327 (setq filename (gnus-read-save-file-name
3328 "Save %s in rmail file" filename
3329 gnus-rmail-save-name gnus-newsgroup-name
3330 gnus-current-headers 'gnus-newsgroup-last-rmail))
3331 (gnus-eval-in-buffer-window gnus-save-article-buffer
3332 (save-excursion
3333 (save-restriction
3334 (widen)
3335 (gnus-output-to-rmail filename))))
3336 filename)
3337
3338 (defun gnus-summary-save-in-mail (&optional filename)
3339 "Append this article to Unix mail file.
3340 Optional argument FILENAME specifies file name.
3341 Directory to save to is default to `gnus-article-save-directory'."
3342 (setq filename (gnus-read-save-file-name
3343 "Save %s in Unix mail file" filename
3344 gnus-mail-save-name gnus-newsgroup-name
3345 gnus-current-headers 'gnus-newsgroup-last-mail))
3346 (gnus-eval-in-buffer-window gnus-save-article-buffer
3347 (save-excursion
3348 (save-restriction
3349 (widen)
3350 (if (and (file-readable-p filename)
3351 (file-regular-p filename)
3352 (mail-file-babyl-p filename))
3353 (rmail-output-to-rmail-file filename t)
3354 (gnus-output-to-mail filename)))))
3355 filename)
3356
3357 (defun gnus-summary-save-in-file (&optional filename overwrite)
3358 "Append this article to file.
3359 Optional argument FILENAME specifies file name.
3360 Directory to save to is default to `gnus-article-save-directory'."
3361 (setq filename (gnus-read-save-file-name
3362 "Save %s in file" filename
3363 gnus-file-save-name gnus-newsgroup-name
3364 gnus-current-headers 'gnus-newsgroup-last-file))
3365 (gnus-eval-in-buffer-window gnus-save-article-buffer
3366 (save-excursion
3367 (save-restriction
3368 (widen)
3369 (when (and overwrite
3370 (file-exists-p filename))
3371 (delete-file filename))
3372 (gnus-output-to-file filename))))
3373 filename)
3374
3375 (defun gnus-summary-write-to-file (&optional filename)
3376 "Write this article to a file, overwriting it if the file exists.
3377 Optional argument FILENAME specifies file name.
3378 The directory to save in defaults to `gnus-article-save-directory'."
3379 (gnus-summary-save-in-file nil t))
3380
3381 (defun gnus-summary-save-body-in-file (&optional filename)
3382 "Append this article body to a file.
3383 Optional argument FILENAME specifies file name.
3384 The directory to save in defaults to `gnus-article-save-directory'."
3385 (setq filename (gnus-read-save-file-name
3386 "Save %s body in file" filename
3387 gnus-file-save-name gnus-newsgroup-name
3388 gnus-current-headers 'gnus-newsgroup-last-file))
3389 (gnus-eval-in-buffer-window gnus-save-article-buffer
3390 (save-excursion
3391 (save-restriction
3392 (widen)
3393 (when (article-goto-body)
3394 (narrow-to-region (point) (point-max)))
3395 (gnus-output-to-file filename))))
3396 filename)
3397
3398 (defun gnus-summary-save-in-pipe (&optional command)
3399 "Pipe this article to subprocess."
3400 (setq command
3401 (cond ((and (eq command 'default)
3402 gnus-last-shell-command)
3403 gnus-last-shell-command)
3404 ((stringp command)
3405 command)
3406 (t (read-string
3407 (format
3408 "Shell command on %s: "
3409 (if (and gnus-number-of-articles-to-be-saved
3410 (> gnus-number-of-articles-to-be-saved 1))
3411 (format "these %d articles"
3412 gnus-number-of-articles-to-be-saved)
3413 "this article"))
3414 gnus-last-shell-command))))
3415 (when (string-equal command "")
3416 (if gnus-last-shell-command
3417 (setq command gnus-last-shell-command)
3418 (error "A command is required")))
3419 (gnus-eval-in-buffer-window gnus-article-buffer
3420 (save-restriction
3421 (widen)
3422 (shell-command-on-region (point-min) (point-max) command nil)))
3423 (setq gnus-last-shell-command command))
3424
3425 (defmacro gnus-read-string (prompt &optional initial-contents history
3426 default-value)
3427 "Like `read-string' but allow for older XEmacsen that don't have the 5th arg."
3428 (if (and (featurep 'xemacs)
3429 (< emacs-minor-version 2))
3430 `(read-string ,prompt ,initial-contents ,history)
3431 `(read-string ,prompt ,initial-contents ,history ,default-value)))
3432
3433 (defun gnus-summary-pipe-to-muttprint (&optional command)
3434 "Pipe this article to muttprint."
3435 (setq command (gnus-read-string
3436 "Print using command: " gnus-summary-muttprint-program
3437 nil gnus-summary-muttprint-program))
3438 (gnus-summary-save-in-pipe command))
3439
3440 ;;; Article file names when saving.
3441
3442 (defun gnus-capitalize-newsgroup (newsgroup)
3443 "Capitalize NEWSGROUP name."
3444 (when (not (zerop (length newsgroup)))
3445 (concat (char-to-string (upcase (aref newsgroup 0)))
3446 (substring newsgroup 1))))
3447
3448 (defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
3449 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3450 If variable `gnus-use-long-file-name' is non-nil, it is ~/News/News.group/num.
3451 Otherwise, it is like ~/News/news/group/num."
3452 (let ((default
3453 (expand-file-name
3454 (concat (if (gnus-use-long-file-name 'not-save)
3455 (gnus-capitalize-newsgroup newsgroup)
3456 (gnus-newsgroup-directory-form newsgroup))
3457 "/" (int-to-string (mail-header-number headers)))
3458 gnus-article-save-directory)))
3459 (if (and last-file
3460 (string-equal (file-name-directory default)
3461 (file-name-directory last-file))
3462 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3463 default
3464 (or last-file default))))
3465
3466 (defun gnus-numeric-save-name (newsgroup headers &optional last-file)
3467 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3468 If variable `gnus-use-long-file-name' is non-nil, it is
3469 ~/News/news.group/num. Otherwise, it is like ~/News/news/group/num."
3470 (let ((default
3471 (expand-file-name
3472 (concat (if (gnus-use-long-file-name 'not-save)
3473 newsgroup
3474 (gnus-newsgroup-directory-form newsgroup))
3475 "/" (int-to-string (mail-header-number headers)))
3476 gnus-article-save-directory)))
3477 (if (and last-file
3478 (string-equal (file-name-directory default)
3479 (file-name-directory last-file))
3480 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3481 default
3482 (or last-file default))))
3483
3484 (defun gnus-plain-save-name (newsgroup headers &optional last-file)
3485 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3486 If variable `gnus-use-long-file-name' is non-nil, it is
3487 ~/News/news.group. Otherwise, it is like ~/News/news/group/news."
3488 (or last-file
3489 (expand-file-name
3490 (if (gnus-use-long-file-name 'not-save)
3491 newsgroup
3492 (file-relative-name
3493 (expand-file-name "news" (gnus-newsgroup-directory-form newsgroup))
3494 default-directory))
3495 gnus-article-save-directory)))
3496
3497 (defun gnus-sender-save-name (newsgroup headers &optional last-file)
3498 "Generate file name from sender."
3499 (let ((from (mail-header-from headers)))
3500 (expand-file-name
3501 (if (and from (string-match "\\([^ <]+\\)@" from))
3502 (match-string 1 from)
3503 "nobody")
3504 gnus-article-save-directory)))
3505
3506 (defun article-verify-x-pgp-sig ()
3507 "Verify X-PGP-Sig."
3508 (interactive)
3509 (if (gnus-buffer-live-p gnus-original-article-buffer)
3510 (let ((sig (with-current-buffer gnus-original-article-buffer
3511 (gnus-fetch-field "X-PGP-Sig")))
3512 items info headers)
3513 (when (and sig
3514 mml2015-use
3515 (mml2015-clear-verify-function))
3516 (with-temp-buffer
3517 (insert-buffer-substring gnus-original-article-buffer)
3518 (setq items (split-string sig))
3519 (message-narrow-to-head)
3520 (let ((inhibit-point-motion-hooks t)
3521 (case-fold-search t))
3522 ;; Don't verify multiple headers.
3523 (setq headers (mapconcat (lambda (header)
3524 (concat header ": "
3525 (mail-fetch-field header)
3526 "\n"))
3527 (split-string (nth 1 items) ",") "")))
3528 (delete-region (point-min) (point-max))
3529 (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
3530 (insert "X-Signed-Headers: " (nth 1 items) "\n")
3531 (insert headers)
3532 (widen)
3533 (forward-line)
3534 (while (not (eobp))
3535 (if (looking-at "^-")
3536 (insert "- "))
3537 (forward-line))
3538 (insert "\n-----BEGIN PGP SIGNATURE-----\n")
3539 (insert "Version: " (car items) "\n\n")
3540 (insert (mapconcat 'identity (cddr items) "\n"))
3541 (insert "\n-----END PGP SIGNATURE-----\n")
3542 (let ((mm-security-handle (list (format "multipart/signed"))))
3543 (mml2015-clean-buffer)
3544 (let ((coding-system-for-write (or gnus-newsgroup-charset
3545 'iso-8859-1)))
3546 (funcall (mml2015-clear-verify-function)))
3547 (setq info
3548 (or (mm-handle-multipart-ctl-parameter
3549 mm-security-handle 'gnus-details)
3550 (mm-handle-multipart-ctl-parameter
3551 mm-security-handle 'gnus-info)))))
3552 (when info
3553 (let ((inhibit-read-only t) bface eface)
3554 (save-restriction
3555 (message-narrow-to-head)
3556 (goto-char (point-max))
3557 (forward-line -1)
3558 (setq bface (get-text-property (gnus-point-at-bol) 'face)
3559 eface (get-text-property (1- (gnus-point-at-eol)) 'face))
3560 (message-remove-header "X-Gnus-PGP-Verify")
3561 (if (re-search-forward "^X-PGP-Sig:" nil t)
3562 (forward-line)
3563 (goto-char (point-max)))
3564 (narrow-to-region (point) (point))
3565 (insert "X-Gnus-PGP-Verify: " info "\n")
3566 (goto-char (point-min))
3567 (forward-line)
3568 (while (not (eobp))
3569 (if (not (looking-at "^[ \t]"))
3570 (insert " "))
3571 (forward-line))
3572 ;; Do highlighting.
3573 (goto-char (point-min))
3574 (when (looking-at "\\([^:]+\\): *")
3575 (put-text-property (match-beginning 1) (1+ (match-end 1))
3576 'face bface)
3577 (put-text-property (match-end 0) (point-max)
3578 'face eface)))))))))
3579
3580 (defun article-verify-cancel-lock ()
3581 "Verify Cancel-Lock header."
3582 (interactive)
3583 (if (gnus-buffer-live-p gnus-original-article-buffer)
3584 (canlock-verify gnus-original-article-buffer)))
3585
3586 (eval-and-compile
3587 (mapcar
3588 (lambda (func)
3589 (let (afunc gfunc)
3590 (if (consp func)
3591 (setq afunc (car func)
3592 gfunc (cdr func))
3593 (setq afunc func
3594 gfunc (intern (format "gnus-%s" func))))
3595 (defalias gfunc
3596 (when (fboundp afunc)
3597 `(lambda (&optional interactive &rest args)
3598 ,(documentation afunc t)
3599 (interactive (list t))
3600 (save-excursion
3601 (set-buffer gnus-article-buffer)
3602 (if interactive
3603 (call-interactively ',afunc)
3604 (apply ',afunc args))))))))
3605 '(article-hide-headers
3606 article-verify-x-pgp-sig
3607 article-verify-cancel-lock
3608 article-hide-boring-headers
3609 article-treat-overstrike
3610 article-fill-long-lines
3611 article-capitalize-sentences
3612 article-remove-cr
3613 article-remove-leading-whitespace
3614 article-display-x-face
3615 article-display-face
3616 article-de-quoted-unreadable
3617 article-de-base64-unreadable
3618 article-decode-HZ
3619 article-wash-html
3620 article-unsplit-urls
3621 article-hide-list-identifiers
3622 article-strip-banner
3623 article-babel
3624 article-hide-pem
3625 article-hide-signature
3626 article-strip-headers-in-body
3627 article-remove-trailing-blank-lines
3628 article-strip-leading-blank-lines
3629 article-strip-multiple-blank-lines
3630 article-strip-leading-space
3631 article-strip-trailing-space
3632 article-strip-blank-lines
3633 article-strip-all-blank-lines
3634 article-date-local
3635 article-date-english
3636 article-date-iso8601
3637 article-date-original
3638 article-date-ut
3639 article-decode-mime-words
3640 article-decode-charset
3641 article-decode-encoded-words
3642 article-date-user
3643 article-date-lapsed
3644 article-emphasize
3645 article-treat-dumbquotes
3646 article-normalize-headers
3647 ;; (article-show-all . gnus-article-show-all-headers)
3648 )))
3649 \f
3650 ;;;
3651 ;;; Gnus article mode
3652 ;;;
3653
3654 (put 'gnus-article-mode 'mode-class 'special)
3655
3656 (set-keymap-parent gnus-article-mode-map widget-keymap)
3657
3658 (gnus-define-keys gnus-article-mode-map
3659 " " gnus-article-goto-next-page
3660 "\177" gnus-article-goto-prev-page
3661 [delete] gnus-article-goto-prev-page
3662 [backspace] gnus-article-goto-prev-page
3663 "\C-c^" gnus-article-refer-article
3664 "h" gnus-article-show-summary
3665 "s" gnus-article-show-summary
3666 "\C-c\C-m" gnus-article-mail
3667 "?" gnus-article-describe-briefly
3668 "e" gnus-summary-edit-article
3669 "<" beginning-of-buffer
3670 ">" end-of-buffer
3671 "\C-c\C-i" gnus-info-find-node
3672 "\C-c\C-b" gnus-bug
3673 "R" gnus-article-reply-with-original
3674 "F" gnus-article-followup-with-original
3675 "\C-hk" gnus-article-describe-key
3676 "\C-hc" gnus-article-describe-key-briefly
3677
3678 "\C-d" gnus-article-read-summary-keys
3679 "\M-*" gnus-article-read-summary-keys
3680 "\M-#" gnus-article-read-summary-keys
3681 "\M-^" gnus-article-read-summary-keys
3682 "\M-g" gnus-article-read-summary-keys)
3683
3684 (substitute-key-definition
3685 'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
3686
3687 (defun gnus-article-make-menu-bar ()
3688 (unless (boundp 'gnus-article-commands-menu)
3689 (gnus-summary-make-menu-bar))
3690 (gnus-turn-off-edit-menu 'article)
3691 (unless (boundp 'gnus-article-article-menu)
3692 (easy-menu-define
3693 gnus-article-article-menu gnus-article-mode-map ""
3694 '("Article"
3695 ["Scroll forwards" gnus-article-goto-next-page t]
3696 ["Scroll backwards" gnus-article-goto-prev-page t]
3697 ["Show summary" gnus-article-show-summary t]
3698 ["Fetch Message-ID at point" gnus-article-refer-article t]
3699 ["Mail to address at point" gnus-article-mail t]
3700 ["Send a bug report" gnus-bug t]))
3701
3702 (easy-menu-define
3703 gnus-article-treatment-menu gnus-article-mode-map ""
3704 ;; Fixme: this should use :active (and maybe :visible).
3705 '("Treatment"
3706 ["Hide headers" gnus-article-hide-headers t]
3707 ["Hide signature" gnus-article-hide-signature t]
3708 ["Hide citation" gnus-article-hide-citation t]
3709 ["Treat overstrike" gnus-article-treat-overstrike t]
3710 ["Remove carriage return" gnus-article-remove-cr t]
3711 ["Remove leading whitespace" gnus-article-remove-leading-whitespace t]
3712 ["Remove quoted-unreadable" gnus-article-de-quoted-unreadable t]
3713 ["Remove base64" gnus-article-de-base64-unreadable t]
3714 ["Treat html" gnus-article-wash-html t]
3715 ["Remove newlines from within URLs" gnus-article-unsplit-urls t]
3716 ["Decode HZ" gnus-article-decode-HZ t]))
3717
3718 ;; Note "Commands" menu is defined in gnus-sum.el for consistency
3719
3720 ;; Note "Post" menu is defined in gnus-sum.el for consistency
3721
3722 (gnus-run-hooks 'gnus-article-menu-hook)))
3723
3724 (defun gnus-article-mode ()
3725 "Major mode for displaying an article.
3726
3727 All normal editing commands are switched off.
3728
3729 The following commands are available in addition to all summary mode
3730 commands:
3731 \\<gnus-article-mode-map>
3732 \\[gnus-article-next-page]\t Scroll the article one page forwards
3733 \\[gnus-article-prev-page]\t Scroll the article one page backwards
3734 \\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
3735 \\[gnus-article-show-summary]\t Display the summary buffer
3736 \\[gnus-article-mail]\t Send a reply to the address near point
3737 \\[gnus-article-describe-briefly]\t Describe the current mode briefly
3738 \\[gnus-info-find-node]\t Go to the Gnus info node"
3739 (interactive)
3740 (kill-all-local-variables)
3741 (gnus-simplify-mode-line)
3742 (setq mode-name "Article")
3743 (setq major-mode 'gnus-article-mode)
3744 (make-local-variable 'minor-mode-alist)
3745 (use-local-map gnus-article-mode-map)
3746 (when (gnus-visual-p 'article-menu 'menu)
3747 (gnus-article-make-menu-bar)
3748 (when gnus-summary-tool-bar-map
3749 (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
3750 (gnus-update-format-specifications nil 'article-mode)
3751 (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
3752 (set (make-local-variable 'gnus-page-broken) nil)
3753 (make-local-variable 'gnus-button-marker-list)
3754 (make-local-variable 'gnus-article-current-summary)
3755 (make-local-variable 'gnus-article-mime-handles)
3756 (make-local-variable 'gnus-article-decoded-p)
3757 (make-local-variable 'gnus-article-mime-handle-alist)
3758 (make-local-variable 'gnus-article-wash-types)
3759 (make-local-variable 'gnus-article-image-alist)
3760 (make-local-variable 'gnus-article-charset)
3761 (make-local-variable 'gnus-article-ignored-charsets)
3762 ;; Prevent recent Emacsen from displaying non-break space as "\ ".
3763 (set (make-local-variable 'nobreak-char-display) nil)
3764 (gnus-set-default-directory)
3765 (buffer-disable-undo)
3766 (setq buffer-read-only t)
3767 (set-syntax-table gnus-article-mode-syntax-table)
3768 (mm-enable-multibyte)
3769 (gnus-run-mode-hooks 'gnus-article-mode-hook))
3770
3771 (defun gnus-article-setup-buffer ()
3772 "Initialize the article buffer."
3773 (let* ((name (if gnus-single-article-buffer "*Article*"
3774 (concat "*Article " gnus-newsgroup-name "*")))
3775 (original
3776 (progn (string-match "\\*Article" name)
3777 (concat " *Original Article"
3778 (substring name (match-end 0))))))
3779 (setq gnus-article-buffer name)
3780 (setq gnus-original-article-buffer original)
3781 (setq gnus-article-mime-handle-alist nil)
3782 ;; This might be a variable local to the summary buffer.
3783 (unless gnus-single-article-buffer
3784 (save-excursion
3785 (set-buffer gnus-summary-buffer)
3786 (setq gnus-article-buffer name)
3787 (setq gnus-original-article-buffer original)
3788 (gnus-set-global-variables)))
3789 (gnus-article-setup-highlight-words)
3790 ;; Init original article buffer.
3791 (save-excursion
3792 (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
3793 (mm-enable-multibyte)
3794 (setq major-mode 'gnus-original-article-mode)
3795 (make-local-variable 'gnus-original-article))
3796 (if (and (get-buffer name)
3797 (with-current-buffer name
3798 (if gnus-article-edit-mode
3799 (if (y-or-n-p "Article mode edit in progress; discard? ")
3800 (progn
3801 (set-buffer-modified-p nil)
3802 (gnus-kill-buffer name)
3803 (message "")
3804 nil)
3805 (error "Action aborted"))
3806 t)))
3807 (save-excursion
3808 (set-buffer name)
3809 (set (make-local-variable 'gnus-article-edit-mode) nil)
3810 (when gnus-article-mime-handles
3811 (mm-destroy-parts gnus-article-mime-handles)
3812 (setq gnus-article-mime-handles nil))
3813 ;; Set it to nil in article-buffer!
3814 (setq gnus-article-mime-handle-alist nil)
3815 (buffer-disable-undo)
3816 (setq buffer-read-only t)
3817 ;; This list just keeps growing if we don't reset it.
3818 (setq gnus-button-marker-list nil)
3819 (unless (eq major-mode 'gnus-article-mode)
3820 (gnus-article-mode))
3821 (current-buffer))
3822 (save-excursion
3823 (set-buffer (gnus-get-buffer-create name))
3824 (gnus-article-mode)
3825 (make-local-variable 'gnus-summary-buffer)
3826 (gnus-summary-set-local-parameters gnus-newsgroup-name)
3827 (current-buffer)))))
3828
3829 ;; Set article window start at LINE, where LINE is the number of lines
3830 ;; from the head of the article.
3831 (defun gnus-article-set-window-start (&optional line)
3832 (set-window-start
3833 (gnus-get-buffer-window gnus-article-buffer t)
3834 (save-excursion
3835 (set-buffer gnus-article-buffer)
3836 (goto-char (point-min))
3837 (if (not line)
3838 (point-min)
3839 (gnus-message 6 "Moved to bookmark")
3840 (search-forward "\n\n" nil t)
3841 (forward-line line)
3842 (point)))))
3843
3844 (defun gnus-article-prepare (article &optional all-headers header)
3845 "Prepare ARTICLE in article mode buffer.
3846 ARTICLE should either be an article number or a Message-ID.
3847 If ARTICLE is an id, HEADER should be the article headers.
3848 If ALL-HEADERS is non-nil, no headers are hidden."
3849 (save-excursion
3850 ;; Make sure we start in a summary buffer.
3851 (unless (eq major-mode 'gnus-summary-mode)
3852 (set-buffer gnus-summary-buffer))
3853 (setq gnus-summary-buffer (current-buffer))
3854 (let* ((gnus-article (if header (mail-header-number header) article))
3855 (summary-buffer (current-buffer))
3856 (gnus-tmp-internal-hook gnus-article-internal-prepare-hook)
3857 (group gnus-newsgroup-name)
3858 result)
3859 (save-excursion
3860 (gnus-article-setup-buffer)
3861 (set-buffer gnus-article-buffer)
3862 ;; Deactivate active regions.
3863 (when (and (boundp 'transient-mark-mode)
3864 transient-mark-mode)
3865 (setq mark-active nil))
3866 (if (not (setq result (let ((inhibit-read-only t))
3867 (gnus-request-article-this-buffer
3868 article group))))
3869 ;; There is no such article.
3870 (save-excursion
3871 (when (and (numberp article)
3872 (not (memq article gnus-newsgroup-sparse)))
3873 (setq gnus-article-current
3874 (cons gnus-newsgroup-name article))
3875 (set-buffer gnus-summary-buffer)
3876 (setq gnus-current-article article)
3877 (if (and (memq article gnus-newsgroup-undownloaded)
3878 (not (gnus-online (gnus-find-method-for-group
3879 gnus-newsgroup-name))))
3880 (progn
3881 (gnus-summary-set-agent-mark article)
3882 (message "Message marked for downloading"))
3883 (gnus-summary-mark-article article gnus-canceled-mark)
3884 (unless (memq article gnus-newsgroup-sparse)
3885 (gnus-error 1 "No such article (may have expired or been canceled)")))))
3886 (if (or (eq result 'pseudo)
3887 (eq result 'nneething))
3888 (progn
3889 (save-excursion
3890 (set-buffer summary-buffer)
3891 (push article gnus-newsgroup-history)
3892 (setq gnus-last-article gnus-current-article
3893 gnus-current-article 0
3894 gnus-current-headers nil
3895 gnus-article-current nil)
3896 (if (eq result 'nneething)
3897 (gnus-configure-windows 'summary)
3898 (gnus-configure-windows 'article))
3899 (gnus-set-global-variables))
3900 (let ((gnus-article-mime-handle-alist-1
3901 gnus-article-mime-handle-alist))
3902 (gnus-set-mode-line 'article)))
3903 ;; The result from the `request' was an actual article -
3904 ;; or at least some text that is now displayed in the
3905 ;; article buffer.
3906 (when (and (numberp article)
3907 (not (eq article gnus-current-article)))
3908 ;; Seems like a new article has been selected.
3909 ;; `gnus-current-article' must be an article number.
3910 (save-excursion
3911 (set-buffer summary-buffer)
3912 (push article gnus-newsgroup-history)
3913 (setq gnus-last-article gnus-current-article
3914 gnus-current-article article
3915 gnus-current-headers
3916 (gnus-summary-article-header gnus-current-article)
3917 gnus-article-current
3918 (cons gnus-newsgroup-name gnus-current-article))
3919 (unless (vectorp gnus-current-headers)
3920 (setq gnus-current-headers nil))
3921 (gnus-summary-goto-subject gnus-current-article)
3922 (when (gnus-summary-show-thread)
3923 ;; If the summary buffer really was folded, the
3924 ;; previous goto may not actually have gone to
3925 ;; the right article, but the thread root instead.
3926 ;; So we go again.
3927 (gnus-summary-goto-subject gnus-current-article))
3928 (gnus-run-hooks 'gnus-mark-article-hook)
3929 (gnus-set-mode-line 'summary)
3930 (when (gnus-visual-p 'article-highlight 'highlight)
3931 (gnus-run-hooks 'gnus-visual-mark-article-hook))
3932 ;; Set the global newsgroup variables here.
3933 (gnus-set-global-variables)
3934 (setq gnus-have-all-headers
3935 (or all-headers gnus-show-all-headers))))
3936 (save-excursion
3937 (gnus-configure-windows 'article))
3938 (when (or (numberp article)
3939 (stringp article))
3940 (gnus-article-prepare-display)
3941 ;; Do page break.
3942 (goto-char (point-min))
3943 (when gnus-break-pages
3944 (gnus-narrow-to-page)))
3945 (let ((gnus-article-mime-handle-alist-1
3946 gnus-article-mime-handle-alist))
3947 (gnus-set-mode-line 'article))
3948 (article-goto-body)
3949 (unless (bobp)
3950 (forward-line -1))
3951 (set-window-point (get-buffer-window (current-buffer)) (point))
3952 (gnus-configure-windows 'article)
3953 t))))))
3954
3955 ;;;###autoload
3956 (defun gnus-article-prepare-display ()
3957 "Make the current buffer look like a nice article."
3958 ;; Hooks for getting information from the article.
3959 ;; This hook must be called before being narrowed.
3960 (let ((gnus-article-buffer (current-buffer))
3961 buffer-read-only
3962 (inhibit-read-only t))
3963 (unless (eq major-mode 'gnus-article-mode)
3964 (gnus-article-mode))
3965 (setq buffer-read-only nil
3966 gnus-article-wash-types nil
3967 gnus-article-image-alist nil)
3968 (gnus-run-hooks 'gnus-tmp-internal-hook)
3969 (when gnus-display-mime-function
3970 (funcall gnus-display-mime-function))
3971 (gnus-run-hooks 'gnus-article-prepare-hook)))
3972
3973 ;;;
3974 ;;; Gnus MIME viewing functions
3975 ;;;
3976
3977 (defvar gnus-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n"
3978 "Format of the MIME buttons.
3979
3980 Valid specifiers include:
3981 %t The MIME type
3982 %T MIME type, along with additional info
3983 %n The `name' parameter
3984 %d The description, if any
3985 %l The length of the encoded part
3986 %p The part identifier number
3987 %e Dots if the part isn't displayed
3988
3989 General format specifiers can also be used. See Info node
3990 `(gnus)Formatting Variables'.")
3991
3992 (defvar gnus-mime-button-line-format-alist
3993 '((?t gnus-tmp-type ?s)
3994 (?T gnus-tmp-type-long ?s)
3995 (?n gnus-tmp-name ?s)
3996 (?d gnus-tmp-description ?s)
3997 (?p gnus-tmp-id ?s)
3998 (?l gnus-tmp-length ?d)
3999 (?e gnus-tmp-dots ?s)))
4000
4001 (defvar gnus-mime-button-commands
4002 '((gnus-article-press-button "\r" "Toggle Display")
4003 (gnus-mime-view-part "v" "View Interactively...")
4004 (gnus-mime-view-part-as-type "t" "View As Type...")
4005 (gnus-mime-view-part-as-charset "C" "View As charset...")
4006 (gnus-mime-save-part "o" "Save...")
4007 (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
4008 (gnus-mime-delete-part "d" "Delete part")
4009 (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
4010 (gnus-mime-inline-part "i" "View As Text, In This Buffer")
4011 (gnus-mime-view-part-internally "E" "View Internally")
4012 (gnus-mime-view-part-externally "e" "View Externally")
4013 (gnus-mime-print-part "p" "Print")
4014 (gnus-mime-pipe-part "|" "Pipe To Command...")
4015 (gnus-mime-action-on-part "." "Take action on the part...")))
4016
4017 (defun gnus-article-mime-part-status ()
4018 (if gnus-article-mime-handle-alist-1
4019 (if (eq 1 (length gnus-article-mime-handle-alist-1))
4020 " (1 part)"
4021 (format " (%d parts)" (length gnus-article-mime-handle-alist-1)))
4022 ""))
4023
4024 (defvar gnus-mime-button-map
4025 (let ((map (make-sparse-keymap)))
4026 (unless (>= (string-to-number emacs-version) 21)
4027 ;; XEmacs doesn't care.
4028 (set-keymap-parent map gnus-article-mode-map))
4029 (define-key map gnus-mouse-2 'gnus-article-push-button)
4030 (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
4031 (dolist (c gnus-mime-button-commands)
4032 (define-key map (cadr c) (car c)))
4033 map))
4034
4035 (easy-menu-define
4036 gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
4037 `("MIME Part"
4038 ,@(mapcar (lambda (c)
4039 (vector (caddr c) (car c) :enable t))
4040 gnus-mime-button-commands)))
4041
4042 (eval-when-compile
4043 (define-compiler-macro popup-menu (&whole form
4044 menu &optional position prefix)
4045 (if (and (fboundp 'popup-menu)
4046 (not (memq 'popup-menu (assoc "lmenu" load-history))))
4047 form
4048 ;; Gnus is probably running under Emacs 20.
4049 `(let* ((menu (cdr ,menu))
4050 (response (x-popup-menu
4051 t (list (car menu)
4052 (cons "" (mapcar (lambda (c)
4053 (cons (caddr c) (car c)))
4054 (cdr menu)))))))
4055 (if response
4056 (call-interactively (nth 3 (assq response menu))))))))
4057
4058 (defun gnus-mime-button-menu (event prefix)
4059 "Construct a context-sensitive menu of MIME commands."
4060 (interactive "e\nP")
4061 (save-window-excursion
4062 (let ((pos (event-start event)))
4063 (select-window (posn-window pos))
4064 (goto-char (posn-point pos))
4065 (gnus-article-check-buffer)
4066 (popup-menu gnus-mime-button-menu nil prefix))))
4067
4068 (defun gnus-mime-view-all-parts (&optional handles)
4069 "View all the MIME parts."
4070 (interactive)
4071 (save-current-buffer
4072 (set-buffer gnus-article-buffer)
4073 (let ((handles (or handles gnus-article-mime-handles))
4074 (mail-parse-charset gnus-newsgroup-charset)
4075 (mail-parse-ignored-charsets
4076 (with-current-buffer gnus-summary-buffer
4077 gnus-newsgroup-ignored-charsets)))
4078 (when handles
4079 (mm-remove-parts handles)
4080 (goto-char (point-min))
4081 (or (search-forward "\n\n") (goto-char (point-max)))
4082 (let ((inhibit-read-only t))
4083 (delete-region (point) (point-max))
4084 (mm-display-parts handles))))))
4085
4086 (defun gnus-mime-save-part-and-strip ()
4087 "Save the MIME part under point then replace it with an external body."
4088 (interactive)
4089 (gnus-article-check-buffer)
4090 (when (gnus-group-read-only-p)
4091 (error "The current group does not support deleting of parts"))
4092 (when (mm-complicated-handles gnus-article-mime-handles)
4093 (error "\
4094 The current article has a complicated MIME structure, giving up..."))
4095 (when (gnus-yes-or-no-p "\
4096 Deleting parts may malfunction or destroy the article; continue? ")
4097 (let* ((data (get-text-property (point) 'gnus-data))
4098 file param
4099 (handles gnus-article-mime-handles))
4100 (setq file (and data (mm-save-part data)))
4101 (when file
4102 (with-current-buffer (mm-handle-buffer data)
4103 (erase-buffer)
4104 (insert "Content-Type: " (mm-handle-media-type data))
4105 (mml-insert-parameter-string (cdr (mm-handle-type data))
4106 '(charset))
4107 (insert "\n")
4108 (insert "Content-ID: " (message-make-message-id) "\n")
4109 (insert "Content-Transfer-Encoding: binary\n")
4110 (insert "\n"))
4111 (setcdr data
4112 (cdr (mm-make-handle nil
4113 `("message/external-body"
4114 (access-type . "LOCAL-FILE")
4115 (name . ,file)))))
4116 (set-buffer gnus-summary-buffer)
4117 (gnus-article-edit-article
4118 `(lambda ()
4119 (erase-buffer)
4120 (let ((mail-parse-charset (or gnus-article-charset
4121 ',gnus-newsgroup-charset))
4122 (mail-parse-ignored-charsets
4123 (or gnus-article-ignored-charsets
4124 ',gnus-newsgroup-ignored-charsets))
4125 (mbl mml-buffer-list))
4126 (setq mml-buffer-list nil)
4127 (insert-buffer-substring gnus-original-article-buffer)
4128 (mime-to-mml ',handles)
4129 (setq gnus-article-mime-handles nil)
4130 (let ((mbl1 mml-buffer-list))
4131 (setq mml-buffer-list mbl)
4132 (set (make-local-variable 'mml-buffer-list) mbl1))
4133 (gnus-make-local-hook 'kill-buffer-hook)
4134 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
4135 `(lambda (no-highlight)
4136 (let ((mail-parse-charset (or gnus-article-charset
4137 ',gnus-newsgroup-charset))
4138 (message-options message-options)
4139 (message-options-set-recipient)
4140 (mail-parse-ignored-charsets
4141 (or gnus-article-ignored-charsets
4142 ',gnus-newsgroup-ignored-charsets)))
4143 (mml-to-mime)
4144 (mml-destroy-buffers)
4145 (remove-hook 'kill-buffer-hook
4146 'mml-destroy-buffers t)
4147 (kill-local-variable 'mml-buffer-list))
4148 (gnus-summary-edit-article-done
4149 ,(or (mail-header-references gnus-current-headers) "")
4150 ,(gnus-group-read-only-p)
4151 ,gnus-summary-buffer no-highlight)))))))
4152
4153 (defun gnus-mime-delete-part ()
4154 "Delete the MIME part under point.
4155 Replace it with some information about the removed part."
4156 (interactive)
4157 (gnus-article-check-buffer)
4158 (when (gnus-group-read-only-p)
4159 (error "The current group does not support deleting of parts"))
4160 (when (mm-complicated-handles gnus-article-mime-handles)
4161 (error "\
4162 The current article has a complicated MIME structure, giving up..."))
4163 (when (gnus-yes-or-no-p "\
4164 Deleting parts may malfunction or destroy the article; continue? ")
4165 (let* ((data (get-text-property (point) 'gnus-data))
4166 (handles gnus-article-mime-handles)
4167 (none "(none)")
4168 (description
4169 (or
4170 (mail-decode-encoded-word-string (or (mm-handle-description data)
4171 none))))
4172 (filename
4173 (or (mail-content-type-get (mm-handle-disposition data) 'filename)
4174 none))
4175 (type (mm-handle-media-type data)))
4176 (unless data
4177 (error "No MIME part under point"))
4178 (with-current-buffer (mm-handle-buffer data)
4179 (let ((bsize (format "%s" (buffer-size))))
4180 (erase-buffer)
4181 (insert
4182 (concat
4183 ",----\n"
4184 "| The following attachment has been deleted:\n"
4185 "|\n"
4186 "| Type: " type "\n"
4187 "| Filename: " filename "\n"
4188 "| Size (encoded): " bsize " Byte\n"
4189 "| Description: " description "\n"
4190 "`----\n"))
4191 (setcdr data
4192 (cdr (mm-make-handle
4193 nil `("text/plain") nil nil
4194 (list "attachment")
4195 (format "Deleted attachment (%s bytes)" bsize))))))
4196 (set-buffer gnus-summary-buffer)
4197 ;; FIXME: maybe some of the following code (borrowed from
4198 ;; `gnus-mime-save-part-and-strip') isn't necessary?
4199 (gnus-article-edit-article
4200 `(lambda ()
4201 (erase-buffer)
4202 (let ((mail-parse-charset (or gnus-article-charset
4203 ',gnus-newsgroup-charset))
4204 (mail-parse-ignored-charsets
4205 (or gnus-article-ignored-charsets
4206 ',gnus-newsgroup-ignored-charsets))
4207 (mbl mml-buffer-list))
4208 (setq mml-buffer-list nil)
4209 (insert-buffer-substring gnus-original-article-buffer)
4210 (mime-to-mml ',handles)
4211 (setq gnus-article-mime-handles nil)
4212 (let ((mbl1 mml-buffer-list))
4213 (setq mml-buffer-list mbl)
4214 (set (make-local-variable 'mml-buffer-list) mbl1))
4215 (gnus-make-local-hook 'kill-buffer-hook)
4216 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
4217 `(lambda (no-highlight)
4218 (let ((mail-parse-charset (or gnus-article-charset
4219 ',gnus-newsgroup-charset))
4220 (message-options message-options)
4221 (message-options-set-recipient)
4222 (mail-parse-ignored-charsets
4223 (or gnus-article-ignored-charsets
4224 ',gnus-newsgroup-ignored-charsets)))
4225 (mml-to-mime)
4226 (mml-destroy-buffers)
4227 (remove-hook 'kill-buffer-hook
4228 'mml-destroy-buffers t)
4229 (kill-local-variable 'mml-buffer-list))
4230 (gnus-summary-edit-article-done
4231 ,(or (mail-header-references gnus-current-headers) "")
4232 ,(gnus-group-read-only-p)
4233 ,gnus-summary-buffer no-highlight)))))
4234 ;; Not in `gnus-mime-save-part-and-strip':
4235 (gnus-article-edit-done)
4236 (gnus-summary-expand-window)
4237 (gnus-summary-show-article))
4238
4239 (defun gnus-mime-save-part ()
4240 "Save the MIME part under point."
4241 (interactive)
4242 (gnus-article-check-buffer)
4243 (let ((data (get-text-property (point) 'gnus-data)))
4244 (when data
4245 (mm-save-part data))))
4246
4247 (defun gnus-mime-pipe-part ()
4248 "Pipe the MIME part under point to a process."
4249 (interactive)
4250 (gnus-article-check-buffer)
4251 (let ((data (get-text-property (point) 'gnus-data)))
4252 (when data
4253 (mm-pipe-part data))))
4254
4255 (defun gnus-mime-view-part ()
4256 "Interactively choose a viewing method for the MIME part under point."
4257 (interactive)
4258 (gnus-article-check-buffer)
4259 (let ((data (get-text-property (point) 'gnus-data)))
4260 (when data
4261 (setq gnus-article-mime-handles
4262 (mm-merge-handles
4263 gnus-article-mime-handles (setq data (copy-sequence data))))
4264 (mm-interactively-view-part data))))
4265
4266 (defun gnus-mime-view-part-as-type-internal ()
4267 (gnus-article-check-buffer)
4268 (let* ((name (mail-content-type-get
4269 (mm-handle-type (get-text-property (point) 'gnus-data))
4270 'name))
4271 (def-type (and name (mm-default-file-encoding name))))
4272 (and def-type (cons def-type 0))))
4273
4274 (defun gnus-mime-view-part-as-type (&optional mime-type)
4275 "Choose a MIME media type, and view the part as such."
4276 (interactive)
4277 (unless mime-type
4278 (setq mime-type (completing-read
4279 "View as MIME type: "
4280 (mapcar #'list (mailcap-mime-types))
4281 nil nil
4282 (gnus-mime-view-part-as-type-internal))))
4283 (gnus-article-check-buffer)
4284 (let ((handle (get-text-property (point) 'gnus-data)))
4285 (when handle
4286 (setq handle
4287 (mm-make-handle (mm-handle-buffer handle)
4288 (cons mime-type (cdr (mm-handle-type handle)))
4289 (mm-handle-encoding handle)
4290 (mm-handle-undisplayer handle)
4291 (mm-handle-disposition handle)
4292 (mm-handle-description handle)
4293 nil
4294 (mm-handle-id handle)))
4295 (setq gnus-article-mime-handles
4296 (mm-merge-handles gnus-article-mime-handles handle))
4297 (gnus-mm-display-part handle))))
4298
4299 (eval-when-compile
4300 (require 'jka-compr))
4301
4302 ;; jka-compr.el uses a "sh -c" to direct stderr to err-file, but these days
4303 ;; emacs can do that itself.
4304 ;;
4305 (defun gnus-mime-jka-compr-maybe-uncompress ()
4306 "Uncompress the current buffer if `auto-compression-mode' is enabled.
4307 The uncompress method used is derived from `buffer-file-name'."
4308 (when (and (fboundp 'jka-compr-installed-p)
4309 (jka-compr-installed-p))
4310 (let ((info (jka-compr-get-compression-info buffer-file-name)))
4311 (when info
4312 (let ((basename (file-name-nondirectory buffer-file-name))
4313 (args (jka-compr-info-uncompress-args info))
4314 (prog (jka-compr-info-uncompress-program info))
4315 (message (jka-compr-info-uncompress-message info))
4316 (err-file (jka-compr-make-temp-name)))
4317 (if message
4318 (message "%s %s..." message basename))
4319 (unwind-protect
4320 (unless (memq (apply 'call-process-region
4321 (point-min) (point-max)
4322 prog
4323 t (list t err-file) nil
4324 args)
4325 jka-compr-acceptable-retval-list)
4326 (jka-compr-error prog args basename message err-file))
4327 (jka-compr-delete-temp-file err-file)))))))
4328
4329 (defun gnus-mime-copy-part (&optional handle)
4330 "Put the MIME part under point into a new buffer.
4331 If `auto-compression-mode' is enabled, compressed files like .gz and .bz2
4332 are decompressed."
4333 (interactive)
4334 (gnus-article-check-buffer)
4335 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4336 (contents (and handle (mm-get-part handle)))
4337 (base (and handle
4338 (file-name-nondirectory
4339 (or
4340 (mail-content-type-get (mm-handle-type handle) 'name)
4341 (mail-content-type-get (mm-handle-disposition handle)
4342 'filename)
4343 "*decoded*"))))
4344 (buffer (and base (generate-new-buffer base))))
4345 (when contents
4346 (switch-to-buffer buffer)
4347 (insert contents)
4348 ;; We do it this way to make `normal-mode' set the appropriate mode.
4349 (unwind-protect
4350 (progn
4351 (setq buffer-file-name (expand-file-name base))
4352 (gnus-mime-jka-compr-maybe-uncompress)
4353 (normal-mode))
4354 (setq buffer-file-name nil))
4355 (goto-char (point-min)))))
4356
4357 (defun gnus-mime-print-part (&optional handle filename)
4358 "Print the MIME part under point."
4359 (interactive (list nil (ps-print-preprint current-prefix-arg)))
4360 (gnus-article-check-buffer)
4361 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4362 (contents (and handle (mm-get-part handle)))
4363 (file (mm-make-temp-file (expand-file-name "mm." mm-tmp-directory)))
4364 (printer (mailcap-mime-info (mm-handle-media-type handle) "print")))
4365 (when contents
4366 (if printer
4367 (unwind-protect
4368 (progn
4369 (mm-save-part-to-file handle file)
4370 (call-process shell-file-name nil
4371 (generate-new-buffer " *mm*")
4372 nil
4373 shell-command-switch
4374 (mm-mailcap-command
4375 printer file (mm-handle-type handle))))
4376 (delete-file file))
4377 (with-temp-buffer
4378 (insert contents)
4379 (gnus-print-buffer))
4380 (ps-despool filename)))))
4381
4382 (defun gnus-mime-inline-part (&optional handle arg)
4383 "Insert the MIME part under point into the current buffer."
4384 (interactive (list nil current-prefix-arg))
4385 (gnus-article-check-buffer)
4386 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4387 contents charset
4388 (b (point))
4389 (inhibit-read-only t))
4390 (when handle
4391 (if (and (not arg) (mm-handle-undisplayer handle))
4392 (mm-remove-part handle)
4393 (setq contents (mm-get-part handle))
4394 (cond
4395 ((not arg)
4396 (setq charset (or (mail-content-type-get
4397 (mm-handle-type handle) 'charset)
4398 gnus-newsgroup-charset)))
4399 ((numberp arg)
4400 (if (mm-handle-undisplayer handle)
4401 (mm-remove-part handle))
4402 (setq charset
4403 (or (cdr (assq arg
4404 gnus-summary-show-article-charset-alist))
4405 (mm-read-coding-system "Charset: "))))
4406 (t
4407 (if (mm-handle-undisplayer handle)
4408 (mm-remove-part handle))))
4409 (forward-line 2)
4410 (mm-insert-inline
4411 handle
4412 (if (and charset
4413 (setq charset (mm-charset-to-coding-system
4414 charset))
4415 (not (eq charset 'ascii)))
4416 (mm-decode-coding-string contents charset)
4417 (mm-string-to-multibyte contents)))
4418 (goto-char b)))))
4419
4420 (defun gnus-mime-view-part-as-charset (&optional handle arg)
4421 "Insert the MIME part under point into the current buffer using the
4422 specified charset."
4423 (interactive (list nil current-prefix-arg))
4424 (gnus-article-check-buffer)
4425 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4426 contents charset
4427 (b (point))
4428 (inhibit-read-only t))
4429 (when handle
4430 (if (mm-handle-undisplayer handle)
4431 (mm-remove-part handle))
4432 (let ((gnus-newsgroup-charset
4433 (or (cdr (assq arg
4434 gnus-summary-show-article-charset-alist))
4435 (mm-read-coding-system "Charset: ")))
4436 (gnus-newsgroup-ignored-charsets 'gnus-all))
4437 (gnus-article-press-button)))))
4438
4439 (defun gnus-mime-view-part-externally (&optional handle)
4440 "View the MIME part under point with an external viewer."
4441 (interactive)
4442 (gnus-article-check-buffer)
4443 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4444 (mm-user-display-methods nil)
4445 (mm-inlined-types nil)
4446 (mail-parse-charset gnus-newsgroup-charset)
4447 (mail-parse-ignored-charsets
4448 (save-excursion (set-buffer gnus-summary-buffer)
4449 gnus-newsgroup-ignored-charsets)))
4450 (when handle
4451 (if (mm-handle-undisplayer handle)
4452 (mm-remove-part handle)
4453 (mm-display-part handle)))))
4454
4455 (defun gnus-mime-view-part-internally (&optional handle)
4456 "View the MIME part under point with an internal viewer.
4457 If no internal viewer is available, use an external viewer."
4458 (interactive)
4459 (gnus-article-check-buffer)
4460 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4461 (mm-inlined-types '(".*"))
4462 (mm-inline-large-images t)
4463 (mail-parse-charset gnus-newsgroup-charset)
4464 (mail-parse-ignored-charsets
4465 (save-excursion (set-buffer gnus-summary-buffer)
4466 gnus-newsgroup-ignored-charsets))
4467 (inhibit-read-only t))
4468 (when handle
4469 (if (mm-handle-undisplayer handle)
4470 (mm-remove-part handle)
4471 (mm-display-part handle)))))
4472
4473 (defun gnus-mime-action-on-part (&optional action)
4474 "Do something with the MIME attachment at \(point\)."
4475 (interactive
4476 (list (completing-read "Action: " gnus-mime-action-alist nil t)))
4477 (gnus-article-check-buffer)
4478 (let ((action-pair (assoc action gnus-mime-action-alist)))
4479 (if action-pair
4480 (funcall (cdr action-pair)))))
4481
4482 (defun gnus-article-part-wrapper (n function)
4483 (save-current-buffer
4484 (set-buffer gnus-article-buffer)
4485 (when (> n (length gnus-article-mime-handle-alist))
4486 (error "No such part"))
4487 (gnus-article-goto-part n)
4488 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
4489 (funcall function handle))))
4490
4491 (defun gnus-article-pipe-part (n)
4492 "Pipe MIME part N, which is the numerical prefix."
4493 (interactive "p")
4494 (gnus-article-part-wrapper n 'mm-pipe-part))
4495
4496 (defun gnus-article-save-part (n)
4497 "Save MIME part N, which is the numerical prefix."
4498 (interactive "p")
4499 (gnus-article-part-wrapper n 'mm-save-part))
4500
4501 (defun gnus-article-interactively-view-part (n)
4502 "View MIME part N interactively, which is the numerical prefix."
4503 (interactive "p")
4504 (gnus-article-part-wrapper n 'mm-interactively-view-part))
4505
4506 (defun gnus-article-copy-part (n)
4507 "Copy MIME part N, which is the numerical prefix."
4508 (interactive "p")
4509 (gnus-article-part-wrapper n 'gnus-mime-copy-part))
4510
4511 (defun gnus-article-view-part-as-charset (n)
4512 "View MIME part N using a specified charset.
4513 N is the numerical prefix."
4514 (interactive "p")
4515 (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
4516
4517 (defun gnus-article-view-part-externally (n)
4518 "View MIME part N externally, which is the numerical prefix."
4519 (interactive "p")
4520 (gnus-article-part-wrapper n 'gnus-mime-view-part-externally))
4521
4522 (defun gnus-article-inline-part (n)
4523 "Inline MIME part N, which is the numerical prefix."
4524 (interactive "p")
4525 (gnus-article-part-wrapper n 'gnus-mime-inline-part))
4526
4527 (defun gnus-article-mime-match-handle-first (condition)
4528 (if condition
4529 (let ((alist gnus-article-mime-handle-alist) ihandle n)
4530 (while (setq ihandle (pop alist))
4531 (if (and (cond
4532 ((functionp condition)
4533 (funcall condition (cdr ihandle)))
4534 ((eq condition 'undisplayed)
4535 (not (or (mm-handle-undisplayer (cdr ihandle))
4536 (equal (mm-handle-media-type (cdr ihandle))
4537 "multipart/alternative"))))
4538 ((eq condition 'undisplayed-alternative)
4539 (not (mm-handle-undisplayer (cdr ihandle))))
4540 (t t))
4541 (gnus-article-goto-part (car ihandle))
4542 (or (not n) (< (car ihandle) n)))
4543 (setq n (car ihandle))))
4544 (or n 1))
4545 1))
4546
4547 (defun gnus-article-view-part (&optional n)
4548 "View MIME part N, which is the numerical prefix."
4549 (interactive "P")
4550 (save-current-buffer
4551 (set-buffer gnus-article-buffer)
4552 (or (numberp n) (setq n (gnus-article-mime-match-handle-first
4553 gnus-article-mime-match-handle-function)))
4554 (when (> n (length gnus-article-mime-handle-alist))
4555 (error "No such part"))
4556 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
4557 (when (gnus-article-goto-part n)
4558 (if (equal (car handle) "multipart/alternative")
4559 (gnus-article-press-button)
4560 (when (eq (gnus-mm-display-part handle) 'internal)
4561 (gnus-set-window-start)))))))
4562
4563 (defsubst gnus-article-mime-total-parts ()
4564 (if (bufferp (car gnus-article-mime-handles))
4565 1 ;; single part
4566 (1- (length gnus-article-mime-handles))))
4567
4568 (defun gnus-mm-display-part (handle)
4569 "Display HANDLE and fix MIME button."
4570 (let ((id (get-text-property (point) 'gnus-part))
4571 (point (point))
4572 (inhibit-read-only t))
4573 (forward-line 1)
4574 (prog1
4575 (let ((window (selected-window))
4576 (mail-parse-charset gnus-newsgroup-charset)
4577 (mail-parse-ignored-charsets
4578 (if (gnus-buffer-live-p gnus-summary-buffer)
4579 (save-excursion
4580 (set-buffer gnus-summary-buffer)
4581 gnus-newsgroup-ignored-charsets)
4582 nil)))
4583 (save-excursion
4584 (unwind-protect
4585 (let ((win (gnus-get-buffer-window (current-buffer) t))
4586 (beg (point)))
4587 (when win
4588 (select-window win))
4589 (goto-char point)
4590 (forward-line)
4591 (if (mm-handle-displayed-p handle)
4592 ;; This will remove the part.
4593 (mm-display-part handle)
4594 (save-restriction
4595 (narrow-to-region (point)
4596 (if (eobp) (point) (1+ (point))))
4597 (mm-display-part handle)
4598 ;; We narrow to the part itself and
4599 ;; then call the treatment functions.
4600 (goto-char (point-min))
4601 (forward-line 1)
4602 (narrow-to-region (point) (point-max))
4603 (gnus-treat-article
4604 nil id
4605 (gnus-article-mime-total-parts)
4606 (mm-handle-media-type handle)))))
4607 (if (window-live-p window)
4608 (select-window window)))))
4609 (goto-char point)
4610 (gnus-delete-line)
4611 (gnus-insert-mime-button
4612 handle id (list (mm-handle-displayed-p handle)))
4613 (goto-char point))))
4614
4615 (defun gnus-article-goto-part (n)
4616 "Go to MIME part N."
4617 (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
4618
4619 (defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
4620 (let ((gnus-tmp-name
4621 (or (mail-content-type-get (mm-handle-type handle) 'name)
4622 (mail-content-type-get (mm-handle-disposition handle) 'filename)
4623 (mail-content-type-get (mm-handle-type handle) 'url)
4624 ""))
4625 (gnus-tmp-type (mm-handle-media-type handle))
4626 (gnus-tmp-description
4627 (mail-decode-encoded-word-string (or (mm-handle-description handle)
4628 "")))
4629 (gnus-tmp-dots
4630 (if (if displayed (car displayed)
4631 (mm-handle-displayed-p handle))
4632 "" "..."))
4633 (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle)
4634 (buffer-size)))
4635 gnus-tmp-type-long b e)
4636 (when (string-match ".*/" gnus-tmp-name)
4637 (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name)))
4638 (setq gnus-tmp-type-long (concat gnus-tmp-type
4639 (and (not (equal gnus-tmp-name ""))
4640 (concat "; " gnus-tmp-name))))
4641 (unless (equal gnus-tmp-description "")
4642 (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long)))
4643 (unless (bolp)
4644 (insert "\n"))
4645 (setq b (point))
4646 (gnus-eval-format
4647 gnus-mime-button-line-format gnus-mime-button-line-format-alist
4648 `(,@(gnus-local-map-property gnus-mime-button-map)
4649 gnus-callback gnus-mm-display-part
4650 gnus-part ,gnus-tmp-id
4651 article-type annotation
4652 gnus-data ,handle))
4653 (setq e (if (bolp)
4654 ;; Exclude a newline.
4655 (1- (point))
4656 (point)))
4657 (widget-convert-button
4658 'link b e
4659 :mime-handle handle
4660 :action 'gnus-widget-press-button
4661 :button-keymap gnus-mime-button-map
4662 :help-echo
4663 (lambda (widget/window &optional overlay pos)
4664 ;; Needed to properly clear the message due to a bug in
4665 ;; wid-edit (XEmacs only).
4666 (if (boundp 'help-echo-owns-message)
4667 (setq help-echo-owns-message t))
4668 (format
4669 "%S: %s the MIME part; %S: more options"
4670 (aref gnus-mouse-2 0)
4671 ;; XEmacs will get a single widget arg; Emacs 21 will get
4672 ;; window, overlay, position.
4673 (if (mm-handle-displayed-p
4674 (if overlay
4675 (with-current-buffer (gnus-overlay-buffer overlay)
4676 (widget-get (widget-at (gnus-overlay-start overlay))
4677 :mime-handle))
4678 (widget-get widget/window :mime-handle)))
4679 "hide" "show")
4680 (aref gnus-down-mouse-3 0))))))
4681
4682 (defun gnus-widget-press-button (elems el)
4683 (goto-char (widget-get elems :from))
4684 (gnus-article-press-button))
4685
4686 (defvar gnus-displaying-mime nil)
4687
4688 (defun gnus-display-mime (&optional ihandles)
4689 "Display the MIME parts."
4690 (save-excursion
4691 (save-selected-window
4692 (let ((window (get-buffer-window gnus-article-buffer))
4693 (point (point)))
4694 (when window
4695 (select-window window)
4696 ;; We have to do this since selecting the window
4697 ;; may change the point. So we set the window point.
4698 (set-window-point window point)))
4699 (let* ((handles (or ihandles
4700 (mm-dissect-buffer nil gnus-article-loose-mime)
4701 (and gnus-article-emulate-mime
4702 (mm-uu-dissect))))
4703 (inhibit-read-only t) handle name type b e display)
4704 (when (and (not ihandles)
4705 (not gnus-displaying-mime))
4706 ;; Top-level call; we clean up.
4707 (when gnus-article-mime-handles
4708 (mm-destroy-parts gnus-article-mime-handles)
4709 (setq gnus-article-mime-handle-alist nil));; A trick.
4710 (setq gnus-article-mime-handles handles)
4711 ;; We allow users to glean info from the handles.
4712 (when gnus-article-mime-part-function
4713 (gnus-mime-part-function handles)))
4714 (if (and handles
4715 (or (not (stringp (car handles)))
4716 (cdr handles)))
4717 (progn
4718 (when (and (not ihandles)
4719 (not gnus-displaying-mime))
4720 ;; Clean up for mime parts.
4721 (article-goto-body)
4722 (delete-region (point) (point-max)))
4723 (let ((gnus-displaying-mime t))
4724 (gnus-mime-display-part handles)))
4725 (save-restriction
4726 (article-goto-body)
4727 (narrow-to-region (point) (point-max))
4728 (gnus-treat-article nil 1 1)
4729 (widen)))
4730 (unless ihandles
4731 ;; Highlight the headers.
4732 (save-excursion
4733 (save-restriction
4734 (article-goto-body)
4735 (narrow-to-region (point-min) (point))
4736 (gnus-article-save-original-date
4737 (gnus-treat-article 'head)))))))))
4738
4739 (defcustom gnus-mime-display-multipart-as-mixed nil
4740 "Display \"multipart\" parts as \"multipart/mixed\".
4741
4742 If t, it overrides nil values of
4743 `gnus-mime-display-multipart-alternative-as-mixed' and
4744 `gnus-mime-display-multipart-related-as-mixed'."
4745 :group 'gnus-article-mime
4746 :type 'boolean)
4747
4748 (defcustom gnus-mime-display-multipart-alternative-as-mixed nil
4749 "Display \"multipart/alternative\" parts as \"multipart/mixed\"."
4750 :version "22.1"
4751 :group 'gnus-article-mime
4752 :type 'boolean)
4753
4754 (defcustom gnus-mime-display-multipart-related-as-mixed nil
4755 "Display \"multipart/related\" parts as \"multipart/mixed\".
4756
4757 If displaying \"text/html\" is discouraged \(see
4758 `mm-discouraged-alternatives'\) images or other material inside a
4759 \"multipart/related\" part might be overlooked when this variable is nil."
4760 :version "22.1"
4761 :group 'gnus-article-mime
4762 :type 'boolean)
4763
4764 (defun gnus-mime-display-part (handle)
4765 (cond
4766 ;; Maybe a broken MIME message.
4767 ((null handle))
4768 ;; Single part.
4769 ((not (stringp (car handle)))
4770 (gnus-mime-display-single handle))
4771 ;; User-defined multipart
4772 ((cdr (assoc (car handle) gnus-mime-multipart-functions))
4773 (funcall (cdr (assoc (car handle) gnus-mime-multipart-functions))
4774 handle))
4775 ;; multipart/alternative
4776 ((and (equal (car handle) "multipart/alternative")
4777 (not (or gnus-mime-display-multipart-as-mixed
4778 gnus-mime-display-multipart-alternative-as-mixed)))
4779 (let ((id (1+ (length gnus-article-mime-handle-alist))))
4780 (push (cons id handle) gnus-article-mime-handle-alist)
4781 (gnus-mime-display-alternative (cdr handle) nil nil id)))
4782 ;; multipart/related
4783 ((and (equal (car handle) "multipart/related")
4784 (not (or gnus-mime-display-multipart-as-mixed
4785 gnus-mime-display-multipart-related-as-mixed)))
4786 ;;;!!!We should find the start part, but we just default
4787 ;;;!!!to the first part.
4788 ;;(gnus-mime-display-part (cadr handle))
4789 ;;;!!! Most multipart/related is an HTML message plus images.
4790 ;;;!!! Unfortunately we are unable to let W3 display those
4791 ;;;!!! included images, so we just display it as a mixed multipart.
4792 ;;(gnus-mime-display-mixed (cdr handle))
4793 ;;;!!! No, w3 can display everything just fine.
4794 (gnus-mime-display-part (cadr handle)))
4795 ((equal (car handle) "multipart/signed")
4796 (gnus-add-wash-type 'signed)
4797 (gnus-mime-display-security handle))
4798 ((equal (car handle) "multipart/encrypted")
4799 (gnus-add-wash-type 'encrypted)
4800 (gnus-mime-display-security handle))
4801 ;; Other multiparts are handled like multipart/mixed.
4802 (t
4803 (gnus-mime-display-mixed (cdr handle)))))
4804
4805 (defun gnus-mime-part-function (handles)
4806 (if (stringp (car handles))
4807 (mapcar 'gnus-mime-part-function (cdr handles))
4808 (funcall gnus-article-mime-part-function handles)))
4809
4810 (defun gnus-mime-display-mixed (handles)
4811 (mapcar 'gnus-mime-display-part handles))
4812
4813 (defun gnus-mime-display-single (handle)
4814 (let ((type (mm-handle-media-type handle))
4815 (ignored gnus-ignored-mime-types)
4816 (not-attachment t)
4817 (move nil)
4818 display text)
4819 (catch 'ignored
4820 (progn
4821 (while ignored
4822 (when (string-match (pop ignored) type)
4823 (throw 'ignored nil)))
4824 (if (and (setq not-attachment
4825 (and (not (mm-inline-override-p handle))
4826 (or (not (mm-handle-disposition handle))
4827 (equal (car (mm-handle-disposition handle))
4828 "inline")
4829 (mm-attachment-override-p handle))))
4830 (mm-automatic-display-p handle)
4831 (or (and
4832 (mm-inlinable-p handle)
4833 (mm-inlined-p handle))
4834 (mm-automatic-external-display-p type)))
4835 (setq display t)
4836 (when (equal (mm-handle-media-supertype handle) "text")
4837 (setq text t)))
4838 (let ((id (1+ (length gnus-article-mime-handle-alist)))
4839 beg)
4840 (push (cons id handle) gnus-article-mime-handle-alist)
4841 (when (or (not display)
4842 (not (gnus-unbuttonized-mime-type-p type)))
4843 ;(gnus-article-insert-newline)
4844 (gnus-insert-mime-button
4845 handle id (list (or display (and not-attachment text))))
4846 (gnus-article-insert-newline)
4847 ;(gnus-article-insert-newline)
4848 ;; Remember modify the number of forward lines.
4849 (setq move t))
4850 (setq beg (point))
4851 (cond
4852 (display
4853 (when move
4854 (forward-line -1)
4855 (setq beg (point)))
4856 (let ((mail-parse-charset gnus-newsgroup-charset)
4857 (mail-parse-ignored-charsets
4858 (save-excursion (condition-case ()
4859 (set-buffer gnus-summary-buffer)
4860 (error))
4861 gnus-newsgroup-ignored-charsets)))
4862 (mm-display-part handle t))
4863 (goto-char (point-max)))
4864 ((and text not-attachment)
4865 (when move
4866 (forward-line -1)
4867 (setq beg (point)))
4868 (gnus-article-insert-newline)
4869 (mm-insert-inline
4870 handle
4871 (let ((charset (mail-content-type-get (mm-handle-type handle)
4872 'charset)))
4873 (cond ((not charset)
4874 (mm-string-as-multibyte (mm-get-part handle)))
4875 ((eq charset 'gnus-decoded)
4876 (with-current-buffer (mm-handle-buffer handle)
4877 (buffer-string)))
4878 (t
4879 (mm-decode-string (mm-get-part handle) charset)))))
4880 (goto-char (point-max))))
4881 ;; Do highlighting.
4882 (save-excursion
4883 (save-restriction
4884 (narrow-to-region beg (point))
4885 (gnus-treat-article
4886 nil id
4887 (gnus-article-mime-total-parts)
4888 (mm-handle-media-type handle)))))))))
4889
4890 (defun gnus-unbuttonized-mime-type-p (type)
4891 "Say whether TYPE is to be unbuttonized."
4892 (unless gnus-inhibit-mime-unbuttonizing
4893 (when (catch 'found
4894 (let ((types gnus-unbuttonized-mime-types))
4895 (while types
4896 (when (string-match (pop types) type)
4897 (throw 'found t)))))
4898 (not (catch 'found
4899 (let ((types gnus-buttonized-mime-types))
4900 (while types
4901 (when (string-match (pop types) type)
4902 (throw 'found t)))))))))
4903
4904 (defun gnus-article-insert-newline ()
4905 "Insert a newline, but mark it as undeletable."
4906 (gnus-put-text-property
4907 (point) (progn (insert "\n") (point)) 'gnus-undeletable t))
4908
4909 (defun gnus-mime-display-alternative (handles &optional preferred ibegend id)
4910 (let* ((preferred (or preferred (mm-preferred-alternative handles)))
4911 (ihandles handles)
4912 (point (point))
4913 handle (inhibit-read-only t) from props begend not-pref)
4914 (save-window-excursion
4915 (save-restriction
4916 (when ibegend
4917 (narrow-to-region (car ibegend)
4918 (or (cdr ibegend)
4919 (progn
4920 (goto-char (car ibegend))
4921 (forward-line 2)
4922 (point))))
4923 (delete-region (point-min) (point-max))
4924 (mm-remove-parts handles))
4925 (setq begend (list (point-marker)))
4926 ;; Do the toggle.
4927 (unless (setq not-pref (cadr (member preferred ihandles)))
4928 (setq not-pref (car ihandles)))
4929 (when (or ibegend
4930 (not preferred)
4931 (not (gnus-unbuttonized-mime-type-p
4932 "multipart/alternative")))
4933 (gnus-add-text-properties
4934 (setq from (point))
4935 (progn
4936 (insert (format "%d. " id))
4937 (point))
4938 `(gnus-callback
4939 (lambda (handles)
4940 (unless ,(not ibegend)
4941 (setq gnus-article-mime-handle-alist
4942 ',gnus-article-mime-handle-alist))
4943 (gnus-mime-display-alternative
4944 ',ihandles ',not-pref ',begend ,id))
4945 ,@(gnus-local-map-property gnus-mime-button-map)
4946 ,gnus-mouse-face-prop ,gnus-article-mouse-face
4947 face ,gnus-article-button-face
4948 gnus-part ,id
4949 gnus-data ,handle))
4950 (widget-convert-button 'link from (point)
4951 :action 'gnus-widget-press-button
4952 :button-keymap gnus-widget-button-keymap)
4953 ;; Do the handles
4954 (while (setq handle (pop handles))
4955 (gnus-add-text-properties
4956 (setq from (point))
4957 (progn
4958 (insert (format "(%c) %-18s"
4959 (if (equal handle preferred) ?* ? )
4960 (mm-handle-media-type handle)))
4961 (point))
4962 `(gnus-callback
4963 (lambda (handles)
4964 (unless ,(not ibegend)
4965 (setq gnus-article-mime-handle-alist
4966 ',gnus-article-mime-handle-alist))
4967 (gnus-mime-display-alternative
4968 ',ihandles ',handle ',begend ,id))
4969 ,@(gnus-local-map-property gnus-mime-button-map)
4970 ,gnus-mouse-face-prop ,gnus-article-mouse-face
4971 face ,gnus-article-button-face
4972 gnus-part ,id
4973 gnus-data ,handle))
4974 (widget-convert-button 'link from (point)
4975 :action 'gnus-widget-press-button
4976 :button-keymap gnus-widget-button-keymap)
4977 (insert " "))
4978 (insert "\n\n"))
4979 (when preferred
4980 (if (stringp (car preferred))
4981 (gnus-display-mime preferred)
4982 (let ((mail-parse-charset gnus-newsgroup-charset)
4983 (mail-parse-ignored-charsets
4984 (save-excursion (set-buffer gnus-summary-buffer)
4985 gnus-newsgroup-ignored-charsets)))
4986 (mm-display-part preferred)
4987 ;; Do highlighting.
4988 (save-excursion
4989 (save-restriction
4990 (narrow-to-region (car begend) (point-max))
4991 (gnus-treat-article
4992 nil (length gnus-article-mime-handle-alist)
4993 (gnus-article-mime-total-parts)
4994 (mm-handle-media-type handle))))))
4995 (goto-char (point-max))
4996 (setcdr begend (point-marker)))))
4997 (when ibegend
4998 (goto-char point))))
4999
5000 (defconst gnus-article-wash-status-strings
5001 (let ((alist '((cite "c" "Possible hidden citation text"
5002 " " "All citation text visible")
5003 (headers "h" "Hidden headers"
5004 " " "All headers visible.")
5005 (pgp "p" "Encrypted or signed message status hidden"
5006 " " "No hidden encryption nor digital signature status")
5007 (signature "s" "Signature has been hidden"
5008 " " "Signature is visible")
5009 (overstrike "o" "Overstrike (^H) characters applied"
5010 " " "No overstrike characters applied")
5011 (emphasis "e" "/*_Emphasis_*/ characters applied"
5012 " " "No /*_emphasis_*/ characters applied")))
5013 result)
5014 (dolist (entry alist result)
5015 (let ((key (nth 0 entry))
5016 (on (copy-sequence (nth 1 entry)))
5017 (on-help (nth 2 entry))
5018 (off (copy-sequence (nth 3 entry)))
5019 (off-help (nth 4 entry)))
5020 (put-text-property 0 1 'help-echo on-help on)
5021 (put-text-property 0 1 'help-echo off-help off)
5022 (push (list key on off) result))))
5023 "Alist of strings describing wash status in the mode line.
5024 Each entry has the form (KEY ON OF), where the KEY is a symbol
5025 representing the particular washing function, ON is the string to use
5026 in the article mode line when the washing function is active, and OFF
5027 is the string to use when it is inactive.")
5028
5029 (defun gnus-article-wash-status-entry (key value)
5030 (let ((entry (assoc key gnus-article-wash-status-strings)))
5031 (if value (nth 1 entry) (nth 2 entry))))
5032
5033 (defun gnus-article-wash-status ()
5034 "Return a string which display status of article washing."
5035 (save-excursion
5036 (set-buffer gnus-article-buffer)
5037 (let ((cite (memq 'cite gnus-article-wash-types))
5038 (headers (memq 'headers gnus-article-wash-types))
5039 (boring (memq 'boring-headers gnus-article-wash-types))
5040 (pgp (memq 'pgp gnus-article-wash-types))
5041 (pem (memq 'pem gnus-article-wash-types))
5042 (signed (memq 'signed gnus-article-wash-types))
5043 (encrypted (memq 'encrypted gnus-article-wash-types))
5044 (signature (memq 'signature gnus-article-wash-types))
5045 (overstrike (memq 'overstrike gnus-article-wash-types))
5046 (emphasis (memq 'emphasis gnus-article-wash-types)))
5047 (concat
5048 (gnus-article-wash-status-entry 'cite cite)
5049 (gnus-article-wash-status-entry 'headers (or headers boring))
5050 (gnus-article-wash-status-entry 'pgp (or pgp pem signed encrypted))
5051 (gnus-article-wash-status-entry 'signature signature)
5052 (gnus-article-wash-status-entry 'overstrike overstrike)
5053 (gnus-article-wash-status-entry 'emphasis emphasis)))))
5054
5055 (defun gnus-add-wash-type (type)
5056 "Add a washing of TYPE to the current status."
5057 (add-to-list 'gnus-article-wash-types type))
5058
5059 (defun gnus-delete-wash-type (type)
5060 "Add a washing of TYPE to the current status."
5061 (setq gnus-article-wash-types (delq type gnus-article-wash-types)))
5062
5063 (defun gnus-add-image (category image)
5064 "Add IMAGE of CATEGORY to the list of displayed images."
5065 (let ((entry (assq category gnus-article-image-alist)))
5066 (unless entry
5067 (setq entry (list category))
5068 (push entry gnus-article-image-alist))
5069 (nconc entry (list image))))
5070
5071 (defun gnus-delete-images (category)
5072 "Delete all images in CATEGORY."
5073 (let ((entry (assq category gnus-article-image-alist)))
5074 (dolist (image (cdr entry))
5075 (gnus-remove-image image category))
5076 (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
5077 (gnus-delete-wash-type category)))
5078
5079 (defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
5080
5081 (defun gnus-article-maybe-hide-headers ()
5082 "Hide unwanted headers if `gnus-have-all-headers' is nil.
5083 Provided for backwards compatibility."
5084 (when (and (or (not (gnus-buffer-live-p gnus-summary-buffer))
5085 (not (save-excursion (set-buffer gnus-summary-buffer)
5086 gnus-have-all-headers)))
5087 (not gnus-inhibit-hiding))
5088 (gnus-article-hide-headers)))
5089
5090 ;;; Article savers.
5091
5092 (defun gnus-output-to-file (file-name)
5093 "Append the current article to a file named FILE-NAME."
5094 (let ((artbuf (current-buffer)))
5095 (with-temp-buffer
5096 (insert-buffer-substring artbuf)
5097 ;; Append newline at end of the buffer as separator, and then
5098 ;; save it to file.
5099 (goto-char (point-max))
5100 (insert "\n")
5101 (let ((file-name-coding-system nnmail-pathname-coding-system))
5102 (mm-append-to-file (point-min) (point-max) file-name))
5103 t)))
5104
5105 (defun gnus-narrow-to-page (&optional arg)
5106 "Narrow the article buffer to a page.
5107 If given a numerical ARG, move forward ARG pages."
5108 (interactive "P")
5109 (setq arg (if arg (prefix-numeric-value arg) 0))
5110 (save-excursion
5111 (set-buffer gnus-article-buffer)
5112 (goto-char (point-min))
5113 (widen)
5114 ;; Remove any old next/prev buttons.
5115 (when (gnus-visual-p 'page-marker)
5116 (let ((inhibit-read-only t))
5117 (gnus-remove-text-with-property 'gnus-prev)
5118 (gnus-remove-text-with-property 'gnus-next)))
5119 (if
5120 (cond ((< arg 0)
5121 (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
5122 ((> arg 0)
5123 (re-search-forward page-delimiter nil 'move arg)))
5124 (goto-char (match-end 0))
5125 (save-excursion
5126 (goto-char (point-min))
5127 (setq gnus-page-broken
5128 (and (re-search-forward page-delimiter nil t) t))))
5129 (when gnus-page-broken
5130 (narrow-to-region
5131 (point)
5132 (if (re-search-forward page-delimiter nil 'move)
5133 (match-beginning 0)
5134 (point)))
5135 (when (and (gnus-visual-p 'page-marker)
5136 (> (point-min) (save-restriction (widen) (point-min))))
5137 (save-excursion
5138 (goto-char (point-min))
5139 (gnus-insert-prev-page-button)))
5140 (when (and (gnus-visual-p 'page-marker)
5141 (< (point-max) (save-restriction (widen) (point-max))))
5142 (save-excursion
5143 (goto-char (point-max))
5144 (gnus-insert-next-page-button))))))
5145
5146 ;; Article mode commands
5147
5148 (defun gnus-article-goto-next-page ()
5149 "Show the next page of the article."
5150 (interactive)
5151 (when (gnus-article-next-page)
5152 (goto-char (point-min))
5153 (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
5154
5155
5156 (defun gnus-article-goto-prev-page ()
5157 "Show the previous page of the article."
5158 (interactive)
5159 (if (bobp)
5160 (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
5161 (gnus-article-prev-page nil)))
5162
5163 ;; This is cleaner but currently breaks `gnus-pick-mode':
5164 ;;
5165 ;; (defun gnus-article-goto-next-page ()
5166 ;; "Show the next page of the article."
5167 ;; (interactive)
5168 ;; (gnus-eval-in-buffer-window gnus-summary-buffer
5169 ;; (gnus-summary-next-page)))
5170 ;;
5171 ;; (defun gnus-article-goto-prev-page ()
5172 ;; "Show the next page of the article."
5173 ;; (interactive)
5174 ;; (gnus-eval-in-buffer-window gnus-summary-buffer
5175 ;; (gnus-summary-prev-page)))
5176
5177 (defun gnus-article-next-page (&optional lines)
5178 "Show the next page of the current article.
5179 If end of article, return non-nil. Otherwise return nil.
5180 Argument LINES specifies lines to be scrolled up."
5181 (interactive "p")
5182 (move-to-window-line -1)
5183 (if (save-excursion
5184 (end-of-line)
5185 (and (pos-visible-in-window-p) ;Not continuation line.
5186 (>= (1+ (point)) (point-max)))) ;Allow for trailing newline.
5187 ;; Nothing in this page.
5188 (if (or (not gnus-page-broken)
5189 (save-excursion
5190 (save-restriction
5191 (widen)
5192 (forward-line)
5193 (eobp)))) ;Real end-of-buffer?
5194 (progn
5195 (when gnus-article-over-scroll
5196 (gnus-article-next-page-1 lines))
5197 t) ;Nothing more.
5198 (gnus-narrow-to-page 1) ;Go to next page.
5199 nil)
5200 ;; More in this page.
5201 (gnus-article-next-page-1 lines)
5202 nil))
5203
5204 (defmacro gnus-article-beginning-of-window ()
5205 "Move point to the beginning of the window.
5206 In Emacs, the point is placed at the line number which `scroll-margin'
5207 specifies."
5208 (if (featurep 'xemacs)
5209 '(move-to-window-line 0)
5210 '(move-to-window-line
5211 (min (max 0 scroll-margin)
5212 (max 1 (- (window-height)
5213 (if mode-line-format 1 0)
5214 (if (and (boundp 'header-line-format)
5215 (symbol-value 'header-line-format))
5216 1 0)))))))
5217
5218 (defun gnus-article-next-page-1 (lines)
5219 (when (and (not (featurep 'xemacs))
5220 (numberp lines)
5221 (> lines 0)
5222 (numberp (symbol-value 'scroll-margin))
5223 (> (symbol-value 'scroll-margin) 0))
5224 ;; Protect against the bug that Emacs 21.x hangs up when scrolling up for
5225 ;; too many number of lines if `scroll-margin' is set as two or greater.
5226 (setq lines (min lines
5227 (max 0 (- (count-lines (window-start) (point-max))
5228 (symbol-value 'scroll-margin))))))
5229 (condition-case ()
5230 (let ((scroll-in-place nil))
5231 (scroll-up lines))
5232 (end-of-buffer
5233 ;; Long lines may cause an end-of-buffer error.
5234 (goto-char (point-max))))
5235 (gnus-article-beginning-of-window))
5236
5237 (defun gnus-article-prev-page (&optional lines)
5238 "Show previous page of current article.
5239 Argument LINES specifies lines to be scrolled down."
5240 (interactive "p")
5241 (move-to-window-line 0)
5242 (if (and gnus-page-broken
5243 (bobp)
5244 (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
5245 (progn
5246 (gnus-narrow-to-page -1) ;Go to previous page.
5247 (goto-char (point-max))
5248 (recenter -1))
5249 (prog1
5250 (condition-case ()
5251 (let ((scroll-in-place nil))
5252 (scroll-down lines))
5253 (beginning-of-buffer
5254 (goto-char (point-min))))
5255 (gnus-article-beginning-of-window))))
5256
5257 (defun gnus-article-only-boring-p ()
5258 "Decide whether there is only boring text remaining in the article.
5259 Something \"interesting\" is a word of at least two letters that does
5260 not have a face in `gnus-article-boring-faces'."
5261 (when (and gnus-article-skip-boring
5262 (boundp 'gnus-article-boring-faces)
5263 (symbol-value 'gnus-article-boring-faces))
5264 (save-excursion
5265 (catch 'only-boring
5266 (while (re-search-forward "\\b\\w\\w" nil t)
5267 (forward-char -1)
5268 (when (not (gnus-intersection
5269 (gnus-faces-at (point))
5270 (symbol-value 'gnus-article-boring-faces)))
5271 (throw 'only-boring nil)))
5272 (throw 'only-boring t)))))
5273
5274 (defun gnus-article-refer-article ()
5275 "Read article specified by message-id around point."
5276 (interactive)
5277 (save-excursion
5278 (re-search-backward "[ \t]\\|^" (gnus-point-at-bol) t)
5279 (re-search-forward "<?news:<?\\|<" (gnus-point-at-eol) t)
5280 (if (re-search-forward "[^@ ]+@[^ \t>]+" (gnus-point-at-eol) t)
5281 (let ((msg-id (concat "<" (match-string 0) ">")))
5282 (set-buffer gnus-summary-buffer)
5283 (gnus-summary-refer-article msg-id))
5284 (error "No references around point"))))
5285
5286 (defun gnus-article-show-summary ()
5287 "Reconfigure windows to show summary buffer."
5288 (interactive)
5289 (if (not (gnus-buffer-live-p gnus-summary-buffer))
5290 (error "There is no summary buffer for this article buffer")
5291 (gnus-article-set-globals)
5292 (gnus-configure-windows 'article)
5293 (gnus-summary-goto-subject gnus-current-article)
5294 (gnus-summary-position-point)))
5295
5296 (defun gnus-article-describe-briefly ()
5297 "Describe article mode commands briefly."
5298 (interactive)
5299 (gnus-message 6 (substitute-command-keys "\\<gnus-article-mode-map>\\[gnus-article-goto-next-page]:Next page \\[gnus-article-goto-prev-page]:Prev page \\[gnus-article-show-summary]:Show summary \\[gnus-info-find-node]:Run Info \\[gnus-article-describe-briefly]:This help")))
5300
5301 (defun gnus-article-summary-command ()
5302 "Execute the last keystroke in the summary buffer."
5303 (interactive)
5304 (let ((obuf (current-buffer))
5305 (owin (current-window-configuration))
5306 func)
5307 (switch-to-buffer gnus-article-current-summary 'norecord)
5308 (setq func (lookup-key (current-local-map) (this-command-keys)))
5309 (call-interactively func)
5310 (set-buffer obuf)
5311 (set-window-configuration owin)
5312 (set-window-point (get-buffer-window (current-buffer)) (point))))
5313
5314 (defun gnus-article-summary-command-nosave ()
5315 "Execute the last keystroke in the summary buffer."
5316 (interactive)
5317 (let (func)
5318 (pop-to-buffer gnus-article-current-summary 'norecord)
5319 (setq func (lookup-key (current-local-map) (this-command-keys)))
5320 (call-interactively func)))
5321
5322 (defun gnus-article-check-buffer ()
5323 "Beep if not in an article buffer."
5324 (unless (equal major-mode 'gnus-article-mode)
5325 (error "Command invoked outside of a Gnus article buffer")))
5326
5327 (defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
5328 "Read a summary buffer key sequence and execute it from the article buffer."
5329 (interactive "P")
5330 (gnus-article-check-buffer)
5331 (let ((nosaves
5332 '("q" "Q" "c" "r" "\C-c\C-f" "m" "a" "f"
5333 "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
5334 "=" "^" "\M-^" "|"))
5335 (nosave-but-article
5336 '("A\r"))
5337 (nosave-in-article
5338 '("\C-d"))
5339 (up-to-top
5340 '("n" "Gn" "p" "Gp"))
5341 keys new-sum-point)
5342 (save-excursion
5343 (set-buffer gnus-article-current-summary)
5344 (let (gnus-pick-mode)
5345 (push (or key last-command-event) unread-command-events)
5346 (setq keys (if (featurep 'xemacs)
5347 (events-to-keys (read-key-sequence nil))
5348 (read-key-sequence nil)))))
5349
5350 (message "")
5351
5352 (if (or (member keys nosaves)
5353 (member keys nosave-but-article)
5354 (member keys nosave-in-article))
5355 (let (func)
5356 (save-window-excursion
5357 (pop-to-buffer gnus-article-current-summary 'norecord)
5358 ;; We disable the pick minor mode commands.
5359 (let (gnus-pick-mode)
5360 (setq func (lookup-key (current-local-map) keys))))
5361 (if (or (not func)
5362 (numberp func))
5363 (ding)
5364 (unless (member keys nosave-in-article)
5365 (set-buffer gnus-article-current-summary))
5366 (call-interactively func)
5367 (setq new-sum-point (point)))
5368 (when (member keys nosave-but-article)
5369 (pop-to-buffer gnus-article-buffer 'norecord)))
5370 ;; These commands should restore window configuration.
5371 (let ((obuf (current-buffer))
5372 (owin (current-window-configuration))
5373 (opoint (point))
5374 win func in-buffer selected new-sum-start new-sum-hscroll)
5375 (cond (not-restore-window
5376 (pop-to-buffer gnus-article-current-summary 'norecord))
5377 ((setq win (get-buffer-window gnus-article-current-summary))
5378 (select-window win))
5379 (t
5380 (switch-to-buffer gnus-article-current-summary 'norecord)))
5381 (setq in-buffer (current-buffer))
5382 ;; We disable the pick minor mode commands.
5383 (if (and (setq func (let (gnus-pick-mode)
5384 (lookup-key (current-local-map) keys)))
5385 (functionp func))
5386 (progn
5387 (call-interactively func)
5388 (when (eq win (selected-window))
5389 (setq new-sum-point (point)
5390 new-sum-start (window-start win)
5391 new-sum-hscroll (window-hscroll win)))
5392 (when (eq in-buffer (current-buffer))
5393 (setq selected (gnus-summary-select-article))
5394 (set-buffer obuf)
5395 (unless not-restore-window
5396 (set-window-configuration owin))
5397 (when (eq selected 'old)
5398 (article-goto-body)
5399 (set-window-start (get-buffer-window (current-buffer))
5400 1)
5401 (set-window-point (get-buffer-window (current-buffer))
5402 (point)))
5403 (when (and (not not-restore-window)
5404 new-sum-point)
5405 (set-window-point win new-sum-point)
5406 (set-window-start win new-sum-start)
5407 (set-window-hscroll win new-sum-hscroll))))
5408 (set-window-configuration owin)
5409 (ding))))))
5410
5411 (defun gnus-article-describe-key (key)
5412 "Display documentation of the function invoked by KEY. KEY is a string."
5413 (interactive "kDescribe key: ")
5414 (gnus-article-check-buffer)
5415 (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5416 (save-excursion
5417 (set-buffer gnus-article-current-summary)
5418 (let (gnus-pick-mode)
5419 (if (featurep 'xemacs)
5420 (progn
5421 (push (elt key 0) unread-command-events)
5422 (setq key (events-to-keys
5423 (read-key-sequence "Describe key: "))))
5424 (setq unread-command-events
5425 (mapcar
5426 (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5427 (string-to-list key)))
5428 (setq key (read-key-sequence "Describe key: "))))
5429 (describe-key key))
5430 (describe-key key)))
5431
5432 (defun gnus-article-describe-key-briefly (key &optional insert)
5433 "Display documentation of the function invoked by KEY. KEY is a string."
5434 (interactive "kDescribe key: \nP")
5435 (gnus-article-check-buffer)
5436 (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5437 (save-excursion
5438 (set-buffer gnus-article-current-summary)
5439 (let (gnus-pick-mode)
5440 (if (featurep 'xemacs)
5441 (progn
5442 (push (elt key 0) unread-command-events)
5443 (setq key (events-to-keys
5444 (read-key-sequence "Describe key: "))))
5445 (setq unread-command-events
5446 (mapcar
5447 (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5448 (string-to-list key)))
5449 (setq key (read-key-sequence "Describe key: "))))
5450 (describe-key-briefly key insert))
5451 (describe-key-briefly key insert)))
5452
5453 (defun gnus-article-reply-with-original (&optional wide)
5454 "Start composing a reply mail to the current message.
5455 The text in the region will be yanked. If the region isn't active,
5456 the entire article will be yanked."
5457 (interactive "P")
5458 (let ((article (cdr gnus-article-current))
5459 contents)
5460 (if (not (gnus-mark-active-p))
5461 (with-current-buffer gnus-summary-buffer
5462 (gnus-summary-reply (list (list article)) wide))
5463 (setq contents (buffer-substring (point) (mark t)))
5464 ;; Deactivate active regions.
5465 (when (and (boundp 'transient-mark-mode)
5466 transient-mark-mode)
5467 (setq mark-active nil))
5468 (with-current-buffer gnus-summary-buffer
5469 (gnus-summary-reply
5470 (list (list article contents)) wide)))))
5471
5472 (defun gnus-article-followup-with-original ()
5473 "Compose a followup to the current article.
5474 The text in the region will be yanked. If the region isn't active,
5475 the entire article will be yanked."
5476 (interactive)
5477 (let ((article (cdr gnus-article-current))
5478 contents)
5479 (if (not (gnus-mark-active-p))
5480 (with-current-buffer gnus-summary-buffer
5481 (gnus-summary-followup (list (list article))))
5482 (setq contents (buffer-substring (point) (mark t)))
5483 ;; Deactivate active regions.
5484 (when (and (boundp 'transient-mark-mode)
5485 transient-mark-mode)
5486 (setq mark-active nil))
5487 (with-current-buffer gnus-summary-buffer
5488 (gnus-summary-followup
5489 (list (list article contents)))))))
5490
5491 (defun gnus-article-hide (&optional arg force)
5492 "Hide all the gruft in the current article.
5493 This means that signatures, cited text and (some) headers will be
5494 hidden.
5495 If given a prefix, show the hidden text instead."
5496 (interactive (append (gnus-article-hidden-arg) (list 'force)))
5497 (gnus-article-hide-headers arg)
5498 (gnus-article-hide-list-identifiers arg)
5499 (gnus-article-hide-citation-maybe arg force)
5500 (gnus-article-hide-signature arg))
5501
5502 (defun gnus-article-maybe-highlight ()
5503 "Do some article highlighting if article highlighting is requested."
5504 (when (gnus-visual-p 'article-highlight 'highlight)
5505 (gnus-article-highlight-some)))
5506
5507 (defun gnus-check-group-server ()
5508 ;; Make sure the connection to the server is alive.
5509 (unless (gnus-server-opened
5510 (gnus-find-method-for-group gnus-newsgroup-name))
5511 (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
5512 (gnus-request-group gnus-newsgroup-name t)))
5513
5514 (eval-when-compile
5515 (autoload 'nneething-get-file-name "nneething"))
5516
5517 (defun gnus-request-article-this-buffer (article group)
5518 "Get an article and insert it into this buffer."
5519 (let (do-update-line sparse-header)
5520 (prog1
5521 (save-excursion
5522 (erase-buffer)
5523 (gnus-kill-all-overlays)
5524 (setq group (or group gnus-newsgroup-name))
5525
5526 ;; Using `gnus-request-article' directly will insert the article into
5527 ;; `nntp-server-buffer' - so we'll save some time by not having to
5528 ;; copy it from the server buffer into the article buffer.
5529
5530 ;; We only request an article by message-id when we do not have the
5531 ;; headers for it, so we'll have to get those.
5532 (when (stringp article)
5533 (gnus-read-header article))
5534
5535 ;; If the article number is negative, that means that this article
5536 ;; doesn't belong in this newsgroup (possibly), so we find its
5537 ;; message-id and request it by id instead of number.
5538 (when (and (numberp article)
5539 gnus-summary-buffer
5540 (get-buffer gnus-summary-buffer)
5541 (gnus-buffer-exists-p gnus-summary-buffer))
5542 (save-excursion
5543 (set-buffer gnus-summary-buffer)
5544 (let ((header (gnus-summary-article-header article)))
5545 (when (< article 0)
5546 (cond
5547 ((memq article gnus-newsgroup-sparse)
5548 ;; This is a sparse gap article.
5549 (setq do-update-line article)
5550 (setq article (mail-header-id header))
5551 (setq sparse-header (gnus-read-header article))
5552 (setq gnus-newsgroup-sparse
5553 (delq article gnus-newsgroup-sparse)))
5554 ((vectorp header)
5555 ;; It's a real article.
5556 (setq article (mail-header-id header)))
5557 (t
5558 ;; It is an extracted pseudo-article.
5559 (setq article 'pseudo)
5560 (gnus-request-pseudo-article header))))
5561
5562 (let ((method (gnus-find-method-for-group
5563 gnus-newsgroup-name)))
5564 (when (and (eq (car method) 'nneething)
5565 (vectorp header))
5566 (let ((dir (nneething-get-file-name
5567 (mail-header-id header))))
5568 (when (and (stringp dir)
5569 (file-directory-p dir))
5570 (setq article 'nneething)
5571 (gnus-group-enter-directory dir))))))))
5572
5573 (cond
5574 ;; Refuse to select canceled articles.
5575 ((and (numberp article)
5576 gnus-summary-buffer
5577 (get-buffer gnus-summary-buffer)
5578 (gnus-buffer-exists-p gnus-summary-buffer)
5579 (eq (cdr (save-excursion
5580 (set-buffer gnus-summary-buffer)
5581 (assq article gnus-newsgroup-reads)))
5582 gnus-canceled-mark))
5583 nil)
5584 ;; We first check `gnus-original-article-buffer'.
5585 ((and (get-buffer gnus-original-article-buffer)
5586 (numberp article)
5587 (save-excursion
5588 (set-buffer gnus-original-article-buffer)
5589 (and (equal (car gnus-original-article) group)
5590 (eq (cdr gnus-original-article) article))))
5591 (insert-buffer-substring gnus-original-article-buffer)
5592 'article)
5593 ;; Check the backlog.
5594 ((and gnus-keep-backlog
5595 (gnus-backlog-request-article group article (current-buffer)))
5596 'article)
5597 ;; Check asynchronous pre-fetch.
5598 ((gnus-async-request-fetched-article group article (current-buffer))
5599 (gnus-async-prefetch-next group article gnus-summary-buffer)
5600 (when (and (numberp article) gnus-keep-backlog)
5601 (gnus-backlog-enter-article group article (current-buffer)))
5602 'article)
5603 ;; Check the cache.
5604 ((and gnus-use-cache
5605 (numberp article)
5606 (gnus-cache-request-article article group))
5607 'article)
5608 ;; Check the agent cache.
5609 ((gnus-agent-request-article article group)
5610 'article)
5611 ;; Get the article and put into the article buffer.
5612 ((or (stringp article)
5613 (numberp article))
5614 (let ((gnus-override-method gnus-override-method)
5615 (methods (and (stringp article)
5616 gnus-refer-article-method))
5617 (backend (car (gnus-find-method-for-group
5618 gnus-newsgroup-name)))
5619 result
5620 (inhibit-read-only t))
5621 (if (or (not (listp methods))
5622 (and (symbolp (car methods))
5623 (assq (car methods) nnoo-definition-alist)))
5624 (setq methods (list methods)))
5625 (when (and (null gnus-override-method)
5626 methods)
5627 (setq gnus-override-method (pop methods)))
5628 (while (not result)
5629 (when (eq gnus-override-method 'current)
5630 (setq gnus-override-method
5631 (with-current-buffer gnus-summary-buffer
5632 gnus-current-select-method)))
5633 (erase-buffer)
5634 (gnus-kill-all-overlays)
5635 (let ((gnus-newsgroup-name group))
5636 (gnus-check-group-server))
5637 (cond
5638 ((gnus-request-article article group (current-buffer))
5639 (when (numberp article)
5640 (gnus-async-prefetch-next group article
5641 gnus-summary-buffer)
5642 (when gnus-keep-backlog
5643 (gnus-backlog-enter-article
5644 group article (current-buffer))))
5645 (setq result 'article))
5646 (methods
5647 (setq gnus-override-method (pop methods)))
5648 ((not (string-match "^400 "
5649 (nnheader-get-report backend)))
5650 ;; If we get 400 server disconnect, reconnect and
5651 ;; retry; otherwise, assume the article has expired.
5652 (setq result 'done))))
5653 (and (eq result 'article) 'article)))
5654 ;; It was a pseudo.
5655 (t article)))
5656
5657 ;; Associate this article with the current summary buffer.
5658 (setq gnus-article-current-summary gnus-summary-buffer)
5659
5660 ;; Take the article from the original article buffer
5661 ;; and place it in the buffer it's supposed to be in.
5662 (when (and (get-buffer gnus-article-buffer)
5663 (equal (buffer-name (current-buffer))
5664 (buffer-name (get-buffer gnus-article-buffer))))
5665 (save-excursion
5666 (if (get-buffer gnus-original-article-buffer)
5667 (set-buffer gnus-original-article-buffer)
5668 (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
5669 (buffer-disable-undo)
5670 (setq major-mode 'gnus-original-article-mode)
5671 (setq buffer-read-only t))
5672 (let ((inhibit-read-only t))
5673 (erase-buffer)
5674 (insert-buffer-substring gnus-article-buffer))
5675 (setq gnus-original-article (cons group article)))
5676
5677 ;; Decode charsets.
5678 (run-hooks 'gnus-article-decode-hook)
5679 ;; Mark article as decoded or not.
5680 (setq gnus-article-decoded-p gnus-article-decode-hook))
5681
5682 ;; Update sparse articles.
5683 (when (and do-update-line
5684 (or (numberp article)
5685 (stringp article)))
5686 (let ((buf (current-buffer)))
5687 (set-buffer gnus-summary-buffer)
5688 (gnus-summary-update-article do-update-line sparse-header)
5689 (gnus-summary-goto-subject do-update-line nil t)
5690 (set-window-point (gnus-get-buffer-window (current-buffer) t)
5691 (point))
5692 (set-buffer buf))))))
5693
5694 ;;;
5695 ;;; Article editing
5696 ;;;
5697
5698 (defcustom gnus-article-edit-mode-hook nil
5699 "Hook run in article edit mode buffers."
5700 :group 'gnus-article-various
5701 :type 'hook)
5702
5703 (defvar gnus-article-edit-done-function nil)
5704
5705 (defvar gnus-article-edit-mode-map nil)
5706 (defvar gnus-article-edit-mode nil)
5707
5708 ;; Should we be using derived.el for this?
5709 (unless gnus-article-edit-mode-map
5710 (setq gnus-article-edit-mode-map (make-keymap))
5711 (set-keymap-parent gnus-article-edit-mode-map text-mode-map)
5712
5713 (gnus-define-keys gnus-article-edit-mode-map
5714 "\C-c?" describe-mode
5715 "\C-c\C-c" gnus-article-edit-done
5716 "\C-c\C-k" gnus-article-edit-exit
5717 "\C-c\C-f\C-t" message-goto-to
5718 "\C-c\C-f\C-o" message-goto-from
5719 "\C-c\C-f\C-b" message-goto-bcc
5720 ;;"\C-c\C-f\C-w" message-goto-fcc
5721 "\C-c\C-f\C-c" message-goto-cc
5722 "\C-c\C-f\C-s" message-goto-subject
5723 "\C-c\C-f\C-r" message-goto-reply-to
5724 "\C-c\C-f\C-n" message-goto-newsgroups
5725 "\C-c\C-f\C-d" message-goto-distribution
5726 "\C-c\C-f\C-f" message-goto-followup-to
5727 "\C-c\C-f\C-m" message-goto-mail-followup-to
5728 "\C-c\C-f\C-k" message-goto-keywords
5729 "\C-c\C-f\C-u" message-goto-summary
5730 "\C-c\C-f\C-i" message-insert-or-toggle-importance
5731 "\C-c\C-f\C-a" message-generate-unsubscribed-mail-followup-to
5732 "\C-c\C-b" message-goto-body
5733 "\C-c\C-i" message-goto-signature
5734
5735 "\C-c\C-t" message-insert-to
5736 "\C-c\C-n" message-insert-newsgroups
5737 "\C-c\C-o" message-sort-headers
5738 "\C-c\C-e" message-elide-region
5739 "\C-c\C-v" message-delete-not-region
5740 "\C-c\C-z" message-kill-to-signature
5741 "\M-\r" message-newline-and-reformat
5742 "\C-c\C-a" mml-attach-file
5743 "\C-a" message-beginning-of-line
5744 "\t" message-tab
5745 "\M-;" comment-region)
5746
5747 (gnus-define-keys (gnus-article-edit-wash-map
5748 "\C-c\C-w" gnus-article-edit-mode-map)
5749 "f" gnus-article-edit-full-stops))
5750
5751 (easy-menu-define
5752 gnus-article-edit-mode-field-menu gnus-article-edit-mode-map ""
5753 '("Field"
5754 ["Fetch To" message-insert-to t]
5755 ["Fetch Newsgroups" message-insert-newsgroups t]
5756 "----"
5757 ["To" message-goto-to t]
5758 ["From" message-goto-from t]
5759 ["Subject" message-goto-subject t]
5760 ["Cc" message-goto-cc t]
5761 ["Reply-To" message-goto-reply-to t]
5762 ["Summary" message-goto-summary t]
5763 ["Keywords" message-goto-keywords t]
5764 ["Newsgroups" message-goto-newsgroups t]
5765 ["Followup-To" message-goto-followup-to t]
5766 ["Mail-Followup-To" message-goto-mail-followup-to t]
5767 ["Distribution" message-goto-distribution t]
5768 ["Body" message-goto-body t]
5769 ["Signature" message-goto-signature t]))
5770
5771 (define-derived-mode gnus-article-edit-mode message-mode "Article Edit"
5772 "Major mode for editing articles.
5773 This is an extended text-mode.
5774
5775 \\{gnus-article-edit-mode-map}"
5776 (make-local-variable 'gnus-article-edit-done-function)
5777 (make-local-variable 'gnus-prev-winconf)
5778 (set (make-local-variable 'font-lock-defaults)
5779 '(message-font-lock-keywords t))
5780 (set (make-local-variable 'mail-header-separator) "")
5781 (set (make-local-variable 'gnus-article-edit-mode) t)
5782 (easy-menu-add message-mode-field-menu message-mode-map)
5783 (mml-mode)
5784 (setq buffer-read-only nil)
5785 (buffer-enable-undo)
5786 (widen))
5787
5788 (defun gnus-article-edit (&optional force)
5789 "Edit the current article.
5790 This will have permanent effect only in mail groups.
5791 If FORCE is non-nil, allow editing of articles even in read-only
5792 groups."
5793 (interactive "P")
5794 (when (and (not force)
5795 (gnus-group-read-only-p))
5796 (error "The current newsgroup does not support article editing"))
5797 (gnus-article-date-original)
5798 (gnus-article-edit-article
5799 'ignore
5800 `(lambda (no-highlight)
5801 'ignore
5802 (gnus-summary-edit-article-done
5803 ,(or (mail-header-references gnus-current-headers) "")
5804 ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight))))
5805
5806 (defun gnus-article-edit-article (start-func exit-func)
5807 "Start editing the contents of the current article buffer."
5808 (let ((winconf (current-window-configuration)))
5809 (set-buffer gnus-article-buffer)
5810 (let ((message-auto-save-directory
5811 ;; Don't associate the article buffer with a draft file.
5812 nil))
5813 (gnus-article-edit-mode))
5814 (funcall start-func)
5815 (set-buffer-modified-p nil)
5816 (gnus-configure-windows 'edit-article)
5817 (setq gnus-article-edit-done-function exit-func)
5818 (setq gnus-prev-winconf winconf)
5819 (gnus-message 6 "C-c C-c to end edits")))
5820
5821 (defun gnus-article-edit-done (&optional arg)
5822 "Update the article edits and exit."
5823 (interactive "P")
5824 (let ((func gnus-article-edit-done-function)
5825 (buf (current-buffer))
5826 (start (window-start))
5827 (p (point))
5828 (winconf gnus-prev-winconf))
5829 (widen) ;; Widen it in case that users narrowed the buffer.
5830 (funcall func arg)
5831 (set-buffer buf)
5832 ;; The cache and backlog have to be flushed somewhat.
5833 (when gnus-keep-backlog
5834 (gnus-backlog-remove-article
5835 (car gnus-article-current) (cdr gnus-article-current)))
5836 ;; Flush original article as well.
5837 (save-excursion
5838 (when (get-buffer gnus-original-article-buffer)
5839 (set-buffer gnus-original-article-buffer)
5840 (setq gnus-original-article nil)))
5841 (when gnus-use-cache
5842 (gnus-cache-update-article
5843 (car gnus-article-current) (cdr gnus-article-current)))
5844 ;; We remove all text props from the article buffer.
5845 (kill-all-local-variables)
5846 (gnus-set-text-properties (point-min) (point-max) nil)
5847 (gnus-article-mode)
5848 (set-window-configuration winconf)
5849 (set-buffer buf)
5850 (set-window-start (get-buffer-window buf) start)
5851 (set-window-point (get-buffer-window buf) (point)))
5852 (gnus-summary-show-article))
5853
5854 (defun gnus-article-edit-exit ()
5855 "Exit the article editing without updating."
5856 (interactive)
5857 (when (or (not (buffer-modified-p))
5858 (yes-or-no-p "Article modified; kill anyway? "))
5859 (let ((curbuf (current-buffer))
5860 (p (point))
5861 (window-start (window-start)))
5862 (erase-buffer)
5863 (if (gnus-buffer-live-p gnus-original-article-buffer)
5864 (insert-buffer-substring gnus-original-article-buffer))
5865 (let ((winconf gnus-prev-winconf))
5866 (kill-all-local-variables)
5867 (gnus-article-mode)
5868 (set-window-configuration winconf)
5869 ;; Tippy-toe some to make sure that point remains where it was.
5870 (save-current-buffer
5871 (set-buffer curbuf)
5872 (set-window-start (get-buffer-window (current-buffer)) window-start)
5873 (goto-char p))))
5874 (gnus-summary-show-article)))
5875
5876 (defun gnus-article-edit-full-stops ()
5877 "Interactively repair spacing at end of sentences."
5878 (interactive)
5879 (save-excursion
5880 (goto-char (point-min))
5881 (search-forward-regexp "^$" nil t)
5882 (let ((case-fold-search nil))
5883 (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2"))))
5884
5885 ;;;
5886 ;;; Article highlights
5887 ;;;
5888
5889 ;; Written by Per Abrahamsen <abraham@iesd.auc.dk>.
5890
5891 ;;; Internal Variables:
5892
5893 (defcustom gnus-button-url-regexp
5894 (if (string-match "[[:digit:]]" "1") ;; support POSIX?
5895 "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
5896 "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
5897 "Regular expression that matches URLs."
5898 :group 'gnus-article-buttons
5899 :type 'regexp)
5900
5901 (defcustom gnus-button-valid-fqdn-regexp
5902 message-valid-fqdn-regexp
5903 "Regular expression that matches a valid FQDN."
5904 :version "22.1"
5905 :group 'gnus-article-buttons
5906 :type 'regexp)
5907
5908 ;; Regexp suggested by Felix Wiemann in <87oeuomcz9.fsf@news2.ososo.de>
5909 (defcustom gnus-button-valid-localpart-regexp
5910 "[a-z0-9$%(*-=?[_][^<>\")!;:,{}\n\t ]*"
5911 "Regular expression that matches a localpart of mail addresses or MIDs."
5912 :version "22.1"
5913 :group 'gnus-article-buttons
5914 :type 'regexp)
5915
5916 (defcustom gnus-button-man-handler 'manual-entry
5917 "Function to use for displaying man pages.
5918 The function must take at least one argument with a string naming the
5919 man page."
5920 :version "22.1"
5921 :type '(choice (function-item :tag "Man" manual-entry)
5922 (function-item :tag "Woman" woman)
5923 (function :tag "Other"))
5924 :group 'gnus-article-buttons)
5925
5926 (defcustom gnus-ctan-url "http://tug.ctan.org/tex-archive/"
5927 "Top directory of a CTAN \(Comprehensive TeX Archive Network\) archive.
5928 If the default site is too slow, try to find a CTAN mirror, see
5929 <URL:http://tug.ctan.org/tex-archive/CTAN.sites?action=/index.html>. See also
5930 the variable `gnus-button-handle-ctan'."
5931 :version "22.1"
5932 :group 'gnus-article-buttons
5933 :link '(custom-manual "(gnus)Group Parameters")
5934 :type '(choice (const "http://www.tex.ac.uk/tex-archive/")
5935 (const "http://tug.ctan.org/tex-archive/")
5936 (const "http://www.dante.de/CTAN/")
5937 (string :tag "Other")))
5938
5939 (defcustom gnus-button-ctan-handler 'browse-url
5940 "Function to use for displaying CTAN links.
5941 The function must take one argument, the string naming the URL."
5942 :version "22.1"
5943 :type '(choice (function-item :tag "Browse Url" browse-url)
5944 (function :tag "Other"))
5945 :group 'gnus-article-buttons)
5946
5947 (defcustom gnus-button-handle-ctan-bogus-regexp "^/?tex-archive/\\|^/"
5948 "Bogus strings removed from CTAN URLs."
5949 :version "22.1"
5950 :group 'gnus-article-buttons
5951 :type '(choice (const "^/?tex-archive/\\|/")
5952 (regexp :tag "Other")))
5953
5954 (defcustom gnus-button-ctan-directory-regexp
5955 (regexp-opt
5956 (list "archive-tools" "biblio" "bibliography" "digests" "documentation"
5957 "dviware" "fonts" "graphics" "help" "indexing" "info" "language"
5958 "languages" "macros" "nonfree" "obsolete" "support" "systems"
5959 "tds" "tools" "usergrps" "web") t)
5960 "Regular expression for ctan directories.
5961 It should match all directories in the top level of `gnus-ctan-url'."
5962 :version "22.1"
5963 :group 'gnus-article-buttons
5964 :type 'regexp)
5965
5966 (defcustom gnus-button-mid-or-mail-regexp
5967 (concat "\\b\\(<?" gnus-button-valid-localpart-regexp "@"
5968 gnus-button-valid-fqdn-regexp
5969 ">?\\)\\b")
5970 "Regular expression that matches a message ID or a mail address."
5971 :version "22.1"
5972 :group 'gnus-article-buttons
5973 :type 'regexp)
5974
5975 (defcustom gnus-button-prefer-mid-or-mail 'gnus-button-mid-or-mail-heuristic
5976 "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
5977 Strings like this can be either a message ID or a mail address. If it is one
5978 of the symbols `mid' or `mail', Gnus will always assume that the string is a
5979 message ID or a mail address, respectively. If this variable is set to the
5980 symbol `ask', always query the user what do do. If it is a function, this
5981 function will be called with the string as it's only argument. The function
5982 must return `mid', `mail', `invalid' or `ask'."
5983 :version "22.1"
5984 :group 'gnus-article-buttons
5985 :type '(choice (function-item :tag "Heuristic function"
5986 gnus-button-mid-or-mail-heuristic)
5987 (const ask)
5988 (const mid)
5989 (const mail)))
5990
5991 (defcustom gnus-button-mid-or-mail-heuristic-alist
5992 '((-10.0 . ".+\\$.+@")
5993 (-10.0 . "#")
5994 (-10.0 . "\\*")
5995 (-5.0 . "\\+[^+]*\\+.*@") ;; # two plus signs
5996 (-5.0 . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
5997 (-5.0 . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
5998 (-1.0 . "^[^a-z]+@")
5999 ;;
6000 (-5.0 . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
6001 (-5.0 . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
6002 (-3.0 . "[A-Z][A-Z][a-z][a-z].*@")
6003 (-5.0 . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
6004 ;;
6005 (-2.0 . "^[0-9]")
6006 (-1.0 . "^[0-9][0-9]")
6007 ;;
6008 ;; -3.0 /^[0-9][0-9a-fA-F]{2,2}/;
6009 (-3.0 . "^[0-9][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6010 ;; -5.0 /^[0-9][0-9a-fA-F]{3,3}/;
6011 (-5.0 . "^[0-9][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6012 ;;
6013 (-3.0 . "[0-9][0-9][0-9][0-9][0-9][^0-9].*@") ;; "[0-9]{5,}.*\@"
6014 (-3.0 . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][^0-9].*@")
6015 ;; "[0-9]{8,}.*\@"
6016 (-3.0
6017 . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*@")
6018 ;; "[0-9]{12,}.*\@"
6019 ;; compensation for TDMA dated mail addresses:
6020 (25.0 . "-dated-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+.*@")
6021 ;;
6022 (-20.0 . "\\.fsf@") ;; Gnus
6023 (-20.0 . "^slrn")
6024 (-20.0 . "^Pine")
6025 (-20.0 . "_-_") ;; Subject change in thread
6026 ;;
6027 (-20.0 . "\\.ln@") ;; leafnode
6028 (-30.0 . "@ID-[0-9]+\\.[a-zA-Z]+\\.dfncis\\.de")
6029 (-30.0 . "@4[Aa][Xx]\\.com") ;; Forte Agent
6030 ;;
6031 ;; (5.0 . "") ;; $local_part_len <= 7
6032 (10.0 . "^[^0-9]+@")
6033 (3.0 . "^[^0-9]+[0-9][0-9]?[0-9]?@")
6034 ;; ^[^0-9]+[0-9]{1,3}\@ digits only at end of local part
6035 (3.0 . "\@stud")
6036 ;;
6037 (2.0 . "[a-z][a-z][._-][A-Z][a-z].*@")
6038 ;;
6039 (0.5 . "^[A-Z][a-z]")
6040 (0.5 . "^[A-Z][a-z][a-z]")
6041 (1.5 . "^[A-Z][a-z][A-Z][a-z][^a-z]") ;; ^[A-Z][a-z]{3,3}
6042 (2.0 . "^[A-Z][a-z][A-Z][a-z][a-z][^a-z]")) ;; ^[A-Z][a-z]{4,4}
6043 "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
6044
6045 A negative RATE indicates a message IDs, whereas a positive indicates a mail
6046 address. The REGEXP is processed with `case-fold-search' set to nil."
6047 :version "22.1"
6048 :group 'gnus-article-buttons
6049 :type '(repeat (cons (number :tag "Rate")
6050 (regexp :tag "Regexp"))))
6051
6052 (defun gnus-button-mid-or-mail-heuristic (mid-or-mail)
6053 "Guess whether MID-OR-MAIL is a message ID or a mail address.
6054 Returns `mid' if MID-OR-MAIL is a message IDs, `mail' if it's a mail
6055 address, `ask' if unsure and `invalid' if the string is invalid."
6056 (let ((case-fold-search nil)
6057 (list gnus-button-mid-or-mail-heuristic-alist)
6058 (result 0) rate regexp lpartlen elem)
6059 (setq lpartlen
6060 (length (gnus-replace-in-string mid-or-mail "^\\(.*\\)@.*$" "\\1")))
6061 (gnus-message 8 "`%s', length of local part=`%s'." mid-or-mail lpartlen)
6062 ;; Certain special cases...
6063 (when (string-match
6064 (concat
6065 "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$\\|"
6066 "^[0-9]+\\.[0-9]+@compuserve\\|"
6067 "@public\\.gmane\\.org")
6068 mid-or-mail)
6069 (gnus-message 8 "`%s' is a known mail address." mid-or-mail)
6070 (setq result 'mail))
6071 (when (string-match "@.*@\\| " mid-or-mail)
6072 (gnus-message 8 "`%s' is invalid." mid-or-mail)
6073 (setq result 'invalid))
6074 ;; Nothing more to do, if result is not a number here...
6075 (when (numberp result)
6076 (while list
6077 (setq elem (car list)
6078 rate (car elem)
6079 regexp (cdr elem)
6080 list (cdr list))
6081 (when (string-match regexp mid-or-mail)
6082 (setq result (+ result rate))
6083 (gnus-message
6084 9 "`%s' matched `%s', rate `%s', result `%s'."
6085 mid-or-mail regexp rate result)))
6086 (when (<= lpartlen 7)
6087 (setq result (+ result 5.0))
6088 (gnus-message 9 "`%s' matched (<= lpartlen 7), result `%s'."
6089 mid-or-mail result))
6090 (when (>= lpartlen 12)
6091 (gnus-message 9 "`%s' matched (>= lpartlen 12)" mid-or-mail)
6092 (cond
6093 ((string-match "[0-9][^0-9]+[0-9].*@" mid-or-mail)
6094 ;; Long local part should contain realname if e-mail address,
6095 ;; too many digits: message-id.
6096 ;; $score -= 5.0 + 0.1 * $local_part_len;
6097 (setq rate (* -1.0 (+ 5.0 (* 0.1 lpartlen))))
6098 (setq result (+ result rate))
6099 (gnus-message
6100 9 "Many digits in `%s', rate `%s', result `%s'."
6101 mid-or-mail rate result))
6102 ((string-match "[^aeiouy][^aeiouy][^aeiouy][^aeiouy]+.*\@"
6103 mid-or-mail)
6104 ;; Too few vowels [^aeiouy]{4,}.*\@
6105 (setq result (+ result -5.0))
6106 (gnus-message
6107 9 "Few vowels in `%s', rate `%s', result `%s'."
6108 mid-or-mail -5.0 result))
6109 (t
6110 (setq result (+ result 5.0))
6111 (gnus-message
6112 9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
6113 (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
6114 ;; Maybe we should make this a customizable alist: (condition . 'result)
6115 (cond
6116 ((symbolp result) result)
6117 ;; Now convert number into proper results:
6118 ((< result -10.0) 'mid)
6119 ((> result 10.0) 'mail)
6120 (t 'ask))))
6121
6122 (defun gnus-button-handle-mid-or-mail (mid-or-mail)
6123 (let* ((pref gnus-button-prefer-mid-or-mail) guessed
6124 (url-mid (concat "news" ":" mid-or-mail))
6125 (url-mailto (concat "mailto" ":" mid-or-mail)))
6126 (gnus-message 9 "mid-or-mail=%s" mid-or-mail)
6127 (when (fboundp pref)
6128 (setq guessed
6129 ;; get rid of surrounding angles...
6130 (funcall pref
6131 (gnus-replace-in-string mid-or-mail "^<\\|>$" "")))
6132 (if (or (eq 'mid guessed) (eq 'mail guessed))
6133 (setq pref guessed)
6134 (setq pref 'ask)))
6135 (if (eq pref 'ask)
6136 (save-window-excursion
6137 (if (y-or-n-p (concat "Is <" mid-or-mail "> a mail address? "))
6138 (setq pref 'mail)
6139 (setq pref 'mid))))
6140 (cond ((eq pref 'mid)
6141 (gnus-message 8 "calling `gnus-button-handle-news' %s" url-mid)
6142 (gnus-button-handle-news url-mid))
6143 ((eq pref 'mail)
6144 (gnus-message 8 "calling `gnus-url-mailto' %s" url-mailto)
6145 (gnus-url-mailto url-mailto))
6146 (t (gnus-message 3 "Invalid string.")))))
6147
6148 (defun gnus-button-handle-custom (url)
6149 "Follow a Custom URL."
6150 (customize-apropos (gnus-url-unhex-string url)))
6151
6152 (defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
6153
6154 ;; FIXME: Maybe we should merge some of the functions that do quite similar
6155 ;; stuff?
6156
6157 (defun gnus-button-handle-describe-function (url)
6158 "Call `describe-function' when pushing the corresponding URL button."
6159 (describe-function
6160 (intern
6161 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6162
6163 (defun gnus-button-handle-describe-variable (url)
6164 "Call `describe-variable' when pushing the corresponding URL button."
6165 (describe-variable
6166 (intern
6167 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6168
6169 (defun gnus-button-handle-symbol (url)
6170 "Display help on variable or function.
6171 Calls `describe-variable' or `describe-function'."
6172 (let ((sym (intern url)))
6173 (cond
6174 ((fboundp sym) (describe-function sym))
6175 ((boundp sym) (describe-variable sym))
6176 (t (gnus-message 3 "`%s' is not a known function of variable." url)))))
6177
6178 (defun gnus-button-handle-describe-key (url)
6179 "Call `describe-key' when pushing the corresponding URL button."
6180 (let* ((key-string
6181 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))
6182 (keys (ignore-errors (eval `(kbd ,key-string)))))
6183 (if keys
6184 (describe-key keys)
6185 (gnus-message 3 "Invalid key sequence in button: %s" key-string))))
6186
6187 (defun gnus-button-handle-apropos (url)
6188 "Call `apropos' when pushing the corresponding URL button."
6189 (apropos (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6190
6191 (defun gnus-button-handle-apropos-command (url)
6192 "Call `apropos' when pushing the corresponding URL button."
6193 (apropos-command
6194 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6195
6196 (defun gnus-button-handle-apropos-variable (url)
6197 "Call `apropos' when pushing the corresponding URL button."
6198 (funcall
6199 (if (fboundp 'apropos-variable) 'apropos-variable 'apropos)
6200 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6201
6202 (defun gnus-button-handle-apropos-documentation (url)
6203 "Call `apropos' when pushing the corresponding URL button."
6204 (funcall
6205 (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
6206 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6207
6208 (defun gnus-button-handle-library (url)
6209 "Call `locate-library' when pushing the corresponding URL button."
6210 (gnus-message 9 "url=`%s'" url)
6211 (let* ((lib (locate-library url))
6212 (file (gnus-replace-in-string (or lib "") "\.elc" ".el")))
6213 (if (not lib)
6214 (gnus-message 1 "Cannot locale library `%s'." url)
6215 (find-file-read-only file))))
6216
6217 (defun gnus-button-handle-ctan (url)
6218 "Call `browse-url' when pushing a CTAN URL button."
6219 (funcall
6220 gnus-button-ctan-handler
6221 (concat
6222 gnus-ctan-url
6223 (gnus-replace-in-string url gnus-button-handle-ctan-bogus-regexp ""))))
6224
6225 (defcustom gnus-button-tex-level 5
6226 "*Integer that says how many TeX-related buttons Gnus will show.
6227 The higher the number, the more buttons will appear and the more false
6228 positives are possible. Note that you can set this variable local to
6229 specific groups. Setting it higher in TeX groups is probably a good idea.
6230 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6231 how to set variables in specific groups."
6232 :version "22.1"
6233 :group 'gnus-article-buttons
6234 :link '(custom-manual "(gnus)Group Parameters")
6235 :type 'integer)
6236
6237 (defcustom gnus-button-man-level 5
6238 "*Integer that says how many man-related buttons Gnus will show.
6239 The higher the number, the more buttons will appear and the more false
6240 positives are possible. Note that you can set this variable local to
6241 specific groups. Setting it higher in Unix groups is probably a good idea.
6242 See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6243 how to set variables in specific groups."
6244 :version "22.1"
6245 :group 'gnus-article-buttons
6246 :link '(custom-manual "(gnus)Group Parameters")
6247 :type 'integer)
6248
6249 (defcustom gnus-button-emacs-level 5
6250 "*Integer that says how many emacs-related buttons Gnus will show.
6251 The higher the number, the more buttons will appear and the more false
6252 positives are possible. Note that you can set this variable local to
6253 specific groups. Setting it higher in Emacs or Gnus related groups is
6254 probably a good idea. See Info node `(gnus)Group Parameters' and the variable
6255 `gnus-parameters' on how to set variables in specific groups."
6256 :version "22.1"
6257 :group 'gnus-article-buttons
6258 :link '(custom-manual "(gnus)Group Parameters")
6259 :type 'integer)
6260
6261 (defcustom gnus-button-message-level 5
6262 "*Integer that says how many buttons for news or mail messages will appear.
6263 The higher the number, the more buttons will appear and the more false
6264 positives are possible."
6265 ;; mail addresses, MIDs, URLs for news, ...
6266 :version "22.1"
6267 :group 'gnus-article-buttons
6268 :type 'integer)
6269
6270 (defcustom gnus-button-browse-level 5
6271 "*Integer that says how many buttons for browsing will appear.
6272 The higher the number, the more buttons will appear and the more false
6273 positives are possible."
6274 ;; stuff handled by `browse-url' or `gnus-button-embedded-url'
6275 :version "22.1"
6276 :group 'gnus-article-buttons
6277 :type 'integer)
6278
6279 (defcustom gnus-button-alist
6280 '(("<\\(url:[>\n\t ]*?\\)?\\(nntp\\|news\\):[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
6281 0 (>= gnus-button-message-level 0) gnus-button-handle-news 3)
6282 ((concat "\\b\\(nntp\\|news\\):\\("
6283 gnus-button-valid-localpart-regexp "@[a-z0-9.-]+[a-z]\\)")
6284 0 t gnus-button-handle-news 2)
6285 ("\\(\\b<\\(url:[>\n\t ]*\\)?\\(nntp\\|news\\):[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
6286 1 (>= gnus-button-message-level 0) gnus-button-fetch-group 5)
6287 ("\\b\\(nntp\\|news\\):\\(//\\)?\\([^'\">\n\t ]+\\)"
6288 0 (>= gnus-button-message-level 0) gnus-button-fetch-group 3)
6289 ;; RFC 2392 (Don't allow `/' in domain part --> CID)
6290 ("\\bmid:\\(//\\)?\\([^'\">\n\t ]+@[^'\">\n\t /]+\\)"
6291 0 (>= gnus-button-message-level 0) gnus-button-message-id 2)
6292 ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)"
6293 2 (>= gnus-button-message-level 0) gnus-button-message-id 3)
6294 ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>"
6295 0 (>= gnus-button-message-level 0) gnus-url-mailto 2)
6296 ;; RFC 2368 (The mailto URL scheme)
6297 ("\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
6298 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6299 ("\\bmailto:\\([^ \n\t]+\\)"
6300 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6301 ;; CTAN
6302 ((concat "\\bCTAN:[ \t\n]?[^>)!;:,'\n\t ]*\\("
6303 gnus-button-ctan-directory-regexp
6304 "[^][>)!;:,'\n\t ]+\\)")
6305 0 (>= gnus-button-tex-level 1) gnus-button-handle-ctan 1)
6306 ((concat "\\btex-archive/\\("
6307 gnus-button-ctan-directory-regexp
6308 "/[-_.a-z0-9/]+[-_./a-z0-9]+[/a-z0-9]\\)")
6309 1 (>= gnus-button-tex-level 6) gnus-button-handle-ctan 1)
6310 ((concat
6311 "\\b\\("
6312 gnus-button-ctan-directory-regexp
6313 "/[-_.a-z0-9]+/[-_./a-z0-9]+[/a-z0-9]\\)")
6314 1 (>= gnus-button-tex-level 8) gnus-button-handle-ctan 1)
6315 ;; This is info (home-grown style) <info://foo/bar+baz>
6316 ("\\binfo://\\([^'\">\n\t ]+\\)"
6317 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
6318 ;; Info GNOME style <info:foo#bar_baz>
6319 ("\\binfo:\\([^('\n\t\r \"><][^'\n\t\r \"><]*\\)"
6320 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-gnome 1)
6321 ;; Info KDE style <info:(foo)bar baz>
6322 ("<\\(info:\\(([^)]+)[^>\n\r]*\\)\\)>"
6323 1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
6324 ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
6325 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
6326 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
6327 ;; Info links like `C-h i d m CC Mode RET'
6328 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 2)
6329 ;; This is custom
6330 ("\\bcustom:\\(//\\)?\\([^'\">\n\t ]+\\)"
6331 0 (>= gnus-button-emacs-level 5) gnus-button-handle-custom 2)
6332 ("M-x[ \t\n]customize-[^ ]+[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
6333 (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1)
6334 ;; Emacs help commands
6335 ("M-x[ \t\n]+apropos[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6336 ;; regexp doesn't match arguments containing ` '.
6337 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos 1)
6338 ("M-x[ \t\n]+apropos-command[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6339 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-command 1)
6340 ("M-x[ \t\n]+apropos-variable[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6341 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
6342 ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6343 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
6344 ;; The following entries may lead to many false positives so don't enable
6345 ;; them by default (use a high button level).
6346 ("/\\([a-z][-a-z0-9]+\\.el\\)\\>[^.?]"
6347 ;; Exclude [.?] for URLs in gmane.emacs.cvs
6348 1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6349 ("`\\([a-z][-a-z0-9]+\\.el\\)'"
6350 1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6351 ("`\\([a-z][a-z0-9]+-[a-z]+-[-a-z]+\\|\\(gnus\\|message\\)-[-a-z]+\\)'"
6352 0 (>= gnus-button-emacs-level 8) gnus-button-handle-symbol 1)
6353 ("`\\([a-z][a-z0-9]+-[a-z]+\\)'"
6354 0 (>= gnus-button-emacs-level 9) gnus-button-handle-symbol 1)
6355 ("(setq[ \t\n]+\\([a-z][a-z0-9]+-[-a-z0-9]+\\)[ \t\n]+.+)"
6356 1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-variable 1)
6357 ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6358 1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-function 1)
6359 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6360 0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
6361 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6362 0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
6363 ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'"
6364 ;; Unlike the other regexps we really have to require quoting
6365 ;; here to determine where it ends.
6366 1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
6367 ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
6368 ("<URL: *\\([^<>]*\\)>"
6369 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6370 ;; RFC 2396 (2.4.3., delims) ...
6371 ("\"URL: *\\([^\"]*\\)\""
6372 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6373 ;; RFC 2396 (2.4.3., delims) ...
6374 ("\"URL: *\\([^\"]*\\)\""
6375 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6376 ;; Raw URLs.
6377 (gnus-button-url-regexp
6378 0 (>= gnus-button-browse-level 0) browse-url 0)
6379 ;; man pages
6380 ("\\b\\([a-z][a-z]+([1-9])\\)\\W"
6381 0 (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
6382 gnus-button-handle-man 1)
6383 ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
6384 ("\\b\\([a-z][-_.a-z0-9]+([1-9])\\)\\W"
6385 0 (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
6386 gnus-button-handle-man 1)
6387 ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
6388 ;; SoWWWAnchor(3iv), XSelectInput(3X11), X(1), X(7)
6389 ("\\b\\(\\(?:[a-z][-+_.:a-z0-9]+([1-9][X1a-z]*)\\)\\|\\b\\(?:X([1-9])\\)\\)\\W"
6390 0 (>= gnus-button-man-level 5) gnus-button-handle-man 1)
6391 ;; MID or mail: To avoid too many false positives we don't try to catch
6392 ;; all kind of allowed MIDs or mail addresses. Domain part must contain
6393 ;; at least one dot. TLD must contain two or three chars or be a know TLD
6394 ;; (info|name|...). Put this entry near the _end_ of `gnus-button-alist'
6395 ;; so that non-ambiguous entries (see above) match first.
6396 (gnus-button-mid-or-mail-regexp
6397 0 (>= gnus-button-message-level 5) gnus-button-handle-mid-or-mail 1))
6398 "*Alist of regexps matching buttons in article bodies.
6399
6400 Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
6401 REGEXP: is the string (case insensitive) matching text around the button (can
6402 also be Lisp expression evaluating to a string),
6403 BUTTON: is the number of the regexp grouping actually matching the button,
6404 FORM: is a Lisp expression which must eval to true for the button to
6405 be added,
6406 CALLBACK: is the function to call when the user push this button, and each
6407 PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
6408
6409 CALLBACK can also be a variable, in that case the value of that
6410 variable it the real callback function."
6411 :group 'gnus-article-buttons
6412 :type '(repeat (list (choice regexp variable sexp)
6413 (integer :tag "Button")
6414 (sexp :tag "Form")
6415 (function :tag "Callback")
6416 (repeat :tag "Par"
6417 :inline t
6418 (integer :tag "Regexp group")))))
6419
6420 (defcustom gnus-header-button-alist
6421 '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
6422 0 (>= gnus-button-message-level 0) gnus-button-message-id 0)
6423 ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$"
6424 1 (>= gnus-button-message-level 0) gnus-button-reply 1)
6425 ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
6426 0 (>= gnus-button-message-level 0) gnus-msg-mail 0)
6427 ("^X-[Uu][Rr][Ll]:" gnus-button-url-regexp
6428 0 (>= gnus-button-browse-level 0) browse-url 0)
6429 ("^Subject:" gnus-button-url-regexp
6430 0 (>= gnus-button-browse-level 0) browse-url 0)
6431 ("^[^:]+:" gnus-button-url-regexp
6432 0 (>= gnus-button-browse-level 0) browse-url 0)
6433 ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
6434 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6435 ("^[^:]+:" "\\(<\\(url: \\)?\\(nntp\\|news\\):\\([^>\n ]*\\)>\\)"
6436 1 (>= gnus-button-message-level 0) gnus-button-message-id 4))
6437 "*Alist of headers and regexps to match buttons in article heads.
6438
6439 This alist is very similar to `gnus-button-alist', except that each
6440 alist has an additional HEADER element first in each entry:
6441
6442 \(HEADER REGEXP BUTTON FORM CALLBACK PAR)
6443
6444 HEADER is a regexp to match a header. For a fuller explanation, see
6445 `gnus-button-alist'."
6446 :group 'gnus-article-buttons
6447 :group 'gnus-article-headers
6448 :type '(repeat (list (regexp :tag "Header")
6449 (choice regexp variable)
6450 (integer :tag "Button")
6451 (sexp :tag "Form")
6452 (function :tag "Callback")
6453 (repeat :tag "Par"
6454 :inline t
6455 (integer :tag "Regexp group")))))
6456
6457 (defvar gnus-button-regexp nil)
6458 (defvar gnus-button-marker-list nil)
6459 ;; Regexp matching any of the regexps from `gnus-button-alist'.
6460
6461 (defvar gnus-button-last nil)
6462 ;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
6463
6464 ;;; Commands:
6465
6466 (defun gnus-article-push-button (event)
6467 "Check text under the mouse pointer for a callback function.
6468 If the text under the mouse pointer has a `gnus-callback' property,
6469 call it with the value of the `gnus-data' text property."
6470 (interactive "e")
6471 (set-buffer (window-buffer (posn-window (event-start event))))
6472 (let* ((pos (posn-point (event-start event)))
6473 (data (get-text-property pos 'gnus-data))
6474 (fun (get-text-property pos 'gnus-callback)))
6475 (goto-char pos)
6476 (when fun
6477 (funcall fun data))))
6478
6479 (defun gnus-article-press-button ()
6480 "Check text at point for a callback function.
6481 If the text at point has a `gnus-callback' property,
6482 call it with the value of the `gnus-data' text property."
6483 (interactive)
6484 (let ((data (get-text-property (point) 'gnus-data))
6485 (fun (get-text-property (point) 'gnus-callback)))
6486 (when fun
6487 (funcall fun data))))
6488
6489 (defun gnus-article-highlight (&optional force)
6490 "Highlight current article.
6491 This function calls `gnus-article-highlight-headers',
6492 `gnus-article-highlight-citation',
6493 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
6494 do the highlighting. See the documentation for those functions."
6495 (interactive (list 'force))
6496 (gnus-article-highlight-headers)
6497 (gnus-article-highlight-citation force)
6498 (gnus-article-highlight-signature)
6499 (gnus-article-add-buttons force)
6500 (gnus-article-add-buttons-to-head))
6501
6502 (defun gnus-article-highlight-some (&optional force)
6503 "Highlight current article.
6504 This function calls `gnus-article-highlight-headers',
6505 `gnus-article-highlight-signature', and `gnus-article-add-buttons' to
6506 do the highlighting. See the documentation for those functions."
6507 (interactive (list 'force))
6508 (gnus-article-highlight-headers)
6509 (gnus-article-highlight-signature)
6510 (gnus-article-add-buttons))
6511
6512 (defun gnus-article-highlight-headers ()
6513 "Highlight article headers as specified by `gnus-header-face-alist'."
6514 (interactive)
6515 (save-excursion
6516 (set-buffer gnus-article-buffer)
6517 (save-restriction
6518 (let ((alist gnus-header-face-alist)
6519 (inhibit-read-only t)
6520 (case-fold-search t)
6521 (inhibit-point-motion-hooks t)
6522 entry regexp header-face field-face from hpoints fpoints)
6523 (article-narrow-to-head)
6524 (while (setq entry (pop alist))
6525 (goto-char (point-min))
6526 (setq regexp (concat "^\\("
6527 (if (string-equal "" (nth 0 entry))
6528 "[^\t ]"
6529 (nth 0 entry))
6530 "\\)")
6531 header-face (nth 1 entry)
6532 field-face (nth 2 entry))
6533 (while (and (re-search-forward regexp nil t)
6534 (not (eobp)))
6535 (beginning-of-line)
6536 (setq from (point))
6537 (unless (search-forward ":" nil t)
6538 (forward-char 1))
6539 (when (and header-face
6540 (not (memq (point) hpoints)))
6541 (push (point) hpoints)
6542 (gnus-put-text-property from (point) 'face header-face))
6543 (when (and field-face
6544 (not (memq (setq from (point)) fpoints)))
6545 (push from fpoints)
6546 (if (re-search-forward "^[^ \t]" nil t)
6547 (forward-char -2)
6548 (goto-char (point-max)))
6549 (gnus-put-text-property from (point) 'face field-face))))))))
6550
6551 (defun gnus-article-highlight-signature ()
6552 "Highlight the signature in an article.
6553 It does this by highlighting everything after
6554 `gnus-signature-separator' using the face `gnus-signature'."
6555 (interactive)
6556 (save-excursion
6557 (set-buffer gnus-article-buffer)
6558 (let ((inhibit-read-only t)
6559 (inhibit-point-motion-hooks t))
6560 (save-restriction
6561 (when (and gnus-signature-face
6562 (gnus-article-narrow-to-signature))
6563 (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
6564 'face gnus-signature-face)
6565 (widen)
6566 (gnus-article-search-signature)
6567 (let ((start (match-beginning 0))
6568 (end (set-marker (make-marker) (1+ (match-end 0)))))
6569 (gnus-article-add-button start (1- end) 'gnus-signature-toggle
6570 end)))))))
6571
6572 (defun gnus-button-in-region-p (b e prop)
6573 "Say whether PROP exists in the region."
6574 (text-property-not-all b e prop nil))
6575
6576 (defun gnus-article-add-buttons (&optional force)
6577 "Find external references in the article and make buttons of them.
6578 \"External references\" are things like Message-IDs and URLs, as
6579 specified by `gnus-button-alist'."
6580 (interactive (list 'force))
6581 (save-excursion
6582 (set-buffer gnus-article-buffer)
6583 (let ((inhibit-read-only t)
6584 (inhibit-point-motion-hooks t)
6585 (case-fold-search t)
6586 (alist gnus-button-alist)
6587 beg entry regexp)
6588 ;; Remove all old markers.
6589 (let (marker entry new-list)
6590 (while (setq marker (pop gnus-button-marker-list))
6591 (if (or (< marker (point-min)) (>= marker (point-max)))
6592 (push marker new-list)
6593 (goto-char marker)
6594 (when (setq entry (gnus-button-entry))
6595 (put-text-property (match-beginning (nth 1 entry))
6596 (match-end (nth 1 entry))
6597 'gnus-callback nil))
6598 (set-marker marker nil)))
6599 (setq gnus-button-marker-list new-list))
6600 ;; We skip the headers.
6601 (article-goto-body)
6602 (setq beg (point))
6603 (while (setq entry (pop alist))
6604 (setq regexp (eval (car entry)))
6605 (goto-char beg)
6606 (while (re-search-forward regexp nil t)
6607 (let* ((start (and entry (match-beginning (nth 1 entry))))
6608 (end (and entry (match-end (nth 1 entry))))
6609 (from (match-beginning 0)))
6610 (when (and (or (eq t (nth 2 entry))
6611 (eval (nth 2 entry)))
6612 (not (gnus-button-in-region-p
6613 start end 'gnus-callback)))
6614 ;; That optional form returned non-nil, so we add the
6615 ;; button.
6616 (gnus-article-add-button
6617 start end 'gnus-button-push
6618 (car (push (set-marker (make-marker) from)
6619 gnus-button-marker-list))))))))))
6620
6621 ;; Add buttons to the head of an article.
6622 (defun gnus-article-add-buttons-to-head ()
6623 "Add buttons to the head of the article."
6624 (interactive)
6625 (save-excursion
6626 (set-buffer gnus-article-buffer)
6627 (save-restriction
6628 (let ((inhibit-read-only t)
6629 (inhibit-point-motion-hooks t)
6630 (case-fold-search t)
6631 (alist gnus-header-button-alist)
6632 entry beg end)
6633 (article-narrow-to-head)
6634 (while alist
6635 ;; Each alist entry.
6636 (setq entry (car alist)
6637 alist (cdr alist))
6638 (goto-char (point-min))
6639 (while (re-search-forward (car entry) nil t)
6640 ;; Each header matching the entry.
6641 (setq beg (match-beginning 0))
6642 (setq end (or (and (re-search-forward "^[^ \t]" nil t)
6643 (match-beginning 0))
6644 (point-max)))
6645 (goto-char beg)
6646 (while (re-search-forward (eval (nth 1 entry)) end t)
6647 ;; Each match within a header.
6648 (let* ((entry (cdr entry))
6649 (start (match-beginning (nth 1 entry)))
6650 (end (match-end (nth 1 entry)))
6651 (form (nth 2 entry)))
6652 (goto-char (match-end 0))
6653 (when (eval form)
6654 (gnus-article-add-button
6655 start end (nth 3 entry)
6656 (buffer-substring (match-beginning (nth 4 entry))
6657 (match-end (nth 4 entry)))))))
6658 (goto-char end)))))))
6659
6660 ;;; External functions:
6661
6662 (defun gnus-article-add-button (from to fun &optional data)
6663 "Create a button between FROM and TO with callback FUN and data DATA."
6664 (when gnus-article-button-face
6665 (gnus-overlay-put (gnus-make-overlay from to)
6666 'face gnus-article-button-face))
6667 (gnus-add-text-properties
6668 from to
6669 (nconc (and gnus-article-mouse-face
6670 (list gnus-mouse-face-prop gnus-article-mouse-face))
6671 (list 'gnus-callback fun)
6672 (and data (list 'gnus-data data))))
6673 (widget-convert-button 'link from to :action 'gnus-widget-press-button
6674 :button-keymap gnus-widget-button-keymap))
6675
6676 ;;; Internal functions:
6677
6678 (defun gnus-article-set-globals ()
6679 (save-excursion
6680 (set-buffer gnus-summary-buffer)
6681 (gnus-set-global-variables)))
6682
6683 (defun gnus-signature-toggle (end)
6684 (save-excursion
6685 (set-buffer gnus-article-buffer)
6686 (let ((inhibit-read-only t)
6687 (inhibit-point-motion-hooks t))
6688 (if (text-property-any end (point-max) 'article-type 'signature)
6689 (progn
6690 (gnus-delete-wash-type 'signature)
6691 (gnus-remove-text-properties-when
6692 'article-type 'signature end (point-max)
6693 (cons 'article-type (cons 'signature
6694 gnus-hidden-properties))))
6695 (gnus-add-wash-type 'signature)
6696 (gnus-add-text-properties-when
6697 'article-type nil end (point-max)
6698 (cons 'article-type (cons 'signature
6699 gnus-hidden-properties)))))
6700 (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist))
6701 (gnus-set-mode-line 'article))))
6702
6703 (defun gnus-button-entry ()
6704 ;; Return the first entry in `gnus-button-alist' matching this place.
6705 (let ((alist gnus-button-alist)
6706 (entry nil))
6707 (while alist
6708 (setq entry (pop alist))
6709 (if (looking-at (eval (car entry)))
6710 (setq alist nil)
6711 (setq entry nil)))
6712 entry))
6713
6714 (defun gnus-button-push (marker)
6715 ;; Push button starting at MARKER.
6716 (save-excursion
6717 (goto-char marker)
6718 (let* ((entry (gnus-button-entry))
6719 (inhibit-point-motion-hooks t)
6720 (fun (nth 3 entry))
6721 (args (mapcar (lambda (group)
6722 (let ((string (match-string group)))
6723 (gnus-set-text-properties
6724 0 (length string) nil string)
6725 string))
6726 (nthcdr 4 entry))))
6727 (cond
6728 ((fboundp fun)
6729 (apply fun args))
6730 ((and (boundp fun)
6731 (fboundp (symbol-value fun)))
6732 (apply (symbol-value fun) args))
6733 (t
6734 (gnus-message 1 "You must define `%S' to use this button"
6735 (cons fun args)))))))
6736
6737 (defun gnus-parse-news-url (url)
6738 (let (scheme server port group message-id articles)
6739 (with-temp-buffer
6740 (insert url)
6741 (goto-char (point-min))
6742 (when (looking-at "\\([A-Za-z]+\\):")
6743 (setq scheme (match-string 1))
6744 (goto-char (match-end 0)))
6745 (when (looking-at "//\\([^:/]+\\)\\(:?\\)\\([0-9]+\\)?/")
6746 (setq server (match-string 1))
6747 (setq port (if (stringp (match-string 3))
6748 (string-to-number (match-string 3))
6749 (match-string 3)))
6750 (goto-char (match-end 0)))
6751
6752 (cond
6753 ((looking-at "\\(.*@.*\\)")
6754 (setq message-id (match-string 1)))
6755 ((looking-at "\\([^/]+\\)/\\([-0-9]+\\)")
6756 (setq group (match-string 1)
6757 articles (split-string (match-string 2) "-")))
6758 ((looking-at "\\([^/]+\\)/?")
6759 (setq group (match-string 1)))
6760 (t
6761 (error "Unknown news URL syntax"))))
6762 (list scheme server port group message-id articles)))
6763
6764 (defun gnus-button-handle-news (url)
6765 "Fetch a news URL."
6766 (destructuring-bind (scheme server port group message-id articles)
6767 (gnus-parse-news-url url)
6768 (cond
6769 (message-id
6770 (save-excursion
6771 (set-buffer gnus-summary-buffer)
6772 (if server
6773 (let ((gnus-refer-article-method
6774 (nconc (list (list 'nntp server))
6775 gnus-refer-article-method))
6776 (nntp-port-number (or port "nntp")))
6777 (gnus-message 7 "Fetching %s with %s"
6778 message-id gnus-refer-article-method)
6779 (gnus-summary-refer-article message-id))
6780 (gnus-summary-refer-article message-id))))
6781 (group
6782 (gnus-button-fetch-group url)))))
6783
6784 (defun gnus-button-handle-man (url)
6785 "Fetch a man page."
6786 (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
6787 (when (eq gnus-button-man-handler 'woman)
6788 (setq url (gnus-replace-in-string url "([1-9][X1a-z]*).*\\'" "")))
6789 (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
6790 (funcall gnus-button-man-handler url))
6791
6792 (defun gnus-button-handle-info-url (url)
6793 "Fetch an info URL."
6794 (setq url (mm-subst-char-in-string ?+ ?\ url))
6795 (cond
6796 ((string-match "^\\([^:/]+\\)?/\\(.*\\)" url)
6797 (gnus-info-find-node
6798 (concat "(" (or (gnus-url-unhex-string (match-string 1 url))
6799 "Gnus")
6800 ")" (gnus-url-unhex-string (match-string 2 url)))))
6801 ((string-match "([^)\"]+)[^\"]+" url)
6802 (setq url
6803 (gnus-replace-in-string
6804 (gnus-replace-in-string url "[\n\t ]+" " ") "\"" ""))
6805 (gnus-info-find-node url))
6806 (t (error "Can't parse %s" url))))
6807
6808 (defun gnus-button-handle-info-url-gnome (url)
6809 "Fetch GNOME style info URL."
6810 (setq url (mm-subst-char-in-string ?_ ?\ url))
6811 (if (string-match "\\([^#]+\\)#?\\(.*\\)" url)
6812 (gnus-info-find-node
6813 (concat "("
6814 (gnus-url-unhex-string
6815 (match-string 1 url))
6816 ")"
6817 (or (gnus-url-unhex-string
6818 (match-string 2 url))
6819 "Top")))
6820 (error "Can't parse %s" url)))
6821
6822 (defun gnus-button-handle-info-url-kde (url)
6823 "Fetch KDE style info URL."
6824 (gnus-info-find-node (gnus-url-unhex-string url)))
6825
6826 (defun gnus-button-handle-info-keystrokes (url)
6827 "Call `info' when pushing the corresponding URL button."
6828 ;; For links like `C-h i d m gnus RET', `C-h i d m CC Mode RET'.
6829 (info)
6830 (Info-directory)
6831 (Info-menu url))
6832
6833 (defun gnus-button-message-id (message-id)
6834 "Fetch MESSAGE-ID."
6835 (save-excursion
6836 (set-buffer gnus-summary-buffer)
6837 (gnus-summary-refer-article message-id)))
6838
6839 (defun gnus-button-fetch-group (address)
6840 "Fetch GROUP specified by ADDRESS."
6841 (if (not (string-match "[:/]" address))
6842 ;; This is just a simple group url.
6843 (gnus-group-read-ephemeral-group address gnus-select-method)
6844 (if (not
6845 (string-match
6846 "^\\([^:/]+\\)\\(:\\([^/]+\\)\\)?/\\([^/]+\\)\\(/\\([0-9]+\\)\\)?"
6847 address))
6848 (error "Can't parse %s" address)
6849 (gnus-group-read-ephemeral-group
6850 (match-string 4 address)
6851 `(nntp ,(match-string 1 address)
6852 (nntp-address ,(match-string 1 address))
6853 (nntp-port-number ,(if (match-end 3)
6854 (match-string 3 address)
6855 "nntp")))
6856 nil nil nil
6857 (and (match-end 6) (list (string-to-number (match-string 6 address))))))))
6858
6859 (defun gnus-url-parse-query-string (query &optional downcase)
6860 (let (retval pairs cur key val)
6861 (setq pairs (split-string query "&"))
6862 (while pairs
6863 (setq cur (car pairs)
6864 pairs (cdr pairs))
6865 (if (not (string-match "=" cur))
6866 nil ; Grace
6867 (setq key (gnus-url-unhex-string (substring cur 0 (match-beginning 0)))
6868 val (gnus-url-unhex-string (substring cur (match-end 0) nil) t))
6869 (if downcase
6870 (setq key (downcase key)))
6871 (setq cur (assoc key retval))
6872 (if cur
6873 (setcdr cur (cons val (cdr cur)))
6874 (setq retval (cons (list key val) retval)))))
6875 retval))
6876
6877 (defun gnus-url-mailto (url)
6878 ;; Send mail to someone
6879 (when (string-match "mailto:/*\\(.*\\)" url)
6880 (setq url (substring url (match-beginning 1) nil)))
6881 (let (to args subject func)
6882 (setq args (gnus-url-parse-query-string
6883 (if (string-match "^\\?" url)
6884 (substring url 1)
6885 (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
6886 (concat "to=" (match-string 1 url) "&"
6887 (match-string 2 url))
6888 (concat "to=" url)))
6889 t)
6890 subject (cdr-safe (assoc "subject" args)))
6891 (gnus-msg-mail)
6892 (while args
6893 (setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
6894 (if (fboundp func)
6895 (funcall func)
6896 (message-position-on-field (caar args)))
6897 (insert (gnus-replace-in-string
6898 (mapconcat 'identity (reverse (cdar args)) ", ")
6899 "\r\n" "\n" t))
6900 (setq args (cdr args)))
6901 (if subject
6902 (message-goto-body)
6903 (message-goto-subject))))
6904
6905 (defun gnus-button-embedded-url (address)
6906 "Activate ADDRESS with `browse-url'."
6907 (browse-url (gnus-strip-whitespace address)))
6908
6909 ;;; Next/prev buttons in the article buffer.
6910
6911 (defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
6912 (defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
6913
6914 (defvar gnus-prev-page-map
6915 (let ((map (make-sparse-keymap)))
6916 (unless (>= emacs-major-version 21)
6917 ;; XEmacs doesn't care.
6918 (set-keymap-parent map gnus-article-mode-map))
6919 (define-key map gnus-mouse-2 'gnus-button-prev-page)
6920 (define-key map "\r" 'gnus-button-prev-page)
6921 map))
6922
6923 (defvar gnus-next-page-map
6924 (let ((map (make-sparse-keymap)))
6925 (unless (>= emacs-major-version 21)
6926 ;; XEmacs doesn't care.
6927 (set-keymap-parent map gnus-article-mode-map))
6928 (define-key map gnus-mouse-2 'gnus-button-next-page)
6929 (define-key map "\r" 'gnus-button-next-page)
6930 map))
6931
6932 (defun gnus-insert-prev-page-button ()
6933 (let ((b (point))
6934 (inhibit-read-only t))
6935 (gnus-eval-format
6936 gnus-prev-page-line-format nil
6937 `(,@(gnus-local-map-property gnus-prev-page-map)
6938 gnus-prev t
6939 gnus-callback gnus-article-button-prev-page
6940 article-type annotation))
6941 (widget-convert-button
6942 'link b (if (bolp)
6943 ;; Exclude a newline.
6944 (1- (point))
6945 (point))
6946 :action 'gnus-button-prev-page
6947 :button-keymap gnus-prev-page-map)))
6948
6949 (defun gnus-button-next-page (&optional args more-args)
6950 "Go to the next page."
6951 (interactive)
6952 (let ((win (selected-window)))
6953 (select-window (gnus-get-buffer-window gnus-article-buffer t))
6954 (gnus-article-next-page)
6955 (select-window win)))
6956
6957 (defun gnus-button-prev-page (&optional args more-args)
6958 "Go to the prev page."
6959 (interactive)
6960 (let ((win (selected-window)))
6961 (select-window (gnus-get-buffer-window gnus-article-buffer t))
6962 (gnus-article-prev-page)
6963 (select-window win)))
6964
6965 (defun gnus-insert-next-page-button ()
6966 (let ((b (point))
6967 (inhibit-read-only t))
6968 (gnus-eval-format gnus-next-page-line-format nil
6969 `(,@(gnus-local-map-property gnus-next-page-map)
6970 gnus-next t
6971 gnus-callback gnus-article-button-next-page
6972 article-type annotation))
6973 (widget-convert-button
6974 'link b (if (bolp)
6975 ;; Exclude a newline.
6976 (1- (point))
6977 (point))
6978 :action 'gnus-button-next-page
6979 :button-keymap gnus-next-page-map)))
6980
6981 (defun gnus-article-button-next-page (arg)
6982 "Go to the next page."
6983 (interactive "P")
6984 (let ((win (selected-window)))
6985 (select-window (gnus-get-buffer-window gnus-article-buffer t))
6986 (gnus-article-next-page)
6987 (select-window win)))
6988
6989 (defun gnus-article-button-prev-page (arg)
6990 "Go to the prev page."
6991 (interactive "P")
6992 (let ((win (selected-window)))
6993 (select-window (gnus-get-buffer-window gnus-article-buffer t))
6994 (gnus-article-prev-page)
6995 (select-window win)))
6996
6997 (defvar gnus-decode-header-methods
6998 '(mail-decode-encoded-word-region)
6999 "List of methods used to decode headers.
7000
7001 This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item
7002 is FUNCTION, FUNCTION will be applied to all newsgroups. If item is a
7003 \(REGEXP . FUNCTION), FUNCTION will be only apply to the newsgroups
7004 whose names match REGEXP.
7005
7006 For example:
7007 \((\"chinese\" . gnus-decode-encoded-word-region-by-guess)
7008 mail-decode-encoded-word-region
7009 (\"chinese\" . rfc1843-decode-region))
7010 ")
7011
7012 (defvar gnus-decode-header-methods-cache nil)
7013
7014 (defun gnus-multi-decode-header (start end)
7015 "Apply the functions from `gnus-encoded-word-methods' that match."
7016 (unless (and gnus-decode-header-methods-cache
7017 (eq gnus-newsgroup-name
7018 (car gnus-decode-header-methods-cache)))
7019 (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
7020 (mapcar (lambda (x)
7021 (if (symbolp x)
7022 (nconc gnus-decode-header-methods-cache (list x))
7023 (if (and gnus-newsgroup-name
7024 (string-match (car x) gnus-newsgroup-name))
7025 (nconc gnus-decode-header-methods-cache
7026 (list (cdr x))))))
7027 gnus-decode-header-methods))
7028 (let ((xlist gnus-decode-header-methods-cache))
7029 (pop xlist)
7030 (save-restriction
7031 (narrow-to-region start end)
7032 (while xlist
7033 (funcall (pop xlist) (point-min) (point-max))))))
7034
7035 ;;;
7036 ;;; Treatment top-level handling.
7037 ;;;
7038
7039 (defun gnus-treat-article (condition &optional part-number total-parts type)
7040 (let ((length (- (point-max) (point-min)))
7041 (alist gnus-treatment-function-alist)
7042 (article-goto-body-goes-to-point-min-p t)
7043 (treated-type
7044 (or (not type)
7045 (catch 'found
7046 (let ((list gnus-article-treat-types))
7047 (while list
7048 (when (string-match (pop list) type)
7049 (throw 'found t)))))))
7050 (highlightp (gnus-visual-p 'article-highlight 'highlight))
7051 val elem)
7052 (gnus-run-hooks 'gnus-part-display-hook)
7053 (dolist (elem alist)
7054 (setq val
7055 (save-excursion
7056 (when (gnus-buffer-live-p gnus-summary-buffer)
7057 (set-buffer gnus-summary-buffer))
7058 (symbol-value (car elem))))
7059 (when (and (or (consp val)
7060 treated-type)
7061 (gnus-treat-predicate val)
7062 (or (not (get (car elem) 'highlight))
7063 highlightp))
7064 (save-restriction
7065 (funcall (cadr elem)))))))
7066
7067 ;; Dynamic variables.
7068 (eval-when-compile
7069 (defvar part-number)
7070 (defvar total-parts)
7071 (defvar type)
7072 (defvar condition)
7073 (defvar length))
7074
7075 (defun gnus-treat-predicate (val)
7076 (cond
7077 ((null val)
7078 nil)
7079 (condition
7080 (eq condition val))
7081 ((and (listp val)
7082 (stringp (car val)))
7083 (apply 'gnus-or (mapcar `(lambda (s)
7084 (string-match s ,(or gnus-newsgroup-name "")))
7085 val)))
7086 ((listp val)
7087 (let ((pred (pop val)))
7088 (cond
7089 ((eq pred 'or)
7090 (apply 'gnus-or (mapcar 'gnus-treat-predicate val)))
7091 ((eq pred 'and)
7092 (apply 'gnus-and (mapcar 'gnus-treat-predicate val)))
7093 ((eq pred 'not)
7094 (not (gnus-treat-predicate (car val))))
7095 ((eq pred 'typep)
7096 (equal (car val) type))
7097 (t
7098 (error "%S is not a valid predicate" pred)))))
7099 ((eq val t)
7100 t)
7101 ((eq val 'head)
7102 nil)
7103 ((eq val 'last)
7104 (eq part-number total-parts))
7105 ((numberp val)
7106 (< length val))
7107 (t
7108 (error "%S is not a valid value" val))))
7109
7110 (defun gnus-article-encrypt-body (protocol &optional n)
7111 "Encrypt the article body."
7112 (interactive
7113 (list
7114 (or gnus-article-encrypt-protocol
7115 (completing-read "Encrypt protocol: "
7116 gnus-article-encrypt-protocol-alist
7117 nil t))
7118 current-prefix-arg))
7119 (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
7120 (unless func
7121 (error "Can't find the encrypt protocol %s" protocol))
7122 (if (member gnus-newsgroup-name '("nndraft:delayed"
7123 "nndraft:drafts"
7124 "nndraft:queue"))
7125 (error "Can't encrypt the article in group %s"
7126 gnus-newsgroup-name))
7127 (gnus-summary-iterate n
7128 (save-excursion
7129 (set-buffer gnus-summary-buffer)
7130 (let ((mail-parse-charset gnus-newsgroup-charset)
7131 (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
7132 (summary-buffer gnus-summary-buffer)
7133 references point)
7134 (gnus-set-global-variables)
7135 (when (gnus-group-read-only-p)
7136 (error "The current newsgroup does not support article encrypt"))
7137 (gnus-summary-show-article t)
7138 (setq references
7139 (or (mail-header-references gnus-current-headers) ""))
7140 (set-buffer gnus-article-buffer)
7141 (let* ((inhibit-read-only t)
7142 (headers
7143 (mapcar (lambda (field)
7144 (and (save-restriction
7145 (message-narrow-to-head)
7146 (goto-char (point-min))
7147 (search-forward field nil t))
7148 (prog2
7149 (message-narrow-to-field)
7150 (buffer-string)
7151 (delete-region (point-min) (point-max))
7152 (widen))))
7153 '("Content-Type:" "Content-Transfer-Encoding:"
7154 "Content-Disposition:"))))
7155 (message-narrow-to-head)
7156 (message-remove-header "MIME-Version")
7157 (goto-char (point-max))
7158 (setq point (point))
7159 (insert (apply 'concat headers))
7160 (widen)
7161 (narrow-to-region point (point-max))
7162 (let ((message-options message-options))
7163 (message-options-set 'message-sender user-mail-address)
7164 (message-options-set 'message-recipients user-mail-address)
7165 (message-options-set 'message-sign-encrypt 'not)
7166 (funcall func))
7167 (goto-char (point-min))
7168 (insert "MIME-Version: 1.0\n")
7169 (widen)
7170 (gnus-summary-edit-article-done
7171 references nil summary-buffer t))
7172 (when gnus-keep-backlog
7173 (gnus-backlog-remove-article
7174 (car gnus-article-current) (cdr gnus-article-current)))
7175 (save-excursion
7176 (when (get-buffer gnus-original-article-buffer)
7177 (set-buffer gnus-original-article-buffer)
7178 (setq gnus-original-article nil)))
7179 (when gnus-use-cache
7180 (gnus-cache-update-article
7181 (car gnus-article-current) (cdr gnus-article-current))))))))
7182
7183 (defvar gnus-mime-security-button-line-format "%{%([[%t:%i]%D]%)%}\n"
7184 "The following specs can be used:
7185 %t The security MIME type
7186 %i Additional info
7187 %d Details
7188 %D Details if button is pressed")
7189
7190 (defvar gnus-mime-security-button-end-line-format "%{%([[End of %t]%D]%)%}\n"
7191 "The following specs can be used:
7192 %t The security MIME type
7193 %i Additional info
7194 %d Details
7195 %D Details if button is pressed")
7196
7197 (defvar gnus-mime-security-button-line-format-alist
7198 '((?t gnus-tmp-type ?s)
7199 (?i gnus-tmp-info ?s)
7200 (?d gnus-tmp-details ?s)
7201 (?D gnus-tmp-pressed-details ?s)))
7202
7203 (defvar gnus-mime-security-button-map
7204 (let ((map (make-sparse-keymap)))
7205 (unless (>= (string-to-number emacs-version) 21)
7206 (set-keymap-parent map gnus-article-mode-map))
7207 (define-key map gnus-mouse-2 'gnus-article-push-button)
7208 (define-key map "\r" 'gnus-article-press-button)
7209 map))
7210
7211 (defvar gnus-mime-security-details-buffer nil)
7212
7213 (defvar gnus-mime-security-button-pressed nil)
7214
7215 (defvar gnus-mime-security-show-details-inline t
7216 "If non-nil, show details in the article buffer.")
7217
7218 (defun gnus-mime-security-verify-or-decrypt (handle)
7219 (mm-remove-parts (cdr handle))
7220 (let ((region (mm-handle-multipart-ctl-parameter handle 'gnus-region))
7221 point (inhibit-read-only t))
7222 (if region
7223 (goto-char (car region)))
7224 (save-restriction
7225 (narrow-to-region (point) (point))
7226 (with-current-buffer (mm-handle-multipart-original-buffer handle)
7227 (let* ((mm-verify-option 'known)
7228 (mm-decrypt-option 'known)
7229 (nparts (mm-possibly-verify-or-decrypt (cdr handle) handle)))
7230 (unless (eq nparts (cdr handle))
7231 (mm-destroy-parts (cdr handle))
7232 (setcdr handle nparts))))
7233 (setq point (point))
7234 (gnus-mime-display-security handle)
7235 (goto-char (point-max)))
7236 (when region
7237 (delete-region (point) (cdr region))
7238 (set-marker (car region) nil)
7239 (set-marker (cdr region) nil))
7240 (goto-char point)))
7241
7242 (defun gnus-mime-security-show-details (handle)
7243 (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
7244 (if (not details)
7245 (gnus-message 5 "No details.")
7246 (if gnus-mime-security-show-details-inline
7247 (let ((gnus-mime-security-button-pressed
7248 (not (get-text-property (point) 'gnus-mime-details)))
7249 (gnus-mime-security-button-line-format
7250 (get-text-property (point) 'gnus-line-format))
7251 (inhibit-read-only t))
7252 (forward-char -1)
7253 (while (eq (get-text-property (point) 'gnus-line-format)
7254 gnus-mime-security-button-line-format)
7255 (forward-char -1))
7256 (forward-char)
7257 (save-restriction
7258 (narrow-to-region (point) (point))
7259 (gnus-insert-mime-security-button handle))
7260 (delete-region (point)
7261 (or (text-property-not-all
7262 (point) (point-max)
7263 'gnus-line-format
7264 gnus-mime-security-button-line-format)
7265 (point-max))))
7266 ;; Not inlined.
7267 (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
7268 (with-current-buffer gnus-mime-security-details-buffer
7269 (erase-buffer)
7270 t)
7271 (setq gnus-mime-security-details-buffer
7272 (gnus-get-buffer-create "*MIME Security Details*")))
7273 (with-current-buffer gnus-mime-security-details-buffer
7274 (insert details)
7275 (goto-char (point-min)))
7276 (pop-to-buffer gnus-mime-security-details-buffer)))))
7277
7278 (defun gnus-mime-security-press-button (handle)
7279 (save-excursion
7280 (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7281 (gnus-mime-security-show-details handle)
7282 (gnus-mime-security-verify-or-decrypt handle))))
7283
7284 (defun gnus-insert-mime-security-button (handle &optional displayed)
7285 (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
7286 (gnus-tmp-type
7287 (concat
7288 (or (nth 2 (assoc protocol mm-verify-function-alist))
7289 (nth 2 (assoc protocol mm-decrypt-function-alist))
7290 "Unknown")
7291 (if (equal (car handle) "multipart/signed")
7292 " Signed" " Encrypted")
7293 " Part"))
7294 (gnus-tmp-info
7295 (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7296 "Undecided"))
7297 (gnus-tmp-details
7298 (mm-handle-multipart-ctl-parameter handle 'gnus-details))
7299 gnus-tmp-pressed-details
7300 b e)
7301 (setq gnus-tmp-details
7302 (if gnus-tmp-details
7303 (concat "\n" gnus-tmp-details)
7304 ""))
7305 (setq gnus-tmp-pressed-details
7306 (if gnus-mime-security-button-pressed gnus-tmp-details ""))
7307 (unless (bolp)
7308 (insert "\n"))
7309 (setq b (point))
7310 (gnus-eval-format
7311 gnus-mime-security-button-line-format
7312 gnus-mime-security-button-line-format-alist
7313 `(,@(gnus-local-map-property gnus-mime-security-button-map)
7314 gnus-callback gnus-mime-security-press-button
7315 gnus-line-format ,gnus-mime-security-button-line-format
7316 gnus-mime-details ,gnus-mime-security-button-pressed
7317 article-type annotation
7318 gnus-data ,handle))
7319 (setq e (if (bolp)
7320 ;; Exclude a newline.
7321 (1- (point))
7322 (point)))
7323 (widget-convert-button
7324 'link b e
7325 :mime-handle handle
7326 :action 'gnus-widget-press-button
7327 :button-keymap gnus-mime-security-button-map
7328 :help-echo
7329 (lambda (widget/window &optional overlay pos)
7330 ;; Needed to properly clear the message due to a bug in
7331 ;; wid-edit (XEmacs only).
7332 (when (boundp 'help-echo-owns-message)
7333 (setq help-echo-owns-message t))
7334 (format
7335 "%S: show detail"
7336 (aref gnus-mouse-2 0))))))
7337
7338 (defun gnus-mime-display-security (handle)
7339 (save-restriction
7340 (narrow-to-region (point) (point))
7341 (unless (gnus-unbuttonized-mime-type-p (car handle))
7342 (gnus-insert-mime-security-button handle))
7343 (gnus-mime-display-mixed (cdr handle))
7344 (unless (bolp)
7345 (insert "\n"))
7346 (unless (gnus-unbuttonized-mime-type-p (car handle))
7347 (let ((gnus-mime-security-button-line-format
7348 gnus-mime-security-button-end-line-format))
7349 (gnus-insert-mime-security-button handle)))
7350 (mm-set-handle-multipart-parameter
7351 handle 'gnus-region
7352 (cons (set-marker (make-marker) (point-min))
7353 (set-marker (make-marker) (point-max))))))
7354
7355 (gnus-ems-redefine)
7356
7357 (provide 'gnus-art)
7358
7359 (run-hooks 'gnus-art-load-hook)
7360
7361 ;;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
7362 ;;; gnus-art.el ends here