summaryrefslogtreecommitdiff
path: root/src/couch/src/couch_httpd_misc_handlers.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/couch/src/couch_httpd_misc_handlers.erl')
-rw-r--r--src/couch/src/couch_httpd_misc_handlers.erl313
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.