%%% Copyright (c) 2014-2015, NORDUnet A/S. %%% See LICENSE for licensing information. %%% @doc Storage node API -module(storage). %% API (URL) -export([request/4]). -define(APPURL_PLOP_STORAGE, "plop/v1/storage"). reportnewentries() -> NodeName = http_auth:own_name(), {LastIndexOrZero, LastHash} = storagedb:lastverifiednewentry(), VerifiedEntries = case LastHash of none -> 0; _ -> LastIndexOrZero + 1 end, NewEntries = index:indexsize(newentries_db) - VerifiedEntries, statusreport:report("storage", NodeName, "newentries", NewEntries). request(post, ?APPURL_PLOP_STORAGE, "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_entry_sync(TreeLeafHash, LogEntry), ok = storagedb:add(TreeLeafHash), {KeyName, Sig} = http_auth:sign_stored(plop:spt_data_from_entry(LogEntry)), reportnewentries(), success({[{result, <<"ok">>}, {"sig", KeyName ++ ":" ++ base64:encode_to_string(Sig)} ]}) end; request(post, ?APPURL_PLOP_STORAGE, "entrycommitted", Input) -> case (catch mochijson2:decode(Input)) of {error, E} -> html("entrycommitted: bad input:", E); {struct, PropList} -> EntryHash = base64:decode(proplists:get_value(<<"entryhash">>, PropList)), LeafHash = base64:decode(proplists:get_value(<<"treeleafhash">>, PropList)), db:add_entryhash(LeafHash, EntryHash), db:commit_entryhash(), case proplists:get_value(<<"timestamp_signature">>, PropList) of undefined -> none; TimestampSignature -> plop:add_spt_sig(LeafHash, base64:decode(TimestampSignature)) end, success({[{result, <<"ok">>}]}) end; request(get, ?APPURL_PLOP_STORAGE, "fetchnewentries", _Input) -> {LastIndexOrZero, LastHash} = storagedb:lastverifiednewentry(), LastVerifiedAndNewHashes = storagedb:fetchnewhashes(LastIndexOrZero), NewHashes = case {LastHash, LastVerifiedAndNewHashes} of {none, LastVerifiedAndNewHashes} -> LastVerifiedAndNewHashes; {Hash1, [Hash2|Rest]} when Hash1 == Hash2 -> Rest; _ -> exit({internalerror, "Incorrect lastverifiedentry"}) end, Entries = lists:map(fun(LeafHash) -> base64:encode(LeafHash) end, NewHashes), reportnewentries(), success({[{result, <<"ok">>}, {entries, Entries}]}); request(get, ?APPURL_PLOP_STORAGE, "getentry", Query) -> Hashes = [base64:decode(Value) || {Key, Value} <- Query, Key == "hash"], Entries = lists:map(fun(LeafHash) -> {[{hash, base64:encode(LeafHash)}, {entry, base64:encode(case db:entry_for_leafhash(LeafHash) of noentry -> <<>>; Entry -> Entry end)}]} end, Hashes), success({[{result, <<"ok">>}, {entries, Entries}]}). %% 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)}.