1 (* Copyright (C) 2002-2004, HELM Team.
3 * This file is part of HELM, an Hypertextual, Electronic
4 * Library of Mathematics, developed at the Computer Science
5 * Department, University of Bologna, Italy.
7 * HELM is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * HELM is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with HELM; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
22 * For details, see the HELM World-Wide-Web page,
23 * http://cs.unibo.it/helm/.
31 let debug_print s = if debug then prerr_endline s
32 let _ = Http_common.debug := false
34 exception Chat_unfinished
35 exception Unbound_identifier of string
36 exception Invalid_action of string (* invalid action for "/search" method *)
38 let daemon_name = "Moogle"
39 let configuration_file = "/projects/helm/etc/moogle.conf.xml"
42 "EXPRESSION"; "ACTION"; "ADVANCED"; "ADVANCED_CHECKED"; "SIMPLE_CHECKED";
43 "TITLE"; "NO_CHOICES"; "CURRENT_CHOICES"; "CHOICES"; "MSG"; "ID_TO_URIS";
44 "ID"; "IDEN"; "INTERPRETATIONS"; "INTERPRETATIONS_LABELS"; "RESULTS";
45 "NEW_ALIASES"; "SEARCH_ENGINE_URL"; "PREV_LINK"; "PAGE"; "PAGES"; "NEXT_LINK"
49 let regexps = Hashtbl.create 25 in
51 (fun tag -> Hashtbl.add regexps tag (Pcre.regexp (sprintf "@%s@" tag)))
55 (* First of all we load the configuration *)
56 let _ = Helm_registry.load_from configuration_file
57 let port = Helm_registry.get_int "search_engine.port"
58 let pages_dir = Helm_registry.get "search_engine.html_dir"
60 let interactive_interpretation_choice_TPL = pages_dir ^ "/moogle_chat2.html"
61 let moogle_TPL = pages_dir ^ "/moogle.html"
64 let ic = Unix.open_process_in "hostname -f" in
65 let hostname = input_line ic in
66 ignore (Unix.close_process_in ic);
67 sprintf "http://%s:%d" hostname port
68 let _ = Helm_registry.set "search_engine.my_own_url" my_own_url
70 let bad_request body outchan =
71 Http_daemon.respond_error ~code:(`Status (`Client_error `Bad_request)) ~body
74 (** chain application of Pcre substitutions *)
75 let rec apply_substs substs line =
78 | (rex, templ) :: rest -> apply_substs rest (Pcre.replace ~rex ~templ line)
79 (** fold like function on files *)
80 let fold_file f init fname =
81 let inchan = open_in fname in
82 let rec fold_lines' value =
84 let line = input_line inchan in
85 fold_lines' (f value line)
86 with End_of_file -> value
88 let res = (try fold_lines' init with e -> (close_in inchan; raise e)) in
91 (** iter like function on files *)
92 let iter_file f = fold_file (fun _ line -> f line) ()
93 let javascript_quote s =
94 let rex = Pcre.regexp "'" in
95 let rex' = Pcre.regexp "\"" in
96 Pcre.replace ~rex ~templ:"\\'"
97 (Pcre.replace ~rex:rex' ~templ:"\\\"" s)
99 let len = String.length s in
100 String.sub s 1 (len-1)
102 let len = String.length s in
103 let suffix = String.sub s (len-4) 4 in
104 not (suffix = ".var")
106 let add_param_substs params =
109 let key' = (Pcre.extract ~pat:"param\\.(.*)" key).(1) in
110 Pcre.regexp ("@" ^ key' ^ "@"), value)
112 (fun ((key,_) as p) -> Pcre.pmatch ~pat:"^param\\." key)
115 let page_RE = Pcre.regexp "¶m\\.page=\\d+"
117 let send_results results
118 ?(id_to_uris = CicTextualParser2.EnvironmentP3.of_string "")
119 (req: Http_types.request) outchan
121 let page_link anchor page =
123 let this = req#param "this" in
125 if Pcre.pmatch ~rex:page_RE this then
126 Pcre.replace ~rex:page_RE ~templ:(sprintf "¶m.page=%d" page)
129 sprintf "%s¶m.page=%d" this page
131 let target = Pcre.replace ~pat:"&" ~templ:"&" target in
132 sprintf "<a href=\"%s\">%s</a>" target anchor
133 with Http_types.Param_not_found _ -> ""
135 Http_daemon.send_basic_headers ~code:(`Code 200) outchan ;
136 Http_daemon.send_header "Content-Type" "text/xml" outchan;
137 Http_daemon.send_CRLF outchan ;
138 let subst, results_string =
140 | `Results results ->
141 let page = try int_of_string (req#param "page") with _ -> 1 in
142 let results_no = List.length results in
143 let results_per_page =
144 Helm_registry.get_int "search_engine.results_per_page"
147 if results_no mod results_per_page = 0 then
148 results_no / results_per_page
150 results_no / results_per_page + 1
152 ([ tag "PAGE", string_of_int page; tag "PAGES", string_of_int pages ] @
154 if page > 1 then page_link "Prev" (page-1) else "" ] @
156 if page < pages then page_link "Next" (page+1) else "" ]),
157 MooglePp.theory_of_result req page results
159 [ tag "PAGE", ""; tag "PAGES", "";
160 tag "PREV_LINK", ""; tag "NEXT_LINK", "" ],
164 (tag "SEARCH_ENGINE_URL", my_own_url) ::
165 (tag "RESULTS", results_string) ::
166 (tag "ADVANCED", req#param "advanced") ::
167 (tag "EXPRESSION", req#param "expression") ::
168 add_param_substs req#params @
169 (if req#param "advanced" = "no" then
170 [ tag "SIMPLE_CHECKED", "checked='true'";
171 tag "ADVANCED_CHECKED", "" ]
173 [ tag "SIMPLE_CHECKED", "";
174 tag "ADVANCED_CHECKED", "checked='true'" ]) @
180 CicTextualParser2.EnvironmentP3.to_string id_to_uris
184 (* CSC: Bug here: this is a string, not an array! *)
185 ((tag "NEW_ALIASES", "'" ^ javascript_quote new_aliases ^ "'") ::
189 output_string outchan (processed_line ^ "\n"))
192 let exec_action dbh (req: Http_types.request) outchan =
193 let term_str = req#param "expression" in
194 let (context, metasenv) = ([], []) in
196 try req#param "aliases"
197 with Http_types.Param_not_found _ -> ""
199 let parse_interpretation_choices choices =
200 List.map int_of_string (Pcre.split ~pat:" " choices) in
201 let parse_choices choices_raw =
202 let choices = Pcre.split ~pat:";" choices_raw in
205 match Pcre.split ~pat:"\\s" x with
207 | id::tail when id<>"" ->
210 Some (List.map (fun u -> Netencoding.Url.decode u) tail)
213 | _ -> failwith "Can't parse choices")
217 let id_to_uris = CicTextualParser2.EnvironmentP3.of_string id_to_uris_raw in
220 parse_choices (req#param "choices")
221 with Http_types.Param_not_found _ -> (fun _ -> None)
223 let interpretation_choices =
225 let choices_raw = req#param "interpretation_choices" in
226 if choices_raw = "" then None
227 else Some (parse_interpretation_choices choices_raw)
228 with Http_types.Param_not_found _ -> None
230 let module Chat: DisambiguateTypes.Callbacks =
232 let interactive_user_uri_choice ~selection_mode ?ok
233 ?enable_button_for_non_vars ~(title: string) ~(msg: string)
234 ~(id: string) (choices: string list)
236 match id_to_choices id with
237 | Some choices -> choices
238 | None -> List.filter nonvar choices
240 let interactive_interpretation_choice interpretations =
241 match interpretation_choices with
244 let html_interpretations =
245 MooglePp.html_of_interpretations interpretations
247 Http_daemon.send_basic_headers ~code:(`Code 200) outchan ;
248 Http_daemon.send_CRLF outchan ;
253 [tag "ADVANCED", req#param "advanced";
254 tag "INTERPRETATIONS", html_interpretations;
255 tag "CURRENT_CHOICES", req#param "choices";
256 tag "EXPRESSION", req#param "expression";
257 tag "ACTION", string_tail req#path ]
260 output_string outchan (processed_line ^ "\n"))
261 interactive_interpretation_choice_TPL;
262 raise Chat_unfinished
264 let input_or_locate_uri ~title ?id () =
266 | Some id -> raise (Unbound_identifier id)
267 | None -> assert false
270 let module Disambiguate' = Disambiguate.Make(Chat) in
271 let ast = CicTextualParser2.parse_term (Stream.of_string term_str) in
272 let (id_to_uris, metasenv, term) =
274 Disambiguate'.disambiguate_term dbh context metasenv ast id_to_uris
276 | [id_to_uris,metasenv,term] -> id_to_uris,metasenv,term
281 | "/match" -> MetadataQuery.match_term ~dbh term
283 let status = ProofEngineTypes.initial_status term metasenv in
284 let intros = PrimitiveTactics.intros_tac () in
285 let subgoals = ProofEngineTypes.apply_tactic intros status in
288 let (uri,metasenv,bo,ty) = proof in
289 List.map fst (MetadataQuery.hint ~dbh (proof, goal))
294 | Cic.MutInd (uri, typeno, _) ->
295 UriManager.string_of_uriref (uri, [typeno])
298 MetadataQuery.elim ~dbh uri
301 send_results ~id_to_uris (`Results uris) req outchan
303 let callback dbh (req: Http_types.request) outchan =
305 debug_print (sprintf "Received request: %s" req#path);
308 (* TODO implement "is_permitted" *)
309 (let is_permitted _ = true in
310 let page = req#param "url" in
313 bool_of_string (req#param "preprocess")
314 with Invalid_argument _ | Http_types.Param_not_found _ -> false)
317 | page when is_permitted page ->
318 (let fname = sprintf "%s/%s" pages_dir page in
319 Http_daemon.send_basic_headers ~code:(`Code 200) outchan;
320 Http_daemon.send_header "Content-Type" "text/html" outchan;
321 Http_daemon.send_CRLF outchan;
322 if preprocess then begin
325 output_string outchan
327 ((tag "SEARCH_ENGINE_URL", my_own_url) ::
328 (tag "ADVANCED", "no") ::
329 (tag "RESULTS", "") ::
330 add_param_substs req#params)
335 Http_daemon.send_file ~src:(Http_types.FileSrc fname) outchan)
336 | page -> Http_daemon.respond_forbidden ~url:page outchan))
337 | "/help" -> Http_daemon.respond ~body:daemon_name outchan
339 let initial_expression =
340 try req#param "expression" with Http_types.Param_not_found _ -> ""
343 Pcre.replace ~pat:"\\s*$"
344 (Pcre.replace ~pat:"^\\s*" initial_expression)
346 if expression = "" then
347 send_results (`Results []) req outchan
349 let results = MetadataQuery.locate ~dbh expression in
350 send_results (`Results results) req outchan
353 | "/match" -> exec_action dbh req outchan
355 Http_daemon.respond_error ~code:(`Status (`Client_error `Bad_request))
357 debug_print (sprintf "%s done!" req#path)
359 | Chat_unfinished -> ()
360 | Http_types.Param_not_found attr_name ->
361 bad_request (sprintf "Parameter '%s' is missing" attr_name) outchan
362 | CicTextualParser2.Parse_error msg ->
363 send_results (`Error (MooglePp.pp_error "Parse_error" msg)) req outchan
364 | Unbound_identifier id ->
365 send_results (`Error (MooglePp.pp_error "Unbound identifier" id)) req
368 let exn_string = Printexc.to_string exn in
369 debug_print exn_string;
370 let msg = MooglePp.pp_error "Uncaught exception" exn_string in
371 send_results (`Error msg) req outchan
374 printf "%s started and listening on port %d\n" daemon_name port;
375 printf "Current directory is %s\n" (Sys.getcwd ());
376 printf "HTML directory is %s\n" pages_dir;
378 Unix.putenv "http_proxy" "";
380 new DB.connection ~host:(Helm_registry.get "db.host")
381 ~user:(Helm_registry.get "db.user") (Helm_registry.get "db.database")
383 Http_daemon.start' ~port (callback dbh);
384 printf "%s is terminating, bye!\n" daemon_name