123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755(* Ocsigen
* http://www.ocsigen.org
* Module ocsigen_parseconfig.ml
* Copyright (C) 2005-2008 Vincent Balat, Nataliya Guts, Stéphane Glondu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, with linking exception;
* either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser 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.
*)(******************************************************************)(** Config file parsing *)openXmlopenOcsigen_configmoduleNetstring_pcre=Ocsigen_lib.Netstring_pcreletsection=Lwt_log.Section.make"ocsigen:config"letblah_of_stringftags=tryf(Ocsigen_lib.String.remove_spacess0((String.lengths)-1))withFailure_->raise(Ocsigen_config.Config_file_error("While parsing <"^tag^"> - "^s^" is not a valid value."))letint_of_string=blah_of_stringint_of_stringletfloat_of_string=blah_of_stringfloat_of_stringletdefault_default_hostname=lethostname=Unix.gethostname()intry(*VVV Is it ok? Is it reliable? *)(List.hd(Unix.getaddrinfohostname"www"[Unix.AI_CANONNAME;Unix.AI_SOCKTYPEUnix.SOCK_STREAM])).Unix.ai_canonnamewithFailure_->Lwt_log.ign_warning_f~section"Cannot determine default host name. Will use \"%s\" \
to create absolute links or redirections dynamically \
if you do not set <host defaulthostname=\"...\" ...> \
in config file."hostname;(*VVV Is it the right behaviour? *)hostnameletparse_size=letkilo=Int64.of_int1000inletmega=Int64.of_int1000000inletgiga=Int64.mulkilomegainlettera=Int64.mulmegamegainletkibi=Int64.of_int1024inletmebi=Int64.of_int1048576inletgibi=Int64.mulkibimebiinlettebi=Int64.mulmebimebiinfuns->letl=String.lengthsinlets=Ocsigen_lib.String.remove_spacess0(l-1)inletvl=tryInt64.of_string(String.subs0l)withFailure_->failwith"Ocsigen_parseconfig.parse_size"inletol=letl1=l-1inifl1>0thenletc1=s.[l1]inif(c1='o')||(c1='B')thenvl1elsevlelsevlinif(s="")||(s="infinity")thenNoneelseSome(letl=String.lengthsinletl1=l-1inifl1>0thenletc1=String.subsl11inif(c1="T")thenInt64.multebi(vl1)elseif(c1="G")thenInt64.mulgibi(vl1)elseif(c1="M")thenInt64.mulmebi(vl1)elseif(c1="k")thenInt64.mulkibi(vl1)elseletl2=l-2inifl2>0thenletc2=String.subsl22inif(c2="To")||(c2="TB")thenInt64.multera(vl2)elseif(c2="Go")||(c2="GB")thenInt64.mulgiga(vl2)elseif(c2="Mo")||(c2="MB")thenInt64.mulmega(vl2)elseif(c2="ko")||(c2="kB")thenInt64.mulkilo(vl2)elseletl3=l-3inifl3>0thenletc3=String.subsl33inif(c3="Tio")||(c3="TiB")thenInt64.multebi(vl3)elseif(c3="Gio")||(c3="GiB")thenInt64.mulgibi(vl3)elseif(c3="Mio")||(c3="MiB")thenInt64.mulmebi(vl3)elseif(c3="kio")||(c3="kiB")thenInt64.mulkibi(vl3)elseolelseolelseolelseol)letparse_size_tagtags=tryparse_sizeswithFailure_->raise(Ocsigen_config.Config_file_error("While parsing <"^tag^"> - "^s^" is not a valid size."))letrecparse_string=function|[]->""|PCDatas::l->s^parse_stringl|_->failwith"ocsigen_parseconfig.parse_string"letparse_string_tagtags=tryparse_stringswithFailure_->raise(Ocsigen_config.Config_file_error("While parsing <"^tag^"> - String expected."))letparser_config=letrecparse_serversn=function|[]->(matchnwith|[]->raise(Config_file_error("<server> tag expected"))|_->n)|(Element("server",[],nouveau))::ll->(matchllwith|[]->()|_->Lwt_log.ign_warning~section"At most one <server> tag possible in config file. \
Ignoring trailing data.");parse_servers(n@[nouveau])[](* ll *)(* Multiple server not supported any more *)(* nouveau at the end *)|_->raise(Config_file_error("syntax error inside <ocsigen>"))infunction|Element("ocsigen",[],l)->parse_servers[]l|_->raise(Config_file_error"<ocsigen> tag expected")letparse_extfile=parser_config(Xml.parse_filefile)letpreloadfileconfig()=Ocsigen_extensions.set_configconfigletpostloadfile()=Ocsigen_extensions.set_config[](* Checking hostnames. We make only make looze efforts.
See RFC 921 and 952 for further details *)letcorrect_hostname=letregexp=Netstring_pcre.regexp"^[a-zA-Z0-9]+((\\.|-)[a-zA-Z0-9]+)*$"infunh->Netstring_pcre.string_matchregexph0<>None(* Splits the [host] field, first according to spaces
(which encode disjunction),
and then according to wildcards '*' ; we then transform the hosts-with-regexp
into a regepx that matches a potential host.
The whole result is cached because user config files (for userconf)
are read at each request. *)letparse_host_field=leth=Hashtbl.create17in(fun(hostfilter:stringoption)->tryHashtbl.findhhostfilterwithNot_found->letr=matchhostfilterwith|None->["*",Netstring_pcre.regexp".*$",None](* default = "*:*" *)|Somes->letparse_one_hostss=lethost,port=tryletdppos=String.indexss':'andlen=String.lengthssinlethost=String.subss0dpposandport=matchString.subss(dppos+1)((len-dppos)-1)with|"*"->None|p->Some(int_of_string"host"p)inhost,portwith|Not_found->ss,None|Failure_->raise(Config_file_error"bad port number")inletsplit_host=function|Str.Delim_->".*"|Str.Textt->Pcre.quotetin(host,Netstring_pcre.regexp(String.concat""((List.mapsplit_host(Str.full_split(Str.regexp"[*]+")host))@["$"])),port)inList.mapparse_one_host(Str.split(Str.regexp"[ \t]+")s)inHashtbl.addhhostfilterr;(r:Ocsigen_extensions.virtual_hosts))(* Extract a default hostname from the "host" field if no default is
provided *)letget_defaulthostname~defaulthostname~host=matchdefaulthostnamewith|Somed->d|None->(* We look for a hostname without wildcard (second case).
Something more clever could be envisioned *)letrecaux=function|[]->default_default_hostname|(t,_,(Some80|None))::_when(not(String.containst'*'))->t|_::q->auxqinlethost=auxhostinLwt_log.ign_warning_f~section"While parsing config file, tag <host>: No defaulthostname, \
assuming it is \"%s\""host;ifcorrect_hostnamehostthenhostelseraise(Ocsigen_config.Config_file_error("Incorrect hostname "^host))letlater_pass_host_attr(name,charset,defaulthostname,defaulthttpport,defaulthttpsport,ishttps)=function|"hostfilter",s->(matchnamewith|None->Somes,charset,defaulthostname,defaulthttpport,defaulthttpsport,ishttps|_->raise(Ocsigen_config.Config_file_error("Duplicate attribute name in <host>")))|"charset",s->(matchcharsetwith|None->name,Somes,defaulthostname,defaulthttpport,defaulthttpsport,ishttps|_->raise(Ocsigen_config.Config_file_error("Duplicate attribute charset in <host>")))|"defaulthostname",s->(matchdefaulthostnamewith|None->ifcorrect_hostnamesthenname,charset,Somes,defaulthttpport,defaulthttpsport,ishttpselseraise(Ocsigen_config.Config_file_error("Incorrect hostname "^s))|_->raise(Ocsigen_config.Config_file_error"Duplicate attribute defaulthostname in <host>"))|"defaulthttpport",s->(matchdefaulthttpportwith|None->name,charset,defaulthostname,Somes,defaulthttpsport,ishttps|_->raise(Ocsigen_config.Config_file_error("Duplicate attribute defaulthttpport in <host>")))|"defaulthttpsport",s->(matchdefaulthttpsportwith|None->name,charset,defaulthostname,defaulthttpport,Somes,ishttps|_->raise(Ocsigen_config.Config_file_error("Duplicate attribute defaulthttpsport in <host>")))|"defaultprotocol",s->(matchishttpswith|None->name,charset,defaulthostname,defaulthttpport,defaulthttpsport,Somes|_->raise(Ocsigen_config.Config_file_error("Duplicate attribute defaultprotocol in <host>")))|attr,_->raise(Ocsigen_config.Config_file_error("Wrong attribute for <host>: "^attr))letlater_pass_hostattrsl=lethost,charset,defaulthostname,defaulthttpport,defaulthttpsport,defaultprotocol=List.fold_leftlater_pass_host_attr(None,None,None,None,None,None)(List.revattrs)inlethost=parse_host_fieldhostinletcharset=matchcharset,Ocsigen_config.get_default_charset()with|Somecharset,_|None,Somecharset->charset|None,None->"utf-8"anddefaulthttpport=matchdefaulthttpportwith|None->Ocsigen_config.get_default_port()|Somep->int_of_string"host"panddefaulthostname=get_defaulthostname~defaulthostname~hostanddefaulthttpsport=matchdefaulthttpsportwith|None->Ocsigen_config.get_default_sslport()|Somep->int_of_string"host"pandserve_everything={Ocsigen_extensions.do_not_serve_regexps=[];do_not_serve_files=[];do_not_serve_extensions=[];}inletconf={Ocsigen_extensions.default_hostname=defaulthostname;default_httpport=defaulthttpport;default_httpsport=defaulthttpsport;default_protocol_is_https=defaultprotocol=Some"https";mime_assoc=Ocsigen_charset_mime.default_mime_assoc();charset_assoc=Ocsigen_charset_mime.empty_charset_assoc~default:charset();default_directory_index=["index.html"];list_directory_content=false;follow_symlinks=`Owner_match;do_not_serve_404=serve_everything;do_not_serve_403=serve_everything;uploaddir=Ocsigen_config.get_uploaddir();maxuploadfilesize=Ocsigen_config.get_maxuploadfilesize();}inletparse_config=Ocsigen_extensions.make_parse_config[](Ocsigen_extensions.parse_config_itemNonehostconf)in(* default site for host *)host,conf,parse_configlletlater_pass_extensiontagattrsl=(* We do not reload extensions *)matchattrswith|[]->raise(Config_file_error("missing module, name or findlib-package attribute in "^tag))|["name",s]->Ocsigen_loader.init_module(preloadfilel)postloadfilefalses|["module",s]->Ocsigen_loader.loadfiles(preloadfilel)postloadfilefalse[s];|["findlib-package",s]->Ocsigen_loader.loadfiles(preloadfilel)postloadfilefalse(Ocsigen_loader.findfiless)|_->raise(Config_file_error("Wrong attribute for "^tag))letreclater_pass_extconfdir=letfaccs=ifFilename.check_suffixs"conf"thenmatchletfilename=dir^"/"^sintryLwt_log.ign_info_f~section"Parsing configuration file %s"filename;parse_extfilenamewithe->Lwt_log.ign_error_f~section~exn:e"Error while loading configuration file %s (ignored)"filename;[]with|[]->acc|s::_->acc@later_passselseaccintryletfiles=Sys.readdirdirinArray.sortcomparefiles;Array.fold_leftf[]fileswith|Sys_error_ase->Lwt_log.ign_error~section~exn:e"Error while loading configuration file (ignored)";[](* Config file is parsed twice. This is the second parsing (site
loading). *)andlater_pass=function|[]->[]|Element("port",_atts,_p)::ll->later_passll|Element("charset"asst,_atts,p)::ll->set_default_charset(Some(parse_string_tagstp));later_passll|Element("logdir",[],_p)::ll->later_passll|Element("syslog",[],_p)::ll->later_passll|Element("ssl",[],_p)::ll->later_passll|Element("user",[],_p)::ll->later_passll|Element("group",[],_p)::ll->later_passll|Element("uploaddir"asst,[],p)::ll->set_uploaddir(Some(parse_string_tagstp));later_passll|Element("datadir"asst,[],p)::ll->set_datadir(parse_string_tagstp);later_passll|Element("minthreads",[],_p)::ll->later_passll|Element("maxthreads",[],_p)::ll->later_passll|Element("maxdetachedcomputationsqueued"asst,[],p)::ll->set_max_number_of_threads_queued(int_of_stringst(parse_string_tagstp));later_passll|Element("maxconnected"asst,[],p)::ll->set_max_number_of_connections(int_of_stringst(parse_string_tagstp));later_passll|Element("mimefile"asst,[],p)::ll->Ocsigen_config.set_mimefile(parse_string_tagstp);later_passll|Element("maxretries"asst,[],p)::ll->set_maxretries(int_of_stringst(parse_string_tagstp));later_passll|Element("timeout"asst,[],p)::ll|Element("clienttimeout"asst,[],p)::ll->set_client_timeout(int_of_stringst(parse_string_tagstp));later_passll|Element("servertimeout"asst,[],p)::ll->set_server_timeout(int_of_stringst(parse_string_tagstp));later_passll|Element("netbuffersize"asst,[],p)::ll->Ocsigen_stream.set_net_buffer_size(int_of_stringst(parse_string_tagstp));later_passll|Element("filebuffersize"asst,[],p)::ll->set_filebuffersize(int_of_stringst(parse_string_tagstp));later_passll|Element("maxrequestbodysize"asst,[],p)::ll->set_maxrequestbodysize(parse_size_tagst(parse_string_tagstp));later_passll|Element("maxuploadfilesize"asst,[],p)::ll->set_maxuploadfilesize(parse_size_tagst(parse_string_tagstp));later_passll|Element("commandpipe"asst,[],p)::ll->set_command_pipe(parse_string_tagstp);later_passll|Element("shutdowntimeout"asst,[],p)::ll->set_shutdown_timeout(matchparse_string_tagstpwith|"notimeout"->None|p->Some(float_of_stringstp));later_passll|Element("debugmode",[],[])::ll->set_debugmodetrue;later_passll|Element("usedefaulthostname",[],[])::ll->set_usedefaulthostnametrue;later_passll|Element("disablepartialrequests",[],[])::ll->set_disablepartialrequeststrue;later_passll|Element("respectpipeline",[],[])::ll->set_respect_pipeline();later_passll|Element("findlib",["path",p],[])::ll->Ocsigen_loader.add_ocamlpathp;later_passll|Element("require",atts,l)::ll|Element("extension",atts,l)::ll->later_pass_extension"<extension>"attsl;later_passll|Element("library",atts,l)::ll->later_pass_extension"<library>"attsl;later_passll|Element("host",atts,l)::ll->(* The evaluation order is important here *)leth=later_pass_hostattslinh::later_passll|Element("extconf",[("dir",dir)],[])::ll->(* The evaluation order is important here *)leth=later_pass_extconfdirinh@later_passll|Element(tag,_,_)::_->raise(Config_file_error("tag <"^tag^"> unexpected inside <server>"))|_->raise(Config_file_error"Syntax error")letlater_passl=Ocsigen_extensions.set_hosts(later_passl)(* Parsing <port> tags *)letparse_port=letall_ipv6=Netstring_pcre.regexp"^\\[::\\]:([0-9]+)$"inletall_ipv4=Netstring_pcre.regexp"^\\*:([0-9]+)$"inletsingle_ipv6=Netstring_pcre.regexp"^\\[([0-9A-Fa-f.:]+)\\]:([0-9]+)$"inletsingle_ipv4=Netstring_pcre.regexp"^([0-9.]+):([0-9]+)$"infuns->letdo_matchr=Netstring_pcre.string_matchrs0inletgetxi=Netstring_pcre.matched_groupxisinmatchdo_matchall_ipv6with|Somer->`IPv6(Unix.inet6_addr_any),int_of_string"port"(getr1)|None->matchdo_matchall_ipv4with|Somer->`IPv4(Unix.inet_addr_any),int_of_string"port"(getr1)|None->matchdo_matchsingle_ipv6with|Somer->`IPv6(Unix.inet_addr_of_string(getr1)),int_of_string"port"(getr2)|None->matchdo_matchsingle_ipv4with|Somer->`IPv4(Unix.inet_addr_of_string(getr1)),int_of_string"port"(getr2)|None->`All,int_of_string"port"sletparse_facility=function|"auth"->`Auth|"authpriv"->`Authpriv|"console"->`Console|"cron"->`Cron|"daemon"->`Daemon|"ftp"->`FTP|"kernel"->`Kernel|"lpr"->`LPR|"local0"->`Local0|"local1"->`Local1|"local2"->`Local2|"local3"->`Local3|"local4"->`Local4|"local5"->`Local5|"local6"->`Local6|"local7"->`Local7|"mail"->`Mail|"ntp"->`NTP|"news"->`News|"security"->`Security|"syslog"->`Syslog|"uucp"->`UUCP|"user"->`User|t->raise(Config_file_error("Unknown "^t^" facility in <syslog>"))(* First parsing of config file *)letconfig_error_for_somes=function|None->()|_->raise(Config_file_errors)letmake_ssl_info~certificate~privatekey~ciphers~dhfile~curve={Ocsigen_config.ssl_certificate=certificate;ssl_privatekey=privatekey;ssl_ciphers=ciphers;ssl_dhfile=dhfile;ssl_curve=curve}letrecparse_ssll~certificate~privatekey~ciphers~dhfile~curve=matchlwith|[]->Some(make_ssl_info~certificate~privatekey~ciphers~dhfile~curve)|Element("certificate"asst,[],p)::l->config_error_for_some"Two certificates inside <ssl>"certificate;letcertificate=Some(parse_string_tagstp)inparse_ssl~certificate~privatekey~ciphers~dhfile~curvel|Element("privatekey"asst,[],p)::l->config_error_for_some"Two private keys inside <ssl>"privatekey;letprivatekey=Some(parse_string_tagstp)inparse_ssl~certificate~privatekey~ciphers~dhfile~curvel|Element("ciphers"asst,[],p)::l->config_error_for_some"Two cipher strings inside <ssl>"ciphers;letciphers=Some(parse_string_tagstp)inparse_ssl~certificate~privatekey~ciphers~dhfile~curvel|Element("dhfile"asst,[],p)::l->config_error_for_some"Two DH files inside <ssl>"dhfile;letdhfile=Some(parse_string_tagstp)inparse_ssl~certificate~privatekey~ciphers~dhfile~curvel|Element("curve"asst,[],p)::l->config_error_for_some"Two (EC) curves inside <ssl>"curve;letcurve=Some(parse_string_tagstp)inparse_ssl~certificate~privatekey~ciphers~dhfile~curvel|Element(tag,_,_)::_l->raise(Config_file_error("<"^tag^"> tag unexpected inside <ssl>"))|_->raise(Config_file_error("Unexpected content inside <ssl>"))letfirst_passc=letrecauxusergroupsslportssslports=function[]->((user,group),(ssl,ports,sslports))|Element("logdir"asst,[],p)::ll->set_logdir(parse_string_tagstp);auxusergroupsslportssslportsll|Element("syslog"asst,[],p)::ll->letstr=String.lowercase_ascii(parse_string_tagstp)inset_syslog_facility(Some(parse_facilitystr));auxusergroupsslportssslportsll|Element("port"asst,atts,p)::ll->(matchattswith[]|[("protocol","HTTP")]->letpo=tryparse_port(parse_string_tagstp)withFailure_->raise(Config_file_error"Wrong value for <port> tag")inauxusergroupssl(po::ports)sslportsll|[("protocol","HTTPS")]->letpo=tryparse_port(parse_string_tagstp)withFailure_->raise(Config_file_error"Wrong value for <port> tag")inauxusergroupsslports(po::sslports)ll|_->raise(Config_file_error"Wrong attribute for <port>"))|Element("minthreads"asst,[],p)::ll->set_minthreads(int_of_stringst(parse_string_tagstp));auxusergroupsslportssslportsll|Element("maxthreads"asst,[],p)::ll->set_maxthreads(int_of_stringst(parse_string_tagstp));auxusergroupsslportssslportsll|Element("ssl",[],p)::ll->(matchsslwithNone->letssl=letcertificate=Noneandprivatekey=Noneandciphers=Noneanddhfile=Noneandcurve=Noneinparse_ssl~certificate~privatekey~ciphers~dhfile~curvepinauxusergroupsslportssslportsll|_->raise(Config_file_error"Only one ssl certificate for each server supported for now"))|Element("user"asst,[],p)::ll->(matchuserwithNone->aux(Some(parse_string_tagstp))groupsslportssslportsll|_->raise(Config_file_error"Only one <user> tag for each server allowed"))|Element("group"asst,[],p)::ll->(matchgroupwithNone->auxuser(Some(parse_string_tagstp))sslportssslportsll|_->raise(Config_file_error"Only one <group> tag for each server allowed"))|Element("commandpipe"asst,[],p)::ll->set_command_pipe(parse_string_tagstp);auxusergroupsslportssslportsll|Element_::ll->auxusergroupsslportssslportsll|_->raise(Config_file_error"Syntax error")inlet(user,group),(si,ports,ssl_ports)=auxNoneNoneNone[][]cinletuser=matchuserwithNone->None(* Some (get_default_user ()) *)|Somes->ifs=""thenNoneelseSomesinletgroup=matchgroupwithNone->None(* Some (get_default_group ()) *)|Somes->ifs=""thenNoneelseSomesinOcsigen_config.set_useruser;Ocsigen_config.set_groupgroup;Ocsigen_config.set_ssl_infosi;Ocsigen_config.set_portsports;Ocsigen_config.set_ssl_portsssl_ports;()letparse_config?file()=letfile=matchfilewith|None->Ocsigen_config.get_config_file()|Somef->finparser_config(Xml.parse_filefile)