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