%% index_handler.erl -module(index_handler). -behaviour(cowboy_handler). -include("db_safe_insert.hrl"). -export([init/2]). init(Req, State) -> {Body, Req2} = read_full_body(Req), case json_validate:validate_activity(Body) of ok -> %% Decodifica il JSON già validato Activity0 = jsx:decode(Body, [return_maps]), Host = cowboy_req:host(Req2), Activity = ensure_id(Activity0, Host), To = maps:get(<<"to">>, Activity, []), case users_local_check:has_local_recipient(To) of true -> log_handler:error("Delivered ~p", [Req2]), cowboy_req:reply(200, #{}, <<"Delivered to local user">>, Req2), {ok, Req2, State}; false -> %% SAFE INSERT NELLA GLOBAL INBOX InsertResult = db_safe_insert:safe_insert(global_message, #global_message{ id = make_ref(), activity = Activity, timestamp = erlang:system_time(second) }), case InsertResult of ok -> log_handler:error("Saved in INBOX database ~p", [Req2]), cowboy_req:reply(202, #{}, <<"Saved to global inbox">>, Req2), {ok, Req2, State}; {aborted, Reason} -> logger:error("DB aborted: ~p", [Reason]), cowboy_req:reply(500, #{}, <<"Database error">>, Req2), {ok, Req2, State}; Other -> logger:error("DB unknown error: ~p", [Other]), cowboy_req:reply(500, #{}, <<"Unknown DB error">>, Req2), {ok, Req2, State} end end; {error, malformed_json} -> log_handler:error("Malformed JSON ricevuto da ~p", [Req2]), cowboy_req:reply(400, #{}, <<"Malformed JSON">>, Req2), {ok, Req2, State}; {error, missing_type} -> cowboy_req:reply(400, #{}, <<"Missing 'type' field">>, Req2), {ok, Req2, State}; {error, {unsupported_type, TypeBin}} -> Msg = <<"Unsupported type: ", TypeBin/binary>>, cowboy_req:reply(400, #{}, Msg, Req2), {ok, Req2, State}; {error, invalid_type_field} -> cowboy_req:reply(400, #{}, <<"Invalid 'type' field">>, Req2), {ok, Req2, State}; {error, Errors} -> ErrorMsg = io_lib:format("Invalid ActivityPub message: ~p", [Errors]), cowboy_req:reply(400, #{}, list_to_binary(ErrorMsg), Req2), {ok, Req2, State} end. %% Funzione di utilità per leggere tutto il body read_full_body(Req) -> case cowboy_req:read_body(Req) of {ok, Body, Req2} -> {Body, Req2}; {more, Data, Req2} -> {Rest, FinalReq} = read_full_body(Req2), {<>, FinalReq} end. ensure_id(Activity, Host) -> case maps:is_key(<<"id">>, Activity) of true -> Activity; false -> UUID = uuid_v4(), UUIDBin = list_to_binary(UUID), Id = <<"https://", Host/binary, "/activity/", UUIDBin/binary>>, maps:put(<<"id">>, Id, Activity) end. uuid_v4() -> <> = crypto:strong_rand_bytes(16), % Imposta i bit di versione e variant secondo lo standard UUID v4 C1 = (C band 16#0fff) bor 16#4000, D1 = (D band 16#3fff) bor 16#8000, lists:flatten( io_lib:format("~8.16.0b-~4.16.0b-~4.16.0b-~4.16.0b-~12.16.0b", [A, B, C1, D1, E]) ).