addfile ./CHANGES hunk ./CHANGES 1 + - 0.11 + - added support for ACL 8.1, thanks to Andrew Philpot + - 0.10 + - added support for multibyte-encodings + - switched default encoding to UTF8 + - there seems to be no full UTF8 support in CMUCL, so CMUCL doesn't + work in UTF8 mode + - 0.9 + - Makefile fixes for dvips, + - bugfix for CMUCL + - 0.8 + - added Support for Lispworks + - 0.7 + - added system definition for asdf + - 0.6.1 + - typo fixes + - 0.6 + - added HTTP cookie support, + - 0.5 + - reworked examples, now with examples for CMUCL and SBCL + - it doesn't work with GCL because GCL barfs on the (defpackage ...) form + - 0.4 + - fixed tag generating functions -> better HTML 4.0 strict compliance, + - fixed up examples for better style as well as full HTML 4.0 strict + compliance - validator.w3c.org is happy with their output + - 0.3 + - better examples, + - added more HTML tags - should be mostly complete now + - 0.2 + - cleanups, first public release + - 0.1 + - internal use only addfile ./LGPL-2.1 hunk ./LGPL-2.1 1 + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at least + three years, to give the same user the materials specified in + Subsection 6a, above, for a charge no more than the cost of + performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + addfile ./README hunk ./README 1 +README for lisp-cgi-utils + + Copyright (C) 2003,2004 Alexander Schreiber + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +author: Alexander Schreiber + +This is lisp-cgi-utils, a toolkit for writing CGI applications in Common Lisp. + +It consists of two packages: + - the package HTTP, implemented in the file http.lisp: + - the basic CGI interface, + - can send the HTTP headers, + - can read CGI environment variables, + - can parse GET/POST CGI parameters and offer easy access to them + - the package HTML: + - basic HTML formatting, with support for the most used tags (not yet + all, will be completed on demand), + - specialized support function for working with HTML forms, + - HTTP cookie support + +usage: + - load the HTTP and HTML packages into your Lisp image simply by + (load "http.lisp") + (load "html.lisp") + +Very simple examples are included in the examples directory. + +Notice: When sending cookies, be careful not to put ";" into the cookie + value as this confuses Microsofts broken browser called + Internet Explorer. This is also true for Mac OS X Safari and + Konqueror. addfile ./changelog hunk ./changelog 1 +cl-lisp-cgi-utils (0.11) stable; urgency=low + + * added support for ACL 8.1, thanks to Andrew Philpot + + -- Alexander Schreiber Sun, 17 Feb 2008 17:15:44 +0200 + +cl-lisp-cgi-utils (0.10) stable; urgency=low + + * fixes for multibyte encoding, made UTF8 the default encoding + + -- Alexander Schreiber Tue, 31 Oct 2006 12:15:08 +0200 + +cl-lisp-cgi-utils (0.9) stable; urgency=low + + * Makefile fixes for dvips, bugfix for CMUCL + + -- Alexander Schreiber Mon, 06 Feb 2006 12:15:08 +0200 + +cl-lisp-cgi-utils (0.8) stable; urgency=low + + * Added support for LispWorks + + -- Alexander Schreiber Sun, 07 Aug 2005 15:50:58 +0200 + + +cl-lisp-cgi-utils (0.7) unstable; urgency=low + + * Initial release + + -- Alexander Schreiber Thu, 19 Mar 2005 23:36:10 +0200 + adddir ./doc addfile ./doc/Makefile hunk ./doc/Makefile 1 +# +# Makefile for the lisp-cgi-utils documentation +# +# author: Alexander Schreiber +# +# CVS: $Id: Makefile 1439 2006-02-06 11:19:10Z als $ +# + +doc: examples cookies + + +examples: examples.dvi examples.ps examples.pdf examples.html examples.txt + +examples.ps: examples.dvi + dvips -o examples.ps examples.dvi + +# LaTeX runs are usually done twice for crossreferences + +examples.dvi: examples.tex + latex examples.tex && latex examples.tex + +examples.pdf: examples.tex + pdflatex examples.tex && pdflatex examples.tex + +examples.html: examples.tex + hevea -pedantic -fix examples.tex + +examples.txt: examples.tex + hevea -text -fix examples.tex + + + + +cookies: cookies.dvi cookies.ps cookies.pdf cookies.html cookies.txt + +cookies.ps: cookies.dvi + dvips -o cookies.ps cookies.dvi + +# LaTeX runs are usually done twice for crossreferences + +cookies.dvi: cookies.tex + latex cookies.tex && latex cookies.tex + +cookies.pdf: cookies.tex + pdflatex cookies.tex && pdflatex cookies.tex + +cookies.html: cookies.tex + hevea -pedantic -fix cookies.tex + +cookies.txt: cookies.tex + hevea -text -fix cookies.tex + + +clean: + rm -f *.ps *.pdf *.dvi *.html *.txt *.aux *.toc *.htoc *.haux *.log addfile ./doc/README hunk ./doc/README 1 +README for lisp-cgi-utils + + Copyright (C) 2003,2004 Alexander Schreiber + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; + version 2 of the License. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + +author: Alexander Schreiber + +This is lisp-cgi-utils, a toolkit for writing CGI applications in Common Lisp. + +It consists of two packages: + - the package HTTP, implemented in the file http.lisp: + - the basic CGI interface, + - can send the HTTP headers, + - can read CGI environment variables, + - can parse GET/POST CGI parameters and offer easy access to them + - the package HTML: + - basic HTML formatting, with support for the most used tags (not yet + all, will be completed on demand), + - specialized support function for working with HTML forms, + - HTTP cookie support + +usage: + - load the HTTP and HTML packages into your Lisp image simply by + (load "http.lisp") + (load "html.lisp") + +Very simple examples are included in the examples directory. + +Notice: When sending cookies, be careful not to put ";" into the cookie + value as this confuses Microsofts broken browser called + Internet Explorer. This is also true for Mac OS X Safari and + Konqueror. addfile ./doc/cookies.tex hunk ./doc/cookies.tex 1 +\documentclass[a4paper]{article} + +\usepackage[latin1]{inputenc} + +\usepackage{a4wide} + +\begin{document} + +\thispagestyle{empty} +\title{lisp-cgi-utils, HTTP cookie support documentation} +\author{Alexander Schreiber $$} +\maketitle + +Documentation of the HTTP cookie support for the +\texttt{lisp-cgi-utils} package. +\thispagestyle{empty} +\pagebreak + +\section{HTTP cookie support} + +Since \texttt{lisp-cgi-utils} is a server-side package, things are +viewed from the server side in the documentation below. The are two +parts to the cookie support: sending cookies and receiving them. Both +are described separately in detail below. + +\subsection{Sending cookies} + +Since cookies are sent as part of the HTTP headers of a HTTP request, +all cookie sending must be done before sending the HTTP headers. The +function for sending cookies is called \texttt{http-add-cookie}, with +the parameters according to figure \ref{fig:http-add-cookie-parm} and +an usage example in figure \ref{fig:http-add-cookie-example}. + +\begin{figure}[htbp] + \centering + \texttt{(http-add-cookie name value [Comment Domain Max-Age Path + Secure Version])} + \begin{itemize} + \item \texttt{name} and \texttt{value} are required parameters, they + contain the name and the value of the cookie to be sent, + \item \texttt{Comment} - a comment for the cookie; optional, string, + \item \texttt{Domain} - the domain for which the cookie is valid; + optional, string, + \item \texttt{Max-Age} - maximum age of the cookie, in seconds; + optional, number, + \item \texttt{Path} - the path for which the cookie is valid; + optional, string, + \item \texttt{Secure} - wether the cookie should only be transmitted + over a secure connection; optional, boolean, default \texttt{nil}, + \item \texttt{Version} - the version of the cookie specification; + optional, number, default 1, modify at your own peril (it breaks + the standard!) + \end{itemize} + \caption{\texttt{http-add-cookie} - parameters} + \label{fig:http-add-cookie-parm} +\end{figure} + +\begin{figure}[htbp] + \centering +\begin{verbatim} +(http-add-cookie "application" "shopping basket" :Path "/appserv/") +(http-send-headers) +(princ (html:html-header "shopping basket")) +(princ (html:body + (html:h1 "Your shopping basket") + (html:p "Reload this page to go shopping ..."))) +(princ (html:html-footer)) +\end{verbatim} + \caption{\texttt{http-add-cookie} - example} + \label{fig:http-add-cookie-example} +\end{figure} + +\subsection{Receiving cookies} + +To read cookies sent by the client you can read the list of all +cookies and access the value of a named cookie. The function to get +the list of all cookies sent by the client is named +\texttt{http-cookie-list} and documented in figure +\ref{fig:http-cookie-list}, the function for accessing the value of a +single name cookie, \texttt{http-cookie} is documented in figure +\ref{fig:http-cookie}. Figure \ref{fig:read-cookies} shows an example +for both. +\begin{figure}[htbp] + \centering + \texttt{(http-cookies-list)} \\ + Returns a list of all cookies sent by the client, the list will be + empty if the client sent no cookies. + \caption{\texttt{http-cookie-list}} + \label{fig:http-cookie-list} +\end{figure} + +\begin{figure}[htbp] + \centering + \texttt{(http-cookie name)} \\ +Returns the value of the cookie \texttt{name}. The cookie name is case +insensitive, as specified in the RFCs. Returns \texttt{nil} if no +matching cookie can be found. + \caption{\texttt{http-cookie}} + \label{fig:http-cookie} +\end{figure} + +\begin{figure}[htbp] + \centering + \begin{verbatim} +(defun show-cookies () + "show all received cookies" + (concatenate 'string + (html:h2 "cookies sent by user agent") + (html:table '((border . "1")) + (html:tr + (html:th "name") + (html:th "value")) + (apply #'concatenate 'string + (mapcar #'(lambda (name) + (html:tr + (html:td + (html:code name)) + (html:td + (html:code (http:http-cookie name))))) + (http:http-cookie-list)))))) +\end{verbatim} + \caption{Example for showing all cookies sent by the client} + \label{fig:read-cookies} +\end{figure} +\section{Standards \dots does anybody follow them?} + +\subsection{Relevant RFCs} + +There are several standards that are of relevance for the HTTP cookie +support as implemented in the \texttt{lisp-cgi-utils} package for +Lisp: + +\begin{itemize} +\item RFC2616: Hypertext Transfer Protocol -- HTTP/1.1, specifying the + basic HTTP protocol, +\item RFC2109: HTTP State Management Mechanism, specifying HTTP state + management using cookies, +\item RFC2965: HTTP State Management Mechanism, specifying HTTP state + management using cookies, supersedes RFC2109. +\end{itemize} + +\subsection{Anybody reading any RFCs lately?} + +The first version of the HTTP cookie support for the +\texttt{lisp-cgi-utils} package was implemented along RFC2965, since +the author naively assumed that it would be best to follow the most +current RFC. \emph{This turned out not to be the case.} RFC2965 +specifies the \texttt{Set-Cookie2}-Header which was happily ignored by +the authors standard test browser, Firefox. Switching to the +\texttt{Set-Cookie}-Header as specified by RFC2109 resulted in things +suddenly working. So much for basing your implementation on the +newest\footnote{At the time of this writing, RFC2965 was almost 4 and +RFC2109 7 years old.} standard. + +\subsection{We don't care, we don't read those RFCs anyway} + +That wasn't the last surprise and the next one is really annoying. The +relevant RFCs specify the value of a cookie to be either a token (a +string of characters not containing whitespace or special characters) or +a quoted string, with semicolon separating the entries of the +\texttt{Set-Cookie}- or \texttt{Set-Cookie2}-Header. Parsing this +format can be done with a very simple state machine. That was +obviously way too complex for some browser programmers and so they +simply decided to chop the entries apart at every semicolon. The +result of this stupidity is that if a cookie value, written as a +quoted string, contains a semicolon, some browser will chop off +everything behind the semicolon, while keeping the starting quote +character, resulting in the breakage shown in figure \ref{fig:cookie-parser}. + +\begin{figure}[htbp] + \centering + \begin{enumerate} + \item The server sends: \\ + \texttt{Set-Cookie: foo="bar;baz"; quux=xyzzy; Version=1} + \item The broken browser breaks the items at the first semicolon, + reading the cookie as: \\ + \texttt{foo="bar} + \item The broken browser sends back to the server: \\ + \texttt{Cookie: foo="bar; quux=xyzzy} + \item The server side, parsing according to the RFCs, reads the + cookie(s) as: \\ + \texttt{foo = "bar; quux=xyzzy"} + \end{enumerate} + Great, isn't it? + \caption{Broken cookie parsing} + \label{fig:cookie-parser} +\end{figure} + +The obvious work-around for this is to avoid sending cookies +containing semicolons. Fortunately, most browsers do the right thing +and work correctly. But since a significant percentage of users out +there is using broken browsers, one has to program with this breakage +in mind. + +\subsection{List of fools} + +At the time of this writing, the following browsers exhibit the +abovementioned broken behaviour of not properly reading quoted-string +cookie values with embedded semicolons: +\begin{itemize} +\item Internet Explorer (all tested versions, including + current)\footnote{No, I'm not the least surprised here.}, +\item Konqueror, the browser of the KDE project (versions from KDE2 + and KDE3, including current)\footnote{Looks they are \emph{really} + faithful about being bug-for-bug compatible with the Windows side.} +\item Safari, the browser of Mac OS X (including current) - no + surprise here, since it uses the Konqueror core, +\item links, a terminal based browser (only version 0.96 tested, more + tests needed) +\end{itemize} + +\end{document} addfile ./doc/examples.tex hunk ./doc/examples.tex 1 +\documentclass[a4paper]{article} + +\usepackage[latin1]{inputenc} + +\usepackage{a4wide} + +\begin{document} + +\thispagestyle{empty} +\title{lisp-cgi-utils, examples documentation} +\author{Alexander Schreiber $$} +\maketitle + +Short documentation for the example programs for the +\texttt{lisp-cgi-utils} package. +\thispagestyle{empty} +\pagebreak + +\section{Supported Lisp implementations} + +For testing and demonstration purpose, the full package contains +examples for all currently explizitly supported Lisp +implementations. These are: +\begin{itemize} +\item CLisp, +\item CMU Common Lisp (CMUCL), +\item Steel Bank Common Lisp (SBCL). +\end{itemize} + +Since \texttt{lisp-cgi-utils} is written in straight Common Lisp (with +currently only one implementation specific function - the one for +reading environment variables), no special adaptions for each +implementation are needed and the test programs for all +implementations share the same Lisp code. The only difference is the +way the Lisp environments are invoked for running as CGI programs. + +Therefore, the example CGIs always consist of two parts: a simple +shell script for invoking the specific Lisp environment with specified +Lisp source file to run as CGI process and the Lisp source file +implementing the program. + +\section{The examples} + +\subsection{simple web page example - \texttt{simple.lisp}} + +As the name implies, this is a very simple example. It loads the two +Lisp files (and packages) which make up the \texttt{lisp-cgi-utils} +library, sends the standard HTML headers to the client and generates a +very simple web page with a title, two headlines, a short list and two +very short paragraphs of text. It is both an very basic introduction +examples and a simple test to check if \texttt{lisp-cgi-utils} +works at all. + +\subsection{HTML forms example - \texttt{forms.lisp}} + +As stated in the general advertisement for the \texttt{lisp-cgi-utils} +package, it has special support for HTML forms. The forms example is +here to demonstrate some of this. It contains a simple HTML forms +input line as well as a multiple selector box. Forms that post their +results back to the CGI sending them are made very simple with the +\texttt{html:form-self-post} function which supports exactly this kind +of self-referential forms. + +The form also lists all environment variables of the CGIs execution +environment. + +\subsection{HTTP cookie example - \texttt{cookies.lisp}} + +Since \texttt{lisp-cgi-utils} also supports HTTP cookies for easily +keeping state across several HTTP request, this example shows the +cookie support. It sends a set of cookies to the client and lists all +cookies it received from the client, showing how to use the functions +for cookie-support. + +\section{Lisp running as CGI} + +\subsection{General considerations} + +Using the CGI interface for web based Lisp applications is rather +unusual as most applications seem to use the application server +concept (either with a Lisp running behind a webserver or with the +Lisp handling HTTP directly). + +Since we'll be using Lisp environments for scripting and most of them +aren't really with scripting as a primary use, we need to employ some +tricks. + + +\subsection{Scripting with CMUCL} + +For scripting wih CMUCL, we use a shell script that invokes CMUCL with +the specified main Lisp file: + +\begin{figure}[htbp] + \centering +\begin{verbatim} +#!/bin/sh +# +# Script for CMUCL + +exec lisp -batch -quiet -load simple.lisp +\end{verbatim} + \caption{Shell script wrapper for CMUCL} + \label{fig:cmucl-script} +\end{figure} + +Here the \texttt{-quiet} and \texttt{-batch} options to CMUCL suppress +further output from the Lisp environment that would normaly occur and +disrupt the CGI output. + +\subsection{Scripting with SBCL} + +Steel Bank Common Lisp is handled similiar to CMUCL - which is not +much of a surprise, considering that SBCL is an offspring from CMUCL. + +\begin{figure}[htbp] + \centering +\begin{verbatim} +#!/bin/sh +# + +export HOME=/ + +exec sbcl --noinform --end-runtime-options --noprogrammer --load simple.lisp +\end{verbatim} + \caption{Shell script wrapper for SBCL} + \label{fig:sbcl-script} +\end{figure} + +Again, there are options to shut up the normal flood of informational +messages the Lisp environment would spew to the standard output. With +\texttt{--noinform} and \texttt{--noprogrammer} it keeps acceptably +quiet (although it still makes noise on standard error). + +If you are familiar with the workings of SBCL, you might at that point +wonder what the hell I'm up to. Everybody else running the SBCL CGI +examples might wonder why the SBCL example is slow as molasses +compared to the other two. + +To calm the first ones, I know that this is +not the way one uses SBCL - but building an image with the full +compiled code would be too much for just a short test and +demonstration. + +The enlighten the others: SBCL has no real interpreter, +it always compiles. So it starts up, loads the \texttt{lisp-cgi-utils} +library, compiles it, loads the example code, compiles it and finally +runs the CGI. For every request. Of course that's slow. It's also not +the way one does things normally with SBCL. Normally, one would build +an image for deployment and run from this image. Consult the SBCL +documentation for more. + +\subsection{Scripting with Clisp} + +Using Clisp for scripting is very simple. Just put it at the first +line of the script, using the UNIX \texttt{\#!}-Magic, write Lisp code +below and you are done. + +\begin{figure}[htbp] + \centering +\begin{verbatim} +#!/usr/bin/clisp -Efile ISO-8859-1 -Eterminal ISO-8859-1 -Emisc ISO-8859-1 +;;;; +;;;; + +(load "simple.lisp") +\end{verbatim} + \caption{Scripting with CLisp} + \label{fig:clisp-script} +\end{figure} + +That's it, nothing more. You can even drop off all the options I used +in this example since they are only there to force CLisp to run in +the ISO Latin-1 character set instead of the default (which {\em + might}, but is not guaranteed to, be UTF-8). + +\section{Closing remarks} + +The examples documented here only show the general usage of the +\texttt{lisp-cgi-utils} library. More documentation is available +in the \texttt{doc} directory of the distribution and as docstrings +attached to the symbols of the library. + +\end{document} adddir ./examples addfile ./examples/cookies.lisp hunk ./examples/cookies.lisp 1 +;;;; +;;;; file: examples/cookies.cgi +;;;; +;;;; Example CGI script for lisp-cgi-utils, shows use of cookies +;;;; +;;;; Copyright (C) 2003,2004 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: cookies.lisp 1517 2006-10-26 19:44:51Z als $ +;;;; + + +(load "../http.lisp") +(load "../html.lisp") + + +(defun header (title) + "Set up the page head" + (http:http-send-headers) + (princ (html:html-header title))) + +(defun iso8601-timestamp (&optional universal-time) + "Returns an ISO8601 timestamp, using either current time or the +supplied universal time." + (multiple-value-bind (ss mm hh dd mo yy dow dst tz) + (decode-universal-time + (if (integerp universal-time) + universal-time + (get-universal-time))) + (format nil "~4,'0D-~2,'0D-~2,'0DT~2,'0D:~2,'0D:~2,'0D" + yy mo dd hh mm ss))) + + +(defun make-standard-cookies () + "Create a set of standard cookies" + (http:http-add-cookie "timestamp" + (iso8601-timestamp) + :Path "/lisp") + (http:http-add-cookie "application" "lisp-cgi-utils cookietest" + :Path "/lisp") + (http:http-add-cookie "magic-word" "gogglefrog" + :Path "/lisp" + :Max-Age 128) + (http:http-add-cookie "nasty-one" + "this ; is ; a ; list = true (\\\"false\\\")")) + + +(defun simple-form-query (text) + "Prints a simple form, using selfreferential POST" + (html:table + (html:tr + (html:td "your input: ") + (html:td + (if (null (http:http-query-parameter "some-string")) + "no input given" + (html:kbd + (http:http-query-parameter "some-string"))))) + (html:tr + (html:td "some-string: ") + (html:td + (html:form-self-post + (html:p ; needed to satisfy HTML 4.0 strict + (html:input (pairlis + '(type size name value) + (append + (list "text" "64" "some-string") + (list + (if (null (http:http-query-parameter "some-string")) + text + (http:http-query-parameter "some-string")))))) + (html:form-reset-submit))))))) + +(defun show-cookies () + "show all received cookies" + (concatenate 'string + (html:h2 "cookies sent by user agent") + (html:table '((border . "1")) + (html:tr + (html:th "name") + (html:th "value")) + (apply #'concatenate 'string + (mapcar #'(lambda (name) + (html:tr + (html:td + (html:code name)) + (html:td + (html:code (http:http-cookie name))))) + (http:http-cookie-list)))))) + +;;;; ------------ main ------------------- + +(http:http-init) +(make-standard-cookies) +(header "lisp-cgi-utils example - cookies") + +(princ (html:body + (html:h1 "lisp-cgi-utils example - cookies"))) + +(princ (simple-form-query "enter something")) +(princ (show-cookies)) +(princ (html:html-footer)) + +;-------------------- + addfile ./examples/forms.lisp hunk ./examples/forms.lisp 1 +;;;; +;;;; file: examples/forms.lisp +;;;; +;;;; +;;;; Forms using example for lisp-cgi-utils +;;;; +;;;; Copyright (C) 2003,2004 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: forms.lisp 1517 2006-10-26 19:44:51Z als $ +;;;; + + +(load "../http.lisp") +(load "../html.lisp") + + + +(defun header (title) + "Set up the page head" + (http:http-send-headers) + (princ (html:html-header title))) + + + +(defun simple-form-query (text) + "Prints a simple form, using selfreferential POST" + (html:table + (html:tr + (html:td "your input: ") + (html:td + (if (null (http:http-query-parameter "some-string")) + "no input given" + (html:kbd + (http::url-encode-string + (http:http-query-parameter "some-string")))))) + (html:tr + (html:td "some-string: ") + (html:td + (html:form-self-post + (html:p ; needed to satisfy HTML 4.0 strict + (html:input (pairlis + '(type size name value) + (append + (list "text" "64" "some-string") + (list + (if (null (http:http-query-parameter "some-string")) + text + (http:http-query-parameter "some-string")))))) + (html:form-reset-submit))))))) + +(defun form-multi-select-query () + "Prints a multi selector and the selected results." + (html:table + (html:tr + (html:td "you selected: ") + (html:td + (if (null (http:http-query-parameter "multi-select")) + "no input given" + (html:kbd + (if (null (listp (http:http-query-parameter "multi-select"))) + (format nil "~a" + (http:http-query-parameter "multi-select")) + (format nil "~{~a ~}" + (http:http-query-parameter "multi-select"))))))) + + + (html:tr + (html:td "select one or more: ") + (html:td + (html:form-self-post + (html:p + (html:form-select-multiple "multi-select" + (list + "one" "two" "three" "four" "five" "six"))) + (html:form-reset-submit)))))) + + + +(defun hash-to-proplist (in-hash) + (let ((prop-list '())) + (maphash (lambda (key value) + (setf prop-list + (append prop-list + (list (cons key value))))) + in-hash) + prop-list)) + +(defun list-env-vars () + "List all availabe CGI environment variables." + (concatenate 'string + (html:h2 "CGI environment variables") + (html:table + (html:tr + (html:th '(("align" . "left")) "variable") + (html:th '(("align" . "left")) "value")) + (apply #'concatenate 'string + (mapcar (lambda (item) + (html:tr + (html:td '(("align" . "left")) + (car item)) + (html:td '(("align" . "left")) + (cdr item)))) + (hash-to-proplist + (http:http-get-env-vars))))))) + + +;;;; ------------ main ------------------- + +(http:http-init) + +(header "lisp-cgi-utils example - forms") + +(princ (html:body + (html:h1 "lisp-cgi-utils example - forms") + (simple-form-query "enter some text ...") + (form-multi-select-query) + (list-env-vars))) + +(princ (html:html-footer)) + +(quit) +;-------------------- + addfile ./examples/simple.lisp hunk ./examples/simple.lisp 1 + +;;;; +;;;; file: examples/simple.lisp +;;;; +;;;; Example CGI script for lisp-cgi-utils, simple test +;;;; +;;;; Copyright (C) 2003,2004 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: simple.lisp 1517 2006-10-26 19:44:51Z als $ +;;;; + + +(load "../http.lisp") +(load "../html.lisp") + +(http:http-init) +(http:http-send-headers) + +(princ (html:html-header "html.lisp example page")) + +(princ (html:body + (html:h1 "test page for html.lisp package") + (html:h2 "lists") + (html:ul + (html:li "first entry") + (html:li "second entry")) + (html:p "a simple paragraph" " and more text") + (html:p '((class . "tester")) "another one"))) +(princ (html:html-footer)) + +(quit) addfile ./examples/test-clisp-cookies.cgi hunk ./examples/test-clisp-cookies.cgi 1 +#!/usr/bin/clisp -Efile UTF-8 -Eterminal UTF-8 -Emisc UTF-8 +;;;; +;;;; file: examples/test-clisp-cookies.cgi +;;;; +;;;; Example CGI script for lisp-cgi-utils, shows use of cookies +;;;; +;;;; Copyright (C) 2003-2006 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: test-clisp-cookies.cgi 1517 2006-10-26 19:44:51Z als $ +;;;; + +(load "cookies.lisp") + addfile ./examples/test-clisp-forms.cgi hunk ./examples/test-clisp-forms.cgi 1 +#!/usr/bin/clisp -Efile UTF-8 -Eterminal UTF-8 -Emisc UTF-8 +;;;; +;;;; file: examples/test-clisp-forms.cgi +;;;; +;;;; Forms using example for lisp-cgi-utils +;;;; +;;;; Copyright (C) 2003-2006 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: test-clisp-forms.cgi 1517 2006-10-26 19:44:51Z als $ +;;;; + +(load "forms.lisp") + + addfile ./examples/test-clisp-simple.cgi hunk ./examples/test-clisp-simple.cgi 1 +#!/usr/bin/clisp -Efile UTF-8 -Eterminal UTF-8 -Emisc UTF-8 +;;;; +;;;; +;;;; +;;;; Copyright (C) 2003-2006 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: test-clisp-simple.cgi 1517 2006-10-26 19:44:51Z als $ +;;;; + +(load "simple.lisp") + addfile ./examples/test-cmucl-cookies.cgi hunk ./examples/test-cmucl-cookies.cgi 1 +#!/bin/sh + +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-cmucl-cookies.cgi 1517 2006-10-26 19:44:51Z als $ +# + +exec lisp -batch -quiet -load cookies.lisp + addfile ./examples/test-cmucl-forms.cgi hunk ./examples/test-cmucl-forms.cgi 1 +#!/bin/sh +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-cmucl-forms.cgi 1517 2006-10-26 19:44:51Z als $ +# + +exec lisp -batch -quiet -load forms.lisp + addfile ./examples/test-cmucl-simple.cgi hunk ./examples/test-cmucl-simple.cgi 1 +#!/bin/sh +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-cmucl-simple.cgi 1517 2006-10-26 19:44:51Z als $ +# + +exec lisp -batch -quiet -load simple.lisp + addfile ./examples/test-sbcl-cookies.cgi hunk ./examples/test-sbcl-cookies.cgi 1 +#!/bin/sh +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-sbcl-cookies.cgi 1517 2006-10-26 19:44:51Z als $ +# + +# it barfs like mad if $HOME isn't set - fucking great + +export HOME=/ + +# this is slow like molasses because it compiles _everything_ first +# Remember: SBCL is a compiler-only Lisp and only simulates the interpreter +# by compiling the form and the executing it. +# for normal deployment, one would work with an already compiled image + +exec sbcl --noinform --end-runtime-options --noprogrammer --load cookies.lisp + addfile ./examples/test-sbcl-forms.cgi hunk ./examples/test-sbcl-forms.cgi 1 +#!/bin/sh +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-sbcl-forms.cgi 1517 2006-10-26 19:44:51Z als $ +# + +# it barfs like mad if $HOME isn't set - fucking great + +export HOME=/ + +# this is slow like molasses because it compiles _everything_ first +# for normal deployment, one would work with an already compiled image + +# exec sbcl --noinform --end-runtime-options --noprogrammer --load forms.lisp +exec sbcl --noinform --end-runtime-options --noprint --load forms.lisp + addfile ./examples/test-sbcl-simple.cgi hunk ./examples/test-sbcl-simple.cgi 1 +#!/bin/sh +# +# +# +# Copyright (C) 2003-2006 Alexander Schreiber +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Library General Public +# License as published by the Free Software Foundation; +# version 2 of the License. +# +# This library 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 +# Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public +# License along with this library; if not, write to the Free +# Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# author : Alexander Schreiber +# version: $Id: test-sbcl-simple.cgi 1517 2006-10-26 19:44:51Z als $ +# + +# it barfs like mad if $HOME isn't set - fucking great + +export HOME=/ + +# this is slow like molasses because it compiles _everything_ first +# for normal deployment, one would work with an already compiled image + +exec sbcl --noinform --end-runtime-options --noprogrammer --load simple.lisp + addfile ./html.lisp hunk ./html.lisp 1 +;;;; file: html.lisp +;;;; +;;;; Function library for generating HTML output from CommonLisp. +;;;; +;;;; Copyright (C) 2003,2004 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: html.lisp 1728 2008-02-17 16:16:22Z als $ +;;;; + + +(defpackage :html + (:use :common-lisp) + (:documentation + "A simple HTML generating package. Offers standard functions for most + HTML tags (some are missing) and special function/macros for different +jobs.") + (:export #:a #:br #:hr + #:h1 #:h2 #:h3 #:h4 #:h5 #:h6 + #:body #:head #:html #:title + #:img + #:center + #:font #:basefont + #:ins #:del + #:ol #:ul #:li #:dl #:menu #:dir + #:p + #:em #:strong #:code #:samp #:kbd #:var #:cite #:dfn + #:acronym #:abbr + #:table #:thead #:tfoot #:tbody #:tr #:td #:th #:col #:colgroup + #:address + #:frameset #:frame #:noframes #:iframe + #:object #:applet + #:script #:noscript + #:div #:span + #:pre #:blockquote + #:html-header #:html-footer + #:form #:form-reset-submit #:form-hidden #:form-checkbox + #:form-self-post + #:form-select #:form-select-multiple #:form-textarea + #:input #:textarea #:button #:fieldset #:legend #:label + #:force-linebreaks + #:label)) + +(in-package :html) + +(eval-when (:compile-toplevel) + (proclaim '(optimize (safety 3) (speed 3)))) + + +;; (defconstant +html-header+ +;; " +;;" +;; "default HTML header") + + +(defconstant +html-header+ + " + + +" + "default html header") + +;;; macro to generate generic HTML tag functions +;;; the generated functions take a list of string arguments and print +;;; them separated by newline, surrounded by the appropriate opening +;;; and closing tags + +(defmacro def-html-fun (tag &key start-with-newline (close-tag t)) + "generate a function to return a HTML tagged string" + `(defun ,tag (&rest args) + (let ((attr (if (consp (first args)) (first args) + nil)) + (content (if (consp (first args)) (rest args) + args))) + (apply #'concatenate 'string + (append + ,(if start-with-newline + `(list (string #\Newline))) + (list "<" (string-downcase (symbol-name ',tag))) + (unless (null attr) + (list (apply #'concatenate 'string + (append + (mapcar #'(lambda (pair) + (concatenate + 'string + " " + (if (stringp (car pair)) (car pair) + (string-downcase (car pair))) + "=\"" + (string (cdr pair)) + "\"")) + attr))))) + (list ">") + content + ,(if close-tag + `(list "")) + (list (string #\Newline))))))) + + + +;;; generate misc. generic HTML tag functions +;;; highly incomplete list, more will be added as needed + + +(def-html-fun html) +(def-html-fun head :start-with-newline t) +(def-html-fun body :start-with-newline t) +(def-html-fun title) + +(def-html-fun h1 :start-with-newline t) +(def-html-fun h2 :start-with-newline t) +(def-html-fun h3 :start-with-newline t) +(def-html-fun h4 :start-with-newline t) +(def-html-fun h5 :start-with-newline t) +(def-html-fun h6 :start-with-newline t) + +(def-html-fun table :start-with-newline t) +(def-html-fun tr :start-with-newline t) +(def-html-fun thead :start-with-newline t) +(def-html-fun tfoot :start-with-newline t) +(def-html-fun tbody :start-with-newline t) +(def-html-fun td) +(def-html-fun th :start-with-newline t) +(def-html-fun col) +(def-html-fun colgroup :start-with-newline t) + +; logical text markup +(def-html-fun em) +(def-html-fun strong) +(def-html-fun code) +(def-html-fun samp) +(def-html-fun kbd) +(def-html-fun var) +(def-html-fun cite) +(def-html-fun dfn) +(def-html-fun acronym) +(def-html-fun abbr) + +(def-html-fun p :start-with-newline t) + +(def-html-fun center :start-with-newline t) + +(def-html-fun ol :start-with-newline t) +(def-html-fun ul :start-with-newline t) +(def-html-fun dl :start-with-newline t) +(def-html-fun menu :start-with-newline t) +(def-html-fun dir :start-with-newline t) +(def-html-fun li) + +(def-html-fun a) +(def-html-fun img) + +(def-html-fun ins) +(def-html-fun del) + +(def-html-fun font) +(def-html-fun basefont) + +; frame-crap +(def-html-fun frameset :start-with-newline t) +(def-html-fun noframes :start-with-newline t) +(def-html-fun frame :start-with-newline t) +(def-html-fun iframe :start-with-newline t) + +; JavaScript +(def-html-fun script :start-with-newline t) +(def-html-fun noscript :start-with-newline t) + +; embedded multimedia objects/applets +(def-html-fun object :start-with-newline t) +(def-html-fun applet :start-with-newline t) + + +(def-html-fun address) + +(def-html-fun br :start-with-newline t) +(def-html-fun hr :start-with-newline t) + +(def-html-fun div) +(def-html-fun span) + +(def-html-fun pre :start-with-newline t) +(def-html-fun blockquote :start-with-newline t) + +;;; form handling + +(def-html-fun form :start-with-newline t) +(def-html-fun input :start-with-newline t :close-tag nil) +(def-html-fun textarea :start-with-newline t) +(def-html-fun button :start-with-newline t) +(def-html-fun fieldset :start-with-newline t) +(def-html-fun legend :start-with-newline t) +(def-html-fun label :start-with-newline t) + +;;; specialiced functions + +(defun html-header (title &key stylesheet made metatags) + "return html head section with page title and optional +stylesheet, made entry, meta tag list, ends with an open body tag." + (let ((buf (make-string-output-stream))) + (check-type metatags list) + (format buf "~a~%~%" +html-header+) + (unless (null stylesheet) ; handle stylesheet argument + ;; first, handle old browsers + (format buf "~%") + ;; then, handle new browsers + (format buf "~%")) + (unless (null made) ; handle made argument + (let ((line "~%" made))) + (unless (null metatags) ; handle meta tag list + (dolist (entry metatags) + (format buf "~%" + (first entry) (second entry)))) + (format buf "~%") + (format buf "~A~%" title) + (format buf "~%") + (get-output-stream-string buf))) + + + +(defun html-footer (&key (body-close nil)) + "Return body and html closing tags. Note: closing body tag +defaults to off." + (concatenate 'string + (if body-close + (format nil "~%")) + (format nil "~%"))) + + + +(defmacro form-reset-submit (&key (reset "reset") (submit "submit")) + "simple macro for generating the usual reset/submit button pair with + custom writing on them." + `(concatenate 'string + (html:input '((type . "reset") + (value . ,reset))) + (html:input '((type . "submit") + (value . ,submit))))) + +(defun form-hidden (pairlist) + "Function to return hidden value entries for forms. Accepts + property lists and returns one hidden value entry for each + key/value pair encountered." + (apply #'concatenate 'string + (mapcar #'(lambda (pair) + (concatenate 'string + "" + (string #\Newline))) + pairlist))) + + +(defun form-checkbox (pair &rest text) + "Returns HTML for a checkbox for forms, uses a cons for key-value." + (concatenate 'string + "" + (apply #'concatenate 'string text) + (string #\Newline))) + + +(defun form-select (name entries &optional selected) + "Returns HTML for a form select field with the provided name and list +of elements." + (concatenate 'string + (format nil "~%"))) + +(defun form-select-multiple (name entries &optional selected) + "Returns HTML for a form select field with the provided name and list +of elements." + (concatenate 'string + (format nil "~%"))) + + +(defun form-textarea (name cols rows &optional text) + "Returns HTML for a form textarea with the supplied name and size (in + columns and rows as well as optional predefined content." + (declare (type fixnum cols rows)) + (declare (type string name)) + (concatenate 'string + (format nil "~%"))) + + +(defmacro form-self-post (&rest text) + "Expands to form with action=$SCRIPT_NAME and METHOD=POST as parameters." + `(html:form + (pairlis + '("action" "method") + (list (http:http-getenv "SCRIPT_NAME") "POST")) + ,@text)) + + + +(defun force-linebreaks (original) + "Forces linebreaks from the original be insert
tags." + (let ((linebreaks 0)) + (dotimes (i (length original)) + (if (equal (char original i) #\Newline) + (setf linebreaks (1+ linebreaks)))) + (let ((broken (make-string (+ (length original) (* 4 linebreaks)))) + (broken-pos 0)) + (dotimes (i (length original)) + (if (equal (char original i) #\Newline) + (replace broken + (concatenate 'string + "
" + (string #\Newline)) + :start1 broken-pos + :end1 (setf broken-pos (+ broken-pos 5)) + :start2 0 + :end2 5) + (replace broken + original + :start1 broken-pos + :end1 (setf broken-pos (1+ broken-pos)) + :start2 i + :end2 (1+ i)))) + broken))) + addfile ./http.lisp hunk ./http.lisp 1 +;;;; file: http.lisp +;;;; +;;;; Function library with HTTP protocol helper functions for CommonLisp +;;;; +;;;; Copyright (C) 2003,2004 Alexander Schreiber +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Library General Public +;;;; License as published by the Free Software Foundation; +;;;; version 2 of the License. +;;;; +;;;; This library 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 +;;;; Library General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Library General Public +;;;; License along with this library; if not, write to the Free +;;;; Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +;;;; +;;;; author : Alexander Schreiber +;;;; version: $Id: http.lisp 1728 2008-02-17 16:16:22Z als $ +;;;; +;;;; +;;;; design note: Since this code really is only useful in a CGI environment +;;;; and pretty much nowhere else, I happily read/write only +;;;; to/from std(in|out) with no option to change this. + + +(defpackage :http + (:use :common-lisp) + (:documentation + "A simple interface to HTTP for CGI programming, mainly offerering access + to environment variables, query strings, parsed query parameters + and cookies.") + (:export #:http-add-header + #:http-send-headers + #:http-get-env-vars + #:http-get-query-string + #:http-parse-query-string + #:http-query-parameter + #:http-query-parameter-list + #:http-getenv + #:http-add-cookie + #:http-cookie + #:http-cookie-list + #:http-timestamp + #:http-dump-query-parms + #:http-init + #:*http-url-encoding-charset*)) + +(in-package :http) + + +(eval-when (:compile-toplevel) + (proclaim '(optimize (safety 3)))) ; this should be the default + +;; in case of SBCL, shut the compiler up + +#+sbcl +(declaim (sb-ext:muffle-conditions sb-ext:compiler-note)) + + + +;;; globals + +(defparameter *http-library-version* "$Revision: 1728 $" "revision of library") + +(defparameter *http-env-vars* + '("CONTENT_LENGTH" + "CONTENT_TYPE" + "DOCUMENT_ROOT" + "GATEWAY_INTERFACE" + "HTTP_ACCEPT" + "HTTP_ACCEPT_CHARSET" + "HTTP_ACCEPT_ENCODING" + "HTTP_ACCEPT_LANGUAGE" + "HTTP_CONNECTION" + "HTTP_COOKIE" + "HTTP_HOST" + "HTTP_KEEP_ALIVE" + "HTTP_REFERER" + "HTTP_USER_AGENT" + "PATH" + "QUERY_STRING" + "REMOTE_ADDR" + "REMOTE_PORT" + "REQUEST_METHOD" + "REQUEST_URI" + "SCRIPT_FILENAME" + "SCRIPT_NAME" + "SERVER_ADDR" + "SERVER_ADMIN" + "SERVER_NAME" + "SERVER_PORT" + "SERVER_PROTOCOL" + "SERVER_SIGNATURE" + "SERVER_SOFTWARE") + "list of HTTP environment variables set by the webserver for the CGI") + +(defvar *http-pending-headers* () + "HTTP headers pending write, list of (header value) pairs, don't touch") + +(defvar *http-headers-done* nil "HTTP headers already sent?") + +(defvar *http-query-string* nil "The cached HTTP query string") + +(defvar *query-string-parsed* nil "HTTP query string already parsed?") + +(defvar *http-query-parameters* (make-hash-table :test #'equal) + "The parsed HTTP query parameters") + +(defvar *query-arguments* (make-hash-table :test #'equal) + "parsed query arguments, empty upon startup, needs to be filled by +parse-query-string") + +(defvar *cookie-jar* (make-hash-table :test #'equal) + "A place for cookies found in the requests") + +(defvar *read-cookies-done* nil "reading of cookie data done?") + +(defvar *cookie-name-xlator* (make-hash-table :test #'equal) + "translator for case insensitive cookie lookup (according to RFC2109)") + +(defconstant +hex-char-digits+ "0123456789ABCDEF" "valid digits of +hexadecimal numbers, inorder") + +(defvar *http-url-encoding-charset* :utf-8 + "Charset used to url-decode HTTP query, url-encode cookies. One of +:latin1 (alias :iso-8859-1), :latin9 (alias :iso-8859-9), :utf-8") + + +(defconstant +url-encode-ok-chars+ '( #\. #\/) + "Characters that are ok in URL-encoding and need not be encoded. + Note: alphanumerics are automagically assumed to not need encoding.") + +;;; potentially implementation dependent functions +;;; these are wrappers that might need changing for your CL implementation +#+clisp +(defun http-getenv (variable) + "get value of the named environment variable" + (ext:getenv variable)) ; works for CLISP + +#+sbcl +(defun http-getenv (variable) + "get value of the named environment variable" + (sb-ext:posix-getenv variable)) + +#+gcl +(defun http-getenv (variable) + "get value of the named environment variable" + (system:getenv variable)) + +;;; CMU CL demands a bit more work +#+cmu +(defun http-getenv (variable) + "get value of the named environment variable" + (cdr (assoc (intern variable :keyword) + ext:*environment-list*))) + +#+ecl +(defun http-getenv (variable) + "get value of the named environment variable" + (si:getenv variable)) + +;;; support for Lispworks thanks to Bob Hutchison +;;; he tested it with Lispworks Pro 4.4.5 on Mac +#+lispworks +(defun http-getenv (variable) + "get value of the named environment variable" + (lispworks:environment-variable variable)) + +#+allegro +(defun http-getenv (variable) + "get value of the named environment variable" + (sys:getenv variable)) + +;;;; functions (plain CommonLisp) + + +(defun http-get-env-vars () + "Processes *http-env-vars*, tries to get values for all environment +variables mentioned there, stuffs them into a hash and returns this hash." + (let ((env-vars (make-hash-table :test #'equal))) + (dolist (var *http-env-vars*) + (setf (gethash var env-vars) (http-getenv var))) + env-vars)) + + +(defun http-add-header (name value) + "add a HTTP header pair (name & value) to the list of HTTP headers +to be sent - unless they have already been sent" + (unless *http-headers-done* ; no sense adding new ones after sending + (setf *http-pending-headers* + (append *http-pending-headers* (list name value))))) + + +(defun http-send-headers (&optional (content-type + "text/html; charset=UTF-8")) + "write any additional HTTP-headers and the mandatory HTTP Content-Type +header - unless they have already been sent" + (unless *http-headers-done* ; sending them twice would be ... bad + (format t "~&~{~A: ~A~%~}" *http-pending-headers*) + (format t "Content-Type: ~a~%~%" content-type) + (setq *http-headers-done* t))) + + + +(defun http-get-query-string () + "Returns the HTTP QUERY_STRING. Uses the value cached in *http-query-string* +or, if it is empty, reads the POST/GET value, stores it in *http-query-string* +and returns it. Only handles simple GET or POST with content type +application/x-www-form-url-encoded" + (unless *http-query-string* + (setq *http-query-string* + (cond ((string-equal (http-getenv "REQUEST_METHOD") "POST") + (read-line *standard-input* nil "")) + (t ; assume GET data by default + (http-getenv "QUERY_STRING"))))) + *http-query-string*) + + +(defun hex-digit-char (hex-digit) + "returns the appropriate character for the specified hexadecimal +digit, nil if the specified value is outside 0 .. 15" + (when (and (< hex-digit 16) (> hex-digit -1)) + (char +hex-char-digits+ hex-digit))) + + + +(defun hex-char-digit (hex-char) + "returns the decimal digit for the specified hex char, nil if it +is not a hexadecimal char (not in 0 .. 9 a .. f). Works case-insensitive" + (position (char-upcase hex-char) +hex-char-digits+)) + + +(defun url-ok-char-p (testchar) + "returns T if testchar is ok for verbatim printing in URLs, NIL if not" + (cond ((alphanumericp testchar) t) + ((member testchar +url-encode-ok-chars+) t) + ( t nil))) + + +;; the following three functions: +;; - implementation-name-for-encoding +;; - convert-string-to-bytes +;; - convert-string-from-bytes +;; have been copied from Eric Marsdens "pg.lisp" project, specifically +;; the file sysdep.lisp + + +(defun implementation-name-for-encoding (encoding) + #+(and clisp unicode) + (case encoding + ((:latin1 :iso-8859-1) charset:iso-8859-1) + ((:latin9 :iso-8859-9) charset:iso-8859-9) + (:utf-8 charset:utf-8) + (otherwise (error "unknown encoding ~A" encoding))) + #+(and allegro ics) + (case encoding + ((:latin1 :iso-8859-1) :latin1) + ((:latin9 :iso-8859-9) :latin9) + (:utf-8 :utf8) + (otherwise (error "unknown encoding ~A" encoding))) + #+(and sbcl sb-unicode) + (case encoding + ((:latin1 :iso-8859-1) :latin1) + ((:latin9 :iso-8859-9) :latin9) + (:utf-8 :utf8) + (otherwise (error "unknown encoding ~A" encoding))) + #+(or cmu gcl ecl abcl openmcl) + nil) + + +(defun convert-string-to-bytes (string encoding) + (declare (type string string)) + (format *error-output* "string |~a| encoding |~a|~%" string encoding) ; debug + + #+(and clisp unicode) + (ext:convert-string-to-bytes string + (implementation-name-for-encoding + encoding)) + #+(and allegro ics) + (excl:string-to-octets string + :null-terminate nil + :external-format + (implementation-name-for-encoding encoding)) + #+(and :sbcl :sb-unicode) + (sb-ext:string-to-octets string + :external-format + (implementation-name-for-encoding encoding)) + #+(or cmu gcl ecl abcl openmcl) + (if (member encoding '(:latin1 :iso-8859-1 :latin9 :iso-8859-9)) + (let ((octets (make-array (length string) :element-type + '(unsigned-byte 8)))) + (map-into octets #'char-code string)) + (error "Can't convert ~A string to octets" encoding))) + + + +(defun convert-string-from-bytes (bytes encoding) + (declare (type (vector (unsigned-byte 8)) bytes)) + #+cmu + (declare (ignore encoding)) + #+(and clisp unicode) + (ext:convert-string-from-bytes bytes (implementation-name-for-encoding + encoding)) + #+(and allegro ics) + (excl:octets-to-string bytes + :external-format + (implementation-name-for-encoding encoding)) + #+(and :sbcl :sb-unicode) + (sb-ext:octets-to-string bytes + :external-format + (implementation-name-for-encoding encoding)) + ;; for implementations that have no support for character + ;; encoding, we assume that the encoding is an octet-for-octet + ;; encoding, and convert directly + #+(or cmu (and sbcl (not :sb-unicode)) gcl ecl abcl openmcl) + (let ((string (make-string (length bytes)))) + (map-into string #'code-char bytes))) + + + +(defun url-escape-char (char charset) + (let ((bytes (convert-string-to-bytes (string char) charset))) + (with-output-to-string (out) + (map nil + #'(lambda (byte) (format out "%~2,'0X" byte)) + bytes)))) + + + +(defun url-encode-string (input-string + &optional (charset + *http-url-encoding-charset*)) + "URL-encodes the input-string and returns an url-encoded output-string" + (let ((work-stream (make-string-output-stream))) + (dotimes (position (length input-string) + (get-output-stream-string work-stream)) + (if (url-ok-char-p (char input-string position)) + (princ (char input-string position) work-stream) ; copy verbatim + (princ (url-escape-char (char input-string position) charset) + work-stream))))) + + + + +(defun url-decode-bytes (input-string) + "Decode bytes coming in as URL-encoded string." + (let ((byte-vect (make-array (length input-string) + :element-type '(unsigned-byte 8) + :fill-pointer 0)) + (num-low 0) + (num-high 0) + (skip 0)) + (dotimes (position (length input-string)) + (cond ((= skip 1) (setf num-low (hex-char-digit + (char input-string position))) + (vector-push (+ (* num-high 16) num-low) byte-vect) + (setf skip (- skip 1))) + ((= skip 2) (setf num-high (hex-char-digit + (char input-string position))) + (setf skip (- skip 1))) + (t (cond ((char= (char input-string position) #\+) ; space + (vector-push (char-code #\Space) byte-vect)) + ((char= (char input-string position) #\%) ; hexdigit? + (if (and + (>= (- (length input-string) position) 3) + (hex-char-digit (char input-string (+ position + 1))) + (hex-char-digit (char input-string (+ position + 2)))) + (setf skip 2) ; hexdigits to process follow + + ;; Not escaped characters are assumed to have codes + ;; that fit into one byte and have the same + ;; representation in all charsets. + (vector-push (logand #xff + (char-code + (char input-string position))) + byte-vect))) + (t (vector-push (logand #xff + (char-code + (char input-string position))) + byte-vect)))))) + byte-vect)) + + + + +(defun url-decode-string (input-string &optional (charset + *http-url-encoding-charset*)) + "URL-decodes the input-string and returns an url-decoded strings, deals +correctly with partly encoded (parts encoded and parts plain) strings. +Invalid hex-code sequences are ignored and printed through verbatim." + (convert-string-from-bytes (url-decode-bytes input-string) + charset)) + + + + + +(defun add-item-to-hash! (hash key value) + "Adds an item to the named hash. Upon encountering and existing value, +automagically does chaining (turns the simple value into a list of values). +Note: modifies the hash in-place." + (cond ((null (gethash key hash)) ; nothing here, make simple value + (setf (gethash key hash) value)) + ((listp (gethash key hash)) ; already chained, append + (setf (gethash key hash) + (append (gethash key hash) + (list value)))) + (t ; plain value, make a chain + (setf (gethash key hash) + (append (list (gethash key hash)) + (list value))))) + hash) + + + +(defun http-parse-query-string (&optional query-string) + "Parses the provided query string into key-value groups and puts that +into a hash, returns the hash." +;;; example: "foo=bar&answer=42&question=wanted" + (unless *query-string-parsed* + (unless query-string (setf query-string (http-get-query-string))) + (let ((key nil) + (value nil) + (pointer 0)) + (dotimes (position (length query-string)) + (cond + ((char= (char query-string position) #\=) + (setf key (subseq query-string pointer position)) + (setf pointer (1+ position))) + ((char= (char query-string position) #\&) + (setf value (subseq query-string pointer position)) + (setf pointer (1+ position)) + (add-item-to-hash! *http-query-parameters* + (url-decode-string key) + (url-decode-string value)) + (setf value nil)))) + (when (and (< pointer (length query-string)) (null value)) + (add-item-to-hash! *http-query-parameters* + (url-decode-string key) + (url-decode-string + (subseq query-string pointer)))) + (setf *query-string-parsed* t) + *http-query-parameters*))) + + +(defun http-query-parameter (name) + "Return the value of the named HTTP query parameter." + (unless *query-string-parsed* + (http-parse-query-string)) + (gethash name *http-query-parameters*)) + + + + +(defun http-get-content-type () + "Tries to get the content type from the environment, returns either the +content-type string or NIL." + (let ((cti (http-getenv "CONTENT_TYPE"))) ; cache the content-type string + (if (string= "" cti) + (if (position #\; cti) + (subseq cti 0 (position #\; cti)))))) + + +(defun http-get-multipart-boundary () + "Tries to get the multipart boundary string from the content type, returns +either the corrent boundary string or NIL." + (let ((cti (http-getenv "CONTENT_TYPE"))) ; cache the content-type string + (if (search "multipart" cti) + (if (search "boundary=" cti) + (concatenate 'string + "--" + (subseq cti (+ (search "boundary=" cti) 1))))))) + + +(defun http-dump-query-parms () + "returns string of HTTP query parameters." + (maphash #'(lambda (key value) + (format t "~&
  • ~a = ~a
  • ~%" key value )) + (http-parse-query-string ))) + + +(defun needs-quoting (test-string) + "Tests if a string needs quoting by checking if it contains non-alphanumeric +characters, returns T or NIL" + (if (< 0 (length (remove-if #'alphanumericp test-string))) + t)) + + +(defun http-add-cookie (name value &key + (Comment nil) + (Domain nil) + (Max-Age nil) + (Path nil) + (Secure nil) + (Version "1")) + "Add a RFC2109 cookie with the specified data to the list of headers to + send. Modify the Version argument at your own peril. + Note: http-add-cookie _must_ be called _before_ calling http-send-headers + because cookies are transmitted as part of the HTTP headers." + (http-add-header "Set-Cookie" + (concatenate 'string + (format nil "~a=~a" + name + (if (needs-quoting value) + (format nil "\"~a\"" value) + value)) + (unless (null Comment) + (format nil "; Comment=\"~a\"" Comment)) + (unless (null Domain) + (format nil "; Domain=\"~a\"" Domain)) + (unless (null Max-Age) + (format nil "; Max-Age=~a" Max-Age)) + (unless (null Path) + (format nil "; Path=~a" + (url-encode-string Path))) + (unless (null Secure) + (format nil "; Secure")) + (format nil "; Version=\"~a\"" + Version)))) + + +(defun read-cookies () + "Read all cookies from the request and store them into the cookie jar." +;;; example: foo=bar; quux="super baz"; grumble="Mr. \"RockIt\" Joe" +;;; +;;; This is going to be a primitive state machine and the code will be +;;; rather ugly. +;;; But it works, so I'll leave it for the time being. + (unless (null (http-getenv "HTTP_COOKIE")) + (let ((cookies (http-getenv "HTTP_COOKIE")) + (key nil) + (value nil) + (work nil) + (is-key t) + (is-value nil) + (is-to-be-quoted nil) + (open-quote nil)) + (dotimes (position (length cookies)) + (cond + ((and (char= (char cookies position) #\=) ; end-of-name + is-key) + (setf key (remove-if #'(lambda (item) + (char= #\Space item)) + work)) + (setf work nil) + (setf is-key nil) + (setf is-value t)) + ((and (char= (char cookies position) #\") ; start/end-of-quoted-value + is-value + (not is-to-be-quoted)) + (if open-quote + (setf open-quote nil) + (setf open-quote t)) + (setf is-value t)) + ((and (char= (char cookies position) #\\) ; quote next char + is-value + (not is-to-be-quoted)) + (setf is-to-be-quoted t)) + ((and (or (char= (char cookies position) #\;) + (char= (char cookies position) #\,)) + (not open-quote) + (not is-to-be-quoted) + is-value) + (setf value work) + (unless (gethash key *cookie-jar*) + (setf (gethash key *cookie-jar*) value)) + (setf (gethash (string-downcase key) *cookie-name-xlator*) key) + (setf work nil) + (setf is-value nil) + (setf is-key t)) + + (t ; nothing special, copy to buffer + (setf work + (concatenate 'string work + (string (char cookies position)))) + (setf is-to-be-quoted nil)))) + (unless (null key) + (setf value work) + (unless (gethash key *cookie-jar*) + (setf (gethash key *cookie-jar*) value) + (setf (gethash (string-downcase key) *cookie-name-xlator*) key)))))) + + +(defun http-cookie (name) + "Fetch the value of the specified cookie. Case insensitive. Returns NIL +if the cookie is not found." + (unless *read-cookies-done* + (read-cookies)) + (if (gethash name *cookie-jar*) + (gethash name *cookie-jar*) + (gethash (gethash (string-downcase name) + *cookie-name-xlator*) + *cookie-jar*))) + + +(defun http-cookie-list () + "Returns a list of all stored cookies received from the user agent." + (unless *read-cookies-done* + (read-cookies)) + (let ((cookies '())) + (maphash (lambda (key value) + (declare (ignore value)) + (setf cookies + (append cookies (list key)))) + *cookie-jar*) + cookies)) + + +(defun http-query-parameter-list () + "Returns a list of all stored HTTP query parameters." + (unless *query-string-parsed* + (http-parse-query-string)) + (let ((query-parameters '())) + (maphash (lambda (key value) + (declare (ignore value)) + (setf query-parameters + (append query-parameters (list key)))) + *http-query-parameters*) + query-parameters)) + + +(defun http-timestamp (&optional (timeval nil)) + "Returns a HTTP timestamp (see RFC2616). Expects timeval to be universal +time or NIL, assumes current time if NIL." + (multiple-value-bind (second minute hour date month year day daylight-p zone) + (decode-universal-time (if (null timeval) + (get-universal-time) + timeval)) + (declare (ignore daylight-p)) + (format nil "~A, ~2,'0D ~A ~D ~2,'0D:~2,'0D:~2,'0D ~D" + (nth day '("Mon" "Tue" "Wed" "Thu" "Fri" "Sat" "Sun")) + date + (nth (1- month) '("Jan" "Feb" "Mar" "Apr" "May" "Jun" + "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")) + year + hour minute second + zone))) + + +(defun http-init () + "Init function, runs fixups where needed." + ;; SBCL fixup: For some reason, SBCL initially opened the + ;; *standard-output* stream with :external-format :ascii + ;; when running as CGI. If we are running with another encoding + ;; (like, UTF8) this is ... less than optimal. So we are pulling + ;; a dirty trick that very likely only works on Linux. + #+sbcl + (if (not (equal (stream-external-format *standard-output*) + *http-url-encoding-charset*)) + (if (string-equal (software-type) "Linux") + (setf *standard-output* + (open "/dev/stdout" + :external-format *http-url-encoding-charset* + :direction :output + :if-exists :append + :if-does-not-exist :error)))) + + t) ; do _something_ on other platforms *g* addfile ./lisp-cgi-utils.asd hunk ./lisp-cgi-utils.asd 1 +;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*- +;;;; ************************************************************************* +;;;; FILE IDENTIFICATION +;;;; +;;;; Name: lisp-cgi-utils.asd +;;;; Purpose: ASDF definition file for lisp-cgi-utils +;;;; Author: Alexander Schreiber +;;;; Date Started: 2004-07-30 +;;;; +;;;; $Id: lisp-cgi-utils.asd 1409 2005-08-07 13:52:05Z als $ +;;;; + + +(defpackage #:lisp-cgi-utils-system (:use #:asdf #:cl)) + +(in-package :lisp-cgi-utils-system) + +(defsystem lisp-cgi-utils + :name "cl-lisp-cgi-utils" + :author "Alexander Schreiber" + :version "0.8" + :maintainer "Alexander Schreiber " + :licence "LGPL" + :description "Lisp CGI utilitiles" + :long-description "THis package contains code to support writing CGI applications in Common lisp." + + :components ((:file "http") + (:file "html"))) + +