123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324(* Js_of_ocaml library
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2010 Raphaël Proust
* Laboratoire PPS - CNRS Université Paris Diderot
*
* 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.
*)open!Import(* Url tampering. *)letsplitcs=Js.str_array(s##split(Js.string(String.make1c)))letsplit_2cs=letindex=s##indexOf(Js.string(String.make1c))inifindex<0thenJs.undefinedelseJs.def(s##slice0index,s##slice_end(index+1))exceptionLocal_exnletinterrupt()=raiseLocal_exn(* url (AKA percent) encoding/decoding *)letplus_re=Regexp.regexp_string"+"letescape_pluss=Regexp.global_replaceplus_res"%2B"letunescape_pluss=Regexp.global_replaceplus_res" "letplus_re_js_string=new%jsJs.regExp_withFlags(Js.string"\\+")(Js.string"g")letunescape_plus_js_strings=plus_re_js_string##.lastIndex:=0;s##replaceplus_re_js_string(Js.string" ")leturldecode_js_string_strings=Js.to_bytestring(Js.unescape(unescape_plus_js_strings))leturldecodes=Js.to_bytestring(Js.unescape(Js.bytestring(unescape_pluss)))(*let urlencode_js_string_string s =
Js.to_bytestring (Js.escape s)*)leturlencode?(with_plus=true)s=ifwith_plusthenescape_plus(Js.to_bytestring(Js.escape(Js.bytestrings)))elseJs.to_bytestring(Js.escape(Js.bytestrings))typehttp_url={hu_host:string(** The host part of the url. *);hu_port:int(** The port for the connection if any. *);hu_path:stringlist(** The path split on ['/'] characters. *);hu_path_string:string(** The original entire path. *);hu_arguments:(string*string)list(** Arguments as a field-value
association list.*);hu_fragment:string(** The fragment part (after the ['#'] character). *)}(** The type for HTTP url. *)typefile_url={fu_path:stringlist;fu_path_string:string;fu_arguments:(string*string)list;fu_fragment:string}(** The type for local file urls. *)typeurl=|Httpofhttp_url|Httpsofhttp_url|Fileoffile_url(** The type for urls. [File] is for local files and [Exotic s] is for
unknown/unsupported protocols. *)exceptionNot_an_http_protocolletis_secureprot_string=matchJs.to_bytestringprot_string##toLowerCasewith|"https:"|"https"->true|"http:"|"http"->false|"file:"|"file"|_->raiseNot_an_http_protocol(* port number *)letdefault_http_port=80letdefault_https_port=443(* path *)letpath_of_path_strings=letl=String.lengthsinletrecauxi=letj=tryString.index_fromsi'/'withNot_found->linletword=String.subsi(j-i)inifj>=lthen[word]elseword::aux(j+1)inmatchaux0with|[""]->[]|["";""]->[""]|a->a(* Arguments *)letencode_argumentsl=String.concat"&"(List.map(fun(n,v)->urlencoden^"="^urlencodev)l)letdecode_arguments_js_strings=letarr=split'&'sinletlen=arr##.lengthinletname_value_splits=split_2'='sinletrecauxaccidx=ifidx<0thenaccelsetryaux(Js.Optdef.case(Js.array_getarridx)interrupt(funs->Js.Optdef.case(name_value_splits)interrupt(fun(x,y)->letget=urldecode_js_string_stringingetx,gety))::acc)(predidx)withLocal_exn->auxacc(predidx)inaux[](len-1)letdecode_argumentss=decode_arguments_js_string(Js.bytestrings)leturl_re=new%jsJs.regExp(Js.bytestring"^([Hh][Tt][Tt][Pp][Ss]?)://([0-9a-zA-Z.-]+|\\[[0-9a-zA-Z.-]+\\]|\\[[0-9A-Fa-f:.]+\\])?(:([0-9]+))?(/([^\\?#]*)(\\?([^#]*))?(#(.*))?)?$")letfile_re=new%jsJs.regExp(Js.bytestring"^([Ff][Ii][Ll][Ee])://([^\\?#]*)(\\?([^#]*))?(#(.*))?$")leturl_of_js_strings=Js.Opt.case(url_re##execs)(fun()->Js.Opt.case(file_re##execs)(fun()->None)(funhandle->letres=Js.match_resulthandleinletpath_str=urldecode_js_string_string(Js.Optdef.get(Js.array_getres2)interrupt)inSome(File{fu_path=path_of_path_stringpath_str;fu_path_string=path_str;fu_arguments=decode_arguments_js_string(Js.Optdef.get(Js.array_getres4)(fun()->Js.bytestring""));fu_fragment=Js.to_bytestring(Js.Optdef.get(Js.array_getres6)(fun()->Js.bytestring""))})))(funhandle->letres=Js.match_resulthandleinletssl=is_secure(Js.Optdef.get(Js.array_getres1)interrupt)inletport_of_string=function|""->ifsslthen443else80|s->int_of_stringsinletpath_str=urldecode_js_string_string(Js.Optdef.get(Js.array_getres6)(fun()->Js.bytestring""))inleturl={hu_host=urldecode_js_string_string(Js.Optdef.get(Js.array_getres2)interrupt);hu_port=port_of_string(Js.to_bytestring(Js.Optdef.get(Js.array_getres4)(fun()->Js.bytestring"")));hu_path=path_of_path_stringpath_str;hu_path_string=path_str;hu_arguments=decode_arguments_js_string(Js.Optdef.get(Js.array_getres8)(fun()->Js.bytestring""));hu_fragment=urldecode_js_string_string(Js.Optdef.get(Js.array_getres10)(fun()->Js.bytestring""))}inSome(ifsslthenHttpsurlelseHttpurl))leturl_of_strings=url_of_js_string(Js.bytestrings)letstring_of_url=function|File{fu_path=path;fu_arguments=args;fu_fragment=frag;_}->("file://"^String.concat"/"(List.map(funx->urlencodex)path)^(matchargswith|[]->""|l->"?"^encode_argumentsl)^matchfragwith|""->""|s->"#"^urlencodes)|Http{hu_host=host;hu_port=port;hu_path=path;hu_arguments=args;hu_fragment=frag;_}->("http://"^urlencodehost^(matchportwith|80->""|n->":"^string_of_intn)^"/"^String.concat"/"(List.map(funx->urlencodex)path)^(matchargswith|[]->""|l->"?"^encode_argumentsl)^matchfragwith|""->""|s->"#"^urlencodes)|Https{hu_host=host;hu_port=port;hu_path=path;hu_arguments=args;hu_fragment=frag;_}->("https://"^urlencodehost^(matchportwith|443->""|n->":"^string_of_intn)^"/"^String.concat"/"(List.map(funx->urlencodex)path)^(matchargswith|[]->""|l->"?"^encode_argumentsl)^matchfragwith|""->""|s->"#"^urlencodes)moduleCurrent=structletl=ifJs.Optdef.test(Js.Optdef.returnDom_html.window##.location)thenDom_html.window##.locationelseletempty=Js.string""inobject%jsvalmutablehref=emptyvalmutableprotocol=emptyvalmutablehost=emptyvalmutablehostname=emptyvalmutableport=emptyvalmutablepathname=emptyvalmutablesearch=emptyvalmutablehash=emptyvalorigin=Js.undefinedmethodreload=()methodreplace_=()methodassign_=()endlethost=urldecode_js_string_stringl##.hostnameletprotocol=urldecode_js_string_stringl##.protocolletport=(fun()->trySome(int_of_string(Js.to_bytestringl##.port))withFailure_->None)()letpath_string=urldecode_js_string_stringl##.pathnameletpath=path_of_path_stringpath_stringletarguments=decode_arguments_js_string(ifJs.equals(l##.search##charAt0)(Js.string"?")thenl##.search##slice_end1elsel##.search)letget_fragment()=lets=Js.to_bytestringl##.hashinifString.lengths>0&&Char.equals.[0]'#'thenString.subs1(String.lengths-1)elsesletset_fragments=l##.hash:=Js.bytestringsletget()=url_of_js_stringl##.hrefletsetu=l##.href:=Js.bytestring(string_of_urlu)letas_string=urldecode_js_string_stringl##.hrefend