WIP: port to webmachine
diff --git a/src/config.erl b/src/config.erl
index d123b12..78176c0 100644
--- a/src/config.erl
+++ b/src/config.erl
@@ -23,6 +23,7 @@
 -export([start_link/1, stop/0, reload/0]).
 
 -export([all/0]).
+-export([all_list/0, section_list/1]).
 -export([get/1, get/2, get/3]).
 -export([set/3, set/4, set/5]).
 -export([delete/2, delete/3, delete/4]).
@@ -37,6 +38,8 @@
 -export([init/1, terminate/2, code_change/3]).
 -export([handle_call/3, handle_cast/2, handle_info/2]).
 
+-export([couch_dispatch/0]).
+
 -record(config, {
     notify_funs=[],
     ini_files=undefined,
@@ -57,6 +60,23 @@
 all() ->
     lists:sort(gen_server:call(?MODULE, all, infinity)).
 
+all_list() ->
+    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()),
+    dict:fold(fun(Section, Values, Acc) ->
+        [{list_to_binary(Section), {Values}} | Acc]
+    end, [], Grouped).
+
+section_list(Section) ->
+    [{list_to_binary(Key), list_to_binary(Value)}
+        || {Key, Value} <- config:get(Section)].
+
 get_integer(Section, Key, Default) when is_integer(Default) ->
     try
         to_integer(get(Section, Key, Default))
@@ -347,3 +367,6 @@
             ok
     end.
 
+
+couch_dispatch() ->
+    {["_config", '*'], config_httpr, []}.
diff --git a/src/config_httpr.erl b/src/config_httpr.erl
new file mode 100644
index 0000000..f4f4019
--- /dev/null
+++ b/src/config_httpr.erl
@@ -0,0 +1,110 @@
+% 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(config_httpr).
+
+
+-export([
+    init/1,
+    allowed_methods/2,
+    content_types_provided/2,
+    content_types_accepted/2,
+    resource_exists/2,
+    delete_resource/2,
+    to_json/2,
+    from_json/2
+]).
+
+
+-include_lib("webmachine/include/webmachine.hrl").
+-include_lib("chttpd2/include/chttpd2.hrl").
+
+
+-spec init(list()) -> {ok, term()}.
+init([]) ->
+    {ok, undefined}.
+
+
+content_types_provided(ReqData, Context) ->
+    {[{"application/json", to_json}], ReqData, Context}.
+
+
+content_types_accepted(ReqData, Context) ->
+    {[{"application/json", from_json}], ReqData, Context}.
+
+
+allowed_methods(ReqData, Context) ->
+    {['GET', 'PUT', 'DELETE'], ReqData, Context}.
+
+
+resource_exists(ReqData, Context) ->
+    {true, ReqData, Context}.
+
+
+-spec to_json(wrq:reqdata(), term()) -> {iodata(), wrq:reqdata(), term()}.
+to_json(ReqData, State) ->
+    Parts = wrq:path_tokens(ReqData),
+    Data = get_config(Parts),
+    couch_log:error("PATH IS(~p): ~p", [Parts, Data]),
+    {?JSON_ENCODE(Data), ReqData, State}.
+
+
+-spec from_json(wrq:reqdata(), term()) -> {iodata(), wrq:reqdata(), term()}.
+from_json(ReqData, State) ->
+    %% how to handle intentional pattern matching fail on malformed requests?
+    [Section, Key] = wrq:path_tokens(ReqData),
+    %% this will also throw an error when body is undefined
+    Value = chttpd2_util:json_body(ReqData),
+    Persist = wrq:get_req_header("X-Couch-Persist", ReqData) /= "false",
+    OldValue = config:get(Section, Key, ""),
+    ok = config:set(Section, Key, binary_to_list(Value), Persist),
+    couch_log:error("Setting value(~p): ~p [~p]", [Persist, Value, OldValue]),
+    Resp = wrq:set_resp_body(?JSON_ENCODE(list_to_binary(OldValue)), ReqData),
+    Resp1 = wrq:set_resp_header("Location", "/_config/foo/baroob", Resp),
+    {true, Resp1, State}.
+
+
+delete_resource(ReqData, State) ->
+    %% how to handle intentional pattern matching fail on malformed requests?
+    [Section, Key] = wrq:path_tokens(ReqData),
+    Persist = wrq:get_req_header("X-Couch-Persist", ReqData) /= "false",
+    case config:get(Section, Key, null) of
+        %% why does this clause throw a 500?
+        null ->
+            {false, ReqData, State};
+        OldValue ->
+            config:delete(Section, Key, Persist),
+            Resp = wrq:set_resp_body(
+                ?JSON_ENCODE(list_to_binary(OldValue)),
+                ReqData
+            ),
+            {true, Resp, State}
+    end.
+
+
+get_config([]) ->
+    {config:all_list()};
+get_config([Section]) ->
+    {config:section_list(Section)};
+get_config([Section, Key]) ->
+    case config:get(Section, Key, null) of
+    null ->
+        %% move this to resource_exists/2?
+        throw({not_found, unknown_config_value});
+    Value ->
+        %% Should we turn this into a proper json value?
+        list_to_binary(Value)
+    end.
+
+
+
+