shr: Render td content with shr-descend
[gnus] / lisp / nndoc.el
index b3fa024..0dee06d 100644 (file)
@@ -1,16 +1,18 @@
 ;;; 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))