--- /dev/null
+;;; cgi.el -- using Emacs for CGI scripting
+;;;
+;;; Author: Eric Marsden <emarsden@laas.fr>
+;;; Michael Olson <mwolson@gnu.org> (slight modifications)
+;;; Keywords: CGI web scripting slow
+;;; Version: 0.3
+;;; Time-stamp: <2001-08-24 emarsden>
+;;; Copyright: (C) 2000 Eric Marsden
+;;; Parts copyright (C) 2006 Free Software Foundation, Inc.
+;;
+;; This program 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 of
+;; the License, or (at your option) any later version.
+;;
+;; This program 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 this program; if not, write to the Free
+;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+;; MA 02111-1307, USA.
+;;
+;;
+;;; Commentary:
+;;
+;; People who like this sort of thing will find this the sort of
+;; thing they like. -- Abraham Lincoln
+;;
+;;
+;; Overview ==========================================================
+;;
+;; A simple library for the Common Gateway Interface for Emacs,
+;; allowing you to service requests for non static web pages in elisp.
+;; Provides routines for decoding arguments to GET- and POST-type CGI
+;; requests.
+;;
+;; Usage: place a shell script such as the following in your web
+;; server's CGI directory (typically called something like
+;; /var/www/cgi-bin/):
+;;
+;; ,-------------------------------------------------------------------
+;; | #!/bin/sh
+;; |
+;; | emacs -batch -l cgi.el -f cgi-calendar
+;; `-------------------------------------------------------------------
+;;
+;; (`cgi-calendar' is a sample elisp CGI script provided at the end of
+;; this file).
+;;
+;; Alternatively, if you're running version 2.x of the linux kernel
+;; you could make .elc files directly executable via the binfmt_misc
+;; mechanism and run them straight from the cgi-bin directory.
+;;
+;; Efficiency would be improved by having Emacs bind to the http
+;; service port and spawn a thread per connection. Extending Emacs to
+;; support server sockets and multithreading is left as an exercise
+;; for the reader.
+;;
+;; References:
+;; * rfc1738 "Uniform Resource Locators"
+;; * rfc1630 "Universal Resource Identifiers in WWW"
+;;
+;; Thanks to Christoph Conrad <christoph.conrad@gmx.de> for pointing
+;; out a bug in the URI-decoding.
+
+;;; Code:
+
+(eval-when-compile
+ (require 'cl)
+ (require 'calendar))
+
+(defconst cgi-url-unreserved-chars '(
+ ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m
+ ?n ?o ?p ?q ?r ?s ?t ?u ?v ?w ?x ?y ?z
+ ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M
+ ?N ?O ?P ?Q ?R ?S ?T ?U ?V ?W ?X ?Y ?Z
+ ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
+ ?\$ ?\- ?\_ ?\. ?\! ?\~ ?\* ?\' ?\( ?\) ?\,))
+
+(defun cgi-int-char (i)
+ (if (fboundp 'int-char) (int-char i) i))
+
+(defun cgi-hex-char-p (ch)
+ (declare (character ch))
+ (let ((hexchars '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
+ ?A ?B ?C ?D ?E ?F)))
+ (member (upcase ch) hexchars)))
+
+;; decode %xx to the corresponding character and + to ' '
+(defun cgi-decode-string (str)
+ (do ((i 0)
+ (len (length str))
+ (decoded '()))
+ ((>= i len) (concat (nreverse decoded)))
+ (let ((ch (aref str i)))
+ (cond ((eq ?+ ch)
+ (push ?\ decoded)
+ (incf i))
+ ((and (eq ?% ch)
+ (< (+ i 2) len)
+ (cgi-hex-char-p (aref str (+ i 1)))
+ (cgi-hex-char-p (aref str (+ i 2))))
+ (let ((hex (string-to-number (substring str (+ i 1) (+ i 3)) 16)))
+ (push (cgi-int-char hex) decoded)
+ (incf i 3)))
+ (t (push ch decoded)
+ (incf i))))))
+
+(defun cgi-position (item seq &optional start end)
+ (or start (setq start 0))
+ (or end (setq end (length seq)))
+ (while (and (< start end)
+ (not (equal item (aref seq start))))
+ (setq start (1+ start)))
+ (and (< start end) start))
+
+;; Parse "foo=x&bar=y+re" into (("foo" . "x") ("bar" . "y re"))
+;; Substrings are plus-decoded and then URI-decoded.
+(defun cgi-decode (q)
+ (when q
+ (flet ((split-= (str)
+ (let ((pos (or (cgi-position ?= str) 0)))
+ (cons (cgi-decode-string (substring str 0 pos))
+ (cgi-decode-string (substring str (+ pos 1)))))))
+ (mapcar #'split-= (split-string q "&")))))
+
+(defun cgi-lose (fmt &rest args)
+ (let ((why (apply #'format fmt args)))
+ (message "Script error: %s" why) ; to error_log
+ (princ "Content-type: text/html\n\n") ; to browser
+ (princ "<html><head><title>Script error</title></head>\r\n")
+ (princ "<body><h1>Script error</h1>\r\n<p>\r\n")
+ (princ why)
+ (princ "\r\n</body></html>\r\n")
+ (kill-emacs 0)))
+
+(defmacro cgi-evaluate (&rest forms)
+ `(condition-case why
+ (princ (with-output-to-string ,@forms))
+ (error (cgi-lose "Emacs Lisp error: %s" why))))
+
+(defun cgi-arguments ()
+ (let ((method (getenv "REQUEST_METHOD"))
+ req buf)
+ (cond ((null method)
+ (cgi-lose "No request method specified"))
+ ((string= "GET" method)
+ (unless (getenv "QUERY_STRING")
+ (cgi-lose "No query string for GET request"))
+ (cgi-decode (getenv "QUERY_STRING")))
+ ((string= "POST" method)
+ (setq req (getenv "CONTENT_LENGTH"))
+ (unless req
+ (cgi-lose "No content-length for POST request"))
+ (setq buf (get-buffer-create " *cgi*"))
+ (set-buffer buf)
+ (erase-buffer)
+ (loop for i from 1 to (string-to-number req)
+ do (insert (read-event)))
+ (cgi-decode (buffer-string)))
+ (t
+ (cgi-lose "Can't handle request method %s" method)))))
+
+;; ====================================================================
+;; a sample application: calendar via the web. If invoked without
+;; arguments, presents a calendar for the three months around the
+;; current date. You can request a calendar for a specific period by
+;; specifying the year and the month in the query string:
+;;
+;; ~$ lynx -dump 'http://localhost/cgi-bin/cal?year=1975&month=6'
+;;
+;; When run in batch mode, text normally displayed in the echo area
+;; (via `princ' for example) goes to stdout, and thus to the browser.
+;; Text output using `message' goes to stderr, and thus normally to
+;; your web server's error_log.
+;; ====================================================================
+
+(eval-and-compile
+ (if (fboundp 'calendar-extract-month)
+ (defalias 'cgi-calendar-extract-month 'calendar-extract-month)
+ (defalias 'cgi-calendar-extract-month 'extract-calendar-month))
+
+ (if (fboundp 'calendar-extract-year)
+ (defalias 'cgi-calendar-extract-year 'calendar-extract-year)
+ (defalias 'cgi-calendar-extract-year 'extract-calendar-year))
+
+ (if (fboundp 'calendar-generate)
+ (defalias 'cgi-calendar-generate 'calendar-generate)
+ (defalias 'cgi-calendar-generate 'generate-calendar)))
+
+(defun cgi-calendar-string ()
+ (require 'calendar)
+ (let* ((args (cgi-arguments))
+ (now (calendar-current-date))
+ (mnth (cdr (assoc "month" args)))
+ (month (if mnth (string-to-number mnth)
+ (cgi-calendar-extract-month now)))
+ (yr (cdr (assoc "year" args)))
+ (year (if yr (string-to-number yr)
+ (cgi-calendar-extract-year now))))
+ (with-temp-buffer
+ (cgi-calendar-generate month year)
+ (buffer-string))))
+
+(defun cgi-calendar ()
+ (cgi-evaluate
+ (princ "Content-type: text/html\n\n")
+ (princ "<html><head><title>Emacs calendar</title></head>\r\n")
+ (princ "<body> <h1>Emacs calendar</h1>\r\n")
+ (princ "<pre>\r\n")
+ (princ (cgi-calendar-string))
+ (princ "\r\n</pre></body></html>\r\n")))
+
+(provide 'cgi)
+
+;; cgi.el ends here