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 (nnmbox-open-server-internal host service))
114 (defun nnmbox-close-server (&optional server)
116 (nnmbox-close-server-internal))
118 (fset 'nnmbox-request-quit (symbol-function 'nnmbox-close-server))
120 (defun nnmbox-server-opened (&optional server)
121 "Return server process status, T or NIL.
122 If the stream is opened, return T, otherwise return NIL."
123 (and nntp-server-buffer
124 (get-buffer nntp-server-buffer)))
126 (defun nnmbox-status-message ()
127 "Return server status response as string."
128 nnmbox-status-string)
130 (defun nnmbox-request-article (article &optional newsgroup server buffer)
131 "Select ARTICLE by number."
132 (nnmbox-possibly-change-newsgroup newsgroup)
133 (if (stringp article)
136 (set-buffer nnmbox-mbox-buffer)
138 (if (search-forward (nnmbox-article-string article) nil t)
140 (re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
143 (or (and (re-search-forward
144 (concat "^" rmail-unix-mail-delimiter) nil t)
146 (goto-char (point-max)))
148 (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
149 (set-buffer nntp-server-buffer)
151 (insert-buffer-substring nnmbox-mbox-buffer start stop)
154 (defun nnmbox-request-group (group &optional server dont-check)
157 (if (nnmbox-possibly-change-newsgroup group)
160 (nnmbox-get-new-mail)
162 (set-buffer nntp-server-buffer)
164 (let ((active (assoc group nnmbox-group-alist)))
165 (insert (format "211 %d %d %d %s\n"
166 (1+ (- (cdr (car (cdr active)))
167 (car (car (cdr active)))))
168 (car (car (cdr active)))
169 (cdr (car (cdr active)))
173 (defun nnmbox-close-group (group &optional server)
176 (defun nnmbox-request-list (&optional server)
177 "List active newsgoups."
178 (if server (nnmbox-get-new-mail))
179 (nnmail-find-file nnmbox-active-file))
181 (defun nnmbox-request-newgroups (date &optional server)
182 "List groups created after DATE."
183 (nnmbox-request-list server))
185 (defun nnmbox-request-list-newsgroups (&optional server)
186 "List newsgroups (defined in NNTP2)."
187 (setq nnmbox-status-string "nnmbox: LIST NEWSGROUPS is not implemented.")
190 (defun nnmbox-request-post (&optional server)
191 "Post a new news in current buffer."
192 (mail-send-and-exit nil))
194 (fset 'nnmbox-request-post-buffer 'nnmbox-request-post-buffer)
196 (defun nnmbox-request-expire-articles (articles newsgroup &optional server force)
197 "Expire all articles in the ARTICLES list in group GROUP.
198 The list of unexpired articles will be returned (ie. all articles that
199 were too fresh to be expired).
200 If FORCE is non-nil, the ARTICLES will be deleted without looking at
202 (nnmbox-possibly-change-newsgroup newsgroup)
203 (let* ((days (or (and nnmail-expiry-wait-function
204 (funcall nnmail-expiry-wait-function newsgroup))
208 (set-buffer nnmbox-mbox-buffer)
211 (if (search-forward (nnmbox-article-string (car articles)) nil t)
213 (> (nnmail-days-between
214 (current-time-string)
216 (point) (progn (end-of-line) (point))))
219 (and gnus-verbose-backends
220 (message "Deleting: %s" (car articles)))
221 (nnmbox-delete-mail))
222 (setq rest (cons (car articles) rest))))
223 (setq articles (cdr articles)))
227 (defun nnmbox-request-move-article (article group server accept-form)
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))
277 ;;; Low-Level Interface
279 (defun nnmbox-delete-mail ()
280 ;; Delete the current X-Gnus-Newsgroup line.
282 (progn (beginning-of-line) (point))
283 (progn (forward-line 1) (point)))
284 ;; Beginning of the article.
289 (re-search-backward (concat "^" rmail-unix-mail-delimiter) nil t)
292 (re-search-forward (concat "^" rmail-unix-mail-delimiter) nil t)
293 (match-beginning 0)))
294 (goto-char (point-min))
295 ;; Only delete the article if no other groups owns it as well.
296 (if (not (re-search-forward "^X-Gnus-Newsgroup: " nil t))
297 (delete-region (point-min) (point-max))))))
299 (defun nnmbox-open-server-internal (host &optional service)
300 "Open connection to news server on HOST by SERVICE (default is nntp)."
302 (if (not (string-equal host (system-name)))
303 (error "nnmbox: cannot talk to %s." host))
304 ;; Initialize communication buffer.
305 (setq nntp-server-buffer (get-buffer-create " *nntpd*"))
306 (set-buffer nntp-server-buffer)
307 (buffer-disable-undo (current-buffer))
309 (kill-all-local-variables)
310 (setq case-fold-search t) ;Should ignore case.
311 (setq nnmbox-group-alist nil)
314 (defun nnmbox-close-server-internal ()
315 "Close connection to news server."
318 (defun nnmbox-possibly-change-newsgroup (newsgroup)
319 (if (not (get-buffer nnmbox-mbox-buffer))
321 (set-buffer (setq nnmbox-mbox-buffer
322 (find-file-noselect nnmbox-mbox-file)))
323 (buffer-disable-undo (current-buffer))))
324 (if (not nnmbox-group-alist)
325 (setq nnmbox-group-alist (nnmail-get-active)))
327 (if (assoc newsgroup nnmbox-group-alist)
328 (setq nnmbox-current-group newsgroup))))
330 (defun nnmbox-article-string (article)
331 (concat "\nX-Gnus-Newsgroup: " nnmbox-current-group ":"
332 (int-to-string article)))
334 (defun nnmbox-save-mail ()
335 "Called narrowed to an article."
336 (let ((group-art (nreverse (nnmail-article-group 'nnmbox-active-number))))
337 (nnmail-insert-lines)
338 (nnmail-insert-xref group-art)
339 (nnmbox-insert-newsgroup-line group-art)))
341 (defun nnmbox-insert-newsgroup-line (group-art)
343 (goto-char (point-min))
344 (if (search-forward "\n\n" nil t)
348 (insert (format "X-Gnus-Newsgroup: %s:%d %s\n"
349 (car (car group-art)) (cdr (car group-art))
350 (current-time-string)))
351 (setq group-art (cdr group-art)))))))
353 (defun nnmbox-active-number (group)
354 "Find the next article number in GROUP."
355 (let ((active (car (cdr (assoc group nnmbox-group-alist)))))
356 (setcdr active (1+ (cdr active)))
359 (defun nnmbox-read-mbox ()
360 (nnmbox-request-list)
361 (setq nnmbox-group-alist (nnmail-get-active))
362 (if (not (file-exists-p nnmbox-mbox-file))
363 (write-region 1 1 nnmbox-mbox-file t 'nomesg))
364 (if (and nnmbox-mbox-buffer
365 (get-buffer nnmbox-mbox-buffer)
366 (buffer-name nnmbox-mbox-buffer)
368 (set-buffer nnmbox-mbox-buffer)
369 (= (buffer-size) (nth 7 (file-attributes nnmbox-mbox-file)))))
372 (let ((delim (concat "^" rmail-unix-mail-delimiter))
374 (set-buffer (setq nnmbox-mbox-buffer
375 (find-file-noselect nnmbox-mbox-file)))
376 (buffer-disable-undo (current-buffer))
377 (goto-char (point-min))
378 (while (re-search-forward delim nil t)
379 (setq start (match-beginning 0))
380 (if (not (search-forward "\nX-Gnus-Newsgroup: "
385 (re-search-forward delim nil t)
391 (narrow-to-region start end)
392 (nnmbox-save-mail))))
395 (defun nnmbox-get-new-mail ()
398 (if (and nnmail-spool-file
399 (file-exists-p nnmail-spool-file)
400 (> (nth 7 (file-attributes nnmail-spool-file)) 0))
402 (and gnus-verbose-backends
403 (message "nnmbox: Reading incoming mail..."))
405 (nnmail-move-inbox nnmail-spool-file
406 (concat nnmbox-mbox-file "-Incoming")))
408 (let ((in-buf (nnmail-split-incoming
409 incoming 'nnmbox-save-mail t)))
410 (set-buffer nnmbox-mbox-buffer)
411 (goto-char (point-max))
412 (insert-buffer-substring in-buf)
413 (kill-buffer in-buf)))
414 (run-hooks 'nnmail-read-incoming-hook)
415 (and gnus-verbose-backends
416 (message "nnmbox: Reading incoming mail...done"))))
417 (and (buffer-modified-p nnmbox-mbox-buffer)
419 (nnmail-save-active nnmbox-group-alist nnmbox-active-file)
420 (set-buffer nnmbox-mbox-buffer)
423 ; (delete-file incoming))
428 ;;; nnmbox.el ends here