;;; nndoc.el --- single file access for Gnus
-;; Copyright (C) 1995 Free Software Foundation, Inc.
-;; Author: Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
-;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+;; 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
;; Keywords: news
;; This file is part of GNU Emacs.
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, 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
;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
+;; For Outlook mail boxes format, see http://mbx2mbox.sourceforge.net/
+
;;; Code:
(require 'nnheader)
-(require 'rmail)
+(require 'message)
(require 'nnmail)
+(require 'nnoo)
+(require 'gnus-util)
+(require 'mm-util)
+(eval-when-compile (require 'cl))
-(defvar nndoc-article-type 'mbox
- "*Type of the file - one of `mbox', `babyl', `digest', or `forward'.")
-
-(defvar nndoc-digest-type 'traditional
- "Type of the last digest. Auto-detected from the article header.
-Possible values:
- `traditional' -- the \"lots of dashes\" (30+) rules used;
- we currently also do unconditional RFC 934 unquoting.
- `rfc1341' -- RFC 1341 digest (MIME, unique boundary, no quoting).")
-
-(defconst nndoc-type-to-regexp
- `((mbox
- ,(concat "^" rmail-unix-mail-delimiter)
- ,(concat "^" rmail-unix-mail-delimiter)
- nil "^$" nil nil nil)
- (babyl "\^_\^L *\n" "\^_" "^[0-9].*\n" "^$" nil nil
- "^$")
- (digest
- "^------------------------------*[\n \t]+"
- "^------------------------------*[\n \t]+"
- nil "^ ?$"
- "^------------------------------*[\n \t]+"
- "^End of" nil)
- (forward
- "^-+ Start of forwarded message -+\n+"
- "^-+ End of forwarded message -+\n"
- nil "^ ?$" nil nil nil)
- (mmfd
- "^\^A\^A\^A\^A\n" "^\^A\^A\^A\^A\n" nil "^$"
- nil nil nil))
- "Regular expressions for articles of the various types.
-article-begin, article-end, head-begin, head-end,
-first-article, end-of-file, body-begin.")
+(nnoo-declare nndoc)
+(defvoo nndoc-article-type 'guess
+ "*Type of the file.
+One of `mbox', `babyl', `digest', `news', `rnews', `mmdf', `forward',
+`rfc934', `rfc822-forward', `mime-parts', `standard-digest',
+`slack-digest', `clari-briefs', `nsmail', `outlook', `oe-dbx',
+`mailman', `exim-bounce', or `guess'.")
-\f
+(defvoo nndoc-post-type 'mail
+ "*Whether the nndoc group is `mail' or `post'.")
-(defvar nndoc-article-begin nil)
-(defvar nndoc-article-end nil)
-(defvar nndoc-head-begin nil)
-(defvar nndoc-head-end nil)
-(defvar nndoc-first-article nil)
-(defvar nndoc-end-of-file nil)
-(defvar nndoc-body-begin nil)
-
-(defvar nndoc-current-server nil)
-(defvar nndoc-server-alist nil)
-(defvar nndoc-server-variables
- (list
- (list 'nndoc-article-type nndoc-article-type)
- '(nndoc-article-begin nil)
- '(nndoc-article-end nil)
- '(nndoc-head-begin nil)
- '(nndoc-head-end nil)
- '(nndoc-first-article nil)
- '(nndoc-current-buffer nil)
- '(nndoc-group-alist nil)
- '(nndoc-end-of-file nil)
- '(nndoc-body-begin nil)
- '(nndoc-address nil)))
+(defvoo nndoc-open-document-hook 'nnheader-ms-strip-cr
+ "Hook run after opening a document.
+The default function removes all trailing carriage returns
+from the document.")
-(defconst nndoc-version "nndoc 1.0"
- "nndoc version.")
+(defvar nndoc-type-alist
+ `((mmdf
+ (article-begin . "^\^A\^A\^A\^A\n")
+ (body-end . "^\^A\^A\^A\^A\n"))
+ (mime-digest
+ (article-begin . "")
+ (head-begin . "^ ?\n")
+ (head-end . "^ ?$")
+ (body-end . "")
+ (file-end . "")
+ (subtype digest guess))
+ (nsmail
+ (article-begin . "^From - "))
+ (news
+ (article-begin . "^Path:"))
+ (rnews
+ (article-begin . "^#! *rnews +\\([0-9]+\\) *\n")
+ (body-end-function . nndoc-rnews-body-end))
+ (mbox
+ (article-begin-function . nndoc-mbox-article-begin)
+ (body-end-function . nndoc-mbox-body-end))
+ (babyl
+ (article-begin . "\^_\^L *\n")
+ (body-end . "\^_")
+ (body-begin-function . nndoc-babyl-body-begin)
+ (head-begin-function . nndoc-babyl-head-begin))
+ (mime-parts
+ (generate-head-function . nndoc-generate-mime-parts-head)
+ (article-transform-function . nndoc-transform-mime-parts))
+ (exim-bounce
+ (article-begin . "^------ This is a copy of the message, including all the headers. ------\n\n")
+ (body-end-function . nndoc-exim-bounce-body-end-function))
+ (rfc934
+ (article-begin . "^--.*\n+")
+ (body-end . "^--.*$")
+ (prepare-body-function . nndoc-unquote-dashes))
+ (mailman
+ (article-begin . "^--__--__--\n\nMessage:")
+ (body-end . "^--__--__--$")
+ (prepare-body-function . nndoc-unquote-dashes))
+ (clari-briefs
+ (article-begin . "^ \\*")
+ (body-end . "^\t------*[ \t]^*\n^ \\*")
+ (body-begin . "^\t")
+ (head-end . "^\t")
+ (generate-head-function . nndoc-generate-clari-briefs-head)
+ (article-transform-function . nndoc-transform-clari-briefs))
-(defvar nndoc-current-buffer nil
- "Current nndoc news buffer.")
+ (standard-digest
+ (first-article . ,(concat "^" (make-string 70 ?-) "\n *\n+"))
+ (article-begin . ,(concat "^\n" (make-string 30 ?-) "\n *\n+"))
+ (prepare-body-function . nndoc-unquote-dashes)
+ (body-end-function . nndoc-digest-body-end)
+ (head-end . "^ *$")
+ (body-begin . "^ *\n")
+ (file-end . "^End of .*digest.*[0-9].*\n\\*\\*\\|^End of.*Digest *$")
+ (subtype digest guess))
+ (slack-digest
+ (article-begin . "^------------------------------*[\n \t]+")
+ (head-end . "^ ?$")
+ (body-end-function . nndoc-digest-body-end)
+ (body-begin . "^ ?$")
+ (file-end . "^End of")
+ (prepare-body-function . nndoc-unquote-dashes)
+ (subtype digest guess))
+ (google
+ (pre-dissection-function . nndoc-decode-content-transfer-encoding)
+ (article-begin . "^== [0-9]+ of [0-9]+ ==$")
+ (head-begin . "^Date:")
+ (head-end . "^$")
+ (body-end-function . nndoc-digest-body-end)
+ (body-begin . "^$")
+ (file-end . "^==============================================================================$")
+ (prepare-body-function . nndoc-unquote-dashes)
+ (subtype digest guess))
+ (lanl-gov-announce
+ (article-begin . "^\\\\\\\\\n")
+ (head-begin . "^\\(Paper.*:\\|arXiv:\\)")
+ (head-end . "\\(^\\\\\\\\.*\n\\|-----------------\\)")
+ (body-begin . "")
+ (body-end . "\\(-------------------------------------------------\\|%-%-%-%-%-%-%-%-%-%-%-%-%-%-\\|%%--%%--%%--%%--%%--%%--%%--%%--\\|%%%---%%%---%%%---%%%---\\)")
+ (file-end . "\\(^Title: Recent Seminal\\|%%%---%%%---%%%---%%%---\\)")
+ (generate-head-function . nndoc-generate-lanl-gov-head)
+ (article-transform-function . nndoc-transform-lanl-gov-announce)
+ (subtype preprints guess))
+ (git
+ (file-begin . "\n- Log ---.*")
+ (article-begin . "^commit ")
+ (head-begin . "^Author: ")
+ (body-begin . "^$")
+ (file-end . "\n-----------------------------------------------------------------------")
+ (article-transform-function . nndoc-transform-git-article)
+ (header-transform-function . nndoc-transform-git-headers))
+ (rfc822-forward
+ (article-begin . "^\n+")
+ (body-end-function . nndoc-rfc822-forward-body-end-function)
+ (generate-head-function . nndoc-rfc822-forward-generate-head)
+ (generate-article-function . nndoc-rfc822-forward-generate-article))
+ (outlook
+ (article-begin-function . nndoc-outlook-article-begin)
+ (body-end . "\0"))
+ (oe-dbx ;; Outlook Express DBX format
+ (dissection-function . nndoc-oe-dbx-dissection)
+ (generate-head-function . nndoc-oe-dbx-generate-head)
+ (generate-article-function . nndoc-oe-dbx-generate-article))
+ (forward
+ (article-begin . "^-+ \\(Start of \\)?forwarded message.*\n+")
+ (body-end . "^-+ End \\(of \\)?forwarded message.*$")
+ (prepare-body-function . nndoc-unquote-dashes))
+ (mail-in-mail ;; Wild guess on mailer daemon's messages or others
+ (article-begin-function . nndoc-mail-in-mail-article-begin))
+ (guess
+ (guess . t)
+ (subtype nil))
+ (digest
+ (guess . t)
+ (subtype nil))
+ (preprints
+ (guess . t)
+ (subtype nil))))
-(defvar nndoc-address nil)
+(defvar nndoc-binary-file-names ".[Dd][Bb][Xx]$"
+ "Regexp for binary nndoc file names.")
\f
+(defvoo nndoc-file-begin nil)
+(defvoo nndoc-first-article nil)
+(defvoo nndoc-article-begin nil)
+(defvoo nndoc-head-begin nil)
+(defvoo nndoc-head-end nil)
+(defvoo nndoc-file-end nil)
+(defvoo nndoc-body-begin nil)
+(defvoo nndoc-body-end-function nil)
+(defvoo nndoc-body-begin-function nil)
+(defvoo nndoc-head-begin-function nil)
+(defvoo nndoc-body-end nil)
+;; nndoc-dissection-alist is a list of sublists. Each sublist holds the
+;; following items. ARTICLE acts as the association key and is an ordinal
+;; starting at 1. HEAD-BEGIN [0], HEAD-END [1], BODY-BEGIN [2] and BODY-END
+;; [3] are positions in the `nndoc' buffer. LINE-COUNT [4] is a count of
+;; lines in the body. For MIME dissections only, ARTICLE-INSERT [5] and
+;; SUMMARY-INSERT [6] give headers to insert for full article or summary line
+;; generation, respectively. Other headers usually follow directly from the
+;; buffer. Value `nil' means no insert.
+(defvoo nndoc-dissection-alist nil)
+(defvoo nndoc-prepare-body-function nil)
+(defvoo nndoc-generate-head-function nil)
+(defvoo nndoc-article-transform-function nil)
+(defvoo nndoc-header-transform-function nil)
+(defvoo nndoc-article-begin-function nil)
+(defvoo nndoc-generate-article-function nil)
+(defvoo nndoc-dissection-function nil)
+(defvoo nndoc-pre-dissection-function nil)
-(defvar nndoc-status-string "")
-
-(defvar nndoc-group-alist nil)
+(defvoo nndoc-status-string "")
+(defvoo nndoc-group-alist nil)
+(defvoo nndoc-current-buffer nil
+ "Current nndoc news buffer.")
+(defvoo nndoc-address nil)
-;;; Interface functions
+(defconst nndoc-version "nndoc 1.0"
+ "nndoc version.")
-(defun nndoc-retrieve-headers (sequence &optional newsgroup server fetch-old)
- (save-excursion
- (set-buffer nntp-server-buffer)
- (erase-buffer)
- (let ((prev 2)
- article p beg lines)
- (nndoc-possibly-change-buffer newsgroup server)
- (if (stringp (car sequence))
- 'headers
- (set-buffer nndoc-current-buffer)
- (widen)
- (goto-char (point-min))
- (re-search-forward (or nndoc-first-article
- nndoc-article-begin) nil t)
- (or (not nndoc-head-begin)
- (re-search-forward nndoc-head-begin nil t))
- (re-search-forward nndoc-head-end nil t)
- (while sequence
- (setq article (car sequence))
- (set-buffer nndoc-current-buffer)
- (if (not (nndoc-forward-article (max 0 (- article prev))))
- ()
- (setq p (point))
- (setq beg (or (and
- (re-search-backward nndoc-article-begin nil t)
- (match-end 0))
- (point-min)))
- (goto-char p)
- (setq lines (count-lines
- (point)
- (or
- (and (re-search-forward nndoc-article-end nil t)
- (goto-char (match-beginning 0)))
- (goto-char (point-max)))))
-
- (set-buffer nntp-server-buffer)
- (insert (format "221 %d Article retrieved.\n" article))
- (insert-buffer-substring nndoc-current-buffer beg p)
- (goto-char (point-max))
- (or (= (char-after (1- (point))) ?\n) (insert "\n"))
- (insert (format "Lines: %d\n" lines))
- (insert ".\n"))
-
- (setq prev article
- sequence (cdr sequence)))
-
- ;; Fold continuation lines.
- (set-buffer nntp-server-buffer)
- (goto-char (point-min))
- (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
- (replace-match " " t t))
- 'headers))))
-
-(defun nndoc-open-server (server &optional defs)
- (nnheader-init-server-buffer)
- (if (equal server nndoc-current-server)
- t
- (if nndoc-current-server
- (setq nndoc-server-alist
- (cons (list nndoc-current-server
- (nnheader-save-variables nndoc-server-variables))
- nndoc-server-alist)))
- (let ((state (assoc server nndoc-server-alist)))
- (if state
- (progn
- (nnheader-restore-variables (nth 1 state))
- (setq nndoc-server-alist (delq state nndoc-server-alist)))
- (nnheader-set-init-variables nndoc-server-variables defs)))
- (setq nndoc-current-server server)
- (unless (eq nndoc-article-type 'guess)
- (nndoc-set-delims))
- t))
+\f
-(defun nndoc-close-server (&optional server)
- t)
+;;; Interface functions
-(defun nndoc-server-opened (&optional server)
- (and (equal server nndoc-current-server)
- nntp-server-buffer
- (buffer-name nntp-server-buffer)))
+(nnoo-define-basics nndoc)
-(defun nndoc-status-message (&optional server)
- nndoc-status-string)
+(deffoo nndoc-retrieve-headers (articles &optional newsgroup server fetch-old)
+ (when (nndoc-possibly-change-buffer newsgroup server)
+ (with-current-buffer nntp-server-buffer
+ (erase-buffer)
+ (let (article entry)
+ (if (stringp (car articles))
+ 'headers
+ (while articles
+ (when (setq entry (cdr (assq (setq article (pop articles))
+ nndoc-dissection-alist)))
+ (let ((start (point)))
+ (insert (format "221 %d Article retrieved.\n" article))
+ (if nndoc-generate-head-function
+ (funcall nndoc-generate-head-function article)
+ (insert-buffer-substring
+ nndoc-current-buffer (car entry) (nth 1 entry)))
+ (goto-char (point-max))
+ (unless (eq (char-after (1- (point))) ?\n)
+ (insert "\n"))
+ (insert (format "Lines: %d\n" (nth 4 entry)))
+ (insert ".\n")
+ (when nndoc-header-transform-function
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start (point))
+ (funcall nndoc-header-transform-function entry)))))))
+ (nnheader-fold-continuation-lines)
+ 'headers)))))
-(defun nndoc-request-article (article &optional newsgroup server buffer)
+(deffoo nndoc-request-article (article &optional newsgroup server buffer)
(nndoc-possibly-change-buffer newsgroup server)
(save-excursion
- (let ((buffer (or buffer nntp-server-buffer)))
+ (let ((buffer (or buffer nntp-server-buffer))
+ (entry (cdr (assq article nndoc-dissection-alist)))
+ beg)
(set-buffer buffer)
(erase-buffer)
- (if (stringp article)
- nil
- (nndoc-insert-article article)
- ;; Unquote quoted non-separators in digests.
- (if (and (eq nndoc-article-type 'digest)
- (eq nndoc-digest-type 'traditional))
- (progn
- (goto-char (point-min))
- (while (re-search-forward "^- -"nil t)
- (replace-match "-" t t))))
- ;; Some assholish digests do not have a blank line after the
- ;; headers. Aargh!
- (goto-char (point-min))
- (if (search-forward "\n\n" nil t)
- () ; We let this one pass.
- (if (re-search-forward "^[ \t]+$" nil t)
- (replace-match "" t t) ; We nix out a line of blanks.
- (while (and (looking-at "[^ ]+:")
- (zerop (forward-line 1))))
- ;; We just insert a couple of lines. If you read digests
- ;; that are so badly formatted, you don't deserve any
- ;; better. Blphphpht!
- (insert "\n\n")))
- t))))
-
-(defun nndoc-request-group (group &optional server dont-check)
+ (when entry
+ (cond
+ ((stringp article) nil)
+ (nndoc-generate-article-function
+ (funcall nndoc-generate-article-function article))
+ (t
+ (insert-buffer-substring
+ nndoc-current-buffer (car entry) (nth 1 entry))
+ (insert "\n")
+ (setq beg (point))
+ (insert-buffer-substring
+ nndoc-current-buffer (nth 2 entry) (nth 3 entry))
+ (goto-char beg)
+ (when nndoc-prepare-body-function
+ (funcall nndoc-prepare-body-function))
+ (when nndoc-article-transform-function
+ (funcall nndoc-article-transform-function article))
+ t))))))
+
+(deffoo nndoc-request-group (group &optional server dont-check info)
"Select news GROUP."
- (save-excursion
- (if (not (nndoc-possibly-change-buffer group server))
- (progn
- (setq nndoc-status-string "No such file or buffer")
- nil)
- (nndoc-set-header-dependent-regexps) ; hack for MIME digests
- (if dont-check
- t
- (save-excursion
- (set-buffer nntp-server-buffer)
- (erase-buffer)
- (let ((number (nndoc-number-of-articles)))
- (if (zerop number)
- (progn
- (nndoc-close-group group)
- nil)
- (insert (format "211 %d %d %d %s\n" number 1 number group))
- t)))))))
+ (let (number)
+ (cond
+ ((not (nndoc-possibly-change-buffer group server))
+ (nnheader-report 'nndoc "No such file or buffer: %s"
+ nndoc-address))