Compare commits
No commits in common. "b6735ee54d81afe68ef4fd8e3adb168eecdf7846" and "774a626af67319f0dcebb4744cf8ca6473df2570" have entirely different histories.
b6735ee54d
...
774a626af6
|
@ -3,14 +3,13 @@
|
||||||
*.beam
|
*.beam
|
||||||
*.o
|
*.o
|
||||||
*.plt
|
*.plt
|
||||||
*.lock
|
|
||||||
|
|
||||||
# Dipendenze scaricate
|
# Dipendenze scaricate
|
||||||
/deps/
|
/deps/
|
||||||
|
|
||||||
# File di test e log
|
# File di test e log
|
||||||
.eunit/
|
.eunit/
|
||||||
log/
|
logs/
|
||||||
erl_crash.dump
|
erl_crash.dump
|
||||||
|
|
||||||
# File generati da strumenti
|
# File generati da strumenti
|
||||||
|
|
|
@ -1,13 +1,4 @@
|
||||||
%% db_safe_insert.hrl
|
%% db_safe_insert.hrl
|
||||||
|
|
||||||
-record(global_message, {id, activity, timestamp}).
|
-record(global_message, {id, activity, timestamp}).
|
||||||
-record(ap_users, {
|
-record(ap_users, {id, name, email, created_at}).
|
||||||
id,
|
|
||||||
username,
|
|
||||||
domain,
|
|
||||||
profile_url,
|
|
||||||
actor_url,
|
|
||||||
email,
|
|
||||||
avatar_url,
|
|
||||||
created_at
|
|
||||||
}).
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
||||||
"title": "ActivityPub Note",
|
|
||||||
"type": "object",
|
|
||||||
"required": ["type", "id", "content"],
|
|
||||||
"properties": {
|
|
||||||
"@context": { "type": "string" },
|
|
||||||
"type": { "type": "string", "enum": ["Note"] },
|
|
||||||
"id": { "type": "string" },
|
|
||||||
"attributedTo": { "type": "string" },
|
|
||||||
"content": { "type": "string" },
|
|
||||||
"to": {
|
|
||||||
"type": "array",
|
|
||||||
"items": { "type": "string" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": true
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
{deps, [
|
{deps, [
|
||||||
jsonlog,
|
|
||||||
jesse,
|
|
||||||
{cowboy, "2.10.0"},
|
{cowboy, "2.10.0"},
|
||||||
{jsx, "3.1.0"}
|
{jsx, "3.1.0"}
|
||||||
]}.
|
]}.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{"1.2.0",
|
||||||
|
[{<<"cowboy">>,{pkg,<<"cowboy">>,<<"2.10.0">>},0},
|
||||||
|
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.12.1">>},1},
|
||||||
|
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},0},
|
||||||
|
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.8.0">>},1}]}.
|
||||||
|
[
|
||||||
|
{pkg_hash,[
|
||||||
|
{<<"cowboy">>, <<"FF9FFEFF91DAE4AE270DD975642997AFE2A1179D94B1887863E43F681A203E26">>},
|
||||||
|
{<<"cowlib">>, <<"A9FA9A625F1D2025FE6B462CB865881329B5CAFF8F1854D1CBC9F9533F00E1E1">>},
|
||||||
|
{<<"jsx">>, <<"D12516BAA0BB23A59BB35DCCAF02A1BD08243FCBB9EFE24F2D9D056CCFF71268">>},
|
||||||
|
{<<"ranch">>, <<"8C7A100A139FD57F17327B6413E4167AC559FBC04CA7448E9BE9057311597A1D">>}]},
|
||||||
|
{pkg_hash_ext,[
|
||||||
|
{<<"cowboy">>, <<"3AFDCCB7183CC6F143CB14D3CF51FA00E53DB9EC80CDCD525482F5E99BC41D6B">>},
|
||||||
|
{<<"cowlib">>, <<"163B73F6367A7341B33C794C4E88E7DBFE6498AC42DCD69EF44C5BC5507C8DB0">>},
|
||||||
|
{<<"jsx">>, <<"0C5CC8FDC11B53CC25CF65AC6705AD39E54ECC56D1C22E4ADB8F5A53FB9427F3">>},
|
||||||
|
{<<"ranch">>, <<"49FBCFD3682FAB1F5D109351B61257676DA1A2FDBE295904176D5E521A2DDFE5">>}]}
|
||||||
|
].
|
|
@ -10,7 +10,6 @@
|
||||||
start(_Type, _Args) ->
|
start(_Type, _Args) ->
|
||||||
|
|
||||||
%% creiamo un utente di test, solo per vedere se mnesia va
|
%% creiamo un utente di test, solo per vedere se mnesia va
|
||||||
log_handler:init(), %% va per primo, se no, non logga l'avvio
|
|
||||||
db_organization:setup(),
|
db_organization:setup(),
|
||||||
db_organization:init(),
|
db_organization:init(),
|
||||||
users_local_check:check_ENV(), %% controlla le variabili d'ambiente
|
users_local_check:check_ENV(), %% controlla le variabili d'ambiente
|
||||||
|
|
|
@ -26,7 +26,7 @@ remove_oldest(Tab, Fraction) ->
|
||||||
lists:foreach(fun(Rec) -> mnesia:dirty_delete_object(Rec) end, ToDelete).
|
lists:foreach(fun(Rec) -> mnesia:dirty_delete_object(Rec) end, ToDelete).
|
||||||
|
|
||||||
make_pattern(global_message) -> #global_message{id='_', activity='_', timestamp='_'};
|
make_pattern(global_message) -> #global_message{id='_', activity='_', timestamp='_'};
|
||||||
make_pattern(ap_users) -> #ap_users{id='_', username='_', email='_', created_at='_'}. %% <-- PUNTO QUI DIOCANE!
|
make_pattern(ap_users) -> #ap_users{id='_', name='_', email='_', created_at='_'}. %% <-- PUNTO QUI DIOCANE!
|
||||||
|
|
||||||
get_timestamp(#global_message{timestamp=T}) -> T;
|
get_timestamp(#global_message{timestamp=T}) -> T;
|
||||||
get_timestamp(#ap_users{created_at=T}) -> T. %% <-- PUNTO SOLO ALLA FINE, DIOCANE!
|
get_timestamp(#ap_users{created_at=T}) -> T. %% <-- PUNTO SOLO ALLA FINE, DIOCANE!
|
||||||
|
|
|
@ -1,66 +1,40 @@
|
||||||
%% index_handler.erl
|
%% index_handler.erl
|
||||||
-module(index_handler).
|
-module(index_handler).
|
||||||
-behaviour(cowboy_handler).
|
-behaviour(cowboy_handler).
|
||||||
-include("db_safe_insert.hrl").
|
|
||||||
-export([init/2]).
|
-export([init/2]).
|
||||||
|
|
||||||
init(Req, State) ->
|
init(Req, State) ->
|
||||||
{Body, Req2} = read_full_body(Req),
|
{ok, Body, Req2} = cowboy_req:read_body(Req),
|
||||||
case json_validate:validate_activity(Body) of
|
|
||||||
ok ->
|
|
||||||
%% Decodifica il JSON già validato
|
|
||||||
Activity = jsx:decode(Body, [return_maps]),
|
Activity = jsx:decode(Body, [return_maps]),
|
||||||
|
|
||||||
|
%% Validazione dei campi obbligatori
|
||||||
|
case validate_activity(Activity) of
|
||||||
|
false ->
|
||||||
|
{ok, Resp} = cowboy_req:reply(400, #{}, <<"Invalid ActivityPub message">>, Req2),
|
||||||
|
{ok, Resp, State};
|
||||||
|
true ->
|
||||||
To = maps:get(<<"to">>, Activity, []),
|
To = maps:get(<<"to">>, Activity, []),
|
||||||
|
%% Qui uso la funzione del nuovo modulo!
|
||||||
case users_local_check:has_local_recipient(To) of
|
case users_local_check:has_local_recipient(To) of
|
||||||
true ->
|
true ->
|
||||||
cowboy_req:reply(200, #{}, <<"Delivered to local user">>, Req2),
|
{ok, Resp} = cowboy_req:reply(200, #{}, <<"Delivered to local user">>, Req2),
|
||||||
{ok, Req2, State};
|
{ok, Resp, State};
|
||||||
false ->
|
false ->
|
||||||
%% SAFE INSERT NELLA GLOBAL INBOX
|
%% Salva nell'inbox globale
|
||||||
InsertResult = db_safe_insert:safe_insert(global_message, #global_message{
|
timeline_db:add_message(Activity),
|
||||||
id = make_ref(),
|
{ok, Resp} = cowboy_req:reply(202, #{}, <<"Saved to global inbox">>, Req2),
|
||||||
activity = Activity,
|
{ok, Resp, State}
|
||||||
timestamp = erlang:system_time(second)
|
|
||||||
}),
|
|
||||||
case InsertResult of
|
|
||||||
ok ->
|
|
||||||
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
|
||||||
end;
|
|
||||||
{error, malformed_json} ->
|
|
||||||
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.
|
end.
|
||||||
|
|
||||||
%% Funzione di utilità per leggere tutto il body
|
|
||||||
read_full_body(Req) ->
|
%% vediamo di controllare se ci sono i campi obbligatori minimi di ActivityPub:
|
||||||
case cowboy_req:read_body(Req) of
|
|
||||||
{ok, Body, Req2} ->
|
validate_activity(Activity) when is_map(Activity) ->
|
||||||
{Body, Req2};
|
maps:is_key(<<"type">>, Activity) andalso
|
||||||
{more, Data, Req2} ->
|
maps:is_key(<<"to">>, Activity) andalso
|
||||||
{Rest, FinalReq} = read_full_body(Req2),
|
maps:is_key(<<"actor">>, Activity) andalso
|
||||||
{<<Data/binary, Rest/binary>>, FinalReq}
|
maps:is_key(<<"object">>, Activity) andalso
|
||||||
end.
|
maps:is_key(<<"content">>, Activity).
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
-module(json_validate).
|
|
||||||
-export([validate_activity/1, load_schema/1]).
|
|
||||||
|
|
||||||
load_schema(SchemaName) ->
|
|
||||||
Filename = "priv/" ++ SchemaName ++ ".json",
|
|
||||||
{ok, Bin} = file:read_file(Filename),
|
|
||||||
jsx:decode(Bin, [return_maps]).
|
|
||||||
|
|
||||||
validate_activity(JsonBin) ->
|
|
||||||
case catch jsx:decode(JsonBin, [return_maps]) of
|
|
||||||
{'EXIT', _} ->
|
|
||||||
{error, malformed_json};
|
|
||||||
JsonMap when is_map(JsonMap) ->
|
|
||||||
case maps:get(<<"type">>, JsonMap, undefined) of
|
|
||||||
undefined ->
|
|
||||||
{error, missing_type};
|
|
||||||
TypeBin when is_binary(TypeBin) ->
|
|
||||||
TypeLower = string:lowercase(binary_to_list(TypeBin)),
|
|
||||||
case TypeLower of
|
|
||||||
"note" ->
|
|
||||||
Schema = load_schema("note"),
|
|
||||||
case jesse:validate_with_schema(Schema, JsonMap) of
|
|
||||||
{ok, _} -> ok;
|
|
||||||
{error, Errors} -> {error, Errors}
|
|
||||||
end;
|
|
||||||
OtherType ->
|
|
||||||
{error, {unsupported_type, TypeBin}}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
{error, invalid_type_field}
|
|
||||||
end
|
|
||||||
end.
|
|
|
@ -1,36 +0,0 @@
|
||||||
-module(log_handler).
|
|
||||||
-export([init/0, info/2, error/2, warning/2, debug/2, log_map/2]).
|
|
||||||
|
|
||||||
init() ->
|
|
||||||
ok = ensure_log_dir(),
|
|
||||||
lists:foreach(fun(H) -> logger:remove_handler(H) end, logger:get_handler_ids()),
|
|
||||||
logger:add_handler(file, logger_std_h, #{
|
|
||||||
level => debug,
|
|
||||||
config => #{file => "log/erlang.json", sync_mode_qlen => 0},
|
|
||||||
formatter => {logger_formatter, #{}}
|
|
||||||
}),
|
|
||||||
ok.
|
|
||||||
|
|
||||||
|
|
||||||
info(Format, Args) ->
|
|
||||||
logger:info(Format, Args).
|
|
||||||
|
|
||||||
error(Format, Args) ->
|
|
||||||
logger:error(Format, Args).
|
|
||||||
|
|
||||||
warning(Format, Args) ->
|
|
||||||
logger:warning(Format, Args).
|
|
||||||
|
|
||||||
debug(Format, Args) ->
|
|
||||||
logger:debug(Format, Args).
|
|
||||||
|
|
||||||
%% Logga direttamente una mappa come evento JSON
|
|
||||||
log_map(Level, Map) when is_map(Map) ->
|
|
||||||
logger:log(Level, Map).
|
|
||||||
|
|
||||||
%% Utility per assicurarsi che la directory log/ esista
|
|
||||||
ensure_log_dir() ->
|
|
||||||
case file:read_file_info("log") of
|
|
||||||
{ok, _} -> ok;
|
|
||||||
_ -> file:make_dir("log")
|
|
||||||
end.
|
|
|
@ -5,7 +5,7 @@
|
||||||
add_user(Name, Email) ->
|
add_user(Name, Email) ->
|
||||||
Id = erlang:unique_integer([monotonic, positive]),
|
Id = erlang:unique_integer([monotonic, positive]),
|
||||||
Timestamp = erlang:system_time(microsecond),
|
Timestamp = erlang:system_time(microsecond),
|
||||||
Record = #ap_users{id=Id, username=Name, email=Email, created_at=Timestamp},
|
Record = #ap_users{id=Id, name=Name, email=Email, created_at=Timestamp},
|
||||||
db_safe_insert:safe_insert(ap_users, Record).
|
db_safe_insert:safe_insert(ap_users, Record).
|
||||||
|
|
||||||
get_user(Id) ->
|
get_user(Id) ->
|
||||||
|
@ -20,7 +20,7 @@ get_user(Id) ->
|
||||||
|
|
||||||
all_users() ->
|
all_users() ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
mnesia:match_object(#ap_users{id='_', username='_', email='_', created_at='_'})
|
mnesia:match_object(#ap_users{id='_', name='_', email='_', created_at='_'})
|
||||||
end,
|
end,
|
||||||
{atomic, Users} = mnesia:transaction(F),
|
{atomic, Users} = mnesia:transaction(F),
|
||||||
Users.
|
Users.
|
||||||
|
|
|
@ -12,10 +12,9 @@
|
||||||
check_ENV() ->
|
check_ENV() ->
|
||||||
case os:getenv("AP_FQDN") of
|
case os:getenv("AP_FQDN") of
|
||||||
false ->
|
false ->
|
||||||
log_handler:error("Errore: la variabile di ambiente AP_FQDN non è settata!~n"),
|
io:format("Errore: la variabile di ambiente AP_FQDN non è settata!~n"),
|
||||||
erlang:halt(1);
|
erlang:halt(1);
|
||||||
_ ->
|
_ ->
|
||||||
log_handler:debug("La variabile AP_FQDN esiste, bene", []),
|
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -25,10 +24,10 @@ get_fqdn() ->
|
||||||
logger:notice("Chiamata get_fqdn()~n"),
|
logger:notice("Chiamata get_fqdn()~n"),
|
||||||
case os:getenv("AP_FQDN") of
|
case os:getenv("AP_FQDN") of
|
||||||
false ->
|
false ->
|
||||||
log_handler:error("Errore: la variabile di ambiente AP_FQDN deve essere settata!~n"),
|
logger:notice("Errore: la variabile di ambiente AP_FQDN deve essere settata!~n"),
|
||||||
exit({error, ap_fqdn_not_set});
|
exit({error, ap_fqdn_not_set});
|
||||||
FQDN ->
|
FQDN ->
|
||||||
log_handler:debug("FQDN settata correttamente: ~p", [FQDN]),
|
logger:notice("FQDN settata correttamente: ~p", [FQDN]),
|
||||||
list_to_binary(FQDN)
|
list_to_binary(FQDN)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
|
@ -1,50 +1,40 @@
|
||||||
|
%% webfinger_handler.erl
|
||||||
|
%% Handler per l'endpoint /.well-known/webfinger
|
||||||
-module(webfinger_handler).
|
-module(webfinger_handler).
|
||||||
-behaviour(cowboy_handler).
|
-behaviour(cowboy_handler).
|
||||||
-export([init/2]).
|
|
||||||
|
|
||||||
-include("db_safe_insert.hrl").
|
%% Cowboy vuole che esportiamo la funzione init/2
|
||||||
|
-export([init/2]).
|
||||||
|
|
||||||
init(Req, State) ->
|
init(Req, State) ->
|
||||||
Params = cowboy_req:parse_qs(Req),
|
Params = cowboy_req:parse_qs(Req),
|
||||||
Resource = proplists:get_value(<<"resource">>, Params),
|
Resource = proplists:get_value(<<"resource">>, Params),
|
||||||
|
|
||||||
case Resource of
|
case Resource of
|
||||||
undefined ->
|
undefined ->
|
||||||
reply_json(400, <<"Missing resource parameter">>, Req, State);
|
{ok, Resp} = cowboy_req:reply(
|
||||||
|
400,
|
||||||
|
#{<<"content-type">> => <<"application/json">>},
|
||||||
|
<<"{\"error\": \"Missing resource parameter\"}">>,
|
||||||
|
Req
|
||||||
|
),
|
||||||
|
{ok, Resp, State};
|
||||||
_ ->
|
_ ->
|
||||||
case parse_acct(Resource) of
|
case parse_acct(Resource) of
|
||||||
{ok, Username, Domain} ->
|
{ok, Username, Domain} ->
|
||||||
|
%% Qui interroghiamo Mnesia!
|
||||||
case user_db:get_user(Username) of
|
case user_db:get_user(Username) of
|
||||||
{ok, User} ->
|
{ok, _User} ->
|
||||||
%% Estrai i campi dal record
|
ActorUrl = <<"https://", Domain/binary, "/users/", Username/binary>>,
|
||||||
ProfileUrl = to_binary(User#ap_users.profile_url),
|
|
||||||
ActorUrl = to_binary(User#ap_users.actor_url),
|
|
||||||
AvatarUrl = to_binary(User#ap_users.avatar_url),
|
|
||||||
Subject = <<"acct:", Username/binary, "@", Domain/binary>>,
|
|
||||||
WebfingerMap = #{
|
WebfingerMap = #{
|
||||||
<<"subject">> => Subject,
|
<<"subject">> => <<"acct:", Username/binary, "@", Domain/binary>>,
|
||||||
<<"aliases">> => [ProfileUrl, ActorUrl],
|
|
||||||
<<"links">> => [
|
<<"links">> => [
|
||||||
#{
|
#{
|
||||||
<<"rel">> => <<"self">>,
|
<<"rel">> => <<"self">>,
|
||||||
<<"type">> => <<"application/activity+json">>,
|
<<"type">> => <<"application/activity+json">>,
|
||||||
<<"href">> => ActorUrl
|
<<"href">> => ActorUrl
|
||||||
},
|
|
||||||
#{
|
|
||||||
<<"rel">> => <<"http://webfinger.net/rel/profile-page">>,
|
|
||||||
<<"type">> => <<"text/html">>,
|
|
||||||
<<"href">> => ProfileUrl
|
|
||||||
}
|
|
||||||
] ++
|
|
||||||
(case AvatarUrl of
|
|
||||||
<<>> -> [];
|
|
||||||
_ -> [
|
|
||||||
#{
|
|
||||||
<<"rel">> => <<"http://webfinger.net/rel/avatar">>,
|
|
||||||
<<"type">> => <<"image/jpeg">>,
|
|
||||||
<<"href">> => AvatarUrl
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
end)
|
|
||||||
},
|
},
|
||||||
Json = jsx:encode(WebfingerMap),
|
Json = jsx:encode(WebfingerMap),
|
||||||
{ok, Resp} = cowboy_req:reply(
|
{ok, Resp} = cowboy_req:reply(
|
||||||
|
@ -55,23 +45,27 @@ init(Req, State) ->
|
||||||
),
|
),
|
||||||
{ok, Resp, State};
|
{ok, Resp, State};
|
||||||
not_found ->
|
not_found ->
|
||||||
reply_json(404, <<"User not found">>, Req, State)
|
{ok, Resp} = cowboy_req:reply(
|
||||||
|
404,
|
||||||
|
#{<<"content-type">> => <<"application/json">>},
|
||||||
|
<<"{\"error\": \"User not found\"}">>,
|
||||||
|
Req
|
||||||
|
),
|
||||||
|
{ok, Resp, State}
|
||||||
end;
|
end;
|
||||||
error ->
|
error ->
|
||||||
reply_json(400, <<"Invalid resource format">>, Req, State)
|
{ok, Resp} = cowboy_req:reply(
|
||||||
|
400,
|
||||||
|
#{<<"content-type">> => <<"application/json">>},
|
||||||
|
<<"{\"error\": \"Invalid resource format\"}">>,
|
||||||
|
Req
|
||||||
|
),
|
||||||
|
{ok, Resp, State}
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
reply_json(Status, Msg, Req, State) ->
|
|
||||||
{ok, Resp} = cowboy_req:reply(
|
|
||||||
Status,
|
|
||||||
#{<<"content-type">> => <<"application/json">>},
|
|
||||||
jsx:encode(#{<<"error">> => Msg}),
|
|
||||||
Req
|
|
||||||
),
|
|
||||||
{ok, Resp, State}.
|
|
||||||
|
|
||||||
%% Utility per estrarre username e dominio da "acct:username@domain"
|
%% Funzione di utilità per estrarre username e dominio da "acct:username@domain"
|
||||||
parse_acct(<<"acct:", Rest/binary>>) ->
|
parse_acct(<<"acct:", Rest/binary>>) ->
|
||||||
case binary:split(Rest, <<"@">>) of
|
case binary:split(Rest, <<"@">>) of
|
||||||
[Username, Domain] when Username =/= <<>>, Domain =/= <<>> ->
|
[Username, Domain] when Username =/= <<>>, Domain =/= <<>> ->
|
||||||
|
@ -81,8 +75,3 @@ parse_acct(<<"acct:", Rest/binary>>) ->
|
||||||
end;
|
end;
|
||||||
parse_acct(_) ->
|
parse_acct(_) ->
|
||||||
error.
|
error.
|
||||||
|
|
||||||
%% Utility per gestire stringhe/binary
|
|
||||||
to_binary(undefined) -> <<>>;
|
|
||||||
to_binary(Str) when is_list(Str) -> unicode:characters_to_binary(Str);
|
|
||||||
to_binary(Bin) when is_binary(Bin) -> Bin.
|
|
||||||
|
|
Loading…
Reference in New Issue