Commit | Line | Data |
---|---|---|
3d0f8a67 MB |
1 | ;;; nnmairix.el --- Mairix back end for Gnus, the Emacs newsreader |
2 | ||
114f9c96 | 3 | ;; Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc. |
3d0f8a67 MB |
4 | |
5 | ;; Author: David Engster <dengste@eml.cc> | |
6 | ;; Keywords: mail searching | |
2b968687 | 7 | ;; Version: 0.6 |
3d0f8a67 | 8 | |
da13b6cc GM |
9 | ;; This file is part of GNU Emacs. |
10 | ||
5e809f55 | 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
3d0f8a67 | 12 | ;; it under the terms of the GNU General Public License as published by |
5e809f55 GM |
13 | ;; the Free Software Foundation, either version 3 of the License, or |
14 | ;; (at your option) any later version. | |
3d0f8a67 | 15 | |
da13b6cc | 16 | ;; GNU Emacs is distributed in the hope that it will be useful, |
3d0f8a67 MB |
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 | |
5e809f55 | 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
3d0f8a67 MB |
23 | |
24 | ;;; Commentary: | |
25 | ||
3d0f8a67 MB |
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/ | |
3d0f8a67 MB |
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: | |
3d0f8a67 MB |
57 | ;; * Mairix does only support us-ascii characters. |
58 | ||
59 | ;; TODO/MISSING FEATURES: | |
3d0f8a67 MB |
60 | ;; * Support of more back ends (nnmh, nnfolder, nnmbox...)? |
61 | ;; * Maybe use an active file instead of group parameters? | |
2b968687 | 62 | ;; * Maybe use "-a" when updating groups which are not newly created? |
da13b6cc | 63 | |
3d0f8a67 | 64 | ;;; Changelog: |
2b968687 MB |
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. | |
3d0f8a67 MB |
84 | ;; |
85 | ;; 02/06/2008 - version 0.5 | |
da13b6cc | 86 | ;; |
3d0f8a67 MB |
87 | ;; * New function: nnmairix-goto-original-article. Uses the |
88 | ;; registry or the mail file path for determining original group. | |
da13b6cc | 89 | ;; |
3d0f8a67 MB |
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 | ;; | |
2b968687 | 101 | ;; 11/18/2007 - version 0.3 |
3d0f8a67 MB |
102 | ;; |
103 | ;; * Fixed bugs when dealing with nnml and native servers | |
da13b6cc | 104 | ;; |
3d0f8a67 MB |
105 | ;; * Make variables customizable |
106 | ;; | |
107 | ;; 10/10/2007 - version 0.2 | |
108 | ;; | |
109 | ;; * Use nnml-directory/directory server variables for nnml and | |
2b968687 | 110 | ;; nnmaildir back ends as path for search folders. This way it |
3d0f8a67 MB |
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 | |
2b968687 | 116 | ;; know which one is responsible for current back end. |
3d0f8a67 | 117 | ;; |
2b968687 | 118 | ;; * Rename files when using nnml back ends so that there are no |
3d0f8a67 MB |
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 | ||
32bed934 SM |
137 | (eval-when-compile (require 'cl)) ;For (pop (cdr ogroup)). |
138 | ||
3d0f8a67 MB |
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 | ||
656b4fa1 MB |
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 | ||
3d0f8a67 MB |
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 | |
2b968687 MB |
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)) | |
3d0f8a67 MB |
186 | |
187 | ;; Summary mode | |
188 | (defun nnmairix-summary-mode-hook () | |
189 | "Nnmairix summary mode keymap." | |
190 | (define-key gnus-summary-mode-map | |
191 | (kbd "$ t") 'nnmairix-search-thread-this-article) | |
192 | (define-key gnus-summary-mode-map | |
193 | (kbd "$ f") 'nnmairix-search-from-this-article) | |
194 | (define-key gnus-summary-mode-map | |
195 | (kbd "$ m") 'nnmairix-widget-search-from-this-article) | |
196 | (define-key gnus-summary-mode-map | |
197 | (kbd "$ g") 'nnmairix-create-search-group-from-message) | |
198 | (define-key gnus-summary-mode-map | |
2b968687 MB |
199 | (kbd "$ o") 'nnmairix-goto-original-article) |
200 | (define-key gnus-summary-mode-map | |
201 | (kbd "$ u") 'nnmairix-remove-tick-mark-original-article)) | |
3d0f8a67 MB |
202 | |
203 | (add-hook 'gnus-group-mode-hook 'nnmairix-group-mode-hook) | |
204 | (add-hook 'gnus-summary-mode-hook 'nnmairix-summary-mode-hook) | |
205 | ||
2b968687 MB |
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))) | |
3d0f8a67 MB |
214 | |
215 | ;; Customizable stuff | |
216 | ||
217 | (defgroup nnmairix nil | |
2b968687 | 218 | "Back end for the Mairix mail search engine." |
3d0f8a67 MB |
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'." | |
da13b6cc | 228 | :version "23.1" |
3d0f8a67 MB |
229 | :type 'string |
230 | :group 'nnmairix) | |
231 | ||
232 | (defcustom nnmairix-mairix-output-buffer "*mairix output*" | |
233 | "Buffer used for mairix output." | |
da13b6cc | 234 | :version "23.1" |
3d0f8a67 MB |
235 | :type 'string |
236 | :group 'nnmairix) | |
237 | ||
238 | (defcustom nnmairix-customize-query-buffer "*mairix query*" | |
239 | "Name of the buffer for customizing Mairix queries." | |
da13b6cc | 240 | :version "23.1" |
3d0f8a67 MB |
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)." | |
da13b6cc | 249 | :version "23.1" |
3d0f8a67 MB |
250 | :type '(repeat string) |
251 | :group 'nnmairix) | |
252 | ||
2b968687 MB |
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 | ||
3d0f8a67 MB |
260 | (defcustom nnmairix-mairix-synchronous-update nil |
261 | "Set this to t if you want Emacs to wait for mairix updating the database." | |
da13b6cc | 262 | :version "23.1" |
3d0f8a67 MB |
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. | |
2b968687 | 268 | When using nnml as back end, mairix might produce holes in the |
3d0f8a67 MB |
269 | article numbers which will produce wrong article counts by |
270 | Gnus. This option controls whether nnmairix should rename the | |
271 | files consecutively." | |
da13b6cc | 272 | :version "23.1" |
3d0f8a67 MB |
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)." | |
da13b6cc | 288 | :version "23.1" |
3d0f8a67 MB |
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 the mairix query. | |
304 | The default chooses the largest window in the current frame." | |
da13b6cc | 305 | :version "23.1" |
3d0f8a67 MB |
306 | :type 'function |
307 | :group 'nnmairix) | |
308 | ||
2b968687 MB |
309 | (defcustom nnmairix-propagate-marks-upon-close t |
310 | "Flag if marks should be propagated upon closing a group. | |
311 | The default of this variable is t. If set to 'ask, the | |
312 | user will be asked if the flags should be propagated when the | |
313 | group is closed. If set to nil, the user will have to manually | |
314 | call 'nnmairix-propagate-marks'." | |
315 | :version "23.1" | |
316 | :type '(choice (const :tag "always" t) | |
317 | (const :tag "ask" 'ask) | |
318 | (const :tag "never" nil)) | |
319 | :group 'nnmairix) | |
320 | ||
321 | (defcustom nnmairix-propagate-marks-to-nnmairix-groups nil | |
322 | "Flag if marks from original articles should be seen in nnmairix groups. | |
323 | The default is nil since it will only work if the articles are in | |
324 | maildir format and NOT managed by the nnmaildir back end but | |
325 | e.g. an IMAP server (which stores the marks in the maildir file | |
326 | name). You may safely set this to t for testing - the worst that | |
327 | can happen are wrong marks in nnmairix groups." | |
328 | :version "23.1" | |
329 | :type 'boolean | |
330 | :group 'nnmairix) | |
331 | ||
332 | (defcustom nnmairix-only-use-registry nil | |
333 | "Use only the registry for determining original group(s). | |
334 | If set to t, nnmairix will only use the registry for determining | |
335 | the original group(s) of an article (which is also necessary for | |
336 | propapagting marks). If set to nil, it will also try to determine | |
337 | the group from an additional mairix search which might be slow | |
338 | when propagating lots of marks." | |
339 | :version "23.1" | |
340 | :type 'boolean | |
341 | :group 'nnmairix) | |
342 | ||
343 | (defcustom nnmairix-allowfast-default nil | |
344 | "Whether fast entering should be the default for nnmairix groups. | |
345 | You may set this to t to make entering the group faster, but note that | |
346 | this might lead to problems, especially when used with marks propagation." | |
347 | :version "23.1" | |
348 | :type 'boolean | |
349 | :group 'nnmairix) | |
350 | ||
3d0f8a67 MB |
351 | ;; ==== Other variables |
352 | ||
353 | (defvar nnmairix-widget-other | |
354 | '(threads flags) | |
355 | "Other editable mairix commands when using customization widgets. | |
356 | Currently there are 'threads and 'flags.") | |
357 | ||
358 | (defvar nnmairix-interactive-query-parameters | |
359 | '((?f "from" "f" "From") (?t "to" "t" "To") (?c "to" "tc" "To or Cc") | |
360 | (?a "from" "a" "Address") (?s "subject" "s" "Subject") (?b nil "b" "Body") | |
361 | (?d nil "d" "Date") (?n nil "n" "Attachment")) | |
362 | "Things that should be editable during interactive query generation. | |
363 | Every list element consists of the following entries: Keystroke, | |
364 | message field (if any), mairix command and description.") | |
365 | ||
366 | (defvar nnmairix-delete-and-create-on-change '(nnimap nnmaildir nnml) | |
2b968687 MB |
367 | "Controls on which back ends groups should be deleted and re-created. |
368 | This variable is a list of back ends where the search group | |
369 | should be completely deleted and re-created when the query or | |
370 | thread parameter changes. The default is to this for all | |
371 | currently supported back ends. It usually also corrects the | |
372 | problem of \"holes\" in the article numbers which often lead to a | |
3d0f8a67 MB |
373 | wrong count of total articles shown by Gnus.") |
374 | ||
375 | ;;; === Server variables | |
376 | ||
377 | (defvoo nnmairix-backend nil | |
2b968687 | 378 | "Back end where mairix stores its searches.") |
3d0f8a67 MB |
379 | |
380 | (defvoo nnmairix-backend-server nil | |
381 | "Name of the server where mairix stores its searches.") | |
382 | ||
383 | (defvoo nnmairix-mairix-command "mairix" | |
384 | "Command to call mairix for this nnmairix server.") | |
385 | ||
386 | (defvoo nnmairix-hidden-folders nil | |
387 | "Set this to t if the back end server uses hidden directories for | |
388 | its maildir mail folders (e.g. the Dovecot IMAP server or mutt).") | |
389 | ||
390 | (defvoo nnmairix-default-group nil | |
391 | "Default search group. This is the group which is used for all | |
392 | temporary searches, e.g. nnmairix-search.") | |
393 | ||
394 | ;;; === Internal variables | |
395 | ||
e80c7164 GM |
396 | (defconst nnmairix-group-regexp |
397 | (format "%s-\\(.*\\)-[0-9]+" nnmairix-group-prefix) | |
398 | "Regexp for mairix groups on back end.") | |
3d0f8a67 | 399 | |
e80c7164 | 400 | (defconst nnmairix-valid-backends '(nnimap nnml nnmaildir) |
2b968687 MB |
401 | "Back ends supported by nnmairix. |
402 | Other back ends might or might not work.") | |
3d0f8a67 | 403 | |
e80c7164 GM |
404 | (defvar nnmairix-last-server nil |
405 | "Last chosen server.") | |
3d0f8a67 | 406 | |
e80c7164 GM |
407 | (defvar nnmairix-current-server nil |
408 | "Current server.") | |
3d0f8a67 | 409 | |
2b968687 MB |
410 | (defvar nnmairix-marks-cache nil |
411 | "Cache for marks which should be set upon closing current group.") | |
412 | ||
413 | (defvar nnmairix-version-output nil | |
414 | "Version string of mairix binary.") | |
415 | ||
416 | ;;; === Gnus back end functions | |
e80c7164 | 417 | |
3d0f8a67 MB |
418 | (nnoo-define-basics nnmairix) |
419 | ||
420 | (gnus-declare-backend "nnmairix" 'mail 'address) | |
421 | ||
422 | (deffoo nnmairix-open-server (server &optional definitions) | |
423 | ;; just set server variables | |
424 | (setq nnmairix-current-server server) | |
425 | (nnoo-change-server 'nnmairix server definitions)) | |
426 | ||
427 | (deffoo nnmairix-request-group (group &optional server fast) | |
428 | ;; Call mairix and request group on back end server | |
429 | (when server (nnmairix-open-server server)) | |
430 | (let* ((qualgroup (if server | |
431 | (gnus-group-prefixed-name group (list 'nnmairix server)) | |
432 | group)) | |
2b968687 MB |
433 | (folder (gnus-group-get-parameter qualgroup 'folder)) |
434 | (allowfast (gnus-group-get-parameter qualgroup 'allow-fast)) | |
3d0f8a67 | 435 | (query (gnus-group-get-parameter qualgroup 'query t)) |
2b968687 MB |
436 | (threads (gnus-group-get-parameter qualgroup 'threads)) |
437 | (backendmethod (gnus-server-to-method | |
438 | (format "%s:%s" (symbol-name nnmairix-backend) | |
439 | nnmairix-backend-server))) | |
440 | rval mfolder folderpath args) | |
3d0f8a67 MB |
441 | (cond |
442 | ((not folder) | |
443 | ;; No folder parameter -> error | |
444 | (nnheader-report 'nnmairix "Check folder parameter for group %s" group) | |
445 | nil) | |
446 | ((not query) | |
447 | ;; No query -> return empty group | |
448 | (save-excursion | |
449 | (set-buffer nntp-server-buffer) | |
450 | (erase-buffer) | |
451 | (insert (concat "211 0 1 0 " group)) | |
452 | t)) | |
453 | (t | |
454 | ;; For maildir++ folders: create a hidden directory (prepend dot) | |
455 | (setq mfolder (if (and nnmairix-hidden-folders | |
456 | (not (string-match "^\\." folder))) | |
457 | (concat "." folder) | |
458 | folder)) | |
459 | ;; For nnml and nnmaildir, precede mfolder with directory where mail | |
460 | ;; is actually stored so that it's independent of 'base' setting | |
461 | ;; in .mairixrc. | |
462 | (when (eq nnmairix-backend 'nnml) | |
463 | (setq folderpath (cadr (assoc 'nnml-directory backendmethod))) | |
464 | ;; if nnml-directory is not explicitly set, use global value | |
465 | (when (not folderpath) | |
466 | (setq folderpath nnml-directory))) | |
467 | (when (eq nnmairix-backend 'nnmaildir) | |
468 | (setq folderpath | |
469 | (cadr (assoc 'directory backendmethod)))) | |
470 | (when folderpath | |
471 | (setq mfolder | |
472 | (concat | |
473 | (file-name-as-directory | |
474 | (expand-file-name | |
475 | folderpath)) | |
476 | mfolder))) | |
477 | ;; If (not fast), call Mairix binary | |
2b968687 | 478 | ;; recreate underlying folder on the back end |
3d0f8a67 | 479 | (setq rval |
2b968687 MB |
480 | (if (and fast allowfast) |
481 | 0 | |
3d0f8a67 MB |
482 | (nnmairix-call-mairix-binary |
483 | (split-string nnmairix-mairix-command) | |
484 | mfolder query threads))) | |
485 | ;; Check return value | |
486 | (cond | |
487 | ((zerop rval) ; call was succesful | |
488 | (nnmairix-call-backend | |
489 | "open-server" nnmairix-backend-server) | |
490 | ;; If we're dealing with nnml, rename files | |
491 | ;; consecutively and make new active file for this | |
492 | ;; group | |
493 | (when (eq nnmairix-backend 'nnml) | |
494 | (when nnmairix-rename-files-for-nnml | |
495 | (nnmairix-rename-files-consecutively mfolder)) | |
0cf681b6 | 496 | (nnml-generate-nov-databases-directory mfolder nil t)) |
3d0f8a67 MB |
497 | (nnmairix-call-backend |
498 | "request-scan" folder nnmairix-backend-server) | |
2b968687 | 499 | (if (and fast allowfast) |
3d0f8a67 | 500 | t |
14e8de0c MB |
501 | (nnmairix-request-group-with-article-number-correction |
502 | folder qualgroup))) | |
3d0f8a67 MB |
503 | ((and (= rval 1) |
504 | (save-excursion (set-buffer nnmairix-mairix-output-buffer) | |
505 | (goto-char (point-min)) | |
506 | (looking-at "^Matched 0 messages"))) | |
507 | ;; No messages found -> return empty group | |
508 | (nnheader-message 5 "Mairix: No matches found.") | |
509 | (set-buffer nntp-server-buffer) | |
510 | (erase-buffer) | |
511 | (insert (concat "211 0 1 0 " group)) | |
512 | t) | |
513 | ;; Everything else is an error | |
514 | (t | |
515 | (nnheader-report | |
516 | 'nnmairix "Error running marix. See buffer %s for details" | |
517 | nnmairix-mairix-output-buffer) | |
518 | nil)))))) | |
519 | ||
520 | ||
521 | (deffoo nnmairix-request-create-group (group &optional server args) | |
522 | (let ((qualgroup (if server (gnus-group-prefixed-name group (list 'nnmairix server)) | |
523 | group)) | |
524 | (exist t) | |
525 | (count 0) | |
526 | groupname info) | |
527 | (when server (nnmairix-open-server server)) | |
528 | (gnus-group-add-parameter qualgroup '(query . nil)) | |
529 | (gnus-group-add-parameter qualgroup '(threads . nil)) | |
530 | (while exist | |
531 | (setq count (1+ count)) | |
532 | (setq groupname (format "%s-%s-%s" nnmairix-group-prefix group | |
533 | (number-to-string count))) | |
534 | (setq exist (nnmairix-call-backend | |
535 | "request-group" groupname nnmairix-backend-server))) | |
536 | (nnmairix-call-backend | |
537 | "request-create-group" groupname nnmairix-backend-server) | |
538 | (gnus-group-add-parameter qualgroup '(folder . nil)) | |
2b968687 MB |
539 | (when nnmairix-allowfast-default |
540 | (gnus-group-add-parameter qualgroup '(allow-fast . t))) | |
3d0f8a67 MB |
541 | (gnus-group-set-parameter qualgroup 'folder groupname)) |
542 | t) | |
543 | ||
544 | ||
545 | (deffoo nnmairix-retrieve-headers (articles group &optional server fetch-old) | |
546 | (when server (nnmairix-open-server server)) | |
547 | (let* ((folder (nnmairix-get-backend-folder group server)) | |
548 | (corr (nnmairix-get-numcorr group server)) | |
549 | (numcorr 0) | |
550 | rval) | |
551 | (when (and corr | |
552 | (not (zerop (cadr corr))) | |
553 | (numberp (car articles))) | |
554 | (setq numcorr (cadr corr)) | |
555 | (setq articles | |
556 | (mapcar | |
557 | (lambda (arg) (- arg numcorr)) | |
558 | articles))) | |
c9fc72fa | 559 | (setq rval |
0cf681b6 KY |
560 | (if (eq nnmairix-backend 'nnimap) |
561 | (let ((gnus-nov-is-evil t)) | |
562 | (nnmairix-call-backend | |
563 | "retrieve-headers" articles folder nnmairix-backend-server fetch-old)) | |
564 | (nnmairix-call-backend | |
565 | "retrieve-headers" articles folder nnmairix-backend-server fetch-old))) | |
3d0f8a67 MB |
566 | (when (eq rval 'nov) |
567 | (nnmairix-replace-group-and-numbers articles folder group numcorr) | |
568 | rval))) | |
569 | ||
570 | (deffoo nnmairix-request-article (article &optional group server to-buffer) | |
571 | (when server (nnmairix-open-server server)) | |
572 | (let ((folder (nnmairix-get-backend-folder group server)) | |
573 | (corr (nnmairix-get-numcorr group server))) | |
574 | (when (and | |
575 | (numberp article) | |
576 | corr | |
577 | (not (zerop (cadr corr)))) | |
578 | (setq article (- article (cadr corr)))) | |
579 | (nnmairix-call-backend | |
580 | "request-article" article folder nnmairix-backend-server to-buffer)) | |
581 | t) | |
582 | ||
3d0f8a67 MB |
583 | (deffoo nnmairix-request-list (&optional server) |
584 | (when server (nnmairix-open-server server)) | |
585 | (if (nnmairix-call-backend "request-list" nnmairix-backend-server) | |
586 | (let (cpoint cur qualgroup folder) | |
587 | (save-excursion | |
588 | (set-buffer nntp-server-buffer) | |
589 | (goto-char (point-min)) | |
590 | (setq cpoint (point)) | |
591 | (while (re-search-forward nnmairix-group-regexp (point-max) t) | |
592 | (setq cur (match-string 1) | |
593 | qualgroup (gnus-group-prefixed-name cur | |
594 | (list 'nnmairix server))) | |
595 | (if (and (gnus-group-entry qualgroup) | |
596 | (string= (match-string 0) | |
597 | (gnus-group-get-parameter qualgroup 'folder))) | |
598 | (progn | |
599 | (replace-match cur) | |
600 | (delete-region cpoint (point-at-bol)) | |
601 | (forward-line) | |
602 | (setq cpoint (point))) | |
603 | (forward-line))) | |
604 | (delete-region cpoint (point-max))) | |
605 | t) | |
606 | nil)) | |
607 | ||
2b968687 MB |
608 | ;; Silence byte-compiler. |
609 | (defvar gnus-registry-install) | |
610 | (autoload 'gnus-registry-fetch-group "gnus-registry") | |
611 | (autoload 'gnus-registry-fetch-groups "gnus-registry") | |
612 | (autoload 'gnus-registry-add-group "gnus-registry") | |
613 | ||
614 | (deffoo nnmairix-request-set-mark (group actions &optional server) | |
615 | (when server | |
616 | (nnmairix-open-server server)) | |
617 | (let* ((qualgroup (gnus-group-prefixed-name group (list 'nnmairix nnmairix-current-server))) | |
618 | (propmarks (gnus-group-get-parameter qualgroup 'propmarks)) | |
619 | (propto (gnus-group-get-parameter qualgroup 'propto t)) | |
620 | (corr (nnmairix-get-numcorr group server)) | |
621 | (folder (nnmairix-get-backend-folder group server))) | |
622 | (save-excursion | |
623 | (dolist (cur actions) | |
624 | (let ((type (nth 1 cur)) | |
625 | (cmdmarks (nth 2 cur)) | |
626 | (range (gnus-uncompress-range (nth 0 cur))) | |
627 | mid ogroup number method temp) | |
628 | (when (and corr | |
629 | (not (zerop (cadr corr)))) | |
630 | (setq range (mapcar (lambda (arg) | |
631 | (- arg (cadr corr))) | |
632 | range))) | |
633 | (when propmarks | |
634 | (nnheader-message 7 "nnmairix: Setting marks...") | |
635 | (dolist (article range) | |
636 | ;; get article (header) and extract message id | |
637 | ;; we try to determine as many original articles as possible | |
638 | (catch 'problem | |
639 | (nnmairix-call-backend "open-server" nnmairix-backend-server) | |
640 | (unless (gnus-request-head | |
641 | article | |
642 | (gnus-group-prefixed-name | |
643 | folder | |
644 | (list nnmairix-backend nnmairix-backend-server))) | |
645 | (nnheader-message | |
646 | 3 "Unable to set mark: couldn't fetch article header for article number %d" | |
647 | article) | |
648 | (throw 'problem nil)) | |
649 | (set-buffer nntp-server-buffer) | |
650 | (goto-char (point-min)) | |
651 | (let ((case-fold-search t)) | |
652 | (re-search-forward "^message-id:.*\\(<.+>\\)" nil t)) | |
653 | (setq mid (match-string 1)) | |
654 | (unless mid | |
655 | (nnheader-message | |
656 | 3 "Unable to set mark: article number %d has no message-id header" | |
657 | article) | |
658 | (throw 'problem nil)) | |
659 | ;; get original group. First try registry, then file path | |
660 | (setq ogroup | |
661 | (nnmairix-determine-original-group-from-registry mid)) | |
662 | (unless (or ogroup | |
663 | nnmairix-only-use-registry) | |
664 | (setq ogroup | |
665 | (nnmairix-determine-original-group-from-path | |
666 | mid nnmairix-current-server)) | |
667 | ;; if available and allowed, add this entry to the registry | |
668 | (when (and (boundp 'gnus-registry-install) | |
669 | gnus-registry-install) | |
670 | (dolist (cur ogroup) | |
671 | (unless (gnus-parameter-registry-ignore cur) | |
672 | (gnus-registry-add-group mid cur))))) | |
673 | (unless ogroup | |
674 | (nnheader-message | |
675 | 3 "Unable to set mark: couldn't find original group for %s" mid) | |
676 | (throw 'problem nil)) | |
677 | ;; store original groups with mid's. We cannot get | |
678 | ;; the article number immediately since this would | |
679 | ;; generate problems with maildir (articles might | |
680 | ;; get moved from /new to /cur and further marks | |
681 | ;; could then not be set) | |
682 | (dolist (cur ogroup) | |
683 | (setq temp (assoc cur | |
684 | nnmairix-marks-cache)) | |
685 | (if temp | |
686 | (nconc temp (list (list mid type cmdmarks))) | |
687 | (push (list cur (list mid type cmdmarks)) | |
688 | nnmairix-marks-cache))))) | |
689 | (nnheader-message 7 "nnmairix: Setting marks... done"))))))) | |
690 | ||
691 | (deffoo nnmairix-close-group (group &optional server) | |
692 | (when server | |
693 | (nnmairix-open-server server)) | |
694 | (let* ((qualgroup (gnus-group-prefixed-name group (list 'nnmairix nnmairix-current-server))) | |
695 | (propmarks (gnus-group-get-parameter qualgroup 'propmarks)) | |
696 | method) | |
697 | (when (and propmarks | |
698 | nnmairix-marks-cache) | |
699 | (when (or (eq nnmairix-propagate-marks-upon-close t) | |
700 | (and (eq nnmairix-propagate-marks-upon-close 'ask) | |
701 | (y-or-n-p "Propagate marks to original articles? "))) | |
702 | (save-excursion | |
703 | (set-buffer gnus-group-buffer) | |
704 | (nnmairix-propagate-marks) | |
705 | ;; update mairix group | |
706 | (gnus-group-jump-to-group qualgroup) | |
707 | (gnus-group-get-new-news-this-group)))))) | |
708 | ||
709 | (autoload 'nnimap-request-update-info-internal "nnimap") | |
710 | ||
711 | (deffoo nnmairix-request-update-info (group info &optional server) | |
712 | ;; propagate info from underlying IMAP folder to nnmairix group | |
713 | ;; This is currently experimental and must be explicitly activated | |
714 | ;; with nnmairix-propagate-marks-to-nnmairix-group | |
715 | (when server | |
716 | (nnmairix-open-server server)) | |
717 | (let* ((qualgroup (gnus-group-prefixed-name | |
718 | group | |
719 | (list 'nnmairix nnmairix-current-server))) | |
720 | (readmarks (gnus-group-get-parameter qualgroup 'readmarks)) | |
721 | (propmarks (gnus-group-get-parameter qualgroup 'propmarks)) | |
722 | (folder (nnmairix-get-backend-folder group server)) | |
723 | (corr (nnmairix-get-numcorr group server)) | |
724 | (docorr (and corr (not (zerop (cadr corr))))) | |
725 | (folderinfo `(,group 1 ((1 . 1)))) | |
726 | readrange marks) | |
727 | (when (and propmarks | |
728 | nnmairix-propagate-marks-to-nnmairix-groups) | |
729 | ;; these groups are not subscribed, so we have to ask the back end directly | |
730 | (if (eq nnmairix-backend 'nnimap) | |
731 | (nnimap-request-update-info-internal folder folderinfo nnmairix-backend-server) | |
732 | (nnmairix-call-backend "request-update-info" folder folderinfo nnmairix-backend-server)) | |
733 | ;; set range of read articles | |
734 | (gnus-info-set-read | |
735 | info | |
736 | (if docorr | |
737 | (nnmairix-map-range | |
738 | `(lambda (x) (+ x ,(cadr corr))) | |
739 | (gnus-info-read folderinfo)) | |
740 | (gnus-info-read folderinfo))) | |
741 | ;; set other marks | |
742 | (gnus-info-set-marks | |
743 | info | |
744 | (if docorr | |
745 | (mapcar (lambda (cur) | |
746 | (cons | |
747 | (car cur) | |
748 | (nnmairix-map-range | |
749 | `(lambda (x) (+ x ,(cadr corr))) | |
750 | (list (cadr cur))))) | |
751 | (gnus-info-marks folderinfo)) | |
752 | (gnus-info-marks folderinfo)))) | |
753 | (when (eq readmarks 'unread) | |
754 | (gnus-info-set-read info nil)) | |
755 | (when (eq readmarks 'read) | |
756 | (gnus-info-set-read info (gnus-active qualgroup)))) | |
757 | t) | |
e80c7164 | 758 | |
3d0f8a67 MB |
759 | (nnoo-define-skeleton nnmairix) |
760 | ||
761 | ||
762 | ;;; === Interactive functions | |
763 | ||
764 | (defun nnmairix-create-search-group (server group query threads) | |
765 | "Create on SERVER nnmairix search group GROUP with QUERY. | |
766 | If THREADS is t, include whole threads from found messages. If | |
767 | called interactively, user will be asked for parameters." | |
768 | (interactive | |
769 | (list | |
770 | (gnus-server-to-method (car (nnmairix-get-server))) | |
771 | (read-string "Group name: ") | |
772 | (read-string "Query: ") | |
773 | (y-or-n-p "Include threads? "))) | |
774 | (when (and (stringp query) | |
775 | (string-match "\\s-" query)) | |
776 | (setq query (split-string query))) | |
777 | (when (not (listp query)) | |
778 | (setq query (list query))) | |
779 | (when (and server group query) | |
780 | (save-excursion | |
781 | (let ((groupname (gnus-group-prefixed-name group server)) | |
782 | info) | |
783 | (set-buffer gnus-group-buffer) | |
784 | (gnus-group-make-group group server) | |
785 | (gnus-group-set-parameter groupname 'query query) | |
786 | (gnus-group-set-parameter groupname 'threads threads) | |
787 | (nnmairix-update-and-clear-marks groupname))))) | |
788 | ||
789 | (defun nnmairix-search-interactive () | |
790 | "Create mairix search interactively with the minibuffer." | |
791 | (interactive) | |
792 | (let ((char-header nnmairix-interactive-query-parameters) | |
793 | header finished query achar) | |
794 | (while (not finished) | |
795 | (while (not achar) | |
796 | (message "Query (%s): " (nnmairix-create-message-line-for-search)) | |
797 | (setq achar (read-char)) | |
798 | (when (not (assoc achar char-header)) | |
799 | (setq achar nil))) | |
800 | (setq header (read-string | |
801 | (concat "Match " (nth 3 (assoc achar char-header)) " on: "))) | |
802 | (push (concat (nth 2 (assoc achar char-header)) ":" header) query) | |
803 | (setq finished (not (y-or-n-p "Add another search query? ")) | |
804 | achar nil)) | |
805 | (nnmairix-search | |
806 | (mapconcat 'identity query " ") | |
807 | (car (nnmairix-get-server)) | |
808 | (y-or-n-p "Include whole threads? ")))) | |
809 | ||
810 | (defun nnmairix-create-search-group-from-message () | |
811 | "Interactively create search group with query based on current message." | |
812 | (interactive) | |
813 | (let ((char-header nnmairix-interactive-query-parameters) | |
814 | (server (nnmairix-backend-to-server gnus-current-select-method)) | |
815 | query achar header finished group threads cq) | |
816 | (when (or (not (gnus-buffer-live-p gnus-article-buffer)) | |
817 | (not (gnus-buffer-live-p gnus-summary-buffer))) | |
818 | (error "No article or summary buffer")) | |
819 | (when (not server) | |
820 | (error "No nnmairix server found for back end %s:%s" | |
821 | (symbol-name (car gnus-current-select-method)) | |
822 | (nth 1 gnus-current-select-method))) | |
823 | (while (not finished) | |
824 | (save-excursion | |
825 | (gnus-summary-toggle-header 1) | |
826 | (while (not achar) | |
827 | (message "Query (%s): " (nnmairix-create-message-line-for-search)) | |
828 | (setq achar (read-char)) | |
829 | (when (not (assoc achar char-header)) | |
830 | (setq achar nil))) | |
831 | (set-buffer gnus-article-buffer) | |
832 | (setq header nil) | |
833 | (when (setq cq (nth 1 (assoc achar char-header))) | |
834 | (setq header | |
835 | (nnmairix-replace-illegal-chars | |
836 | (gnus-fetch-field (nth 1 (assoc achar char-header)))))) | |
837 | (setq header (read-string | |
838 | (concat "Match " (nth 3 (assoc achar char-header)) " on: ") | |
839 | header)) | |
840 | (push (concat (nth 2 (assoc achar char-header)) ":" header) query) | |
841 | (setq finished (not (y-or-n-p "Add another search query? ")) | |
842 | achar nil))) | |
843 | (setq threads (y-or-n-p "Include whole threads? ")) | |
844 | (setq group (read-string "Group name: ")) | |
845 | (set-buffer gnus-summary-buffer) | |
846 | (message "Creating group %s on server %s with query %s." group | |
847 | (gnus-method-to-server server) (mapconcat 'identity query " ")) | |
848 | (nnmairix-create-search-group server group query threads))) | |
849 | ||
850 | (defun nnmairix-create-server-and-default-group () | |
851 | "Interactively create new nnmairix server with default search group. | |
852 | All necessary information will be queried from the user." | |
853 | (interactive) | |
854 | (let* ((name (read-string "Name of the mairix server: ")) | |
855 | (server (completing-read "Back end server (TAB for completion): " | |
9e601b8d | 856 | (nnmairix-get-valid-servers) nil 1)) |
3d0f8a67 MB |
857 | (mairix (read-string "Command to call mairix: " "mairix")) |
858 | (defaultgroup (read-string "Default search group: ")) | |
859 | (backend (symbol-name (car (gnus-server-to-method server)))) | |
860 | (servername (nth 1 (gnus-server-to-method server))) | |
861 | (hidden (and (string-match "^nn\\(imap\\|maildir\\)$" backend) | |
862 | (y-or-n-p | |
863 | "Does the back end server work with maildir++ (i.e. hidden directories)? "))) | |
864 | create) | |
865 | ||
866 | (apply (intern (format "%s-%s" backend "open-server")) | |
867 | (list servername)) | |
868 | ||
869 | (when (and hidden | |
870 | (string-match "^\\." defaultgroup)) | |
871 | (setq defaultgroup (substring defaultgroup 1))) | |
872 | ;; Create default search group | |
873 | (gnus-group-make-group | |
874 | defaultgroup (list 'nnmairix name (list 'nnmairix-backend (intern backend)) | |
875 | (list 'nnmairix-backend-server servername) | |
876 | (list 'nnmairix-mairix-command mairix) | |
877 | (list 'nnmairix-hidden-folders hidden) | |
878 | (list 'nnmairix-default-group defaultgroup))))) | |
879 | ||
3d0f8a67 MB |
880 | (defun nnmairix-group-change-query-this-group (&optional query) |
881 | "Set QUERY for group under cursor." | |
882 | (interactive) | |
883 | (let* ((group (gnus-group-group-name)) | |
884 | (method (gnus-find-method-for-group group)) | |
885 | (oldquery (gnus-group-get-parameter group 'query t))) | |
886 | (if (eq (car method) 'nnmairix) | |
887 | (progn | |
888 | (when (listp oldquery) | |
889 | (setq oldquery (mapconcat 'identity oldquery " "))) | |
890 | (setq query (or query | |
891 | (read-string "New query: " oldquery))) | |
892 | (when (stringp query) | |
893 | (setq query (split-string query))) | |
894 | (when query | |
895 | (gnus-group-set-parameter group 'query query) | |
896 | (nnmairix-update-and-clear-marks group))) | |
897 | (error "This is no nnmairix group")))) | |
e80c7164 | 898 | |
3d0f8a67 MB |
899 | |
900 | (defun nnmairix-group-toggle-threads-this-group (&optional threads) | |
901 | "Toggle threads parameter for this group. | |
902 | If THREADS is a positive number, set threads parameter to t. | |
903 | If THREADS is a negative number, set it to nil." | |
904 | (interactive) | |
2b968687 MB |
905 | (let ((group (gnus-group-group-name))) |
906 | (when (nnmairix-group-toggle-parameter | |
907 | group 'threads "Threads" threads) | |
908 | (nnmairix-update-and-clear-marks group)))) | |
909 | ||
910 | (defun nnmairix-group-toggle-propmarks-this-group (&optional propmarks) | |
911 | "Toggle marks propagation for this group. | |
912 | If PROPMARKS is a positive number, set parameter to t. | |
913 | If PROPMARKS is a negative number, set it to nil." | |
914 | (interactive) | |
915 | (unless (nnmairix-check-mairix-version "maildirpatch") | |
916 | (error "You need a mairix binary with maildir patch to use this feature. See docs for details")) | |
917 | (let ((group (gnus-group-group-name))) | |
918 | (when (or (not (string= (gnus-group-short-name group) | |
919 | (cadr (assoc 'nnmairix-default-group | |
920 | (gnus-find-method-for-group group))))) | |
921 | (y-or-n-p "You should not activate marks propagation for the default \ | |
922 | search group. Are you sure? ")) | |
923 | (nnmairix-group-toggle-parameter | |
924 | group 'propmarks "Marks propagation" propmarks)))) | |
925 | ||
926 | (defun nnmairix-group-toggle-allowfast-this-group (&optional allowfast) | |
927 | "Toggle fast entering for this group. | |
928 | If ALLOWFAST is a positive number, set parameter to t. | |
929 | If ALLOWFAST is a negative number, set it to nil." | |
930 | (interactive) | |
931 | (nnmairix-group-toggle-parameter | |
932 | (gnus-group-group-name) 'allow-fast "Fast entering" allowfast)) | |
933 | ||
934 | ||
935 | (defun nnmairix-group-toggle-readmarks-this-group (&optional readmarks) | |
936 | "Toggle read/unread marks for this group. | |
937 | If READMARKS is a positive number, articles will always be read. | |
938 | If READMARKS is a negative number, articles will always be unread. | |
939 | If READMARKS is t or zero, marks will stay unchanged." | |
940 | (interactive) | |
941 | (let* ((group (gnus-group-group-name)) | |
942 | (method (gnus-find-method-for-group group)) | |
943 | (readmarks (or readmarks | |
944 | (gnus-group-get-parameter group 'readmarks)))) | |
3d0f8a67 | 945 | (if (eq (car method) 'nnmairix) |
2b968687 MB |
946 | (cond |
947 | ((or (and (numberp readmarks) (< readmarks 0)) | |
948 | (eq readmarks 'read)) | |
949 | (gnus-group-set-parameter group 'readmarks 'unread) | |
950 | (nnheader-message 3 "Articles in %s always unread." group)) | |
951 | ((or (and (numberp readmarks) (> readmarks 0)) | |
952 | (not readmarks)) | |
953 | (gnus-group-set-parameter group 'readmarks 'read) | |
954 | (nnheader-message 3 "Articles in %s always read." group)) | |
955 | (t | |
956 | (gnus-group-set-parameter group 'readmarks nil) | |
957 | (nnheader-message 3 "Read marks in %s stay unchanged." group))) | |
3d0f8a67 | 958 | (error "This is no nnmairix group")))) |
e80c7164 | 959 | |
3d0f8a67 MB |
960 | |
961 | (defun nnmairix-search (query &optional server threads) | |
962 | "Sends QUERY to nnmairix backend SERVER, using default its search group. | |
963 | ||
964 | Default search group is automatically entered and results are shown. | |
965 | If THREADS is t, enable threads. | |
966 | If THREADS is a negative number, disable threads. | |
967 | Otherwise, leave threads parameter as it is." | |
968 | (interactive (list (read-string "Query: "))) | |
969 | (when (not server) | |
970 | (setq server (car (nnmairix-get-server)))) | |
971 | (if (not server) | |
972 | (error "No opened nnmairix server found") | |
973 | (setq server (gnus-server-to-method server))) | |
974 | (nnmairix-open-server (nth 1 server)) | |
975 | (let* ((qualgroup (gnus-group-prefixed-name nnmairix-default-group | |
976 | (list 'nnmairix (nth 1 server))))) | |
977 | (set-buffer gnus-group-buffer) | |
978 | (when (stringp query) | |
979 | (setq query (split-string query))) | |
980 | (gnus-group-set-parameter qualgroup 'query query) | |
981 | (if (symbolp threads) | |
982 | (when (eq threads 't) | |
983 | (gnus-group-set-parameter qualgroup 'threads t)) | |
984 | (when (< threads 0) | |
985 | (gnus-group-set-parameter qualgroup 'threads nil))) | |
986 | (nnmairix-update-and-clear-marks qualgroup) | |
2b968687 | 987 | (unless (equal (gnus-active qualgroup) '(1 . 0)) |
3d0f8a67 MB |
988 | (gnus-group-read-group nil t qualgroup)))) |
989 | ||
990 | (defun nnmairix-search-thread-this-article () | |
991 | "Search thread for the current article. | |
992 | This is effectively a shortcut for calling `nnmairix-search' | |
993 | with m:msgid of the current article and enabled threads." | |
994 | (interactive) | |
995 | (let* ((server | |
996 | (nnmairix-backend-to-server gnus-current-select-method)) | |
997 | mid) | |
998 | (if server | |
999 | (if (gnus-buffer-live-p gnus-article-buffer) | |
1000 | (progn | |
1001 | (save-excursion | |
1002 | (set-buffer gnus-article-buffer) | |
1003 | (gnus-summary-toggle-header 1) | |
1004 | (setq mid (message-fetch-field "Message-ID"))) | |
1005 | (while (string-match "[<>]" mid) | |
1006 | (setq mid (replace-match "" t t mid))) | |
1007 | (nnmairix-search (concat "m:" mid) server t)) | |
1008 | (message "No article buffer.")) | |
1009 | (error "No nnmairix server found for back end %s:%s" | |
1010 | (symbol-name (car gnus-current-select-method)) | |
1011 | (nth 1 gnus-current-select-method))))) | |
1012 | ||
1013 | (defun nnmairix-search-from-this-article () | |
1014 | "Search messages from sender of the current article. | |
1015 | This is effectively a shortcut for calling `nnmairix-search' with | |
1016 | f:current_from." | |
1017 | (interactive) | |
1018 | (let* ((server | |
1019 | (nnmairix-backend-to-server gnus-current-select-method)) | |
1020 | from) | |
1021 | (if server | |
1022 | (if (gnus-buffer-live-p gnus-article-buffer) | |
1023 | (progn | |
1024 | (save-excursion | |
1025 | (set-buffer gnus-article-buffer) | |
1026 | (gnus-summary-toggle-header 1) | |
1027 | (setq from (cadr (gnus-extract-address-components | |
1028 | (gnus-fetch-field "From")))) | |
1029 | (nnmairix-search (concat "f:" from) server -1))) | |
1030 | (message "No article buffer.")) | |
1031 | (error "No nnmairix server found for back end %s:%s" | |
1032 | (symbol-name (car gnus-current-select-method)) | |
1033 | (nth 1 gnus-current-select-method))))) | |
1034 | ||
1035 | ||
1036 | (defun nnmairix-purge-old-groups (&optional dontask server) | |
1037 | "Delete mairix search groups which are no longer used. | |
1038 | ||
1039 | You may want to call this from time to time if you are creating | |
1040 | and deleting lots of nnmairix groups. If DONTASK is t, do not ask | |
1041 | before deleting a group on the back end. SERVER specifies nnmairix server." | |
1042 | (interactive) | |
1043 | (let ((server (or server | |
1044 | (gnus-server-to-method (car (nnmairix-get-server)))))) | |
1045 | (if (nnmairix-open-server (nth 1 server)) | |
1046 | (when (nnmairix-call-backend | |
1047 | "request-list" nnmairix-backend-server) | |
1048 | (let (cur qualgroup folder) | |
1049 | (save-excursion | |
1050 | (set-buffer nntp-server-buffer) | |
1051 | (goto-char (point-min)) | |
1052 | (while (re-search-forward nnmairix-group-regexp (point-max) t) | |
1053 | (setq cur (match-string 0) | |
1054 | qualgroup (gnus-group-prefixed-name | |
1055 | (match-string 1) server)) | |
1056 | (when (not (and (gnus-group-entry qualgroup) | |
1057 | (string= cur | |
1058 | (gnus-group-get-parameter | |
1059 | qualgroup 'folder)))) | |
1060 | (when (or dontask | |
1061 | (y-or-n-p | |
1062 | (concat "Delete group " cur | |
1063 | " on server " nnmairix-backend-server "? "))) | |
1064 | (nnmairix-call-backend | |
1065 | "request-delete-group" cur t nnmairix-backend-server))))))) | |
1066 | (message "Couldn't open server %s" (nth 1 server))))) | |
1067 | ||
1068 | ||
1069 | (defun nnmairix-update-database (&optional servers) | |
1070 | "Call mairix for updating the database for SERVERS. | |
1071 | ||
1072 | If SERVERS is nil, do update for all nnmairix servers. Mairix | |
1073 | will be called asynchronously unless | |
1074 | `nnmairix-mairix-synchronous-update' is t. Mairix will be called | |
1075 | with `nnmairix-mairix-update-options'." | |
1076 | (interactive) | |
1077 | (let ((servers (or servers | |
1078 | (nnmairix-get-nnmairix-servers))) | |
1079 | args cur commandsplit) | |
1080 | (while servers | |
1081 | (setq cur (car (pop servers))) | |
1082 | (nnmairix-open-server | |
1083 | (nth 1 (gnus-server-to-method cur))) | |
1084 | (setq commandsplit (split-string nnmairix-mairix-command)) | |
1085 | (nnheader-message 7 "Updating mairix database for %s..." cur) | |
1086 | (if nnmairix-mairix-synchronous-update | |
1087 | (progn | |
1088 | (setq args (append (list (car commandsplit) nil | |
1089 | (get-buffer nnmairix-mairix-output-buffer) | |
1090 | nil))) | |
1091 | (if (> (length commandsplit) 1) | |
1092 | (setq args (append args (cdr commandsplit) nnmairix-mairix-update-options)) | |
1093 | (setq args (append args nnmairix-mairix-update-options))) | |
1094 | (apply 'call-process args) | |
1095 | (nnheader-message 7 "Updating mairix database for %s... done" cur)) | |
1096 | (progn | |
1097 | (setq args (append (list cur (get-buffer nnmairix-mairix-output-buffer) | |
1098 | (car commandsplit)))) | |
1099 | (if (> (length commandsplit) 1) | |
1100 | (setq args (append args (cdr commandsplit) nnmairix-mairix-update-options)) | |
1101 | (setq args (append args nnmairix-mairix-update-options))) | |
1102 | (set-process-sentinel (apply 'start-process args) | |
1103 | 'nnmairix-sentinel-mairix-update-finished)))))) | |
1104 | ||
2b968687 MB |
1105 | (defun nnmairix-group-delete-recreate-this-group () |
1106 | "Deletes and recreates group on the back end. | |
4963739e | 1107 | You can use this function on nnmairix groups which continuously |
2b968687 MB |
1108 | show wrong article counts." |
1109 | (interactive) | |
1110 | (let* ((group (gnus-group-group-name)) | |
1111 | (method (gnus-find-method-for-group group))) | |
1112 | (unless (eq (car method) 'nnmairix) | |
1113 | (error "This is not a nnmairix group")) | |
1114 | (when (y-or-n-p | |
1115 | (format "Really recreate group %s on the back end? " group)) | |
1116 | (nnmairix-delete-recreate-group group) | |
1117 | (gnus-group-get-new-news-this-group)))) | |
1118 | ||
1119 | (defun nnmairix-propagate-marks (&optional server) | |
1120 | "Propagate marks from nnmairix group to original articles. | |
1121 | Unless SERVER is explicitly specified, will use the last opened | |
1122 | nnmairix server. Only marks from current session will be set." | |
1123 | (interactive) | |
1124 | (if server | |
1125 | (nnmairix-open-server server) | |
1126 | (unless (eq (car gnus-current-select-method) 'nnmairix) | |
1127 | (if nnmairix-current-server | |
1128 | (nnmairix-open-server nnmairix-current-server) | |
1129 | (error "No opened nnmairix server")))) | |
1130 | (if nnmairix-marks-cache | |
1131 | (let (number ogroup number-cache method mid-marks temp) | |
1132 | ;; first we get the article numbers | |
1133 | (catch 'problem | |
1134 | (while (setq ogroup (pop nnmairix-marks-cache)) | |
1135 | (while (setq mid-marks (pop (cdr ogroup))) | |
1136 | (setq number | |
1137 | (cdr | |
1138 | (gnus-request-head (car mid-marks) (car ogroup)))) | |
1139 | (unless number | |
1140 | (nnheader-message | |
1141 | 3 "Unable to set mark: couldn't determine article number for %s in %s" | |
1142 | (car mid-marks) (car ogroup)) | |
1143 | (throw 'problem nil)) | |
1144 | (setq temp (assoc (car ogroup) number-cache)) | |
1145 | (if temp | |
1146 | (catch 'done | |
1147 | (dolist (cur (cdr temp)) | |
1148 | (when (equal (cdr cur) (list (nth 1 mid-marks) (nth 2 mid-marks))) | |
1149 | (nconc (car cur) (list number)) | |
1150 | (throw 'done nil))) | |
1151 | (nconc temp (list (list (list number) (nth 1 mid-marks) (nth 2 mid-marks))))) | |
1152 | (push (list (car ogroup) (list (list number) (nth 1 mid-marks) (nth 2 mid-marks))) | |
1153 | number-cache))))) | |
1154 | ;; now we set the marks | |
1155 | (save-excursion | |
1156 | (set-buffer gnus-group-buffer) | |
1157 | (nnheader-message 5 "nnmairix: Propagating marks...") | |
1158 | (dolist (cur number-cache) | |
1159 | (setq method (gnus-find-method-for-group (car cur))) | |
1160 | (apply (intern (format "%s-%s" | |
1161 | (symbol-name (car method)) | |
1162 | "request-set-mark")) | |
1163 | (gnus-group-short-name (car cur)) | |
1164 | (cdr cur) | |
1165 | (list (nth 1 method))) | |
1166 | (gnus-group-jump-to-group (car cur)) | |
1167 | (gnus-group-get-new-news-this-group))) | |
1168 | (nnheader-message 5 "nnmairix: Propagating marks... done")) | |
1169 | (nnheader-message 3 "No marks to propagate."))) | |
1170 | ||
1171 | (defun nnmairix-update-groups (servername &optional skipdefault updatedb) | |
1172 | "Update all search groups on SERVERNAME. | |
1173 | If SKIPDEFAULT is t, the default search group will not be | |
1174 | updated. | |
1175 | If UPDATEDB is t, database for SERVERNAME will be updated first." | |
1176 | (interactive (list (completing-read "Update groups on server: " | |
1177 | (nnmairix-get-nnmairix-servers)))) | |
1178 | (save-excursion | |
1179 | (when (string-match ".*:\\(.*\\)" servername) | |
1180 | (setq servername (match-string 1 servername))) | |
1181 | (if (not (assoc (format "nnmairix:%s" servername) | |
1182 | (nnmairix-get-nnmairix-servers))) | |
1183 | (nnheader-message 3 "Server %s not opened" servername) | |
1184 | (when updatedb | |
1185 | (let ((nnmairix-mairix-synchronous-update t)) | |
1186 | (nnmairix-update-database | |
1187 | (list (list (format "nnmairix:%s" servername)))))) | |
1188 | (let ((groups (nnmairix-get-groups-from-server servername)) | |
1189 | default) | |
1190 | (when skipdefault | |
1191 | (setq default | |
1192 | (format "nnmairix+%s:%s" | |
1193 | servername | |
1194 | (cadr | |
1195 | (assoc 'nnmairix-default-group | |
1196 | (gnus-server-to-method | |
1197 | (format "nnmairix:%s" servername))))))) | |
1198 | (dolist (cur groups) | |
1199 | (unless (and skipdefault | |
1200 | (string= (car cur) default)) | |
1201 | (gnus-group-jump-to-group (car cur)) | |
0cf681b6 KY |
1202 | (gnus-group-mark-group 1))) |
1203 | (gnus-group-get-new-news-this-group))))) | |
2b968687 MB |
1204 | |
1205 | (defun nnmairix-remove-tick-mark-original-article () | |
1206 | "Remove tick mark from original article. | |
1207 | Marks propagation has to be enabled for this to work." | |
1208 | (interactive) | |
1209 | (unless (eq (car gnus-current-select-method) 'nnmairix) | |
1210 | (error "Not in a nnmairix group")) | |
1211 | (save-excursion | |
1212 | (let ((mid (mail-header-message-id (gnus-summary-article-header))) | |
1213 | groups cur) | |
1214 | (when mid | |
1215 | (setq groups (nnmairix-determine-original-group-from-registry mid)) | |
1216 | (unless (or groups | |
1217 | nnmairix-only-use-registry) | |
1218 | (setq groups | |
1219 | (nnmairix-determine-original-group-from-path mid nnmairix-current-server))) | |
1220 | (unless groups | |
1221 | (error "Couldn't find original article")) | |
1222 | (dolist (cur groups) | |
1223 | (push `(,cur (,mid del (tick))) nnmairix-marks-cache)) | |
1224 | (nnheader-message 5 "Will remove tick mark for %s upon closing." mid))))) | |
3d0f8a67 MB |
1225 | |
1226 | ;;; ==== Helper functions | |
1227 | ||
1228 | (defun nnmairix-request-group-with-article-number-correction (folder qualgroup) | |
2b968687 | 1229 | "Request FOLDER on back end for nnmairix QUALGROUP and article number correction." |
3d0f8a67 | 1230 | (save-excursion |
14e8de0c | 1231 | (nnmairix-call-backend "request-group" folder nnmairix-backend-server) |
3d0f8a67 MB |
1232 | (set-buffer nnmairix-mairix-output-buffer) |
1233 | (goto-char (point-min)) | |
1234 | (re-search-forward "^Matched.*messages") | |
1235 | (nnheader-message 7 (match-string 0)) | |
1236 | (set-buffer nntp-server-buffer) | |
1237 | (goto-char (point-min)) | |
1238 | (let ((status (read (current-buffer))) | |
1239 | (total (read (current-buffer))) | |
1240 | (low (read (current-buffer))) | |
1241 | (high (read (current-buffer))) | |
1242 | (corr (gnus-group-get-parameter qualgroup 'numcorr t))) | |
1243 | (if (= status 211) | |
1244 | (progn | |
1245 | ;; Article number correction | |
1246 | (if (and corr | |
1247 | (> (+ (car (cddr corr)) high) 0)) | |
1248 | (progn | |
1249 | (when (car corr) ;Group has changed | |
1250 | (setq corr | |
1251 | (list nil | |
1252 | (car (cddr corr)) | |
1253 | (+ (car (cddr corr)) high))) | |
1254 | (gnus-group-set-parameter | |
1255 | qualgroup 'numcorr corr)) | |
1256 | (setq low (+ low (cadr corr)) | |
1257 | high (+ high (cadr corr)))) | |
1258 | (when (member nnmairix-backend | |
1259 | nnmairix-delete-and-create-on-change) | |
1260 | (gnus-group-set-parameter | |
1261 | qualgroup 'numcorr (list nil 0 high)))) | |
1262 | (erase-buffer) | |
be021c01 | 1263 | (insert (format "%d %d %d %d %s" status total low high |
14e8de0c | 1264 | (gnus-group-real-name qualgroup))) |
3d0f8a67 MB |
1265 | t) |
1266 | (progn | |
1267 | (nnheader-report | |
1268 | 'nnmairix "Error calling back end on group %s" folder) | |
1269 | nil))))) | |
1270 | ||
2b968687 MB |
1271 | (defun nnmairix-call-mairix-binary (command folder searchquery threads) |
1272 | "Call mairix binary with COMMAND, using FOLDER and SEARCHQUERY. | |
3d0f8a67 MB |
1273 | If THREADS is non-nil, enable full threads." |
1274 | (let ((args (cons (car command) '(nil t nil)))) | |
1275 | (save-excursion | |
1276 | (set-buffer | |
1277 | (get-buffer-create nnmairix-mairix-output-buffer)) | |
1278 | (erase-buffer) | |
1279 | (when (> (length command) 1) | |
1280 | (setq args (append args (cdr command)))) | |
2b968687 MB |
1281 | (when nnmairix-mairix-search-options |
1282 | (setq args (append args nnmairix-mairix-search-options))) | |
1283 | ;; If we have a patched mairix binary, call it with "-c" | |
1284 | (when (nnmairix-check-mairix-version "maildirpatch") | |
1285 | (setq args (append args '("-c")))) | |
3d0f8a67 MB |
1286 | (when threads |
1287 | (setq args (append args '("-t")))) | |
1288 | (apply 'call-process | |
2b968687 | 1289 | (append args (list "-o" folder) searchquery))))) |
3d0f8a67 MB |
1290 | |
1291 | (defun nnmairix-call-mairix-binary-raw (command query) | |
1292 | "Call mairix binary with COMMAND and QUERY in raw mode." | |
1293 | (let ((args (cons (car command) '(nil t nil)))) | |
1294 | (save-excursion | |
1295 | (set-buffer | |
1296 | (get-buffer-create nnmairix-mairix-output-buffer)) | |
1297 | (erase-buffer) | |
1298 | (when (> (length command) 1) | |
1299 | (setq args (append args (cdr command)))) | |
1300 | (setq args (append args '("-r"))) | |
1301 | (apply 'call-process | |
1302 | (append args query))))) | |
1303 | ||
1304 | (defun nnmairix-get-server () | |
1305 | "If there exists just one nnmairix server, return its value. | |
1306 | Otherwise, ask user for server." | |
1307 | (let ((openedserver (nnmairix-get-nnmairix-servers))) | |
1308 | (when (not openedserver) | |
1309 | (error "No opened nnmairix server found")) | |
1310 | (if (> (length openedserver) 1) | |
1311 | (progn | |
1312 | (while | |
1313 | (equal '("") | |
1314 | (setq nnmairix-last-server | |
1315 | (list (completing-read "Server: " openedserver nil 1 | |
1316 | (or nnmairix-last-server | |
1317 | "nnmairix:")))))) | |
1318 | nnmairix-last-server) | |
1319 | (car openedserver)))) | |
1320 | ||
1321 | (defun nnmairix-get-nnmairix-servers (&optional all) | |
1322 | "Return available nnmairix servers. | |
1323 | If ALL is t, return also the unopened/failed ones." | |
1324 | (let ((alist gnus-opened-servers) | |
1325 | server openedserver) | |
1326 | (while alist | |
1327 | (setq server (pop alist)) | |
1328 | (when (and server | |
1329 | (or all | |
1330 | (eq (cadr server) 'ok)) | |
1331 | (eq (caar server) 'nnmairix) | |
1332 | (not (member (car server) gnus-ephemeral-servers))) | |
1333 | (setq server | |
1334 | (concat (symbol-name (caar server)) ":" (nth 1 (car server)))) | |
1335 | (push (list server) openedserver))) | |
1336 | openedserver)) | |
1337 | ||
3d0f8a67 | 1338 | (defun nnmairix-get-valid-servers () |
2b968687 | 1339 | "Return list of valid back end servers for nnmairix groups." |
3d0f8a67 MB |
1340 | (let ((alist gnus-opened-servers) |
1341 | (mairixservers (nnmairix-get-nnmairix-servers t)) | |
1342 | server mserver openedserver occ cur) | |
1343 | ;; Get list of all nnmairix backends (i.e. backends which are | |
1344 | ;; already occupied) | |
1345 | (dolist (cur mairixservers) | |
1346 | (push | |
1347 | (concat | |
1348 | (symbol-name | |
1349 | (cadr (assoc 'nnmairix-backend | |
1350 | (gnus-server-to-method (car cur))))) | |
1351 | ":" | |
1352 | (cadr (assoc 'nnmairix-backend-server | |
1353 | (gnus-server-to-method (car cur))))) | |
1354 | occ)) | |
1355 | (while alist | |
1356 | (setq server (pop alist)) | |
1357 | (setq mserver (gnus-method-to-server (car server))) | |
1358 | ;; If this is the native server, convert it to the real server | |
1359 | ;; name to avoid confusion | |
1360 | (when (string= mserver "native") | |
1361 | (setq mserver (format "%s:%s" | |
1362 | (caar server) | |
1363 | (nth 1 (car server))))) | |
1364 | (when (and server | |
1365 | (eq (cadr server) 'ok) | |
1366 | (member (caar server) nnmairix-valid-backends) | |
1367 | (not (member (car server) gnus-ephemeral-servers)) | |
1368 | (not (member (gnus-method-to-server (car server)) occ))) | |
1369 | (push | |
1370 | (list mserver) | |
1371 | openedserver))) | |
1372 | openedserver)) | |
1373 | ||
2b968687 MB |
1374 | (defun nnmairix-get-groups-from-server (servername) |
1375 | "Return all groups for nnmairix server SERVERNAME." | |
1376 | (let ((searchstring (format "nnmairix\\+%s:" servername)) | |
1377 | groups) | |
1378 | (dolist (cur gnus-newsrc-alist) | |
1379 | (when (string-match searchstring | |
1380 | (car cur)) | |
1381 | (push (list (car cur)) groups))) | |
1382 | groups)) | |
1383 | ||
3d0f8a67 MB |
1384 | (defun nnmairix-call-backend (func &rest args) |
1385 | "Call a function FUNC on backend with ARGS." | |
1386 | (apply (intern (format "%s-%s" (symbol-name nnmairix-backend) func)) args)) | |
1387 | ||
1388 | (defun nnmairix-get-backend-folder (group &optional server) | |
1389 | "Return back end GROUP from nnmairix group on SERVER." | |
1390 | (let* ((qualgroup (if server | |
1391 | (gnus-group-prefixed-name group (list 'nnmairix server)) | |
1392 | group)) | |
1393 | (folder (gnus-group-get-parameter qualgroup 'folder))) | |
1394 | folder)) | |
1395 | ||
1396 | (defun nnmairix-get-numcorr (group &optional server) | |
1397 | "Return values for article number correction nnmairix GROUP on SERVER." | |
1398 | (let* ((qualgroup (if server | |
1399 | (gnus-group-prefixed-name group (list 'nnmairix server)) | |
1400 | group)) | |
1401 | (corr (gnus-group-get-parameter qualgroup 'numcorr t))) | |
1402 | corr)) | |
1403 | ||
1404 | ||
1405 | (defun nnmairix-rename-files-consecutively (path) | |
1406 | "Rename all nnml mail files in PATH so that they have consecutive numbers. | |
1407 | This should correct problems of wrong article counts when using | |
1408 | nnmairix with nnml backends." | |
1409 | (let* ((files | |
1410 | (sort | |
1411 | (mapcar 'string-to-number | |
1412 | (directory-files path nil "[0-9]+" t)) | |
1413 | '<)) | |
1414 | (lastplusone (car files)) | |
1415 | (path (file-name-as-directory path))) | |
1416 | (dolist (cur files) | |
1417 | (when (not (= cur lastplusone)) | |
1418 | (rename-file (concat path | |
1419 | (number-to-string cur)) | |
1420 | (concat path | |
1421 | (number-to-string lastplusone))) | |
1422 | (setq cur lastplusone)) | |
1423 | (setq lastplusone (1+ cur))))) | |
1424 | ||
1425 | (defun nnmairix-replace-group-and-numbers (articles backendgroup mairixgroup numc) | |
1426 | "Replace folder names in Xref header and correct article numbers. | |
1427 | Do this for all ARTICLES on BACKENDGROUP. Replace using | |
1428 | MAIRIXGROUP. NUMC contains values for article number correction." | |
1429 | (let ((buf (get-buffer-create " *nnmairix buffer*")) | |
1430 | (corr (not (zerop numc))) | |
1431 | (name (buffer-name nntp-server-buffer)) | |
1432 | header cur xref) | |
1433 | (save-excursion | |
1434 | (set-buffer buf) | |
1435 | (erase-buffer) | |
1436 | (set-buffer nntp-server-buffer) | |
1437 | (goto-char (point-min)) | |
1438 | (nnheader-message 7 "nnmairix: Rewriting headers...") | |
e80c7164 GM |
1439 | (mapc |
1440 | (lambda (article) | |
1441 | (when (or (looking-at (number-to-string article)) | |
1442 | (nnheader-find-nov-line article)) | |
1443 | (setq cur (nnheader-parse-nov)) | |
1444 | (when corr | |
1445 | (setq article (+ (mail-header-number cur) numc)) | |
1446 | (mail-header-set-number cur article)) | |
1447 | (setq xref (mail-header-xref cur)) | |
1448 | (when (and (stringp xref) | |
1449 | (string-match (format "[ \t]%s:[0-9]+" backendgroup) xref)) | |
1450 | (setq xref (replace-match (format " %s:%d" mairixgroup article) t nil xref)) | |
1451 | (mail-header-set-xref cur xref)) | |
1452 | (set-buffer buf) | |
1453 | (nnheader-insert-nov cur) | |
1454 | (set-buffer nntp-server-buffer) | |
1455 | (when (not (eobp)) | |
1456 | (forward-line 1)))) | |
3d0f8a67 MB |
1457 | articles) |
1458 | (nnheader-message 7 "nnmairix: Rewriting headers... done") | |
1459 | (kill-buffer nntp-server-buffer) | |
1460 | (set-buffer buf) | |
1461 | (rename-buffer name) | |
1462 | (setq nntp-server-buffer buf)))) | |
1463 | ||
1464 | (defun nnmairix-backend-to-server (server) | |
1465 | "Return nnmairix server most probably responsible for back end SERVER. | |
1466 | User will be asked if this cannot be determined. Result is saved in | |
1467 | parameter 'indexed-servers of corresponding default search | |
1468 | group." | |
1469 | (let ((allservers (nnmairix-get-nnmairix-servers)) | |
1470 | mairixserver found defaultgroup) | |
1471 | (if (> (length allservers) 1) | |
1472 | (progn | |
1473 | ;; If there is more than one nnmairix server, we go through them | |
1474 | (while (and allservers (not found)) | |
1475 | (setq mairixserver (gnus-server-to-method (car (pop allservers)))) | |
1476 | ;; First we look if SERVER is the backend of current nnmairix server | |
1477 | (setq found (and (eq (cadr (assoc 'nnmairix-backend mairixserver)) | |
1478 | (car server)) | |
1479 | (string= (cadr (assoc 'nnmairix-backend-server mairixserver)) | |
1480 | (nth 1 server)))) | |
1481 | ;; If that's not the case, we look at 'indexed-servers | |
1482 | ;; variable in default search group | |
1483 | (when (not found) | |
1484 | (setq defaultgroup (cadr (assoc 'nnmairix-default-group mairixserver))) | |
1485 | (setq found (member (gnus-method-to-server server) | |
1486 | (gnus-group-get-parameter | |
1487 | (gnus-group-prefixed-name defaultgroup | |
1488 | mairixserver) | |
1489 | 'indexed-servers t))))) | |
1490 | ;; If still not found, we ask user | |
1491 | (when (not found) | |
1492 | (setq mairixserver | |
1493 | (gnus-server-to-method | |
1494 | (completing-read | |
1495 | (format "Cannot determine which nnmairix server indexes %s. Please specify: " | |
1496 | (gnus-method-to-server server)) | |
1497 | (nnmairix-get-nnmairix-servers) nil nil "nnmairix:"))) | |
1498 | ;; Save result in parameter of default search group so that | |
1499 | ;; we don't have to ask again | |
1500 | (setq defaultgroup (gnus-group-prefixed-name | |
1501 | (cadr (assoc 'nnmairix-default-group mairixserver)) mairixserver)) | |
1502 | (gnus-group-set-parameter | |
1503 | defaultgroup | |
1504 | 'indexed-servers | |
1505 | (append (gnus-group-get-parameter defaultgroup 'indexed-servers t) | |
1506 | (list (gnus-method-to-server server))))) | |
1507 | mairixserver) | |
1508 | ;; If there is just one (or none) nnmairix server: | |
1509 | (gnus-server-to-method (caar allservers))))) | |
1510 | ||
2b968687 MB |
1511 | |
1512 | (defun nnmairix-delete-recreate-group (group) | |
1513 | "Delete and recreate folder from GROUP on the back end." | |
1514 | (when (member nnmairix-backend nnmairix-delete-and-create-on-change) | |
1515 | (let ((folder (gnus-group-get-parameter group 'folder))) | |
1516 | (if (string-match nnmairix-group-regexp folder) | |
1517 | (progn | |
1518 | (nnmairix-call-backend "open-server" | |
1519 | nnmairix-backend-server) | |
1520 | (nnmairix-call-backend "request-delete-group" | |
1521 | folder t nnmairix-backend-server) | |
1522 | (nnmairix-call-backend "request-create-group" | |
1523 | folder nnmairix-backend-server)) | |
1524 | (error "`nnmairix-delete-recreate-group' called on \ | |
1525 | non-mairix group. Check folder parameter"))))) | |
1526 | ||
3d0f8a67 MB |
1527 | (defun nnmairix-update-and-clear-marks (group &optional method) |
1528 | "Update group and clear all marks from GROUP using METHOD." | |
3d0f8a67 MB |
1529 | (let ((method (or method |
1530 | (gnus-find-method-for-group group))) | |
3d0f8a67 MB |
1531 | (corr (gnus-group-get-parameter group 'numcorr t)) |
1532 | info) | |
2b968687 MB |
1533 | (unless (or (gnus-group-prefixed-p group) |
1534 | (not method)) | |
1535 | (setq group (gnus-group-prefixed-name group method))) | |
3d0f8a67 MB |
1536 | (if (eq (nth 0 method) 'nnmairix) |
1537 | (save-excursion | |
1538 | (nnmairix-open-server (nth 1 method)) | |
1539 | (set-buffer gnus-group-buffer) | |
2b968687 | 1540 | ;; (gnus-group-set-parameter group 'propmarks nil) |
3d0f8a67 MB |
1541 | (setq info (gnus-get-info group)) |
1542 | ;; Clear active and info | |
1543 | (gnus-set-active group nil) | |
1544 | (gnus-info-clear-data info) | |
1545 | ;; Delete and re-create group if needed | |
2b968687 MB |
1546 | (nnmairix-delete-recreate-group group) |
1547 | ;; set flag that group has changed for article number correction | |
3d0f8a67 | 1548 | (when (member nnmairix-backend nnmairix-delete-and-create-on-change) |
2b968687 MB |
1549 | (when corr |
1550 | (setcar corr t) | |
1551 | (gnus-group-set-parameter group 'numcorr corr))) | |
1552 | (gnus-group-jump-to-group group) | |
1553 | (gnus-group-get-new-news-this-group)) | |
1554 | (error "`nnmairix-update-and-clear-marks' called with non-nnmairix group")))) | |
3d0f8a67 MB |
1555 | |
1556 | (defun nnmairix-sentinel-mairix-update-finished (proc status) | |
1557 | "Sentinel for mairix update process PROC with STATUS." | |
1558 | (if (equal status "finished\n") | |
1559 | (nnheader-message 7 "Updating mairix database for %s... done" proc) | |
1560 | (error "There was an error updating the mairix database for server %s. \ | |
1561 | See %s for details" proc nnmairix-mairix-output-buffer))) | |
1562 | ||
1563 | (defun nnmairix-create-message-line-for-search () | |
1564 | "Create message line for interactive query in minibuffer." | |
1565 | (mapconcat | |
1566 | (function | |
1567 | (lambda (cur) | |
1568 | (format "%c=%s" (car cur) (nth 3 cur)))) | |
1569 | nnmairix-interactive-query-parameters ",")) | |
1570 | ||
1571 | (defun nnmairix-replace-illegal-chars (header) | |
1572 | "Replace illegal characters in HEADER for mairix query." | |
1573 | (when header | |
1574 | (if (> emacs-major-version 20) | |
1575 | (while (string-match "[^-.@/,& [:alnum:]]" header) | |
1576 | (setq header (replace-match "" t t header))) | |
1577 | (while (string-match "[[]{}:<>]" header) | |
1578 | (setq header (replace-match "" t t header)))) | |
1579 | (while (string-match "[-& ]" header) | |
1580 | (setq header (replace-match "," t t header))) | |
1581 | header)) | |
1582 | ||
2b968687 MB |
1583 | (defun nnmairix-group-toggle-parameter (group parameter description &optional par) |
1584 | "Toggle on GROUP a certain PARAMETER. | |
1585 | DESCRIPTION will be shown to the user with the activation | |
1586 | status. If PAR is a positive number, the group parameter will be | |
1587 | set to t and to nil otherwise." | |
1588 | (let* ((method (gnus-find-method-for-group group)) | |
1589 | (par (or par | |
1590 | (not (gnus-group-get-parameter group parameter))))) | |
1591 | (if (eq (car method) 'nnmairix) | |
1592 | (progn | |
1593 | (when (numberp par) | |
1594 | (setq par (> par 0))) | |
1595 | (gnus-group-set-parameter group parameter par) | |
1596 | (if par | |
1597 | (message "%s activated for group %s" description group) | |
1598 | (message "%s deactivated for group %s" description group)) | |
1599 | t) | |
1600 | (error "This is no nnmairix group") | |
1601 | nil))) | |
1602 | ||
1603 | ;; Search for original article helper functions | |
1604 | ||
1605 | (defun nnmairix-goto-original-article (&optional no-registry) | |
1606 | "Jump to the original group and display article. | |
1607 | The original group of the article is first determined with the | |
1608 | registry (if enabled). If the registry is not enabled or did not | |
1609 | find the article or the prefix NO-REGISTRY is non-nil, this | |
1610 | function will try to determine the original group form the path | |
1611 | of the mail file. The path is obtained through another mairix | |
1612 | search in raw mode." | |
1613 | (interactive "P") | |
1614 | (when (not (eq (car gnus-current-select-method) 'nnmairix)) | |
1615 | (let ((method (gnus-find-method-for-group gnus-newsgroup-name))) | |
1616 | (if (eq (car method) 'nnmairix) | |
1617 | (nnmairix-open-server (nth 1 method)) | |
1618 | (error "Not in a nnmairix group")))) | |
1619 | (when (not (gnus-buffer-live-p gnus-article-buffer)) | |
1620 | (error "No article buffer available")) | |
1621 | (let ((server (nth 1 gnus-current-select-method)) | |
1622 | mid rval group allgroups) | |
1623 | ;; get message id | |
1624 | (save-excursion | |
1625 | (set-buffer gnus-article-buffer) | |
1626 | (gnus-summary-toggle-header 1) | |
1627 | (setq mid (message-fetch-field "Message-ID")) | |
1628 | ;; first check the registry (if available) | |
1629 | (unless no-registry | |
1630 | (setq allgroups (nnmairix-determine-original-group-from-registry mid))) | |
1631 | (unless (or allgroups | |
1632 | nnmairix-only-use-registry) | |
1633 | ;; registry was not available or did not find article | |
1634 | ;; so we search again with mairix in raw mode to get filename | |
1635 | (setq allgroups | |
1636 | (nnmairix-determine-original-group-from-path mid server)) | |
1637 | ;; if available and allowed, add this entry to the registry | |
1638 | (when (and (not no-registry) | |
1639 | (boundp 'gnus-registry-install) | |
1640 | gnus-registry-install) | |
1641 | (dolist (cur allgroups) | |
1642 | (unless (gnus-parameter-registry-ignore cur) | |
1643 | (gnus-registry-add-group mid cur))))) | |
1644 | (if (> (length allgroups) 1) | |
1645 | (setq group | |
1646 | (completing-read | |
1647 | "Message exists in more than one group. Choose: " | |
1648 | allgroups nil t)) | |
1649 | (setq group (car allgroups)))) | |
1650 | (if group | |
1651 | ;; show article in summary buffer | |
1652 | (nnmairix-show-original-article group mid) | |
1653 | (nnheader-message 3 "Couldn't find original article")))) | |
1654 | ||
1655 | (defun nnmairix-determine-original-group-from-registry (mid) | |
1656 | "Try to determinale original group for message-id MID from the registry." | |
1657 | (when (and (boundp 'gnus-registry-install) | |
1658 | gnus-registry-install) | |
1659 | (unless (string-match "^<" mid) | |
1660 | (set mid (concat "<" mid))) | |
1661 | (unless (string-match ">$" mid) | |
1662 | (set mid (concat mid ">"))) | |
1663 | (gnus-registry-fetch-groups mid))) | |
1664 | ||
1665 | (defun nnmairix-determine-original-group-from-path (mid server) | |
1666 | "Determine original group(s) for message-id MID from the file path. | |
1667 | The file path is obtained through a mairix search for the id on | |
1668 | SERVER." | |
1669 | (nnmairix-open-server server) | |
1670 | (while (string-match "[<>]" mid) | |
1671 | (setq mid (replace-match "" t t mid))) | |
0cf681b6 KY |
1672 | ;; mairix somehow does not like '$' in message-id |
1673 | (when (string-match "\\$" mid) | |
1674 | (setq mid (concat mid "="))) | |
1675 | (while (string-match "\\$" mid) | |
1676 | (setq mid (replace-match "=," t t mid))) | |
2b968687 MB |
1677 | (let (allgroups) |
1678 | (if (zerop (nnmairix-call-mairix-binary-raw | |
1679 | (split-string nnmairix-mairix-command) | |
1680 | (list (concat "m:" mid)))) | |
1681 | (save-excursion | |
1682 | (set-buffer nnmairix-mairix-output-buffer) | |
1683 | (goto-char (point-min)) | |
1684 | (while (re-search-forward "^/.*$" nil t) | |
1685 | (push (nnmairix-get-group-from-file-path (match-string 0)) | |
1686 | allgroups) | |
1687 | (forward-line 1))) | |
1688 | (error "Mairix could not find original article. See buffer %s for details" | |
1689 | nnmairix-mairix-output-buffer)) | |
1690 | allgroups)) | |
1691 | ||
1692 | (defun nnmairix-get-group-from-file-path (file) | |
1693 | "Get group by parsing the message location FILE." | |
1694 | (let (path filename serverbase group maildirflag allgroups) | |
1695 | (string-match "^\\(.*\\)/\\(.*?\\)$" file) | |
1696 | (setq path (expand-file-name (match-string 1 file))) | |
1697 | (setq filename (match-string 2 file)) | |
1698 | ;; when we deal with maildir, remove cur/new/tmp from path | |
1699 | (setq maildirflag (string-match ".+\\..+\\..+" filename)) | |
1700 | (when maildirflag | |
1701 | (setq path | |
1702 | (replace-regexp-in-string | |
1703 | ".*\\(/cur\\|/new\\|/tmp\\)$" "" path t t 1))) | |
1704 | ;; we first check nnml and nnmaildir servers | |
1705 | (setq | |
1706 | group | |
1707 | (catch 'found | |
1708 | (dolist (cur gnus-opened-servers) | |
1709 | (when (or (and (not maildirflag) | |
1710 | (eq (caar cur) 'nnml)) | |
1711 | (and maildirflag | |
1712 | (eq (caar cur) 'nnmaildir))) | |
1713 | ;; get base path from server | |
1714 | (if maildirflag | |
1715 | (setq serverbase (cadr (assoc 'directory (car cur)))) | |
1716 | (setq serverbase (cadr (assoc 'nnml-directory (car cur)))) | |
1717 | (unless serverbase | |
1718 | (setq serverbase nnml-directory))) | |
1719 | (setq serverbase (file-name-as-directory | |
1720 | (expand-file-name serverbase))) | |
1721 | (when (string-match (concat serverbase "\\(.*\\)") path) | |
1722 | ;; looks good - rest of the path should be the group | |
1723 | (setq group (match-string 1 path)) | |
1724 | (when (string-match "/$" group) | |
1725 | (setq group (replace-match "" t t group))) | |
1726 | (unless maildirflag | |
1727 | ;; for nnml: convert slashes to dots | |
1728 | (while (string-match "/" group) | |
1729 | (setq group (replace-match "." t t group)))) | |
1730 | (setq group (gnus-group-prefixed-name group (car cur))) | |
1731 | ;; check whether this group actually exists | |
1732 | (when (gnus-group-entry group) | |
1733 | (throw 'found group))))))) | |
1734 | (unless group | |
1735 | ;; we haven't found it yet --> look for nnimap groups. Assume | |
1736 | ;; last element of the path is the group. This might fail since | |
1737 | ;; IMAP servers may present groups to the client in arbitrary | |
1738 | ;; ways... | |
1739 | (string-match "^.*/\\.?\\(.*\\)$" path) | |
1740 | (setq group (match-string 1 path)) | |
1741 | ;; convert dots to slashes (nested group) | |
1742 | (while (string-match "\\." group) | |
1743 | (setq group (replace-match "/" t t group))) | |
1744 | (dolist (cur gnus-opened-servers) | |
1745 | (when (eq (caar cur) 'nnimap) | |
1746 | (when (gnus-group-entry | |
1747 | (gnus-group-prefixed-name group (car cur))) | |
1748 | (push | |
1749 | (gnus-group-prefixed-name group (car cur)) | |
1750 | allgroups)))) | |
1751 | (if (> (length allgroups) 1) | |
1752 | (setq group (completing-read | |
1753 | "Group %s exists on more than one IMAP server. Choose: " | |
1754 | allgroups nil t)) | |
1755 | (setq group (car allgroups)))) | |
1756 | group)) | |
1757 | ||
3d0f8a67 MB |
1758 | (defun nnmairix-show-original-article (group mid) |
1759 | "Switch to GROUP and display Article with message-id MID." | |
2b968687 MB |
1760 | (unless (string-match "^<" mid) |
1761 | (set mid (concat "<" mid))) | |
1762 | (unless (string-match ">$" mid) | |
1763 | (set mid (concat mid ">"))) | |
3d0f8a67 MB |
1764 | (when (string-match "Summary" (buffer-name (current-buffer))) |
1765 | (gnus-summary-exit)) | |
1766 | (pop-to-buffer gnus-group-buffer) | |
1767 | (gnus-group-jump-to-group group) | |
e80c7164 | 1768 | (gnus-summary-read-group group 1 t) |
3d0f8a67 | 1769 | (gnus-summary-refer-article mid) |
2b968687 | 1770 | (gnus-summary-limit-to-headers (format "message-id: %s" mid)) |
3d0f8a67 MB |
1771 | (gnus-summary-select-article) |
1772 | ;; Force redisplay | |
1773 | (gnus-summary-show-article) | |
1774 | (nnheader-message 5 "Switched to group %s." group)) | |
1775 | ||
2b968687 MB |
1776 | (defun nnmairix-map-range (func range) |
1777 | "Map function FUNC on all members of RANGE." | |
1778 | (cond | |
1779 | ((numberp range) | |
1780 | (funcall func range)) | |
1781 | (t | |
1782 | (mapcar (lambda (cur) | |
1783 | (cond | |
1784 | ((listp cur) | |
1785 | (cons | |
1786 | (funcall func (car cur)) | |
1787 | (funcall func (cdr cur)))) | |
1788 | ((numberp cur) | |
1789 | (funcall func cur)))) | |
1790 | range)))) | |
1791 | ||
1792 | (defun nnmairix-check-mairix-version (version &optional server) | |
1793 | "Check mairix VERSION on SERVER. | |
1794 | If VERSION is a number: specifies the minimum version. | |
1795 | If VERSION is a string: must be contained in mairix version output." | |
1796 | (unless server | |
1797 | (setq server nnmairix-current-server)) | |
1798 | (let ((versionstring (cadr (assoc server nnmairix-version-output)))) | |
1799 | (unless versionstring | |
1800 | ;; call "mairix -V" to get the version string | |
1801 | (with-temp-buffer | |
1802 | (setq versionstring | |
1803 | (let* ((commandsplit (split-string nnmairix-mairix-command)) | |
1804 | (args (append (list (car commandsplit)) | |
1805 | `(nil t nil) (cdr commandsplit) '("-V")))) | |
1806 | (apply 'call-process args) | |
1807 | (goto-char (point-min)) | |
1808 | (re-search-forward "mairix.*") | |
1809 | (match-string 0)))) | |
1810 | ;; save version string for current session | |
1811 | (setq nnmairix-version-output | |
1812 | (append nnmairix-version-output | |
1813 | (list (list server versionstring))))) | |
1814 | (cond | |
1815 | ((stringp version) | |
1816 | (string-match version versionstring)) | |
1817 | ((numberp version) | |
1818 | (<= version (string-to-number | |
1819 | (progn | |
1820 | (string-match "mairix \\([0-9\\.]+\\)" versionstring) | |
1821 | (match-string 1 versionstring)))))))) | |
3d0f8a67 MB |
1822 | |
1823 | ;; ==== Widget stuff | |
1824 | ||
1825 | (defvar nnmairix-widgets) | |
1826 | (defvar nnmairix-widgets-values nil) | |
1827 | ||
1828 | (defun nnmairix-widget-search-from-this-article () | |
1829 | "Create mairix query based on current article using graphical widgets." | |
1830 | (interactive) | |
1831 | (nnmairix-widget-search | |
1832 | (nnmairix-widget-get-values))) | |
1833 | ||
3d0f8a67 MB |
1834 | (defun nnmairix-widget-get-values () |
1835 | "Create values for editable fields from current article." | |
1836 | (if (not (gnus-buffer-live-p gnus-article-buffer)) | |
1837 | (error "No article buffer available") | |
1838 | (save-excursion | |
1839 | (gnus-summary-toggle-header 1) | |
1840 | (set-buffer gnus-article-buffer) | |
1841 | (mapcar | |
1842 | (function | |
1843 | (lambda (field) | |
e80c7164 | 1844 | (list (car (cddr field)) |
3d0f8a67 MB |
1845 | (if (car field) |
1846 | (nnmairix-replace-illegal-chars | |
1847 | (gnus-fetch-field (car field))) | |
1848 | nil)))) | |
1849 | nnmairix-widget-fields-list)))) | |
e80c7164 | 1850 | |
3d0f8a67 MB |
1851 | |
1852 | (defun nnmairix-widget-search (&optional mvalues) | |
1853 | "Create mairix query interactively using graphical widgets. | |
1854 | MVALUES may contain values from current article." | |
1855 | (interactive) | |
1856 | ;; Select window for mairix customization | |
1857 | (funcall nnmairix-widget-select-window-function) | |
1858 | ;; generate widgets | |
1859 | (nnmairix-widget-create-query mvalues) | |
1860 | ;; generate Buttons | |
1861 | (widget-create 'push-button | |
1862 | :notify | |
1863 | (if mvalues | |
1864 | (lambda (&rest ignore) | |
1865 | (nnmairix-widget-send-query nnmairix-widgets | |
1866 | t)) | |
1867 | (lambda (&rest ignore) | |
1868 | (nnmairix-widget-send-query nnmairix-widgets | |
1869 | nil))) | |
1870 | "Send Query") | |
1871 | (widget-insert " ") | |
1872 | (widget-create 'push-button | |
1873 | :notify | |
1874 | (if mvalues | |
1875 | (lambda (&rest ignore) | |
1876 | (nnmairix-widget-create-group nnmairix-widgets | |
1877 | t)) | |
1878 | (lambda (&rest ignore) | |
1879 | (nnmairix-widget-create-group nnmairix-widgets | |
1880 | nil))) | |
1881 | "Create permanent group") | |
1882 | (widget-insert " ") | |
1883 | (widget-create 'push-button | |
1884 | :notify (lambda (&rest ignore) | |
1885 | (kill-buffer nnmairix-customize-query-buffer)) | |
1886 | "Cancel") | |
1887 | (use-local-map widget-keymap) | |
1888 | (widget-setup) | |
1889 | (goto-char (point-min))) | |
1890 | ||
1891 | (defun nnmairix-widget-send-query (widgets &optional withvalues) | |
1892 | "Send query from WIDGETS to mairix binary. | |
1893 | If WITHVALUES is t, query is based on current article." | |
1894 | (nnmairix-search | |
1895 | (nnmairix-widget-make-query-from-widgets widgets) | |
1896 | (if withvalues | |
1897 | (gnus-method-to-server | |
1898 | (nnmairix-backend-to-server gnus-current-select-method)) | |
1899 | (car (nnmairix-get-server))) | |
1900 | (if (widget-value (cadr (assoc "Threads" widgets))) | |
1901 | t | |
1902 | -1)) | |
1903 | (kill-buffer nnmairix-customize-query-buffer)) | |
1904 | ||
1905 | (defun nnmairix-widget-create-group (widgets &optional withvalues) | |
1906 | "Create nnmairix group based on current widget values WIDGETS. | |
1907 | If WITHVALUES is t, query is based on current article." | |
1908 | (let ((group (read-string "Name of the group: "))) | |
1909 | (when (not (zerop (length group))) | |
1910 | (nnmairix-create-search-group | |
1911 | (if withvalues | |
1912 | (gnus-method-to-server | |
1913 | (nnmairix-backend-to-server gnus-current-select-method)) | |
1914 | (car (nnmairix-get-server))) | |
1915 | group | |
1916 | (nnmairix-widget-make-query-from-widgets widgets) | |
1917 | (widget-value (cadr (assoc "Threads" widgets)))))) | |
1918 | (kill-buffer nnmairix-customize-query-buffer)) | |
1919 | ||
1920 | ||
1921 | (defun nnmairix-widget-make-query-from-widgets (widgets) | |
1922 | "Create mairix query from widget values WIDGETS." | |
1923 | (let (query temp flag) | |
1924 | ;; first we do the editable fields | |
1925 | (dolist (cur nnmairix-widget-fields-list) | |
1926 | ;; See if checkbox is checked | |
1927 | (when (widget-value | |
e80c7164 | 1928 | (cadr (assoc (concat "c" (car (cddr cur))) widgets))) |
3d0f8a67 MB |
1929 | ;; create query for the field |
1930 | (push | |
1931 | (concat | |
1932 | (nth 1 cur) | |
1933 | ":" | |
1934 | (nnmairix-replace-illegal-chars | |
1935 | (widget-value | |
e80c7164 | 1936 | (cadr (assoc (concat "e" (car (cddr cur))) widgets))))) |
3d0f8a67 MB |
1937 | query))) |
1938 | ;; Flags | |
1939 | (when (member 'flags nnmairix-widget-other) | |
1940 | (setq flag | |
1941 | (mapconcat | |
1942 | (function | |
1943 | (lambda (flag) | |
1944 | (setq temp | |
1945 | (widget-value (cadr (assoc (car flag) nnmairix-widgets)))) | |
1946 | (if (string= "yes" temp) | |
1947 | (cadr flag) | |
1948 | (if (string= "no" temp) | |
1949 | (concat "-" (cadr flag)))))) | |
1950 | '(("seen" "s") ("replied" "r") ("flagged" "f")) "")) | |
1951 | (when (not (zerop (length flag))) | |
1952 | (push (concat "F:" flag) query))) | |
1953 | ;; return query string | |
1954 | (mapconcat 'identity query " "))) | |
1955 | ||
1956 | ||
1957 | (defun nnmairix-widget-create-query (&optional values) | |
1958 | "Create widgets for creating mairix queries. | |
1959 | Fill in VALUES if based on an article." | |
1960 | (let (allwidgets) | |
1961 | (when (get-buffer nnmairix-customize-query-buffer) | |
1962 | (kill-buffer nnmairix-customize-query-buffer)) | |
1963 | (switch-to-buffer nnmairix-customize-query-buffer) | |
1964 | (kill-all-local-variables) | |
1965 | (erase-buffer) | |
1966 | (widget-insert "Specify your query for Mairix (check boxes for activating fields):\n\n") | |
1967 | (widget-insert "(Whitespaces will be converted to ',' (i.e. AND). Use '/' for OR.)\n\n") | |
1968 | ; (make-local-variable 'nnmairix-widgets) | |
1969 | (setq nnmairix-widgets (nnmairix-widget-build-editable-fields values)) | |
1970 | (when (member 'flags nnmairix-widget-other) | |
1971 | (widget-insert "\nFlags:\n Seen: ") | |
1972 | (nnmairix-widget-add "seen" | |
1973 | 'menu-choice | |
1974 | :value "ignore" | |
1975 | '(item "yes") '(item "no") '(item "ignore")) | |
1976 | (widget-insert " Replied: ") | |
1977 | (nnmairix-widget-add "replied" | |
1978 | 'menu-choice | |
1979 | :value "ignore" | |
1980 | '(item "yes") '(item "no") '(item "ignore")) | |
1981 | (widget-insert " Ticked: ") | |
1982 | (nnmairix-widget-add "flagged" | |
1983 | 'menu-choice | |
1984 | :value "ignore" | |
1985 | '(item "yes") '(item "no") '(item "ignore"))) | |
1986 | (when (member 'threads nnmairix-widget-other) | |
1987 | (widget-insert "\n") | |
1988 | (nnmairix-widget-add "Threads" 'checkbox nil)) | |
1989 | (widget-insert " Show full threads\n\n"))) | |
1990 | ||
3d0f8a67 MB |
1991 | (defun nnmairix-widget-build-editable-fields (values) |
1992 | "Build editable field widgets in `nnmairix-widget-fields-list'. | |
1993 | VALUES may contain values for editable fields from current article." | |
1994 | ;; how can this be done less ugly? | |
1995 | (let ((ret)) | |
1996 | (mapc | |
1997 | (function | |
1998 | (lambda (field) | |
e80c7164 | 1999 | (setq field (car (cddr field))) |
3d0f8a67 MB |
2000 | (setq ret |
2001 | (nconc | |
2002 | (list | |
2003 | (list | |
2004 | (concat "c" field) | |
2005 | (widget-create 'checkbox | |
2006 | :tag field | |
2007 | :notify (lambda (widget &rest ignore) | |
2008 | (nnmairix-widget-toggle-activate widget)) | |
2009 | nil))) | |
2010 | (list | |
2011 | (list | |
2012 | (concat "e" field) | |
2013 | (widget-create 'editable-field | |
2014 | :size 60 | |
2015 | :format (concat " " field ":" | |
2016 | (make-string (- 11 (length field)) ?\ ) | |
2017 | "%v") | |
2018 | :value (or (cadr (assoc field values)) "")))) | |
2019 | ret)) | |
2020 | (widget-insert "\n") | |
2021 | ;; Deactivate editable field | |
2022 | (widget-apply (cadr (nth 1 ret)) :deactivate))) | |
2023 | nnmairix-widget-fields-list) | |
2024 | ret)) | |
2025 | ||
2026 | (defun nnmairix-widget-add (name &rest args) | |
2027 | "Add a widget NAME with optional ARGS." | |
2028 | (push | |
2029 | (list name | |
2030 | (apply 'widget-create args)) | |
2031 | nnmairix-widgets)) | |
2032 | ||
2033 | (defun nnmairix-widget-toggle-activate (widget) | |
2b968687 | 2034 | "Toggle activation status of WIDGET depending on corresponding checkbox value." |
3d0f8a67 MB |
2035 | (let ((field (widget-get widget :tag))) |
2036 | (if (widget-value widget) | |
2037 | (widget-apply | |
2038 | (cadr (assoc (concat "e" field) nnmairix-widgets)) | |
2039 | :activate) | |
2040 | (widget-apply | |
2041 | (cadr (assoc (concat "e" field) nnmairix-widgets)) | |
2042 | :deactivate))) | |
2043 | (widget-setup)) | |
2044 | ||
2045 | (provide 'nnmairix) | |
2046 | ||
3d0f8a67 | 2047 | ;;; nnmairix.el ends here |