(spam-initialize): spam-stat-maybe-{save,load} already respect spam-use-stat
[gnus] / lisp / nnheader.el
1 ;;; nnheader.el --- header access macros for Gnus and its backends
2
3 ;; Copyright (C) 1987, 1988, 1989, 1990, 1993, 1994, 1995, 1996,
4 ;;        1997, 1998, 2000, 2001, 2002, 2003
5 ;;        Free Software Foundation, Inc.
6
7 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
8 ;;      Lars Magne Ingebrigtsen <larsi@gnus.org>
9 ;; Keywords: news
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
25 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29
30 ;;; Code:
31
32 (eval-when-compile (require 'cl))
33
34 ;; Requiring `gnus-util' at compile time creates a circular
35 ;; dependency between nnheader.el and gnus-util.el.
36 ;;(eval-when-compile (require 'gnus-util))
37
38 (require 'mail-utils)
39 (require 'mm-util)
40 (require 'gnus-util)
41 (eval-and-compile
42   (autoload 'gnus-sorted-intersection "gnus-range")
43   (autoload 'gnus-intersection "gnus-range")
44   (autoload 'gnus-sorted-complement "gnus-range")
45   (autoload 'gnus-sorted-difference "gnus-range"))
46
47 (defcustom gnus-verbose-backends 7
48   "Integer that says how verbose the Gnus backends should be.
49 The higher the number, the more messages the Gnus backends will flash
50 to say what it's doing.  At zero, the Gnus backends will be totally
51 mute; at five, they will display most important messages; and at ten,
52 they will keep on jabbering all the time."
53   :group 'gnus-start
54   :type 'integer)
55
56 (defcustom gnus-nov-is-evil nil
57   "If non-nil, Gnus backends will never output headers in the NOV format."
58   :group 'gnus-server
59   :type 'boolean)
60
61 (defvar nnheader-max-head-length 4096
62   "*Max length of the head of articles.
63
64 Value is an integer, nil, or t.  nil means read in chunks of a file
65 indefinitely until a complete head is found\; t means always read the
66 entire file immediately, disregarding `nnheader-head-chop-length'.
67
68 Integer values will in effect be rounded up to the nearest multiple of
69 `nnheader-head-chop-length'.")
70
71 (defvar nnheader-head-chop-length 2048
72   "*Length of each read operation when trying to fetch HEAD headers.")
73
74 (defvar nnheader-read-timeout
75   (if (string-match "windows-nt\\|os/2\\|emx\\|cygwin"
76                     (symbol-name system-type))
77       1.0                               ; why?
78     0.1)
79   "How long nntp should wait between checking for the end of output.
80 Shorter values mean quicker response, but are more CPU intensive.")
81
82 (defvar nnheader-file-name-translation-alist
83   (let ((case-fold-search t))
84     (cond
85      ((string-match "windows-nt\\|os/2\\|emx\\|cygwin"
86                     (symbol-name system-type))
87       (append (mapcar (lambda (c) (cons c ?_))
88                       '(?: ?* ?\" ?< ?> ??))
89               (if (string-match "windows-nt\\|cygwin"
90                                 (symbol-name system-type))
91                   nil
92                 '((?+ . ?-)))))
93      (t nil)))
94   "*Alist that says how to translate characters in file names.
95 For instance, if \":\" is invalid as a file character in file names
96 on your system, you could say something like:
97
98 \(setq nnheader-file-name-translation-alist '((?: . ?_)))")
99
100 (defvar nnheader-directory-separator-character
101   (string-to-char (substring (file-name-as-directory ".") -1))
102   "*A character used to a directory separator.")
103
104 (eval-and-compile
105   (autoload 'nnmail-message-id "nnmail")
106   (autoload 'mail-position-on-field "sendmail")
107   (autoload 'message-remove-header "message")
108   (autoload 'gnus-point-at-eol "gnus-util")
109   (autoload 'gnus-buffer-live-p "gnus-util"))
110
111 ;;; Header access macros.
112
113 ;; These macros may look very much like the ones in GNUS 4.1.  They
114 ;; are, in a way, but you should note that the indices they use have
115 ;; been changed from the internal GNUS format to the NOV format.  The
116 ;; makes it possible to read headers from XOVER much faster.
117 ;;
118 ;; The format of a header is now:
119 ;; [number subject from date id references chars lines xref extra]
120 ;;
121 ;; (That next-to-last entry is defined as "misc" in the NOV format,
122 ;; but Gnus uses it for xrefs.)
123
124 (defmacro mail-header-number (header)
125   "Return article number in HEADER."
126   `(aref ,header 0))
127
128 (defmacro mail-header-set-number (header number)
129   "Set article number of HEADER to NUMBER."
130   `(aset ,header 0 ,number))
131
132 (defmacro mail-header-subject (header)
133   "Return subject string in HEADER."
134   `(aref ,header 1))
135
136 (defmacro mail-header-set-subject (header subject)
137   "Set article subject of HEADER to SUBJECT."
138   `(aset ,header 1 ,subject))
139
140 (defmacro mail-header-from (header)
141   "Return author string in HEADER."
142   `(aref ,header 2))
143
144 (defmacro mail-header-set-from (header from)
145   "Set article author of HEADER to FROM."
146   `(aset ,header 2 ,from))
147
148 (defmacro mail-header-date (header)
149   "Return date in HEADER."
150   `(aref ,header 3))
151
152 (defmacro mail-header-set-date (header date)
153   "Set article date of HEADER to DATE."
154   `(aset ,header 3 ,date))
155
156 (defalias 'mail-header-message-id 'mail-header-id)
157 (defmacro mail-header-id (header)
158   "Return Id in HEADER."
159   `(aref ,header 4))
160
161 (defalias 'mail-header-set-message-id 'mail-header-set-id)
162 (defmacro mail-header-set-id (header id)
163   "Set article Id of HEADER to ID."
164   `(aset ,header 4 ,id))
165
166 (defmacro mail-header-references (header)
167   "Return references in HEADER."
168   `(aref ,header 5))
169
170 (defmacro mail-header-set-references (header ref)
171   "Set article references of HEADER to REF."
172   `(aset ,header 5 ,ref))
173
174 (defmacro mail-header-chars (header)
175   "Return number of chars of article in HEADER."
176   `(aref ,header 6))
177
178 (defmacro mail-header-set-chars (header chars)
179   "Set number of chars in article of HEADER to CHARS."
180   `(aset ,header 6 ,chars))
181
182 (defmacro mail-header-lines (header)
183   "Return lines in HEADER."
184   `(aref ,header 7))
185
186 (defmacro mail-header-set-lines (header lines)
187   "Set article lines of HEADER to LINES."
188   `(aset ,header 7 ,lines))
189
190 (defmacro mail-header-xref (header)
191   "Return xref string in HEADER."
192   `(aref ,header 8))
193
194 (defmacro mail-header-set-xref (header xref)
195   "Set article XREF of HEADER to xref."
196   `(aset ,header 8 ,xref))
197
198 (defmacro mail-header-extra (header)
199   "Return the extra headers in HEADER."
200   `(aref ,header 9))
201
202 (defmacro mail-header-set-extra (header extra)
203   "Set the extra headers in HEADER to EXTRA."
204   `(aset ,header 9 ',extra))
205
206 (defsubst make-mail-header (&optional init)
207   "Create a new mail header structure initialized with INIT."
208   (make-vector 10 init))
209
210 (defsubst make-full-mail-header (&optional number subject from date id
211                                            references chars lines xref
212                                            extra)
213   "Create a new mail header structure initialized with the parameters given."
214   (vector number subject from date id references chars lines xref extra))
215
216 ;; fake message-ids: generation and detection
217
218 (defvar nnheader-fake-message-id 1)
219
220 (defsubst nnheader-generate-fake-message-id ()
221   (concat "fake+none+" (int-to-string (incf nnheader-fake-message-id))))
222
223 (defsubst nnheader-fake-message-id-p (id)
224   (save-match-data                     ; regular message-id's are <.*>
225     (string-match "\\`fake\\+none\\+[0-9]+\\'" id)))
226
227 ;; Parsing headers and NOV lines.
228
229 (defsubst nnheader-remove-cr-followed-by-lf ()
230   (goto-char (point-max))
231   (while (search-backward "\r\n" nil t)
232     (delete-char 1)))
233
234 (defsubst nnheader-header-value ()
235   (skip-chars-forward " \t")
236   (buffer-substring (point) (gnus-point-at-eol)))
237
238 (defun nnheader-parse-naked-head (&optional number)
239   ;; This function unfolds continuation lines in this buffer
240   ;; destructively.  When this side effect is unwanted, use
241   ;; `nnheader-parse-head' instead of this function.
242   (let ((case-fold-search t)
243         (buffer-read-only nil)
244         (cur (current-buffer))
245         (p (point-min))
246         in-reply-to lines ref)
247     (nnheader-remove-cr-followed-by-lf)
248     (ietf-drums-unfold-fws)
249     (subst-char-in-region (point-min) (point-max) ?\t ? )
250     (goto-char p)
251     (insert "\n")
252     (prog1
253         ;; This implementation of this function, with nine
254         ;; search-forwards instead of the one re-search-forward and a
255         ;; case (which basically was the old function) is actually
256         ;; about twice as fast, even though it looks messier.  You
257         ;; can't have everything, I guess.  Speed and elegance don't
258         ;; always go hand in hand.
259         (vector
260          ;; Number.
261          (or number 0)
262          ;; Subject.
263          (progn
264            (goto-char p)
265            (if (search-forward "\nsubject:" nil t)
266                (nnheader-header-value) "(none)"))
267          ;; From.
268          (progn
269            (goto-char p)
270            (if (search-forward "\nfrom:" nil t)
271                (nnheader-header-value) "(nobody)"))
272          ;; Date.
273          (progn
274            (goto-char p)
275            (if (search-forward "\ndate:" nil t)
276                (nnheader-header-value) ""))
277          ;; Message-ID.
278          (progn
279            (goto-char p)
280            (if (search-forward "\nmessage-id:" nil t)
281                (buffer-substring
282                 (1- (or (search-forward "<" (gnus-point-at-eol) t)
283                         (point)))
284                 (or (search-forward ">" (gnus-point-at-eol) t) (point)))
285              ;; If there was no message-id, we just fake one to make
286              ;; subsequent routines simpler.
287              (nnheader-generate-fake-message-id)))
288          ;; References.
289          (progn
290            (goto-char p)
291            (if (search-forward "\nreferences:" nil t)
292                (nnheader-header-value)
293              ;; Get the references from the in-reply-to header if
294              ;; there were no references and the in-reply-to header
295              ;; looks promising.
296              (if (and (search-forward "\nin-reply-to:" nil t)
297                       (setq in-reply-to (nnheader-header-value))
298                       (string-match "<[^\n>]+>" in-reply-to))
299                  (let (ref2)
300                    (setq ref (substring in-reply-to (match-beginning 0)
301                                         (match-end 0)))
302                    (while (string-match "<[^\n>]+>"
303                                         in-reply-to (match-end 0))
304                      (setq ref2 (substring in-reply-to (match-beginning 0)
305                                            (match-end 0)))
306                      (when (> (length ref2) (length ref))
307                        (setq ref ref2)))
308                    ref)
309                nil)))
310          ;; Chars.
311          0
312          ;; Lines.
313          (progn
314            (goto-char p)
315            (if (search-forward "\nlines: " nil t)
316                (if (numberp (setq lines (read cur)))
317                    lines 0)
318              0))
319          ;; Xref.
320          (progn
321            (goto-char p)
322            (and (search-forward "\nxref:" nil t)
323                 (nnheader-header-value)))
324          ;; Extra.
325          (when nnmail-extra-headers
326            (let ((extra nnmail-extra-headers)
327                  out)
328