;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10; Package: modlisp -*- ;;;; ************************************************************************* ;;;; FILE IDENTIFICATION ;;;; ;;;; Name: utils.lisp ;;;; Purpose: Utility functions for modlisp package ;;;; Programmer: Kevin M. Rosenberg ;;;; Date Started: Dec 2002 ;;;; ;;;; $Id$ ;;;; ************************************************************************* (in-package #:modlisp) (defun format->string (fmt) (case fmt (:html "text/html") (:xml "text/xml") (:text "text/plain") (otherwise fmt))) (defun format-string (fmt headers) `(("Content-Type" . ,(format->string fmt)) . ,headers)) (defmacro write-response ((&key headers len (status "200 OK")) &body body) (let ((result (gensym "RES-"))) `(progn (write-header-line "Status" ,status) (dolist (hdr ,headers) (write-header-line (car hdr) (cdr hdr))) ,@(and len `((write-header-line "Content-Length" ,len) (write-header-line "Keep-Socket" "1") (write-header-line "Connection" "Keep-Alive"))) (write-string "end" *modlisp-socket*) (write-char #\NewLine *modlisp-socket*) (let ((,result (progn ,@body))) (,(if len 'force-output 'finish-output) *modlisp-socket*) (setq *close-modlisp-socket* ,(not len)) ,result)))) (defmacro with-ml-page ((&key (format :html) (precompute t) headers) &body body) (if precompute `(output-ml-page ,format (with-output-to-string (*modlisp-socket*) ,@body) :headers ,headers) `(write-response (:headers (format-string ,format ,headers)) ,@body))) (defun redirect-to-location (url) (write-response (:status "307 Temporary Redirect" :headers `(("Location" . ,url))))) (defmacro output-ml-page (format html &key headers) (let ((str (gensym "STR-"))) `(let ((,str ,html)) (write-response (:len (format nil "~d" (length ,str)) :headers (format-string ,format ,headers)) (write-string ,str *modlisp-socket*))))) (defun output-html-page (str &key headers) (output-ml-page :html str :headers headers)) (defun output-xml-page (str &key headers) (output-ml-page :xml str :headers headers)) ;; Utility functions for library users (defun query-to-alist (posted-string &key (keyword t)) "Converts a posted string to an assoc list of keyword names and values, \"a=1&bc=demo\" => ((:a . \"1\") (:bc . \"demo\"))" (when posted-string (let ((alist '())) (dolist (name=val (kmrcl:delimited-string-to-list posted-string #\&) (nreverse alist)) (let ((name-val-list (kmrcl:delimited-string-to-list name=val #\=))) (if (= 2 (length name-val-list)) (destructuring-bind (name val) name-val-list (push (cons (if keyword (kmrcl:ensure-keyword name) name) (kmrcl:decode-uri-query-string val)) alist)) (cmsg-c :debug "Invalid number of #\= in ~S" name-val-list)))))))