file uu-decode.pbm was added on branch t-gnus-6_17 on 2005-05-01 23:25:48 +0000
[elisp/gnus.git-] / lisp / gnus-logic.el
1 ;;; gnus-logic.el --- advanced scoring code for Gnus
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; Keywords: news
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-score)
33 (require 'gnus-util)
34
35 ;;; Internal variables.
36
37 (defvar gnus-advanced-headers nil)
38
39 ;; To avoid having 8-bit characters in the source file.
40 (defvar gnus-advanced-not (intern (format "%c" 172)))
41
42 (defconst gnus-advanced-index
43   ;; Name to index alist.
44   `(("number"
45      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'location)
46      gnus-advanced-integer)
47     ("subject"
48      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'subject)
49      gnus-advanced-string)
50     ("from"
51      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'from)
52      gnus-advanced-string)
53     ("date"
54      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'date)
55      gnus-advanced-date)
56     ("message-id"
57      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'id)
58      gnus-advanced-string)
59     ("references"
60      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'references)
61      gnus-advanced-string)
62     ("chars"
63      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'chars)
64      gnus-advanced-integer)
65     ("lines"
66      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'lines)
67      gnus-advanced-integer)
68     ("xref"
69      ,(luna-class-slot-index (luna-find-class 'mime-gnus-entity) 'xref)
70      gnus-advanced-string)
71     ("head" nil gnus-advanced-body)
72     ("body" nil gnus-advanced-body)
73     ("all" nil gnus-advanced-body)))
74
75 (eval-and-compile
76   (autoload 'parse-time-string "parse-time"))
77
78 (defun gnus-score-advanced (rule &optional trace)
79   "Apply advanced scoring RULE to all the articles in the current group."
80   (let (new-score score multiple)
81     (dolist (gnus-advanced-headers gnus-newsgroup-headers)
82       (when (setq multiple (gnus-advanced-score-rule (car rule)))
83         (setq new-score (or (nth 1 rule)
84                             gnus-score-interactive-default-score))
85         (when (numberp multiple)
86           (setq new-score (* multiple new-score)))
87         ;; This rule was successful, so we add the score to this
88         ;; article.
89         (if (setq score (assq (mail-header-number gnus-advanced-headers)
90                               gnus-newsgroup-scored))
91             (setcdr score
92                     (+ (cdr score) new-score))
93           (push (cons (mail-header-number gnus-advanced-headers)
94                       new-score)
95                 gnus-newsgroup-scored)
96           (when trace
97             (push (cons "A file" rule)
98                   ;; Must be synced with `gnus-score-edit-file-at-point'.
99                   gnus-score-trace)))))))
100
101 (defun gnus-advanced-score-rule (rule)
102   "Apply RULE to `gnus-advanced-headers'."
103   (let ((type (car rule)))
104     (cond
105      ;; "And" rule.
106      ((or (eq type '&) (eq type 'and))
107       (pop rule)
108       (if (not rule)
109           t                             ; Empty rule is true.
110         (while (and rule
111                     (gnus-advanced-score-rule (car rule)))
112           (pop rule))
113         ;; If all the rules were true, then `rule' should be nil.
114         (not rule)))
115      ;; "Or" rule.
116      ((or (eq type '|) (eq type 'or))
117       (pop rule)
118       (if (not rule)
119           nil
120         (while (and rule
121                     (not (gnus-advanced-score-rule (car rule))))
122           (pop rule))
123         ;; If one of the rules returned true, then `rule' should be non-nil.
124         rule))
125      ;; "Not" rule.
126      ((or (eq type '!) (eq type 'not) (eq type gnus-advanced-not))
127       (not (gnus-advanced-score-rule (nth 1 rule))))
128      ;; This is a `1-'-type redirection rule.
129      ((and (symbolp type)
130            (string-match "^[0-9]+-$\\|^\\^+$" (symbol-name type)))
131       (let ((gnus-advanced-headers
132              (gnus-parent-headers
133               gnus-advanced-headers
134               (if (string-match "^\\([0-9]+\\)-$" (symbol-name type))
135                   ;; 1- type redirection.
136                   (string-to-number
137                    (substring (symbol-name type)
138                               (match-beginning 1) (match-end 1)))
139                 ;; ^^^ type redirection.
140                 (length (symbol-name type))))))
141         (when gnus-advanced-headers
142           (gnus-advanced-score-rule (nth 1 rule)))))
143      ;; Plain scoring rule.
144      ((stringp type)
145       (gnus-advanced-score-article rule))
146      ;; Bug-out time!
147      (t
148       (error "Unknown advanced score type: %s" rule)))))
149
150 (defun gnus-advanced-score-article (rule)
151   ;; `rule' is a semi-normal score rule, so we find out what function
152   ;; that's supposed to do the actual processing.
153   (let* ((header (car rule))
154          (func (assoc (downcase header) gnus-advanced-index)))
155     (if (not func)
156         (error "No such header: %s" rule)
157       ;; Call the score function.
158       (funcall (caddr func) (or (cadr func) header)
159                (cadr rule) (caddr rule)))))
160
161 (defun gnus-advanced-string (index match type)
162   "See whether string MATCH of TYPE matches `gnus-advanced-headers' in INDEX."
163   (let* ((type (or type 's))
164          (case-fold-search (not (eq (downcase (symbol-name type))
165                                     (symbol-name type))))
166          (header (or (aref gnus-advanced-headers index) "")))
167     (cond
168      ((memq type '(r R regexp Regexp))
169       (string-match match header))
170      ((memq type '(s S string String))
171       (string-match (regexp-quote match) header))
172      ((memq type '(e E exact Exact))
173       (string= match header))
174      ((memq type '(f F fuzzy Fuzzy))
175       (string-match (regexp-quote (gnus-simplify-subject-fuzzy match))
176                     header))
177      (t
178       (error "No such string match type: %s" type)))))
179
180 (defun gnus-advanced-integer (index match type)
181   (if (not (memq type '(< > <= >= =)))
182       (error "No such integer score type: %s" type)
183     (funcall type (or (aref gnus-advanced-headers index) 0) match)))
184
185 (defun gnus-advanced-date (index match type)
186   (let ((date (apply 'encode-time (parse-time-string
187                                    (aref gnus-advanced-headers index))))
188         (match (apply 'encode-time (parse-time-string match))))
189     (cond
190      ((eq type 'at)
191       (equal date match))
192      ((eq type 'before)
193       (time-less-p match date))
194      ((eq type 'after)
195       (time-less-p date match))
196      (t
197       (error "No such date score type: %s" type)))))
198
199 (defun gnus-advanced-body (header match type)
200   (when (string= header "all")
201     (setq header "article"))
202   (save-excursion
203     (set-buffer nntp-server-buffer)
204     (let* ((request-func (cond ((string= "head" header)
205                                 'gnus-request-head)
206                                ((string= "body" header)
207                                 'gnus-request-body)
208                                (t 'gnus-request-article)))
209            ofunc article)
210       ;; Not all backends support partial fetching.  In that case, we
211       ;; just fetch the entire article.
212       (unless (gnus-check-backend-function
213                (intern (concat "request-" header))
214                gnus-newsgroup-name)
215         (setq ofunc request-func)
216         (setq request-func 'gnus-request-article))
217       (setq article (mail-header-number gnus-advanced-headers))
218       (gnus-message 7 "Scoring article %s..." article)
219       (when (funcall request-func article gnus-newsgroup-name)
220         (goto-char (point-min))
221         ;; If just parts of the article is to be searched and the
222         ;; backend didn't support partial fetching, we just narrow to
223         ;; the relevant parts.
224         (when ofunc
225           (if (eq ofunc 'gnus-request-head)
226               (narrow-to-region
227                (point)
228                (or (search-forward "\n\n" nil t) (point-max)))
229             (narrow-to-region
230              (or (search-forward "\n\n" nil t) (point))
231              (point-max))))
232         (let* ((case-fold-search (not (eq (downcase (symbol-name type))
233                                           (symbol-name type))))
234                (search-func
235                 (cond ((memq type '(r R regexp Regexp))
236                        're-search-forward)
237                       ((memq type '(s S string String))
238                        'search-forward)
239                       (t
240                        (error "Invalid match type: %s" type)))))
241           (goto-char (point-min))
242           (prog1
243               (funcall search-func match nil t)
244             (widen)))))))
245
246 (provide 'gnus-logic)
247
248 ;;; gnus-logic.el ends here