1 ;;; nnmbox.el --- mail mbox access for Gnus
2 ;; Copyright (C) 1995 Free Software Foundation, Inc.
4 ;; Author: Lars Ingebrigtsen <larsi@ifi.uio.no>
5 ;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6 ;; Keywords: news, mail
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs 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 ;; GNU Emacs 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
22 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
32 (defvar nnmbox-mbox-file (expand-file-name "~/mbox")
33 "The name of the mail box file in the users home directory.")
35 (defvar nnmbox-active-file (expand-file-name "~/.mbox-active")
36 "The name of the active file for the mail box.")
38 (defvar nnmbox-get-new-mail t
39 "If non-nil, nnml will check the incoming mail file and split the mail.")
43 (defconst nnmbox-version "nnmbox 0.1"
46 (defvar nnmbox-current-group nil
47 "Current nnmbox news group directory.")
49 (defconst nnmbox-mbox-buffer " *nnmbox mbox buffer*")
51 (defvar nnmbox-status-string "")
53 (defvar nnmbox-group-alist nil)
55 ;;; Interface functions
57 (defun nnmbox-retrieve-headers (sequence &optional newsgroup server)
58 "Retrieve the headers for the articles in SEQUENCE.
59 Newsgroup must be selected before calling this function."
61 (set-buffer nntp-server-buffer)
64 (number (length sequence))
66 beg article art-string start stop)
67 (nnmbox-possibly-change-newsgroup newsgroup)
69 (setq article (car sequence))
70 (setq art-string (nnmbox-article-string article))
71 (set-buffer nnmbox-mbox-buffer)
72 (if (or (search-forward art-string nil t)
74 (search-forward art-string nil t)))
79 (concat "^" rmail-unix-mail-delimiter) nil t)
81 (search-forward "\n\n" nil t)
82 (setq stop (1- (point)))
83 (set-buffer nntp-server-buffer)
84 (insert (format "221 %d Article retrieved.\n" article))
86 (insert-buffer-substring nnmbox-mbox-buffer start stop)
87 (goto-char (point-max))
89 (setq sequence (cdr sequence))
90 (setq count (1+ count))
91 (and (numberp nnmail-large-newsgroup)
92 (> number nnmail-large-newsgroup)
95 (message "nnmbox: Receiving headers... %d%%"
96 (/ (* count 100) number))))
98 (and (numberp nnmail-large-newsgroup)
99 (> number nnmail-large-newsgroup)
100 gnus-verbose-backends
101 (message "nnmbox: Receiving headers... done"))
103 ;; Fold continuation lines.
105 (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
106 (replace-match " " t t))
109 (defun nnmbox-open-server (host &optional service)
111 (setq nnmbox-status-string "")
112 (setq nnmbox-group-alist nil)
113 (nnheader-init-server-buffer))
115 (defun nnmbox-close-server (&optional server)
119 (defun nnmbox-server-opened (&optional server)
120 "Return server process status, T or NIL.
121 If the stream is opened, return T, otherwise return NIL."
122 (and nntp-server-buffer
123 (get-buffer nntp-server-buffer)))
125 (defun nnmbox-status-message ()
126 "Return server status response as string."
127 nnmbox-status-string)
129 (defun nnmbox-request-article (article &optional newsgroup server buffer)
130 "Select ARTICLE by number."
131 (nnmbox-possibly-change-newsgroup newsgroup)
132 (if (stringp article)
135 (set-buffer nnmbox-mbox-buffer)
137 (if (search-forward (nnmbox-article-string article) nil t)
139 (re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
142 (or (and (re-search-forward
143 (concat "^" rmail-unix-mail-delimiter) nil t)
145 (goto-char (point-max)))
147 (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
148 (set-buffer nntp-server-buffer)
150 (insert-buffer-substring nnmbox-mbox-buffer start stop)
153 (defun nnmbox-request-group (group &optional server dont-check)
156 (if (nnmbox-possibly-change-newsgroup group)
159 (nnmbox-get-new-mail)
161 (set-buffer nntp-server-buffer)
163 (let ((active (assoc group nnmbox-group-alist)))
164 (insert (format "211 %d %d %d %s\n"
165 (1+ (- (cdr (car (cdr active)))
166 (car (car (cdr active)))))
167 (car (car (cdr active)))
168 (cdr (car (cdr active)))
172 (defun nnmbox-close-group (group &optional server)
175 (defun nnmbox-request-list (&optional server)
176 "List active newsgoups."
177 (if server (nnmbox-get-new-mail))
178 (nnmail-find-file nnmbox-active-file))
180 (defun nnmbox-request-newgroups (date &optional server)
181 "List groups created after DATE."
182 (nnmbox-request-list server))
184 (defun nnmbox-request-list-newsgroups (&optional server)
185 "List newsgroups (defined in NNTP2)."
186 (setq nnmbox-status-string "nnmbox: LIST NEWSGROUPS is not implemented.")
189 (defun nnmbox-request-post (&optional server)
190 "Post a new news in current buffer."
191 (mail-send-and-exit nil))
193 (fset 'nnmbox-request-post-buffer 'nnmail-request-post-buffer)
195 (defun nnmbox-request-expire-articles (articles newsgroup &optional server force)
196 "Expire all articles in the ARTICLES list in group GROUP.
197 The list of unexpired articles will be returned (ie. all articles that
198 were too fresh to be expired).
199 If FORCE is non-nil, the ARTICLES will be deleted without looking at
201 (nnmbox-possibly-change-newsgroup newsgroup)
202 (let* ((days (or (and nnmail-expiry-wait-function
203 (funcall nnmail-expiry-wait-function newsgroup))
207 (set-buffer nnmbox-mbox-buffer)
210 (if (search-forward (nnmbox-article-string (car articles)) nil t)
212 (> (nnmail-days-between
213 (current-time-string)
215 (point) (progn (end-of-line) (point))))
218 (and gnus-verbose-backends
219 (message "Deleting: %s" (car articles)))
220 (nnmbox-delete-mail))
221 (setq rest (cons (car articles) rest))))
222 (setq articles (cdr articles)))
226 (defun nnmbox-request-move-article (article group server accept-form)
227 (nnmbox-possibly-change-newsgroup group)
228 (let ((buf (get-buffer-create " *nnmbox move*"))
231 (nnmbox-request-article article group server)
234 (insert-buffer-substring nntp-server-buffer)
235 (goto-char (point-min))
236 (if (re-search-forward
238 (save-excursion (search-forward "\n\n" nil t) (point)) t)
239 (delete-region (progn (beginning-of-line) (point))
240 (progn (forward-line 1) (point))))
241 (setq result (eval accept-form))
242 (kill-buffer (current-buffer))
245 (set-buffer nnmbox-mbox-buffer)
247 (if (search-forward (nnmbox-article-string article) nil t)
248 (nnmbox-delete-mail))
252 (defun nnmbox-request-accept-article (group)
253 (let ((buf (current-buffer))
256 (setq nnmbox-group-alist (nnmail-get-active))
258 (set-buffer nnmbox-mbox-buffer)
259 (setq beg (goto-char (point-max)))
260 (insert-buffer-substring buf)
264 (search-forward "\n\n" nil t)
267 (while (re-search-backward "^X-Gnus-Newsgroup: " beg t)
268 (delete-region (point) (progn (forward-line 1) (point)))))
269 (setq result (nnmbox-insert-newsgroup-line group)))
270 (setq result (nnmbox-save-mail)))
273 (nnmail-save-active nnmbox-group-alist nnmbox-active-file))
276 (defun nnmbox-request-replace-article (article group buffer)
277 (nnmbox-possibly-change-newsgroup group)
279 (set-buffer nnmbox-mbox-buffer)
281 (if (not (search-forward (nnmbox-article-string article) nil t))
283 (nnmbox-delete-mail t t)
284 (insert-buffer-substring buffer)
289 ;;; Low-Level Interface
291 (defun nnmbox-delete-mail (&optional force leave-delim)
292 "If FORCE, delete article no matter how many X-Gnus-Newsgroup
293 headers there are. If LEAVE-DELIM, don't delete the Unix mbox
295 ;; Delete the current X-Gnus-Newsgroup line.
298 (progn (beginning-of-line) (point))
299 (progn (forward-line 1) (point))))
300 ;; Beginning of the article.
305 (re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
306 (if leave-delim (progn (forward-line 1) (point))
307 (match-beginning 0)))
310 (or (and (re-search-forward (concat "^" rmail-unix-mail-delimiter)
312 (if (and (not (bobp)) leave-delim)
313 (progn (forward-line -2) (point))
314 (match-beginning 0)))
316 (goto-char (point-min))
317 ;; Only delete the article if no other groups owns it as well.
318 (if (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
319 (delete-region (point-min) (point-max))))))
321 (defun nnmbox-possibly-change-newsgroup (newsgroup)
322 (if (not (get-buffer nnmbox-mbox-buffer))
324 (set-buffer (setq nnmbox-mbox-buffer
325 (find-file-noselect nnmbox-mbox-file)))
326 (buffer-disable-undo (current-buffer))))
327 (if (not nnmbox-group-alist)
328 (setq nnmbox-group-alist (nnmail-get-active)))
330 (if (assoc newsgroup nnmbox-group-alist)
331 (setq nnmbox-current-group newsgroup))))
333 (defun nnmbox-article-string (article)
334 (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"
335 (int-to-string article)))
337 (defun nnmbox-save-mail ()
338 "Called narrowed to an article."
339 (let ((group-art (nreverse (nnmail-article-group 'nnmbox-active-number))))
340 (nnmail-insert-lines)
341 (nnmail-insert-xref group-art)
342 (nnmbox-insert-newsgroup-line group-art)))
344 (defun nnmbox-insert-newsgroup-line (group-art)
346 (goto-char (point-min))
347 (if (search-forward "\n\n" nil t)
351 (insert (format "X-Gnus-Newsgroup: %s:%d %s\n"
352 (car (car group-art)) (cdr (car group-art))
353 (current-time-string)))
354 (setq group-art (cdr group-art)))))))
356 (defun nnmbox-active-number (group)
357 "Find the next article number in GROUP."
358 (let ((active (car (cdr (assoc group nnmbox-group-alist)))))
359 (setcdr active (1+ (cdr active)))
362 (defun nnmbox-read-mbox ()
363 (nnmbox-request-list)
364 (setq nnmbox-group-alist (nnmail-get-active))
365 (if (not (file-exists-p nnmbox-mbox-file))
366 (write-region 1 1 nnmbox-mbox-file t 'nomesg))
367 (if (and nnmbox-mbox-buffer
368 (get-buffer nnmbox-mbox-buffer)
369 (buffer-name nnmbox-mbox-buffer)
371 (set-buffer nnmbox-mbox-buffer)
372 (= (buffer-size) (nth 7 (file-attributes nnmbox-mbox-file)))))
375 (let ((delim (concat "^" rmail-unix-mail-delimiter))
377 (set-buffer (setq nnmbox-mbox-buffer
378 (find-file-noselect nnmbox-mbox-file)))
379 (buffer-disable-undo (current-buffer))
380 (goto-char (point-min))
381 (while (re-search-forward delim nil t)
382 (setq start (match-beginning 0))
383 (if (not (search-forward "\nX-Gnus-Newsgroup: "
388 (re-search-forward delim nil t)
394 (narrow-to-region start end)
395 (nnmbox-save-mail))))
398 (defun nnmbox-get-new-mail ()
401 (if (and nnmail-spool-file
402 (file-exists-p nnmail-spool-file)
403 (> (nth 7 (file-attributes nnmail-spool-file)) 0))
405 (and gnus-verbose-backends
406 (message "nnmbox: Reading incoming mail..."))
408 (nnmail-move-inbox nnmail-spool-file
409 (concat nnmbox-mbox-file "-Incoming")))
411 (let ((in-buf (nnmail-split-incoming
412 incoming 'nnmbox-save-mail t)))
413 (set-buffer nnmbox-mbox-buffer)
414 (goto-char (point-max))
415 (insert-buffer-substring in-buf)
416 (kill-buffer in-buf)))
417 (run-hooks 'nnmail-read-incoming-hook)
418 (and gnus-verbose-backends
419 (message "nnmbox: Reading incoming mail...done"))))
420 (and (buffer-modified-p nnmbox-mbox-buffer)
422 (nnmail-save-active nnmbox-group-alist nnmbox-active-file)
423 (set-buffer nnmbox-mbox-buffer)
426 ; (delete-file incoming))
431 ;;; nnmbox.el ends here