Merge changes made Emacs trunk.
[gnus] / lisp / nnmairix.el
1 ;;; nnmairix.el --- Mairix back end for Gnus, the Emacs newsreader
2
3 ;; Copyright (C) 2007-2011  Free Software Foundation, Inc.
4
5 ;; Author: David Engster <dengste@eml.cc>
6 ;; Keywords: mail searching
7 ;; Version: 0.6
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; This is a back end for using the mairix search engine with
27 ;; Gnus.  Mairix is a tool for searching words in locally stored
28 ;; mail.  Mairix is very fast which allows using it efficiently for
29 ;; "smart folders", e.g. folders which are associated with search
30 ;; queries.  Of course, you can also use this back end just for
31 ;; calling mairix with some search query.
32 ;;
33 ;; Mairix is written by Richard Curnow.  More information can be found at
34 ;; http://www.rpcurnow.force9.co.uk/mairix/
35
36 ;; Commentary on the code: nnmairix sits between Gnus and the "real"
37 ;; back end which handles the mail (currently nnml, nnimap and
38 ;; nnmaildir were tested). I know this is all a bit hacky, but so far
39 ;; it works for me.  This is the first back end I've written for Gnus,
40 ;; so I'd appreciate any comments, suggestions, bug reports (and, of
41 ;; course, patches) for improving nnmairix.
42
43 ;; nnmairix does not use an active file, since I wanted to contain the
44 ;; back end "inside Gnus" as much as possible without the need of an
45 ;; external file.  It stores the query/folder information in the group
46 ;; parameters instead.  This also implies that once you kill a mairix
47 ;; group, it's gone for good.  I don't think that this is really
48 ;; problematic, since I don't see the need in unsubscribing and
49 ;; re-subscribing search groups
50
51 ;; Every mairix server is "responsible" for one mairix installation,
52 ;; i.e. you can have several mairix servers for different mairix
53 ;; configurations.  Not that I think anyone will actually do this, but
54 ;; I thought it would be a "nice to have feature"...
55
56 ;; KNOWN BUGS:
57 ;; * Mairix does only support us-ascii characters.
58
59 ;; TODO/MISSING FEATURES:
60 ;; * Support of more back ends (nnmh, nnfolder, nnmbox...)?
61 ;; * Maybe use an active file instead of group parameters?
62 ;; * Maybe use "-a" when updating groups which are not newly created?
63
64 ;;; Changelog:
65 ;; 05/30/2008 - version 0.6
66 ;;
67 ;;    * It is now possible to propagate marks from the nnmairix groups
68 ;;      to the original messages (and for maildir also vice versa). See
69 ;;      the docs for details on this feature - it's pretty delicate
70 ;;      and currently needs a patched mairix binary to work smoothly.
71 ;;
72 ;;    * Keep messages in nnmairix groups always read/unread
73 ;;      (bound to 'G b r').
74 ;;
75 ;;    * Recreate back end folder for nnmairix groups in case you
76 ;;      somehow get wrong article counts (bound to 'G b d').
77 ;;
78 ;;    * New group parameter 'allow-fast'. Toggling of parameter bound
79 ;;      to 'G b a'. The default is nil, meaning that the group will
80 ;;      always be updated with a mairix search, even when only entered.
81 ;;
82 ;;    * More/Better use of the registry (if available). Can now also
83 ;;      deal with duplicate messages in different groups.
84 ;;
85 ;; 02/06/2008 - version 0.5
86 ;;
87 ;;    * New function: nnmairix-goto-original-article. Uses the
88 ;;      registry or the mail file path for determining original group.
89 ;;
90 ;;    * Deal with empty Xref header
91 ;;
92 ;;    * Changed summary mode keybindings since the old ones were
93 ;;      already taken
94 ;;
95 ;;   (Thanks to Tassilo Horn and Ted Zlatanov for their help)
96 ;;
97 ;; 01/07/2008 - version 0.4
98 ;;
99 ;;    * New/fixed doc strings and code cleanup.
100 ;;
101 ;; 11/18/2007 - version 0.3
102 ;;
103 ;;    * Fixed bugs when dealing with nnml and native servers
104 ;;
105 ;;    * Make variables customizable
106 ;;
107 ;; 10/10/2007 - version 0.2
108 ;;
109 ;;    * Use nnml-directory/directory server variables for nnml and
110 ;;    nnmaildir back ends as path for search folders. This way it
111 ;;    becomes independent of 'base' setting in .mairixirc (but not for
112 ;;    nnimap).
113 ;;
114 ;;    * As a result: Changed nnmairix-backend-to-server so that user
115 ;;    is asked when more than one nnmairix server exists and we do not
116 ;;    know which one is responsible for current back end.
117 ;;
118 ;;    * Rename files when using nnml back ends so that there are no
119 ;;    holes in article numbers. This should fix all problems regarding
120 ;;    wrong article counts with nnml.
121 ;;
122 ;;    * More commands for creating queries (using widgets or the
123 ;;    minibuffer).
124 ;;
125 ;;    * Fixed bug in nnmairix-create-search-group-from-message
126 ;;
127 ;;    * Changed copyright to FSF
128 ;;
129 ;;      (Thanks to Georg C. F. Greve and Bastien for suggestions and
130 ;;      ideas!)
131 ;;
132 ;; 10/03/2007 - version 0.1 - first release
133
134
135 ;;; Code:
136
137 (eval-when-compile (require 'cl))       ;For (pop (cdr ogroup)).
138
139 (require 'nnoo)
140 (require 'gnus-group)
141 (require 'gnus-sum)
142 (require 'message)
143 (require 'nnml)
144 (require 'widget)
145
146 (nnoo-declare nnmairix)
147
148 ;;; === Keymaps
149
150 (eval-when-compile
151   (when (featurep 'xemacs)
152     ;; The `kbd' macro requires that the `read-kbd-macro' macro is available.
153     (require 'edmacro)))
154
155 ;; Group mode
156 (defun nnmairix-group-mode-hook ()
157   "Nnmairix group mode keymap."
158   (define-key gnus-group-mode-map
159     (kbd "G b") (make-sparse-keymap))
160   (define-key gnus-group-mode-map
161     (kbd "G b g") 'nnmairix-create-search-group)
162   (define-key gnus-group-mode-map
163     (kbd "G b c") 'nnmairix-create-server-and-default-group)
164   (define-key gnus-group-mode-map
165     (kbd "G b q") 'nnmairix-group-change-query-this-group)
166   (define-key gnus-group-mode-map
167     (kbd "G b t") 'nnmairix-group-toggle-threads-this-group)
168   (define-key gnus-group-mode-map
169     (kbd "G b u") 'nnmairix-update-database)
170   (define-key gnus-group-mode-map
171     (kbd "G b s") 'nnmairix-search)
172   (define-key gnus-group-mode-map
173     (kbd "G b i") 'nnmairix-search-interactive)
174   (define-key gnus-group-mode-map
175     (kbd "G b m") 'nnmairix-widget-search)
176   (define-key gnus-group-mode-map
177     (kbd "G b p") 'nnmairix-group-toggle-propmarks-this-group)
178   (define-key gnus-group-mode-map
179     (kbd "G b r") 'nnmairix-group-toggle-readmarks-this-group)
180   (define-key gnus-group-mode-map
181     (kbd "G b d") 'nnmairix-group-delete-recreate-this-group)
182   (define-key gnus-group-mode-map
183     (kbd "G b a") 'nnmairix-group-toggle-allowfast-this-group)
184   (define-key gnus-group-mode-map
185     (kbd "G b o") 'nnmairix-propagate-marks))
186
187 ;; Summary mode
188 (defun nnmairix-summary-mode-hook ()
189   "Nnmairix summary mode keymap."
190   (define-key gnus-summary-mode-map
191     (kbd "G G t") 'nnmairix-search-thread-this-article)
192   (define-key gnus-summary-mode-map
193     (kbd "G G f") 'nnmairix-search-from-this-article)
194   (define-key gnus-summary-mode-map
195     (kbd "G G m") 'nnmairix-widget-search-from-this-article)
196   (define-key gnus-summary-mode-map
197     (kbd "G G g") 'nnmairix-create-search-group-from-message)
198   (define-key gnus-summary-mode-map
199     (kbd "G G o") 'nnmairix-goto-original-article)
200   (define-key gnus-summary-mode-map
201     (kbd "G G u") 'nnmairix-remove-tick-mark-original-article))
202
203 (add-hook 'gnus-group-mode-hook 'nnmairix-group-mode-hook)
204 (add-hook 'gnus-summary-mode-hook 'nnmairix-summary-mode-hook)
205
206 ;; ;;;###autoload
207 ;; (defun nnmairix-initalize (&optional force)
208 ;;   (interactive "P")
209 ;;   (if (not (or (file-readable-p "~/.mairixrc")
210 ;;             force))
211 ;;       (message "No file `~/.mairixrc', skipping nnmairix setup")
212 ;;     (add-hook 'gnus-group-mode-hook 'nnmairix-group-mode-hook)
213 ;;     (add-hook 'gnus-summary-mode-hook 'nnmairix-summary-mode-hook)))
214
215 ;; Customizable stuff
216
217 (defgroup nnmairix nil
218   "Back end for the Mairix mail search engine."
219   :group 'gnus)
220
221 (defcustom nnmairix-group-prefix "zz_mairix"
222   "Prefix for mairix search groups on back end server.
223 nnmairix will create these groups automatically on the back end
224 server for each nnmairix search group.  The name on the back end
225 server will be this prefix plus a random number.  You can delete
226 unused nnmairix groups on the back end using
227 `nnmairix-purge-old-groups'."
228   :version "23.1"
229   :type 'string
230   :group 'nnmairix)
231
232 (defcustom nnmairix-mairix-output-buffer "*mairix output*"
233   "Buffer used for mairix output."
234   :version "23.1"
235   :type 'string
236   :group 'nnmairix)
237
238 (defcustom nnmairix-customize-query-buffer "*mairix query*"
239   "Name of the buffer for customizing Mairix queries."
240   :version "23.1"
241   :type 'string
242   :group 'nnmairix)
243
244 (defcustom nnmairix-mairix-update-options '("-F" "-Q")
245   "Options when calling mairix for updating the database.
246 The default is '-F' and '-Q' for making updates faster.  You
247 should call mairix without these options from time to
248 time (e.g. via cron job)."
249   :version "23.1"
250   :type '(repeat string)
251   :group 'nnmairix)
252
253 (defcustom nnmairix-mairix-search-options '("-Q")
254   "Options when calling mairix for searching.
255 The default is '-Q' for making searching faster."
256   :version "23.1"
257   :type '(repeat string)
258   :group 'nnmairix)
259
260 (defcustom nnmairix-mairix-synchronous-update nil
261   "Set this to t if you want Emacs to wait for mairix updating the database."
262   :version "23.1"
263   :type 'boolean
264   :group 'nnmairix)
265
266 (defcustom nnmairix-rename-files-for-nnml t
267   "Rename nnml mail files so that they are consecutively numbered.
268 When using nnml as back end, mairix might produce holes in the
269 article numbers which will produce wrong article counts by
270 Gnus.  This option controls whether nnmairix should rename the
271 files consecutively."
272   :version "23.1"
273   :type 'boolean
274   :group 'nnmairix)
275
276 (defcustom nnmairix-widget-fields-list
277   '(("from" "f" "From") ("to" "t" "To") ("cc" "c" "Cc")
278     ("subject" "s" "Subject")  ("to" "tc" "To or Cc")
279     ("from" "a" "Address") (nil "b" "Body") (nil "n" "Attachment")
280     ("Message-ID" "m" "Message ID") (nil "s" "Size") (nil "d" "Date"))
281   "Fields that should be editable during interactive query customization.
282
283 Header, corresponding mairix command and description for editable
284 fields in interactive query customization.  The header specifies
285 which header contents should be inserted into the editable field
286 when creating a Mairix query based on the current message (can be
287 nil for disabling this)."
288   :version "23.1"
289   :type '(repeat (list
290                   (choice :tag "Field"
291                           (const :tag "none" nil)
292                           (const :tag "From" "from")
293                           (const :tag "To" "to")
294                           (const :tag "Cc" "cc")
295                           (const :tag "Subject" "subject")
296                           (const :tag "Message ID" "Message-ID"))
297                   (string :tag "Command")
298                   (string :tag "Description")))
299   :group 'nnmairix)
300
301 (defcustom nnmairix-widget-select-window-function
302   (lambda () (select-window (get-largest-window)))
303   "Function for selecting the window for customizing