1 ;;; elmo-split.el --- Split messages according to the user defined rules.
3 ;; Copyright (C) 2002 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
28 ;; Put following lines on your .emacs.
30 ;; (autoload 'elmo-split "elmo-split" "Split messages on the folder." t)
32 ;; A command elmo-split is provided. If you enter:
36 ;; Messages in the `elmo-split-folder' are splitted to the folders
37 ;; according to the definition of `elmo-split-rule'.
43 (defcustom elmo-split-rule nil
44 "Split rule for the command `elmo-split'.
45 The format of this variable is a list of RULEs which has form like:
46 \(CONDITION FOLDER [continue]\)
48 The 1st element CONDITION is a sexp which consists of following.
50 1. Functions which accept argument FIELD-NAME and VALUE.
51 FIELD-NAME is a symbol of the field name.
53 `equal' ... True if the field value equals to VALUE.
54 Case of the letters are ignored.
55 `match' ... True if the field value matches to VALUE.
56 VALUE can contain \\& and \\N which will substitute
57 from matching \\(\\) patterns in the previous VALUE.
58 `address-equal' ... True if one of the addresses in the field equals to
59 VALUE. Case of the letters are ignored.
60 `address-match' ... True if one of the addresses in the field matches to
62 VALUE can contain \\& and \\N which will substitute
63 from matching \\(\\) patterns in the previous VALUE.
65 2. Functions which accept any number of arguments.
67 `or' ... True if one of the argument returns true.
68 `and' ... True if all of the arguments return true.
72 When a symbol is specified, it is evaluated.
74 The 2nd element FOLDER is the name of the folder to split messages into.
76 When the 3rd element `continue' is specified as symbol, evaluating rules is
77 not stopped even when the condition is satisfied.
81 \(setq elmo-split-rule
82 ;; Messages from spammers are stored in `+junk'
83 '(((or (address-equal from \"i.am@spammer\")
84 (address-equal from \"dull-work@dull-boy\")
85 (address-equal from \"death-march@software\")
86 (address-equal from \"ares@aon.at\")
87 (address-equal from \"get-money@richman\"))
89 ;; Messages from mule mailing list are stored in `%mule'
90 ((equal x-ml-name \"mule\") \"%mule\")
91 ;; Messages from wanderlust mailing list are stored in `%wanderlust'
92 ;; and continue evaluating following rules.
93 ((equal x-ml-name \"wanderlust\") \"%wanderlust\" continue)
94 ;; Messages from DoCoMo user are stored in `+docomo-{username}'.
95 ((match from \"\\\\(.*\\\\)@docomo\\\\.ne\\\\.jp\")
97 ;; Unmatched mails go to `+inbox'.
102 (defcustom elmo-split-folder "%inbox"
103 "Target folder or list of folders for splitting."
104 :type '(choice (string :tag "folder name")
105 (repeat (string :tag "folder name")))
108 (defcustom elmo-split-log-coding-system 'x-ctext
109 "A coding-system for writing log file."
113 (defcustom elmo-split-log-file "~/.elmo/split-log"
114 "The file name of the split log."
119 (defvar elmo-split-match-string-internal nil
120 "Internal variable for string matching. Don't touch this variable by hand.")
122 (defvar elmo-split-message-entity nil
123 "Buffer local variable to store mime-entity.")
124 (make-variable-buffer-local 'elmo-split-message-entity)
127 (defun elmo-split-or (buffer &rest args)
130 (if (elmo-split-eval buffer arg)
134 (defun elmo-split-and (buffer &rest args)
137 (unless (elmo-split-eval buffer arg)
141 (defun elmo-split-address-equal (buffer field value)
142 (with-current-buffer buffer
144 'std11-address-string
145 (std11-parse-addresses-string
146 (std11-field-body (symbol-name field)))))
150 (when (string-match (concat "^"
155 (setq addrs (cdr addrs)))
158 (defun elmo-split-address-match (buffer field value)
159 (with-current-buffer buffer
161 'std11-address-string
162 (std11-parse-addresses-string
163 (std11-field-body (symbol-name field)))))
166 (when (string-match value (car addrs))
167 (setq elmo-split-match-string-internal (car addrs)
170 (setq addrs (cdr addrs)))
173 (defun elmo-split-fetch-decoded-field (entity field-name)
174 (let ((sym (intern (capitalize field-name)))
175 (field-body (mime-entity-fetch-field entity field-name)))
177 (mime-decode-field-body field-body sym 'plain))))
179 (defun elmo-split-equal (buffer field value)
180 (with-current-buffer buffer
181 (let ((field-value (and
182 elmo-split-message-entity
183 (elmo-split-fetch-decoded-field
184 elmo-split-message-entity
185 (symbol-name field)))))
186 (equal field-value value))))
188 (defun elmo-split-match (buffer field value)
189 (with-current-buffer buffer
190 (let ((field-value (and elmo-split-message-entity
191 (elmo-split-fetch-decoded-field
192 elmo-split-message-entity
193 (symbol-name field)))))
195 (when (string-match value field-value)
196 (setq elmo-split-match-string-internal field-value))))))
198 (defun elmo-split-eval (buffer sexp)
201 (apply (intern (concat "elmo-split-" (symbol-name (car sexp))))
205 (std11-field-body sexp))
208 (defun elmo-split-log (message reharsal)
209 (with-current-buffer (get-buffer-create "*elmo-split*")
210 (goto-char (point-max))
211 (let ((start (point))
212 (coding-system-for-write elmo-split-log-coding-system))
216 (pop-to-buffer (current-buffer))
218 (write-region start (point) elmo-split-log-file t 'no-msg)))))
221 (defun elmo-split (&optional arg)
222 "Split messages in the `elmo-split-folder' according to `elmo-split-rule'.
223 If prefix argument ARG is specified, do a reharsal (no harm)."
225 (unless elmo-split-rule
226 (error "Split rule does not exist. Set `elmo-split-rule' first"))
227 (let ((folders (if (listp elmo-split-folder)
229 (list elmo-split-folder)))
233 (dolist (folder folders)
234 (setq ret (elmo-split-subr (elmo-make-folder folder) arg)
235 count (+ count (car ret))
236 fcount (+ fcount (cdr ret))))
237 (run-hooks 'elmo-split-hook)
242 "No message is splitted")
244 "1 message is splitted")
246 (format "%d messages are splitted" count)))
249 (format " (%d failure)." fcount))))))
251 (defun elmo-split-subr (folder &optional reharsal)
252 (let ((elmo-inhibit-display-retrieval-progress t)
255 msgs fname target-folder failure)
256 (message "Splitting...")
257 (elmo-folder-open-internal folder)
258 (setq msgs (elmo-folder-list-messages folder))
259 (elmo-progress-set 'elmo-split (length msgs) "Splitting...")
266 (elmo-message-fetch folder msg
267 (elmo-make-fetch-strategy 'entire)
268 nil (current-buffer) 'unread))
269 (setq elmo-split-message-entity (mime-parse-buffer))
271 (dolist (rule elmo-split-rule)
272 (setq elmo-split-match-string-internal nil)
273 (when (elmo-split-eval (current-buffer) (car rule))
274 (if elmo-split-match-string-internal
275 (setq fname (elmo-expand-newtext
277 elmo-split-match-string-internal))
278 (setq fname (nth 1 rule)))
283 (setq target-folder (elmo-make-folder fname))
284 (unless (elmo-folder-exists-p target-folder)
287 (elmo-folder-creatable-p
291 "Folder %s does not exist, Create it? "
293 (elmo-folder-create target-folder)))
294 (elmo-folder-open-internal target-folder)
295 (elmo-folder-append-buffer target-folder 'unread)
296 (elmo-folder-close-internal target-folder))
297 (error (setq failure t)
301 (elmo-folder-delete-messages folder (list msg))))
305 (nth 1 (std11-extract-address-components
306 (or (std11-field-body "from") "")))
307 " " (or (std11-field-body "date") "") "\n"
309 (eword-decode-string (or (std11-field-body
317 (unless (eq (nth 2 rule) 'continue)
318 (throw 'terminate nil))))))
319 (elmo-progress-notify 'elmo-split)))
320 (elmo-folder-close-internal folder))
321 (elmo-progress-clear 'elmo-split))
322 (cons count fcount)))
325 (product-provide (provide 'elmo-split) (require 'elmo-version))
327 ;;; elmo-split.el ends here