From: yamaoka Date: Fri, 21 Dec 2001 01:57:51 +0000 (+0000) Subject: Synch with Oort Gnus. X-Git-Tag: t-gnus-6_15_4-09-quimby-last-~10 X-Git-Url: http://git.chise.org/gitweb/?a=commitdiff_plain;h=89aca6df4a3706800e4956c2328afb54fa69fc20;p=elisp%2Fgnus.git- Synch with Oort Gnus. --- diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e058581..7a60ad9 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,19 @@ +2001-12-20 15:00:00 ShengHuo ZHU + + * nnmaildir.el: Copyright changes. Require cl only at compile time. + +2001-12-20 Simon Josefsson + + * nnimap.el (top-level): Don't require cl. Suggested by ShengHuo + ZHU . + (nnimap-close-group): Don't quote KEYLIST items. Suggested by + Brian P Templeton . + +2001-12-19 17:00:00 ShengHuo ZHU + + * nnmaildir.el: New. + From Paul Jarc . + 2001-12-19 16:00:00 ShengHuo ZHU * nndoc.el (nndoc-type-alist): Move forward to the end. diff --git a/lisp/nnimap.el b/lisp/nnimap.el index 48b43e1..9ff83b4 100644 --- a/lisp/nnimap.el +++ b/lisp/nnimap.el @@ -60,7 +60,7 @@ (eval-when-compile (require 'cl)) (eval-when-compile (require 'gnus-clfns)) -(eval-and-compile (require 'imap)) +(require 'imap) (require 'nnoo) (require 'nnmail) @@ -846,9 +846,9 @@ function is generally only called when Gnus is shutting down." (when (and (imap-opened) (nnimap-possibly-change-group group server)) (case nnimap-expunge-on-close - ('always (imap-mailbox-expunge nnimap-close-asynchronous) + (always (imap-mailbox-expunge nnimap-close-asynchronous) (imap-mailbox-close nnimap-close-asynchronous)) - ('ask (if (and (imap-search "DELETED") + (ask (if (and (imap-search "DELETED") (gnus-y-or-n-p (format "Expunge articles in group `%s'? " imap-current-mailbox))) diff --git a/lisp/nnmaildir.el b/lisp/nnmaildir.el new file mode 100644 index 0000000..b4c0a8f --- /dev/null +++ b/lisp/nnmaildir.el @@ -0,0 +1,1664 @@ +;;; nnmaildir.el --- maildir backend for Gnus +;; Copyright (c) 2001 Free Software Foundation, Inc. +;; Copyright (c) 2000, 2001 Paul Jarc + +;; Author: Paul Jarc + +;; This file is part of GNU Emacs. + +;; 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, or (at your option) +;; any later version. + +;; 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. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. + +;;; Commentary: + +;;; + +;;; Code: + +(eval-and-compile + (require 'nnheader) + (require 'gnus) + (require 'gnus-util) + (require 'gnus-range) + (require 'gnus-start) + (require 'gnus-int) + (require 'message)) +(eval-when-compile + (require 'cl) + (require 'nnmail)) + +(gnus-declare-backend "nnmaildir" 'mail 'respool 'address) +(defconst nnmaildir-version "2001.12.19") + +(defvar nnmaildir-article-file-name nil + "*The filename of the most recently requested article. This variable is set +by nnmaildir-request-article.") + +;; The filename of the article being moved/copied: +(defvar nnmaildir--file nil) + +;; Variables to generate filenames of messages being delivered: +(defvar nnmaildir--delivery-time "") +(defconst nnmaildir--delivery-pid (number-to-string (emacs-pid))) +(defvar nnmaildir--delivery-ct nil) + +;; An obarry containing symbols whose names are server names and whose values +;; are servers: +(defvar nnmaildir--servers (make-vector 3 0)) +;; A server which has not necessarily been added to nnmaildir--servers, or nil: +(defvar nnmaildir--tmp-server nil) +;; The current server: +(defvar nnmaildir--cur-server nil) + +;; A server is a vector: +["server-name" + select-method + "/expanded/path/to/directory/containing/symlinks/to/maildirs/" + directory-files-function + group-name-transformation-function + ;; An obarray containing symbols whose names are group names and whose values + ;; are groups: + group-hash + ;; A group which has not necessarily been added to the group hash, or nil: + tmp-group + current-group ;; or nil + "Last error message, or nil" + directory-modtime + get-new-mail-p ;; Should we split mail from mail-sources? + "new/group/creation/directory"] + +;; A group is a vector: +["group.name" + "prefixed:group.name" + ;; Modification times of the "new", and "cur" directories: + new-modtime + cur-modtime + ;; A vector containing lists of articles: + [;; A list of articles, with article numbers in descending order, ending with + ;; article 1: + article-list + ;; An obarray containing symbols whose names are filename prefixes and whose + ;; values are articles: + file-hash + ;; Same as above, but keyed on Message-ID: + msgid-hash + ;; An article which has not necessarily been added to the file and msgid + ;; hashes, or nil: + tmp-article] + ;; A vector containing nil, or articles with NOV data: + nov-cache + ;; The index of the next nov-cache entry to be replaced: + nov-cache-index + ;; An obarray containing symbols whose names are mark names and whose values + ;; are modtimes of mark directories: + mark-modtime-hash] + +;; An article is a vector: +["file.name.prefix" + ":2,suffix" ;; or 'expire if expired + number + "msgid" + ;; A NOV data vector, or nil: + ["subject\tfrom\tdate" + "references\tchars\lines" + "extra" + article-file-modtime]] + +(defmacro nnmaildir--srv-new () '(make-vector 11 nil)) +(defmacro nnmaildir--srv-get-name (server) `(aref ,server 0)) +(defmacro nnmaildir--srv-get-method (server) `(aref ,server 1)) +(defmacro nnmaildir--srv-get-dir (server) `(aref ,server 2)) +(defmacro nnmaildir--srv-get-ls (server) `(aref ,server 3)) +(defmacro nnmaildir--srv-get-groups (server) `(aref ,server 4)) +(defmacro nnmaildir--srv-get-tmpgrp (server) `(aref ,server 5)) +(defmacro nnmaildir--srv-get-curgrp (server) `(aref ,server 6)) +(defmacro nnmaildir--srv-get-error (server) `(aref ,server 7)) +(defmacro nnmaildir--srv-get-mtime (server) `(aref ,server 8)) +(defmacro nnmaildir--srv-get-gnm (server) `(aref ,server 9)) +(defmacro nnmaildir--srv-get-create-dir (server) `(aref ,server 10)) +(defmacro nnmaildir--srv-set-name (server val) `(aset ,server 0 ,val)) +(defmacro nnmaildir--srv-set-method (server val) `(aset ,server 1 ,val)) +(defmacro nnmaildir--srv-set-dir (server val) `(aset ,server 2 ,val)) +(defmacro nnmaildir--srv-set-ls (server val) `(aset ,server 3 ,val)) +(defmacro nnmaildir--srv-set-groups (server val) `(aset ,server 4 ,val)) +(defmacro nnmaildir--srv-set-tmpgrp (server val) `(aset ,server 5 ,val)) +(defmacro nnmaildir--srv-set-curgrp (server val) `(aset ,server 6 ,val)) +(defmacro nnmaildir--srv-set-error (server val) `(aset ,server 7 ,val)) +(defmacro nnmaildir--srv-set-mtime (server val) `(aset ,server 8 ,val)) +(defmacro nnmaildir--srv-set-gnm (server val) `(aset ,server 9 ,val)) +(defmacro nnmaildir--srv-set-create-dir (server val) `(aset ,server 10 ,val)) + +(defmacro nnmaildir--grp-new () '(make-vector 8 nil)) +(defmacro nnmaildir--grp-get-name (group) `(aref ,group 0)) +(defmacro nnmaildir--grp-get-pname (group) `(aref ,group 1)) +(defmacro nnmaildir--grp-get-new (group) `(aref ,group 2)) +(defmacro nnmaildir--grp-get-cur (group) `(aref ,group 3)) +(defmacro nnmaildir--grp-get-lists (group) `(aref ,group 4)) +(defmacro nnmaildir--grp-get-cache (group) `(aref ,group 5)) +(defmacro nnmaildir--grp-get-index (group) `(aref ,group 6)) +(defmacro nnmaildir--grp-get-mmth (group) `(aref ,group 7)) +(defmacro nnmaildir--grp-set-name (group val) `(aset ,group 0 ,val)) +(defmacro nnmaildir--grp-set-pname (group val) `(aset ,group 1 ,val)) +(defmacro nnmaildir--grp-set-new (group val) `(aset ,group 2 ,val)) +(defmacro nnmaildir--grp-set-cur (group val) `(aset ,group 3 ,val)) +(defmacro nnmaildir--grp-set-lists (group val) `(aset ,group 4 ,val)) +(defmacro nnmaildir--grp-set-cache (group val) `(aset ,group 5 ,val)) +(defmacro nnmaildir--grp-set-index (group val) `(aset ,group 6 ,val)) +(defmacro nnmaildir--grp-set-mmth (group val) `(aset ,group 7 ,val)) + +(defmacro nnmaildir--lists-new () '(make-vector 4 nil)) +(defmacro nnmaildir--lists-get-nlist (lists) `(aref ,lists 0)) +(defmacro nnmaildir--lists-get-flist (lists) `(aref ,lists 1)) +(defmacro nnmaildir--lists-get-mlist (lists) `(aref ,lists 2)) +(defmacro nnmaildir--lists-get-tmpart (lists) `(aref ,lists 3)) +(defmacro nnmaildir--lists-set-nlist (lists val) `(aset ,lists 0 ,val)) +(defmacro nnmaildir--lists-set-flist (lists val) `(aset ,lists 1 ,val)) +(defmacro nnmaildir--lists-set-mlist (lists val) `(aset ,lists 2 ,val)) +(defmacro nnmaildir--lists-set-tmpart (lists val) `(aset ,lists 3 ,val)) + +(defmacro nnmaildir--nlist-last-num (list) + `(if ,list (nnmaildir--art-get-num (car ,list)) 0)) +(defmacro nnmaildir--nlist-art (list num) + `(and ,list + (>= (nnmaildir--art-get-num (car ,list)) ,num) + (nth (- (nnmaildir--art-get-num (car ,list)) ,num) ,list))) +(defmacro nnmaildir--flist-art (list file) + `(symbol-value (intern-soft ,file ,list))) +(defmacro nnmaildir--mlist-art (list msgid) + `(symbol-value (intern-soft ,msgid ,list))) + +(defmacro nnmaildir--art-new () '(make-vector 5 nil)) +(defmacro nnmaildir--art-get-prefix (article) `(aref ,article 0)) +(defmacro nnmaildir--art-get-suffix (article) `(aref ,article 1)) +(defmacro nnmaildir--art-get-num (article) `(aref ,article 2)) +(defmacro nnmaildir--art-get-msgid (article) `(aref ,article 3)) +(defmacro nnmaildir--art-get-nov (article) `(aref ,article 4)) +(defmacro nnmaildir--art-set-prefix (article val) `(aset ,article 0 ,val)) +(defmacro nnmaildir--art-set-suffix (article val) `(aset ,article 1 ,val)) +(defmacro nnmaildir--art-set-num (article val) `(aset ,article 2 ,val)) +(defmacro nnmaildir--art-set-msgid (article val) `(aset ,article 3 ,val)) +(defmacro nnmaildir--art-set-nov (article val) `(aset ,article 4 ,val)) + +(defmacro nnmaildir--nov-new () '(make-vector 4 nil)) +(defmacro nnmaildir--nov-get-beg (nov) `(aref ,nov 0)) +(defmacro nnmaildir--nov-get-mid (nov) `(aref ,nov 1)) +(defmacro nnmaildir--nov-get-end (nov) `(aref ,nov 2)) +(defmacro nnmaildir--nov-get-mtime (nov) `(aref ,nov 3)) +(defmacro nnmaildir--nov-set-beg (nov val) `(aset ,nov 0 ,val)) +(defmacro nnmaildir--nov-set-mid (nov val) `(aset ,nov 1 ,val)) +(defmacro nnmaildir--nov-set-end (nov val) `(aset ,nov 2 ,val)) +(defmacro nnmaildir--nov-set-mtime (nov val) `(aset ,nov 3 ,val)) + +(defmacro nnmaildir--srv-grp-dir (srv-dir gname) + `(file-name-as-directory (concat ,srv-dir ,gname))) + +(defun nnmaildir--param (prefixed-group-name param) + (setq param + (gnus-group-find-parameter prefixed-group-name param 'allow-list) + param (if (vectorp param) (aref param 0) param)) + (eval param)) + +(defmacro nnmaildir--unlink (file) + `(if (file-attributes ,file) (delete-file ,file))) + +(defmacro nnmaildir--tmp (dir) `(file-name-as-directory (concat ,dir "tmp"))) +(defmacro nnmaildir--new (dir) `(file-name-as-directory (concat ,dir "new"))) +(defmacro nnmaildir--cur (dir) `(file-name-as-directory (concat ,dir "cur"))) +(defmacro nnmaildir--nndir (dir) + `(file-name-as-directory (concat ,dir ".nnmaildir"))) + +(defun nnmaildir--lists-fix (lists) + (let ((tmp (nnmaildir--lists-get-tmpart lists))) + (when tmp + (set (intern (nnmaildir--art-get-prefix tmp) + (nnmaildir--lists-get-flist lists)) + tmp) + (set (intern (nnmaildir--art-get-msgid tmp) + (nnmaildir--lists-get-mlist lists)) + tmp) + (nnmaildir--lists-set-tmpart lists nil)))) + +(defun nnmaildir--prepare (server group) + (let (x groups) + (catch 'return + (setq x nnmaildir--tmp-server) + (when x + (set (intern (nnmaildir--srv-get-name x) nnmaildir--servers) x) + (setq nnmaildir--tmp-server nil)) + (if (null server) + (or (setq server nnmaildir--cur-server) + (throw 'return nil)) + (or (setq server (intern-soft server nnmaildir--servers)) + (throw 'return nil)) + (setq server (symbol-value server) + nnmaildir--cur-server server)) + (setq groups (nnmaildir--srv-get-groups server)) + (if groups nil (throw 'return nil)) + (if (nnmaildir--srv-get-method server) nil + (setq x (concat "nnmaildir:" (nnmaildir--srv-get-name server)) + x (gnus-server-to-method x)) + (if x nil (throw 'return nil)) + (nnmaildir--srv-set-method server x)) + (setq x (nnmaildir--srv-get-tmpgrp server)) + (when x + (set (intern (nnmaildir--grp-get-name x) groups) x) + (nnmaildir--srv-set-tmpgrp server nil)) + (if (null group) + (or (setq group (nnmaildir--srv-get-curgrp server)) + (throw 'return nil)) + (setq group (intern-soft group groups)) + (if group nil (throw 'return nil)) + (setq group (symbol-value group))) + (nnmaildir--lists-fix (nnmaildir--grp-get-lists group)) + group))) + +(defun nnmaildir--update-nov (srv-dir group article) + (let ((nnheader-file-coding-system 'binary) + dir gname pgname msgdir prefix suffix file attr mtime novdir novfile + nov msgid nov-beg nov-mid nov-end field pos extra val deactivate-mark) + (catch 'return + (setq suffix (nnmaildir--art-get-suffix article)) + (if (stringp suffix) nil + (nnmaildir--art-set-nov article nil) + (throw 'return nil)) + (setq gname (nnmaildir--grp-get-name group) + pgname (nnmaildir--grp-get-pname group) + dir (nnmaildir--srv-grp-dir srv-dir gname) + msgdir (if (nnmaildir--param pgname 'read-only) + (nnmaildir--new dir) (nnmaildir--cur dir)) + prefix (nnmaildir--art-get-prefix article) + file (concat msgdir prefix suffix) + attr (file-attributes file)) + (if attr nil + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + (throw 'return nil)) + (setq mtime (nth 5 attr) + attr (nth 7 attr) + nov (nnmaildir--art-get-nov article) + novdir (concat (nnmaildir--nndir dir) "nov") + novdir (file-name-as-directory novdir) + novfile (concat novdir prefix)) + (save-excursion + (set-buffer (get-buffer-create " *nnmaildir nov*")) + (when (file-exists-p novfile) + (and nov + (equal mtime (nnmaildir--nov-get-mtime nov)) + (throw 'return nov)) + (erase-buffer) + (nnheader-insert-file-contents novfile) + (setq nov (read (current-buffer))) + (nnmaildir--art-set-msgid article (car nov)) + (setq nov (cadr nov)) + (and (equal mtime (nnmaildir--nov-get-mtime nov)) + (throw 'return nov))) + (erase-buffer) + (nnheader-insert-file-contents file) + (insert "\n") + (goto-char (point-min)) + (save-restriction + (if (search-forward "\n\n" nil 'noerror) + (progn + (setq nov-mid (count-lines (point) (point-max))) + (narrow-to-region (point-min) (1- (point)))) + (setq nov-mid 0)) + (goto-char (point-min)) + (delete-char 1) + (nnheader-fold-continuation-lines) + (setq nov (nnheader-parse-head 'naked) + field (or (mail-header-lines nov) 0))) + (if (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:)) nil + (setq nov-mid field)) + (setq nov-mid (number-to-string nov-mid) + nov-mid (concat (number-to-string attr) "\t" nov-mid) + field (or (mail-header-references nov) "") + pos 0) + (save-match-data + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq nov-mid (concat field "\t" nov-mid) + extra (mail-header-extra nov) + nov-end "") + (while extra + (setq field (car extra) extra (cdr extra) + val (cdr field) field (symbol-name (car field)) + pos 0) + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq pos 0) + (while (string-match "\t" val pos) + (aset val (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq nov-end (concat nov-end "\t" field ": " val))) + (setq nov-end (if (zerop (length nov-end)) "" (substring nov-end 1)) + field (or (mail-header-subject nov) "") + pos 0) + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq nov-beg field + field (or (mail-header-from nov) "") + pos 0) + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq nov-beg (concat nov-beg "\t" field) + field (or (mail-header-date nov) "") + pos 0) + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq nov-beg (concat nov-beg "\t" field) + field (mail-header-id nov) + pos 0) + (while (string-match "\t" field pos) + (aset field (match-beginning 0) ? ) + (setq pos (match-end 0))) + (setq msgid field)) + (if (or (null msgid) (nnheader-fake-message-id-p msgid)) + (setq msgid (concat "<" prefix "@nnmaildir>"))) + (erase-buffer) + (setq nov (nnmaildir--nov-new)) + (nnmaildir--nov-set-beg nov nov-beg) + (nnmaildir--nov-set-mid nov nov-mid) + (nnmaildir--nov-set-end nov nov-end) + (nnmaildir--nov-set-mtime nov mtime) + (prin1 (list msgid nov) (current-buffer)) + (setq file (concat novdir ":")) + (nnmaildir--unlink file) + (write-region (point-min) (point-max) file nil 'no-message)) + (rename-file file novfile 'replace) + (nnmaildir--art-set-msgid article msgid) + nov))) + +(defun nnmaildir--cache-nov (group article nov) + (let ((cache (nnmaildir--grp-get-cache group)) + (index (nnmaildir--grp-get-index group)) + goner) + (if (nnmaildir--art-get-nov article) nil + (setq goner (aref cache index)) + (if goner (nnmaildir--art-set-nov goner nil)) + (aset cache index article) + (nnmaildir--grp-set-index group (% (1+ index) (length cache)))) + (nnmaildir--art-set-nov article nov))) + +(defun nnmaildir--grp-add-art (srv-dir group article) + (let ((nov (nnmaildir--update-nov srv-dir group article)) + old-lists new-lists) + (when nov + (setq old-lists (nnmaildir--grp-get-lists group) + new-lists (nnmaildir--lists-new)) + (nnmaildir--lists-set-nlist + new-lists (cons article (nnmaildir--lists-get-nlist old-lists))) + (nnmaildir--lists-set-flist new-lists + (nnmaildir--lists-get-flist old-lists)) + (nnmaildir--lists-set-mlist new-lists + (nnmaildir--lists-get-mlist old-lists)) + (nnmaildir--lists-set-tmpart new-lists article) + (nnmaildir--grp-set-lists group new-lists) + (nnmaildir--lists-fix new-lists) + (nnmaildir--cache-nov group article nov) + t))) + +(defun nnmaildir--mkdir (dir) + (or (file-exists-p (file-name-as-directory dir)) + (make-directory-internal (directory-file-name dir)))) + +(defun nnmaildir--article-count (group) + (let ((ct 0) + (min 0)) + (setq group (nnmaildir--grp-get-lists group) + group (nnmaildir--lists-get-nlist group)) + (while group + (if (stringp (nnmaildir--art-get-suffix (car group))) + (setq ct (1+ ct) + min (nnmaildir--art-get-num (car group)))) + (setq group (cdr group))) + (cons ct min))) + +(defun nnmaildir-article-number-to-file-name + (number group-name server-address-string) + (let ((group (nnmaildir--prepare server-address-string group-name)) + list article suffix dir filename) + (catch 'return + (if (null group) + ;; The given group or server does not exist. + (throw 'return nil)) + (setq list (nnmaildir--grp-get-lists group) + list (nnmaildir--lists-get-nlist list) + article (nnmaildir--nlist-art list number)) + (if (null article) + ;; The given article number does not exist in this group. + (throw 'return nil)) + (setq suffix (nnmaildir--art-get-suffix article)) + (if (not (stringp suffix)) + ;; The article has expired. + (throw 'return nil)) + (setq dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir dir group-name) + group (if (nnmaildir--param (nnmaildir--grp-get-pname group) + 'read-only) + (nnmaildir--new dir) (nnmaildir--cur dir)) + filename (concat group (nnmaildir--art-get-prefix article) suffix)) + (if (file-exists-p filename) + filename + ;; The article disappeared out from under us. + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + nil)))) + +(defun nnmaildir-request-type (group &optional article) + 'mail) + +(defun nnmaildir-status-message (&optional server) + (nnmaildir--prepare server nil) + (nnmaildir--srv-get-error nnmaildir--cur-server)) + +(defun nnmaildir-server-opened (&optional server) + (and nnmaildir--cur-server + (if server + (string-equal server + (nnmaildir--srv-get-name nnmaildir--cur-server)) + t) + (nnmaildir--srv-get-groups nnmaildir--cur-server) + t)) + +(defun nnmaildir-open-server (server &optional defs) + (let ((x server) + dir size) + (catch 'return + (setq server (intern-soft x nnmaildir--servers)) + (if server + (and (setq server (symbol-value server)) + (nnmaildir--srv-get-groups server) + (setq nnmaildir--cur-server server) + (throw 'return t)) + (setq server (nnmaildir--srv-new)) + (nnmaildir--srv-set-name server x) + (setq nnmaildir--tmp-server server) + (set (intern x nnmaildir--servers) server) + (setq nnmaildir--tmp-server nil)) + (setq dir (assq 'directory defs)) + (if dir nil + (nnmaildir--srv-set-error + server "You must set \"directory\" in the select method") + (throw 'return nil)) + (setq dir (cadr dir) + dir (eval dir) + dir (expand-file-name dir) + dir (file-name-as-directory dir)) + (if (file-exists-p dir) nil + (nnmaildir--srv-set-error server (concat "No such directory: " dir)) + (throw 'return nil)) + (nnmaildir--srv-set-dir server dir) + (setq x (assq 'directory-files defs)) + (if (null x) + (setq x (symbol-function (if nnheader-directory-files-is-safe + 'directory-files + 'nnheader-directory-files-safe))) + (setq x (cadr x)) + (if (functionp x) nil + (nnmaildir--srv-set-error + server (concat "Not a function: " (prin1-to-string x))) + (throw 'return nil))) + (nnmaildir--srv-set-ls server x) + (setq x (funcall x dir nil "\\`[^.]" 'nosort) + x (length x) + size 1) + (while (<= size x) (setq size (* 2 size))) + (if (/= size 1) (setq size (1- size))) + (and (setq x (assq 'get-new-mail defs)) + (setq x (cdr x)) + (car x) + (nnmaildir--srv-set-gnm server t) + (require 'nnmail)) + (setq x (assq 'create-directory defs)) + (when x + (setq x (cadr x) + x (eval x)) + (nnmaildir--srv-set-create-dir server x)) + (nnmaildir--srv-set-groups server (make-vector size 0)) + (setq nnmaildir--cur-server server) + t))) + +(defun nnmaildir--parse-filename (file) + (let ((prefix (car file)) + timestamp len) + (if (string-match + "\\`\\([0-9]+\\)\\.\\([0-9]+\\)\\(_\\([0-9]+\\)\\)?\\(\\..*\\)\\'" + prefix) + (progn + (setq timestamp (concat "0000" (match-string 1 prefix)) + len (- (length timestamp) 4)) + (vector (string-to-number (substring timestamp 0 len)) + (string-to-number (substring timestamp len)) + (string-to-number (match-string 2 prefix)) + (string-to-number (or (match-string 4 prefix) "-1")) + (match-string 5 prefix) + file)) + file))) + +(defun nnmaildir--sort-files (a b) + (catch 'return + (if (consp a) + (throw 'return (and (consp b) (string-lessp (car a) (car b))))) + (if (consp b) (throw 'return t)) + (if (< (aref a 0) (aref b 0)) (throw 'return t)) + (if (> (aref a 0) (aref b 0)) (throw 'return nil)) + (if (< (aref a 1) (aref b 1)) (throw 'return t)) + (if (> (aref a 1) (aref b 1)) (throw 'return nil)) + (if (< (aref a 2) (aref b 2)) (throw 'return t)) + (if (> (aref a 2) (aref b 2)) (throw 'return nil)) + (if (< (aref a 3) (aref b 3)) (throw 'return t)) + (if (> (aref a 3) (aref b 3)) (throw 'return nil)) + (string-lessp (aref a 4) (aref b 4)))) + +(defun nnmaildir--scan (gname scan-msgs groups method srv-dir srv-ls) + (catch 'return + (let ((36h-ago (- (car (current-time)) 2)) + absdir nndir tdir ndir cdir nattr cattr isnew pgname read-only ls + files file num dir flist group x) + (setq absdir (file-name-as-directory (concat srv-dir gname)) + nndir (nnmaildir--nndir absdir)) + (if (file-attributes absdir) nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such directory: " absdir)) + (throw 'return nil)) + (setq tdir (nnmaildir--tmp absdir) + ndir (nnmaildir--new absdir) + cdir (nnmaildir--cur absdir) + nattr (file-attributes ndir) + cattr (file-attributes cdir)) + (if (and (file-exists-p tdir) nattr cattr) nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Not a maildir: " absdir)) + (throw 'return nil)) + (setq group (nnmaildir--prepare nil gname)) + (if group + (setq isnew nil + pgname (nnmaildir--grp-get-pname group)) + (setq isnew t + group (nnmaildir--grp-new) + pgname (gnus-group-prefixed-name gname method)) + (nnmaildir--grp-set-name group gname) + (nnmaildir--grp-set-pname group pgname) + (nnmaildir--grp-set-lists group (nnmaildir--lists-new)) + (nnmaildir--grp-set-index group 0) + (nnmaildir--mkdir nndir) + (nnmaildir--mkdir (concat nndir "nov")) + (nnmaildir--mkdir (concat nndir "marks")) + (write-region "" nil (concat nndir "markfile") nil 'no-message)) + (setq read-only (nnmaildir--param pgname 'read-only) + ls (or (nnmaildir--param pgname 'directory-files) srv-ls)) + (if read-only nil + (setq x (nth 11 (file-attributes tdir))) + (if (and (= x (nth 11 nattr)) (= x (nth 11 cattr))) nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Maildir spans filesystems: " + absdir)) + (throw 'return nil)) + (setq files (funcall ls tdir 'full "\\`[^.]" 'nosort)) + (while files + (setq file (car files) files (cdr files) + x (file-attributes file)) + (if (or (< 1 (cadr x)) (> 36h-ago (car (nth 4 x)))) + (delete-file file)))) + (or scan-msgs + isnew + (throw 'return t)) + (setq nattr (nth 5 nattr)) + (if (equal nattr (nnmaildir--grp-get-new group)) + (setq nattr nil)) + (if read-only (setq dir (and (or isnew nattr) ndir)) + (when (or isnew nattr) + (setq files (funcall ls ndir nil "\\`[^.]" 'nosort)) + (while files + (setq file (car files) files (cdr files)) + (rename-file (concat ndir file) (concat cdir file ":2,"))) + (nnmaildir--grp-set-new group nattr)) + (setq cattr (file-attributes cdir) + cattr (nth 5 cattr)) + (if (equal cattr (nnmaildir--grp-get-cur group)) + (setq cattr nil)) + (setq dir (and (or isnew cattr) cdir))) + (if dir nil (throw 'return t)) + (setq files (funcall ls dir nil "\\`[^.]" 'nosort)) + (when isnew + (setq x (length files) + num 1) + (while (<= num x) (setq num (* 2 num))) + (if (/= num 1) (setq num (1- num))) + (setq x (nnmaildir--grp-get-lists group)) + (nnmaildir--lists-set-flist x (make-vector num 0)) + (nnmaildir--lists-set-mlist x (make-vector num 0)) + (nnmaildir--grp-set-mmth group (make-vector 1 0)) + (setq num (nnmaildir--param pgname 'nov-cache-size)) + (if (numberp num) (if (< num 1) (setq num 1)) + (setq x files + num 16 + cdir (file-name-as-directory (concat nndir "marks")) + ndir (file-name-as-directory (concat cdir "tick")) + cdir (file-name-as-directory (concat cdir "read"))) + (while x + (setq file (car x) x (cdr x)) + (string-match "\\`\\([^:]*\\)\\(\\(:.*\\)?\\)\\'" file) + (setq file (match-string 1 file)) + (if (or (not (file-exists-p (concat cdir file))) + (file-exists-p (concat ndir file))) + (setq num (1+ num))))) + (nnmaildir--grp-set-cache group (make-vector num nil)) + (nnmaildir--srv-set-tmpgrp nnmaildir--cur-server group) + (set (intern gname groups) group) + (nnmaildir--srv-set-tmpgrp nnmaildir--cur-server nil) + (or scan-msgs (throw 'return t))) + (setq flist (nnmaildir--grp-get-lists group) + num (nnmaildir--lists-get-nlist flist) + flist (nnmaildir--lists-get-flist flist) + num (nnmaildir--nlist-last-num num) + x files + files nil) + (while x + (setq file (car x) x (cdr x)) + (string-match "\\`\\([^:]*\\)\\(\\(:.*\\)?\\)\\'" file) + (setq file (cons (match-string 1 file) (match-string 2 file))) + (if (nnmaildir--flist-art flist (car file)) nil + (setq files (cons file files)))) + (setq files (mapcar 'nnmaildir--parse-filename files) + files (sort files 'nnmaildir--sort-files)) + (while files + (setq file (car files) files (cdr files) + file (if (consp file) file (aref file 5)) + x (nnmaildir--art-new)) + (nnmaildir--art-set-prefix x (car file)) + (nnmaildir--art-set-suffix x (cdr file)) + (nnmaildir--art-set-num x (1+ num)) + (if (nnmaildir--grp-add-art srv-dir group x) + (setq num (1+ num)))) + (if read-only (nnmaildir--grp-set-new group nattr) + (nnmaildir--grp-set-cur group cattr))) + t)) + +(defun nnmaildir-request-scan (&optional scan-group server) + (let ((coding-system-for-write nnheader-file-coding-system) + (buffer-file-coding-system nil) + (file-coding-system-alist nil) + (nnmaildir-get-new-mail t) + (nnmaildir-group-alist nil) + (nnmaildir-active-file nil) + x srv-ls srv-dir method groups group dirs grp-dir seen deactivate-mark) + (nnmaildir--prepare server nil) + (setq srv-ls (nnmaildir--srv-get-ls nnmaildir--cur-server) + srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + method (nnmaildir--srv-get-method nnmaildir--cur-server) + groups (nnmaildir--srv-get-groups nnmaildir--cur-server)) + (save-excursion + (set-buffer (get-buffer-create " *nnmaildir work*")) + (save-match-data + (if (stringp scan-group) + (if (nnmaildir--scan scan-group t groups method srv-dir srv-ls) + (if (nnmaildir--srv-get-gnm nnmaildir--cur-server) + (nnmail-get-new-mail 'nnmaildir nil nil scan-group)) + (unintern scan-group groups)) + (setq x (nth 5 (file-attributes srv-dir))) + (if (equal x (nnmaildir--srv-get-mtime nnmaildir--cur-server)) + (if scan-group nil + (mapatoms (lambda (sym) + (nnmaildir--scan (symbol-name sym) t groups + method srv-dir srv-ls)) + groups)) + (setq dirs (funcall srv-ls srv-dir nil "\\`[^.]" 'nosort) + x (length dirs) + seen 1) + (while (<= seen x) (setq seen (* 2 seen))) + (if (/= seen 1) (setq seen (1- seen))) + (setq seen (make-vector seen 0) + scan-group (null scan-group)) + (while dirs + (setq grp-dir (car dirs) dirs (cdr dirs)) + (if (nnmaildir--scan grp-dir scan-group groups method srv-dir + srv-ls) + (intern grp-dir seen))) + (setq x nil) + (mapatoms (lambda (group) + (setq group (symbol-name group)) + (if (intern-soft group seen) nil + (setq x (cons group x)))) + groups) + (while x + (unintern (car x) groups) + (setq x (cdr x))) + (nnmaildir--srv-set-mtime nnmaildir--cur-server + (nth 5 (file-attributes srv-dir)))) + (if (nnmaildir--srv-get-gnm nnmaildir--cur-server) + (nnmail-get-new-mail 'nnmaildir nil nil)))))) + t) + +(defun nnmaildir-request-list (&optional server) + (nnmaildir-request-scan 'find-new-groups server) + (let (pgname ro ct-min deactivate-mark) + (nnmaildir--prepare server nil) + (save-excursion + (set-buffer nntp-server-buffer) + (erase-buffer) + (mapatoms (lambda (group) + (setq group (symbol-value group) + ro (nnmaildir--param (nnmaildir--grp-get-pname group) + 'read-only) + ct-min (nnmaildir--article-count group)) + (insert (nnmaildir--grp-get-name group) " ") + (princ (car ct-min) nntp-server-buffer) + (insert " ") + (princ (cdr ct-min) nntp-server-buffer) + (insert " " (if ro "n" "y") "\n")) + (nnmaildir--srv-get-groups nnmaildir--cur-server)))) + t) + +(defun nnmaildir-request-newgroups (date &optional server) + (nnmaildir-request-list server)) + +(defun nnmaildir-retrieve-groups (groups &optional server) + (let (gname group ct-min deactivate-mark) + (nnmaildir--prepare server nil) + (save-excursion + (set-buffer nntp-server-buffer) + (erase-buffer) + (while groups + (setq gname (car groups) groups (cdr groups)) + (nnmaildir-request-scan gname server) + (setq group (nnmaildir--prepare nil gname)) + (if (null group) (insert "411 no such news group\n") + (setq ct-min (nnmaildir--article-count group)) + (insert "211 ") + (princ (car ct-min) nntp-server-buffer) + (insert " ") + (princ (cdr ct-min) nntp-server-buffer) + (insert " ") + (princ (nnmaildir--nlist-last-num + (nnmaildir--lists-get-nlist + (nnmaildir--grp-get-lists group))) + nntp-server-buffer) + (insert " " gname "\n"))))) + 'group) + +(defun nnmaildir-request-update-info (gname info &optional server) + (nnmaildir-request-scan gname server) + (let ((group (nnmaildir--prepare server gname)) + srv-ls pgname nlist flist last always-marks never-marks old-marks + dotfile num dir markdirs marks mark ranges articles article read end + new-marks ls old-mmth new-mmth mtime mark-sym deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (setq srv-ls (nnmaildir--srv-get-ls nnmaildir--cur-server) + gname (nnmaildir--grp-get-name group) + pgname (nnmaildir--grp-get-pname group) + nlist (nnmaildir--grp-get-lists group) + flist (nnmaildir--lists-get-flist nlist) + nlist (nnmaildir--lists-get-nlist nlist)) + (if nlist nil + (gnus-info-set-read info nil) + (gnus-info-set-marks info nil 'extend) + (throw 'return info)) + (setq old-marks (cons 'read (gnus-info-read info)) + old-marks (cons old-marks (gnus-info-marks info)) + last (nnmaildir--nlist-last-num nlist) + always-marks (nnmaildir--param pgname 'always-marks) + never-marks (nnmaildir--param pgname 'never-marks) + dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir dir gname) + dir (nnmaildir--nndir dir) + dir (concat dir "marks") + dir (file-name-as-directory dir) + ls (nnmaildir--param pgname 'directory-files) + ls (or ls srv-ls) + markdirs (funcall ls dir nil "\\`[^.]" 'nosort) + num (length markdirs) + new-mmth 1) + (while (<= new-mmth num) (setq new-mmth (* 2 new-mmth))) + (if (/= new-mmth 1) (setq new-mmth (1- new-mmth))) + (setq new-mmth (make-vector new-mmth 0) + old-mmth (nnmaildir--grp-get-mmth group)) + (while markdirs + (setq mark (car markdirs) markdirs (cdr markdirs) + articles (concat dir mark) + articles (file-name-as-directory articles) + mark-sym (intern mark) + ranges nil) + (catch 'got-ranges + (if (memq mark-sym never-marks) (throw 'got-ranges nil)) + (when (memq mark-sym always-marks) + (setq ranges (list (cons 1 last))) + (throw 'got-ranges nil)) + (setq mtime (file-attributes articles) + mtime (nth 5 mtime)) + (set (intern mark new-mmth) mtime) + (when (equal mtime (symbol-value (intern-soft mark old-mmth))) + (setq ranges (assq mark-sym old-marks)) + (if ranges (setq ranges (cdr ranges))) + (throw 'got-ranges nil)) + (setq articles (funcall ls articles nil "\\`[^.]" 'nosort)) + (while articles + (setq article (car articles) articles (cdr articles) + article (nnmaildir--flist-art flist article)) + (if article + (setq num (nnmaildir--art-get-num article) + ranges (gnus-add-to-range ranges (list num)))))) + (if (eq mark-sym 'read) (setq read ranges) + (if ranges (setq marks (cons (cons mark-sym ranges) marks))))) + (gnus-info-set-read info read) + (gnus-info-set-marks info marks 'extend) + (nnmaildir--grp-set-mmth group new-mmth) + info))) + +(defun nnmaildir-request-group (gname &optional server fast) + (nnmaildir-request-scan gname server) + (let ((group (nnmaildir--prepare server gname)) + ct-min deactivate-mark) + (save-excursion + (set-buffer nntp-server-buffer) + (erase-buffer) + (catch 'return + (if group nil + (insert "411 no such news group\n") + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (nnmaildir--srv-set-curgrp nnmaildir--cur-server group) + (if fast (throw 'return t)) + (setq ct-min (nnmaildir--article-count group)) + (insert "211 ") + (princ (car ct-min) nntp-server-buffer) + (insert " ") + (princ (cdr ct-min) nntp-server-buffer) + (insert " ") + (princ (nnmaildir--nlist-last-num + (nnmaildir--lists-get-nlist + (nnmaildir--grp-get-lists group))) + nntp-server-buffer) + (insert " " gname "\n") + t)))) + +(defun nnmaildir-request-create-group (gname &optional server args) + (nnmaildir--prepare server nil) + (catch 'return + (let ((create-dir (nnmaildir--srv-get-create-dir nnmaildir--cur-server)) + srv-dir dir groups) + (when (zerop (length gname)) + (nnmaildir--srv-set-error nnmaildir--cur-server + "Invalid (empty) group name") + (throw 'return nil)) + (when (eq (aref "." 0) (aref gname 0)) + (nnmaildir--srv-set-error nnmaildir--cur-server + "Group names may not start with \".\"") + (throw 'return nil)) + (when (save-match-data (string-match "[\0/\t]" gname)) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Illegal characters (null, tab, or /) in group name: " + gname)) + (throw 'return nil)) + (setq groups (nnmaildir--srv-get-groups nnmaildir--cur-server)) + (when (intern-soft gname groups) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Group already exists: " gname)) + (throw 'return nil)) + (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)) + (if (file-name-absolute-p create-dir) + (setq dir (expand-file-name create-dir)) + (setq dir srv-dir + dir (file-truename dir) + dir (concat dir create-dir))) + (setq dir (file-name-as-directory dir) + dir (concat dir gname)) + (nnmaildir--mkdir dir) + (setq dir (file-name-as-directory dir)) + (nnmaildir--mkdir (concat dir "tmp")) + (nnmaildir--mkdir (concat dir "new")) + (nnmaildir--mkdir (concat dir "cur")) + (setq create-dir (file-name-as-directory create-dir)) + (make-symbolic-link (concat create-dir gname) (concat srv-dir gname)) + (nnmaildir-request-scan 'find-new-groups)))) + +(defun nnmaildir-request-rename-group (gname new-name &optional server) + (let ((group (nnmaildir--prepare server gname)) + (coding-system-for-write nnheader-file-coding-system) + (buffer-file-coding-system nil) + (file-coding-system-alist nil) + srv-dir x groups) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (when (zerop (length new-name)) + (nnmaildir--srv-set-error nnmaildir--cur-server + "Invalid (empty) group name") + (throw 'return nil)) + (when (eq (aref "." 0) (aref new-name 0)) + (nnmaildir--srv-set-error nnmaildir--cur-server + "Group names may not start with \".\"") + (throw 'return nil)) + (when (save-match-data (string-match "[\0/\t]" new-name)) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Illegal characters (null, tab, or /) in group name: " + new-name)) + (throw 'return nil)) + (if (string-equal gname new-name) (throw 'return t)) + (when (intern-soft new-name + (nnmaildir--srv-get-groups nnmaildir--cur-server)) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Group already exists: " new-name)) + (throw 'return nil)) + (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server)) + (condition-case err + (rename-file (concat srv-dir gname) + (concat srv-dir new-name)) + (error + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Error renaming link: " + (prin1-to-string err))) + (throw 'return nil))) + (setq x (nnmaildir--srv-get-groups nnmaildir--cur-server) + groups (make-vector (length x) 0)) + (mapatoms (lambda (sym) + (if (eq (symbol-value sym) group) nil + (set (intern (symbol-name sym) groups) + (symbol-value sym)))) + x) + (setq group (copy-sequence group)) + (nnmaildir--grp-set-name group new-name) + (set (intern new-name groups) group) + (nnmaildir--srv-set-groups nnmaildir--cur-server groups) + t))) + +(defun nnmaildir-request-delete-group (gname force &optional server) + (let ((group (nnmaildir--prepare server gname)) + pgname grp-dir dir dirs files ls deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (if (eq group (nnmaildir--srv-get-curgrp nnmaildir--cur-server)) + (nnmaildir--srv-set-curgrp nnmaildir--cur-server nil)) + (setq gname (nnmaildir--grp-get-name group) + pgname (nnmaildir--grp-get-pname group)) + (unintern gname (nnmaildir--srv-get-groups nnmaildir--cur-server)) + (setq grp-dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + grp-dir (nnmaildir--srv-grp-dir grp-dir gname)) + (if (not force) (setq grp-dir (directory-file-name grp-dir)) + (if (nnmaildir--param pgname 'read-only) + (progn (delete-directory (nnmaildir--tmp grp-dir)) + (nnmaildir--unlink (nnmaildir--new grp-dir)) + (delete-directory (nnmaildir--cur grp-dir))) + (save-excursion + (set-buffer (get-buffer-create " *nnmaildir work*")) + (erase-buffer) + (setq ls (or (nnmaildir--param pgname 'directory-files) + (nnmaildir--srv-get-ls nnmaildir--cur-server)) + files (funcall ls (nnmaildir--tmp grp-dir) 'full "\\`[^.]" + 'nosort)) + (while files + (delete-file (car files)) + (setq files (cdr files))) + (delete-directory (concat grp-dir "tmp")) + (setq files (funcall ls (nnmaildir--new grp-dir) 'full "\\`[^.]" + 'nosort)) + (while files + (delete-file (car files)) + (setq files (cdr files))) + (delete-directory (concat grp-dir "new")) + (setq files (funcall ls (nnmaildir--cur grp-dir) 'full "\\`[^.]" + 'nosort)) + (while files + (delete-file (car files)) + (setq files (cdr files))) + (delete-directory (concat grp-dir "cur")))) + (setq dir (nnmaildir--nndir grp-dir) + dirs (cons (concat dir "nov") + (funcall ls (concat dir "marks") 'full "\\`[^.]" + 'nosort))) + (while dirs + (setq dir (car dirs) dirs (cdr dirs) + files (funcall ls dir 'full "\\`[^.]" 'nosort)) + (while files + (delete-file (car files)) + (setq files (cdr files))) + (delete-directory dir)) + (setq dir (nnmaildir--nndir grp-dir) + files (concat dir "markfile")) + (nnmaildir--unlink files) + (delete-directory (concat dir "marks")) + (delete-directory dir) + (setq grp-dir (directory-file-name grp-dir) + dir (car (file-attributes grp-dir))) + (if (eq (aref "/" 0) (aref dir 0)) nil + (setq dir (concat (file-truename + (nnmaildir--srv-get-dir nnmaildir--cur-server)) + dir))) + (delete-directory dir)) + (nnmaildir--unlink grp-dir) + t))) + +(defun nnmaildir-retrieve-headers (articles &optional gname server fetch-old) + (let ((group (nnmaildir--prepare server gname)) + srv-dir dir nlist mlist article num stop nov nlist2 deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (if gname (concat "No such group: " gname) + "No current group")) + (throw 'return nil)) + (save-excursion + (set-buffer nntp-server-buffer) + (erase-buffer) + (setq nlist (nnmaildir--grp-get-lists group) + mlist (nnmaildir--lists-get-mlist nlist) + nlist (nnmaildir--lists-get-nlist nlist) + gname (nnmaildir--grp-get-name group) + srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir srv-dir gname)) + (cond + ((null nlist)) + ((and fetch-old (not (numberp fetch-old))) + (while nlist + (setq article (car nlist) nlist (cdr nlist) + nov (nnmaildir--update-nov srv-dir group article)) + (when nov + (nnmaildir--cache-nov group article nov) + (setq num (nnmaildir--art-get-num article)) + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-beg nov) "\t" + (nnmaildir--art-get-msgid article) "\t" + (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname + ":") + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-end nov) "\n") + (goto-char (point-min))))) + ((null articles)) + ((stringp (car articles)) + (while articles + (setq article (car articles) articles (cdr articles) + article (nnmaildir--mlist-art mlist article)) + (when (and article + (setq nov (nnmaildir--update-nov srv-dir group + article))) + (nnmaildir--cache-nov group article nov) + (setq num (nnmaildir--art-get-num article)) + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-beg nov) "\t" + (nnmaildir--art-get-msgid article) "\t" + (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname + ":") + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-end nov) "\n")))) + (t + (if fetch-old + ;; Assume the article range is sorted ascending + (setq stop (car articles) + num (car (last articles)) + stop (if (numberp stop) stop (car stop)) + num (if (numberp num) num (cdr num)) + stop (- stop fetch-old) + stop (if (< stop 1) 1 stop) + articles (list (cons stop num)))) + (while articles + (setq stop (car articles) articles (cdr articles)) + (while (eq stop (car articles)) + (setq articles (cdr articles))) + (if (numberp stop) (setq num stop) + (setq num (cdr stop) stop (car stop))) + (setq nlist2 (nthcdr (- (nnmaildir--art-get-num (car nlist)) num) + nlist)) + (while (and nlist2 + (setq article (car nlist2) + num (nnmaildir--art-get-num article)) + (>= num stop)) + (setq nlist2 (cdr nlist2) + nov (nnmaildir--update-nov srv-dir group article)) + (when nov + (nnmaildir--cache-nov group article nov) + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-beg nov) "\t" + (nnmaildir--art-get-msgid article) "\t" + (nnmaildir--nov-get-mid nov) "\tXref: nnmaildir " gname + ":") + (princ num nntp-server-buffer) + (insert "\t" (nnmaildir--nov-get-end nov) "\n") + (goto-char (point-min))))))) + (sort-numeric-fields 1 (point-min) (point-max)) + 'nov)))) + +(defun nnmaildir-request-article (num-msgid &optional gname server to-buffer) + (let ((group (nnmaildir--prepare server gname)) + (case-fold-search t) + list article suffix dir deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (if gname (concat "No such group: " gname) + "No current group")) + (throw 'return nil)) + (setq list (nnmaildir--grp-get-lists group)) + (if (numberp num-msgid) + (setq list (nnmaildir--lists-get-nlist list) + article (nnmaildir--nlist-art list num-msgid)) + (setq list (nnmaildir--lists-get-mlist list) + article (nnmaildir--mlist-art list num-msgid)) + (if article (setq num-msgid (nnmaildir--art-get-num article)) + (catch 'found + (mapatoms + (lambda (grp) + (setq group (symbol-value grp) + list (nnmaildir--grp-get-lists group) + list (nnmaildir--lists-get-mlist list) + article (nnmaildir--mlist-art list num-msgid)) + (when article + (setq num-msgid (nnmaildir--art-get-num article)) + (throw 'found nil))) + (nnmaildir--srv-get-groups nnmaildir--cur-server))))) + (if article nil + (nnmaildir--srv-set-error nnmaildir--cur-server "No such article") + (throw 'return nil)) + (if (stringp (setq suffix (nnmaildir--art-get-suffix article))) nil + (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired") + (throw 'return nil)) + (setq gname (nnmaildir--grp-get-name group) + dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir dir gname) + group (if (nnmaildir--param (nnmaildir--grp-get-pname group) + 'read-only) + (nnmaildir--new dir) (nnmaildir--cur dir)) + nnmaildir-article-file-name (concat group + (nnmaildir--art-get-prefix + article) + suffix)) + (if (file-exists-p nnmaildir-article-file-name) nil + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired") + (throw 'return nil)) + (save-excursion + (set-buffer (or to-buffer nntp-server-buffer)) + (erase-buffer) + (nnheader-insert-file-contents nnmaildir-article-file-name)) + (cons gname num-msgid)))) + +(defun nnmaildir-request-post (&optional server) + (let (message-required-mail-headers) + (funcall message-send-mail-function))) + +(defun nnmaildir-request-replace-article (article gname buffer) + (let ((group (nnmaildir--prepare nil gname)) + (coding-system-for-write nnheader-file-coding-system) + (buffer-file-coding-system nil) + (file-coding-system-alist nil) + file dir suffix tmpfile deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (when (nnmaildir--param (nnmaildir--grp-get-pname group) 'read-only) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Read-only group: " group)) + (throw 'return nil)) + (setq dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir dir gname) + file (nnmaildir--grp-get-lists group) + file (nnmaildir--lists-get-nlist file) + file (nnmaildir--nlist-art file article)) + (if (and file (stringp (setq suffix (nnmaildir--art-get-suffix file)))) + nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (format "No such article: %d" article)) + (throw 'return nil)) + (save-excursion + (set-buffer buffer) + (setq article file + file (nnmaildir--art-get-prefix article) + tmpfile (concat (nnmaildir--tmp dir) file)) + (when (file-exists-p tmpfile) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "File exists: " tmpfile)) + (throw 'return nil)) + (write-region (point-min) (point-max) tmpfile nil 'no-message nil + 'confirm-overwrite)) ;; error would be preferred :( + (unix-sync) ;; no fsync :( + (rename-file tmpfile (concat (nnmaildir--cur dir) file suffix) 'replace) + t))) + +(defun nnmaildir-request-move-article (article gname server accept-form + &optional last) + (let ((group (nnmaildir--prepare server gname)) + pgname list suffix result nnmaildir--file deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (setq gname (nnmaildir--grp-get-name group) + pgname (nnmaildir--grp-get-pname group) + list (nnmaildir--grp-get-lists group) + list (nnmaildir--lists-get-nlist list) + article (nnmaildir--nlist-art list article)) + (if article nil + (nnmaildir--srv-set-error nnmaildir--cur-server "No such article") + (throw 'return nil)) + (if (stringp (setq suffix (nnmaildir--art-get-suffix article))) nil + (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired") + (throw 'return nil)) + (setq nnmaildir--file (nnmaildir--srv-get-dir nnmaildir--cur-server) + nnmaildir--file (nnmaildir--srv-grp-dir nnmaildir--file gname) + nnmaildir--file (if (nnmaildir--param pgname 'read-only) + (nnmaildir--new nnmaildir--file) + (nnmaildir--cur nnmaildir--file)) + nnmaildir--file (concat nnmaildir--file + (nnmaildir--art-get-prefix article) + suffix)) + (if (file-exists-p nnmaildir--file) nil + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + (nnmaildir--srv-set-error nnmaildir--cur-server "Article has expired") + (throw 'return nil)) + (save-excursion + (set-buffer (get-buffer-create " *nnmaildir move*")) + (erase-buffer) + (nnheader-insert-file-contents nnmaildir--file) + (setq result (eval accept-form))) + (if (or (null result) (nnmaildir--param pgname 'read-only)) nil + (nnmaildir--unlink nnmaildir--file) + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil)) + result))) + +(defun nnmaildir-request-accept-article (gname &optional server last) + (let ((group (nnmaildir--prepare server gname)) + (coding-system-for-write nnheader-file-coding-system) + (buffer-file-coding-system nil) + (file-coding-system-alist nil) + srv-dir dir file tmpfile curfile 24h num article) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (throw 'return nil)) + (setq gname (nnmaildir--grp-get-name group)) + (when (nnmaildir--param (nnmaildir--grp-get-pname group) 'read-only) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Read-only group: " gname)) + (throw 'return nil)) + (setq srv-dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir srv-dir gname) + file (format-time-string "%s" nil)) + (if (string= nnmaildir--delivery-time file) nil + (setq nnmaildir--delivery-time file + nnmaildir--delivery-ct 0)) + (setq file (concat file "." nnmaildir--delivery-pid)) + (if (zerop nnmaildir--delivery-ct) nil + (setq file (concat file "_" + (number-to-string nnmaildir--delivery-ct)))) + (setq file (concat file "." (system-name)) + tmpfile (concat (nnmaildir--tmp dir) file) + curfile (concat (nnmaildir--cur dir) file ":2,")) + (when (file-exists-p tmpfile) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "File exists: " tmpfile)) + (throw 'return nil)) + (when (file-exists-p curfile) + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "File exists: " curfile)) + (throw 'return nil)) + (setq nnmaildir--delivery-ct (1+ nnmaildir--delivery-ct) + 24h (run-with-timer 86400 nil + (lambda () + (nnmaildir--unlink tmpfile) + (nnmaildir--srv-set-error + nnmaildir--cur-server + "24-hour timer expired") + (throw 'return nil)))) + (condition-case nil + (add-name-to-file nnmaildir--file tmpfile) + (error + (write-region (point-min) (point-max) tmpfile nil 'no-message nil + 'confirm-overwrite) ;; error would be preferred :( + (unix-sync))) ;; no fsync :( + (cancel-timer 24h) + (condition-case err + (add-name-to-file tmpfile curfile) + (error + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "Error linking: " + (prin1-to-string err))) + (nnmaildir--unlink tmpfile) + (throw 'return nil))) + (nnmaildir--unlink tmpfile) + (setq article (nnmaildir--art-new) + num (nnmaildir--grp-get-lists group) + num (nnmaildir--lists-get-nlist num) + num (1+ (nnmaildir--nlist-last-num num))) + (nnmaildir--art-set-prefix article file) + (nnmaildir--art-set-suffix article ":2,") + (nnmaildir--art-set-num article num) + (if (nnmaildir--grp-add-art srv-dir group article) (cons gname num))))) + +(defun nnmaildir-save-mail (group-art) + (catch 'return + (if group-art nil + (throw 'return nil)) + (let ((group-art group-art) + x nnmaildir--file deactivate-mark) + (save-excursion + (goto-char (point-min)) + (save-match-data + (while (looking-at "From ") + (replace-match "X-From-Line: ") + (forward-line 1)))) + (setq x (caar group-art) group-art (cdr group-art)) + (if (nnmaildir-request-accept-article x) nil + (throw 'return nil)) ; not that nnmail bothers to check + (setq x (nnmaildir--prepare nil x) + nnmaildir--file (nnmaildir--srv-get-dir nnmaildir--cur-server) + nnmaildir--file (concat nnmaildir--file + (nnmaildir--grp-get-name x)) + nnmaildir--file (file-name-as-directory nnmaildir--file) + x (nnmaildir--grp-get-lists x) + x (nnmaildir--lists-get-nlist x) + x (car x) + nnmaildir--file (concat nnmaildir--file + (nnmaildir--art-get-prefix x) + (nnmaildir--art-get-suffix x))) + (while group-art + (setq x (caar group-art) group-art (cdr group-art)) + (if (nnmaildir-request-accept-article x) nil + (throw 'return nil)))) + group-art)) + +(defun nnmaildir-active-number (group) + (let ((x (nnmaildir--prepare nil group))) + (catch 'return + (if x nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " group)) + (throw 'return nil)) + (setq x (nnmaildir--grp-get-lists x) + x (nnmaildir--lists-get-nlist x)) + (if x + (setq x (car x) + x (nnmaildir--art-get-num x) + x (1+ x)) + 1)))) + +(defun nnmaildir-request-expire-articles (ranges &optional gname server force) + (let ((no-force (not force)) + (group (nnmaildir--prepare server gname)) + pgname time boundary time-iter bound-iter high low target dir nlist + stop num article didnt suffix nnmaildir--file deactivate-mark) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (if gname (concat "No such group: " gname) + "No current group")) + (throw 'return (gnus-uncompress-range ranges))) + (setq gname (nnmaildir--grp-get-name group) + pgname (nnmaildir--grp-get-pname group)) + (if (nnmaildir--param pgname 'read-only) + (throw 'return (gnus-uncompress-range ranges))) + (setq time (or (nnmaildir--param pgname 'expire-age) 604800)) + (if (or force (integerp time)) nil + (throw 'return (gnus-uncompress-range ranges))) + (setq boundary (current-time) + high (- (car boundary) (/ time 65536)) + low (- (cadr boundary) (% time 65536))) + (if (< low 0) + (setq low (+ low 65536) + high (1- high))) + (setcar (cdr boundary) low) + (setcar boundary high) + (setq target (nnmaildir--param pgname 'expire-group) + target (and (stringp target) + (not (string-equal target pgname)) + target) + dir (nnmaildir--srv-get-dir nnmaildir--cur-server) + dir (nnmaildir--srv-grp-dir dir gname) + dir (nnmaildir--cur dir) + nlist (nnmaildir--grp-get-lists group) + nlist (nnmaildir--lists-get-nlist nlist) + ranges (reverse ranges)) + (save-excursion + (set-buffer (get-buffer-create " *nnmaildir move*")) + (while ranges + (setq num (car ranges) ranges (cdr ranges)) + (while (eq num (car ranges)) + (setq ranges (cdr ranges))) + (if (numberp num) (setq stop num) + (setq stop (car num) num (cdr num))) + (setq nlist (nthcdr (- (nnmaildir--art-get-num (car nlist)) num) + nlist)) + (while (and nlist + (setq article (car nlist) + num (nnmaildir--art-get-num article)) + (>= num stop)) + (setq nlist (cdr nlist) + suffix (nnmaildir--art-get-suffix article)) + (catch 'continue + (if (stringp suffix) nil + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + (throw 'continue nil)) + (setq nnmaildir--file (nnmaildir--art-get-prefix article) + nnmaildir--file (concat dir nnmaildir--file suffix) + time (file-attributes nnmaildir--file)) + (if time nil + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil) + (throw 'continue nil)) + (setq time (nth 5 time) + time-iter time + bound-iter boundary) + (if (and no-force + (progn + (while (and bound-iter time-iter + (= (car bound-iter) (car time-iter))) + (setq bound-iter (cdr bound-iter) + time-iter (cdr time-iter))) + (and bound-iter time-iter + (car-less-than-car bound-iter time-iter)))) + (setq didnt (cons (nnmaildir--art-get-num article) didnt)) + (when target + (erase-buffer) + (nnheader-insert-file-contents nnmaildir--file) + (gnus-request-accept-article target nil nil 'no-encode)) + (nnmaildir--unlink nnmaildir--file) + (nnmaildir--art-set-suffix article 'expire) + (nnmaildir--art-set-nov article nil))))) + (erase-buffer)) + didnt))) + +(defun nnmaildir-request-set-mark (gname actions &optional server) + (let ((group (nnmaildir--prepare server gname)) + (coding-system-for-write nnheader-file-coding-system) + (buffer-file-coding-system nil) + (file-coding-system-alist nil) + del-mark add-marks marksdir markfile action group-nlist nlist ranges + begin end article all-marks todo-marks did-marks marks form mdir mfile + deactivate-mark) + (setq del-mark + (lambda () + (setq mfile (car marks) + mfile (symbol-name mfile) + mfile (concat marksdir mfile) + mfile (file-name-as-directory mfile) + mfile (concat mfile (nnmaildir--art-get-prefix article))) + (nnmaildir--unlink mfile)) + add-marks + (lambda () + (while marks + (setq mdir (concat marksdir (symbol-name (car marks))) + mfile (concat (file-name-as-directory mdir) + (nnmaildir--art-get-prefix article))) + (if (memq (car marks) did-marks) nil + (nnmaildir--mkdir mdir) + (setq did-marks (cons (car marks) did-marks))) + (if (file-exists-p mfile) nil + (condition-case nil + (add-name-to-file markfile mfile) + (file-error ;; too many links, probably + (if (file-exists-p mfile) nil + (nnmaildir--unlink markfile) + (write-region "" nil markfile nil 'no-message) + (add-name-to-file markfile mfile + 'ok-if-already-exists))))) + (setq marks (cdr marks))))) + (catch 'return + (if group nil + (nnmaildir--srv-set-error nnmaildir--cur-server + (concat "No such group: " gname)) + (while actions + (setq ranges (gnus-range-add ranges (caar actions)) + actions (cdr actions))) + (throw 'return ranges)) + (setq group-nlist (nnmaildir--grp-get-lists group) + group-nlist (nnmaildir--lists-get-nlist group-nlist) + marksdir (nnmaildir--srv-get-dir nnmaildir--cur-server) + marksdir (nnmaildir--srv-grp-dir marksdir gname) + marksdir (nnmaildir--nndir marksdir) + markfile (concat marksdir "markfile") + marksdir (concat marksdir "marks") + marksdir (file-name-as-directory marksdir) + gname (nnmaildir--grp-get-name group) + all-marks (nnmaildir--grp-get-pname group) + all-marks (or (nnmaildir--param all-marks 'directory-files) + (nnmaildir--srv-get-ls nnmaildir--cur-server)) + all-marks (funcall all-marks marksdir nil "\\`[^.]" 'nosort) + marks all-marks) + (while marks + (setcar marks (intern (car marks))) + (setq marks (cdr marks))) + (while actions + (setq action (car actions) actions (cdr actions) + nlist group-nlist + ranges (car action) + todo-marks (caddr action) + marks todo-marks) + (while marks + (if (memq (car marks) all-marks) nil + (setq all-marks (cons (car marks) all-marks))) + (setq marks (cdr marks))) + (setq form + (cond + ((eq 'del (cadr action)) + '(while marks + (funcall del-mark) + (setq marks (cdr marks)))) + ((eq 'add (cadr action)) '(funcall add-marks)) + (t + '(progn + (funcall add-marks) + (setq marks all-marks) + (while marks + (if (memq (car marks) todo-marks) nil + (funcall del-mark)) + (setq marks (cdr marks))))))) + (if (numberp (cdr ranges)) (setq ranges (list ranges)) + (setq ranges (reverse ranges))) + (while ranges + (setq begin (car ranges) ranges (cdr ranges)) + (while (eq begin (car ranges)) + (setq ranges (cdr ranges))) + (if (numberp begin) (setq end begin) + (setq end (cdr begin) begin (car begin))) + (setq nlist (nthcdr (- (nnmaildir--art-get-num (car nlist)) end) + nlist)) + (while (and nlist + (setq article (car nlist)) + (>= (nnmaildir--art-get-num article) begin)) + (setq nlist (cdr nlist)) + (when (stringp (nnmaildir--art-get-suffix article)) + (setq marks todo-marks) + (eval form))))) + nil))) + +(defun nnmaildir-close-group (group &optional server) + t) + +(defun nnmaildir-close-server (&optional server) + (let (srv-ls flist ls dirs dir files file x) + (nnmaildir--prepare server nil) + (setq server nnmaildir--cur-server) + (when server + (setq nnmaildir--cur-server nil + srv-ls (nnmaildir--srv-get-ls server)) + (save-match-data + (mapatoms + (lambda (group) + (setq group (symbol-value group) + x (nnmaildir--grp-get-pname group) + ls (nnmaildir--param x 'directory-files) + ls (or ls srv-ls) + dir (nnmaildir--srv-get-dir server) + dir (nnmaildir--srv-grp-dir + dir (nnmaildir--grp-get-name group)) + x (nnmaildir--param x 'read-only) + x (if x (nnmaildir--new dir) (nnmaildir--cur dir)) + files (funcall ls x nil "\\`[^.]" 'nosort) + x (length files) + flist 1) + (while (<= flist x) (setq flist (* 2 flist))) + (if (/= flist 1) (setq flist (1- flist))) + (setq flist (make-vector flist 0)) + (while files + (setq file (car files) files (cdr files)) + (string-match "\\`\\([^:]*\\)\\(:.*\\)?\\'" file) + (intern (match-string 1 file) flist)) + (setq dir (nnmaildir--nndir dir) + dirs (cons (concat dir "nov") + (funcall ls (concat dir "marks") 'full "\\`[^.]" + 'nosort))) + (while dirs + (setq dir (car dirs) dirs (cdr dirs) + files (funcall ls dir nil "\\`[^.]" 'nosort) + dir (file-name-as-directory dir)) + (while files + (setq file (car files) files (cdr files)) + (if (intern-soft file flist) nil + (setq file (concat dir file)) + (delete-file file))))) + (nnmaildir--srv-get-groups server))) + (unintern (nnmaildir--srv-get-name server) nnmaildir--servers))) + t) + +(defun nnmaildir-request-close () + (let (servers buffer) + (mapatoms (lambda (server) + (setq servers (cons (symbol-name server) servers))) + nnmaildir--servers) + (while servers + (nnmaildir-close-server (car servers)) + (setq servers (cdr servers))) + (setq buffer (get-buffer " *nnmaildir work*")) + (if buffer (kill-buffer buffer)) + (setq buffer (get-buffer " *nnmaildir nov*")) + (if buffer (kill-buffer buffer)) + (setq buffer (get-buffer " *nnmaildir move*")) + (if buffer (kill-buffer buffer))) + t) + +(provide 'nnmaildir) + +;;; nnmaildir.el ends here