*** empty log message ***
[gnus] / lisp / nnbabyl.el
1 ;;; nnbabyl.el --- mail mbox access for Gnus
2 ;; Copyright (C) 1995 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
5 ;;      Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
6 ;; Keywords: news, mail
7
8 ;; This file is part of GNU Emacs.
9
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)
13 ;; any later version.
14
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.
19
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.
23
24 ;;; Commentary:
25
26 ;; For an overview of what the interface functions do, please see the
27 ;; Gnus sources.  
28
29 ;;; Code:
30
31 (require 'nnheader)
32 (require 'rmail)
33 (require 'nnmail)
34
35 (defvar nnbabyl-mbox-file (expand-file-name "~/RMAIL")
36   "The name of the rmail box file in the users home directory.")
37
38 (defvar nnbabyl-active-file (expand-file-name "~/.rmail-active")
39   "The name of the active file for the rmail box.")
40
41 (defvar nnbabyl-get-new-mail t
42   "If non-nil, nnbabyl will check the incoming mail file and split the mail.")
43
44 \f
45
46 (defvar nnbabyl-mail-delimiter "\^_")
47
48 (defconst nnbabyl-version "nnbabyl 0.1"
49   "nnbabyl version.")
50
51 (defconst nnbabyl-mbox-buffer " *nnbabyl mbox buffer*")
52
53 (defvar nnbabyl-current-group nil)
54 (defvar nnbabyl-status-string "")
55 (defvar nnbabyl-group-alist nil)
56
57 \f
58
59 (defvar nnbabyl-current-server nil)
60 (defvar nnbabyl-server-alist nil)
61 (defvar nnbabyl-server-variables 
62   (list
63    (list 'nnbabyl-mbox-file nnbabyl-mbox-file)
64    (list 'nnbabyl-active-file nnbabyl-active-file)
65    (list 'nnbabyl-get-new-mail nnbabyl-get-new-mail)
66    '(nnbabyl-current-group nil)
67    '(nnbabyl-status-string "")
68    '(nnbabyl-group-alist nil)))
69
70 \f
71
72 ;;; Interface functions
73
74 (defun nnbabyl-retrieve-headers (sequence &optional newsgroup server)
75   (save-excursion
76     (set-buffer nntp-server-buffer)
77     (erase-buffer)
78     (let ((file nil)
79           (number (length sequence))
80           (count 0)
81           beg article art-string start stop)
82       (nnbabyl-possibly-change-newsgroup newsgroup)
83       (while sequence
84         (setq article (car sequence))
85         (setq art-string (nnbabyl-article-string article))
86         (set-buffer nnbabyl-mbox-buffer)
87         (if (or (search-forward art-string nil t)
88                 (progn (goto-char 1)
89                        (search-forward art-string nil t)))
90             (progn
91               (setq start 
92                     (save-excursion
93                       (re-search-backward 
94                        (concat "^" nnbabyl-mail-delimiter) nil t)
95                       (while (and (not (looking-at ".+:"))
96                                   (zerop (forward-line 1))))
97                       (point)))
98               (search-forward "\n\n" nil t)
99               (setq stop (1- (point)))
100               (set-buffer nntp-server-buffer)
101               (insert (format "221 %d Article retrieved.\n" article))
102               (setq beg (point))
103               (insert-buffer-substring nnbabyl-mbox-buffer start stop)
104               (goto-char (point-max))
105               (insert ".\n")))
106         (setq sequence (cdr sequence))
107         (setq count (1+ count))
108         (and (numberp nnmail-large-newsgroup)
109              (> number nnmail-large-newsgroup)
110              (zerop (% count 20))
111              gnus-verbose-backends
112              (message "nnbabyl: Receiving headers... %d%%"
113                       (/ (* count 100) number))))
114
115       (and (numberp nnmail-large-newsgroup)
116            (> number nnmail-large-newsgroup)
117            gnus-verbose-backends
118            (message "nnbabyl: Receiving headers... done"))
119
120       ;; Fold continuation lines.
121       (goto-char 1)
122       (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
123         (replace-match " " t t))
124       'headers)))
125
126 (defun nnbabyl-open-server (server &optional defs)
127   (nnheader-init-server-buffer)
128   (if (equal server nnbabyl-current-server)
129       t
130     (if nnbabyl-current-server
131         (setq nnbabyl-server-alist 
132               (cons (list nnbabyl-current-server
133                           (nnheader-save-variables nnbabyl-server-variables))
134                     nnbabyl-server-alist)))
135     (let ((state (assoc server nnbabyl-server-alist)))
136       (if state 
137           (progn
138             (nnheader-restore-variables (nth 1 state))
139             (setq nnbabyl-server-alist (delq state nnbabyl-server-alist)))
140         (nnheader-set-init-variables nnbabyl-server-variables defs)))
141     (setq nnbabyl-current-server server)))
142
143 (defun nnbabyl-close-server (&optional server)
144   t)
145
146 (defun nnbabyl-server-opened (&optional server)
147   (and (equal server nnbabyl-current-server)
148        nntp-server-buffer
149        (buffer-name nntp-server-buffer)))
150
151 (defun nnbabyl-status-message (&optional server)
152   nnbabyl-status-string)
153
154 (defun nnbabyl-request-article (article &optional newsgroup server buffer)
155   (nnbabyl-possibly-change-newsgroup newsgroup)
156   (if (stringp article)
157       nil
158     (save-excursion
159       (set-buffer nnbabyl-mbox-buffer)
160       (goto-char 1)
161       (if (search-forward (nnbabyl-article-string article) nil t)
162           (let (start stop)
163             (re-search-backward (concat "^" nnbabyl-mail-delimiter) nil t)
164             (while (and (not (looking-at ".+:"))
165                         (zerop (forward-line 1))))
166             (setq start (point))
167             (or (and (re-search-forward 
168                       (concat "^" nnbabyl-mail-delimiter) nil t)
169                      (forward-line -1))
170                 (goto-char (point-max)))
171             (setq stop (point))
172             (let ((nntp-server-buffer (or buffer nntp-server-buffer)))
173               (set-buffer nntp-server-buffer)
174               (erase-buffer)
175               (insert-buffer-substring nnbabyl-mbox-buffer start stop)
176               (goto-char (point-min))
177               (if (search-forward "\n*** EOOH ***" nil t)
178                   (progn
179                     (delete-region (progn (beginning-of-line) (point))
180                                    (or (search-forward "\n\n" nil t)
181                                        (point)))))
182               t))))))
183
184 (defun nnbabyl-request-group (group &optional server dont-check)
185   (save-excursion
186     (if (nnbabyl-possibly-change-newsgroup group)
187         (if dont-check
188             t
189           (nnbabyl-get-new-mail)
190           (save-excursion
191             (set-buffer nntp-server-buffer)
192             (erase-buffer)
193             (let ((active (assoc group nnbabyl-group-alist)))
194               (insert (format "211 %d %d %d %s\n" 
195                               (1+ (- (cdr (car (cdr active)))
196                                      (car (car (cdr active)))))
197                               (car (car (cdr active)))
198                               (cdr (car (cdr active)))
199                               (car active))))
200             t)))))
201
202 (defun nnbabyl-close-group (group &optional server)
203   t)
204
205 (defun nnbabyl-request-list (&optional server)
206   (if server (nnbabyl-get-new-mail))
207   (save-excursion
208     (or (nnmail-find-file nnbabyl-active-file)
209         (progn
210           (setq nnbabyl-group-alist (nnmail-get-active))
211           (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
212           (nnmail-find-file nnbabyl-active-file)))))
213
214 (defun nnbabyl-request-newgroups (date &optional server)
215   (nnbabyl-request-list server))
216
217 (defun nnbabyl-request-list-newsgroups (&optional server)
218   (setq nnbabyl-status-string "nnbabyl: LIST NEWSGROUPS is not implemented.")
219   nil)
220
221 (defun nnbabyl-request-post (&optional server)
222   (mail-send-and-exit nil))
223
224 (fset 'nnbabyl-request-post-buffer 'nnmail-request-post-buffer)
225
226 (defun nnbabyl-request-expire-articles (articles newsgroup &optional server force)
227   (nnbabyl-possibly-change-newsgroup newsgroup)
228   (let* ((days (or (and nnmail-expiry-wait-function
229                         (funcall nnmail-expiry-wait-function newsgroup))
230                    nnmail-expiry-wait))
231          article rest)
232     (save-excursion 
233       (set-buffer nnbabyl-mbox-buffer)
234       (while articles
235         (goto-char 1)
236         (if (search-forward (nnbabyl-article-string (car articles)) nil t)
237             (if (or force
238                     (> (nnmail-days-between 
239                         (current-time-string)
240                         (buffer-substring 
241                          (point) (progn (end-of-line) (point))))
242                        days))
243                 (progn
244                   (and gnus-verbose-backends
245                        (message "Deleting: %s" (car articles)))
246                   (nnbabyl-delete-mail))
247               (setq rest (cons (car articles) rest))))
248         (setq articles (cdr articles)))
249       (save-buffer)
250       ;; Find the lowest active article in this group.
251       (let ((active (nth 1 (assoc newsgroup nnbabyl-group-alist))))
252         (goto-char (point-min))
253         (while (not (search-forward
254                      (nnbabyl-article-string (car active)) nil t))
255           (setcar (car active) (1+ (car active)))
256           (goto-char (point-min))))
257       (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
258       rest)))
259
260 (defun nnbabyl-request-move-article 
261   (article group server accept-form &optional last)
262   (nnbabyl-possibly-change-newsgroup group)
263   (let ((buf (get-buffer-create " *nnbabyl move*"))
264         result)
265     (and 
266      (nnbabyl-request-article article group server)
267      (save-excursion
268        (set-buffer buf)
269        (insert-buffer-substring nntp-server-buffer)
270        (goto-char (point-min))
271        (if (re-search-forward 
272             "^X-Gnus-Newsgroup:" 
273             (save-excursion (search-forward "\n\n" nil t) (point)) t)
274            (delete-region (progn (beginning-of-line) (point))
275                           (progn (forward-line 1) (point))))
276        (setq result (eval accept-form))
277        (kill-buffer (current-buffer))
278        result)
279      (save-excursion
280        (set-buffer nnbabyl-mbox-buffer)
281        (goto-char 1)
282        (if (search-forward (nnbabyl-article-string article) nil t)
283            (nnbabyl-delete-mail))
284        (and last (save-buffer))))
285     result))
286
287 (defun nnbabyl-request-accept-article (group &optional last)
288   (let ((buf (current-buffer))
289         result beg)
290     (and 
291      (setq nnbabyl-group-alist (nnmail-get-active))
292      (save-excursion
293        (set-buffer nnbabyl-mbox-buffer)
294        (setq beg (goto-char (point-max)))
295        (insert-buffer-substring buf)
296        (goto-char beg)
297        (if (stringp group)
298            (progn
299              (search-forward "\n\n" nil t)
300              (forward-line -1)
301              (save-excursion
302                (while (re-search-backward "^X-Gnus-Newsgroup: " beg t)
303                  (delete-region (point) (progn (forward-line 1) (point)))))
304              (setq result (nnbabyl-insert-newsgroup-line group)))
305          (setq result (nnbabyl-save-mail)))
306        (and last (save-buffer))
307        result)
308      (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file))
309     result))
310
311 (defun nnbabyl-request-replace-article (article group buffer)
312   (nnbabyl-possibly-change-newsgroup group)
313   (save-excursion
314     (set-buffer nnbabyl-mbox-buffer)
315     (goto-char 1)
316     (if (not (search-forward (nnbabyl-article-string article) nil t))
317         nil
318       (nnbabyl-delete-mail t t)
319       (insert-buffer-substring buffer)
320       (save-buffer)
321       t)))
322
323 \f
324 ;;; Low-Level Interface
325
326 ;; If FORCE, delete article no matter how many X-Gnus-Newsgroup
327 ;; headers there are. If LEAVE-DELIM, don't delete the Unix mbox
328 ;; delimeter line.
329 (defun nnbabyl-delete-mail (&optional force leave-delim)
330   ;; Delete the current X-Gnus-Newsgroup line.
331   (or force
332       (delete-region
333        (progn (beginning-of-line) (point))
334        (progn (forward-line 1) (point))))
335   ;; Beginning of the article.
336   (save-excursion
337     (save-restriction
338       (narrow-to-region
339        (save-excursion
340          (re-search-backward (concat "^" nnbabyl-mail-delimiter) nil t)
341          (if leave-delim (progn (forward-line 1) (point))
342            (match-beginning 0)))
343        (progn
344          (forward-line 1)
345          (or (and (re-search-forward (concat "^" nnbabyl-mail-delimiter) 
346                                      nil t)
347                   (if (and (not (bobp)) leave-delim)
348                       (progn (forward-line -2) (point))
349                     (match-beginning 0)))
350              (point-max))))
351       (goto-char (point-min))
352       ;; Only delete the article if no other groups owns it as well.
353       (if (or force (not (re-search-forward "^X-Gnus-Newsgroup: " nil t)))
354           (delete-region (point-min) (point-max))))))
355
356 (defun nnbabyl-possibly-change-newsgroup (newsgroup)
357   (if (or (not nnbabyl-mbox-buffer)
358           (not (buffer-name nnbabyl-mbox-buffer)))
359       (save-excursion
360         (nnbabyl-read-mbox)))
361   (if (not nnbabyl-group-alist)
362       (setq nnbabyl-group-alist (nnmail-get-active)))
363   (if newsgroup
364       (if (assoc newsgroup nnbabyl-group-alist)
365           (setq nnbabyl-current-group newsgroup)
366         (setq nnbabyl-status-string "No such group in file")
367         nil)))
368
369 (defun nnbabyl-article-string (article)
370   (concat "\nX-Gnus-Newsgroup: " nnbabyl-current-group ":" 
371           (int-to-string article)))
372
373 (defun nnbabyl-save-mail ()
374   ;; Called narrowed to an article.
375   (let ((group-art (nreverse (nnmail-article-group 'nnbabyl-active-number))))
376     (nnmail-insert-lines)
377     (nnmail-insert-xref group-art)
378     (nnbabyl-insert-newsgroup-line group-art)))
379
380 (defun nnbabyl-insert-newsgroup-line (group-art)
381   (save-excursion
382     (goto-char (point-min))
383     (or (looking-at "\^_")
384         (insert "\^_\^L\n0, unseen,,\n*** EOOH ***\n"))
385     (while (looking-at "From ")
386       (replace-match "Mail-from: ")
387       (forward-line 1))
388     (if (search-forward "\n\n" nil t)
389         (progn
390           (forward-char -1)
391           (while group-art
392             (insert (format "X-Gnus-Newsgroup: %s:%d   %s\n" 
393                             (car (car group-art)) (cdr (car group-art))
394                             (current-time-string)))
395             (setq group-art (cdr group-art)))))))
396
397 (defun nnbabyl-active-number (group)
398   ;; Find the next article number in GROUP.
399   (let ((active (car (cdr (assoc group nnbabyl-group-alist)))))
400     (setcdr active (1+ (cdr active)))
401     (cdr active)))
402
403 (defun nnbabyl-read-mbox ()
404   (nnbabyl-request-list)
405   (setq nnbabyl-group-alist (nnmail-get-active))
406   (if (not (file-exists-p nnbabyl-mbox-file))
407       (write-region 1 1 nnbabyl-mbox-file t 'nomesg))
408   (if (and nnbabyl-mbox-buffer
409            (get-buffer nnbabyl-mbox-buffer)
410            (buffer-name nnbabyl-mbox-buffer)
411            (save-excursion
412              (set-buffer nnbabyl-mbox-buffer)
413              (= (buffer-size) (nth 7 (file-attributes nnbabyl-mbox-file)))))
414       ()
415     (save-excursion
416       (let ((delim (concat "^" nnbabyl-mail-delimiter))
417             (buf (or (get-buffer (file-name-nondirectory nnbabyl-mbox-file))
418                      (create-file-buffer nnbabyl-mbox-file)))
419             start end)
420         (set-buffer (setq nnbabyl-mbox-buffer buf))
421         (buffer-disable-undo (current-buffer))
422
423         (insert-file-contents nnbabyl-mbox-file)
424         (setq buffer-file-name nnbabyl-mbox-file)
425         (set-buffer-modified-p nil)
426
427         (goto-char (point-min))
428         (while (re-search-forward delim nil t)
429           (setq start (match-beginning 0))
430           (if (and
431                (save-excursion (re-search-forward delim nil t))
432                (not (search-forward 
433                      "\nX-Gnus-Newsgroup: " 
434                      (save-excursion 
435                        (setq end (or (and (re-search-forward delim nil t)
436                                           (match-beginning 0))
437                                      (point-max)))) t)))
438               (progn
439                 (goto-char end)
440                 (save-excursion
441                   (save-restriction
442                     (goto-char start)
443                     (narrow-to-region start end)
444                     (nnbabyl-save-mail))))))
445         (save-buffer)
446         (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)))))
447
448 (defun nnbabyl-get-new-mail ()
449   (let (incoming)
450     (nnbabyl-read-mbox)
451     (if (and nnmail-spool-file nnbabyl-get-new-mail
452              (file-exists-p nnmail-spool-file)
453              (> (nth 7 (file-attributes nnmail-spool-file)) 0))
454         (progn
455           (and gnus-verbose-backends
456                (message "nnbabyl: Reading incoming mail..."))
457           (setq incoming 
458                 (nnmail-move-inbox nnmail-spool-file
459                                    (concat nnbabyl-mbox-file "-Incoming")))
460           (save-excursion
461             (let ((in-buf (nnmail-split-incoming 
462                            incoming 'nnbabyl-save-mail t)))
463               (set-buffer nnbabyl-mbox-buffer)
464               (goto-char (point-max))
465               (search-backward "\n\^_" nil t)
466               (insert-buffer-substring in-buf)
467               (kill-buffer in-buf)))
468           (run-hooks 'nnmail-read-incoming-hook)
469           (and gnus-verbose-backends
470                (message "nnbabyl: Reading incoming mail...done"))))
471     (and (buffer-modified-p nnbabyl-mbox-buffer) 
472          (save-excursion
473            (nnmail-save-active nnbabyl-group-alist nnbabyl-active-file)
474            (set-buffer nnbabyl-mbox-buffer)
475            (save-buffer)))
476     ;; (if incoming (delete-file incoming))
477     ))
478
479 (provide 'nnbabyl)
480
481 ;;; nnbabyl.el ends here