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