%%% Copyright (c) 2014, NORDUnet A/S. %%% See LICENSE for licensing information. %%% @doc Frontend node API -module(frontend). %% API (URL) -export([request/3]). request(post, "ct/frontend/sendentry", Input) -> case (catch mochijson2:decode(Input)) of {error, E} -> html("sendentry: bad input:", E); {struct, PropList} -> LogEntry = base64:decode(proplists:get_value(<<"entry">>, PropList)), TreeLeafHash = base64:decode(proplists:get_value(<<"treeleafhash">>, PropList)), ok = db:add(TreeLeafHash, LogEntry), success({[{result, <<"ok">>}]}) end; request(post, "ct/frontend/sendlog", Input) -> case (catch mochijson2:decode(Input)) of {error, E} -> html("sendentry: bad input:", E); {struct, PropList} -> Start = proplists:get_value(<<"start">>, PropList), Hashes = lists:map(fun (S) -> base64:decode(S) end, proplists:get_value(<<"hashes">>, PropList)), Indices = lists:seq(Start, Start + length(Hashes) - 1), lists:foreach(fun ({Hash, Index}) -> ok = db:add_index(Hash, Index) end, lists:zip(Hashes, Indices)), success({[{result, <<"ok">>}]}) end; request(post, "ct/frontend/sendsth", Input) -> case (catch mochijson2:decode(Input)) of {error, E} -> html("sendentry: bad input:", E); {struct, PropList} -> OldSize = db:size(), Treesize = proplists:get_value(<<"tree_size">>, PropList), Indexsize = db:indexsize(), if Treesize < OldSize -> html("Size is older than current size", OldSize); Treesize == OldSize -> success({[{result, <<"ok">>}]}); Treesize > Indexsize -> html("Has too few entries", Indexsize); true -> NewEntries = db:leafhash_for_indices(OldSize, Treesize - 1), lager:debug("old size: ~p new size: ~p entries: ~p", [OldSize, Treesize, NewEntries]), Errors = check_entries(NewEntries, OldSize, Treesize - 1), case Errors of [] -> ok = db:set_treesize(Treesize), ht:reset_tree([db:size() - 1]), success({[{result, <<"ok">>}]}); _ -> html("Database not complete", Errors) end end end; request(get, "ct/frontend/currentposition", _Query) -> Size = db:size(), success({[{result, <<"ok">>}, {position, Size}]}); request(get, "ct/frontend/missingentries", _Query) -> Size = db:size(), Missing = fetchmissingentries(Size), lager:debug("missingentries: ~p", [Missing]), success({[{result, <<"ok">>}, {entries, lists:map(fun (Entry) -> base64:encode(Entry) end, Missing)}]}). check_entries(Entries, Start, End) -> lists:foldl(fun ({Hash, Index}, Acc) -> case check_entry(Hash, Index) of ok -> Acc; Error -> [Error | Acc] end end, [], lists:zip(Entries, lists:seq(Start, End))). check_entry(Hash, Index) -> case db:get_by_leaf_hash(Hash) of notfound -> {notfound, Index}; _ -> ok end. fetchmissingentries(Index) -> lists:reverse(fetchmissingentries(Index, [])). fetchmissingentries(Index, Acc) -> lager:debug("index ~p", [Index]), case db:leafhash_for_index(Index) of noentry -> Acc; Hash -> case db:entry_for_leafhash(Hash) of noentry -> lager:debug("didn't find hash ~p", [Hash]), fetchmissingentries(Index + 1, [Hash | Acc]); _ -> fetchmissingentries(Index + 1, Acc) end end. %% Private functions. html(Text, Input) -> {400, [{"Content-Type", "text/html"}], io_lib:format( "

~n" ++ "~s~n" ++ "~p~n" ++ "~n", [Text, Input])}. success(Data) -> {200, [{"Content-Type", "text/json"}], mochijson2:encode(Data)}.