CVE-2013-7448 (Other, published 2016-02-23T19:59Z)
Description: value: Directory traversal vulnerability in wiki.c in didiwiki allows remote attackers to read arbitrary files via the page parameter to api/page/get.
Severity: MEDIUM (CVSS v3 base score: 7.5)
Weakness: CWE-22 \u2014 Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Repository: https://github.com/yarolig/didiwiki
Files changed:
AUTHORS
COPYING
ChangeLog
INSTALL
Makefile.am
README
TODO
autogen.sh
configure.ac
src/Makefile.am
src/didi.c
src/didi.h
src/http.c
src/http.h
src/util.c
src/util.h
src/wiki.c
src/wiki.c.orig
src/wiki.h
src/wikitext.h
website/didiwiki-favicon.pnm
website/favicon.h
website/index.htm
website/new-index.html
website/styles.css
Commit message: page_name_is_good function


Vulnerable code:

Fixed code:
Primary developer: 
	Matthew Allum <mallum@o-hand.com>

Contributers:
	Carsten Graeser <c.g.berlin@web.de> - table support. 
		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 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.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, 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 or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
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 give any other recipients of the Program a copy of this License
along with the Program.

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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) 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; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, 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 executable.  However, as a
special exception, the source code 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.

If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.

  5. 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 Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.

  7. 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 Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.

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.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.

  9. The Free Software Foundation may publish revised and/or new versions
of the 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 Program
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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	    How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  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.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) year  name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.
2004-12-12  mallum,,,  <mallum@openedhand.com>

	* website/index.htm:
	Fix SVN info

2004-12-12  mallum  <mallum@openedhand.com>

	* website/index.htm:
	Add Openedhand Link

2004-11-14  mallum  <mallum@handhelds.org>

	* src/http.c: (http_server):
	* src/wiki.c: (wiki_show_changes_page_rss),
	(wiki_handle_http_request):
	Improve RSS feed ( add full links tidy up )

2004-11-14  mallum,,,  <mallum@handhelds.org>

	* src/http.c: (http_response_send):
	* src/wiki.c: (check_for_link), (file_write),
	(wiki_show_search_results_page), (wiki_show_template),
	(wiki_show_header):
	* src/wikitext.h:
	Dont send '\0' in http response. Dont write '\0' to disk.
	Make all links relative, not absolute ( for reverse proxys )
	Add initial RSS changes page ( thanks to <j.theess@tu-bs.de> )

2004-09-30 0.5 released. 

2004-09-30  mallum,,,  <mallum@handhelds.org>

	* configure.ac:
	Bump up version,
	* src/wiki.c: (wiki_print_data_as_html), (wiki_get_pages),
	(wiki_handle_http_request):
	Fix italic brokeness. Add check for extra /'s in req path. 
	* website/index.htm
	Update for 0.5 release. 

2004-09-28  mallum,,,  <mallum@handhelds.org>

	* README:
	Added notes on HTTP API. Made forking/memory thing a bit clearer.
	* TODO:
	Updated.
	* configure.ac:
	* src/Makefile.am:
	Add missing -Wall
	* src/didi.h:
	* src/http.c: (http_server):
	* src/wiki.c: (check_for_link), (wiki_print_data_as_html),
	(wiki_show_create_page), (changes_compar),
	(wiki_show_changes_page), (wiki_show_search_results_page),
	(wiki_show_template), (wiki_show_header), (wiki_show_footer),
	(wiki_handle_rest_call), (wiki_handle_http_request), (wiki_init):
	* src/wiki.h:
	Added simple http api. 
	Rewrote searchs / changes stuff so more generic
	Applied patches from Tiago Cogumbreiro for more printf safety etc.
	Fixed -Wall warnings.	
	* website/index.htm:
	Updated with hopeful amamzon wish list link. 

2004-09-13 0.4 released. 

2004-09-13  mallum  <mallum@handhelds.org>

	* README:
	* configure.ac:
	Bump up versions
	* website/index.htm:
	* website/styles.css:
	bring in to 21st century. 

2004-09-10  mallum,,,  <mallum@handhelds.org>

	* configure.ac:
	Add message about upgrades and wikihelp
	* src/wiki.c: (check_for_link), (wiki_show_header),
	(wiki_handle_http_request):
	Add title attr for external www links
	Add favicon.ico
	* src/wikitext.h:
	Update help for ordered lists. Add favicon data. 

2004-09-10  mallum,  <mallum@handhelds.org>

	* Makefile.am:
	* TODO:
	Add TODO. 

2004-09-09  mallum,  <mallum@handhelds.org>

	* src/wiki.c: (check_for_link), (wiki_print_data_as_html):
	Dont let %'s in links end up as printf formatters. fixes seg.	
	* src/wikitext.h:
	Add little javascript to edit page to autofocus textentry. 
	Possibly evil. 

2004-09-08  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (check_for_link):
	Only let alphanums be valid in camel case links 
	( via Eric Newman )
	* src/wikitext.h:
	Fix missing ' in anchor tag

2004-09-02  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (wiki_handle_http_request):
	Remove any http encoding of requested page name, 
        thanks again to Kanru Chen.

2004-09-02  mallum,,,  <mallum@handhelds.org>

	* src/http.c: (http_response_send_headers):
	Send correct UTF8 type, thanks Kanru Chen.
	* src/wiki.c: (check_for_link), (wiki_print_data_as_html):
	Add support for file:// urls. Add Support for ordered lists.

2004-08-17  0.3 release. 

2004-08-17  mallum  <mallum@handhelds.org>

	* README:
	* configure.ac:
	Bump up version to 0.3

	* src/wikitext.h:
	More CSS tweakery
	* website/index.htm:
	Update for 0.3 release 

2004-08-10  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (wiki_show_header):
	* src/wikitext.h:
	Add titles and shortcut keys to hrefs

2004-08-08  mallum,,,  <mallum@handhelds.org>

	* src/http.c: (http_response_new), (http_response_send_headers):
	* src/http.h:
	All content type in response to be set. 
	* src/wiki.c: (wiki_show_changes_page),
	(wiki_show_search_results_page), (wiki_show_header),
	(wiki_handle_http_request):
	* src/wikitext.h:
	Fix styles.css to have corect content type.
	More web standard related fixes. Now xhtml transitional.

2004-08-06  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (wiki_print_data_as_html), (wiki_show_header):
	* src/wikitext.h:
	CSS + HTML cleanups/improvements

2004-08-02  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (wiki_print_data_as_html):
	Attempt to detect path names and not make them italic 

2004-08-02  mallum,,,  <mallum@handhelds.org>

	* src/wiki.c: (file_write), (wiki_print_data_as_html):
	More improvements to bold/italic/underline etc handling

2004-07-30  mallum,,,  <mallum@handhelds.org>

	* README:
	update
	* src/wiki.c: (wiki_print_data_as_html), (wiki_init):
	honor DIDIWIKIHOME env var. Improve */-_ handling.
	* src/wikitext.h:
	Escape %%'s
	* website/index.htm:
	Update for 0.2 release

2004-07-30  0.2 Release

2004-07-29  mallum,,,  <mallum@handhelds.org>

	* AUTHORS:
	Populated
	* src/wiki.c: (wiki_print_data_as_html):
	* src/wikitext.h:
	Basic Table support. Help updates. pre fixes

2004-07-29  mallum,,,  <mallum@handhelds.org>

	* configure.ac:
	Bump up version
	* src/didi.c: (main):
	* src/didi.h:
	* src/http.c: (malformed_request):
	* src/util.c: (util_ascii_to_hex), (util_dehttpize),
	(util_htmlize):
	* src/util.h:
	* src/wiki.c: (check_for_link), (file_read),
	(wiki_print_data_as_html), (wiki_init):
	Quick source cleanup. 
	Make sure lingering </pre>'s get closed

2004-07-28  mallum,,,  <mallum@handhelds.org>

	* src/http.c: (http_server):
	reuse socket address 
	* src/wiki.c: (wiki_print_data_as_html):
	Dont stick <p>'s in <pre>'s

2004-07-20  0.1 Release. 

2004-07-20  mallum,,,  <mallum@handhelds.org>

	* README:
	* src/wikitext.h:
	* website/index.htm:
	More updates to wikitext.h , stylesheets etc.  
	Added website stuff to svn. 
	A bit more info in the README.

2004-07-20  mallum,,,  <mallum@handhelds.org>

	* src/didi.h:
	* src/http.c:
	* src/util.c:
	* src/wiki.c: (wiki_print_data_as_html), (changes_compar),
	(wiki_show_changes_page), (wiki_show_search_results_page),
	(wiki_show_header):
	* src/wikitext.h:
	Add license to source files. 
	More predefined Wiki Text. 

2004-07-19  mallum  <mallum@handhelds.org>

	* src/http.c: (http_server):
	* src/wiki.c: (wiki_print_data_as_html), (wiki_show_changes_page),
	(wiki_show_search_results_page), (wiki_show_header),
	(wiki_show_footer), (wiki_handle_http_request), (wiki_init):
	* src/wikitext.h:
	Added simple search functionality. 
	Messed with some header defines
	Imporved webserver startup

Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software
Foundation, Inc.

   This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.

Basic Installation
==================

   These are generic installation instructions.

   The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation.  It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions.  Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').

   It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring.  (Caching is
disabled by default to prevent problems with accidental use of stale
cache files.)

   If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release.  If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.

   The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'.  You only need
`configure.ac' if you want to change it or regenerate `configure' using
a newer version of `autoconf'.

The simplest way to compile this package is:

  1. `cd' to the directory containing the package's source code and type
     `./configure' to configure the package for your system.  If you're
     using `csh' on an old version of System V, you might need to type
     `sh ./configure' instead to prevent `csh' from trying to execute
     `configure' itself.

     Running `configure' takes awhile.  While running, it prints some
     messages telling which features it is checking for.

  2. Type `make' to compile the package.

  3. Optionally, type `make check' to run any self-tests that come with
     the package.

  4. Type `make install' to install the programs and any data files and
     documentation.

  5. You can remove the program binaries and object files from the
     source code directory by typing `make clean'.  To also remove the
     files that `configure' created (so you can compile the package for
     a different kind of computer), type `make distclean'.  There is
     also a `make maintainer-clean' target, but that is intended mainly
     for the package's developers.  If you use it, you may have to get
     all sorts of other programs in order to regenerate files that came
     with the distribution.

Compilers and Options
=====================

   Some systems require unusual options for compilation or linking that
the `configure' script does not know about.  Run `./configure --help'
for details on some of the pertinent environment variables.

   You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment.  Here
is an example:

     ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix

   *Note Defining Variables::, for more details.

Compiling For Multiple Architectures
====================================

   You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory.  To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'.  `cd' to the
directory where you want the object files and executables to go and run
the `configure' script.  `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.

   If you have to use a `make' that does not support the `VPATH'
variable, you have to compile the package for one architecture at a
time in the source code directory.  After you have installed the
package for one architecture, use `make distclean' before reconfiguring
for another architecture.

Installation Names
==================

   By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc.  You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.

   You can specify separate installation prefixes for
architecture-specific files and architecture-independent files.  If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.

   In addition, if you use an unusual directory layout you can give
options like `--bindir=PATH' to specify different values for particular
kinds of files.  Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.

   If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.

Optional Features
=================

   Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System).  The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.

   For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.

Specifying the System Type
==========================

   There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on.  Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option.  TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:

     CPU-COMPANY-SYSTEM

where SYSTEM can have one of these forms:

     OS KERNEL-OS

   See the file `config.sub' for the possible values of each field.  If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.

   If you are _building_ compiler tools for cross-compiling, you should
use the `--target=TYPE' option to select the type of system they will
produce code for.

   If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.

Sharing Defaults
================

   If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists.  Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.

Defining Variables
==================

   Variables not defined in a site shell script can be set in the
environment passed to `configure'.  However, some packages may run
configure again during the build, and the customized values of these
variables may be lost.  In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'.  For example:

     ./configure CC=/usr/local2/bin/gcc

will cause the specified gcc to be used as the C compiler (unless it is
overridden in the site shell script).

`configure' Invocation
======================

   `configure' recognizes the following options to control how it
operates.

`--help'
`-h'
     Print a summary of the options to `configure', and exit.

`--version'
`-V'
     Print the version of Autoconf used to generate the `configure'
     script, and exit.

`--cache-file=FILE'
     Enable the cache: use and save the results of the tests in FILE,
     traditionally `config.cache'.  FILE defaults to `/dev/null' to
     disable caching.

`--config-cache'
`-C'
     Alias for `--cache-file=config.cache'.

`--quiet'
`--silent'
`-q'
     Do not print messages saying which checks are being made.  To
     suppress all normal output, redirect it to `/dev/null' (any error
     messages will still be shown).

`--srcdir=DIR'
     Look for the package's source code in directory DIR.  Usually
     `configure' can determine that directory automatically.

`configure' also accepts some other, not widely useful, options.  Run
`configure --help' for more details.

SUBDIRS = src 

EXTRA_DIST = TODO

snapshot:
	$(MAKE) dist distdir=$(PACKAGE)-snapshot-`date +"%Y%m%d"`
DidiWiki 0.5
==============

DidiWiki is a small and simple WikiWikiWeb implementation written in
C. Its intended for personal use for notes, Todo's etc. It includes its
own webserver and hopefully its as simple as just compiling, running
and pointing your browser at it. 

Installation
============

To build do the usage ./configure and make. You can then make install or
run DidiWiki right from the src dir. On startup it'll tell you the address
to point your browser at. You should then be ready to go. 

If upgrading from an earlier version its a good idea to delete
~/.didiwiki/DidiHelp before running so you get the latest version of
the help file.


Remote API
==========

As from 0.5 DidiWiki has a very simple 'REST' like http api. Hopefully
this will make it possible to write alternate front ends to didiwiki.

The api looks like this with parameter passable via GET or POST;

http://didiwiki/api/page/get?page=XXX

 Get the raw wiki text for specified page. Returns http 500 code if
 page does not exist.

http://didiwiki/api/page/set?page=XXX&text=XXX

 Set the wikitext of a page. Page is created if it doesn't exist. 

http://didiwiki/api/page/exists?page=XXX

 Returns 'success' if page exists, 500 error if not. 

http://didiwiki/api/page/delete?page=XXX

 Deletes a page. 

http://didiwiki/api/pages

 Returns a seperated list ( one entry per line containing page
 title,TAB,modified date) of wiki pages with the most recently modified
 first.

http://didiwiki/api/search?expr=XXX

 Like the above but lists only pages which contain 'expr'


Notes & Excuses
===============

- If you want to contribute, please check the TODO !

- Pages are stored in ~/.didiwiki ( you can override this by setting 
  'DIDIWIKIHOME' env var to a valid path. ). You can add a styles.css 
  to this directory to override the 'inbuilt' stylesheet.

- The webserver part is heavily based on cvstrac's internal server,
  following the same kind of process;

  - Its a pretty simple lightweight forking server. A child process is
    forked to handle each http request.

  - These forked children are assumed to not be around for very long (
    less than a second ), thus no real attempt is made to free up
    memory in these child processes.

  - Its probably not very secure at all. !!PLEASE DONT RUN AS ROOT!!

- The code uses asprintf in a couple of places. I dont know how portable
  this is to non Linux systems. If its a problem to you Feel free to send 
  patches.

- You can debug segv's by running like 'didiwiki debug' which will not
  start the http server, it existing in a single process but expecting the
  http request on stdin. 

- The formatting style is very similar to that of kwiki's - I just like 
  kwiki :-)

- I wrote it in C as I've mostly forgotten Python, Perl etc 
  ( and I wanted it to fit easily on a PDA ).

Notes
=====

Want to keep things simple,quick and useful. Didiwiki intended for personal
use - Notes, Todos, basic PIM etc - functionality should reflect that.

Keep deps lows, binary should just run - no dependance on external files. 
Should just work. 

Pending
=====

Apply Halski's '-' patch.

WTF am I not doing this in didiwiki ?


Stuff that would be nice to have
=====

- Auth
  - basic http auth 
    - set via user/pass in ~/.didiwiki/.htacess ?
  - Limit by IP

- Any secruity improvements in code. 

- RSS feed of changes / searches ?

- Simple REST uri like API for external getting/setting raw wiki pages

  - didiwiki:8000/api/page/ get|set|exists|delete

    <?xml version="1.0"?>
    <page>
    blah blah b    

    </page>

    * could allow param to just return data as text, is xml really needed *
    * - xml could be a pain for shell scripts                             *

  - didiwiki:8000/api/pages lists all pages
    - could do the same as changes ?

  - didiwiki:8000/api/search

  - didiwiki:8000/api/changes


- Rewrite Parser.
  - can be much faster but its not that slow atm. 
  - make generic, output text, docbook ( bloat - uneeded )
                   - REST api would make it easy to write other 
		     programs that did this. 

- Wikipedia style icons after external links etc ( done in css )
  - 'xxd' can convert pngs etc to c arrays useful for including
    small images in binary. 

- Also favicon.ico *DONE*, though better icon would be nice. 

- Improve serach.

- Interwiki style links ?



Stuff thats a little more out there
======

- metadata on pages / tagging / tagging by dates
  - used to build custom searchs, group stuff togeather
    - more PIM like. 
  - need to think more on this

- #include 'wikipage' style directive ?

- GTD tool, how would this work ?
  - lists
    - allow lists in pages to be referenced ?
      - /pagename?list=#    referenced by number
      - /pagename?list=name 
        - How do we name a list though ?
	  - *[listname] ?
      - also deliver lists as RSS
      - is this much use though ?

- Templating, let user overide header and footer templates.    
#! /bin/sh
autoreconf -v --install || exit 1
./configure --enable-maintainer-mode "$@"
AC_PREREQ(2.59)
AC_INIT(didiwiki, 0.6-cvs, [mallum@handhelds.org])
AC_CONFIG_SRCDIR([src/util.c])
AC_CONFIG_HEADER([config.h])

AM_INIT_AUTOMAKE()

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
AC_CHECK_HEADERS([arpa/inet.h netinet/in.h stdlib.h string.h sys/socket.h sys/time.h unistd.h])

# Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_HEADER_TIME
AC_STRUCT_TM

# Checks for library functions.
AC_FUNC_ALLOCA
AC_FUNC_FORK
AC_FUNC_SELECT_ARGTYPES
AC_FUNC_STAT
AC_FUNC_STRFTIME
AC_CHECK_FUNCS([asprintf inet_ntoa memset mkdir putenv select socket strdup strncasecmp])

if test "x$GCC" = "xyes"; then
        GCC_FLAGS="-g -Wall -fno-strict-aliasing"
fi

AC_SUBST(GCC_FLAGS)

AC_OUTPUT([
Makefile
src/Makefile
])

echo
echo "  NOTE:"
echo "  If you are upgrading from a earlier version of DidiWiki, delete"
echo "  \$HOME/.didiwiki/WikiHelp to get updated with the latest version."
echo
PREFIXDIR  = $(prefix)
PKGDATADIR = $(datadir)/matchbox
DATADIR    = $(datadir)

AM_CFLAGS = $(GCC_FLAGS)

INCLUDES = -DDATADIR=\"$(DATADIR)\" -DPKGDATADIR=\"$(PKGDATADIR)\" -DPREFIX=\"$(PREFIXDIR)\"

bin_PROGRAMS = didiwiki

didiwiki_SOURCES = didi.c  didi.h  http.c  http.h  \
                   util.c  util.h  wiki.c  wiki.h  \
                   wikitext.h




#include "didi.h"

void 
usage(char *progname)
{
  fprintf(stderr, "usage..\n");
}

int 
main(int argc, char **argv)
{
  HttpRequest  *req  = NULL;
  int           port = 8000;

  wiki_init();

  if(argc > 1 && !strcmp(argv[1],"debug"))
    req = http_request_new(); 	/* reads request from stdin */
  else
    req = http_server(port);    /* forks here */

  wiki_handle_http_request(req);

  return 0;
}
/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Copyright 2004 Matthew Allum <mallum@o-hand.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#ifndef _HAVE_DIDIWIKI_HEADER
#define _HAVE_DIDIWIKI_HEADER

#define _GNU_SOURCE

#include "config.h"

#if defined(__MINGW32__)

/* This is broken */

#undef DATADIR

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <sys/stat.h>
#include <sys/timeb.h>
#include <sys/types.h>
#include <dirent.h>

#ifndef WNOHANG
#define WNOHANG 1
#endif

#ifndef F_OK
#    define F_OK 00
#endif
#ifndef X_OK
#    define X_OK 01
#endif
#ifndef W_OK
#    define W_OK 02
#endif
#ifndef R_OK
#    define R_OK 04
#endif

#else

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <time.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <sys/times.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <dirent.h>

#endif

#define TRUE  1
#define FALSE 0


#include "util.h"
#include "http.h"
#include "wiki.h"

#endif
/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Parts of this http and util code based on cvstrac sources. 
 *  See http://www.cvstrac.org
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "didi.h"

struct HttpRequestParam  {
  char *key;
  char *val;
};

struct HttpRequest  
{
  HttpRequestParam *params;
  int               n_params;
  int               n_params_alloc;
  char             *uri;
  char             *path_info;
  char             *query_string;
};

struct HttpResponse 
{
  char *content_type;
  char *status_str;
  int   status;

  char *extra_headers;

  char *data;
  int   data_len;
  int   data_len_alloced;
};

/*
** Send a reply indicating that the HTTP request was malformed
*/
static void 
malformed_request(void)
{
  HttpResponse *res = NULL;

  res = http_response_new(NULL);

  http_response_set_status(res, 501, "Not Implemented");
  http_response_printf(res, "<html><body>Unrecognized HTTP Request</body></html>\n");
  http_response_send(res);

  exit(0);
}

/*
 * Add another query parameter or cookie to the parameter set.
 */
static void 
http_request_add_param(HttpRequest *req, char *key, char *value)
{
  if( req->n_params_alloc <= req->n_params )
    {
      req->n_params_alloc = req->n_params_alloc*2 + 10;
      req->params = realloc( req->params, 
			     req->n_params_alloc * sizeof(HttpRequestParam));
      if( !req->params ) exit(1); /* XXX handle better  */
    }

  req->params[req->n_params].key = key;
  req->params[req->n_params].val = value;

  req->n_params++;
}


/*
 * Add a list of query parameters or cookies to the parameter set.
 *
 * Each parameter is of the form NAME=VALUE.  Both the NAME and the
 * VALUE may be url-encoded ("+" for space, "%HH" for other special
 * characters).  But this routine assumes that NAME contains no
 * special character and therefore does not decode it.
 *
 * Parameters are separated by the "terminator" character.  Whitespace
 * before the NAME is ignored.
 *
 * The input string "z" is modified but no copies is made.  "z"
 * should not be deallocated or changed again after this routine
 * returns or it will corrupt the parameter table.
 */
static void 
http_request_parse_params (HttpRequest *req, 
			   char        *data, 
			   int          terminator)
{
  while( *data )
    {
      char *key = NULL, *val = NULL;

      while( isspace(*data) ){ data++; }
      
      key = data;

      while( *data && *data != '=' && *data != terminator )
	data++; 
      
      if( *data == '=' )
	{
	  *data = '\0';
	  data++;
	  val= data;

	  while( *data && *data != terminator ) 
	    data++; 

	  if( *data ) { *data = '\0'; data++; }
	  
	  util_dehttpize(val);

	  http_request_add_param(req, key, val);

	}
      else
	{
	  if( *data ){ *data++ = 0; }

	  http_request_add_param(req, key, NULL);
	}
    }
}


char*
http_request_param_get(HttpRequest *req, char *key)
{
  int i;

  for (i=0; i<req->n_params; i++)
    if (!strcmp(req->params[i].key, key))
      return req->params[i].val;

  return NULL;
} 

char*
http_request_get_uri(HttpRequest *req)
{
  return req->uri;
}

char*
http_request_get_path_info(HttpRequest *req)
{
  return req->path_info;
}

char*
http_request_get_query_string(HttpRequest *req)
{
  return req->query_string;
}


/*
 * This routine handles a single HTTP request which is coming in on
 * standard input ( and which replies on standard output. ). 
 * 
 */
HttpRequest*
http_request_new(void)
{
  char              *token, *content_type, *post_data, *z;
  int                i, len;
  struct sockaddr_in remoteName;
  int                size = sizeof(struct sockaddr_in);
  char               request_line[2048];     /* A single line of input. */
  HttpRequest       *req = NULL;

  req = malloc(sizeof(HttpRequest));
  memset(req, 0, sizeof(HttpRequest)); 

  if( fgets(request_line, sizeof(request_line), stdin) == NULL )
    malformed_request();

  if ((token = util_extract_token(request_line, &z)) == NULL)
    malformed_request();

  if(!(!strcmp(token,"GET") || !strcmp(token,"POST") || !strcmp(token,"HEAD")))
    malformed_request();

  putenv("GATEWAY_INTERFACE=CGI/1.0");
  putenv(util_mprintf("REQUEST_METHOD=%s",token));

  if ((token = util_extract_token(z, &z)) == NULL)
    malformed_request();

  putenv(util_mprintf("REQUEST_URI=%s", token));

  if (token)
    req->uri = strdup(token);

  /* eg. GET /blah.htm?crap HTTP/1.0 */

  for(i=0; token[i] && token[i]!='?'; i++);

  if( token[i] ) token[i++] = 0;

  putenv(util_mprintf("PATH_INFO=%s", token));

  if (token)
    req->path_info = strdup(token);

  putenv(util_mprintf("QUERY_STRING=%s", &token[i]));

  if (&token[i])
    req->query_string = strdup(&token[i]);

  if( getpeername(fileno(stdin), (struct sockaddr*)&remoteName, &size) >=0 )
    putenv(util_mprintf("REMOTE_ADDR=%s", inet_ntoa(remoteName.sin_addr)));

  /* Get all the optional fields that follow the first line.
  */
  while( fgets(request_line, sizeof(request_line), stdin) )
    {
      char *key = NULL, *val = NULL;

      key = util_extract_token(request_line, &val);

      if (key == NULL || *key == '\0')
	break;

      /* strip any spacing around key */
      while( isspace(*val) ){ val++; }
      i = strlen(val);
      while( i>0 && isspace(val[i-1]) ){ i--; } val[i] = '\0';

      /* and lower case it */
      for(i=0; key[i]; i++)
	key[i] = tolower(key[i]); 

      if(!strcmp(key,"user-agent:")) 
	{
	  putenv(util_mprintf("HTTP_USER_AGENT=%s", val));
	} 
      else if (!strcmp(key,"content-length:"))
	{
	  putenv(util_mprintf("CONTENT_LENGTH=%s", val));
	}
      else if (!strcmp(key,"referer:"))
	{
	  putenv(util_mprintf("HTTP_REFERER=%s", val));
	} 
      else if (!strcmp(key,"host:"))
	{
	  putenv(util_mprintf("HTTP_HOST=%s", val));
	}
      else if (!strcmp(key,"content-type:"))
	{
	  putenv(util_mprintf("CONTENT_TYPE=%s", val));
	}
      else if (!strcmp(key,"cookie:"))
	{
	  putenv(util_mprintf("HTTP_COOKIE=%s", val));
	}
  }

  /* Parse and store QUERY_STRING/POST/Cookie data in req object */

  if (getenv("QUERY_STRING"))
    http_request_parse_params (req, strdup((getenv("QUERY_STRING"))), '&'); 

  /* POST type data */

  content_type = getenv("CONTENT_TYPE");
  len = getenv("CONTENT_LENGTH") ? atoi(getenv("CONTENT_LENGTH")) : 0;

  if (len && content_type)
    {
      /* XXX multipart/form-data not handled .. yet XXX */
      /* only handle application/x-www-form-urlencoded  */
      if (strncmp(content_type,"application/x-www-form-urlencoded",19) != 0)
	malformed_request();

      post_data = malloc( len+1 );
      len = fread(post_data, 1, len, stdin);
      post_data[len] = '\0';
      http_request_parse_params (req, post_data, '&');
  }

  if (getenv("HTTP_COOKIE"))
    http_request_parse_params (req, strdup((getenv("HTTP_COOKIE"))), '&'); 

  return req;
}

HttpResponse*
http_response_new(HttpRequest *req) 
{
  HttpResponse *res = NULL;

  res = malloc(sizeof(HttpResponse));
  memset(res, 0, sizeof(HttpResponse)); 

  res->status       = 200;
  res->status_str   = "OK";
  res->content_type = NULL;

  return res;
}

void
http_response_set_content_type(HttpResponse *res, char *type)
{
  res->content_type = strdup(type);
}

/* like Location: hello.html\r\n */
void
http_response_append_header(HttpResponse *res, char *header)
{
  if (res->extra_headers)
    {
      res->extra_headers 
	= realloc( res->extra_headers, 
		   strlen(res->extra_headers) + strlen(header) + 1);
      strcat(res->extra_headers, header);
    }
  else
    {
      res->extra_headers = strdup(header);
    }
}


void
http_response_printf_alloc_buffer(HttpResponse *res, int bytes)
{
  res->data_len_alloced += bytes;
  res->data = realloc( res->data, res->data_len_alloced );
}


void
http_response_printf(HttpResponse *res, const char *format, ...)
{
  va_list ap;
  char    *tmp = NULL;

  va_start(ap,format);
  vasprintf(&tmp, format, ap);
  va_end(ap);

  if ((res->data_len + strlen(tmp) + 1) < res->data_len_alloced)
    {
      if (res->data_len)
	memcpy(res->data + res->data_len - 1, tmp, strlen(tmp)+1);
      else
	memcpy(res->data, tmp, strlen(tmp)+1);
    }
  else if (!res->data_len) 		/* no data printed yet */
    {
      res->data = malloc(strlen(tmp)+1);
      memcpy(res->data, tmp, strlen(tmp)+1);
      res->data_len_alloced = strlen(tmp)+1;
    }
  else
    {
      res->data = realloc( res->data, res->data_len + strlen(tmp) );
      memcpy(res->data + res->data_len - 1, tmp, strlen(tmp)+1);
      res->data_len_alloced = res->data_len + strlen(tmp);
    }

  res->data_len = strlen(res->data)+1;      
  free(tmp);
}


void
http_response_set_status(HttpResponse *res,
			 int           status_code,
			 char         *status_desc) 
{
  res->status       = status_code;
  res->status_str   = strdup(status_desc);;
}


void
http_response_set_data(HttpResponse *res, void *data, int data_len)
{
  res->data     = (char *)data;
  res->data_len = data_len; 
}


void
http_response_send_headers(HttpResponse *res)
{
  printf("HTTP/1.0 %d %s\r\n", res->status, res->status_str);

  if (res->extra_headers) printf("%s", res->extra_headers);

  /* XXX didi likes cookies etc */

  printf("Content-Type: %s; charset=UTF-8\r\n", 
	 res->content_type == NULL ? "text/html" : res->content_type);

  if (res->data_len)
    {
      printf("Content-Length: %d\r\n", res->data_len);
      printf("Connection: close\r\n"); /* if fullHttpReply */
    }

  printf("\r\n");
}

void
http_response_send(HttpResponse *res)
{
  http_response_send_headers(res);

  if( res->data )
    {
      int n_bytes = res->data_len;
      
      /* Dont send '\0' in response */
      if (res->data[n_bytes-1] == '\0') 
	n_bytes--;

      fwrite(res->data, 1, n_bytes, stdout);
    }
}

/*
** Maximum number of child processes that we can have running
** at one time before we start slowing things down.
*/
#define MAX_PARALLEL 5

/*
** Implement an HTTP server daemon.
*/
HttpRequest*
http_server(int iPort)
{
  int                listener;      /* The server socket */
  int                connection;    /* A socket for each connection */
  fd_set             readfds;       /* Set of file descriptors for select() */
  int                lenaddr;       /* Length of the inaddr structure */
  int                child;         /* PID of the child process */
  int                nchildren = 0; /* Number of child processes */
  struct timeval     delay;         /* How long to wait inside select() */
  struct sockaddr_in inaddr;        /* The socket address */
  int                reuse = 1;
  int                n = 0;
  char               url_prefix[256];

  memset(&inaddr, 0, sizeof(inaddr));
  inaddr.sin_family = AF_INET;
  inaddr.sin_addr.s_addr = INADDR_ANY;
  inaddr.sin_port = htons(iPort);
  listener = socket(AF_INET, SOCK_STREAM, 0);

  fprintf(stderr,"DidiWiki firing up ...\n");

  if( listener < 0 )
    {
      fprintf(stderr,"Can't create a socket\n");
      exit(1);
    }

#ifdef SO_REUSEADDR
  setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); 
#endif

  while (n < 10)
    {
      fprintf(stderr,"Attempting to use port %d .. ", iPort+n);

      inaddr.sin_port = htons(iPort + n);

      if( bind(listener, (struct sockaddr*)&inaddr, sizeof(inaddr)) < 0 )
	{
	  fprintf(stderr,"Failed! \n");
	  n++;
	  continue;
	}

      fprintf(stderr,"Success! \n");
      break;
    }

  if (n == 10)
    {
      fprintf(stderr,"Can't bind to any ports, giving up.\n");
      exit(1);
    }

  fprintf(stderr,"DidiWiki Started. Please point your browser at http://localhost:%i\n", iPort+n);

  /* Set DIDIWIKI_URL_PREFIX if not already set - rss uses it */
  snprintf(url_prefix, 256, "http://localhost:%i/", iPort+n);
  setenv("DIDIWIKI_URL_PREFIX", url_prefix , 0);

  listen(listener,10);

  while( 1 )
    {
      if( nchildren>MAX_PARALLEL )
	{
	  /* Slow down if connections are arriving too fast */
	  sleep( nchildren-MAX_PARALLEL );
	}

      delay.tv_sec = 60;
      delay.tv_usec = 0;
      FD_ZERO(&readfds);
      FD_SET( listener, &readfds);

      if( select( listener+1, &readfds, 0, 0, &delay) )
	{
	  lenaddr = sizeof(inaddr);
	  connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
	  if( connection>=0 )
	    {
	      child = fork();
	      if( child!=0 )
		{
		  if( child>0 ) 
		    nchildren++;
		  close(connection);
		}
	      else
		{
		  /* *child*, connect stdin/out to socket */
		  /* then return req object for caller to handle */
		  close(0);
		  dup(connection);
		  close(1);
		  dup(connection);
		  close(2);
		  dup(connection);
		  close(connection);
		  return http_request_new();
		}
	    }
	}
      
      /* Bury dead children */
      while( waitpid(0, 0, WNOHANG)>0 ) nchildren--;

      
    }

  /* NOT REACHED */  
  exit(1);
}
#ifndef _HAVE_HTTP_HEADER
#define _HAVE_HTTP_HEADER

typedef struct HttpResponse     HttpResponse;
typedef struct HttpRequest      HttpRequest;
typedef struct HttpRequestParam HttpRequestParam;

HttpRequest*
http_server(int iPort);

HttpRequest*
http_request_new(void);

char*
http_request_param_get(HttpRequest *req, char *key);

char*
http_request_get_uri(HttpRequest *req);

char*
http_request_get_path_info(HttpRequest *req);

char*
http_request_get_query_string(HttpRequest *req);


HttpResponse*
http_response_new(HttpRequest *req);

void
http_response_printf(HttpResponse *res, const char *format, ...);

void
http_response_printf_alloc_buffer(HttpResponse *res, int bytes);

void
http_response_set_content_type(HttpResponse *res, char *type);

void
http_response_set_status(HttpResponse *res,
			 int           status_code,
			 char         *status_desc) ;

void
http_response_set_data(HttpResponse *res, void *data, int data_len);

void
http_response_append_header(HttpResponse *res, char *header);

void
http_response_send_headers(HttpResponse *res);

void
http_response_send(HttpResponse *res);


#endif
/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Parts of this http and util code based on cvstrac sources. 
 *  See http://www.cvstrac.org
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "didi.h"

char *
util_mprintf(const char *format, ...)
{
  va_list ap;
  char   *buf = NULL;

  va_start(ap,format);
  vasprintf(&buf, format, ap);
  va_end(ap);

  return buf;
}


/*
 * Convert a single HEX digit to an integer
 */
int 
util_ascii_to_hex(int c)
{
  if( c>='a' && c<='f' ){
    c += 10 - 'a';
  }else if( c>='A' && c<='F' ){
    c += 10 - 'A';
  }else if( c>='0' && c<='9' ){
    c -= '0';
  }else{
    c = 0;
  }
  return c;
}

/*
 * Remove the HTTP encodings from a string.  The conversion is done
 * in-place.
 */
void 
util_dehttpize(char *z)
{
  int i = 0, j = 0;

  while(z[i])
    {
      switch(z[i])
	{
	case '%':
	  if( z[i+1] && z[i+2] ){
	    z[j] = util_ascii_to_hex(z[i+1]) << 4;
	    z[j] |= util_ascii_to_hex(z[i+2]);
	    i += 2;
	  }
	  break;
	case '+':
	  z[j] = ' ';
	  break;
	default:
	  z[j] = z[i];
	  break;
	}
      i++; j++;
    }
  z[j] = 0;
}

/*
 * Remove the first space-delimited token from a string and return
 * a pointer to it.  Add a NULL to the string to terminate the token.
 * Make *zLeftOver point to the start of the next token.
 *
 */
char *
util_extract_token(char *input, char **leftOver)
{
  char *result = 0;

  if (input == 0)
    {
      if( leftOver ) *leftOver = 0;
      return 0;
    }

  while( isspace(*input) ) 
    input++; 

  result = input;

  while( *input && !isspace(*input) )
    input++; 

  if (*input)
    {
      *input = 0;
      input++;
      while( isspace(*input) ){ input++; }
    }

  if( leftOver )
    *leftOver = input; 

  return result;
}


/*
 * Make the given string safe for HTML by converting every "<" into "&lt;",
 * every ">" into "&gt;" and every "&" into "&amp;".  Return a pointer
 * to a new string obtained from malloc().
 *
 * We also encode " as &quot; so that it can appear as an argument
 * to markup.
 *
 */
char*
util_htmlize(const char *in, int n)
{
  int c;
  int i = 0;
  int count = 0;
  char *out;

  if( n<0 ) n = strlen(in);
  while( i<n && (c = in[i])!=0 ){
    switch( c ){
      case '<':   count += 4;       break;
      case '>':   count += 4;       break;
      case '&':   count += 5;       break;
      case '"':   count += 6;       break;
      default:    count++;          break;
    }
    i++;
  }
  i = 0;
  out = malloc( count+1 );
  if( out==0 ) return 0;
  while( n-->0 && (c = *in)!=0 ){
    switch( c ){
      case '<':   
        out[i++] = '&';
        out[i++] = 'l';
        out[i++] = 't';
        out[i++] = ';';
        break;
      case '>':   
        out[i++] = '&';
        out[i++] = 'g';
        out[i++] = 't';
        out[i++] = ';';
        break;
      case '&':   
        out[i++] = '&';
        out[i++] = 'a';
        out[i++] = 'm';
        out[i++] = 'p';
        out[i++] = ';';
        break;
      case '"':   
        out[i++] = '&';
        out[i++] = 'q';
        out[i++] = 'u';
        out[i++] = 'o';
        out[i++] = 't';
        out[i++] = ';';
        break;
      default:
        out[i++] = c;
        break;
    }
    in++;
  }
  out[i] = 0;
  return out;
}
#ifndef _HAVE_UTIL_HEADER
#define _HAVE_UTIL_HEADER

char *
util_mprintf(const char *format, ...);

int 
util_ascii_to_hex(int c);

void 
util_dehttpize(char *z);

char *
util_extract_token(char *input, char **leftOver);

char *
util_htmlize(const char *in, int n);


#endif
/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Copyright 2004 Matthew Allum <mallum@o-hand.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "didi.h"
#include "wikitext.h"

static char* CssData = STYLESHEET;

static char *
get_line_from_string(char **lines, int *line_len)
{
  int   i;
  char *z = *lines;

  if( z[0] == '\0' ) return NULL;

  for (i=0; z[i]; i++)
    {
      if (z[i] == '\n')
	{
	  if (i > 0 && z[i-1]=='\r')
	    { z[i-1] = '\0'; }
	  else
	    { z[i] = '\0'; }
	  i++;
	  break;
	}
    }

  /* advance lines on */
  *lines      = &z[i];
  *line_len -= i;

  return z;
}

static char*
check_for_link(char *line, int *skip_chars)
{
  char *start =  line;
  char *p     =  line;
  char *url   =  NULL;
  char *title =  NULL;
  char *result = NULL;
  int   found = 0;

  if (*p == '[') 		/* [ link [title] ] */
    {
      /* XXX TODO XXX 
       * Allow links like [the Main page] ( covert to the_main_page )
       */
      url = start+1; *p = '\0'; p++;
      while (  *p != ']' && *p != '\0' && !isspace(*p) ) p++;

      if (isspace(*p))
	{
	  *p = '\0';
	  title = ++p; 
	  while (  *p != ']' && *p != '\0' ) 
	    p++;
	}

      *p = '\0';
      p++;
    }                     
  else if (!strncasecmp(p, "http://", 7)
	   || !strncasecmp(p, "mailto://", 9)
	   || !strncasecmp(p, "file://", 7))
    {
      while ( *p != '\0' && !isspace(*p) ) p++;


      found = 1;
    }
  else if (isupper(*p))      	/* Camel-case */
    {
      int num_upper_char = 1;
      p++;
      while ( *p != '\0' && isalnum(*p) )
	{
	  if (isupper(*p))
	    { found = 1; num_upper_char++; }
	  p++;
	}

      if (num_upper_char == (p-start)) /* Dont make ALLCAPS links */
	return NULL;
    }

  if (found)  /* cant really set http/camel links in place */
    {
      url = malloc(sizeof(char) * ((p - start) + 2) );
      memset(url, 0, sizeof(char) * ((p - start) + 2));
      strncpy(url, start, p - start);
      *start = '\0';
    }

  if (url != NULL)
    {
      int len = strlen(url);

      *skip_chars = p - start;

      /* is it an image ? */
      if (!strncmp(url+len-4, ".gif", 4) || !strncmp(url+len-4, ".png", 4) 
	  || !strncmp(url+len-4, ".jpg", 4) || !strncmp(url+len-5, ".jpeg", 5))
	{
	  if (title)
	    asprintf(&result, "<a href='%s'><img src='%s' border='0'></a>",
		     title, url);
	  else
	    asprintf(&result, "<img src='%s' border='0'>", url);
	}
      else
	{
	  char *extra_attr = "";

	  if (!strncasecmp(url, "http://", 7))
	    extra_attr = " title='WWW link' ";

	  if (title)
	    asprintf(&result,"<a %s href='%s'>%s</a>", extra_attr, url, title);
	  else
	    asprintf(&result, "<a %s href='%s'>%s</a>", extra_attr, url, url);
	}

      

      return result;
    }

  return NULL;
}


static char *
file_read(char *filename)
{
  struct stat st;
  FILE*       fp;
  char*       str;
  int         len;

  /* Get the file size. */
  if (stat(filename, &st)) 
    return NULL;

  if (!(fp = fopen(filename, "rb"))) 
    return NULL;
  
  str = (char *)malloc(sizeof(char)*(st.st_size + 1));
  len = fread(str, 1, st.st_size, fp);
  if (len >= 0) str[len] = '\0';
  
  fclose(fp);

  return str;
}


static int
file_write(char *filename, char *data)
{
  FILE*       fp;
  int         bytes_written = 0;
  int         len           = strlen(data); /* clip off extra '\0' */

  if (!(fp = fopen(filename, "wb"))) 
    return -1;
 
  while ( len > 0 )
    {
      bytes_written = fwrite(data, sizeof(char), len, fp);
      len = len - bytes_written;
      data = data + bytes_written;
    }

  fclose(fp);

  return 1;
}

static int
is_wiki_format_char_or_space(char c)
{
  if (isspace(c)) return 1;
  if (strchr("/*_-", c)) return 1; 
  return 0;
}

void
wiki_print_data_as_html(HttpResponse *res, char *raw_page_data)
{
  char *p = raw_page_data;	    /* accumalates non marked up text */
  char *q = NULL, *link = NULL; /* temporary scratch stuff */
  char *line = NULL;
  int   line_len;
  int   i, j, skip_chars;

  /* flags, mainly for open tag states */
  int bold_on      = 0;
  int italic_on    = 0;
  int underline_on = 0;
  int strikethrough_on = 0;
  int open_para    = 0;
  int pre_on       = 0;
  int table_on     = 0;

#define ULIST 0
#define OLIST 1
#define NUM_LIST_TYPES 2

  struct { char ident; int  depth; char *tag; } listtypes[] = {
    { '*', 0, "ul" },
    { '#', 0, "ol" }
  };


  q = p;  /* p accumalates non marked up text, q is just a pointer
	   * to the end of the current line - used by below func. 
	   */

  while ( (line = get_line_from_string(&q, &line_len)) )
    {
      int   header_level = 0; 
      char *line_start   = line;
      int   skip_to_content = 0;
      /*
       *  process any initial wiki chars at line beginning 
       */

      if (pre_on && !isspace(*line) && *line != '\0')
	{
	  /* close any preformatting if already on*/
	  http_response_printf(res, "\n</pre>\n") ;
	  pre_on = 0;
	}

      /* Handle ordered & unordered list, code is a bit mental.. */
      for (i=0; i<NUM_LIST_TYPES; i++)
	{

	  /* extra checks avoid bolding */
	  if ( *line == listtypes[i].ident
	       && ( *(line+1) == listtypes[i].ident || isspace(*(line+1)) ) ) 
	    {                     	
	      int item_depth = 0;

	      if (listtypes[!i].depth)
		{
		  for (j=0; j<listtypes[!i].depth; j++)
		    http_response_printf(res, "</%s>\n", listtypes[!i].tag);
		  listtypes[!i].depth = 0;
		}

	      while ( *line == listtypes[i].ident ) { line++; item_depth++; }
	  
	      if (item_depth < listtypes[i].depth)
		{
		  for (j = 0; j < (listtypes[i].depth - item_depth); j++)
		    http_response_printf(res, "</%s>\n", listtypes[i].tag);
		}
	      else
		{
		  for (j = 0; j < (item_depth - listtypes[i].depth); j++)
		    http_response_printf(res, "<%s>\n", listtypes[i].tag);
		}
	      
	      http_response_printf(res, "<li>");
	      
	      listtypes[i].depth = item_depth;
	      
	      skip_to_content = 1;
	    }
	  else if (listtypes[i].depth && !listtypes[!i].depth) 
	    {
	      /* close current list */

	      for (j=0; j<listtypes[i].depth; j++)
		http_response_printf(res, "</%s>\n", listtypes[i].tag);
	      listtypes[i].depth = 0;
	    }
	}

      if (skip_to_content)
	goto line_content; /* skip parsing any more initial chars */

      /* Tables */

      if (*line == '|')
        {
	  if (table_on==0)
	    http_response_printf(res, "<table class='wikitable' cellspacing='0' cellpadding='4'>\n");
	  line++;

	    http_response_printf(res, "<tr><td>");

	  table_on = 1;
	  goto line_content;
        }
      else
        {
	  if(table_on)
	    {
	      http_response_printf(res, "</table>\n");
	      table_on = 0;
	    }
        }

      /* pre formated  */

      if ( (isspace(*line) || *line == '\0'))
	{
	  int n_spaces = 0;

	  while ( isspace(*line) ) { line++; n_spaces++; }

	  if (*line == '\0')  /* empty line - para */
	    {
	      if (pre_on)
		{
		  http_response_printf(res, "\n") ;
		  continue;
		}
	      else if (open_para)
		{
		  http_response_printf(res, "\n</p><p>\n") ;
		}
	      else
		{
		  http_response_printf(res, "\n<p>\n") ;
		  open_para = 1;
		}
	    }
	  else /* starts with space so Pre formatted, see above for close */
	    {
	      if (!pre_on)
		http_response_printf(res, "<pre>\n") ;
	      pre_on = 1;
	      line = line - ( n_spaces - 1 ); /* rewind so extra spaces
                                                 they matter to pre */
	      http_response_printf(res, "%s\n", line);
	      continue;
	    }
	}
      else if ( *line == '=' )
	{
	  while (*line == '=')
	    { header_level++; line++; }

	  http_response_printf(res, "<h%d>", header_level);
	  p = line;
	}
      else if ( *line == '-' && *(line+1) == '-' )
	{
	  /* rule */
	  http_response_printf(res, "<hr/>\n");
	  while ( *line == '-' ) line++;
	}

    line_content:

      /* 
       * now process rest of the line 
       */

      p = line;

      while ( *line != '\0' )
	{
	  if ( *line == '!' && !isspace(*(line+1))) 
	    {                	/* escape next word - skip it */
	      *line = '\0';
	      http_response_printf(res, "%s", p);
	      p = ++line;

	      while (*line != '\0' && !isspace(*line)) line++;
	      if (*line == '\0')
		continue;
	    }
	  else if ((link = check_for_link(line, &skip_chars)) != NULL)
	    {
	      http_response_printf(res, "%s", p);
	      http_response_printf(res, "%s", link); 

	      line += skip_chars;
	      p = line;

	      continue;

	    }
	  /* TODO: Below is getting bloated and messy, need rewriting more
	   *       compactly ( and efficently ).
	   */
	  else if (*line == '*')
	    {
	      /* Try and be smart about what gets bolded */
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !bold_on)
		{ line++; continue; }

	      if ((isspace(*(line+1)) && !bold_on))
		{ line++; continue; }

		/* bold */
		*line = '\0';
		http_response_printf(res, "%s%s\n", p, bold_on ? "</b>" : "<b>");
		bold_on ^= 1; /* reset flag */
		p = line+1;

	    }
	  else if (*line == '_' )
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !underline_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !underline_on)
		{ line++; continue; }
	      /* underline */
	      *line = '\0';
	      http_response_printf(res, "%s%s\n", p, underline_on ? "</u>" : "<u>"); 
	      underline_on ^= 1; /* reset flag */
	      p = line+1;
	    }
	  else if (*line == '-')
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !strikethrough_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !strikethrough_on)
		{ line++; continue; }
	       
	      /* strikethrough */
	      *line = '\0';
	      http_response_printf(res, "%s%s\n", p, strikethrough_on ? "</del>" : "<del>"); 
	      strikethrough_on ^= 1; /* reset flag */
	      p = line+1; 
	      

	    }
	  else if (*line == '/' )
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !italic_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !italic_on)
		{ line++; continue; }

	      /* crude path detection */
	      if (line_start != line && isspace(*(line-1)) && !italic_on)
		{ 
		  char *tmp   = line+1;
		  int slashes = 0;

		  /* Hack to escape out file paths */
		  while (*tmp != '\0' && !isspace(*tmp))
		    { 
		      if (*tmp == '/') slashes++;
		      tmp++;
		    }

		  if (slashes > 1 || (slashes == 1 && *(tmp-1) != '/')) 
		    { line = tmp; continue; }
		}

	      if (*(line+1) == '/')
		line++; 	/* escape out common '//' - eg urls */
	      else
		{
		  /* italic */
		  *line = '\0';
		  http_response_printf(res, "%s%s\n", p, italic_on ? "</i>" : "<i>"); 
		  italic_on ^= 1; /* reset flag */
		  p = line+1; 
		}
	    }
	  else if (*line == '|' && table_on) /* table column */
	    {
	      *line = '\0';
	      http_response_printf(res, "%s", p);
	      http_response_printf(res, "</td><td>\n");
	      p = line+1;
	    }

	  line++;

	} /* next word */

      if (*p != '\0') 			/* accumalated text left over */
	http_response_printf(res, "%s", p);

      /* close any html tags that could be still open */


      if (listtypes[ULIST].depth)
	http_response_printf(res, "</li>");

      if (listtypes[OLIST].depth)
	http_response_printf(res, "</li>");

      if (table_on)
	http_response_printf(res, "</td></tr>\n");

      if (header_level)
	http_response_printf(res, "</h%d>\n", header_level);  
      else
	http_response_printf(res, "\n");


    } /* next line */

  /* clean up anything thats still open */

  if (pre_on)
    http_response_printf(res, "</pre>\n");
  
  /* close any open lists */
  for (i=0; i<listtypes[ULIST].depth; i++)
    http_response_printf(res, "</ul>\n");

  for (i=0; i<listtypes[OLIST].depth; i++)
    http_response_printf(res, "</ol>\n");
  
  /* close any open paras */
  if (open_para)
    http_response_printf(res, "</p>\n");

    /* tables */
    if (table_on)
     http_response_printf(res, "</table>\n");

}

int 
wiki_redirect(HttpResponse *res, char *location)
{
  int   header_len = strlen(location) + 14; 
  char *header = alloca(sizeof(char)*header_len);

  snprintf(header, header_len, "Location: %s\r\n", location);

  http_response_append_header(res, header);
  http_response_printf(res, "<html>\n<p>Redirect to %s</p>\n</html>\n", 
		       location);
  http_response_set_status(res, 302, "Moved Temporarily");
  http_response_send(res);

  exit(0);
}


void
wiki_show_page(HttpResponse *res, char *wikitext, char *page)
{
  char *html_clean_wikitext = NULL;

  http_response_printf_alloc_buffer(res, strlen(wikitext)*2);

  wiki_show_header(res, page, TRUE);

  html_clean_wikitext = util_htmlize(wikitext, strlen(wikitext));

  wiki_print_data_as_html(res, html_clean_wikitext);      

  wiki_show_footer(res);  

  http_response_send(res);

  exit(0);

}

void
wiki_show_edit_page(HttpResponse *res, char *wikitext, char *page)
{
  wiki_show_header(res, page, FALSE);

  if (wikitext == NULL) wikitext = "";
  http_response_printf(res, EDITFORM, page, wikitext);
		       
  wiki_show_footer(res);

  http_response_send(res);
  exit(0);
}

void
wiki_show_create_page(HttpResponse *res)
{
  wiki_show_header(res, "Create New Page", FALSE);
  http_response_printf(res, "%s", CREATEFORM);
  wiki_show_footer(res);

  http_response_send(res);
  exit(0);
}

static int 
changes_compar(const struct dirent **d1, const struct dirent **d2)
{
    struct stat st1, st2;

    stat((*d1)->d_name, &st1);

    stat((*d2)->d_name, &st2);

    if (st1.st_mtime > st2.st_mtime)
      return 1;
    else
      return -1;
}



WikiPageList**
wiki_get_pages(int  *n_pages, char *expr)
{
  WikiPageList  **pages;
  struct dirent **namelist;
  int             n, i = 0;
  struct stat     st;

  n = scandir(".", &namelist, 0, (void *)changes_compar);
  
  pages = malloc(sizeof(WikiPageList*)*n);

  while(n--) 
    {
      if ((namelist[n]->d_name)[0] == '.' 
	  || !strcmp(namelist[n]->d_name, "styles.css"))
	goto cleanup;

      if (expr != NULL) 
	{ 			/* Super Simple Search */
	  char *data = NULL;
	  if ((data = file_read(namelist[n]->d_name)) != NULL)
	    if (strstr(data, expr) == NULL)
	      if (strcmp(namelist[n]->d_name, expr) != 0) 
		goto cleanup; 
	}


      stat(namelist[n]->d_name, &st);

      /* ignore anything but regular readable files */
      if (S_ISREG(st.st_mode) && access(namelist[n]->d_name, R_OK) == 0)
	{
	  pages[i]        = malloc(sizeof(WikiPageList));
	  pages[i]->name  = strdup (namelist[n]->d_name);
	  pages[i]->mtime = st.st_mtime;

	  i++;
	}

    cleanup:
      free(namelist[n]);
    }

  *n_pages = i;

  free(namelist);

  if (i==0) return NULL;

  return pages;
}

void
wiki_show_changes_page(HttpResponse *res)
{
  WikiPageList **pages = NULL;
  int            n_pages, i;

  wiki_show_header(res, "Changes", FALSE);

  pages = wiki_get_pages(&n_pages, NULL);

  for (i=0; i<n_pages; i++)
    {
      struct tm   *pTm;
      char   datebuf[64];

      pTm = localtime(&pages[i]->mtime);
      strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M", pTm);
      http_response_printf(res, "<a href='%s'>%s</a> %s<br />\n", 
			   pages[i]->name, 
			   pages[i]->name, 
			   datebuf);
    }

  wiki_show_footer(res);
  http_response_send(res);
  exit(0);
}

void
wiki_show_changes_page_rss(HttpResponse *res)
{
  WikiPageList **pages = NULL;
  int            n_pages, i;
  /*char          *html_clean_wikitext = NULL;
    char          *wikitext; */

  pages = wiki_get_pages(&n_pages, NULL);

  http_response_printf(res, "<?xml version=\"1.0\"encoding=\"ISO-8859-1\"?>\n"
                            "<rss version=\"2.0\">\n"
		            "<channel><title>DidiWiki Changes feed</title>\n");

  for (i=0; i<n_pages; i++)
    {
      struct tm   *pTm;
      char         datebuf[64];

      pTm = localtime(&pages[i]->mtime);
      strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M", pTm);

      http_response_printf(res, 
			   "<item><title>%s</title>"
			   "<link>%s%s</link><description>"
			   "Modified %s\n",
                           pages[i]->name,
			   getenv("DIDIWIKI_URL_PREFIX") ? getenv("DIDIWIKI_URL_PREFIX") : "", 
                           pages[i]->name,
			   datebuf);

      /*
      wikitext = file_read(pages[i]->name);
      http_response_printf_alloc_buffer(res, strlen(wikitext)*2);
      html_clean_wikitext = util_htmlize(wikitext, strlen(wikitext));
      wiki_print_data_as_html(res, html_clean_wikitext);
      */
      http_response_printf(res, "</description></item>\n");
    }

  http_response_printf(res, "</channel>\n</rss>");

  http_response_send(res);
  exit(0);
}


void
wiki_show_search_results_page(HttpResponse *res, char *expr)
{
  WikiPageList **pages = NULL;
  int            n_pages, i;

  if (expr == NULL || strlen(expr) == 0)
    {
      wiki_show_header(res, "Search", FALSE);
      http_response_printf(res, "No Search Terms supplied");
      wiki_show_footer(res);
      http_response_send(res);
      exit(0);
    }

  pages = wiki_get_pages(&n_pages, expr);

  if (pages)
    {
      for (i=0; i<n_pages; i++)
	if (!strcmp(pages[i]->name, expr)) /* redirect on page name match */
	  wiki_redirect(res, pages[i]->name);

      wiki_show_header(res, "Search", FALSE);

      for (i=0; i<n_pages; i++)
	{
	  http_response_printf(res, "<a href='%s'>%s</a><br />\n", 
			       pages[i]->name, 
			       pages[i]->name);
	}
    }
  else 
    {
      wiki_show_header(res, "Search", FALSE);
      http_response_printf(res, "No matches");
    }

  wiki_show_footer(res);
  http_response_send(res);

  exit(0);
}

void 
wiki_show_template(HttpResponse *res, char *template_data)
{
  /* 4 templates - header.html, footer.html, 
                   header-noedit.html, footer-noedit.html

     Vars;

     $title      - page title. 
     $include()  - ?
     $pages 

  */

}

void
wiki_show_header(HttpResponse *res, char *page_title, int want_edit)
{
  http_response_printf(res, 
                       "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
                       "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
		       "<head>\n"
                       "<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n" 
		       "<link rel='SHORTCUT ICON' href='favicon.ico' />\n"
		       "<link media='all' href='styles.css' rel='stylesheet' type='text/css' />\n"
		       "<title>%s</title>\n"

		       "</head>\n"
		       "<body>\n", page_title
		       );

  http_response_printf(res, PAGEHEADER, page_title, 
		       (want_edit) ? " ( <a href='?edit' title='Edit this wiki page contents. [alt-j]' accesskey='j'>Edit</a> ) " : "" );     
}

void
wiki_show_footer(HttpResponse *res)
{
  http_response_printf(res, "%s", PAGEFOOTER);

  http_response_printf(res, 
		       "</body>\n"
		       "</html>\n"
		       );
}

int page_name_is_good(char* page_name)
{
/* We should give access only to subdirs of didiwiki root.
   I guess that check for absense of '/' is enough.

   TODO: Use realpath()
*/
    if (!page_name)
        return FALSE;

    if (!isalnum(page[0]))
        return FALSE;

    if (strstr(page, ".."))
        return FALSE;

    return TRUE;
}


void
wiki_handle_rest_call(HttpRequest  *req, 
		      HttpResponse *res,
		      char         *func)
{

  if (func != NULL && *func != '\0')
    {
      if (!strcmp(func, "page/get"))
	{
	  char *page = http_request_param_get(req, "page");

	  if (page == NULL)
	    page = http_request_get_query_string(req);

	  if (page && page_name_is_good(page) && (access(page, R_OK) == 0))
	    {
	      http_response_printf(res, "%s", file_read(page));
	      http_response_send(res);
	      return;
	    }  
	}
      else if (!strcmp(func, "page/set"))
	{
	  char *wikitext = NULL, *page = NULL;
	  if( ( (wikitext = http_request_param_get(req, "text")) != NULL)
	      && ( (page = http_request_param_get(req, "page")) != NULL))
	    {
	  if (page_name_is_good(page))
	    {
	      file_write(page, wikitext);
	      http_response_printf(res, "success");
	      http_response_send(res);
	      return;
	    }
	    }
	}
      else if (!strcmp(func, "page/delete"))
	{
	  char *page = http_request_param_get(req, "page");

	  if (page == NULL)
	    page = http_request_get_query_string(req);

	  if (page && page_name_is_good(page) && (unlink(page) > 0))
	    {
	      http_response_printf(res, "success");
	      http_response_send(res);
	      return;  
	    }
	}
      else if (!strcmp(func, "page/exists"))
	{
	  char *page = http_request_param_get(req, "page");

	  if (page == NULL)
	    page = http_request_get_query_string(req);

	  if (page && page_name_is_good(page) && (access(page, R_OK) == 0))
	    {
	      http_response_printf(res, "success");
	      http_response_send(res);
	      return;  
	    }
	}
      else if (!strcmp(func, "pages") || !strcmp(func, "search"))
	{
	  WikiPageList **pages = NULL;
	  int            n_pages, i;
	  char          *expr = http_request_param_get(req, "expr");

	  if (expr == NULL)
	    expr = http_request_get_query_string(req);
	  
	  pages = wiki_get_pages(&n_pages, expr);

	  if (pages)
	    {
	      for (i=0; i<n_pages; i++)
		{
		  struct tm   *pTm;
		  char   datebuf[64];
		  
		  pTm = localtime(&pages[i]->mtime);
		  strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M", pTm);
		  http_response_printf(res, "%s\t%s\n", pages[i]->name, datebuf);
		}

	      http_response_send(res);
	      return;  
	    }
	}
    }

  http_response_set_status(res, 500, "Error");
  http_response_printf(res, "<html><body>Failed</body></html>\n");
  http_response_send(res);

  return;  
}



void
wiki_handle_http_request(HttpRequest *req)
{
  HttpResponse *res      = http_response_new(req);
  char         *page     = http_request_get_path_info(req); 
  char         *command  = http_request_get_query_string(req); 
  char         *wikitext = "";

  util_dehttpize(page); 	/* remove any encoding on the requested
				   page name.                           */

  if (!strcmp(page, "/"))
    {
      if (access("WikiHome", R_OK) != 0)
	wiki_redirect(res, "/WikiHome?create");
      page = "/WikiHome";
    }

  if (!strcmp(page, "/styles.css"))
    {
      /*  Return CSS page */
      http_response_set_content_type(res, "text/css");
      http_response_printf(res, "%s", CssData);
      http_response_send(res);
      exit(0);
    }

  if (!strcmp(page, "/favicon.ico"))
    {
      /*  Return favicon */
      http_response_set_content_type(res, "image/ico");
      http_response_set_data(res, FaviconData, FaviconDataLen);
      http_response_send(res);
      exit(0);
    }


  page = page + 1; 		/* skip slash */

  if (!strncmp(page, "api/", 4))
    {
      char *p;

      page += 4; 
      for (p=page; *p != '\0'; p++)
	if (*p=='?') { *p ='\0'; break; }
      
      wiki_handle_rest_call(req, res, page); 
      exit(0);
    }

  /* A little safety. issue a malformed request for any paths,
   * There shouldn't need to be any..
   */
  if (!page_name_is_good(page))
    {
      http_response_set_status(res, 404, "Not Found");
      http_response_printf(res, "<html><body>404 Not Found</body></html>\n");
      http_response_send(res);
      exit(0);
    }

  if (!strcmp(page, "Changes"))
    {
      wiki_show_changes_page(res);
    }
  else if (!strcmp(page, "ChangesRss"))
    {
      wiki_show_changes_page_rss(res);
    }
  else if (!strcmp(page, "Search"))
    {
      wiki_show_search_results_page(res, http_request_param_get(req, "expr"));
    }
  else if (!strcmp(page, "Create"))
    {
      if ( (wikitext = http_request_param_get(req, "title")) != NULL)
	{
	  /* create page and redirect */
	  wiki_redirect(res, http_request_param_get(req, "title"));
	}
      else
	{
	   /* show create page form  */
	  wiki_show_create_page(res);
	}
    }
  else
    {
      /* TODO: dont blindly write wikitext data to disk */
      if ( (wikitext = http_request_param_get(req, "wikitext")) != NULL)
	{
	  file_write(page, wikitext);	      
	}

      if (access(page, R_OK) == 0) 	/* page exists */
	{
	  wikitext = file_read(page);
	  
	  if (!strcmp(command, "edit"))
	    {
	      /* print edit page */
	      wiki_show_edit_page(res, wikitext, page);
	    }
	  else
	    {
	      wiki_show_page(res, wikitext, page);
	    }
	}
      else
	{
	  if (!strcmp(command, "create"))
	    {
	      wiki_show_edit_page(res, NULL, page);
	    }
	  else
	    {
	      char buf[1024];
	      snprintf(buf, 1024, "%s?create", page);
	      wiki_redirect(res, buf);
	    }
	}
    }

}

int
wiki_init(void)
{
  char datadir[512] = { 0 };
  struct stat st;

  if (getenv("DIDIWIKIHOME"))
    {
      snprintf(datadir, 512, getenv("DIDIWIKIHOME"));
    }
  else
    {
      if (getenv("HOME") == NULL)
	{
	  fprintf(stderr, "Unable to get home directory, is HOME set?\n");
	  exit(1);
	}

      snprintf(datadir, 512, "%s/.didiwiki", getenv("HOME"));
    }  
     
  /* Check if ~/.didiwiki exists and create if not */
  if (stat(datadir, &st) != 0 )
    {
      if (mkdir(datadir, 0755) == -1)
	{
	  fprintf(stderr, "Unable to create '%s', giving up.\n", datadir);
	  exit(1);
	}
    }

  chdir(datadir);

  /* Write Default Help + Home page if it doesn't exist */

  if (access("WikiHelp", R_OK) != 0) 
    file_write("WikiHelp", HELPTEXT);

  if (access("WikiHome", R_OK) != 0) 
    file_write("WikiHome", HOMETEXT);

  /* Read in optional CSS data */

  if (access("styles.css", R_OK) == 0) 
    CssData = file_read("styles.css");
  
  return 1;
}

/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Copyright 2004 Matthew Allum <mallum@o-hand.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#include "didi.h"
#include "wikitext.h"

static char* CssData = STYLESHEET;

static char *
get_line_from_string(char **lines, int *line_len)
{
  int   i;
  char *z = *lines;

  if( z[0] == '\0' ) return NULL;

  for (i=0; z[i]; i++)
    {
      if (z[i] == '\n')
	{
	  if (i > 0 && z[i-1]=='\r')
	    { z[i-1] = '\0'; }
	  else
	    { z[i] = '\0'; }
	  i++;
	  break;
	}
    }

  /* advance lines on */
  *lines      = &z[i];
  *line_len -= i;

  return z;
}

static char*
check_for_link(char *line, int *skip_chars)
{
  char *start =  line;
  char *p     =  line;
  char *url   =  NULL;
  char *title =  NULL;
  char *result = NULL;
  char  tmp;
  int   found = 0;

  if (*p == '[') 		/* [ link [title] ] */
    {
      /* XXX TODO XXX 
       * Allow links like [the Main page] ( covert to the_main_page )
       * 
       *
       */


      url = start+1; *p = '\0'; p++;
      while (  *p != ']' && *p != '\0' && !isspace(*p) ) p++;

      if (isspace(*p))
	{
	  *p = '\0';
	  title = ++p; 
	  while (  *p != ']' && *p != '\0' ) 
	    p++;
	}

      *p = '\0';
      p++;
    }                     
  else if (!strncasecmp(p, "http://", 7)
	   || !strncasecmp(p, "mailto://", 9)
	   || !strncasecmp(p, "file://", 7))
    {
      while ( *p != '\0' && !isspace(*p) ) p++;


      found = 1;
    }
  else if (isupper(*p))      	/* Camel-case */
    {
      int num_upper_char = 1;
      p++;
      while ( *p != '\0' && isalnum(*p) )
	{
	  if (isupper(*p))
	    { found = 1; num_upper_char++; }
	  p++;
	}

      if (num_upper_char == (p-start)) /* Dont make ALLCAPS links */
	return NULL;
    }

  if (found)  /* cant really set http/camel links in place */
    {
      url = malloc(sizeof(char) * ((p - start) + 2) );
      memset(url, 0, sizeof(char) * ((p - start) + 2));
      strncpy(url, start, p - start);
      *start = '\0';
    }

  if (url != NULL)
    {
      int len = strlen(url);

      *skip_chars = p - start;

      /* is it an image ? */
      if (!strncmp(url+len-4, ".gif", 4) || !strncmp(url+len-4, ".png", 4) 
	  || !strncmp(url+len-4, ".jpg", 4) || !strncmp(url+len-5, ".jpeg", 5))
	{
	  if (title)
	    asprintf(&result, "<a href='%s'><img src='%s' border='0'></a>",
		     title, url);
	  else
	    asprintf(&result, "<img src='%s' border='0'>", url);
	}
      else
	{
	  char *extra_attr = NULL;

	  if (!strncasecmp(url, "http://", 7))
	    extra_attr = " title='WWW link' ";

	  if (title)
	    asprintf(&result,"<a %s href='%s'>%s</a>", extra_attr, url, title);
	  else
	    asprintf(&result, "<a %s href='%s'>%s</a>", extra_attr, url, url);
	}

      

      return result;
    }

  return NULL;
}


static char *
file_read(char *filename)
{
  struct stat st;
  FILE*       fp;
  char*       str;
  int         len;

  /* Get the file size. */
  if (stat(filename, &st)) 
    return NULL;

  if (!(fp = fopen(filename, "rb"))) 
    return NULL;
  
  str = (char *)malloc(sizeof(char)*(st.st_size + 1));
  len = fread(str, 1, st.st_size, fp);
  if (len >= 0) str[len] = '\0';
  
  fclose(fp);

  return str;
}


static int
file_write(char *filename, char *data)
{
  FILE*       fp;
  int         bytes_written = 0;
  int         len           = strlen(data)+1;

  if (!(fp = fopen(filename, "wb"))) 
    return -1;
 
  while ( len > 0 )
    {
      bytes_written = fwrite(data, sizeof(char), len, fp);
      len = len - bytes_written;
      data = data + bytes_written;
    }

  fclose(fp);

  return 1;
}

static int
is_wiki_format_char_or_space(char c)
{
  if (isspace(c)) return 1;
  if (strchr("/*_-", c)) return 1; 
  return 0;
}

void
wiki_print_data_as_html(HttpResponse *res, char *raw_page_data)
{
  char *p = raw_page_data;	    /* accumalates non marked up text */
  char *q = NULL, *url = NULL, *link = NULL; /* temporary scratch stuff */
  char *line = NULL;
  int   line_len;
  int   i, j, skip_chars;

  /* flags, mainly for open tag states */
  int bold_on      = 0;
  int italic_on    = 0;
  int underline_on = 0;
  int strikethrough_on = 0;
  int open_para    = 0;
  int pre_on       = 0;
  int list_depth   = 0;
  int table_on     = 0;

#define ULIST 0
#define OLIST 1
#define NUM_LIST_TYPES 2

  struct { char ident; int  depth; char *tag; } listtypes[] = {
    { '*', 0, "ul" },
    { '#', 0, "ol" }
  };


  q = p;  /* p accumalates non marked up text, q is just a pointer
	   * to the end of the current line - used by below func. 
	   */

  while ( (line = get_line_from_string(&q, &line_len)) )
    {
      int   header_level = 0; 
      char *line_start   = line;
      int   skip_to_content = 0;
      /*
       *  process any initial wiki chars at line beginning 
       */

      if (pre_on && !isspace(*line) && *line != '\0')
	{
	  /* close any preformatting if already on*/
	  http_response_printf(res, "\n</pre>\n") ;
	  pre_on = 0;
	}

      /* Handle ordered & unordered list, code is a bit mental.. */
      for (i=0; i<NUM_LIST_TYPES; i++)
	{

	  /* extra checks avoid bolding */
	  if ( *line == listtypes[i].ident
	       && ( *(line+1) == listtypes[i].ident || isspace(*(line+1)) ) ) 
	    {                     	
	      int item_depth = 0;

	      if (listtypes[!i].depth)
		{
		  for (j=0; j<listtypes[!i].depth; j++)
		    http_response_printf(res, "</%s>\n", listtypes[!i].tag);
		  listtypes[!i].depth = 0;
		}

	      while ( *line == listtypes[i].ident ) { line++; item_depth++; }
	  
	      if (item_depth < listtypes[i].depth)
		{
		  for (j = 0; j < (listtypes[i].depth - item_depth); j++)
		    http_response_printf(res, "</%s>\n", listtypes[i].tag);
		}
	      else
		{
		  for (j = 0; j < (item_depth - listtypes[i].depth); j++)
		    http_response_printf(res, "<%s>\n", listtypes[i].tag);
		}
	      
	      http_response_printf(res, "<li>");
	      
	      listtypes[i].depth = item_depth;
	      
	      skip_to_content = 1;
	    }
	  else if (listtypes[i].depth && !listtypes[!i].depth) 
	    {
	      /* close current list */

	      for (j=0; j<listtypes[i].depth; j++)
		http_response_printf(res, "</%s>\n", listtypes[i].tag);
	      listtypes[i].depth = 0;
	    }
	}

      if (skip_to_content)
	goto line_content; /* skip parsing any more initial chars */

      /* Tables */

      if (*line == '|')
        {
	  if (table_on==0)
	    http_response_printf(res, "<table class='wikitable' cellspacing='0' cellpadding='4'>\n");
	  line++;

	    http_response_printf(res, "<tr><td>");

	  table_on = 1;
	  goto line_content;
        }
      else
        {
	  if(table_on)
	    {
	      http_response_printf(res, "</table>\n");
	      table_on = 0;
	    }
        }

      /* pre formated  */

      if ( (isspace(*line) || *line == '\0'))
	{
	  int n_spaces = 0;

	  while ( isspace(*line) ) { line++; n_spaces++; }

	  if (*line == '\0')  /* empty line - para */
	    {
	      if (pre_on)
		{
		  http_response_printf(res, "\n") ;
		  continue;
		}
	      else if (open_para)
		{
		  http_response_printf(res, "\n</p><p>\n") ;
		}
	      else
		{
		  http_response_printf(res, "\n<p>\n") ;
		  open_para = 1;
		}
	    }
	  else /* starts with space so Pre formatted, see above for close */
	    {
	      if (!pre_on)
		http_response_printf(res, "<pre>\n") ;
	      pre_on = 1;
	      line = line - ( n_spaces - 1 ); /* rewind so extra spaces
                                                 they matter to pre */
	      http_response_printf(res, "%s\n", line);
	      continue;
	    }
	}
      else if ( *line == '=' )
	{
	  while (*line == '=')
	    { header_level++; line++; }

	  http_response_printf(res, "<h%d>", header_level);
	  p = line;
	}
      else if ( *line == '-' && *(line+1) == '-' )
	{
	  /* rule */
	  http_response_printf(res, "<hr/>\n");
	  while ( *line == '-' ) line++;
	}

    line_content:

      /* 
       * now process rest of the line 
       */

      p = line;

      while ( *line != '\0' )
	{
	  if ( *line == '!' && !isspace(*(line+1))) 
	    {                	/* escape next word - skip it */
	      *line = '\0';
	      http_response_printf(res, "%s", p);
	      p = ++line;

	      while (*line != '\0' && !isspace(*line)) line++;
	      if (*line == '\0')
		continue;
	    }
	  else if ((link = check_for_link(line, &skip_chars)) != NULL)
	    {
	      http_response_printf(res, "%s", p);
	      http_response_printf(res, "%s", link); 

	      line += skip_chars;
	      p = line;

	      continue;

	    }
	  /* TODO: Below is getting bloated and messy, need rewriting more
	   *       compactly ( and efficently ).
	   */
	  else if (*line == '*')
	    {
	      /* Try and be smart about what gets bolded */
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !bold_on)
		{ line++; continue; }

	      if ((isspace(*(line+1)) && !bold_on))
		{ line++; continue; }

		/* bold */
		*line = '\0';
		http_response_printf(res, "%s%s\n", p, bold_on ? "</b>" : "<b>");
		bold_on ^= 1; /* reset flag */
		p = line+1;

	    }
	  else if (*line == '_' )
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !underline_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !underline_on)
		{ line++; continue; }
	      /* underline */
	      *line = '\0';
	      http_response_printf(res, "%s%s\n", p, underline_on ? "</u>" : "<u>"); 
	      underline_on ^= 1; /* reset flag */
	      p = line+1;
	    }
	  else if (*line == '-')
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !strikethrough_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !strikethrough_on)
		{ line++; continue; }
	       
	      /* strikethrough */
	      *line = '\0';
	      http_response_printf(res, "%s%s\n", p, strikethrough_on ? "</del>" : "<del>"); 
	      strikethrough_on ^= 1; /* reset flag */
	      p = line+1; 
	      

	    }
	  else if (*line == '/' )
	    {
	      if (line_start != line 
		  && !is_wiki_format_char_or_space(*(line-1)) 
		  && !underline_on)
		{ line++; continue; }

	      if (isspace(*(line+1)) && !underline_on)
		{ line++; continue; }

	      /* crude path detection */
	      if (line_start != line && isspace(*(line-1)) && !underline_on)
		{ 
		  char *tmp   = line+1;
		  int slashes = 0;

		  /* Hack to escape out file paths */
		  while (*tmp != '\0' && !isspace(*tmp))
		    { 
		      if (*tmp == '/') slashes++;
		      tmp++;
		    }

		  if (slashes > 1 || (slashes == 1 && *(tmp-1) != '/')) 
		    { line = tmp; continue; }
		}

	      if (*(line+1) == '/')
		line++; 	/* escape out common '//' - eg urls */
	      else
		{
		  /* italic */
		  *line = '\0';
		  http_response_printf(res, "%s%s\n", p, underline_on ? "</i>" : "<i>"); 
		  underline_on ^= 1; /* reset flag */
		  p = line+1; 
		}
	    }
	  else if (*line == '|' && table_on) /* table column */
	    {
	      *line = '\0';
	      http_response_printf(res, p);
	      http_response_printf(res, "</td><td>\n");
	      p = line+1;
	    }

	  line++;

	} /* next word */

      if (*p != '\0') 			/* accumalated text left over */
	http_response_printf(res, "%s", p);

      /* close any html tags that could be still open */


      if (listtypes[ULIST].depth)
	http_response_printf(res, "</li>");

      if (listtypes[OLIST].depth)
	http_response_printf(res, "</li>");

      if (table_on)
	http_response_printf(res, "</td></tr>\n");

      if (header_level)
	http_response_printf(res, "</h%d>\n", header_level);  
      else
	http_response_printf(res, "\n");


    } /* next line */

  /* clean up anything thats still open */

  if (pre_on)
    http_response_printf(res, "</pre>\n");
  
  /* close any open lists */
  for (i=0; i<listtypes[ULIST].depth; i++)
    http_response_printf(res, "</ul>\n");

  for (i=0; i<listtypes[OLIST].depth; i++)
    http_response_printf(res, "</ol>\n");
  
  /* close any open paras */
  if (open_para)
    http_response_printf(res, "</p>\n");

    /* tables */
    if (table_on)
     http_response_printf(res, "</table>\n");

}

int 
wiki_redirect(HttpResponse *res, char *location)
{
  char *header = alloca(strlen(location) + 14);

  sprintf(header, "Location: %s\r\n", location);

  http_response_append_header(res, header);
  http_response_printf(res, "<html>\n<p>Redirect to %s</p>\n</html>\n", 
		       location);
  http_response_set_status(res, 302, "Moved Temporarily");
  http_response_send(res);

  exit(0);
}


void
wiki_show_page(HttpResponse *res, char *wikitext, char *page)
{
  char *html_clean_wikitext = NULL;

  http_response_printf_alloc_buffer(res, strlen(wikitext)*2);

  wiki_show_header(res, page, TRUE);

  html_clean_wikitext = util_htmlize(wikitext, strlen(wikitext));

  wiki_print_data_as_html(res, html_clean_wikitext);      

  wiki_show_footer(res);  

  http_response_send(res);

  exit(0);

}

void
wiki_show_edit_page(HttpResponse *res, char *wikitext, char *page)
{
  wiki_show_header(res, page, FALSE);

  if (wikitext == NULL) wikitext = "";
  http_response_printf(res, EDITFORM, page, wikitext);
		       
  wiki_show_footer(res);

  http_response_send(res);
  exit(0);
}

void
wiki_show_create_page(HttpResponse *res)
{
  wiki_show_header(res, "Create New Page", FALSE);
  http_response_printf(res, CREATEFORM);
  wiki_show_footer(res);

  http_response_send(res);
  exit(0);
}

static int 
changes_compar(const struct dirent **d1, const struct dirent **d2)
{
    struct stat st1, st2;

    stat((*d1)->d_name, &st1);

    stat((*d2)->d_name, &st2);

    if (st1.st_mtime > st2.st_mtime)
      return 1;
    else
      return -1;
}



void
wiki_get_pages(WikiPageList **pages, int *n_pages)
{
  struct dirent **namelist;
  int             n, i = 0;
  WikiPageList   *page;
  struct stat     st;

  n = scandir(".", &namelist, 0, (void *)changes_compar);
  
  pages = malloc(sizeof(WikiPageList*)*n);

  while(n--) 
    {
      if ((namelist[n]->d_name)[0] == '.' 
	  || !strcmp(namelist[n]->d_name, "styles.css"))
	goto cleanup;

      stat(namelist[n]->d_name, &st);

      pages[i]       = malloc(sizeof(WikiPageList));
      pages[i]->name  = strdup (namelist[n]->d_name);
      pages[i]->mtime = st.st_mtime;

      i++;

    cleanup:
      free(namelist[n]);
    }

  *n_pages = i;

  free(namelist);
}

void
wiki_show_changes_page(HttpResponse *res)
{
  WikiPageList **pages;
  int            n_pages, i;

  wiki_show_header(res, "Changes", FALSE);

  wiki_get_pages(pages, &n_pages);

  for (i=0; i<n_pages; i++)
    {
      struct tm   *pTm;
      char   datebuf[64];

      pTm = localtime(&pages[i]->mtime);
      strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M", pTm);
      http_response_printf(res, "<a href='%s'>%s</a> %s<br />\n", 
			   pages[i]->name, 
			   pages[i]->name, 
			   datebuf);
    }

  wiki_show_footer(res);
  http_response_send(res);
  exit(0);
}



#if 0
void
wiki_show_changes_page(HttpResponse *res)
{
  struct dirent **namelist;
  int             n;

  wiki_show_header(res, "Changes", FALSE);

  n = scandir(".", &namelist, 0, (void *)changes_compar);

  /*
  if (n < 0) TODO error
  */

  while(n--) 
    {
      struct stat  st;
      struct tm   *pTm;
      char   datebuf[64];

      if ((namelist[n]->d_name)[0] == '.' 
	  || !strcmp(namelist[n]->d_name, "styles.css"))
	goto cleanup;

      stat(namelist[n]->d_name, &st);

      pTm = localtime(&st.st_mtime);
      strftime(datebuf, sizeof(datebuf), "%Y-%m-%d %H:%M", pTm);
      http_response_printf(res, "<a href='%s'>%s</a> %s<br />\n", 
			   namelist[n]->d_name, 
			   namelist[n]->d_name, 
			   datebuf);
    cleanup:
      free(namelist[n]);
    }
  
  free(namelist);

  wiki_show_footer(res);
  http_response_send(res);
  exit(0);

}
#endif

void
wiki_show_search_results_page(HttpResponse *res, char *expr)
{
  struct dirent **namelist;
  int             n, i, finds = 0;

  if (expr == NULL || strlen(expr) == 0)
    {
      wiki_show_header(res, "Search", FALSE);
      http_response_printf(res, "No Search Terms supplied");
      wiki_show_footer(res);
      http_response_send(res);
      exit(0);
    }

  i = n = scandir(".", &namelist, 0, 0);

  while(i--) 
    {
      if ((namelist[i]->d_name)[0] == '.' 
	  || !strcmp(namelist[i]->d_name, "styles.css"))
	continue;

      if (!strcmp(namelist[i]->d_name, expr))
	wiki_redirect(res, namelist[i]->d_name); /* exits */
    }

  i = n;

  wiki_show_header(res, "Search", FALSE);


  while(i--) 
    {
      char *data = NULL;

      if ((namelist[i]->d_name)[0] == '.' 
	  || !strcmp(namelist[i]->d_name, "styles.css"))
	goto cleanup;

      /* Super simple search functionality TODO Improve */

      if ((data = file_read(namelist[i]->d_name)) != NULL)
	{
	  if (strstr(data, expr))
	    {
	      http_response_printf(res, "<a href='%s'>%s</a><br />\n", 
				   namelist[i]->d_name, 
				   namelist[i]->d_name);
	      finds++;
	    }
	}
      
      if (data) free(data);

    cleanup:
      free(namelist[i]);
    }

  if (!finds)
    http_response_printf(res, "No matches");

  free(namelist);

  wiki_show_footer(res);
  http_response_send(res);

  exit(0);
}

void
wiki_show_header(HttpResponse *res, char *page_title, int want_edit)
{
  http_response_printf(res, 
                       "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"
                       "<html xmlns='http://www.w3.org/1999/xhtml'>\n"
		       "<head>\n"
                       "<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />\n" 
		       "<link rel='SHORTCUT ICON' href='/favicon.ico' />\n"
		       "<link media='all' href='/styles.css' rel='stylesheet' type='text/css' />\n"
		       "<title>%s</title>\n"

		       "</head>\n"
		       "<body>\n", page_title
		       );

  http_response_printf(res, PAGEHEADER, page_title, 
		       (want_edit) ? " ( <a href='?edit' title='Edit this wiki page contents. [alt-j]' accesskey='j'>Edit</a> ) " : "" );     
}

void
wiki_show_footer(HttpResponse *res)
{
  http_response_printf(res, PAGEFOOTER);

  http_response_printf(res, 
		       "</body>\n"
		       "</html>\n"
		       );
}

void
wiki_handle_rest_call(HttpRequest  *req, 
		      HttpResponse *res,
		      char         *func)
{

  if (func != NULL && *func != '\0')
    {
      if (!strcmp(func, "page/get"))
	{
	  char *page = http_request_get_query_string(req);

	  if (page && (access(page, R_OK) == 0)) 
	    {
	      http_response_printf(res, "%s", file_read(page));
	      http_response_send(res);
	      return;
	    }  
	}
      else if (!strcmp(func, "page/set"))
	{
	  
	  return;  
	}
      else if (!strcmp(func, "page/delete"))
	{
	  
	  return;  
	}
      else if (!strcmp(func, "page/exists"))
	{
	  
	  return;  
	}
      else if (!strcmp(func, "pages"))
	{
	  
	  return;  
	}
      else if (!strcmp(func, "search"))
	{
	  
	  return;  
	}
    }

  http_response_set_status(res, 404, "Not Found");
  http_response_printf(res, "<html><body>Not Found</body></html>\n");
  http_response_send(res);

  return;  
}



void
wiki_handle_http_request(HttpRequest *req)
{
  HttpResponse *res      = http_response_new(req);
  char         *page     = http_request_get_path_info(req); 
  char         *command  = http_request_get_query_string(req); 
  char         *wikitext = "";

  util_dehttpize(page); 	/* remove any encoding on the requested
				   page name.                           */

  if (!strcmp(page, "/"))
    {
      if (access("WikiHome", R_OK) != 0)
	wiki_redirect(res, "/WikiHome?create");
      page = "/WikiHome";
    }

  if (!strcmp(page, "/styles.css"))
    {
      /*  Return CSS page */
      http_response_set_content_type(res, "text/css");
      http_response_printf(res, CssData);
      http_response_send(res);
      exit(0);
    }

  if (!strcmp(page, "/favicon.ico"))
    {
      /*  Return favicon */
      http_response_set_content_type(res, "image/ico");
      http_response_set_data(res, FaviconData, FaviconDataLen);
      http_response_send(res);
      exit(0);
    }


  page = page + 1; 		/* skip slash */

  if (!strncmp(page, "api/", 4))
    {
      char *p;

      page += 4; 
      for (p=page; *p != '\0'; p++)
	if (*p=='?') { *p ='\0'; break; }
      
      wiki_handle_rest_call(req, res, page); 
      exit(0);
    }

  if (!strcmp(page, "Changes"))
    {
      /* TODO list recent changes */
      wiki_show_changes_page(res);
    }
  else if (!strcmp(page, "Search"))
    {
      wiki_show_search_results_page(res, http_request_param_get(req, "expr"));
    }
  else if (!strcmp(page, "Create"))
    {
      char *new_page;
      if ( (wikitext = http_request_param_get(req, "title")) != NULL)
	{
	  /* create page and redirect */
	  wiki_redirect(res, http_request_param_get(req, "title"));
	}
      else
	{
	   /* show create page form  */
	  wiki_show_create_page(res);
	}
    }
  else
    {
      /* TODO: dont blindly write wikitext data to disk */
      if ( (wikitext = http_request_param_get(req, "wikitext")) != NULL)
	{
	  file_write(page, wikitext);	      
	}

      if (access(page, R_OK) == 0) 	/* page exists */
	{
	  wikitext = file_read(page);
	  
	  if (!strcmp(command, "edit"))
	    {
	      /* print edit page */
	      wiki_show_edit_page(res, wikitext, page);
	    }
	  else
	    {
	      wiki_show_page(res, wikitext, page);
	    }
	}
      else
	{
	  if (!strcmp(command, "create"))
	    {
	      wiki_show_edit_page(res, NULL, page);
	    }
	  else
	    {
	      char buf[1024];
	      snprintf(buf, 1024, "%s?create", page);
	      wiki_redirect(res, buf);
	    }
	}
    }

}

int
wiki_init(void)
{
  char datadir[512] = { 0 };
  struct stat st;

  if (getenv("DIDIWIKIHOME"))
    {
      snprintf(datadir, 512, getenv("DIDIWIKIHOME"));
    }
  else
    {
      if (getenv("HOME") == NULL)
	{
	  fprintf(stderr, "Unable to get home directory, is HOME set?\n");
	  exit(1);
	}

      snprintf(datadir, 512, "%s/.didiwiki", getenv("HOME"));
    }  
     
  /* Check if ~/.didiwiki exists and create if not */
  if (stat(datadir, &st) != 0 )
    {
      if (mkdir(datadir, 0755) == -1)
	{
	  fprintf(stderr, "Unable to create '%s', giving up.\n");
	  exit(1);
	}
    }

  chdir(datadir);

  /* Write Default Help + Home page if it doesn't exist */

  if (access("WikiHelp", R_OK) != 0) 
    file_write("WikiHelp", HELPTEXT);

  if (access("WikiHome", R_OK) != 0) 
    file_write("WikiHome", HOMETEXT);

  /* Read in optional CSS data */

  if (access("styles.css", R_OK) == 0) 
    CssData = file_read("styles.css");
  

}

#ifndef _HAVE_WIKI_HEADER
#define _HAVE_WIKI_HEADER

typedef struct WikiPageList WikiPageList;

struct WikiPageList 
{
  char   *name;
  time_t  mtime;
};

void
wiki_handle_http_request(HttpRequest *req);

void
wiki_show_header(HttpResponse *res, char *page_title, int want_edit);

void
wiki_show_footer(HttpResponse *res);

void
wiki_print_data_as_html(HttpResponse *res, char *raw_page_data);

int
wiki_init(void);


#endif
/* 
 *  DidiWiki - a small lightweight wiki engine. 
 *
 *  Copyright 2004 Matthew Allum <mallum@o-hand.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 */

#ifndef _HAVE_WIKI_TEXT_HEADER
#define _HAVE_WIKI_TEXT_HEADER


#define PAGEHEADER                                                       \
                                                                         \
"<div id='header'>\n"                                                    \
"<form method='get' action='Search'>\n"                                 \
"<table border='0' width='100%%'>\n"                                      \
"<tr>\n"                                                                 \
"<td align='left' ><strong>%s</strong> %s</td>\n"         \
"<td align='right' >\n"                                    \
"<a href='WikiHome' title='Visit Wiki home page. [alt-z]' accesskey='z'>Home</a> |\n"  \
"<a href='Changes' title='List recent changes in the wiki. [alt-r]' accesskey='r' >Changes</a> | \n"                                    \
"<a href='Create' title='Create a new wiki page by title. [alt-c]' accesskey='c'>New</a> | \n"                                         \
"<a href='WikiHelp' title='Get help on wiki usage and formatting.'>Help</a> |\n"                                       \
"<input type='text' name='expr' size='15' title='Enter text to search for and press return.' /> </td>\n"                               \
"</tr>\n"                                                                \
"</table>\n"                                                             \
"</form>\n"                                                              \
"</div>\n"                                                               \
"<div id='wikidata'>\n"


#define PAGEFOOTER                                                       \
                                                                         \
"</div><div id='footer'>DidiWiki, Version: " VERSION "</div>\n"


#define CREATEFORM                                                       \
                                                                        \
"<form method=POST action='Create'>\n"                                  \
"<input type='text' name='title' />\n"                                   \
"<p><input type='submit' value='Create' /></p>\n"                        \
"</form>\n" 


#define EDITFORM                                                         \
                                                                         \
"<form method=POST action='%s' name='editform'>\n"                        \
"<textarea name='wikitext' rows='20' cols='80' wrap='virtual'>%s</textarea>\n" \
"<p><input type='submit' value='Save' title='[alt-k]' accesskey='k'></p>\n"                            \
"</form>\n"             \
"<script language='JavaScript'>\n"   \
"<!--\n"                             \
"/* Give the textarea focus ...less mouse usage but may annoy people ?*/ \n" \
"document.editform.wikitext.focus()\n" \
"document.editform.wikitext.scrollTop = document.editform.wikitext.scrollHeight;\n"          \
"//-->\n"                            \
"</script>\n"


#define HOMETEXT ""                                                      \
                                                                         \
"==Welcome to !DidiWiki\n"                                               \
"/!DidiWiki / is a small and simple [http://en.wikipedia.org/wiki/Wiki WikiWikiWeb]\n" \
"Implementaion. Its intended for personal note taking, Todo lists and any other uses you can think of.\n"                               \
"\n"                                                                        \
"To learn more about what a [http://www.c2.com/cgi/wiki?WikiWikiWeb WikiWikiWeb] is, read about [http://www.c2.com/cgi/wiki?WhyWikiWorks WhyWikiWorks] and the [http://www.c2.com/cgi/wiki?WikiNature WikiNature]. Also, consult the [http://www.c2.com/cgi/wiki?WikiWikiWebFaq WikiWikiWebFaq].\n"                      \
"\n"                                                                        \
"For an example how a !WikiWiki entry looks in text form you can [?edit edit] this page. Also see WikiHelp for infomation on usage and formatting rules. Use The WikiSandbox to experiment.\n" \
"\n"                                                                        \
"/!DidiWiki / is written by [mailto://mallum@o-hand.com Matthew Allum] in C and is free software, released under the [http://www.gnu.org GNU] [http://www.gnu.org/copyleft/gpl.html GPL]. It uses a formatting style similar to that of [http://www.kwiki.org kwiki] and some webserver code from [http://www.cvstrac.org cvstrac]\n"


#define HELPTEXT ""                                                      \
                                                                         \
"=Quick Guide \n"                                                    \
"Top toolbar usage\n"                                                    \
"* [?edit Edit] Allows you to edit pages ( see below for rules )\n"       \
"* [WikiHome Home] Takes you to the wiki front page\n"                \
"* [Changes Changes] Lists the pages changed by date\n"                  \
"* [Create New] Creates a new wiki page by title\n"                      \
"* [WikiHelp Help] Takes you to this help page\n"                            \
"\n"                                                                     \
"Use the text entry box to perform a very simple keyword search on the\n" \
"Wiki contents. Hit return to activate the search.\n"                    \
"\n"                                                                     \
"Use the WikiSandbox to practice formatting.\n"                         \
"\n"                                                                     \
"----\n"                                                                 \
"=Formatting rules\n"                                                    \
"\n"                                                                     \
"=Top Level Heading\n"                                                   \
"  =Top Level Heading (H1)\n"                                            \
"==Second Level Heading\n"                                               \
"  ==Second Level Heading (H2)\n"                                        \
"More levels \n"                                                         \
"  === (H3), ==== (H4) etc\n"                                            \
"----\n"                                                                 \
"The horizontal lines in this page are made with 4 or more dashes:\n"     \
" ---- Horizonal line\n"                                                 \
"----\n"                                                                 \
"\n"                                                                     \
"Paragraphs are seperated by an empty line\n"                            \
"\n"                                                                     \
"Like this. Another paragraph.\n"                                        \
"\n"                                                                     \
" Paragraphs are separated by a blank line.\n"                           \
"\n"                                                                     \
" Like this. Another paragraph.\n"                                       \
"----\n"                                                                 \
"*Bold text*, /italic text/, _underscore text_ and -strikethrough-.\n"    \
"  *Bold text*, /italic text/, _underscore text_ and -strikethrough-.\n"  \
"/*Combination of bold and italics*/\n"                                  \
"  /*Combination of bold and italics*/\n"                                \
"----\n"                                                                 \
"\n"                                                                     \
"WikiLinks are formed by two or more words in /camelcase/.\n"           \
" WikiLinks are formed by two or more words in /camelcase/.\n"          \
"External links begin with http://, like http://www.freepan.org\n"       \
" External links begin with http://, like http://www.freepan.org\n"      \
"Forced wiki [links] are a alphanumeric string surrounded by square brackets.\n" \
" Forced wiki [links] are a alphanumeric string surrounded by square brackets.\n" \
"Named http links have text with an !http:// link inside, like [http://www.freepan.org FreePAN]\n" \
" Named http links have text with an !http:// link inside, like [http://www.freepan.org FreePAN]\n" \
"Sometimes !WordsShouldNotMakeAWikiLink so put a '! ' beforehand.\n"      \
" Sometimes !WordsShouldNotMakeAWikiLink so put a '! ' beforehand.\n"     \
"Same thing with !http://foobar.com\n"                                   \
" Same thing with !http://foobar.com\n"                                  \
"----\n"                                                                 \
"\n"                                                                     \
"Links to images display the image:\n"                                   \
"\n"                                                                     \
"http://www.google.com/images/logo.gif\n"                                \
" http://www.google.com/images/logo.gif\n"                               \
"----\n"                                                                 \
"Unordered lists begin with a '* '. The number of asterisks determines the level:\n" \
"* foo\n"                                                                \
"* bar\n"                                                                \
"** boom\n"                                                              \
"** bam\n"                                                               \
"* baz\n"                                                                \
"\n"                                                                     \
"  * foo\n"                                                              \
"  * bar\n"                                                              \
"  ** boom\n"                                                            \
"  ** bam\n"                                                             \
"  * baz\n"                                                              \
"\n"                                                                     \
"Ordered lists work the same, but use a '#'\n"                           \
"----\n"                                                                 \
"Tables begin the line with a '|' and before every new column after.\n"  \
"\n"                                                                     \
"| row:1,col:1 | row:1,col:2\n"                                          \
"| row:2,col:1 | row:2,col:2\n"                                          \
" | row:1,col:1 | row:1,col:2\n"                                         \
" | row:2,col:1 | row:2,col:2\n"                                         \
"---\n"                                                                  \
"<Inline> <html> <is> <escaped>.\n"                                      \
"\n"                                                                     \
"----\n"                                                                 \
"Lines prefixed with a space are unformatted ( Like examples on this page )\n" \
"\n"                                                                     \
"----\n"                                                                 \


#define STYLESHEET                                                       \
                                                                         \
"body {\n" \
"  font-family: Verdana, Arial, Helvetica, sans-serif;\n" \
"  font-size: 90%;  \n" \
"  color: #333333;\n" \
"  margin: 2px;\n" \
"  padding: 2px;\n" \
"}\n" \
"\n" \
"#header {\n" \
"    font-size: 90%;\n" \
"    background-color: #eef;\n" \
"    border: 1px solid #aaa;\n" \
"    font-family: Verdana, Arial, Helvetica, sans-serif;\n" \
"    padding: 5px;\n" \
"    margin-bottom:20px;\n" \
"}\n" \
"#header input {\n" \
"    margin:0px;\n" \
"    padding: 0;\n" \
"    background-color: White;\n" \
"    border: 1px solid #aaaaaa;\n" \
"    font-size: 90%;\n" \
"}\n" \
"\n" \
"#footer {\n" \
"    font-size: 10px;\n" \
"    color: #ddd;\n" \
"    margin-top:40px;\n" \
"    width: 100%;\n" \
"    text-align: center;\n" \
"}\n" \
"table.wikitable { background-color: #fff; border: 1px solid #aaa; }\n" \
"table.wikitable td { background-color: #fff; border: 1px solid #aaa; }\n" \
"\n" \
"pre {\n" \
"    font-family: monospace;\n" \
"    background-color: #eee;\n" \
"    padding: 2px;\n" \
"    padding-left: 10px;\n" \
"    margin-left: 20px;\n" \
"    margin-right: 20px;\n" \
"}\n" \
"\n" \
"hr {\n" \
" height: 1px;\n" \
" color: #aaaaaa;\n" \
" background-color: #aaaaaa;\n" \
" border: 0;\n" \
" margin: 0.2em 5px 0.2em 5px;\n" \
"}\n" \
"\n" \
"form {\n" \
"    border: none;\n" \
"    margin: 0;\n" \
"}\n" \
"\n" \
"textarea {\n" \
"    border: 1px solid #aaaaaa;\n" \
"    color: Black;\n" \
"    background-color: white;\n" \
"    width: 100%;\n" \
"    padding: 0.1em;\n" \
"    overflow: auto;\n" \
"}\n" \
"\n" \
"input {\n" \
"    margin-top:1px;\n" \
"    padding: 0 0.4em !important;\n" \
"    background-color: White;\n" \
"    border: 1px solid #aaaaaa;\n" \
"}\n" \
"\n" \
"a, ulink{\n" \
"   color: #333;\n" \
"   text-decoration:none;\n" \
"   border-bottom: 1px #333 dotted;\n" \
"   display: inline;\n" \
" }\n" \
"\n" \
"a:hover {\n" \
"   color: #333;\n" \
"   text-decoration: None;\n" \
"   border-bottom: 1px #000 solid;\n" \
"   }\n" \
"\n" \
"a:visited {\n" \
"   color: #333;\n" \
"}\n" \
"\n" \

/* use xdd -i favicon.ico to generate */

unsigned char FaviconData[] = {
  0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
  0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xee, 0x00, 0xff, 0xff,
  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xba, 0xab, 0x00, 0x00, 0x92, 0xab,
  0x00, 0x00, 0xaa, 0x9b, 0x00, 0x00, 0xbb, 0xaf, 0x00, 0x00, 0xba, 0xab,
  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x8d, 0x1b,
  0x00, 0x00, 0xb5, 0x6b, 0x00, 0x00, 0xb5, 0x6b, 0x00, 0x00, 0xb7, 0x6f,
  0x00, 0x00, 0x8d, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

unsigned int FaviconDataLen = 198;


#endif
P6
# CREATOR: The GIMP's PNM Filter Version 1.0
16 16
255
\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\xÿÿÿÿÿÿÿÿÿ\x\xÿÿÿ\xÿÿÿÿÿÿÿÿÿ\x\xÿÿÿ\x\x\xÿÿÿ\x\xÿÿÿ\x\x\xÿÿÿ\x\xÿÿÿ\x\x\x\x\xÿÿÿ\x\xÿÿÿ\xÿÿÿ\xÿÿÿ\x\xÿÿÿ\xÿÿÿ\x\x\xÿÿÿ\x\xÿÿÿ\xÿÿÿ\xÿÿÿ\x\xÿÿÿ\xÿÿÿ\x\x\xÿÿÿÿÿÿÿÿÿ\x\xÿÿÿ\xÿÿÿÿÿÿÿÿÿ\x\xÿÿÿ\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\xÿÿÿ\x\x\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\x\x\xÿÿÿ\x\x\xÿÿÿ\x\x\xÿÿÿ\xÿÿÿ\x\x\x\x\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿÿÿÿ\x\xÿÿÿ\x\x\xÿÿÿÿÿÿ\xÿÿÿÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\x\x\xÿÿÿ\x\x\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\xÿÿÿ\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x\x
unsigned char favicon_ico[] = {
  0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x28, 0x00,
  0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xee, 0x00, 0xff, 0xff,
  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xba, 0xab, 0x00, 0x00, 0x92, 0xab,
  0x00, 0x00, 0xaa, 0x9b, 0x00, 0x00, 0xbb, 0xaf, 0x00, 0x00, 0xba, 0xab,
  0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x8d, 0x1b,
  0x00, 0x00, 0xb5, 0x6b, 0x00, 0x00, 0xb5, 0x6b, 0x00, 0x00, 0xb7, 0x6f,
  0x00, 0x00, 0x8d, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int favicon_ico_len = 198;
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta name="VERSION" content="$Id$" /> 
  <!-- xhtml page copyright 2004 Matthew Allum -->
  <!-- Time-stamp: <> -->
  <meta http-equiv="keywords" content=""/><meta http-equiv="description" content="" />
  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
  <style type="text/css"></style>
  <link href="styles.css" rel="stylesheet" type="text/css" />
  <title>DidiWiki Home Page</title>
</head>
<body>
<div id="container">
<div id="header">
<img src="didiwiki.png" border="0"/>
</div>
<div id="content">
<h2>About.</h2>
<p>

DidiWiki is a small and simple <i>personal</i> <a href="http://en.wikipedia.org/wiki/Wiki">WikiWikiWeb</a> implementation written in
C. Its intended for usage as an ad-hoc PIM, useful for note taking, TODO lists etc.

</p> 
<p>
Features include;
<ul>
<li>Built in webserver, no database, scripting language or wierd library requirements. Just compile, run and point your browser at it.</li>
<li>Tiny binary size, weighing in at approx 25k stripped. Thus practical for embedded use ( eg Handhelds ). </li>
<li><a href="http://www.kwiki.org">Kwiki</a> like syntax. Support for tables, lists, images etc. </li>
<li>Web standards compliant generated markup.</li>
</ul>

</P>
<p>
DidiWiki is developed on Linux but reported to work on *BSD's ( including OS X ) and Windows with cygwin. The primary author is <a href="mailto://mallum@o-hand.com">Matthew Allum</a>.
</p>
<h2>Announcements.</h2>
<p class="announce">

<h4>Wednesday, September 30, 2004</h4>
<h3>DidiWiki 0.5 Released</h3>
New stuff includes;
<ul>
<li>More little bug fixes and tweaks with slightly increased secruity.</li>
<li>Simple http api for manipulating didiwiki data externally. See <a href="/sources/README">README<a/>  for details.</li>
</ul>
<a href="/sources/ChangeLog">ChangeLog<a/> and <a href="/sources/didiwiki-0.5.tar.gz">Source Tarball</a>.
</p>


<h4>Monday, September 13, 2004</h4>
<h3>DidiWiki 0.4 Released</h3>
New stuff includes;
<ul>
<li>Many little bug fixes and tweaks.</li>
<li>Ordered List support.</li>
<li>Now includes Favicon.</li>
</ul>
<a href="/sources/README">README<a/>, <a href="/sources/ChangeLog">ChangeLog<a/> and <a href="/sources/didiwiki-0.5.tar.gz">Source Tarball</a>.
</p>
<h2>Screenshots.</h2>
<p>
Heres a <a href="screenshot.png">screenshot</a>.
</p>

<h2>Download.</h2>
<p>
Source tarballs of releases are located <a href="/sources/">here</a>.
</p>
<p>
For bleeding edge developer code, use subversion;
<pre>
  svn co http://svn.o-hand.com/repos/didiwiki/trunk didiwiki
</pre> 
You can also <a href="http://svn.o-hand.com/view/didiwiki">browse the repository</a>.
</p>

<h2>Mailing List.</h2>
<p>
To subscribe, send mail to <a href="mailto://didiwiki+subscribe@didiwiki.org">didiwiki+subscribe@didiwiki.org</a> . Archives should appear soon.
</p>

<h2>Links.</h2>
<p>
<a href="http://o-hand.com">OpenedHand</a>, my company.
</p>
<p>
<a href="http://www.gnomefiles.org/app.php?soft_id=404">WebNotes</a> is a Gnome frontend to didwiki.
</p>
<p>
My <a href="http://www.amazon.co.uk/exec/obidos/registry/2QSQ06H8VSM0V/">Amazon Wishlist</a>, donations are always nice.
</p>
</div>

</div>
</body>
</html>
DidiWiki is a small personal WikiWikiWeb implementation. 

It includes its own built in webserver and is dependant on a c library.


Its developed on Linux but reportadly works fine on BSD's ( including OS X )
and windows ( with cygwin ).

body {
  font-family: Verdana, Arial, Helvetica, sans-serif;
  font-size: 80%;
  color: #333333;
  margin: 2px;
  padding: 2px;
}

h2 {
 color: #33bb66;
}

h4 {
 color: #666;
}

#container {
  width: 60%;
  min-width: 500px;
  border: 2px solid #6699cc;
  margin-left: auto;
  margin-right: auto;
  margin-top: 20px;
  margin-bottom: 20px;
  padding: 20px
}

#header {
  padding-left:15px;
}

#content {
  padding-left: 20px;
  padding-right: 20px;
}

#footer {

}

a:Link {color: #6699cc; text-decoration: none; } 
a:Active {color: #6699cc; text-decoration: none; }
a:Visited {color: #6699cc; text-decoration: none;}
a:Hover {color: #ec1c26; text-decoration: dotted;}