;; Restrictions:
;;
-;; * If you don't use HyREX as your search engine, this expects that
-;; you use nnml or another one-file-per-message backend, because the
-;; others doesn't support nnfolder.
+;; * This expects that you use nnml or another one-file-per-message backend,
+;; because the others doesn't support nnfolder.
;; * It can only search the mail backend's which are supported by one
;; search engine, because of different query languages.
;; * There are restrictions to the Wais setup.
;; like this :
;; (setq gnus-secondary-select-methods '(
;; (nnimap "" (nnimap-address "localhost")
-;; (nnir-search-engine hyrex)
-;; (nnir-hyrex-additional-switches ("-d" "ddl-nnimap.xml"))
+;; (nnir-search-engine namazu)
;; )))
;; Or you can define the global ones. The variables set in the mailer-
;; definition will be used first.
;; For maximum searching efficiency I have a cron job set to run this
;; command every four hours.
-;; 3. HyREX
-;;
-;; The HyREX backend requires you to have one directory from where all
-;; your relative paths are to, if you use them. This directory must be
-;; set in the `nnir-hyrex-index-directory' variable, which defaults to
-;; your home directory. You must also pass the base, class and
-;; directory options or simply your dll to the `nnir-hyrex-programm' by
-;; setting the `nnir-hyrex-additional-switches' variable accordently.
-;; To function the `nnir-hyrex-remove-prefix' variable must also be
-;; correct, see the documentation for `nnir-wais-remove-prefix' above.
-
-;; 4. find-grep
+;; 3. find-grep
;;
;; The find-grep engine simply runs find(1) to locate eligible
;; articles and searches them with grep(1). This, of course, is much
;; are simple, but what about the article number?
;; - The article number is encoded in the `X-Gnus-Article-Number'
;; header of each mail.
-;; - The HyREX engine supports nnfolder.
;;
;; * Support compressed mail files. Probably, just stripping off the
;; `.gz' or `.Z' file name extension is sufficient.
(eval-when-compile
(require 'cl))
+
+(eval-when-compile
+ (autoload 'nnimap-buffer "nnimap")
+ (autoload 'nnimap-command "nnimap")
+ (autoload 'nnimap-possibly-change-group "nnimap"))
+
(nnoo-declare nnir)
(nnoo-define-basics nnir)
(gnus-declare-backend "nnir" 'mail)
-(defvar nnir-imap-search-field "TEXT"
- "The IMAP search item when doing an nnir search. To use raw
- imap queries by default set this to \"\"")
+(defvar nnir-imap-default-search-key "Whole message"
+ "The default IMAP search key for an nnir search. Must be one of
+ the keys in nnir-imap-search-arguments. To use raw imap queries
+ by default set this to \"Imap\"")
(defvar nnir-imap-search-arguments
'(("Whole message" . "TEXT")
("Subject" . "SUBJECT")
("To" . "TO")
("From" . "FROM")
- ("Head" . "HEADER \"%s\"")
- (nil . ""))
- "Mapping from user readable strings to IMAP search items for use in nnir")
+ ("Imap" . ""))
+ "Mapping from user readable keys to IMAP search items for use in nnir")
+
+(defvar nnir-imap-search-other "HEADER %S"
+ "The IMAP search item to use for anything other than
+ nnir-imap-search-arguments. By default this is the name of an
+ email header field")
(defvar nnir-imap-search-argument-history ()
"The history for querying search options in nnir")
If this variable is nil, or if the provided function returns nil for a search
result, `gnus-retrieve-headers' will be called instead.")
+(defvar nnir-method-default-engines
+ '((nnimap . imap)
+ (nntp . nil))
+ "Alist of default search engines by server method")
;;; Developer Extension Variable:
())
(imap nnir-run-imap
((criteria
- "Search in: " ; Prompt
- ,nnir-imap-search-arguments ; alist for completing
- nil ; no filtering
+ "Search in" ; Prompt
+ ,(mapcar 'car nnir-imap-search-arguments) ; alist for completing
nil ; allow any user input
nil ; initial value
nnir-imap-search-argument-history ; the history to use
- ,nnir-imap-search-field ; default
+ ,nnir-imap-default-search-key ; default
)))
(swish++ nnir-run-swish++
((group . "Group spec: ")))
((group . "Group spec: ")))
(namazu nnir-run-namazu
())
- (hyrex nnir-run-hyrex
- ((group . "Group spec: ")))
- (find-grep nnir-run-find-grep
- ((grep-options . "Grep options: "))))
+ (find-grep nnir-run-find-grep
+ ((grep-options . "Grep options: "))))
"Alist of supported search engines.
Each element in the alist is a three-element list (ENGINE FUNCTION ARGS).
ENGINE is a symbol designating the searching engine. FUNCTION is also
:group 'nnir)
;; Swish-E.
-;; URL: http://sunsite.berkeley.edu/SWISH-E/
-;; New version: http://www.boe.es/swish-e
+;; URL: http://swish-e.org/
;; Variables `nnir-swish-e-index-file', `nnir-swish-e-program' and
;; `nnir-swish-e-additional-switches'
:type '(regexp)
:group 'nnir)
-;; HyREX engine, see <URL:http://ls6-www.cs.uni-dortmund.de/>
-
-(defcustom nnir-hyrex-program "nnir-search"
- "*Name of the nnir-search executable."
- :type '(string)
- :group 'nnir)
-
-(defcustom nnir-hyrex-additional-switches '()
- "*A list of strings, to be given as additional arguments for nnir-search.
-Note that this should be a list. Ie, do NOT use the following:
- (setq nnir-hyrex-additional-switches \"-ddl ddl.xml -c nnir\") ; wrong !
-Instead, use this:
- (setq nnir-hyrex-additional-switches '(\"-ddl\" \"ddl.xml\" \"-c\" \"nnir\"))"
- :type '(repeat (string))
- :group 'nnir)
-
-(defcustom nnir-hyrex-index-directory (getenv "HOME")
- "*Index directory for HyREX."
- :type '(directory)
- :group 'nnir)
-
-(defcustom nnir-hyrex-remove-prefix (concat (getenv "HOME") "/Mail/")
- "*The prefix to remove from each file name returned by HyREX
-in order to get a group name (albeit with / instead of .).
-
-For example, suppose that HyREX returns file names such as
-\"/home/john/Mail/mail/misc/42\". For this example, use the following
-setting: (setq nnir-hyrex-remove-prefix \"/home/john/Mail/\")
-Note the trailing slash. Removing this prefix gives \"mail/misc/42\".
-`nnir' knows to remove the \"/42\" and to replace \"/\" with \".\" to
-arrive at the correct group name, \"mail.misc\"."
- :type '(directory)
- :group 'nnir)
-
-;; Namazu engine, see <URL:http://ww.namazu.org/>
+;; Namazu engine, see <URL:http://www.namazu.org/>
(defcustom nnir-namazu-program "namazu"
"*Name of Namazu search executable."
gnus-current-window-configuration)
nil)))
-(eval-when-compile
- (when (featurep 'xemacs)
- ;; The `kbd' macro requires that the `read-kbd-macro' macro is available.
- (require 'edmacro)))
-
-(defun nnir-group-mode-hook ()
- (define-key gnus-group-mode-map (kbd "G G")
- 'gnus-group-make-nnir-group))
-(add-hook 'gnus-group-mode-hook 'nnir-group-mode-hook)
-
;; Why is this needed? Is this for compatibility with old/new gnusae? Using
;; gnus-group-server instead works for me. -- Justus Piater
(defmacro nnir-group-server (group)
(let* ((cur (gnus-summary-article-number))
(group (nnir-artlist-artitem-group nnir-artlist cur))
(backend-number (nnir-artlist-artitem-number nnir-artlist cur))
- server backend-group)
- (setq server (nnir-group-server group))
- (setq backend-group (gnus-group-real-name group))
- (gnus-group-read-ephemeral-group
- backend-group
- (gnus-server-to-method server)
- t ; activate
- (cons (current-buffer)
- 'summary) ; window config
- nil
- (list backend-number))
- (gnus-summary-limit (list backend-number))
- (gnus-summary-refer-thread)))
+ (id (mail-header-id (gnus-summary-article-header)))
+ (refs (split-string
+ (mail-header-references (gnus-summary-article-header)))))
+ (if (eq (car (gnus-group-method group)) 'nnimap)
+ (progn (nnimap-possibly-change-group (gnus-group-short-name group) nil)
+ (with-current-buffer (nnimap-buffer)
+ (let* ((cmd (let ((value (format
+ "(OR HEADER REFERENCES %s HEADER Message-Id %s)"
+ id id)))
+ (dolist (refid refs value)
+ (setq value (format
+ "(OR (OR HEADER Message-Id %s HEADER REFERENCES %s) %s)"
+ refid refid value)))))
+ (result (nnimap-command
+ "UID SEARCH %s" cmd)))
+ (gnus-summary-read-group-1 group t t gnus-summary-buffer nil
+ (and (car result)
+ (delete 0 (mapcar #'string-to-number
+ (cdr (assoc "SEARCH" (cdr result))))))))))
+ (gnus-summary-read-group-1 group t t gnus-summary-buffer
+ nil (list backend-number))
+ (gnus-summary-limit (list backend-number))
+ (gnus-summary-refer-thread))))
+
(if (fboundp 'eval-after-load)
(eval-after-load "gnus-sum"
(if nnir-get-article-nov-override-function
(setq novitem (funcall nnir-get-article-nov-override-function
artitem))
- ;; else, set novitem through nnheader-parse-nov/nnheader-parse-head
+ ;; else, set novitem through nnheader-parse-nov/nnheader-parse-head
(case (setq foo (gnus-retrieve-headers (list artno)
artfullgroup nil))
(nov
(goto-char (point-min))
- (setq novitem (nnheader-parse-nov))
- (unless novitem
- (pop-to-buffer nntp-server-buffer)
- (error
- "nnheader-parse-nov returned nil for article %s in group %s"
- artno artfullgroup)))
+ (setq novitem (nnheader-parse-nov)))
(headers
(goto-char (point-min))
- (setq novitem (nnheader-parse-head))
- (unless novitem
- (pop-to-buffer nntp-server-buffer)
- (error
- "nnheader-parse-head returned nil for article %s in group %s"
- artno artfullgroup)))
+ (setq novitem (nnheader-parse-head)))
(t (error "Unknown header type %s while requesting article %s of group %s"
foo artno artfullgroup)))))
;; replace article number in original group with article number
;; in nnir group
- (mail-header-set-number novitem art)
- (mail-header-set-from novitem
- (mail-header-from novitem))
- (mail-header-set-subject
- novitem
- (format "[%d: %s/%d] %s"
- artrsv artgroup artno
- (mail-header-subject novitem)))
- ;;-(mail-header-set-extra novitem nil)
- (push novitem novdata)
- (setq artlist (cdr artlist)))
+ (when novitem
+ (mail-header-set-number novitem art)
+ (mail-header-set-from novitem
+ (mail-header-from novitem))
+ (mail-header-set-subject
+ novitem
+ (format "[%d: %s/%d] %s"
+ artrsv artgroup artno
+ (mail-header-subject novitem)))
+ (push novitem novdata)
+ (setq artlist (cdr artlist))))
(setq novdata (nreverse novdata))
(set-buffer nntp-server-buffer) (erase-buffer)
(mapc 'nnheader-insert-nov novdata)
(when (file-readable-p (concat prefix dirnam article))
;; remove trailing slash and, for nnmaildir, cur/new/tmp
(setq dirnam
- (substring dirnam 0 (if (string= server "nnmaildir:") -5 -1)))
+ (substring dirnam 0
+ (if (string= (gnus-group-server server) "nnmaildir")
+ -5 -1)))
;; Set group to dirnam without any leading dots or slashes,
;; and with all subsequent slashes replaced by dots
"[/\\]" "." t)))
(vector (nnir-group-full-name group server)
- (if (string= server "nnmaildir:")
+ (if (string= (gnus-group-server server) "nnmaildir")
(nnmaildir-base-name-to-article-number
(substring article 0 (string-match ":" article))
group nil)
;; IMAP interface.
;; todo:
-;; nnir invokes this two (2) times???!
-;; we should not use nnimap at all but open our own server connection
-;; we should not LIST * but use nnimap-list-pattern from defs
;; send queries as literals
;; handle errors
-(autoload 'nnimap-open-server "nnimap")
-(defvar nnimap-server-buffer) ;; nnimap.el
-(autoload 'imap-mailbox-select "imap")
-(autoload 'imap-search "imap")
-(autoload 'imap-quote-specials "imap")
-
-(eval-when-compile
- (autoload 'nnimap-buffer "nnimap")
- (autoload 'nnimap-command "nnimap")
- (autoload 'nnimap-possibly-change-group "nnimap"))
(defun nnir-run-imap (query srv &optional group-option)
"Run a search against an IMAP back-end server.
(group (or group-option (gnus-group-group-name)))
(defs (caddr (gnus-server-to-method srv)))
(criteria (or (cdr (assq 'criteria query))
- nnir-imap-search-field))
+ (cdr (assoc nnir-imap-default-search-key
+ nnir-imap-search-arguments))))
(gnus-inhibit-demon t)
artlist)
(message "Opening server %s" server)
(message "Searching %s..." group)
(let ((arts 0)
(result
- (nnimap-command "UID SEARCH %s"
+ (nnimap-command "UID SEARCH %s"
(if (string= criteria "")
qstring
(nnir-imap-make-query criteria qstring)
(cond
;; Simple string term
((stringp expr)
- (format "%s \"%s\"" criteria (imap-quote-specials expr)))
+ (format "%s %S" criteria expr))
;; Trivial term: and
((eq expr 'and) nil)
;; Composite term: or expression
;; is sufficient. Note that we can't only use the value of
;; nnml-use-compressed-files because old articles might have been
;; saved with a different value.
- (article-pattern (if (string= server "nnmaildir:")
+ (article-pattern (if (string= (gnus-group-server server) "nnmaildir")
":[0-9]+"
"^[0-9]+\\(\\.[a-z0-9]+\\)?$"))
score artno dirnam filenam)
(> (nnir-artitem-rsv x)
(nnir-artitem-rsv y)))))))))
-;; HyREX interface
-(defun nnir-run-hyrex (query server &optional group)
- (save-excursion
- (let ((artlist nil)
- (groupspec (cdr (assq 'group query)))
- (qstring (cdr (assq 'query query)))
- (prefix (nnir-read-server-parm 'nnir-hyrex-remove-prefix server))
- score artno dirnam)
- (when (and group groupspec)
- (error (concat "It does not make sense to use a group spec"
- " with process-marked groups.")))
- (when group
- (setq groupspec (gnus-group-real-name group)))
- (when (and group (not (equal group (nnir-group-full-name groupspec server))))
- (message "%s vs. %s" group (nnir-group-full-name groupspec server))
- (error "Server with groupspec doesn't match group !"))
- (set-buffer (get-buffer-create nnir-tmp-buffer))
- (erase-buffer)
- (if groupspec
- (message "Doing hyrex-search query %s on %s..." query groupspec)
- (message "Doing hyrex-search query %s..." query))
- (let* ((cp-list
- `( ,nnir-hyrex-program
- nil ; input from /dev/null
- t ; output
- nil ; don't redisplay
- "-i",(nnir-read-server-parm 'nnir-hyrex-index-directory server) ; index directory
- ,@(nnir-read-server-parm 'nnir-hyrex-additional-switches server)
- ,qstring ; the query, in hyrex-search format
- ))
- (exitstatus
- (progn
- (message "%s args: %s" nnir-hyrex-program
- (mapconcat 'identity (cddddr cp-list) " "))
- (apply 'call-process cp-list))))
- (unless (or (null exitstatus)
- (zerop exitstatus))
- (nnheader-report 'nnir "Couldn't run hyrex-search: %s" exitstatus)
- ;; nnir-search failure reason is in this buffer, show it if
- ;; the user wants it.
- (when (> gnus-verbose 6)
- (display-buffer nnir-tmp-buffer)))) ;; FIXME: Dont clear buffer !
- (if groupspec
- (message "Doing hyrex-search query \"%s\" on %s...done" qstring groupspec)
- (message "Doing hyrex-search query \"%s\"...done" qstring))
- (sit-for 0)
- ;; nnir-search returns:
- ;; for nnml/nnfolder: "filename mailid weigth"
- ;; for nnimap: "group mailid weigth"
- (goto-char (point-min))
- (delete-non-matching-lines "^\\S + [0-9]+ [0-9]+$")
- ;; HyREX couldn't search directly in groups -- so filter out here.
- (when groupspec
- (keep-lines groupspec))
- ;; extract data from result lines
- (goto-char (point-min))
- (while (re-search-forward
- "\\(\\S +\\) \\([0-9]+\\) \\([0-9]+\\)" nil t)
- (setq dirnam (match-string 1)
- artno (match-string 2)
- score (match-string 3))
- (when (string-match prefix dirnam)
- (setq dirnam (replace-match "" t t dirnam)))
- (push (vector (nnir-group-full-name
- (gnus-replace-in-string dirnam "/" ".") server)
- (string-to-number artno)
- (string-to-number score))
- artlist))
- (message "Massaging hyrex-search output...done.")
- (apply 'vector
- (sort artlist
- (function (lambda (x y)
- (if (string-lessp (nnir-artitem-group x)
- (nnir-artitem-group y))
- t
- (< (nnir-artitem-number x)
- (nnir-artitem-number y)))))))
- )))
-
;; Namazu interface
(defun nnir-run-namazu (query server &optional group)
"Run given query against Namazu. Returns a vector of (group name, file name)
(when group
(error "The Namazu backend cannot search specific groups"))
(save-excursion
- (let ((article-pattern (if (string= server "nnmaildir:")
+ (let ((article-pattern (if (string= (gnus-group-server server) "nnmaildir")
":[0-9]+"
"^[0-9]+$"))
artlist
(let ((sym (car parmspec))
(prompt (cdr parmspec)))
(if (listp prompt)
- (let* ((result (gnus-completing-read prompt nil))
+ (let* ((result (apply 'gnus-completing-read prompt))
(mapping (or (assoc result nnir-imap-search-arguments)
- (assoc nil nnir-imap-search-arguments))))
+ (cons nil nnir-imap-search-other))))
(cons sym (format (cdr mapping) result)))
(cons sym (read-string prompt)))))
(if gnus-group-marked
(apply 'vconcat
(mapcar (lambda (x)
- (let ((server (nnir-group-server x))
- search-func)
+ (let* ((server (nnir-group-server x))
+ (engine
+ (or (nnir-read-server-parm 'nnir-search-engine
+ server)
+ (cdr
+ (assoc (car (gnus-server-to-method server))
+ nnir-method-default-engines))))
+ search-func)
(setq search-func (cadr
(assoc
- (nnir-read-server-parm 'nnir-search-engine server) nnir-engines)))
+ engine
+ nnir-engines)))
(if search-func
(funcall search-func q server x)
nil)))
- gnus-group-marked)
- )
+ gnus-group-marked))
(apply 'vconcat
(mapcar (lambda (x)
(if (and (equal (cadr x) 'ok) (not (equal (cadar x) "-ephemeral")))
- (let ((server (format "%s:%s" (caar x) (cadar x)))
- search-func)
+ (let* ((server (format "%s:%s" (caar x) (cadar x)))
+ (engine
+ (or (nnir-read-server-parm 'nnir-search-engine
+ server)
+ (cdr
+ (assoc (car (gnus-server-to-method server))
+ nnir-method-default-engines))))
+ search-func)
(setq search-func (cadr
(assoc
- (nnir-read-server-parm 'nnir-search-engine server) nnir-engines)))
+ engine
+ nnir-engines)))
(if search-func
(funcall search-func q server nil)
nil))