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