123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160(*{{{ Copyright (C) <2012> Anil Madhavapeddy <anil@recoil.org>
* Copyright (C) <2009> David Sheets <sheets@alum.mit.edu>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
}}}*)openSexplib0.Sexp_convtypeexpiration=[|`Session|`Max_ageofint64][@@derivingsexp]typecookie=string*string[@@derivingsexp]moduleSet_cookie_hdr=structtypet={cookie:cookie;expiration:expiration;domain:stringoption;path:stringoption;secure:bool;http_only:bool}[@@derivingfields,sexp](* Does not check the contents of name or value for ';', ',', '\s', or name[0]='$' *)letmake?(expiration=`Session)?path?domain?(secure=false)?(http_only=false)cookie={cookie;expiration;domain;path;secure;http_only}(* TODO: deprecated by RFC 6265 and almost certainly buggy without
reference to cookie field *)letserialize_1_1c=letattrs=["Version=1"]inletattrs=ifc.securethen("Secure"::attrs)elseattrsinletattrs=matchc.pathwithNone->attrs|Somep->("Path="^p)::attrsinletattrs=matchc.expirationwith|`Session->"Discard"::attrs|`Max_ageage->("Max-Age="^(Int64.to_stringage))::attrsinletattrs=matchc.domainwithNone->attrs|Somed->("Domain="^d)::attrsin("Set-Cookie2",String.concat"; "attrs)letserialize_1_0c=letattrs=ifc.http_onlythen["httponly"]else[]inletattrs=ifc.securethen"secure"::attrselseattrsinletattrs=matchc.pathwithNone->attrs|Somep->("path="^p)::attrsinletattrs=matchc.domainwithNone->attrs|Somed->("domain="^d)::attrsinletattrs=matchc.expirationwith|`Session->attrs|`Max_ageage->("Max-Age="^(Int64.to_stringage))::attrsinletn,c=c.cookiein(* TODO: may be buggy, some UAs will ignore cookie-strings without '='*)letattrs=(n^(matchcwith""->""|v->"="^v))::attrsin("Set-Cookie",String.concat"; "attrs)letserialize?(version=`HTTP_1_0)c=matchversionwith|`HTTP_1_0->serialize_1_0c|`HTTP_1_1->serialize_1_1c(* TODO: implement *)letextract_1_1_cstralist=alistletextract_1_0cstralist=letattrs=Stringext.split_trim_leftcstr~on:",;"~trim:" \t"inletattrs=List.map(funattr->matchStringext.split~on:'='attrwith|[]->("","")|n::v->(n,String.concat"="v))attrsintryletcookie=List.hdattrsinletattrs=List.map(fun(n,v)->(String.lowercase_asciin,v))(List.tlattrs)inletpath=tryletv=List.assoc"path"attrsinifv=""||v.[0]<>'/'thenraiseNot_foundelseSomevwithNot_found->Noneinletdomain=tryletv=List.assoc"domain"attrsinifv=""thenraiseNot_foundelseSome(String.lowercase_ascii(ifv.[0]='.'thenStringext.string_afterv1elsev))withNot_found->Nonein(* TODO: trim wsp *)(fstcookie,{cookie;(* TODO: respect expires attribute *)expiration=`Session;domain;path;http_only=List.mem_assoc"httponly"attrs;secure=List.mem_assoc"secure"attrs;})::alistwithFailure_->alist(* TODO: check dupes+order *)letextracthdr=Header.fold(function|"set-cookie"->extract_1_0|"set-cookie2"->extract_1_1|_->(fun_a->a))hdr[]letvalue{cookie=(_,v);_}=vendmoduleCookie_hdr=struct(* RFC 2965 has
cookie = "Cookie:" cookie-version 1*((";" | ",") cookie-value)
cookie-value = NAME "=" VALUE [";" path] [";" domain] [";" port]
cookie-version = "$Version" "=" value
NAME = attr
VALUE = value
path = "$Path" "=" value
domain = "$Domain" "=" value
port = "$Port" [ "=" <"> value <"> ]
*)letextracthdr=List.fold_left(funaccheader->letcomps=Stringext.split_trim_left~on:";"~trim:" \t"headerin(* We don't handle $Path, $Domain, $Port, $Version (or $anything
$else) *)letcookies=List.filter(funs->String.lengths>0&&s.[0]!='$')compsinletsplit_pairnvp=matchStringext.split~on:'='nvp~max:2with|[]->("","")|n::[]->(n,"")|n::v::_->(n,v)in(List.mapsplit_paircookies)@acc)[](Header.get_multihdr"cookie")letserializecookies="cookie",String.concat"; "(List.map(fun(k,v)->k^"="^v)cookies)end