summaryrefslogtreecommitdiff
path: root/src/couch_inets/http_cookie.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch_inets/http_cookie.erl')
-rw-r--r--src/couch_inets/http_cookie.erl389
1 files changed, 0 insertions, 389 deletions
diff --git a/src/couch_inets/http_cookie.erl b/src/couch_inets/http_cookie.erl
deleted file mode 100644
index a8e68651..00000000
--- a/src/couch_inets/http_cookie.erl
+++ /dev/null
@@ -1,389 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%% Description: Cookie handling according to RFC 2109
-
--module(http_cookie).
-
--include("httpc_internal.hrl").
-
--export([header/4, cookies/3, open_cookie_db/1, close_cookie_db/1, insert/2]).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-header(Scheme, {Host, _}, Path, CookieDb) ->
- case lookup_cookies(Host, Path, CookieDb) of
- [] ->
- {"cookie", ""};
- Cookies ->
- {"cookie", cookies_to_string(Scheme, Cookies)}
- end.
-
-cookies(Headers, RequestPath, RequestHost) ->
- Cookies = parse_set_cookies(Headers, {RequestPath, RequestHost}),
- accept_cookies(Cookies, RequestPath, RequestHost).
-
-open_cookie_db({{_, only_session_cookies}, SessionDbName}) ->
- EtsDb = ets:new(SessionDbName, [protected, bag,
- {keypos, #http_cookie.domain}]),
- {undefined, EtsDb};
-
-open_cookie_db({{DbName, Dbdir}, SessionDbName}) ->
- File = filename:join(Dbdir, atom_to_list(DbName)),
- {ok, DetsDb} = dets:open_file(DbName, [{keypos, #http_cookie.domain},
- {type, bag},
- {file, File},
- {ram_file, true}]),
- EtsDb = ets:new(SessionDbName, [protected, bag,
- {keypos, #http_cookie.domain}]),
- {DetsDb, EtsDb}.
-
-close_cookie_db({undefined, EtsDb}) ->
- ets:delete(EtsDb);
-
-close_cookie_db({DetsDb, EtsDb}) ->
- dets:close(DetsDb),
- ets:delete(EtsDb).
-
-%% If no persistent cookie database is defined we
-%% treat all cookies as if they where session cookies.
-insert(Cookie = #http_cookie{max_age = Int},
- Dbs = {undefined, _}) when is_integer(Int) ->
- insert(Cookie#http_cookie{max_age = session}, Dbs);
-
-insert(Cookie = #http_cookie{domain = Key, name = Name,
- path = Path, max_age = session},
- Db = {_, CookieDb}) ->
- case ets:match_object(CookieDb, #http_cookie{domain = Key,
- name = Name,
- path = Path,
- _ = '_'}) of
- [] ->
- ets:insert(CookieDb, Cookie);
- [NewCookie] ->
- delete(NewCookie, Db),
- ets:insert(CookieDb, Cookie)
- end,
- ok;
-insert(#http_cookie{domain = Key, name = Name,
- path = Path, max_age = 0},
- Db = {CookieDb, _}) ->
- case dets:match_object(CookieDb, #http_cookie{domain = Key,
- name = Name,
- path = Path,
- _ = '_'}) of
- [] ->
- ok;
- [NewCookie] ->
- delete(NewCookie, Db)
- end,
- ok;
-insert(Cookie = #http_cookie{domain = Key, name = Name, path = Path},
- Db = {CookieDb, _}) ->
- case dets:match_object(CookieDb, #http_cookie{domain = Key,
- name = Name,
- path = Path,
- _ = '_'}) of
- [] ->
- dets:insert(CookieDb, Cookie);
- [NewCookie] ->
- delete(NewCookie, Db),
- dets:insert(CookieDb, Cookie)
- end,
- ok.
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-lookup_cookies(Key, {undefined, Ets}) ->
- ets:match_object(Ets, #http_cookie{domain = Key,
- _ = '_'});
-lookup_cookies(Key, {Dets,Ets}) ->
- SessionCookies = ets:match_object(Ets, #http_cookie{domain = Key,
- _ = '_'}),
- Cookies = dets:match_object(Dets, #http_cookie{domain = Key,
- _ = '_'}),
- Cookies ++ SessionCookies.
-
-delete(Cookie = #http_cookie{max_age = session}, {_, CookieDb}) ->
- ets:delete_object(CookieDb, Cookie);
-delete(Cookie, {CookieDb, _}) ->
- dets:delete_object(CookieDb, Cookie).
-
-lookup_cookies(Host, Path, Db) ->
- Cookies =
- case http_util:is_hostname(Host) of
- true ->
- HostCookies = lookup_cookies(Host, Db),
- [_| DomainParts] = string:tokens(Host, "."),
- lookup_domain_cookies(DomainParts, Db, HostCookies);
- false -> % IP-adress
- lookup_cookies(Host, Db)
- end,
- ValidCookies = valid_cookies(Cookies, [], Db),
- lists:filter(fun(Cookie) ->
- lists:prefix(Cookie#http_cookie.path, Path)
- end, ValidCookies).
-
-%% For instance if Host=localhost
-lookup_domain_cookies([], _, AccCookies) ->
- lists:flatten(AccCookies);
-%% Top domains can not have cookies
-lookup_domain_cookies([_], _, AccCookies) ->
- lists:flatten(AccCookies);
-lookup_domain_cookies([Next | DomainParts], CookieDb, AccCookies) ->
- Domain = merge_domain_parts(DomainParts, [Next ++ "."]),
- lookup_domain_cookies(DomainParts, CookieDb,
- [lookup_cookies(Domain, CookieDb)
- | AccCookies]).
-
-merge_domain_parts([Part], Merged) ->
- lists:flatten(["." | lists:reverse([Part | Merged])]);
-merge_domain_parts([Part| Rest], Merged) ->
- merge_domain_parts(Rest, [".", Part | Merged]).
-
-cookies_to_string(Scheme, Cookies = [Cookie | _]) ->
- Version = "$Version=" ++ Cookie#http_cookie.version ++ "; ",
- cookies_to_string(Scheme, path_sort(Cookies), [Version]).
-
-cookies_to_string(_, [], CookieStrs) ->
- case length(CookieStrs) of
- 1 ->
- "";
- _ ->
- lists:flatten(lists:reverse(CookieStrs))
- end;
-
-cookies_to_string(https, [Cookie = #http_cookie{secure = true}| Cookies],
- CookieStrs) ->
- Str = case Cookies of
- [] ->
- cookie_to_string(Cookie);
- _ ->
- cookie_to_string(Cookie) ++ "; "
- end,
- cookies_to_string(https, Cookies, [Str | CookieStrs]);
-
-cookies_to_string(Scheme, [#http_cookie{secure = true}| Cookies],
- CookieStrs) ->
- cookies_to_string(Scheme, Cookies, CookieStrs);
-
-cookies_to_string(Scheme, [Cookie | Cookies], CookieStrs) ->
- Str = case Cookies of
- [] ->
- cookie_to_string(Cookie);
- _ ->
- cookie_to_string(Cookie) ++ "; "
- end,
- cookies_to_string(Scheme, Cookies, [Str | CookieStrs]).
-
-cookie_to_string(Cookie = #http_cookie{name = Name, value = Value}) ->
- Str = Name ++ "=" ++ Value,
- add_domain(add_path(Str, Cookie), Cookie).
-
-add_path(Str, #http_cookie{path_default = true}) ->
- Str;
-add_path(Str, #http_cookie{path = Path}) ->
- Str ++ "; $Path=" ++ Path.
-
-add_domain(Str, #http_cookie{domain_default = true}) ->
- Str;
-add_domain(Str, #http_cookie{domain = Domain}) ->
- Str ++ "; $Domain=" ++ Domain.
-
-parse_set_cookies(OtherHeaders, DefaultPathDomain) ->
- SetCookieHeaders = lists:foldl(fun({"set-cookie", Value}, Acc) ->
- [string:tokens(Value, ",")| Acc];
- (_, Acc) ->
- Acc
- end, [], OtherHeaders),
-
- lists:flatten(lists:map(fun(CookieHeader) ->
- NewHeader =
- fix_netscape_cookie(CookieHeader,
- []),
- parse_set_cookie(NewHeader, [],
- DefaultPathDomain) end,
- SetCookieHeaders)).
-
-parse_set_cookie([], AccCookies, _) ->
- AccCookies;
-parse_set_cookie([CookieHeader | CookieHeaders], AccCookies,
- Defaults = {DefaultPath, DefaultDomain}) ->
- [CookieStr | Attributes] = case string:tokens(CookieHeader, ";") of
- [CStr] ->
- [CStr, ""];
- [CStr | Attr] ->
- [CStr, Attr]
- end,
- Pos = string:chr(CookieStr, $=),
- Name = string:substr(CookieStr, 1, Pos - 1),
- Value = string:substr(CookieStr, Pos + 1),
- Cookie = #http_cookie{name = string:strip(Name),
- value = string:strip(Value)},
- NewAttributes = parse_set_cookie_attributes(Attributes),
- TmpCookie = cookie_attributes(NewAttributes, Cookie),
- %% Add runtime defult values if necessary
- NewCookie = domain_default(path_default(TmpCookie, DefaultPath),
- DefaultDomain),
- parse_set_cookie(CookieHeaders, [NewCookie | AccCookies], Defaults).
-
-parse_set_cookie_attributes([]) ->
- [];
-parse_set_cookie_attributes([Attributes]) ->
- lists:map(fun(Attr) ->
- [AttrName, AttrValue] =
- case string:tokens(Attr, "=") of
- %% All attributes have the form
- %% Name=Value except "secure"!
- [Name] ->
- [Name, ""];
- [Name, Value] ->
- [Name, Value];
- %% Anything not expected will be
- %% disregarded
- _ ->
- ["Dummy",""]
- end,
- {http_util:to_lower(string:strip(AttrName)),
- string:strip(AttrValue)}
- end, Attributes).
-
-cookie_attributes([], Cookie) ->
- Cookie;
-cookie_attributes([{"comment", Value}| Attributes], Cookie) ->
- cookie_attributes(Attributes,
- Cookie#http_cookie{comment = Value});
-cookie_attributes([{"domain", Value}| Attributes], Cookie) ->
- cookie_attributes(Attributes,
- Cookie#http_cookie{domain = Value});
-cookie_attributes([{"max-age", Value}| Attributes], Cookie) ->
- ExpireTime = cookie_expires(list_to_integer(Value)),
- cookie_attributes(Attributes,
- Cookie#http_cookie{max_age = ExpireTime});
-%% Backwards compatibility with netscape cookies
-cookie_attributes([{"expires", Value}| Attributes], Cookie) ->
- Time = http_util:convert_netscapecookie_date(Value),
- ExpireTime = calendar:datetime_to_gregorian_seconds(Time),
- cookie_attributes(Attributes,
- Cookie#http_cookie{max_age = ExpireTime});
-cookie_attributes([{"path", Value}| Attributes], Cookie) ->
- cookie_attributes(Attributes,
- Cookie#http_cookie{path = Value});
-cookie_attributes([{"secure", _}| Attributes], Cookie) ->
- cookie_attributes(Attributes,
- Cookie#http_cookie{secure = true});
-cookie_attributes([{"version", Value}| Attributes], Cookie) ->
- cookie_attributes(Attributes,
- Cookie#http_cookie{version = Value});
-%% Disregard unknown attributes.
-cookie_attributes([_| Attributes], Cookie) ->
- cookie_attributes(Attributes, Cookie).
-
-domain_default(Cookie = #http_cookie{domain = undefined},
- DefaultDomain) ->
- Cookie#http_cookie{domain = DefaultDomain, domain_default = true};
-domain_default(Cookie, _) ->
- Cookie.
-
-path_default(Cookie = #http_cookie{path = undefined},
- DefaultPath) ->
- Cookie#http_cookie{path = skip_right_most_slash(DefaultPath),
- path_default = true};
-path_default(Cookie, _) ->
- Cookie.
-
-%% Note: if the path is only / that / will be keept
-skip_right_most_slash("/") ->
- "/";
-skip_right_most_slash(Str) ->
- string:strip(Str, right, $/).
-
-accept_cookies(Cookies, RequestPath, RequestHost) ->
- lists:filter(fun(Cookie) ->
- accept_cookie(Cookie, RequestPath, RequestHost)
- end, Cookies).
-
-accept_cookie(Cookie, RequestPath, RequestHost) ->
- accept_path(Cookie, RequestPath) and accept_domain(Cookie, RequestHost).
-
-accept_path(#http_cookie{path = Path}, RequestPath) ->
- lists:prefix(Path, RequestPath).
-
-accept_domain(#http_cookie{domain = RequestHost}, RequestHost) ->
- true;
-
-accept_domain(#http_cookie{domain = Domain}, RequestHost) ->
- HostCheck = case http_util:is_hostname(RequestHost) of
- true ->
- (lists:suffix(Domain, RequestHost) andalso
- (not
- lists:member($.,
- string:substr(RequestHost, 1,
- (length(RequestHost) -
- length(Domain))))));
- false ->
- false
- end,
- HostCheck andalso (hd(Domain) == $.)
- andalso (length(string:tokens(Domain, ".")) > 1).
-
-cookie_expires(0) ->
- 0;
-cookie_expires(DeltaSec) ->
- NowSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
- NowSec + DeltaSec.
-
-is_cookie_expired(#http_cookie{max_age = session}) ->
- false;
-is_cookie_expired(#http_cookie{max_age = ExpireTime}) ->
- NowSec = calendar:datetime_to_gregorian_seconds({date(), time()}),
- ExpireTime - NowSec =< 0.
-
-valid_cookies([], Valid, _) ->
- Valid;
-
-valid_cookies([Cookie | Cookies], Valid, Db) ->
- case is_cookie_expired(Cookie) of
- true ->
- delete(Cookie, Db),
- valid_cookies(Cookies, Valid, Db);
- false ->
- valid_cookies(Cookies, [Cookie | Valid], Db)
- end.
-
-path_sort(Cookies)->
- lists:reverse(lists:keysort(#http_cookie.path, Cookies)).
-
-
-%% Informally, the Set-Cookie response header comprises the token
-%% Set-Cookie:, followed by a comma-separated list of one or more
-%% cookies. Netscape cookies expires attribute may also have a
-%% , in this case the header list will have been incorrectly split
-%% in parse_set_cookies/2 this functions fixs that problem.
-fix_netscape_cookie([Cookie1, Cookie2 | Rest], Acc) ->
- case regexp:match(Cookie1, "expires=") of
- {_, _, _} ->
- fix_netscape_cookie(Rest, [Cookie1 ++ Cookie2 | Acc]);
- nomatch ->
- fix_netscape_cookie([Cookie2 |Rest], [Cookie1| Acc])
- end;
-fix_netscape_cookie([Cookie | Rest], Acc) ->
- fix_netscape_cookie(Rest, [Cookie | Acc]);
-
-fix_netscape_cookie([], Acc) ->
- Acc.