1 (* Run external shell functions.
2 * $Id: cocanwiki_func_shell.ml,v 1.1 2006/12/08 15:36:44 rich Exp $
9 open Cocanwiki_extensions
11 (* This exception is thrown if there is an error during processing. The
12 * string is an error message.
14 exception Error of string
16 let ws_re = Pcre.regexp "\\s+"
18 (* Disallowed in commands, if path is given. *)
19 let reserved = [ ".."; "/"; "\\" ]
21 let shell r dbh hostid arg =
23 (* Is this function enabled for this host? This is a safety feature
24 * to ensure that people only turn this feature on if they really
28 PGSQL(dbh) "select enable_shell_func from hosts where id = $hostid" in
29 if rows <> [true] then
30 raise (Error "Shell function is disabled on this host. You have to enable it manually by setting enable_shell_func and optionally set enable_shell_func_path for the relevant row in the hosts table. This function is dangerous because it allows editors to run arbitrary shell commands on the server.");
32 let arg = match arg with
33 | None -> raise (Error ("missing command name: try {{shell:command}}"))
36 let args = Pcre.split ~rex:ws_re arg in
39 | command :: args -> command, args
40 | _ -> raise (Error ("missing command name: try {{shell:command}}")) in
42 (* If the path is specified, then limit commands to run in this path.
43 * Disallow '..', '/' and '\'.
44 * If NO PATH is specified, then allow ARBITRARY commands to run (DANGER!)
49 "select enable_shell_func_path from hosts where id = $hostid"
53 | None -> command (* Any command can run. *)
55 (* Disallow reserved characters. *)
56 if List.exists (String.exists command) reserved then
57 raise (Error "command contains reserved characters (eg. '..' or '/').");
58 Filename.concat path command in
60 (* Run the command and capture the output. *)
62 command ^ " " ^ String.concat " " (List.map Filename.quote args) in
64 let chan = Unix.open_process_in cmd in
69 let line = input_line chan in
70 lines := line :: !lines
74 End_of_file -> List.rev !lines in
75 let stat = Unix.close_process_in chan in
77 | Unix.WEXITED 0 -> ()
79 raise (Error ("command failed with code " ^ string_of_int i))
81 raise (Error ("command killed by signal " ^ string_of_int i))
83 raise (Error ("command stopped by signal " ^ string_of_int i)));
85 String.concat "\n" lines
88 "shell: " ^ Cgi_escape.escape_html msg
90 (* Register function. *)
92 external_functions := ("shell", shell) :: !external_functions