Bug fixes and certificate and hostname verification for the Emacs GnuTLS support.
[bpt/emacs.git] / lisp / gnus / gnus-registry.el
CommitLineData
23f87bed 1;;; gnus-registry.el --- article registry for Gnus
e84b4b86 2
73b0cd50 3;; Copyright (C) 2002-2011 Free Software Foundation, Inc.
23f87bed
MB
4
5;; Author: Ted Zlatanov <tzz@lifelogs.com>
0ab5c2be 6;; Keywords: news registry
23f87bed
MB
7
8;; This file is part of GNU Emacs.
9
5e809f55 10;; GNU Emacs is free software: you can redistribute it and/or modify
23f87bed 11;; it under the terms of the GNU General Public License as published by
5e809f55
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
23f87bed
MB
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
5e809f55 17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23f87bed
MB
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
5e809f55 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23f87bed
MB
22
23;;; Commentary:
24
01c52d31 25;; This is the gnus-registry.el package, which works with all
11a3174d 26;; Gnus backends, not just nnmail. The major issue is that it
01c52d31
MB
27;; doesn't go across backends, so for instance if an article is in
28;; nnml:sys and you see a reference to it in nnimap splitting, the
29;; article will end up in nnimap:sys
23f87bed
MB
30
31;; gnus-registry.el intercepts article respooling, moving, deleting,
32;; and copying for all backends. If it doesn't work correctly for
33;; you, submit a bug report and I'll be glad to fix it. It needs
c024b021
TZ
34;; better documentation in the manual (also on my to-do list).
35
36;; If you want to track recipients (and you should to make the
37;; gnus-registry splitting work better), you need the To and Cc
a6e77075
TZ
38;; headers collected by Gnus. Note that in more recent Gnus versions
39;; this is already the case: look at `gnus-extra-headers' to be sure.
c024b021
TZ
40
41;; ;;; you may also want Gcc Newsgroups Keywords X-Face
42;; (add-to-list 'gnus-extra-headers 'To)
43;; (add-to-list 'gnus-extra-headers 'Cc)
44;; (setq nnmail-extra-headers gnus-extra-headers)
23f87bed 45
c3c65d73 46;; Put this in your startup file (~/.gnus.el for instance) or use Customize:
23f87bed 47
c3c65d73 48;; (setq gnus-registry-max-entries 2500
cf8b0c27 49;; gnus-registry-track-extra '(sender subject recipient))
23f87bed
MB
50
51;; (gnus-registry-initialize)
52
53;; Then use this in your fancy-split:
54
55;; (: gnus-registry-split-fancy-with-parent)
56
36d3245f
G
57;; You should also consider using the nnregistry backend to look up
58;; articles. See the Gnus manual for more information.
59
23f87bed
MB
60;; TODO:
61
62;; - get the correct group on spool actions
63
11a3174d
TZ
64;; - articles that are spooled to a different backend should be moved
65;; after splitting
23f87bed
MB
66
67;;; Code:
68
69(eval-when-compile (require 'cl))
70
42b23765 71(eval-when-compile
2237da9c 72 (when (null (ignore-errors (require 'ert)))
42b23765
TZ
73 (defmacro* ert-deftest (name () &body docstring-keys-and-body))))
74
2237da9c
G
75(ignore-errors
76 (require 'ert))
23f87bed
MB
77(require 'gnus)
78(require 'gnus-int)
79(require 'gnus-sum)
11a3174d 80(require 'gnus-art)
996aa8c1 81(require 'gnus-util)
23f87bed 82(require 'nnmail)
ec7995fa 83(require 'easymenu)
11a3174d 84(require 'registry)
23f87bed 85
9efa445f
DN
86(defvar gnus-adaptive-word-syntax-table)
87
23f87bed
MB
88(defvar gnus-registry-dirty t
89 "Boolean set to t when the registry is modified")
90
91(defgroup gnus-registry nil
92 "The Gnus registry."
bf247b6e 93 :version "22.1"
23f87bed
MB
94 :group 'gnus)
95
11a3174d 96(defvar gnus-registry-marks
14e8de0c 97 '((Important
8f7abae3
MB
98 :char ?i
99 :image "summary_important")
14e8de0c 100 (Work
8f7abae3
MB
101 :char ?w
102 :image "summary_work")
14e8de0c 103 (Personal
8f7abae3
MB
104 :char ?p
105 :image "summary_personal")
14e8de0c 106 (To-Do
8f7abae3
MB
107 :char ?t
108 :image "summary_todo")
14e8de0c 109 (Later
8f7abae3
MB
110 :char ?l
111 :image "summary_later"))
14e8de0c
MB
112
113 "List of registry marks and their options.
114
115`gnus-registry-mark-article' will offer symbols from this list
c9fc72fa 116for completion.
14e8de0c
MB
117
118Each entry must have a character to be useful for summary mode
119line display and for keyboard shortcuts.
120
121Each entry must have an image string to be useful for visual
11a3174d 122display.")
0b6799c3
MB
123
124(defcustom gnus-registry-default-mark 'To-Do
14e8de0c 125 "The default mark. Should be a valid key for `gnus-registry-marks'."
0b6799c3
MB
126 :group 'gnus-registry
127 :type 'symbol)
128
11a3174d
TZ
129(defcustom gnus-registry-unfollowed-addresses
130 (list (regexp-quote user-mail-address))
131 "List of addresses that gnus-registry-split-fancy-with-parent won't trace.
cf8b0c27
TZ
132The addresses are matched, they don't have to be fully qualified.
133In the messages, these addresses can be the sender or the
134recipients."
11a3174d
TZ
135 :group 'gnus-registry
136 :type '(repeat regexp))
137
c9fc72fa 138(defcustom gnus-registry-unfollowed-groups
a5954fa5 139 '("delayed$" "drafts$" "queue$" "INBOX$" "^nnmairix:" "archive")
01c52d31
MB
140 "List of groups that gnus-registry-split-fancy-with-parent won't return.
141The group names are matched, they don't have to be fully
11a3174d 142qualified. This parameter tells the Gnus registry 'never split a
01c52d31 143message into a group that matches one of these, regardless of
6b958814
G
144references.'
145
146nnmairix groups are specifically excluded because they are ephemeral."
23f87bed 147 :group 'gnus-registry
01c52d31 148 :type '(repeat regexp))
23f87bed 149
8f7abae3 150(defcustom gnus-registry-install 'ask
23f87bed
MB
151 "Whether the registry should be installed."
152 :group 'gnus-registry
8f7abae3 153 :type '(choice (const :tag "Never Install" nil)
11a3174d
TZ
154 (const :tag "Always Install" t)
155 (const :tag "Ask Me" ask)))
23f87bed 156
ec7995fa
KY
157(defvar gnus-summary-misc-menu) ;; Avoid byte compiler warning.
158
11a3174d 159(defvar gnus-registry-misc-menus nil) ; ugly way to keep the menus
23f87bed 160
11a3174d
TZ
161(make-obsolete-variable 'gnus-registry-clean-empty nil "23.4")
162(make-obsolete-variable 'gnus-registry-use-long-group-names nil "23.4")
163(make-obsolete-variable 'gnus-registry-max-track-groups nil "23.4")
164(make-obsolete-variable 'gnus-registry-entry-caching nil "23.4")
165(make-obsolete-variable 'gnus-registry-trim-articles-without-groups nil "23.4")
b86402ab 166
cf8b0c27 167(defcustom gnus-registry-track-extra '(subject sender recipient)
23f87bed 168 "Whether the registry should track extra data about a message.
cf8b0c27
TZ
169The subject, recipients (To: and Cc:), and Sender (From:) headers
170are tracked this way by default."
23f87bed 171 :group 'gnus-registry
bf247b6e 172 :type
23f87bed
MB
173 '(set :tag "Tracking choices"
174 (const :tag "Track by subject (Subject: header)" subject)
cf8b0c27 175 (const :tag "Track by recipient (To: and Cc: headers)" recipient)
23f87bed
MB
176 (const :tag "Track by sender (From: header)" sender)))
177
58a67d68 178(defcustom gnus-registry-split-strategy nil
11a3174d 179 "The splitting strategy applied to the keys in `gnus-registry-track-extra'.
58a67d68 180
11a3174d
TZ
181Given a set of unique found groups G and counts for each element
182of G, and a key K (typically 'sender or 'subject):
183
184When nil, if G has only one element, use it. Otherwise give up.
185This is the fastest but also least useful strategy.
186
187When 'majority, use the majority by count. So if there is a
188group with the most articles counted by K, use that. Ties are
189resolved in no particular order, simply the first one found wins.
190This is the slowest strategy but also the most accurate one.
191
192When 'first, the first element of G wins. This is fast and
193should be OK if your senders and subjects don't \"bleed\" across
194groups."
23f87bed 195 :group 'gnus-registry
11a3174d
TZ
196 :type
197 '(choice :tag "Splitting strategy"
198 (const :tag "Only use single choices, discard multiple matches" nil)
199 (const :tag "Majority of matches wins" majority)
200 (const :tag "First found wins" first)))
23f87bed
MB
201
202(defcustom gnus-registry-minimum-subject-length 5
203 "The minimum length of a subject before it's considered trackable."
204 :group 'gnus-registry
205 :type 'integer)
206
11a3174d
TZ
207(defcustom gnus-registry-extra-entries-precious '(mark)
208 "What extra keys are precious, meaning entries with them won't get pruned.
209By default, 'mark is included, so articles with marks are
210considered precious.
211
212Before you save the Gnus registry, it's pruned. Any entries with
213keys in this list will not be pruned. All other entries go to
214the Bit Bucket."
0b6799c3
MB
215 :group 'gnus-registry
216 :type '(repeat symbol))
217
c9fc72fa
LMI
218(defcustom gnus-registry-cache-file
219 (nnheader-concat
220 (or gnus-dribble-directory gnus-home-directory "~/")
11a3174d 221 ".gnus.registry.eioio")
23f87bed
MB
222 "File where the Gnus registry will be stored."
223 :group 'gnus-registry
224 :type 'file)
225
226(defcustom gnus-registry-max-entries nil
227 "Maximum number of entries in the registry, nil for unlimited."
228 :group 'gnus-registry
229 :type '(radio (const :format "Unlimited " nil)
11a3174d 230 (integer :format "Maximum number: %v")))
23f87bed 231
11a3174d
TZ
232(defcustom gnus-registry-max-pruned-entries nil
233 "Maximum number of pruned entries in the registry, nil for unlimited."
234 :group 'gnus-registry
235 :type '(radio (const :format "Unlimited " nil)
236 (integer :format "Maximum number: %v")))
237
238(defun gnus-registry-fixup-registry (db)
239 (when db
cf8b0c27
TZ
240 (let ((old (oref db :tracked)))
241 (oset db :precious
242 (append gnus-registry-extra-entries-precious
243 '()))
244 (oset db :max-hard
245 (or gnus-registry-max-entries
246 most-positive-fixnum))
247 (oset db :max-soft
248 (or gnus-registry-max-pruned-entries
249 most-positive-fixnum))
250 (oset db :tracked
251 (append gnus-registry-track-extra
252 '(mark group keyword)))
253 (when (not (equal old (oref db :tracked)))
254 (gnus-message 4 "Reindexing the Gnus registry (tracked change)")
255 (registry-reindex db))))
11a3174d
TZ
256 db)
257
258(defun gnus-registry-make-db (&optional file)
259 (interactive "fGnus registry persistence file: \n")
260 (gnus-registry-fixup-registry
261 (registry-db
262 "Gnus Registry"
263 :file (or file gnus-registry-cache-file)
264 ;; these parameters are set in `gnus-registry-fixup-registry'
265 :max-hard most-positive-fixnum
266 :max-soft most-positive-fixnum
267 :precious nil
268 :tracked nil)))
269
270(defvar gnus-registry-db (gnus-registry-make-db)
271 "*The article registry by Message ID. See `registry-db'")
272
273;; top-level registry data management
274(defun gnus-registry-remake-db (&optional forsure)
275 "Remake the registry database after customization.
276This is not required after changing `gnus-registry-cache-file'."
277 (interactive (list (y-or-n-p "Remake and CLEAR the Gnus registry? ")))
278 (when forsure
1e3b6001 279 (gnus-message 4 "Remaking the Gnus registry")
11a3174d 280 (setq gnus-registry-db (gnus-registry-make-db))))
23f87bed 281
11a3174d 282(defun gnus-registry-read ()
23f87bed
MB
283 "Read the registry cache file."
284 (interactive)
285 (let ((file gnus-registry-cache-file))
11a3174d
TZ
286 (condition-case nil
287 (progn
288 (gnus-message 5 "Reading Gnus registry from %s..." file)
289 (setq gnus-registry-db (gnus-registry-fixup-registry
290 (eieio-persistent-read file)))
291 (gnus-message 5 "Reading Gnus registry from %s...done" file))
292 (error
293 (gnus-message
294 1
295 "The Gnus registry could not be loaded from %s, creating a new one"
296 file)
297 (gnus-registry-remake-db t)))))
298
299(defun gnus-registry-save (&optional file db)
23f87bed
MB
300 "Save the registry cache file."
301 (interactive)
11a3174d
TZ
302 (let ((file (or file gnus-registry-cache-file))
303 (db (or db gnus-registry-db)))
304 (gnus-message 5 "Saving Gnus registry (%d entries) to %s..."
305 (registry-size db) file)
306 (registry-prune db)
307 ;; TODO: call (gnus-string-remove-all-properties v) on all elements?
308 (eieio-persistent-save db file)
309 (gnus-message 5 "Saving Gnus registry (size %d) to %s...done"
310 (registry-size db) file)))
311
312;; article move/copy/spool/delete actions
23f87bed
MB
313(defun gnus-registry-action (action data-header from &optional to method)
314 (let* ((id (mail-header-id data-header))
d515dc24 315 (subject (mail-header-subject data-header))
c024b021 316 (extra (mail-header-extra data-header))
8d6d9c8f 317 (recipients (gnus-registry-sort-addresses
c024b021
TZ
318 (or (cdr-safe (assq 'Cc extra)) "")
319 (or (cdr-safe (assq 'To extra)) "")))
cf8b0c27
TZ
320 (sender (nth 0 (gnus-registry-extract-addresses
321 (mail-header-from data-header))))
11a3174d
TZ
322 (from (gnus-group-guess-full-name-from-command-method from))
323 (to (if to (gnus-group-guess-full-name-from-command-method to) nil))
324 (to-name (if to to "the Bit Bucket")))
325 (gnus-message 7 "Gnus registry: article %s %s from %s to %s"
326 id (if method "respooling" "going") from to)
327
328 (gnus-registry-handle-action
329 id
330 ;; unless copying, remove the old "from" group
331 (if (not (equal 'copy action)) from nil)
cf8b0c27 332 to subject sender recipients)))
23f87bed 333
cf8b0c27 334(defun gnus-registry-spool-action (id group &optional subject sender recipients)
d515dc24 335 (let ((to (gnus-group-guess-full-name-from-command-method group))
cf8b0c27 336 (recipients (or recipients
c024b021
TZ
337 (gnus-registry-sort-addresses
338 (or (message-fetch-field "cc") "")
339 (or (message-fetch-field "to") ""))))
d515dc24
TZ
340 (subject (or subject (message-fetch-field "subject")))
341 (sender (or sender (message-fetch-field "from"))))
23f87bed
MB
342 (when (and (stringp id) (string-match "\r$" id))
343 (setq id (substring id 0 -1)))
11a3174d
TZ
344 (gnus-message 7 "Gnus registry: article %s spooled to %s"
345 id
346 to)
cf8b0c27 347 (gnus-registry-handle-action id nil to subject sender recipients)))
11a3174d 348
cf8b0c27
TZ
349(defun gnus-registry-handle-action (id from to subject sender
350 &optional recipients)
4523dc7f
G
351 (gnus-message
352 10
cf8b0c27 353 "gnus-registry-handle-action %S" (list id from to subject sender recipients))
11a3174d 354 (let ((db gnus-registry-db)
20113380
TZ
355 ;; if the group is ignored, set the destination to nil (same as delete)
356 (to (if (gnus-registry-ignore-group-p to) nil to))
11a3174d 357 ;; safe if not found
d515dc24
TZ
358 (entry (gnus-registry-get-or-make-entry id))
359 (subject (gnus-string-remove-all-properties
360 (gnus-registry-simplify-subject subject)))
361 (sender (gnus-string-remove-all-properties sender)))
11a3174d
TZ
362
363 ;; this could be done by calling `gnus-registry-set-id-key'
364 ;; several times but it's better to bunch the transactions
365 ;; together
366
367 (registry-delete db (list id) nil)
368 (when from
369 (setq entry (cons (delete from (assoc 'group entry))
370 (assq-delete-all 'group entry))))
371
cf8b0c27
TZ
372 (dolist (kv `((group ,to)
373 (sender ,sender)
374 (recipient ,@recipients)
375 (subject ,subject)))
11a3174d
TZ
376 (when (second kv)
377 (let ((new (or (assq (first kv) entry)
378 (list (first kv)))))
cf8b0c27
TZ
379 (dolist (toadd (cdr kv))
380 (add-to-list 'new toadd t))
11a3174d
TZ
381 (setq entry (cons new
382 (assq-delete-all (first kv) entry))))))
383 (gnus-message 10 "Gnus registry: new entry for %s is %S"
384 id
385 entry)
386 (registry-insert db id entry)))
23f87bed
MB
387
388;; Function for nn{mail|imap}-split-fancy: look up all references in
389;; the cache and if a match is found, return that group.
390(defun gnus-registry-split-fancy-with-parent ()
391 "Split this message into the same group as its parent. The parent
392is obtained from the registry. This function can be used as an entry
393in `nnmail-split-fancy' or `nnimap-split-fancy', for example like
bf247b6e 394this: (: gnus-registry-split-fancy-with-parent)
23f87bed 395
01c52d31
MB
396This function tracks ALL backends, unlike
397`nnmail-split-fancy-with-parent' which tracks only nnmail
398messages.
399
23f87bed 400For a message to be split, it looks for the parent message in the
01c52d31
MB
401References or In-Reply-To header and then looks in the registry
402to see which group that message was put in. This group is
14e8de0c
MB
403returned, unless `gnus-registry-follow-group-p' return nil for
404that group.
23f87bed
MB
405
406See the Info node `(gnus)Fancy Mail Splitting' for more details."
14e8de0c 407 (let* ((refstr (or (message-fetch-field "references") "")) ; guaranteed
11a3174d
TZ
408 (reply-to (message-fetch-field "in-reply-to")) ; may be nil
409 ;; now, if reply-to is valid, append it to the References
410 (refstr (if reply-to
411 (concat refstr " " reply-to)
412 refstr))
413 (references (and refstr (gnus-extract-references refstr)))
414 ;; these may not be used, but the code is cleaner having them up here
415 (sender (gnus-string-remove-all-properties
416 (message-fetch-field "from")))
8d6d9c8f 417 (recipients (gnus-registry-sort-addresses
c024b021
TZ
418 (or (message-fetch-field "cc") "")
419 (or (message-fetch-field "to") "")))
11a3174d
TZ
420 (subject (gnus-string-remove-all-properties
421 (gnus-registry-simplify-subject
422 (message-fetch-field "subject"))))
423
424 (nnmail-split-fancy-with-parent-ignore-groups
425 (if (listp nnmail-split-fancy-with-parent-ignore-groups)
426 nnmail-split-fancy-with-parent-ignore-groups
427 (list nnmail-split-fancy-with-parent-ignore-groups))))
428 (gnus-registry--split-fancy-with-parent-internal
429 :references references
430 :refstr refstr
431 :sender sender
cf8b0c27 432 :recipients recipients
11a3174d
TZ
433 :subject subject
434 :log-agent "Gnus registry fancy splitting with parent")))
435
436(defun* gnus-registry--split-fancy-with-parent-internal
437 (&rest spec
cf8b0c27 438 &key references refstr sender subject recipients log-agent
11a3174d
TZ
439 &allow-other-keys)
440 (gnus-message
441 10
2237da9c 442 "gnus-registry--split-fancy-with-parent-internal %S" spec)
11a3174d
TZ
443 (let ((db gnus-registry-db)
444 found)
2237da9c 445 ;; this is a big chain of statements. it uses
14e8de0c
MB
446 ;; gnus-registry-post-process-groups to filter the results after
447 ;; every step.
2237da9c
G
448 ;; the references string must be valid and parse to valid references
449 (when references
450 (gnus-message
451 9
452 "%s is tracing references %s"
453 log-agent refstr)
11a3174d 454 (dolist (reference (nreverse references))
2237da9c
G
455 (gnus-message 9 "%s is looking up %s" log-agent reference)
456 (loop for group in (gnus-registry-get-id-key reference 'group)
457 when (gnus-registry-follow-group-p group)
20113380
TZ
458 do
459 (progn
460 (gnus-message 7 "%s traced %s to %s" log-agent reference group)
461 (push group found))))
14e8de0c 462 ;; filter the found groups and return them
58a67d68 463 ;; the found groups are the full groups
c9fc72fa 464 (setq found (gnus-registry-post-process-groups
11a3174d
TZ
465 "references" refstr found)))
466
ba3bd5b6
TZ
467 ;; else: there were no matches, now try the extra tracking by subject
468 (when (and (null found)
469 (memq 'subject gnus-registry-track-extra)
470 subject
471 (< gnus-registry-minimum-subject-length (length subject)))
472 (let ((groups (apply
473 'append
474 (mapcar
475 (lambda (reference)
476 (gnus-registry-get-id-key reference 'group))
477 (registry-lookup-secondary-value db 'subject subject)))))
478 (setq found
479 (loop for group in groups
480 when (gnus-registry-follow-group-p group)
481 do (gnus-message
482 ;; warn more if gnus-registry-track-extra
483 (if gnus-registry-track-extra 7 9)
484 "%s (extra tracking) traced subject '%s' to %s"
485 log-agent subject group)
20113380 486 and collect group))
ba3bd5b6
TZ
487 ;; filter the found groups and return them
488 ;; the found groups are NOT the full groups
489 (setq found (gnus-registry-post-process-groups
490 "subject" subject found))))
491
11a3174d 492 ;; else: there were no matches, try the extra tracking by sender
2237da9c
G
493 (when (and (null found)
494 (memq 'sender gnus-registry-track-extra)
495 sender
1e3b6001
G
496 (not (gnus-grep-in-list
497 sender
498 gnus-registry-unfollowed-addresses)))
2237da9c
G
499 (let ((groups (apply
500 'append
501 (mapcar
502 (lambda (reference)
503 (gnus-registry-get-id-key reference 'group))
504 (registry-lookup-secondary-value db 'sender sender)))))
505 (setq found
506 (loop for group in groups
507 when (gnus-registry-follow-group-p group)
508 do (gnus-message
509 ;; warn more if gnus-registry-track-extra
510 (if gnus-registry-track-extra 7 9)
511 "%s (extra tracking) traced sender '%s' to %s"
512 log-agent sender group)
20113380 513 and collect group)))
2237da9c
G
514
515 ;; filter the found groups and return them
516 ;; the found groups are NOT the full groups
517 (setq found (gnus-registry-post-process-groups
518 "sender" sender found)))
c9fc72fa 519
cf8b0c27
TZ
520 ;; else: there were no matches, try the extra tracking by recipient
521 (when (and (null found)
522 (memq 'recipient gnus-registry-track-extra)
523 recipients)
524 (dolist (recp recipients)
525 (when (and (null found)
526 (not (gnus-grep-in-list
527 recp
528 gnus-registry-unfollowed-addresses)))
529 (let ((groups (apply 'append
530 (mapcar
531 (lambda (reference)
532 (gnus-registry-get-id-key reference 'group))
533 (registry-lookup-secondary-value
534 db 'recipient recp)))))
535 (setq found
536 (loop for group in groups
537 when (gnus-registry-follow-group-p group)
538 do (gnus-message
539 ;; warn more if gnus-registry-track-extra
540 (if gnus-registry-track-extra 7 9)
541 "%s (extra tracking) traced recipient '%s' to %s"
542 log-agent recp group)
20113380 543 and collect group)))))
cf8b0c27
TZ
544
545 ;; filter the found groups and return them
546 ;; the found groups are NOT the full groups
547 (setq found (gnus-registry-post-process-groups
548 "recipients" (mapconcat 'identity recipients ", ") found)))
549
2237da9c
G
550 ;; after the (cond) we extract the actual value safely
551 (car-safe found)))
14e8de0c 552
11a3174d
TZ
553(defun gnus-registry-post-process-groups (mode key groups)
554 "Inspects GROUPS found by MODE for KEY to determine which ones to follow.
14e8de0c
MB
555
556MODE can be 'subject' or 'sender' for example. The KEY is the
557value by which MODE was searched.
558
559Transforms each group name to the equivalent short name.
560
561Checks if the current Gnus method (from `gnus-command-method' or
562from `gnus-newsgroup-name') is the same as the group's method.
11a3174d 563Foreign methods are not supported so they are rejected.
14e8de0c
MB
564
565Reduces the list to a single group, or complains if that's not
11a3174d 566possible. Uses `gnus-registry-split-strategy'."
14e8de0c 567 (let ((log-agent "gnus-registry-post-process-group")
2237da9c
G
568 (desc (format "%d groups" (length groups)))
569 out chosen)
570 ;; the strategy can be nil, in which case chosen is nil
571 (setq chosen
11a3174d 572 (case gnus-registry-split-strategy
2237da9c
G
573 ;; default, take only one-element lists into chosen
574 ((nil)
575 (and (= (length groups) 1)
576 (car-safe groups)))
577
11a3174d 578 ((first)
2237da9c 579 (car-safe groups))
11a3174d
TZ
580
581 ((majority)
582 (let ((freq (make-hash-table
583 :size 256
584 :test 'equal)))
2237da9c
G
585 (mapc (lambda (x) (let ((x (gnus-group-short-name x)))
586 (puthash x (1+ (gethash x freq 0)) freq)))
11a3174d 587 groups)
2237da9c
G
588 (setq desc (format "%d groups, %d unique"
589 (length groups)
590 (hash-table-count freq)))
591 (car-safe
592 (sort groups
593 (lambda (a b)
594 (> (gethash (gnus-group-short-name a) freq 0)
595 (gethash (gnus-group-short-name b) freq 0)))))))))
596
597 (if chosen
598 (gnus-message
599 9
600 "%s: strategy %s on %s produced %s"
601 log-agent gnus-registry-split-strategy desc chosen)
602 (gnus-message
603 9
604 "%s: strategy %s on %s did not produce an answer"
605 log-agent
606 (or gnus-registry-split-strategy "default")
607 desc))
608
609 (setq groups (and chosen (list chosen)))
11a3174d
TZ
610
611 (dolist (group groups)
612 (let ((m1 (gnus-find-method-for-group group))
613 (m2 (or gnus-command-method
614 (gnus-find-method-for-group gnus-newsgroup-name)))
615 (short-name (gnus-group-short-name group)))
616 (if (gnus-methods-equal-p m1 m2)
617 (progn
618 ;; this is REALLY just for debugging
2237da9c
G
619 (when (not (equal group short-name))
620 (gnus-message
621 10
622 "%s: stripped group %s to %s"
623 log-agent group short-name))
11a3174d
TZ
624 (add-to-list 'out short-name))
625 ;; else...
626 (gnus-message
627 7
2237da9c 628 "%s: ignored foreign group %s"
11a3174d
TZ
629 log-agent group))))
630
2237da9c
G
631 (setq out (delq nil out))
632
11a3174d
TZ
633 (cond
634 ((= (length out) 1) out)
635 ((null out)
636 (gnus-message
637 5
1e3b6001
G
638 "%s: no matches for %s '%s'."
639 log-agent mode key)
11a3174d
TZ
640 nil)
641 (t (gnus-message
642 5
1e3b6001 643 "%s: too many extra matches (%s) for %s '%s'. Returning none."
11a3174d
TZ
644 log-agent out mode key)
645 nil))))
14e8de0c
MB
646
647(defun gnus-registry-follow-group-p (group)
648 "Determines if a group name should be followed.
649Consults `gnus-registry-unfollowed-groups' and
650`nnmail-split-fancy-with-parent-ignore-groups'."
11a3174d
TZ
651 (and group
652 (not (or (gnus-grep-in-list
653 group
654 gnus-registry-unfollowed-groups)
655 (gnus-grep-in-list
656 group
657 nnmail-split-fancy-with-parent-ignore-groups)))))
23f87bed 658
c024b021
TZ
659;; note that gnus-registry-ignored-groups is defined in gnus.el as a
660;; group/topic parameter and an associated variable!
661
662;; we do special logic for ignoring to accept regular expressions and
663;; nnmail-split-fancy-with-parent-ignore-groups as well
20113380
TZ
664(defun gnus-registry-ignore-group-p (group)
665 "Determines if a group name should be ignored.
666Consults `gnus-registry-ignored-groups' and
667`nnmail-split-fancy-with-parent-ignore-groups'."
668 (and group
74db886b 669 (or (gnus-grep-in-list
c024b021
TZ
670 group
671 (delq nil (mapcar (lambda (g)
672 (cond
673 ((stringp g) g)
674 ((and (listp g) (nth 1 g))
675 (nth 0 g))
676 (t nil))) gnus-registry-ignored-groups)))
74db886b
TZ
677 ;; only use `gnus-parameter-registry-ignore' if
678 ;; `gnus-registry-ignored-groups' is a list of lists
679 ;; (it can be a list of regexes)
680 (and (listp (nth 0 gnus-registry-ignored-groups))
681 (gnus-parameter-registry-ignore group))
c024b021
TZ
682 (gnus-grep-in-list
683 group
684 nnmail-split-fancy-with-parent-ignore-groups))))
20113380 685
01c52d31 686(defun gnus-registry-wash-for-keywords (&optional force)
11a3174d
TZ
687 "Get the keywords of the current article.
688Overrides existing keywords with FORCE set non-nil."
01c52d31
MB
689 (interactive)
690 (let ((id (gnus-registry-fetch-message-id-fast gnus-current-article))
11a3174d
TZ
691 word words)
692 (if (or (not (gnus-registry-get-id-key id 'keyword))
693 force)
694 (with-current-buffer gnus-article-buffer
695 (article-goto-body)
696 (save-window-excursion
697 (save-restriction
698 (narrow-to-region (point) (point-max))
699 (with-syntax-table gnus-adaptive-word-syntax-table
700 (while (re-search-forward "\\b\\w+\\b" nil t)
701 (setq word (gnus-string-remove-all-properties
702 (downcase (buffer-substring
703 (match-beginning 0) (match-end 0)))))
704 (if (> (length word) 2)
705 (push word words))))))
706 (gnus-registry-set-id-key id 'keyword words)))))
707
708(defun gnus-registry-keywords ()
709 (let ((table (registry-lookup-secondary gnus-registry-db 'keyword)))
710 (when table (maphash (lambda (k v) k) table))))
01c52d31
MB
711
712(defun gnus-registry-find-keywords (keyword)
11a3174d
TZ
713 (interactive (list
714 (completing-read "Keyword: " (gnus-registry-keywords) nil t)))
715 (registry-lookup-secondary-value gnus-registry-db 'keyword keyword))
01c52d31 716
23f87bed
MB
717(defun gnus-registry-register-message-ids ()
718 "Register the Message-ID of every article in the group"
719 (unless (gnus-parameter-registry-ignore gnus-newsgroup-name)
720 (dolist (article gnus-newsgroup-articles)
11a3174d
TZ
721 (let* ((id (gnus-registry-fetch-message-id-fast article))
722 (groups (gnus-registry-get-id-key id 'group)))
723 (unless (member gnus-newsgroup-name groups)
724 (gnus-message 9 "Registry: Registering article %d with group %s"
725 article gnus-newsgroup-name)
726 (gnus-registry-handle-action id nil gnus-newsgroup-name
727 (gnus-registry-fetch-simplified-message-subject-fast article)
cf8b0c27
TZ
728 (gnus-registry-fetch-sender-fast article)
729 (gnus-registry-fetch-recipients-fast article)))))))
11a3174d
TZ
730
731;; message field fetchers
23f87bed
MB
732(defun gnus-registry-fetch-message-id-fast (article)
733 "Fetch the Message-ID quickly, using the internal gnus-data-list function"
734 (if (and (numberp article)
11a3174d 735 (assoc article (gnus-data-list nil)))
23f87bed
MB
736 (mail-header-id (gnus-data-header (assoc article (gnus-data-list nil))))
737 nil))
738
cf8b0c27
TZ
739(defun gnus-registry-extract-addresses (text)
740 "Extract all the addresses in a normalized way from TEXT.
741Returns an unsorted list of strings in the name <address> format.
742Addresses without a name will say \"noname\"."
743 (mapcar (lambda (add)
744 (gnus-string-remove-all-properties
745 (let* ((name (or (nth 0 add) "noname"))
746 (addr (nth 1 add))
747 (addr (if (bufferp addr)
748 (with-current-buffer addr
749 (buffer-string))
750 addr)))
751 (format "%s <%s>" name addr))))
752 (mail-extract-address-components text t)))
753
8d6d9c8f
KY
754(defun gnus-registry-sort-addresses (&rest addresses)
755 "Return a normalized and sorted list of ADDRESSES."
756 (sort (apply 'nconc (mapcar 'gnus-registry-extract-addresses addresses))
c024b021 757 'string-lessp))
8d6d9c8f 758
23f87bed
MB
759(defun gnus-registry-simplify-subject (subject)
760 (if (stringp subject)
761 (gnus-simplify-subject subject)
762 nil))
763
764(defun gnus-registry-fetch-simplified-message-subject-fast (article)
765 "Fetch the Subject quickly, using the internal gnus-data-list function"
766 (if (and (numberp article)
11a3174d 767 (assoc article (gnus-data-list nil)))
01c52d31
MB
768 (gnus-string-remove-all-properties
769 (gnus-registry-simplify-subject
11a3174d
TZ
770 (mail-header-subject (gnus-data-header
771 (assoc article (gnus-data-list nil))))))
23f87bed
MB
772 nil))
773
774(defun gnus-registry-fetch-sender-fast (article)
cf8b0c27
TZ
775 (gnus-registry-fetch-header-fast "from" article))
776
777(defun gnus-registry-fetch-recipients-fast (article)
8d6d9c8f
KY
778 (gnus-registry-sort-addresses
779 (or (ignore-errors (gnus-registry-fetch-header-fast "Cc" article)) "")
780 (or (ignore-errors (gnus-registry-fetch-header-fast "To" article)) "")))
cf8b0c27
TZ
781
782(defun gnus-registry-fetch-header-fast (article header)
783 "Fetch the HEADER quickly, using the internal gnus-data-list function"
23f87bed 784 (if (and (numberp article)
11a3174d 785 (assoc article (gnus-data-list nil)))
01c52d31 786 (gnus-string-remove-all-properties
6b1f6ce9 787 (cdr (assq header (gnus-data-header
c024b021 788 (assoc article (gnus-data-list nil))))))
23f87bed
MB
789 nil))
790
11a3174d 791;; registry marks glue
14e8de0c
MB
792(defun gnus-registry-do-marks (type function)
793 "For each known mark, call FUNCTION for each cell of type TYPE.
794
795FUNCTION should take two parameters, a mark symbol and the cell value."
796 (dolist (mark-info gnus-registry-marks)
8f7abae3 797 (let* ((mark (car-safe mark-info))
11a3174d
TZ
798 (data (cdr-safe mark-info))
799 (cell-data (plist-get data type)))
8f7abae3 800 (when cell-data
11a3174d 801 (funcall function mark cell-data)))))
14e8de0c
MB
802
803;;; this is ugly code, but I don't know how to do it better
8f7abae3 804(defun gnus-registry-install-shortcuts ()
14e8de0c
MB
805 "Install the keyboard shortcuts and menus for the registry.
806Uses `gnus-registry-marks' to find what shortcuts to install."
8f7abae3 807 (let (keys-plist)
ec7995fa
KY
808 (setq gnus-registry-misc-menus nil)
809 (gnus-registry-do-marks
8f7abae3
MB
810 :char
811 (lambda (mark data)
812 (let ((function-format
11a3174d 813 (format "gnus-registry-%%s-article-%s-mark" mark)))
14e8de0c
MB
814
815;;; The following generates these functions:
816;;; (defun gnus-registry-set-article-Important-mark (&rest articles)
817;;; "Apply the Important mark to process-marked ARTICLES."
818;;; (interactive (gnus-summary-work-articles current-prefix-arg))
819;;; (gnus-registry-set-article-mark-internal 'Important articles nil t))
820;;; (defun gnus-registry-remove-article-Important-mark (&rest articles)
821;;; "Apply the Important mark to process-marked ARTICLES."
822;;; (interactive (gnus-summary-work-articles current-prefix-arg))
823;;; (gnus-registry-set-article-mark-internal 'Important articles t t))
824
11a3174d
TZ
825 (dolist (remove '(t nil))
826 (let* ((variant-name (if remove "remove" "set"))
827 (function-name (format function-format variant-name))
828 (shortcut (format "%c" data))
829 (shortcut (if remove (upcase shortcut) shortcut)))
830 (unintern function-name obarray)
831 (eval
832 `(defun
833 ;; function name
834 ,(intern function-name)
835 ;; parameter definition
836 (&rest articles)
837 ;; documentation
838 ,(format
839 "%s the %s mark over process-marked ARTICLES."
840 (upcase-initials variant-name)
841 mark)
842 ;; interactive definition
843 (interactive
844 (gnus-summary-work-articles current-prefix-arg))
845 ;; actual code
846
847 ;; if this is called and the user doesn't want the
848 ;; registry enabled, we'll ask anyhow
849 (when (eq gnus-registry-install nil)
850 (setq gnus-registry-install 'ask))
851
852 ;; now the user is asked if gnus-registry-install is 'ask
853 (when (gnus-registry-install-p)
854 (gnus-registry-set-article-mark-internal
855 ;; all this just to get the mark, I must be doing it wrong
856 (intern ,(symbol-name mark))
857 articles ,remove t)
858 (gnus-message
859 9
860 "Applying mark %s to %d articles"
861 ,(symbol-name mark) (length articles))
862 (dolist (article articles)
863 (gnus-summary-update-article
864 article
865 (assoc article (gnus-data-list nil)))))))
866 (push (intern function-name) keys-plist)
867 (push shortcut keys-plist)
868 (push (vector (format "%s %s"
869 (upcase-initials variant-name)
870 (symbol-name mark))
871 (intern function-name) t)
872 gnus-registry-misc-menus)
873 (gnus-message
874 9
875 "Defined mark handling function %s"
876 function-name))))))
8f7abae3 877 (gnus-define-keys-1
ec7995fa
KY
878 '(gnus-registry-mark-map "M" gnus-summary-mark-map)
879 keys-plist)
880 (add-hook 'gnus-summary-menu-hook
11a3174d
TZ
881 (lambda ()
882 (easy-menu-add-item
883 gnus-summary-misc-menu
884 nil
885 (cons "Registry Marks" gnus-registry-misc-menus))))))
8f7abae3
MB
886
887;;; use like this:
c9fc72fa 888;;; (defalias 'gnus-user-format-function-M
8f7abae3
MB
889;;; 'gnus-registry-user-format-function-M)
890(defun gnus-registry-user-format-function-M (headers)
891 (let* ((id (mail-header-message-id headers))
11a3174d
TZ
892 (marks (when id (gnus-registry-get-id-key id 'mark))))
893 (apply 'concat (mapcar (lambda (mark)
894 (let ((c
895 (plist-get
896 (cdr-safe
897 (assoc mark gnus-registry-marks))
898 :char)))
899 (if c
900 (list c)
901 nil)))
902 marks))))
0b6799c3
MB
903
904(defun gnus-registry-read-mark ()
905 "Read a mark name from the user with completion."
229b59da
G
906 (let ((mark (gnus-completing-read
907 "Label"
908 (mapcar 'symbol-name (mapcar 'car gnus-registry-marks))
909 nil nil nil
11a3174d 910 (symbol-name gnus-registry-default-mark))))
0b6799c3
MB
911 (when (stringp mark)
912 (intern mark))))
913
914(defun gnus-registry-set-article-mark (&rest articles)
915 "Apply a mark to process-marked ARTICLES."
916 (interactive (gnus-summary-work-articles current-prefix-arg))
11a3174d
TZ
917 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark)
918 articles nil t))
0b6799c3
MB
919
920(defun gnus-registry-remove-article-mark (&rest articles)
921 "Remove a mark from process-marked ARTICLES."
922 (interactive (gnus-summary-work-articles current-prefix-arg))
11a3174d
TZ
923 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark)
924 articles t t))
925
926(defun gnus-registry-set-article-mark-internal (mark
927 articles
928 &optional remove
929 show-message)
930 "Apply or remove MARK across a list of ARTICLES."
0b6799c3 931 (let ((article-id-list
11a3174d 932 (mapcar 'gnus-registry-fetch-message-id-fast articles)))
0b6799c3 933 (dolist (id article-id-list)
11a3174d
TZ
934 (let* ((marks (delq mark (gnus-registry-get-id-key id 'mark)))
935 (marks (if remove marks (cons mark marks))))
936 (when show-message
937 (gnus-message 1 "%s mark %s with message ID %s, resulting in %S"
938 (if remove "Removing" "Adding")
939 mark id marks))
940 (gnus-registry-set-id-key id 'mark marks)))))
0b6799c3
MB
941
942(defun gnus-registry-get-article-marks (&rest articles)
943 "Get the Gnus registry marks for ARTICLES and show them if interactive.
944Uses process/prefix conventions. For multiple articles,
945only the last one's marks are returned."
946 (interactive (gnus-summary-work-articles 1))
11a3174d
TZ
947 (let* ((article (last articles))
948 (id (gnus-registry-fetch-message-id-fast article))
949 (marks (when id (gnus-registry-get-id-key id 'mark))))
0b6799c3 950 (when (interactive-p)
11a3174d 951 (gnus-message 1 "Marks are %S" marks))
0b6799c3
MB
952 marks))
953
23f87bed
MB
954(defun gnus-registry-group-count (id)
955 "Get the number of groups of a message, based on the message ID."
11a3174d
TZ
956 (length (gnus-registry-get-id-key id 'group)))
957
958(defun gnus-registry-get-or-make-entry (id)
959 (let* ((db gnus-registry-db)
960 ;; safe if not found
961 (entries (registry-lookup db (list id))))
962
963 (when (null entries)
964 (registry-insert db id (list (list 'creation-time (current-time))
965 '(group) '(sender) '(subject)))
966 (setq entries (registry-lookup db (list id))))
967
968 (nth 1 (assoc id entries))))
969
42b23765
TZ
970(defun gnus-registry-delete-entries (idlist)
971 (registry-delete gnus-registry-db idlist nil))
972
11a3174d
TZ
973(defun gnus-registry-get-id-key (id key)
974 (cdr-safe (assq key (gnus-registry-get-or-make-entry id))))
975
976(defun gnus-registry-set-id-key (id key vals)
977 (let* ((db gnus-registry-db)
978 (entry (gnus-registry-get-or-make-entry id)))
979 (registry-delete db (list id) nil)
980 (setq entry (cons (cons key vals) (assq-delete-all key entry)))
981 (registry-insert db id entry)
982 entry))
983
42b23765
TZ
984(defun gnus-registry-import-eld (file)
985 (interactive "fOld registry file to import? ")
986 ;; example content:
987 ;; (setq gnus-registry-alist '(
988 ;; ("<messageID>" ((marks nil)
989 ;; (mtime 19365 1776 440496)
990 ;; (sender . "root (Cron Daemon)")
991 ;; (subject . "Cron"))
992 ;; "cron" "nnml+private:cron")
993 (load file t)
994 (when (boundp 'gnus-registry-alist)
995 (let* ((old (symbol-value 'gnus-registry-alist))
996 (count 0)
997 (expected (length old))
998 entry)
999 (while (car-safe old)
1000 (incf count)
1001 ;; don't use progress reporters for backwards compatibility
1002 (when (and (< 0 expected)
1003 (= 0 (mod count 100)))
1004 (message "importing: %d of %d (%.2f%%)"
1005 count expected (/ (* 100 count) expected)))
1006 (setq entry (car-safe old)
1007 old (cdr-safe old))
1008 (let* ((id (car-safe entry))
1009 (new-entry (gnus-registry-get-or-make-entry id))
1010 (rest (cdr-safe entry))
1011 (groups (loop for p in rest
1012 when (stringp p)
1013 collect p))
1014 extra-cell key val)
1015 ;; remove all the strings from the entry
8d6d9c8f 1016 (dolist (elem rest)
c024b021 1017 (if (stringp elem) (setq rest (delq elem rest))))
42b23765
TZ
1018 (gnus-registry-set-id-key id 'group groups)
1019 ;; just use the first extra element
1020 (setq rest (car-safe rest))
1021 (while (car-safe rest)
1022 (setq extra-cell (car-safe rest)
1023 key (car-safe extra-cell)
1024 val (cdr-safe extra-cell)
1025 rest (cdr-safe rest))
1026 (when (and val (atom val))
1027 (setq val (list val)))
1028 (gnus-registry-set-id-key id key val))))
1029 (message "Import done, collected %d entries" count))))
11a3174d 1030
cf8b0c27
TZ
1031(ert-deftest gnus-registry-misc-test ()
1032 (should-error (gnus-registry-extract-addresses '("" "")))
1033
1034 (should (equal '("Ted Zlatanov <tzz@lifelogs.com>"
1035 "noname <ed@you.me>"
1036 "noname <cyd@stupidchicken.com>"
1037 "noname <tzz@lifelogs.com>")
1038 (gnus-registry-extract-addresses
1039 (concat "Ted Zlatanov <tzz@lifelogs.com>, "
1040 "ed <ed@you.me>, " ; "ed" is not a valid name here
1041 "cyd@stupidchicken.com, "
1042 "tzz@lifelogs.com")))))
1043
11a3174d
TZ
1044(ert-deftest gnus-registry-usage-test ()
1045 (let* ((n 100)
1046 (tempfile (make-temp-file "gnus-registry-persist"))
1047 (db (gnus-registry-make-db tempfile))
1048 (gnus-registry-db db)
1049 back size)
1050 (message "Adding %d keys to the test Gnus registry" n)
1051 (dotimes (i n)
1052 (let ((id (number-to-string i)))
1053 (gnus-registry-handle-action id
1054 (if (>= 50 i) "fromgroup" nil)
1055 "togroup"
1056 (when (>= 70 i)
1057 (format "subject %d" (mod i 10)))
1058 (when (>= 80 i)
1059 (format "sender %d" (mod i 10))))))
1060 (message "Testing Gnus registry size is %d" n)
1061 (should (= n (registry-size db)))
1062 (message "Looking up individual keys (registry-lookup)")
1063 (should (equal (loop for e
1064 in (mapcar 'cadr
1065 (registry-lookup db '("20" "83" "72")))
1066 collect (assq 'subject e)
1067 collect (assq 'sender e)
1068 collect (assq 'group e))
1069 '((subject "subject 0") (sender "sender 0") (group "togroup")
1070 (subject) (sender) (group "togroup")
1071 (subject) (sender "sender 2") (group "togroup"))))
1072
1073 (message "Looking up individual keys (gnus-registry-id-key)")
1074 (should (equal (gnus-registry-get-id-key "34" 'group) '("togroup")))
1075 (should (equal (gnus-registry-get-id-key "34" 'subject) '("subject 4")))
1076 (message "Trying to insert a duplicate key")
1077 (should-error (registry-insert db "55" '()))
1078 (message "Looking up individual keys (gnus-registry-get-or-make-entry)")
1079 (should (gnus-registry-get-or-make-entry "22"))
1080 (message "Saving the Gnus registry to %s" tempfile)
1081 (should (gnus-registry-save tempfile db))
1082 (setq size (nth 7 (file-attributes tempfile)))
1083 (message "Saving the Gnus registry to %s: size %d" tempfile size)
1084 (should (< 0 size))
1085 (with-temp-buffer
1086 (insert-file-contents-literally tempfile)
1087 (should (looking-at (concat ";; Object "
1088 "Gnus Registry"
1089 "\n;; EIEIO PERSISTENT OBJECT"))))
1090 (message "Reading Gnus registry back")
1091 (setq back (eieio-persistent-read tempfile))
1092 (should back)
1093 (message "Read Gnus registry back: %d keys, expected %d==%d"
1094 (registry-size back) n (registry-size db))
1095 (should (= (registry-size back) n))
1096 (should (= (registry-size back) (registry-size db)))
1097 (delete-file tempfile)
1098 (message "Pruning Gnus registry to 0 by setting :max-soft")
1099 (oset db :max-soft 0)
1100 (registry-prune db)
1101 (should (= (registry-size db) 0)))
1102 (message "Done with Gnus registry usage testing."))
23f87bed
MB
1103
1104;;;###autoload
1105(defun gnus-registry-initialize ()
8f7abae3 1106"Initialize the Gnus registry."
23f87bed 1107 (interactive)
8f7abae3 1108 (gnus-message 5 "Initializing the registry")
11a3174d 1109 (setq gnus-registry-install t) ; in case it was 'ask or nil
23f87bed 1110 (gnus-registry-install-hooks)
8f7abae3 1111 (gnus-registry-install-shortcuts)
23f87bed
MB
1112 (gnus-registry-read))
1113
1114;;;###autoload
1115(defun gnus-registry-install-hooks ()
1116 "Install the registry hooks."
1117 (interactive)
bf247b6e 1118 (add-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1119 (add-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1120 (add-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1121 (add-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1122
23f87bed
MB
1123 (add-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1124 (add-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1125
1126 (add-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1127
1128(defun gnus-registry-unload-hook ()
1129 "Uninstall the registry hooks."
1130 (interactive)
bf247b6e 1131 (remove-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1132 (remove-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1133 (remove-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1134 (remove-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1135
23f87bed
MB
1136 (remove-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1137 (remove-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1138
1139 (remove-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1140
6d52545d
RS
1141(add-hook 'gnus-registry-unload-hook 'gnus-registry-unload-hook)
1142
8f7abae3
MB
1143(defun gnus-registry-install-p ()
1144 (interactive)
1145 (when (eq gnus-registry-install 'ask)
1146 (setq gnus-registry-install
11a3174d
TZ
1147 (gnus-y-or-n-p
1148 (concat "Enable the Gnus registry? "
1149 "See the variable `gnus-registry-install' "
1150 "to get rid of this query permanently. ")))
8f7abae3
MB
1151 (when gnus-registry-install
1152 ;; we just set gnus-registry-install to t, so initialize the registry!
1153 (gnus-registry-initialize)))
1154;;; we could call it here: (customize-variable 'gnus-registry-install)
1155 gnus-registry-install)
1156
8f7abae3 1157;; TODO: a few things
23f87bed
MB
1158
1159(provide 'gnus-registry)
1160
23f87bed 1161;;; gnus-registry.el ends here