;;; nndiary.el --- A diary backend for Gnus
-;; Copyright (C) 1999-2001 Didier Verna.
-
-;; PRCS: $Id: nndiary.el 1.22 Tue, 04 Sep 2001 11:32:13 +0200 didier $
+;; Copyright (C) 1999, 2000, 2001
+;; Free Software Foundation, Inc.
;; Author: Didier Verna <didier@xemacs.org>
;; Maintainer: Didier Verna <didier@xemacs.org>
;; Created: Fri Jul 16 18:55:42 1999
-;; Last Revision: Wed Aug 8 17:36:21 2001
;; Keywords: calendar mail news
-;; This file is part of NNDiary.
+;; This file is part of GNU Emacs.
-;; NNDiary is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
-;; NNDiary is distributed in the hope that it will be useful,
+;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; Usage:
;; =====
-;; 1/ Diary messages contain several `X-Diary-*' special headers. You *must*
-;; arrange that these messages be split in a private folder *before* Gnus
-;; treat them. You need this because Gnus is not able yet to manage
-;; multiple backends for mail retrieval. Getting them from a separate
-;; source will compensate this misfeature to some extent, as we will see.
-;; As an example, here's my procmailrc entry to store diary files in
-;; ~/.nndiary (the default nndiary mail source file):
-;;
-;; :0 HD :
-;; * ^X-Diary
-;; .nndiary
+;; 1/ NNDiary has two modes of operation: traditional (the default) and
+;; autonomous.
+;; a/ In traditional mode, NNDiary does not get new mail by itself. You
+;; have to move mails from your primary mail backend to nndiary
+;; groups.
+;; b/ In autonomous mode, NNDiary retrieves its own mail and handles it
+;; independantly of your primary mail backend. To use NNDiary in
+;; autonomous mode, you have several things to do:
+;; i/ Put (setq nndiary-get-new-mail t) in your gnusrc file.
+;; ii/ Diary messages contain several `X-Diary-*' special headers.
+;; You *must* arrange that these messages be split in a private
+;; folder *before* Gnus treat them. You need this because Gnus
+;; is not able yet to manage multiple backends for mail
+;; retrieval. Getting them from a separate source will
+;; compensate this misfeature to some extent, as we will see.
+;; As an example, here's my procmailrc entry to store diary files
+;; in ~/.nndiary (the default nndiary mail source file):
;;
+;; :0 HD :
+;; * ^X-Diary
+;; .nndiary
+;; iii/ Customize the variables `nndiary-mail-sources' and
+;; `nndiary-split-methods'. These are replacements for the usual
+;; mail sources and split methods which, and will be used in
+;; autonomous mode. `nndiary-mail-sources' defaults to
+;; '(file :path "~/.nndiary").
;; 2/ Install nndiary somewhere Emacs / Gnus can find it. Normally, you
;; *don't* have to '(require 'nndiary) anywhere. Gnus will do so when
;; appropriate as long as nndiary is somewhere in the load path.
-;; 3/ Now, customize nndiary: type `M-x customize-group', and then `nndiary'
-;; at the prompt (note that if you have not restarted Emacs yet, you'll
-;; have to the load the library by hand before being able to customize it).
-;; In particular, you should customize the following options:
-;; - `nndiary-mail-sources', which overrides the normal `mail-sources'
-;; value for diary messages retrieving. It defaults to
-;; '(file :path "~/.nndiary").
-;; - `nndiary-split-methods', which overrides the normal
-;; `nnmail-split-methods' value for diary messages splitting. You can
-;; have all the diary groups you want (for example, I have a birthdays
-;; group, and stuff like that).
-;; - `nndiary-reminders', the list of times when you want to be reminded
-;; of your appointements (e.g. 3 weeks before, then 2 days before, then
-;; 1 hour before and that's it).
+;; 3/ Now, customize the rest of nndiary. In particular, you should
+;; customize `nndiary-reminders', the list of times when you want to be
+;; reminded of your appointements (e.g. 3 weeks before, then 2 days
+;; before, then 1 hour before and that's it).
;; 4/ You *must* use the group timestamp feature of Gnus. This adds a
;; timestamp to each groups' parameters (please refer to the Gnus
;; documentation ("Group Timestamp" info node) to see how it's done.
;; * Respooling doesn't work because contrary to the request-scan function,
;; Gnus won't allow me to override the split methods when calling the
;; respooling backend functions.
-;; * The time zone mechanism is subject to change.
+;; * There's a bug in the time zone mechanism with variable TZ locations.
;; * We could allow a keyword like `ask' in X-Diary-* headers, that would mean
-;; "ask for value upon reception of the message". Suggested by Jody Klymak.
+;; "ask for value upon reception of the message".
;; * We could add an optional header X-Diary-Reminders to specify a special
;; reminders value for this message. Suggested by Jody Klymak.
-;; * Modify the request-accept-article function to make it prompt for diary
-;; headers if they're missing.
+;; * We should check messages validity in other circumstances than just
+;; moving an article from sonwhere else (request-accept). For instance, when
+;; editing / saving and so on.
+
;; Remarks:
;; =======
;; Compatibility Functions =================================================
-(if (fboundp 'signal-error)
+(eval-and-compile
+ (if (fboundp 'signal-error)
+ (defun nndiary-error (&rest args)
+ (apply #'signal-error 'nndiary args))
(defun nndiary-error (&rest args)
- (apply #'signal-error 'nndiary args))
- (defun nndiary-error (&rest args)
- (apply #'error args)))
+ (apply #'error args))))
;; Backend behavior customization ===========================================
`((file :path ,(expand-file-name "~/.nndiary")))
"*NNDiary specific mail sources.
This variable is used by nndiary in place of the standard `mail-sources'
-variable. These sources must contain diary messages ONLY."
+variable when `nndiary-get-new-mail' is set to non-nil. These sources
+must contain diary messages ONLY."
:group 'nndiary
:group 'mail-source
:type 'sexp)
(defcustom nndiary-split-methods '(("diary" ""))
"*NNDiary specific split methods.
This variable is used by nndiary in place of the standard
-`nnmail-split-methods' variable."
+`nnmail-split-methods' variable when `nndiary-get-new-mail' is set to
+non-nil."
:group 'nndiary
:group 'nnmail-split
:type '(choice (repeat :tag "Alist" (group (string :tag "Name") regexp))
:group 'nndiary
:type 'hook)
+(defcustom nndiary-request-accept-article-hooks nil
+ "*Hooks to run before accepting an article.
+Executed near the beginning of `nndiary-request-accept-article'.
+The hooks will be called with the article in the current buffer."
+ :group 'nndiary
+ :type 'hook)
+
(defcustom nndiary-check-directory-twice t
"*If t, check directories twice to avoid NFS failures."
:group 'nndiary
(expand-file-name "newsgroups" nndiary-directory)
"Newsgroups description file for the nndiary backend.")
-(defvoo nndiary-get-new-mail t
+(defvoo nndiary-get-new-mail nil
"Whether nndiary gets new mail and split it.
-Contrary to traditional mail backends, this variable should always be
-non-nil because nndiary uses its own mail-sources and split-methods.")
+Contrary to traditional mail backends, this variable can be set to t
+even if your primary mail backend also retreives mail. In such a case,
+NDiary uses its own mail-sources and split-methods.")
(defvoo nndiary-nov-is-evil nil
"If non-nil, Gnus will never use nov databases for nndiary groups.
\f
-;; $Format: "(defconst nndiary-prcs-major-version \"$ProjectMajorVersion$\")"$
-(defconst nndiary-prcs-major-version "branch-0-2")
-;; $Format: "(defconst nndiary-prcs-minor-version \"$ProjectMinorVersion$\")"$
-(defconst nndiary-prcs-minor-version "1")
-(defconst nndiary-version
- (let ((level nndiary-prcs-minor-version)
- major minor status)
- (string-match "\\(branch\\|version\\)-\\([0-9]+\\)-\\([0-9]+\\)"
- nndiary-prcs-major-version)
- (setq major (match-string 2 nndiary-prcs-major-version)
- minor (match-string 3 nndiary-prcs-major-version)
- status (match-string 1 nndiary-prcs-major-version))
- (cond ((string= status "version")
- (setq level (int-to-string (1- (string-to-int level))))
- (if (eq level 0)
- (concat major "." minor)
- (concat major "." minor "." level)))
- ((string= status "branch")
- (concat major "." minor "-b" level))))
+(defconst nndiary-version "0.2-b14"
"Current Diary backend version.")
(defun nndiary-version ()
(interactive)
(message "NNDiary version %s" nndiary-version))
-
(defvoo nndiary-nov-file-name ".overview")
(defvoo nndiary-current-directory nil)
;; the (relative) number of seconds ahead GMT.
)
+(defsubst nndiary-schedule ()
+ (let (head)
+ (condition-case arg
+ (mapcar
+ (lambda (elt)
+ (setq head (nth 0 elt))
+ (nndiary-parse-schedule (nth 0 elt) (nth 1 elt) (nth 2 elt)))
+ nndiary-headers)
+ (t
+ (nnheader-report 'nndiary "X-Diary-%s header parse error: %s."
+ head (cdr arg))
+ nil))
+ ))
;;; Interface functions =====================================================
(with-temp-buffer
(nndiary-request-article number group server (current-buffer))
(let ((nndiary-current-directory nil))
- (nnmail-expiry-target-group nnmail-expiry-target group))))
+ (nnmail-expiry-target-group nnmail-expiry-target group)))
+ (nndiary-possibly-change-directory group server))
(nnheader-message 5 "Deleting article %s in %s" number group)
(condition-case ()
(funcall nnmail-delete-file-function article)
(deffoo nndiary-request-accept-article (group &optional server last)
(nndiary-possibly-change-directory group server)
(nnmail-check-syntax)
- (let (result)
- (when nnmail-cache-accepted-message-ids
- (nnmail-cache-insert (nnmail-fetch-field "message-id")))
- (if (stringp group)
+ (run-hooks 'nndiary-request-accept-article-hooks)
+ (when (nndiary-schedule)
+ (let (result)
+ (when nnmail-cache-accepted-message-ids
+ (nnmail-cache-insert (nnmail-fetch-field "message-id") group))
+ (if (stringp group)
+ (and
+ (nnmail-activate 'nndiary)
+ (setq result
+ (car (nndiary-save-mail
+ (list (cons group (nndiary-active-number group))))))
+ (progn
+ (nnmail-save-active nndiary-group-alist nndiary-active-file)
+ (and last (nndiary-save-nov))))
(and
(nnmail-activate 'nndiary)
- (setq result
- (car (nndiary-save-mail
- (list (cons group (nndiary-active-number group))))))
- (progn
+ (if (and (not (setq result
+ (nnmail-article-group 'nndiary-active-number)))
+ (yes-or-no-p "Moved to `junk' group; delete article? "))
+ (setq result 'junk)
+ (setq result (car (nndiary-save-mail result))))
+ (when last
(nnmail-save-active nndiary-group-alist nndiary-active-file)
- (and last (nndiary-save-nov))))
- (and
- (nnmail-activate 'nndiary)
- (if (and (not (setq result
- (nnmail-article-group 'nndiary-active-number)))
- (yes-or-no-p "Moved to `junk' group; delete article? "))
- (setq result 'junk)
- (setq result (car (nndiary-save-mail result))))
- (when last
- (nnmail-save-active nndiary-group-alist nndiary-active-file)
- (when nnmail-cache-accepted-message-ids
- (nnmail-cache-close))
- (nndiary-save-nov))))
- result))
+ (when nnmail-cache-accepted-message-ids
+ (nnmail-cache-close))
+ (nndiary-save-nov))))
+ result))
+ )
(deffoo nndiary-request-post (&optional server)
(nnmail-do-request-post 'nndiary-request-accept-article server))
(narrow-to-region
(goto-char (point-min))
(if (search-forward "\n\n" nil t) (1- (point)) (point-max))))
- ;; Fold continuation lines.
- (goto-char (point-min))
- (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
- (replace-match " " t t))
- ;; Remove any tabs; they are too confusing.
- (subst-char-in-region (point-min) (point-max) ?\t ? )
- (let ((headers (nnheader-parse-head t)))
+ (let ((headers (nnheader-parse-naked-head)))
(mail-header-set-chars headers chars)
(mail-header-set-number headers number)
headers))))
val)))
(defun nndiary-parse-schedule-value (str min-or-values max)
- ;; Parse the schedule string STR.
+ ;; Parse the schedule string STR, or signal an error.
;; Signals are caught by `nndary-schedule'.
(if (string-match "[ \t]*\\*[ \t]*" str)
;; unspecifyed
(nndiary-parse-schedule-value (match-string 1) min-or-values max))
))
-(defsubst nndiary-schedule ()
- (mapcar
- (lambda (elt)
- (condition-case arg
- (nndiary-parse-schedule (nth 0 elt) (nth 1 elt) (nth 2 elt))
- (t
- (nnheader-report 'nndiary "X-Diary-%s header parse error: %s."
- (car elt) (cdr arg))
- nil)))
- nndiary-headers))
-
(defun nndiary-max (spec)
;; Returns the max of specification SPEC, or nil for permanent schedules.
(unless (null spec)
(nth 6 date-elts))))
reminder res)
;; remove the DOW and DST entries
- (setf (nthcdr 6 date-elts) (nthcdr 8 date-elts))
+ (setcdr (nthcdr 5 date-elts) (nthcdr 8 date-elts))
(while (setq reminder (pop reminders))
(push
(cond ((eq (cdr reminder) 'minute)