my-emacs-d/elpa/muse-3.20/muse-http.el

240 lines
9.0 KiB
EmacsLisp

;;; muse-http.el --- publish HTML files over HTTP
;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
;; Free Software Foundation, Inc.
;; This file is part of Emacs Muse. It is not part of GNU Emacs.
;; Emacs Muse 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 3, or (at your
;; option) any later version.
;; Emacs Muse 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 Emacs Muse; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Contributors:
;;; Code:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Publishing HTML over HTTP (using httpd.el)
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(require 'muse-html)
(require 'muse-project)
(require 'httpd)
(require 'cgi)
(defgroup muse-http nil
"Options controlling the behavior of Emacs Muse over HTTP."
:group 'press)
(defcustom muse-http-maintainer (concat "webmaster@" (system-name))
"The maintainer address to use for the HTTP 'From' field."
:type 'string
:group 'muse-http)
(defcustom muse-http-publishing-style "html"
"The style to use when publishing projects over http."
:type 'string
:group 'muse-http)
(defcustom muse-http-max-cache-size 64
"The number of pages to cache when serving over HTTP.
This only applies if set while running the persisted invocation
server. See main documentation for the `muse-http'
customization group."
:type 'integer
:group 'muse-http)
(defvar muse-buffer-mtime nil)
(make-variable-buffer-local 'muse-buffer-mtime)
(defun muse-sort-buffers (l r)
(let ((l-mtime (with-current-buffer l muse-buffer-mtime))
(r-mtime (with-current-buffer r muse-buffer-mtime)))
(cond
((and (null l-mtime) (null r-mtime)) l)
((null l-mtime) r)
((null r-mtime) l)
(t (muse-time-less-p r-mtime l-mtime)))))
(defun muse-winnow-list (entries &optional predicate)
"Return only those ENTRIES for which PREDICATE returns non-nil."
(let ((flist (list t)))
(let ((entry entries))
(while entry
(if (funcall predicate (car entry))
(nconc flist (list (car entry))))
(setq entry (cdr entry))))
(cdr flist)))
(defun muse-http-prune-cache ()
"If the page cache has become too large, prune it."
(let* ((buflist
(sort (muse-winnow-list (buffer-list)
(function
(lambda (buf)
(with-current-buffer buf
muse-buffer-mtime))))
'muse-sort-buffers))
(len (length buflist)))
(while (> len muse-http-max-cache-size)
(kill-buffer (car buflist))
(setq len (1- len)))))
(defvar muse-http-serving-p nil)
(defun muse-http-send-buffer (&optional modified code msg)
"Markup and send the contents of the current buffer via HTTP."
(httpd-send (or code 200) (or msg "OK")
"Server: muse.el/" muse-version httpd-endl
"Connection: close" httpd-endl
"MIME-Version: 1.0" httpd-endl
"Date: " (format-time-string "%a, %e %b %Y %T %Z")
httpd-endl
"From: " muse-http-maintainer httpd-endl)
(when modified
(httpd-send-data "Last-Modified: "
(format-time-string "%a, %e %b %Y %T %Z" modified)
httpd-endl))
(httpd-send-data "Content-Type: text/html; charset=iso-8859-1" httpd-endl
"Content-Length: " (number-to-string (1- (point-max)))
httpd-endl httpd-endl
(buffer-string))
(httpd-send-eof))
(defun muse-http-reject (title msg &optional annotation)
(muse-with-temp-buffer
(insert msg ".\n")
(if annotation
(insert annotation "\n"))
(muse-publish-markup-buffer title muse-http-publishing-style)
(muse-http-send-buffer nil 404 msg)))
(defun muse-http-prepare-url (target explicit)
(save-match-data
(unless (or (not explicit)
(string-match muse-url-regexp target)
(string-match muse-image-regexp target)
(string-match muse-file-regexp target))
(setq target (concat "page?" target
"&project=" muse-http-serving-p))))
(muse-publish-read-only target))
(defun muse-http-render-page (name)
"Render the Muse page identified by NAME.
When serving from a dedicated Emacs process (see the httpd-serve
script), a maximum of `muse-http-max-cache-size' pages will be
cached in memory to speed up serving time."
(let ((file (muse-project-page-file name muse-http-serving-p))
(muse-publish-url-transforms
(cons 'muse-http-prepare-url muse-publish-url-transforms))
(inhibit-read-only t))
(when file
(with-current-buffer (get-buffer-create file)
(let ((modified-time (nth 5 (file-attributes file)))
(muse-publishing-current-file file)
muse-publishing-current-style)
(when (or (null muse-buffer-mtime)
(muse-time-less-p muse-buffer-mtime modified-time))
(erase-buffer)
(setq muse-buffer-mtime modified-time))
(goto-char (point-max))
(when (bobp)
(muse-insert-file-contents file t)
(let ((styles (cddr (muse-project muse-http-serving-p)))
style)
(while (and styles (null style))
(let ((include-regexp
(muse-style-element :include (car styles)))
(exclude-regexp
(muse-style-element :exclude (car styles))))
(when (and (or (and (null include-regexp)
(null exclude-regexp))
(if include-regexp
(string-match include-regexp file)
(not (string-match exclude-regexp file))))
(not (muse-project-private-p file)))
(setq style (car styles))
(while (muse-style-element :base style)
(setq style
(muse-style (muse-style-element :base style))))
(if (string= (car style) muse-http-publishing-style)
(setq style (car styles))
(setq style nil))))
(setq styles (cdr styles)))
(muse-publish-markup-buffer
name (or style muse-http-publishing-style))))
(set-buffer-modified-p nil)
(muse-http-prune-cache)
(current-buffer))))))
(defun muse-http-transmit-page (name)
"Render the Muse page identified by NAME.
When serving from a dedicated Emacs process (see the httpd-serve
script), a maximum of `muse-http-max-cache-size' pages will be
cached in memory to speed up serving time."
(let ((inhibit-read-only t)
(buffer (muse-http-render-page name)))
(if buffer
(with-current-buffer buffer
(muse-http-send-buffer muse-buffer-mtime)))))
(defvar httpd-vars nil)
(defsubst httpd-var (var)
"Return value of VAR as a URL variable. If VAR doesn't exist, nil."
(cdr (assoc var httpd-vars)))
(defsubst httpd-var-p (var)
"Return non-nil if VAR was passed as a URL variable."
(not (null (assoc var httpd-vars))))
(defun muse-http-serve (page &optional content)
"Serve the given PAGE from this press server."
;; index.html is really a reference to the project home page
(if (and muse-project-alist
(string-match "\\`index.html?\\'" page))
(setq page (concat "page?"
(muse-get-keyword :default
(cadr (car muse-project-alist))))))
;; handle the actual request
(let ((vc-follow-symlinks t)
(muse-publish-report-threshhold nil)
muse-http-serving-p
httpd-vars)
(save-excursion
;; process any CGI variables, if cgi.el is available
(if (string-match "\\`\\([^&]+\\)&" page)
(setq httpd-vars (cgi-decode (substring page (match-end 0)))
page (match-string 1 page)))
(unless (setq muse-http-serving-p (httpd-var "project"))
(let ((project (car muse-project-alist)))
(setq muse-http-serving-p (car project))
(setq httpd-vars (cons (cons "project" (car project))
httpd-vars))))
(if (and muse-http-serving-p
(string-match "\\`page\\?\\(.+\\)" page))
(muse-http-transmit-page (match-string 1 page))))))
(if (featurep 'httpd)
(httpd-add-handler "\\`\\(index\\.html?\\|page\\(\\?\\|\\'\\)\\)"
'muse-http-serve))
(provide 'muse-http)
;;; muse-http.el ends here