(message-confirm-send): Add appropriate version.
[gnus] / lisp / nnmairix.el
1 ;;; nnmairix.el --- Mairix back end for Gnus, the Emacs newsreader
2
3 ;; Copyright (C) 2007, 2008  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 BETA SOFTWARE! This back end should not mess up or
27 ;; even delete your mails, but having a backup is always a good idea.
28
29 ;; This is a back end for using the mairix search engine with
30 ;; Gnus.  Mairix is a tool for searching words in locally stored
31 ;; mail.  Mairix is very fast which allows using it efficiently for
32 ;; "smart folders", e.g. folders which are associated with search
33 ;; queries.  Of course, you can also use this back end just for
34 ;; calling mairix with some search query.
35 ;;
36 ;; Mairix is written by Richard Curnow.  More information can be found at
37 ;; http://www.rpcurnow.force9.co.uk/mairix/
38 ;;
39 ;; For details about setting up mairix&Gnus&nnmairix.el, look at the
40 ;; emacswiki:
41 ;;
42 ;; http://www.emacswiki.org/cgi-bin/wiki/GnusMairix
43 ;;
44 ;; The newest version of nnmairix.el can be found at
45 ;;
46 ;; http://www.emacswiki.org/cgi-bin/emacs/nnmairix.el
47
48 ;; For impatient people, here's the setup in a nutshell:
49 ;;
50 ;; This back end requires an installed mairix binary which is
51 ;; configured to index your mail folder.  You don't have to specify a
52 ;; search folder (but it does no harm, either).  Visit the man page of
53 ;; mairix and mairixrc for details.
54 ;;
55 ;; Put nnmairix.el into your search path and "(require 'nnmarix)" into
56 ;; your .gnus.  Then call nnmairix-create-default-group (or 'G b
57 ;; c'). This function will ask for all necessary information to create
58 ;; a mairix server in Gnus with the default search folder.  This
59 ;; default search folder will be used for all temporary searches: call
60 ;; nnmairix-search ('G b s') and enter a mairix query (like
61 ;; f:test@example.com). To create a mairix group for one specific
62 ;; search query, use 'G b g'.  See the emacswiki or the source for more
63 ;; information.
64
65 ;; Commentary on the code: nnmairix sits between Gnus and the "real"
66 ;; back end which handles the mail (currently nnml, nnimap and
67 ;; nnmaildir were tested). I know this is all a bit hacky, but so far
68 ;; it works for me.  This is the first back end I've written for Gnus,
69 ;; so I'd appreciate any comments, suggestions, bug reports (and, of
70 ;; course, patches) for improving nnmairix.
71
72 ;; nnmairix does not use an active file, since I wanted to contain the
73 ;; back end "inside Gnus" as much as possible without the need of an
74 ;; external file.  It stores the query/folder information in the group
75 ;; parameters instead.  This also implies that once you kill a mairix
76 ;; group, it's gone for good.  I don't think that this is really
77 ;; problematic, since I don't see the need in unsubscribing and
78 ;; re-subscribing search groups
79
80 ;; Every mairix server is "responsible" for one mairix installation,
81 ;; i.e. you can have several mairix servers for different mairix
82 ;; configurations.  Not that I think anyone will actually do this, but
83 ;; I thought it would be a "nice to have feature"...
84
85 ;; KNOWN BUGS:
86 ;; * Mairix does only support us-ascii characters.
87
88 ;; TODO/MISSING FEATURES:
89 ;; * Support of more back ends (nnmh, nnfolder, nnmbox...)?
90 ;; * Maybe use an active file instead of group parameters?
91 ;; * Maybe use "-a" when updating groups which are not newly created?
92
93 ;;; Changelog:
94 ;; 05/30/2008 - version 0.6
95 ;;
96 ;;    * It is now possible to propagate marks from the nnmairix groups
97 ;;      to the original messages (and for maildir also vice versa). See
98 ;;      the docs for details on this feature - it's pretty delicate
99 ;;      and currently needs a patched mairix binary to work smoothly.
100 ;;
101 ;;    * Keep messages in nnmairix groups always read/unread
102 ;;      (bound to 'G b r').
103 ;;
104 ;;    * Recreate back end folder for nnmairix groups in case you
105 ;;      somehow get wrong article counts (bound to 'G b d').
106 ;;
107 ;;    * New group parameter 'allow-fast'. Toggling of parameter bound
108 ;;      to 'G b a'. The default is nil, meaning that the group will
109 ;;      always be updated with a mairix search, even when only entered.
110 ;;
111 ;;    * More/Better use of the registry (if available). Can now also
112 ;;      deal with duplicate messages in different groups.
113 ;;
114 ;; 02/06/2008 - version 0.5
115 ;;
116 ;;    * New function: nnmairix-goto-original-article. Uses the
117 ;;      registry or the mail file path for determining original group.
118 ;;
119 ;;    * Deal with empty Xref header
120 ;;
121 ;;    * Changed summary mode keybindings since the old ones were
122 ;;      already taken
123 ;;
124 ;;   (Thanks to Tassilo Horn and Ted Zlatanov for their help)
125 ;;
126 ;; 01/07/2008 - version 0.4
127 ;;
128 ;;    * New/fixed doc strings and code cleanup.
129 ;;
130 ;; 11/18/2007 - version 0.3
131 ;;
132 ;;    * Fixed bugs when dealing with nnml and native servers
133 ;;
134 ;;    * Make variables customizable
135 ;;
136 ;; 10/10/2007 - version 0.2
137 ;;
138 ;;    * Use nnml-directory/directory server variables for nnml and
139 ;;    nnmaildir back ends as path for search folders. This way it
140 ;;    becomes independent of 'base' setting in .mairixirc (but not for
141 ;;    nnimap).
142 ;;
143 ;;    * As a result: Changed nnmairix-backend-to-server so that user
144 ;;    is asked when more than one nnmairix server exists and we do not
145 ;;    know which one is responsible for current back end.
146 ;;
147 ;;    * Rename files when using nnml back ends so that there are no
148 ;;    holes in article numbers. This should fix all problems regarding
149 ;;    wrong article counts with nnml.
150 ;;
151 ;;    * More commands for creating queries (using widgets or the
152 ;;    minibuffer).
153 ;;
154 ;;    * Fixed bug in nnmairix-create-search-group-from-message
155 ;;
156 ;;    * Changed copyright to FSF
157 ;;
158 ;;      (Thanks to Georg C. F. Greve and Bastien for suggestions and
159 ;;      ideas!)
160 ;;
161 ;; 10/03/2007 - version 0.1 - first release
162
163
164 ;;; Code:
165
166 (eval-when-compile (require 'cl))       ;For (pop (cdr ogroup)).
167
168 (require 'nnoo)
169 (require 'gnus-group)
170 (require 'gnus-sum)
171 (require 'message)
172 (require 'nnml)
173 (require 'widget)
174
175 (nnoo-declare nnmairix)
176
177 ;;; === Keymaps
178
179 (eval-when-compile
180   (when (featurep 'xemacs)
181     ;; The `kbd' macro requires that the `read-kbd-macro' macro is available.
182     (require 'edmacro)))
183
184 ;; Group mode
185 (defun nnmairix-group-mode-hook ()
186   "Nnmairix group mode keymap."
187   (define-key gnus-group-mode-map
188     (kbd "G b") (make-sparse-keymap))
189   (define-key gnus-group-mode-map
190     (kbd "G b g") 'nnmairix-create-search-group)
191   (define-key gnus-group-mode-map
192     (kbd "G b c") 'nnmairix-create-server-and-default-group)
193   (define-key gnus-group-mode-map
194     (kbd "G b q") 'nnmairix-group-change-query-this-group)
195   (define-key gnus-group-mode-map
196     (kbd "G b t") 'nnmairix-group-toggle-threads-this-group)
197   (define-key gnus-group-mode-map
198     (kbd "G b u") 'nnmairix-update-database)
199   (define-key gnus-group-mode-map
200     (kbd "G b s") 'nnmairix-search)
201   (define-key gnus-group-mode-map
202     (kbd "G b i") 'nnmairix-search-interactive)
203   (define-key gnus-group-mode-map
204     (kbd "G b m") 'nnmairix-widget-search)
205   (define-key gnus-group-mode-map
206     (kbd "G b p") 'nnmairix-group-toggle-propmarks-this-group)
207   (define-key gnus-group-mode-map
208     (kbd "G b r") 'nnmairix-group-toggle-readmarks-this-group)
209   (define-key gnus-group-mode-map
210     (kbd "G b d") 'nnmairix-group-delete-recreate-this-group)
211   (define-key gnus-group-mode-map
212     (kbd "G b a") 'nnmairix-group-toggle-allowfast-this-group)
213   (define-key gnus-group-mode-map
214     (kbd "G b o") 'nnmairix-propagate-marks))
215
216 ;; Summary mode
217 (defun nnmairix-summary-mode-hook ()
218   "Nnmairix summary mode keymap."
219   (define-key gnus-summary-mode-map
220     (kbd "$ t") 'nnmairix-search-thread-this-article)
221   (define-key gnus-summary-mode-map
222     (kbd "$ f") 'nnmairix-search-from-this-article)
223   (define-key gnus-summary-mode-map
224     (kbd "$ m") 'nnmairix-widget-search-from-this-article)
225   (define-key gnus-summary-mode-map
226     (kbd "$ g") 'nnmairix-create-search-group-from-message)
227   (define-key gnus-summary-mode-map
228     (kbd "$ o") 'nnmairix-goto-original-article)
229   (define-key gnus-summary-mode-map
230     (kbd "$ u") 'nnmairix-remove-tick-mark-original-article))
231
232 (add-hook 'gnus-group-mode-hook 'nnmairix-group-mode-hook)
233 (add-hook 'gnus-summary-mode-hook 'nnmairix-summary-mode-hook)
234
235 ;; ;;;###autoload
236 ;; (defun nnmairix-initalize (&optional force)
237 ;;   (interactive "P")
238 ;;   (if (not (or (file-readable-p "~/.mairixrc")
239 ;;             force))
240 ;;       (message "No file `~/.mairixrc', skipping nnmairix setup")
241 ;;     (add-hook 'gnus-group-mode-hook 'nnmairix-group-mode-hook)
242 ;;     (add-hook 'gnus-summary-mode-hook 'nnmairix-summary-mode-hook)))
243
244 ;; Customizable stuff
245
246 (defgroup nnmairix nil
247   "Back end for the Mairix mail search engine."
248   :group 'gnus)
249
250 (defcustom nnmairix-group-prefix "zz_mairix"
251   "Prefix for mairix search groups on back end server.
252 nnmairix will create these groups automatically on the back end
253 server for each nnmairix search group.  The name on the back end
254 server will be this prefix plus a random number.  You can delete
255 unused nnmairix groups on the back end using
256 `nnmairix-purge-old-groups'."
257   :version "23.1"
258   :type 'string
259   :group 'nnmairix)
260
261 (defcustom nnmairix-mairix-output-buffer "*mairix output*"
262   "Buffer used for mairix output."
263   :version "23.1"
264   :type 'string
265   :group 'nnmairix)
266
267 (defcustom nnmairix-customize-query-buffer "*mairix query*"
268   "Name of the buffer for customizing Mairix queries."
269   :version "23.1"
270   :type 'string
271   :group 'nnmairix)
272
273 (defcustom nnmairix-mairix-update-options '("-F" "-Q")
274   "Options when calling mairix for updating the database.
275 The default is '-F' and '-Q' for making updates faster.  You
276 should call mairix without these options from time to
277 time (e.g. via cron job)."
278   :version "23.1"
279   :type '(repeat string)
280   :group 'nnmairix)
281
282 (defcustom nnmairix-mairix-search-options '("-Q")
283   "Options when calling mairix for searching.
284 The default is '-Q' for making searching faster."
285   :version "23.1"
286   :type '(repeat string)
287   :group 'nnmairix)
288
289 (defcustom nnmairix-mairix-synchronous-update nil
290   "Set this to t if you want Emacs to wait for mairix updating the database."
291   :version "23.1"
292   :type 'boolean
293   :group 'nnmairix)
294
295 (defcustom nnmairix-rename-files-for-nnml t
296   "Rename nnml mail files so that they are consecutively numbered.
297 When using nnml as back end, mairix might produce holes in the
298 article numbers which will produce wrong article counts by
299 Gnus.  This option controls whether nnmairix should rename the
300 files consecutively."
301   :version "23.1"
302   :type 'boolean
303   :group 'nnmairix)
304
305 (defcustom nnmairix-widget-fields-list
306   '(("from" "f" "From") ("to" "t" "To") ("cc" "c" "Cc")
307     ("subject" "s" "Subject")  ("to" "tc" "To or Cc")
308     ("from" "a" "Address") (nil "b" "Body") (nil "n" "Attachment")
309     ("Message-ID" "m" "Message ID") (nil "s" "Size") (nil "d" "Date"))
310   "Fields that should be editable during interactive query customization.
311
312 Header, corresponding mairix command and description for editable
313 fields in interactive query customization.  The header specifies
314 which header contents should be inserted into the editable field
315 when creating a Mairix query based on the current message (can be
316 nil for disabling this)."
317   :version "23.1"
318   :type '(repeat (list
319                   (choice :tag "Field"
320                           (const :tag "none" nil)
321                           (const :tag "From" "from")
322                           (const :tag "To" "to")
323                           (const :tag "Cc" "cc")
324                           (const :tag "Subject" "subject")
325                           (const :tag "Message ID" "Message-ID"))
326                   (string :tag "Command")
327                   (string :tag "Description")))
328   :group 'nnmairix)
329
330 (defcustom nnmairix-widget-select-window-function
331   (lambda () (select-window (get-largest-window)))
332   "Function for selecting the window for customizing the mairix query.
333 The default chooses the largest window in the current frame."
334   :version "23.1"
335   :type 'function
336   :group 'nnmairix)
337
338 (defcustom nnmairix-propagate-marks-upon-close t
339   "Flag if marks should be propagated upon closing a group.
340 The default of this variable is t. If set to 'ask, the
341 user will be asked if the flags should be propagated when the
342 group is closed.  If set to nil, the user will have to manually
343 call 'nnmairix-propagate-marks'."
344   :version "23.1"
345   :type '(choice (const :tag "always" t)
346                  (const :tag "ask" 'ask)
347                  (const :tag "never" nil))
348   :group 'nnmairix)
349
350 (defcustom nnmairix-propagate-marks-to-nnmairix-groups nil
351   "Flag if marks from original articles should be seen in nnmairix groups.
352 The default is nil since it will only work if the articles are in
353 maildir format and NOT managed by the nnmaildir back end but
354 e.g. an IMAP server (which stores the marks in the maildir file
355 name).  You may safely set this to t for testing - the worst that
356 can happen are wrong marks in nnmairix groups."
357   :version "23.1"
358   :type 'boolean
359   :group 'nnmairix)
360
361 (defcustom nnmairix-only-use-registry nil
362   "Use only the registry for determining original group(s).
363 If set to t, nnmairix will only use the registry for determining
364 the original group(s) of an article (which is also necessary for
365 propapagting marks).  If set to nil, it will also try to determine
366 the group from an additional mairix search which might be slow
367 when propagating lots of marks."
368   :version "23.1"
369   :type 'boolean
370   :group 'nnmairix)
371
372 (defcustom nnmairix-allowfast-default nil
373   "Whether fast entering should be the default for nnmairix groups.
374 You may set this to t to make entering the group faster, but note that
375 this might lead to problems, especially when used with marks propagation."
376   :version "23.1"
377   :type 'boolean
378   :group 'nnmairix)
379
380 ;; ==== Other variables
381
382 (defvar nnmairix-widget-other
383   '(threads flags)
384   "Other editable mairix commands when using customization widgets.
385 Currently there are 'threads and 'flags.")
386
387 (defvar nnmairix-interactive-query-parameters
388   '((?f "from" "f" "From") (?t "to" "t" "To") (?c "to" "tc" "To or Cc")
389     (?a "from" "a" "Address") (?s "subject" "s" "Subject") (?b nil "b" "Body")
390     (?d nil "d" "Date") (?n nil "n" "Attachment"))
391   "Things that should be editable during interactive query generation.
392 Every list element consists of the following entries: Keystroke,
393 message field (if any), mairix command and description.")
394
395 (defvar nnmairix-delete-and-create-on-change '(nnimap nnmaildir nnml)
396   "Controls on which back ends groups should be deleted and re-created.
397 This variable is a list of back ends where the search group
398 should be completely deleted and re-created when the query or
399 thread parameter changes.  The default is to this for all
400 currently supported back ends.  It usually also corrects the
401 problem of \"holes\" in the article numbers which often lead to a
402 wrong count of total articles shown by Gnus.")
403
404 ;;; === Server variables
405
406 (defvoo nnmairix-backend  nil
407   "Back end where mairix stores its searches.")
408
409 (defvoo nnmairix-backend-server nil
410   "Name of the server where mairix stores its searches.")
411
412 (defvoo nnmairix-mairix-command "mairix"
413   "Command to call mairix for this nnmairix server.")
414
415 (defvoo nnmairix-hidden-folders nil
416   "Set this to t if the back end server uses hidden directories for
417 its maildir mail folders (e.g. the Dovecot IMAP server or mutt).")
418
419 (defvoo nnmairix-default-group nil
420   "Default search group. This is the group which is used for all
421 temporary searches, e.g. nnmairix-search.")
422
423 ;;; === Internal variables
424