r5408: *** empty log message ***
[kmrcl.git] / web-utils.lisp
1 ;;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
2 ;;;; *************************************************************************
3 ;;;; FILE IDENTIFICATION
4 ;;;;
5 ;;;; Name:          web-utils.lisp
6 ;;;; Purpose:       Basic web utility functions
7 ;;;; Programmer:    Kevin M. Rosenberg
8 ;;;; Date Started:  Apr 2000
9 ;;;;
10 ;;;; $Id: web-utils.lisp,v 1.16 2003/06/17 06:18:09 kevin Exp $
11 ;;;;
12 ;;;; This file, part of KMRCL, is Copyright (c) 2002 by Kevin M. Rosenberg
13 ;;;;
14 ;;;; KMRCL users are granted the rights to distribute and use this software
15 ;;;; as governed by the terms of the Lisp Lesser GNU Public License
16 ;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
17 ;;;; *************************************************************************
18
19 (in-package #:kmrcl)
20
21
22 ;;; HTML/XML constants
23
24 (defvar *standard-xml-header* 
25   #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%"))
26
27 (defvar *standard-html-header* "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">")
28
29 (defvar *standard-xhtml-header*
30   #.(format nil "<?xml version=\"1.0\" encoding=\"iso-8859-1\" standalone=\"yes\"?>~%<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">"))
31
32
33 ;;; User agent functions
34
35 (defun user-agent-ie-p (agent)
36   "Takes a user-agent string and returns T for Internet Explorer."
37   (or (string-starts-with "Microsoft" agent)
38       (string-starts-with "Internet Explore" agent)
39       (search "Safari" agent)
40       (search "MSIE" agent)))
41
42 ;;; URL Functions
43
44 (defvar *base-url* "")
45 (defun base-url! (url)
46   (setq *base-url* url))
47
48 (defun make-url (page-name &key (base-dir *base-url*) (format :html) (vars nil))
49   (let ((amp (case format
50                (:html
51                 "&")
52                ((:xml :ie-xml)
53                 "&amp;"))))
54     (concatenate 'string 
55       base-dir page-name
56       (if vars
57           (let ((first-var (first vars)))
58             (concatenate 'string 
59               "?"  (car first-var) "=" (cadr first-var)
60               (mapcar-append-string 
61                #'(lambda (var) 
62                    (when (and (car var) (cadr var))
63                      (concatenate 'string 
64                        amp (car var) "=" (cadr var))))
65                (rest vars))))
66         ""))))
67
68 (defun make-url-new (page-name &key (base-dir *base-url*) (format :html)
69                                (vars nil))
70   (let ((amp (ecase format
71                (:html "&")
72                ((:xml :ie-xml) "&amp;"))))
73     (concatenate 'string 
74          base-dir page-name
75          (if vars
76              (let ((first-var (first vars)))
77                (concatenate 'string 
78                             "?"  (car first-var) "=" (cadr first-var)
79                             (mapcar-append-string 
80                              #'(lambda (var) 
81                                  (when (and (car var) (cadr var))
82                                    (concatenate 'string 
83                                                 amp (car var) "=" (cadr var))))
84                              (rest vars))))
85            ""))))
86
87 (defun decode-uri-query-string (s)
88   "Decode a URI query string field"
89   (declare (simple-string s)
90            (optimize (speed 3) (safety 0) (space 0)))
91   (do* ((old-len (length s))
92         (new-len (- old-len (* 2 (the fixnum (count-string-char s #\%)))))
93         (new (make-string new-len))
94         (p-old 0)
95         (p-new 0 (1+ p-new)))
96        ((= p-new new-len) new)
97     (declare (simple-string new)
98              (fixnum p-old p-new old-len new-len))
99          (let ((c (schar s p-old)))
100            (when (char= c #\+)
101              (setq c #\space))
102            (case c
103              (#\%
104               (unless (>= old-len (+ p-old 3))
105                 (error "#\% not followed by enough characters"))
106               (setf (schar new p-new)
107                     (code-char
108                      (parse-integer (subseq s (1+ p-old) (+ p-old 3))
109                                     :radix 16)))
110               (incf p-old 3))
111              (t
112               (setf (schar new p-new) c)
113               (incf p-old))))))