(* copyright (c) 2013, simon cruanes all rights reserved. redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *) (** {1 Expose the State of a Program to the Web} We export some values (and associated functions for converting them to html, and update them) as a FastCGI interface. This module depends on CamlGI. *) (** {2 Some combinators to build HTML documents} *) module HTML = struct type t = | Str of string (* content *) | List of t list | Url of url | Img of image | Concat of t list | H of int * t | Link of link | Tag of string * t and url = { url_alt : string option; url_url : string; url_descr : string; } and image = { img_alt : string option; img_url : string; } and link = { link_rel : string; link_url : string; } let str s = Str s let bprintf format = let buffer = Buffer.create 64 in Printf.kbprintf (fun fmt -> str (Buffer.contents buffer)) buffer format let bprintf format = let buffer = Buffer.create 64 in Printf.ksprintf str format let list l = List l let url ?alt ~url ~descr = Url { url_alt = alt; url_url = url; url_descr = descr; } let img ?alt url = Img { img_alt = alt; img_url = url; } let append a b = Concat [a; b] let concat l = Concat l let h1 x = H (1, x) let h2 x = H (2, x) let h3 x = H (3, x) let h n x = H (n, x) let link ~rel ~url = Link { link_rel = rel; link_url = url; } let head x = Tag ("head", x) let body x = Tag ("body", x) let html x = Tag ("html", x) (* XXX UGLY: I copied code from CamlGI because it doesn't provide a way to encode strings with HTML conventions. Bad bad bad. *) module Encode = struct (* Use a table lookup for speed. *) let char_of_hex = let hex = [| '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; 'A'; 'B'; 'C'; 'D'; 'E'; 'F' |] in fun i -> Array.unsafe_get hex i let encode_wrt is_special s0 = let len = String.length s0 in let encoded_length = ref len in for i = 0 to len - 1 do if is_special(String.unsafe_get s0 i) then encoded_length := !encoded_length + 2 done; let s = String.create !encoded_length in let rec do_enc i0 i = (* copy the encoded string in s *) if i0 < len then begin let s0i0 = String.unsafe_get s0 i0 in if is_special s0i0 then begin let c = Char.code s0i0 in let i1 = succ i in let i2 = succ i1 in String.unsafe_set s i '%'; String.unsafe_set s i1 (char_of_hex (c lsr 4)); String.unsafe_set s i2 (char_of_hex (c land 0x0F)); do_enc (succ i0) (succ i2) end else if s0i0 = ' ' then begin String.unsafe_set s i '+'; do_enc (succ i0) (succ i) end else begin String.unsafe_set s i s0i0; do_enc (succ i0) (succ i) end end in do_enc 0 0; s (* Unreserved characters consist of all alphanumeric chars and the following limited set of punctuation marks and symbols: '-' | '_' | '.' | '!' | '~' | '*' | '\'' | '(' | ')'. According to RFC 2396, they should not be escaped unless the context requires it. *) let special_rfc2396 = function | ';' | '/' | '?' | ':' | '@' | '&' | '=' | '+' | '$' | ',' (* Reserved *) | '\000' .. '\031' | '\127' .. '\255' (* Control chars and non-ASCII *) | '<' | '>' | '#' | '%' | '"' (* delimiters *) | '{' | '}' | '|' | '\\' | '^' | '[' | ']' | '`' (* unwise *) -> true | _ -> false (* ' ' must also be encoded but its encoding '+' takes a single char. *) let encode = encode_wrt special_rfc2396 end (* real rendering is always into a buffer (for now) *) let rec to_buf buf x = match x with | Str s -> Buffer.add_string buf (Encode.encode s) | List l -> Buffer.add_string buf "