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