let easycgi_version = "1.23i";; let pr = print_string;; let pre = print_endline;; pr "Content-type: text/html\n\n";; flush stdout;; Unix.dup2 Unix.stdout Unix.stderr;; let prs = print_string;; (* This portion not used --- under testing (* header handling code *) let header = ref "Content-type: text/html\n\n";; (* Default header data *) let header_sent = ref false;; (* flag: true if header is sent *) let (pin, pout) = Unix.pipe ();; (* pipe to redirect stdout till header is sent *) Unix.set_nonblock pin;; (* non blocking for polling with sigalarm timer *) let sout = Unix.dup Unix.stdout;; (* Save stdout *) (* let serr = Unix.dup Unix.stderr;; (* Save stderr *) *) let prs s = ignore(Unix.write sout s 0 (String.length s));; (* helper func *) Unix.dup2 pout Unix.stdout;; (* redirect stdout to the pipe *) (*Unix.dup2 pout Unix.stderr;; (* redirect stderr to the pipe *)*) let start_timer () = Unix.setitimer Unix.ITIMER_REAL {Unix.it_interval = 0.0001; Unix.it_value = 0.0001} ;; (* Timer function *) let stop_timer () = ignore(Unix.setitimer Unix.ITIMER_REAL {Unix.it_interval = 0.0; Unix.it_value = 0.0} );; (* reset timer *) let __count = ref 10 ;; (* Sigalarm handling func *) let check_pin signal = flush_all (); if !__count > 0 && not !header_sent then ( (*prs ("count=" ^ (string_of_int !__count) ^ " \n");*) decr __count; let s = String.create 256 in let nc = ref 0 in (try nc := Unix.read pin s 0 256; (* Try to read from pipe *) (*prs ("nc=" ^ (string_of_int !nc) ^ " \n");*) stop_timer (); flush_all (); Unix.dup2 Unix.stdout Unix.stderr; (* Redirect stderr to stdout *) Unix.dup2 sout Unix.stdout; (* Restore stdout *) prs !header; (* Output header *) flush_all (); header_sent := true; if !nc > 0 then prs (substr s 0 !nc); (* send initial data to stdout *) (try while true do nc := Unix.read pin s 0 256; if !nc > 0 then prs (String.sub s 0 !nc); (* send the rest of data if any to stdout *) done; with _ -> ()); flush_all (); with _ -> ignore (start_timer ()) ) (* restart timer if no data yet *) ) else ( (*prs "??? Why ???";*) stop_timer (); );; Sys.set_signal Sys.sigalrm (Sys.Signal_handle check_pin);; (* set signal func *) at_exit (fun () -> stop_timer (); prs !header; (* Output header *) flush_all (); Unix.dup2 Unix.stdout Unix.stderr; (* Redirect stderr to stdout *) Unix.dup2 sout Unix.stdout; (* Restore stdout *) let s = String.create 256 in let nc = ref 0 in header_sent := true; (try while true do nc := Unix.read pin s 0 256; if !nc > 0 then prs (String.sub s 0 !nc); (* send the rest of data if any to stdout *) done; with _ -> ()); flush_all (); );; (* make sure to send header+data before exit *) start_timer ();; (* Everything ready. Start timer *) (* Now, before any out put will be kept in pipe and http response header will be prefixed without fail. This mimics stupid behavior of PHP and facilitate initail adaptation of PHP user. *) *** End of testing code *** *) let max_post = 2048000;; (* maximum post data size *) (* Alias for string.length *) let strlen = String.length;; (* String.sub alias + accepting negative index *) let substr0 s i len = let slen = strlen s in if slen > 0 then ( let l = if slen < len then slen else len in if i >= 0 then String.sub s i l else String.sub s (slen + i) l ) else "" ;; let substr s i len = let errpr es = let ss = if (String.length s) > 20 then String.sub s 0 20 else s in prerr_string (" substr " ^ ss ^ ", " ^ (string_of_int i) ^ ", " ^ (string_of_int len) ^ ", " ^ es ^ "
\n");flush_all () in let slen = strlen s in if len < 0 then errpr "len < 0"; if slen > 0 then ( let l = if slen < len then (errpr "slen < len";slen) else len in if i >= 0 then ((if (i + l) > slen then errpr "i + l > slen");String.sub s i l ) else ((if (slen + i + l) > slen then errpr "slen + i + l > slen"); String.sub s (slen + i) l) ) else "" ;; let slice s n1 n2 = let len = strlen s in let nn1 = if n1 < 0 then (len + n1) else n1 in let nn2 = if n2 < 0 then len + n2 else if n2 = 0 then len else n2 in substr s nn1 (nn2 - nn1 ) ;; let env s = try Sys.getenv s with Not_found -> "";; let prenv x = pre (env x) ;; let pw = (Unix.getpwuid (Unix.getuid ()));; let user = pw.Unix.pw_name;; (* User name *) let home = pw.Unix.pw_dir;; (* home dir *) let cwd = Unix.getcwd ();; (* stat type checking *) let is_dir pn = (Unix.lstat pn).Unix.st_kind = Unix.S_DIR;; let is_file pn = (Unix.lstat pn).Unix.st_kind = Unix.S_REG;; let is_link pn = (Unix.lstat pn).Unix.st_kind = Unix.S_LNK;; let filesize pn = (Unix.lstat pn).Unix.st_size;; let filemtime pn = (Unix.lstat pn).Unix.st_mtime;; let fileatime pn = (Unix.lstat pn).Unix.st_atime;; let filectime pn = (Unix.lstat pn).Unix.st_ctime;; (* find string -> substring -> position (or -1) *) let find s ss = let i = ref 0 in let si = ref 0 in let len = strlen ss in if len = 0 then 0 else (let c = ss.[0] in try while (String.sub s !si len) <> ss do (* while (substr s !si len) <> ss do *) si := String.index_from s !i c; i := !si + 1 done; !si with _ -> -1 );; (* rfind string -> substring -> position (or -1) *) let rfind s ss = let len = strlen ss in if len = 0 then 0 else (let si = ref ((strlen s) - len) in let c = ss.[0] in try while (String.sub s !si len) <> ss do (* while (substr s !si len) <> ss do *) si := String.rindex_from s (!si - 1) c; done; !si with _ -> -1 );; (* find all occurance of substring 'ss' in the string 's' and return the list of matched positions. *) let find_all s ss = let slen = strlen s in let sslen = strlen ss in let si = ref 0 in let r = ref [] in for i = 0 to (slen - sslen ) do while !si < sslen && s.[i + !si] = ss.[!si] do incr si done; if !si >= sslen then r := i :: !r; si := 0 done; List.rev !r ;; (* replace all occerance of substring 'ss' in the string 's' with the string 'sub' and return the result *) let replace s ss sub = let slen = strlen s in let sslen = strlen ss in let sublen = strlen sub in let si = ref 0 and rsi = ref 0 in let r = find_all s ss in let rs = String.create (slen + ((sublen - sslen) * (List.length r))) in List.iter (fun i -> if i <> !si then (String.blit s !si rs !rsi (i - !si);rsi := !rsi + (i - !si)); if sublen <> 0 then (String.blit sub 0 rs !rsi sublen;rsi := !rsi + sublen); si := i + sslen ) r; if !si < slen then String.blit s !si rs !rsi (slen - !si); rs ;; (* Returns nth line in the string. Accepts negative index. *) let nth_line s n = let len = strlen s in if n < 0 then ( let r = Array.of_list (find_all s "\n") in let rlen = Array.length r in let nn = rlen + (if r.(rlen -1) = (len -1) then n else n +1) in (match nn with 0 -> substr s 0 r.(0) | _ when nn = rlen -> substr s (r.(nn -1) + 1) (len - r.(nn -1) - 1) | _ -> substr s (r.(nn -1) + 1) (r.(nn) - r.(nn -1) -1) ) ) else ( let ni = ref 0 and si = ref 0 and ei = ref ( -1) in try while !ni <= n do si := !ei + 1; ei := String.index_from s !si '\n'; incr ni done; substr s !si (!ei - !si) with Not_found -> if !ni = n then substr s !si (len - !si) else raise Not_found ) ;; let extension s = try let exti = (String.rindex s '.') + 1 in (); let extii = (try (String.rindex s '/') + 2 with _ -> -1 ) in if exti > extii then substr s exti ((strlen s) - exti ) else "" with _ -> "";; (* remove '//' '/./' '/../' making necessary adjustments *) let normpath p = (* pr "normpath p=";pre p; *) let len = strlen p in if len < 2 then p else begin let abs = if p.[0] = '/' then true else false in let on = ref false in let r = ref "" in let ti = ref 0 and dot = ref 0 and back = ref 0 in let pl = ref [] in for i = 0 to (len -1) do if !on then ( (* valid pathname segment *) if p.[i] = '/' then ( on := false; pl := (!ti,i)::!pl ) ) else ( (* '/' or "." or ".." *) match p.[i] with '/' -> if !dot = 2 then ( if abs=false && (List.length !pl) = 0 then incr back else if (List.length !pl) > 0 then pl := List.tl !pl ); dot := 0 | '.' -> if !dot = 0 then (ti := i;incr dot) else if !dot = 2 then (on := true ; dot := 0) else incr dot | _ -> on := true; if !dot = 0 then ti := i ) done ; if !on then pl := (!ti,(len - 1))::!pl else if !dot = 2 then if abs=false && (List.length !pl) = 0 then incr back else if (List.length !pl) > 0 then pl := List.tl !pl; List.iter (fun (s, e) -> r := (substr p s (e-s +1)) ^ !r) !pl; if abs = false && !back > 0 then ( for i=1 to !back do r := "../" ^ !r done ); if abs then "/" ^ !r else !r end;; (* join two paths *) let path_join p1 p2 = normpath (Filename.concat p1 p2) ;; let readwriteline_chan f ic oc= try while true do output_string oc (f (input_line ic)) done with End_of_file -> () let replace_file sreg templ file = let regexp = Str.regexp sreg in let bakfile = (file ^ ".bak") and tmpfile = (file ^ ".tmp") in if (Filename.check_suffix file ".bak") || (Filename.check_suffix file ".tmp") then () else let ic = open_in_bin file in let oc = open_out_bin tmpfile in readwriteline_chan (Str.global_replace regexp templ) ic oc; (* print_endline file; *) close_in ic; close_out oc ; if Sys.file_exists bakfile then Unix.unlink bakfile; Unix.rename file bakfile; Unix.rename tmpfile file ;; let rec walkdir f path = let last_pos = (strlen path)-1 in let path = if path.[last_pos]='/' then substr path 0 last_pos else path in let list = ref [] and count = ref 0 in let dir = Unix.opendir path in try while true do match Unix.readdir dir with "." | ".." -> () | filename -> let npath = (path ^ "/" ^ filename) in if not ((Unix.lstat npath).Unix.st_kind = Unix.S_DIR) then (list := (path ^ "/" ^ filename) :: !list; count := !count + 1) else walkdir f npath done with End_of_file -> List.iter f !list; (* print_string (string_of_int !count); print_string ", ";*) ;; (* print_endline "Starting..." ;; *) (* walkdir print_endline "./CTB/";; *) (* walkdir (replace "html" "XXXhtmlXXX") "./CT";; *) (* walkdir (replace_file "html" "XXXhtmlXXX") "./CTB/";; *) (* walkdir (replace_file Sys.argv.(1) Sys.argv.(2)) Sys.argv.(3);; *) (* print_endline "END" ;; *) (* module Url = struct *) let hex_digits = [| '0'; '1'; '2'; '3'; '4'; '5'; '6'; '7'; '8'; '9'; 'A'; 'B'; 'C'; 'D'; 'E'; 'F' |];; let to_hex2 k = (* Converts k to a 2-digit hex string *) let s = String.create 2 in s.[0] <- hex_digits.( (k lsr 4) land 15 ); s.[1] <- hex_digits.( k land 15 ); s ;; let of_hex1 c = match c with ('0'..'9') -> Char.code c - Char.code '0' | ('A'..'F') -> Char.code c - Char.code 'A' + 10 | ('a'..'f') -> Char.code c - Char.code 'a' + 10 | _ -> raise Not_found ;; let url_encoding_re = Str.regexp "[^A-Za-z0-9_.!*-]";; let unsafe_chars_html4 = "<>\"&\000\001\002\003\004\005\006\007\008\011\012\014\015\016\017\018\019\020\021\022\023\024\025\026\027\028\029\030\031\127" ;; let arg = try Sys.argv.(1) with _ -> "No arg";; let encode s = let buf = Buffer.create (strlen s) in String.iter (fun c -> match c with '0'..'9' | 'A'..'Z' | 'a'..'z' | '_' | '.' | '*' | '!' | '-' | '/' -> Buffer.add_char buf c | _ -> Buffer.add_char buf '%'; Buffer.add_string buf (to_hex2 (int_of_char c)) ) s; Buffer.contents buf;; let url_decoding_re = Str.regexp "%0d%0a\\|%0D%0A\\|\r\n\\|\\+\\|%[0-9a-fA-f][0-9a-fA-f]\\|%u[0-9a-fA-f][0-9a-fA-f][0-9a-fA-f][0-9a-fA-f]";; let decode s = Str.global_substitute url_decoding_re (fun s -> let ss = Str.matched_string s in match ss with "\r\n" | "%0D%0A" | "%0d%0a" -> "\n" |"+" -> " " | _ -> let k1 = of_hex1 ss.[1] in let k2 = of_hex1 ss.[2] in String.make 1 (char_of_int((k1 lsl 4) lor k2)) ) s ;; let udecode s = Str.global_substitute url_decoding_re (fun s -> let ss = Str.matched_string s in match ss with "\r\n" | "%0D%0A" | "%0d%0a" -> "\n" |"+" -> " " | _ -> match ss.[1] with 'u' -> let k1 = of_hex1 ss.[2] in let k2 = of_hex1 ss.[3] in let k3 = of_hex1 ss.[4] in let k4 = of_hex1 ss.[5] in (String.make 1 (char_of_int((k1 lsl 4) lor k2)) ^ String.make 1 (char_of_int((k3 lsl 4) lor k4)) ) | _ -> let k1 = of_hex1 ss.[1] in let k2 = of_hex1 ss.[2] in String.make 1 (char_of_int((k1 lsl 4) lor k2)) ) s ;; let split_name_is_value s = try let p = String.index s '=' in (decode (substr s 0 p), decode (substr s (p+1) (strlen s - p - 1) )) with Not_found -> (decode s ,"") (* | _ -> pre ("Error split_name_is_value:" ^ s);(s,"") *) ;; (* let kv = List.map split_name_is_value pairs;; *) (* List.iter (fun (x,y) -> pre ((henc x) ^ "," ^ (henc y))) kv;; *) let henc s = let buf = Buffer.create (strlen s) in String.iter (fun c -> match c with '&' -> Buffer.add_string buf "&" | '<' -> Buffer.add_string buf "<" | '>' -> Buffer.add_string buf ">" | '"' -> Buffer.add_string buf """ (*'"'*) | '\'' -> Buffer.add_string buf "'" (* | '\n' -> Buffer.add_string buf "\r\n" *) | _ -> Buffer.add_char buf c ) s; Buffer.contents buf;; let filesave fn s = let out = open_out fn in output_string out s; close_out out;; let fileread fn = let ic = open_in fn in let size = in_channel_length ic in let s = String.create size in really_input ic s 0 size; s ;; (* "filecopy" copied from : http://pauillac.inria.fr/~remy/poly/system/camlunix/fich.html#toc13 and added st_perm copying. *) let filecopy input_name output_name = let buffer_size = 8192 in let buffer = String.create buffer_size in let fd_in = Unix.openfile input_name [Unix.O_RDONLY] 0 in let perm = (Unix.lstat input_name).Unix.st_perm in let fd_out = Unix.openfile output_name [Unix.O_WRONLY; Unix.O_CREAT; Unix.O_TRUNC] perm in Unix.fchmod fd_out perm; let rec copy_loop () = match Unix.read fd_in buffer 0 buffer_size with 0 -> () | r -> ignore (Unix.write fd_out buffer 0 r); copy_loop () in copy_loop (); Unix.close fd_in; Unix.close fd_out;; (* let system = (fun s -> pr "
";
  flush stdout;
  let r = Unix.system s in
  pr "
"; r );; *) let close_pin chan = let stat = Unix.close_process_in chan in (match stat with Unix.WEXITED 0 -> ("") | Unix.WEXITED i -> ("command failed with code " ^ string_of_int i) | Unix.WSIGNALED i -> ("command killed by signal " ^ string_of_int i) | Unix.WSTOPPED i -> ("command stopped by signal " ^ string_of_int i));; let systemf f cmd = let ic = Unix.open_process_in cmd in (try while true do f (input_line ic) done with _ -> () ); close_pin ic;; let prsystem cmd= pr "
";
  let r = systemf (fun s -> pre (henc s)) cmd in
  if r <> "" then pre(r);
  pr "
" let system cmd = let s = String.create 2048 in let buf = Buffer.create 2048 in let ic = Unix.open_process_in cmd in let c = ref 1 in while !c <> 0 do c := input ic s 0 2048; Buffer.add_string buf (String.sub s 0 !c) done; let r = close_pin ic in if r <> "" then prerr_string r; Buffer.contents buf;; let status_cmd = "date;hostname;uptime;ps auxww" let server_status () = prsystem status_cmd (*server_status ();;*) (*system "ls -alt";;*) let chop s = if s = "" then "" else substr s 0 ((strlen s) - 1);; let qs = match (env "REQUEST_METHOD") with "POST" -> let cls = env "CONTENT_LENGTH" in let cl = if cls <> "" && (int_of_string cls) < max_post then int_of_string cls else max_post in let rb = ref 0 and rbs = ref 1 in let sb = String.make cl ' ' in (try while !rbs <> 0 && !rb < cl do rbs := (Unix.read Unix.stdin sb !rb (cl - !rb)); rb := !rb + !rbs; done; () with _ -> () ); sb | _ -> env "QUERY_STRING" ;; (* pre qs;; let kv = List.map split_name_is_value (split_pairs qs);; List.iter (fun (x,y) -> pre ((henc x) ^ " = " ^ (henc y))) kv;; *) module SMap = Map.Make(String) let split_pairs s = if s ="" then [] else ( let i = ref 0 and list = ref [] in try while true do let p = String.index_from s !i '&' in list := (substr s !i (p - !i)) :: !list; i := p +1 done; !list with Not_found -> list := (substr s !i (strlen s - !i)) :: !list; !list );; let split_add s m = try let p = String.index s '=' in let k = decode (substr s 0 p) in let v = decode (substr s (p+1) (strlen s - p - 1) ) in (* if SMap.mem k m then SMap.replace k (v ^ (Smap.find k m)) m else *) SMap.add k v m with Not_found -> if s <> "" then SMap.add (decode s) "" m else m ;; let formdat = (List.fold_right (fun s m -> split_add s m) (split_pairs qs) SMap.empty);; let form s = try SMap.find s formdat with Not_found -> "";; let script = env "SCRIPT_NAME";; let docroot = let dre = env "DOCUMENT_ROOT" in if (find cwd home) = 0 then ( (*prs "fin cwd home - 0"; *) let hlen = strlen home in let drt = substr cwd hlen ((strlen cwd) - hlen) in let ri = try String.index_from drt 1 '/' with _ -> 0 in let drx = path_join home (if ri = 0 then drt else substr drt 0 ri ) in if (try ((Unix.lstat dre).Unix.st_ino = (Unix.lstat drx).Unix.st_ino) with _ -> false ) then drx else dre ) else dre ;; let wpath p = if (find p docroot) = 0 then slice p (strlen docroot) 0 else "/ERROR/";; flush_all ();;