diff options
Diffstat (limited to 'src/couch/src/couch_httpd_misc_handlers.erl')
-rw-r--r-- | src/couch/src/couch_httpd_misc_handlers.erl | 313 |
1 files changed, 0 insertions, 313 deletions
diff --git a/src/couch/src/couch_httpd_misc_handlers.erl b/src/couch/src/couch_httpd_misc_handlers.erl deleted file mode 100644 index d9c591875..000000000 --- a/src/couch/src/couch_httpd_misc_handlers.erl +++ /dev/null @@ -1,313 +0,0 @@ -% Licensed under the Apache License, Version 2.0 (the "License"); you may not -% use this file except in compliance with the License. You may obtain a copy of -% the License at -% -% http://www.apache.org/licenses/LICENSE-2.0 -% -% Unless required by applicable law or agreed to in writing, software -% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -% License for the specific language governing permissions and limitations under -% the License. - --module(couch_httpd_misc_handlers). - --export([ - handle_welcome_req/2, - handle_favicon_req/2, - handle_utils_dir_req/2, - handle_all_dbs_req/1, - handle_uuids_req/1, - handle_config_req/1, - handle_task_status_req/1, - handle_file_req/2 -]). - --include_lib("couch/include/couch_db.hrl"). - --import( - couch_httpd, - [ - send_json/2, send_json/3, send_json/4, - send_method_not_allowed/2, - start_json_response/2, - send_chunk/2, - last_chunk/1, - end_json_response/1, - start_chunked_response/3, - send_error/4 - ] -). - -% httpd global handlers - -handle_welcome_req(#httpd{method = 'GET'} = Req, WelcomeMessage) -> - send_json(Req, { - [ - {couchdb, WelcomeMessage}, - {uuid, couch_server:get_uuid()}, - {version, list_to_binary(couch_server:get_version())} - ] ++ - case config:get("vendor") of - [] -> - []; - Properties -> - [{vendor, {[{?l2b(K), ?l2b(V)} || {K, V} <- Properties]}}] - end - }); -handle_welcome_req(Req, _) -> - send_method_not_allowed(Req, "GET,HEAD"). - -handle_favicon_req(#httpd{method = 'GET'} = Req, DocumentRoot) -> - {{Year, Month, Day}, Time} = erlang:universaltime(), - OneYearFromNow = {{Year + 1, Month, Day}, Time}, - CachingHeaders = [ - %favicon should expire a year from now - {"Cache-Control", "public, max-age=31536000"}, - {"Expires", couch_util:rfc1123_date(OneYearFromNow)} - ], - couch_httpd:serve_file(Req, "favicon.ico", DocumentRoot, CachingHeaders); -handle_favicon_req(Req, _) -> - send_method_not_allowed(Req, "GET,HEAD"). - -handle_file_req(#httpd{method = 'GET'} = Req, Document) -> - couch_httpd:serve_file(Req, filename:basename(Document), filename:dirname(Document)); -handle_file_req(Req, _) -> - send_method_not_allowed(Req, "GET,HEAD"). - -handle_utils_dir_req(Req, _) -> - send_error( - Req, - 410, - <<"no_node_local_fauxton">>, - ?l2b("The web interface is no longer available on the node-local port.") - ). - -handle_all_dbs_req(#httpd{method = 'GET'} = Req) -> - {ok, DbNames} = couch_server:all_databases(), - send_json(Req, DbNames); -handle_all_dbs_req(Req) -> - send_method_not_allowed(Req, "GET,HEAD"). - -handle_task_status_req(#httpd{method = 'GET'} = Req) -> - ok = couch_httpd:verify_is_server_admin(Req), - % convert the list of prop lists to a list of json objects - send_json(Req, [{Props} || Props <- couch_task_status:all()]); -handle_task_status_req(Req) -> - send_method_not_allowed(Req, "GET,HEAD"). - -handle_uuids_req(#httpd{method = 'GET'} = Req) -> - Max = config:get_integer("uuids", "max_count", 1000), - Count = - try list_to_integer(couch_httpd:qs_value(Req, "count", "1")) of - N when N > Max -> - throw({bad_request, <<"count parameter too large">>}); - N when N < 0 -> - throw({bad_request, <<"count must be a positive integer">>}); - N -> - N - catch - error:badarg -> - throw({bad_request, <<"count must be a positive integer">>}) - end, - UUIDs = [couch_uuids:new() || _ <- lists:seq(1, Count)], - Etag = couch_httpd:make_etag(UUIDs), - couch_httpd:etag_respond(Req, Etag, fun() -> - CacheBustingHeaders = [ - {"Date", couch_util:rfc1123_date()}, - {"Cache-Control", "no-cache"}, - % Past date, ON PURPOSE! - {"Expires", "Mon, 01 Jan 1990 00:00:00 GMT"}, - {"Pragma", "no-cache"}, - {"ETag", Etag} - ], - send_json(Req, 200, CacheBustingHeaders, {[{<<"uuids">>, UUIDs}]}) - end); -handle_uuids_req(Req) -> - send_method_not_allowed(Req, "GET"). - -% Config request handler - -% GET /_config/ -% GET /_config -handle_config_req(#httpd{method = 'GET', path_parts = [_]} = Req) -> - ok = couch_httpd:verify_is_server_admin(Req), - Grouped = lists:foldl( - fun({{Section, Key}, Value}, Acc) -> - case dict:is_key(Section, Acc) of - true -> - dict:append(Section, {list_to_binary(Key), list_to_binary(Value)}, Acc); - false -> - dict:store(Section, [{list_to_binary(Key), list_to_binary(Value)}], Acc) - end - end, - dict:new(), - config:all() - ), - KVs = dict:fold( - fun(Section, Values, Acc) -> - [{list_to_binary(Section), {Values}} | Acc] - end, - [], - Grouped - ), - send_json(Req, 200, {KVs}); -% GET /_config/Section -handle_config_req(#httpd{method = 'GET', path_parts = [_, Section]} = Req) -> - ok = couch_httpd:verify_is_server_admin(Req), - KVs = [ - {list_to_binary(Key), list_to_binary(Value)} - || {Key, Value} <- config:get(Section) - ], - send_json(Req, 200, {KVs}); -% GET /_config/Section/Key -handle_config_req(#httpd{method = 'GET', path_parts = [_, Section, Key]} = Req) -> - ok = couch_httpd:verify_is_server_admin(Req), - case config:get(Section, Key, undefined) of - undefined -> - throw({not_found, unknown_config_value}); - Value -> - send_json(Req, 200, list_to_binary(Value)) - end; -% POST /_config/_reload - Flushes unpersisted config values from RAM -handle_config_req(#httpd{method = 'POST', path_parts = [_, <<"_reload">>]} = Req) -> - couch_httpd:validate_ctype(Req, "application/json"), - _ = couch_httpd:body(Req), - ok = couch_httpd:verify_is_server_admin(Req), - ok = config:reload(), - send_json(Req, 200, {[{ok, true}]}); -% PUT or DELETE /_config/Section/Key -handle_config_req(#httpd{method = Method, path_parts = [_, Section, Key]} = Req) when - (Method == 'PUT') or (Method == 'DELETE') --> - ok = couch_httpd:verify_is_server_admin(Req), - couch_util:check_config_blacklist(Section), - Persist = couch_httpd:header_value(Req, "X-Couch-Persist") /= "false", - case chttpd_util:get_chttpd_config("config_whitelist") of - undefined -> - % No whitelist; allow all changes. - handle_approved_config_req(Req, Persist); - WhitelistValue -> - % Provide a failsafe to protect against inadvertently locking - % onesself out of the config by supplying a syntactically-incorrect - % Erlang term. To intentionally lock down the whitelist, supply a - % well-formed list which does not include the whitelist config - % variable itself. - FallbackWhitelist = [{<<"chttpd">>, <<"config_whitelist">>}], - - Whitelist = - case couch_util:parse_term(WhitelistValue) of - {ok, Value} when is_list(Value) -> - Value; - {ok, _NonListValue} -> - FallbackWhitelist; - {error, _} -> - [{WhitelistSection, WhitelistKey}] = FallbackWhitelist, - couch_log:error( - "Only whitelisting ~s/~s due to error" - " parsing: ~p", - [ - WhitelistSection, - WhitelistKey, - WhitelistValue - ] - ), - FallbackWhitelist - end, - - IsRequestedKeyVal = fun(Element) -> - case Element of - {A, B} -> - % For readability, tuples may be used instead of binaries - % in the whitelist. - case {couch_util:to_binary(A), couch_util:to_binary(B)} of - {Section, Key} -> - true; - {Section, <<"*">>} -> - true; - _Else -> - false - end; - _Else -> - false - end - end, - - case lists:any(IsRequestedKeyVal, Whitelist) of - true -> - % Allow modifying this whitelisted variable. - handle_approved_config_req(Req, Persist); - _NotWhitelisted -> - % Disallow modifying this non-whitelisted variable. - send_error( - Req, - 400, - <<"modification_not_allowed">>, - ?l2b("This config variable is read-only") - ) - end - end; -handle_config_req(Req) -> - send_method_not_allowed(Req, "GET,PUT,POST,DELETE"). - -% PUT /_config/Section/Key -% "value" -handle_approved_config_req(Req, Persist) -> - Query = couch_httpd:qs(Req), - UseRawValue = - case lists:keyfind("raw", 1, Query) of - % Not specified - false -> false; - % Specified with no value, i.e. "?raw" and "?raw=" - {"raw", ""} -> false; - {"raw", "false"} -> false; - {"raw", "true"} -> true; - {"raw", InvalidValue} -> InvalidValue - end, - handle_approved_config_req(Req, Persist, UseRawValue). - -handle_approved_config_req( - #httpd{method = 'PUT', path_parts = [_, Section, Key]} = Req, - Persist, - UseRawValue -) when - UseRawValue =:= false orelse UseRawValue =:= true --> - RawValue = couch_httpd:json_body(Req), - Value = - case UseRawValue of - true -> - % Client requests no change to the provided value. - RawValue; - false -> - % Pre-process the value as necessary. - case Section of - <<"admins">> -> - couch_passwords:hash_admin_password(RawValue); - _ -> - couch_util:trim(RawValue) - end - end, - OldValue = config:get(Section, Key, ""), - case config:set(Section, Key, ?b2l(Value), Persist) of - ok -> - send_json(Req, 200, list_to_binary(OldValue)); - Error -> - throw(Error) - end; -handle_approved_config_req(#httpd{method = 'PUT'} = Req, _Persist, UseRawValue) -> - Err = io_lib:format("Bad value for 'raw' option: ~s", [UseRawValue]), - send_json(Req, 400, {[{error, ?l2b(Err)}]}); -% DELETE /_config/Section/Key -handle_approved_config_req( - #httpd{method = 'DELETE', path_parts = [_, Section, Key]} = Req, - Persist, - _UseRawValue -) -> - case config:get(Section, Key, undefined) of - undefined -> - throw({not_found, unknown_config_value}); - OldValue -> - config:delete(Section, Key, Persist), - send_json(Req, 200, list_to_binary(OldValue)) - end. |