gnus-logic.el 7.4 KB

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