NBMKIBO6UJKXCOXXVPPENLEBYI4YOU2VCHH5KIOUGH7WJG47N4PQC EVG6AOW4UUH7C6COH5XHYLPBXC3QEZR2372SE3LJCUPPEZQ3BUQQC OP6D2SH4F4H66QA3W5HYUCWQJ6IJHGTX5KVX67CE5IBXBIMA4OTQC FAFY7PLMQPWUIKHTG7YANSBHDBWXMNUOIFBS5MUW2ALNSXDLI42QC XRM2EBGB3Z6MR6XWOE7CZ3ZT2YTUGHJYPYUEBCMT56SDYOQTPVOAC DJ7EM5ZXZRSOBHEAA5EVZNVULJCQ7EX4DQBSK2PKPXITWJDPIUXQC WMCS3B3K2DHIXJUAMFK46GO3CVWBQS3ASBOR6WAVIEOX35GPEQYQC 7CB7WD2VQGU2ZZV3CWXAOFLPM5SY6RADIXRSFLUVXKQXC5472UOAC RZB6HZ2NI5PIUIWQL63CSKXZGGUG4L6XCSCGR3TI723OO57NDT3AC R4JDMB7LL3FLA4NJEAV2DQEXII5XS5KIMG3H4YS5P6W7ZZUE7FIQC 2R3WFEOT3WWS6NFBBABSVRUNUPTXHFFMGPZZQOCPLTD2WB3U55HQC ULS4X3VORQZFBSNPXN76UFH2PWS2MVBA64CJQS3IR4YUHIKEF6GQC EDLKGFB5NWTZTHEO6IAR5M4W4533KJI4O6673MKJNTW3JPHQZP5AC J32TNYTRQJ3YTGFSI5FXW63R5ACT5SZQ6C4YSHLAZ4HXYAHAN7YQC JMYRP5MUPSBHABHBPIO45F6RC7QSQSNO2EJDUDUIQXX3MEEFDOAQC JVURIEXR72OUGZ2EHP5HB6OMXUNPHMAV66YUUP2444TPOG5XGASQC W4Q3IBD3DLMJHATLFRLVO7JRYBEYIMU2ZDNCQP7UG7ARNYJOMCUQC QHK3BXHEC6IAAI7HK76Y7W3IQQIEFH7TD7AYMBCLOE7U2L7BT2LQC BSISJB2O2HKYGSCX6HIIMLBIXCZ66BRCZH3622G2NOQJRJ5S3HLAC CBHKQGLDCAH2E4ZNACITBSMADOKPERFCWQPUGMH7UN5TLJXLYI4QC PFC6VDDZ55HDVMZYGJJOBUA4PTKDYIOBV7GKHT2FCWUJGXHXAPCAC 755UGKECZ3PFYEA2TFFOUZTF27CRQTBZWO7UYFU6WQDJMZDBVPRAC CZG24QKIB45EL7PK4UA6XVG3VEVKI7OAQXN4YMFUKKI2MXLHRANQC IBA3VK7OHBGLEYASI7JNTXPYVTNVFT4L4RRWLNF2VGPPCONDAWMQC M4TNRFRPHEH6T673JAMJ3CHABASCWMAJVU57HH2XEMDJCB3QPT5QC YWCRGWVDIMCPXBQFM23MKCYZCXJJSD733NJEPB6WUU4G4BSREILAC WHYWDNSL67TTB2AGAUACXERJ6X54YMNYVC32KJQFEXFFRYA4K3HQC YA3ELS7AQZ2XCZZVNMZOM3TSVCWU7HSBVMDXFCIOFTXAYFZSRL2AC OJ6KWAG7XUCYNQ6T3FRJK2QOP7DBO26PZEWDVH3Q2MJKIBMNWRPQC H7NYXRO5IR3YTZZRGULCVMOSZCK7BQLU76CAWDHSLR6B4YDQMAKQC 55WLMLEEVBRSTAFRZ5RGF7TOGUF5OPVCPA2TMHAQK45OUO7PA3YQC KFG4XK3ZVBJGMRDOADGSNORUWDPKHYDC7S7QN5EU3ZZQTUEC647AC GMJDM2KTUACQRY7RQYC25U4UCVD6JEIU3A45GPOCK2QTJS56WQ3AC YDZ3R5NY7II7WX4Q5O2UQ72TMCABDPPRLEMTTIBH5DGKOLAOPOPAC DZZ4B3UGIYTN3OHAKS2HNCLK7KM2ZSHPZ4JC6YVQL6I2H4KCD5PAC JGA5PAC4QBPH62IPO4HPJ6ETR6BYZDR6SKE6NOTOP3YZVM4XY62AC AQ2M4QZFKBNHMJF25P275ISB7QV7JWKMFV632PI7NWJJGFXE6Q5AC RUYSGXKQE4F7GWQ6OFI6DABOHVCNOGCYFTUQPK3XTPZ2UPGD4Y2QC IHBNW3GI2XB6KAWUYRLL6KDOBUNUOU3N7RRLS6BFNW6SE7ZDHLWQC ST7Y5WTYIQNKUOVXW6T5PQZL6R3VUYC37BAEQOA2FL6AYVPR4OLAC T3VWIP2EHZMGXM6UQMK77WB225WRNTAW7IWXZ5ZHUZ6PE3I7AATQC MMWEGAYYQ4CX3U6JJH4MNEZHTAYK6WQBGXIVQ7DL7MVSPGNS7V7AC Y6JGJ7676WTHGES5N3VV7L5D4ZJWPTWYUFGZXQOKUMYYUOKS2EMAC QT6LHFTXOG7V3TNZN6SNB7GXIVM454C3GSCAQ74M7P6GQL6B4Q7AC MONVI5STEDKY5ALVMEXJJXDUX6XQRKTFLP7BBNOQML3VSJEM2JAAC [ {public_key, _Public}, {links, _Links}, {type, 0}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {channel, GotChan}, {text, GotText}]
[{public_key, _Public},{links, _Links},{type, 0},{timestamp, _TimeStamp},{hash, _Hash}],[{channel, GotChan},{text, GotText}]
[ {public_key, _Public}, {links, _Links}, {type, 2}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {infos, #{<<"name">> := BinName}}]
[{public_key, _Public},{links, _Links},{type, 2},{timestamp, _TimeStamp},{hash, _Hash}],[{infos, #{<<"name">> := BinName}}]
[ {public_key, _PubKey}, {links, _Links}, {type, 0}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {channel, Channel}, {text, Text}]
[{public_key, _PubKey},{links, _Links},{type, 0},{timestamp, _TimeStamp},{hash, _Hash}],[{channel, Channel},{text, Text}]
[ {public_key, _PubKey}, {links, _Links}, {type, 1}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {hashes, Hashes} ]
[{public_key, _PubKey},{links, _Links},{type, 1},{timestamp, _TimeStamp},{hash, _Hash}],[{hashes, Hashes}]
[ {public_key, _PubKey}, {links, _Links}, {type, 2}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {infos, Infos} ]
[{public_key, _PubKey},{links, _Links},{type, 2},{timestamp, _TimeStamp},{hash, _Hash}],[{infos, Infos}]
[ {public_key, _PubKey}, {links, _Links}, {type, 3}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {channel, Channel}, {topic, Topic}]
[{public_key, _PubKey},{links, _Links},{type, 3},{timestamp, _TimeStamp},{hash, _Hash}],[{channel, Channel},{topic, Topic}]
[ {public_key, _PubKey}, {links, _Links}, {type, 4}, {timestamp, _TimeStamp}, {hash, _Hash}],[ {channel, Channel} ]]= Decoded,
[{public_key, _PubKey},{links, _Links},{type, 4},{timestamp, _TimeStamp},{hash, _Hash}],[{channel, Channel}]] = Decoded,
[ {public_key, _PubKey}, {links, _Links}, {type, 5}, {timestamp, _TimeStamp}, {hash, _Hash}], [ {channel, Channel} ]
[{public_key, _PubKey},{links, _Links},{type, 5},{timestamp, _TimeStamp},{hash, _Hash}],[{channel, Channel}]
{ok, CState} = peer:channel_members(Peer, C),?assertEqual(T, proplists:get_value(topic, CState)),Members = proplists:get_value(members, CState),?assertEqual(2, maps:size(Members)),{AlfiName, AlfiWhen, false} = maps:get(PubAlfi, Members),?assertEqual("Alfi", AlfiName),?assert(AlfiWhen > 0),{BertName, BertWhen, false} = maps:get(PubBert, Members),?assertEqual("Not Ernie!", BertName),?assert(BertWhen > 0)end,
{ok, CState} = peer:channel_members(Peer, C),?assertEqual(T, proplists:get_value(topic, CState)),Members = proplists:get_value(members, CState),?assertEqual(2, maps:size(Members)),{AlfiName, AlfiWhen, false} = maps:get(PubAlfi, Members),?assertEqual("Alfi", AlfiName),?assert(AlfiWhen > 0),{BertName, BertWhen, false} = maps:get(PubBert, Members),?assertEqual("Not Ernie!", BertName),?assert(BertWhen > 0)end,
{ok, {Texts, Users}} = peer:read(Peer, C),[{UserIdAlfi, _, T1}, {UserIdBert, _, T2}] = Texts,{AlfiInfo, UsersSansAlfi} = maps:take(UserIdAlfi, Users),?assertEqual({NickAlfi, PubAlfi}, AlfiInfo),{BertInfo, #{}} = maps:take(UserIdBert, UsersSansAlfi),?assertEqual({NickBert, PubBert}, BertInfo)end,
{ok, {Texts, Users}} = peer:read(Peer, C),[{UserIdAlfi, _, T1}, {UserIdBert, _, T2}] = Texts,{AlfiInfo, UsersSansAlfi} = maps:take(UserIdAlfi, Users),?assertEqual({NickAlfi, PubAlfi}, AlfiInfo),{BertInfo, #{}} = maps:take(UserIdBert, UsersSansAlfi),?assertEqual({NickBert, PubBert}, BertInfo)end,
Table = [ { "Two hands clap and there is a sound. What is the sound of one hand?", "fcd7c41883c3564c5a6abec78e214159efe62d50f124b4afafc184ea3b764cd4"}, { "茶色", "46b321c236880cd861dafae3040cf8cc52990516d1a69ab2c170b1e615a7ebd5"}, { "elf", "ffe809405a3e1eaf77938bde2138832b177a51e47df02935edc12aacf8279f61"}, { "love collapses spacetime", "fea16c09f8aa581500fcf6ee2f6aabc59ccaa271d2a3568843930b7ff929ad86"}],lists:foreach(fun({InStr, Want}) ->In = unicode:characters_to_binary(InStr),Got = enacl:generichash(32, In),GotHex = hex:bin_to_hexstr(Got),?assertEqual(Want, string:lowercase(GotHex))end, Table).
Table = [{"Two hands clap and there is a sound. What is the sound of one hand?","fcd7c41883c3564c5a6abec78e214159efe62d50f124b4afafc184ea3b764cd4"},{"茶色", "46b321c236880cd861dafae3040cf8cc52990516d1a69ab2c170b1e615a7ebd5"},{"elf", "ffe809405a3e1eaf77938bde2138832b177a51e47df02935edc12aacf8279f61"},{"love collapses spacetime","fea16c09f8aa581500fcf6ee2f6aabc59ccaa271d2a3568843930b7ff929ad86"}],lists:foreach(fun({InStr, Want}) ->In = unicode:characters_to_binary(InStr),Got = enacl:generichash(32, In),GotHex = hex:bin_to_hexstr(Got),?assertEqual(Want, string:lowercase(GotHex))end,Table).
Examples = [ examples:post_text(), examples:post_delete(), examples:post_info(), examples:post_topic(), examples:post_join(), examples:post_leave()],
Examples = [examples:post_text(),examples:post_delete(),examples:post_info(),examples:post_topic(),examples:post_join(),examples:post_leave()],
{ok, [LoadedHeader, LoadedBody, LoadedBin]} = db:load_post(Db, Id),?assertEqual(Header, LoadedHeader),?assertEqual(Body, LoadedBody),?assertEqual(Bin, LoadedBin),
{ok, [LoadedHeader, LoadedBody, LoadedBin]} = db:load_post(Db, Id),?assertEqual(Header, LoadedHeader),?assertEqual(Body, LoadedBody),?assertEqual(Bin, LoadedBin),
[Header, [ {ttl, 1},{channel, Channel},{timestart, TimeStart},{timeend, TimeEnd},{limit, Limit}]] = Decoded,
[Header,[{ttl, 1},{channel, Channel},{timestart, TimeStart},{timeend, TimeEnd},{limit, Limit}]] = Decoded,
{sqlite, {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}},{b, {git, "https://github.com/b/hex.git", {branch, "master"}}},{jsone, "~> 1.8.1"},%% rebar/erlang v25 compat{enacl, {git, "https://github.com/helium/enacl.git", {branch, "master"}}},{enoise, {git, "https://github.com/cryptix/enoise.git", {branch, "enacl-fork"}}}
{sqlite, {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.15"}}},{b, {git, "https://github.com/b/hex.git", {branch, "master"}}},{jsone, "~> 1.8.1"},%% rebar/erlang v25 compat{enacl, {git, "https://github.com/helium/enacl.git", {branch, "master"}}},{enoise, {git, "https://github.com/cryptix/enoise.git", {branch, "enacl-fork"}}}
{profiles, [{dev, [{erl_opts, [debug_info]},{relx, [{dev_mode, true},{include_erts, false}]}]},{prod, [{erl_opts, [no_debug_info, warnings_as_errors]},{relx, [{dev_mode, false},{include_erts, true}]}]}]}.
{profiles, [{dev, [{erl_opts, [debug_info]},{relx, [{dev_mode, true},{include_erts, false}]}]},{prod, [{erl_opts, [no_debug_info, warnings_as_errors]},{relx, [{dev_mode, false},{include_erts, true}]}]}]}.
-export([encode_post_request/2, encode_cancel_request/2,encode_channel_time_range_request/5,encode_channel_state_request/3,encode_channel_list_request/3, encode_hash_response/2,encode_post_response/2, encode_channel_list_response/2]).
-export([encode_post_request/2,encode_cancel_request/2,encode_channel_time_range_request/5,encode_channel_state_request/3,encode_channel_list_request/3,encode_hash_response/2,encode_post_response/2,encode_channel_list_response/2]).
[ {requestId, RequestId}, {circuitId, CircuitId}] = Header,length_encode_fields([ <<0>>, CircuitId, RequestId, encode_varint(length(Hashes)), Hashes]).
[{requestId, RequestId},{circuitId, CircuitId}] = Header,length_encode_fields([<<0>>,CircuitId,RequestId,encode_varint(length(Hashes)),Hashes]).
[ {requestId, RequestId}, {circuitId, CircuitId}, {ttl, TTL}] = Header,length_encode_fields([ <<3>>, CircuitId, RequestId, encode_varint(TTL), CancelId]).
[{requestId, RequestId},{circuitId, CircuitId},{ttl, TTL}] = Header,length_encode_fields([<<3>>,CircuitId,RequestId,encode_varint(TTL),CancelId]).
[ {requestId, RequestId}, {circuitId, CircuitId}, {ttl, TTL}] = Header,length_encode_fields([ <<4>>, CircuitId, RequestId, encode_varint(TTL), encode_varint(length(Channel)), Channel, encode_varint(Start), encode_varint(End), encode_varint(Limit)]).
[{requestId, RequestId},{circuitId, CircuitId},{ttl, TTL}] = Header,length_encode_fields([<<4>>,CircuitId,RequestId,encode_varint(TTL),encode_varint(length(Channel)),Channel,encode_varint(Start),encode_varint(End),encode_varint(Limit)]).
length_encode_fields([ <<5>>, CircuitId, RequestId, encode_varint(TTL), encode_varint(byte_size(ChannelBin)), ChannelBin, encode_bool(Future)]).
length_encode_fields([<<5>>,CircuitId,RequestId,encode_varint(TTL),encode_varint(byte_size(ChannelBin)),ChannelBin,encode_bool(Future)]).
[ {requestId, RequestId}, {circuitId, CircuitId}, {ttl, TTL}] = Header,length_encode_fields([ <<6>>, CircuitId, RequestId, encode_varint(TTL), encode_varint(Offset), encode_varint(Limit)]).
[{requestId, RequestId},{circuitId, CircuitId},{ttl, TTL}] = Header,length_encode_fields([<<6>>,CircuitId,RequestId,encode_varint(TTL),encode_varint(Offset),encode_varint(Limit)]).
Body = case proplists:get_value(msgType, Header) of0 -> decode_hash_response(Payload);1 -> decode_post_response(Payload);2 -> decode_post_request(Payload);3 -> decode_cancel_request(Payload);4 -> decode_channel_time_range_request(Payload);5 -> decode_channel_state_request(Payload);6 -> decode_channel_list_request(Payload);7 -> decode_channel_list_response(Payload);Unknown -> erlang:error(io_lib:format("Unknown message type: ~p", [Unknown]))end,
Body =case proplists:get_value(msgType, Header) of0 -> decode_hash_response(Payload);1 -> decode_post_response(Payload);2 -> decode_post_request(Payload);3 -> decode_cancel_request(Payload);4 -> decode_channel_time_range_request(Payload);5 -> decode_channel_state_request(Payload);6 -> decode_channel_list_request(Payload);7 -> decode_channel_list_response(Payload);Unknown -> erlang:error(io_lib:format("Unknown message type: ~p", [Unknown]))end,
<< CircuitId:4/binary, ReqId:4/binary, Rest3/binary >> = Rest2,Header = [ {msgType, MsgType}, {circuitId, CircuitId}, {requestId, ReqId}],
<<CircuitId:4/binary, ReqId:4/binary, Rest3/binary>> = Rest2,Header = [{msgType, MsgType},{circuitId, CircuitId},{requestId, ReqId}],
[ {ttl, Ttl}, {channel, binary_to_list(Channel)}, {timestart, TimeStart}, {timeend, TimeEnd}, {limit, Limit}
[{ttl, Ttl},{channel, binary_to_list(Channel)},{timestart, TimeStart},{timeend, TimeEnd},{limit, Limit}
<<Field:Len/binary, Rest2/binary >> = Rest,NewAcc = Acc ++ case Kind ofstring -> [binary_to_list(Field)];binary -> [Field]end,
<<Field:Len/binary, Rest2/binary>> = Rest,NewAcc =Acc ++case Kind ofstring -> [binary_to_list(Field)];binary -> [Field]end,
TopicData = case length(Topic) of0 -> [0];_ ->TopicBin = unicode:characters_to_binary(Topic),TopicLen = wire:encode_varint(byte_size(TopicBin)),[TopicLen, TopicBin]end,
TopicData =case length(Topic) of0 ->[0];_ ->TopicBin = unicode:characters_to_binary(Topic),TopicLen = wire:encode_varint(byte_size(TopicBin)),[TopicLen, TopicBin]end,
Decoded = case proplists:get_value(type, Header) of0 -> decode_post_text(Body);1 -> decode_post_delete(Body);2 -> decode_post_info(Body);3 -> decode_post_topic(Body);4 -> decode_post_join(Body);5 -> decode_post_leave(Body)end,
Decoded =case proplists:get_value(type, Header) of0 -> decode_post_text(Body);1 -> decode_post_delete(Body);2 -> decode_post_info(Body);3 -> decode_post_topic(Body);4 -> decode_post_join(Body);5 -> decode_post_leave(Body)end,
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TextLen, Rest3} = wire:decode_varint(Rest2),<<Text:(TextLen)/binary>> = Rest3,[ {channel, Channel}, {text, unicode:characters_to_binary(Text)}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TextLen, Rest3} = wire:decode_varint(Rest2),<<Text:(TextLen)/binary>> = Rest3,[{channel, Channel}, {text, unicode:characters_to_binary(Text)}].
{NumHashes, Rest} = wire:decode_varint(Body),<<HashData:(32*NumHashes)/binary >> = Rest,Hashes = [Hash || <<Hash:32/binary>> <= HashData],[ {hashes, Hashes} ].
{NumHashes, Rest} = wire:decode_varint(Body),<<HashData:(32 * NumHashes)/binary>> = Rest,Hashes = [Hash || <<Hash:32/binary>> <= HashData],[{hashes, Hashes}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TopicLen, Rest3} = wire:decode_varint(Rest2),<<Topic:(TopicLen)/binary>> = Rest3,[ {channel, Channel}, {topic, Topic}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary, Rest2/binary>> = Rest,{TopicLen, Rest3} = wire:decode_varint(Rest2),<<Topic:(TopicLen)/binary>> = Rest3,[{channel, Channel}, {topic, Topic}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,[ {channel, Channel}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,[{channel, Channel}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,[ {channel, Channel}].
{ChannelLen, Rest} = wire:decode_varint(Body),<<Channel:(ChannelLen)/binary>> = Rest,[{channel, Channel}].
{ok, Msg} = wire:decode(Chunk),%% TODO: this ommits the byte(s) for the length prefixMsgSize = byte_size(Chunk),Handler ! {incomingMsg, Conn, Msg, MsgSize}end,
{ok, Msg} = wire:decode(Chunk),%% TODO: this ommits the byte(s) for the length prefixMsgSize = byte_size(Chunk),Handler ! {incomingMsg, Conn, Msg, MsgSize}end,
handle_peer_messages(peerLost, {Peer}, State = #state{peers = Peers, channels = Chans, activeIn = ActiveIn, activeOut = ActiveOut}) ->
handle_peer_messages(peerLost,{Peer},State = #state{peers = Peers, channels = Chans, activeIn = ActiveIn, activeOut = ActiveOut}) ->
io:format("[Peer:~p] lost | LastSeen: ~p | Sent: ~p | Received: ~p~n",[Peer, LastSeen, human_bytesize(Sent), human_bytesize(Received)]),
io:format("[Peer:~p] lost | LastSeen: ~p | Sent: ~p | Received: ~p~n",[Peer, LastSeen, human_bytesize(Sent), human_bytesize(Received)]),
MatchRequests = fun(Req, Matched) ->case Req of{_, ReqId, Peer} ->maps:put(ReqId, true, Matched);_Other -> % other requestsMatchedendend,NewMatched = lists:foldl(MatchRequests, MatchedReqs, ChanReqs),NewMatchedend,
MatchRequests = fun(Req, Matched) ->case Req of{_, ReqId, Peer} ->maps:put(ReqId, true, Matched);% other requests_Other ->Matchedendend,NewMatched = lists:foldl(MatchRequests, MatchedReqs, ChanReqs),NewMatchedend,
{ChanReqs, NewChanAcc} = maps:take(ChanName, AccChans),FilterReqsOfPeer = fun(Req) ->case Req of{Direction, ReqId, Peer} ->io:format("[DEBUG Peer:~p] dropping ~p request ~p~n",[Peer, Direction, hex:bin_to_hexstr(ReqId)]),false;_Other -> % other requeststrueendend,NewReqs = lists:filter(FilterReqsOfPeer, ChanReqs),maps:put(ChanName, NewReqs, NewChanAcc)end,
{ChanReqs, NewChanAcc} = maps:take(ChanName, AccChans),FilterReqsOfPeer = fun(Req) ->case Req of{Direction, ReqId, Peer} ->io:format("[DEBUG Peer:~p] dropping ~p request ~p~n",[Peer, Direction, hex:bin_to_hexstr(ReqId)]),false;% other requests_Other ->trueendend,NewReqs = lists:filter(FilterReqsOfPeer, ChanReqs),maps:put(ChanName, NewReqs, NewChanAcc)end,
peers = NewPeers,channels = NewChans,activeOut = NewOut,activeIn = NewIn};handle_peer_messages(peerNew, {Peer}, State = #state{channels = Chans, peers = Peers, activeOut = ActiveOut, db = Db, keyPair = KeyPair}) ->
peers = NewPeers,channels = NewChans,activeOut = NewOut,activeIn = NewIn};handle_peer_messages(peerNew,{Peer},State = #state{channels = Chans, peers = Peers, activeOut = ActiveOut, db = Db, keyPair = KeyPair}) ->
{ok, {StateReqId, StateMsg, StateSize}} = send_channel_state_request(Peer, Chan, true),End = 0,Start = os:system_time(1000) - (12*60*60 * 1000),{ok, {TimeReqId, TimeMsg, TimeSize}} = send_channel_time_range_request(Peer, Chan, Start, End, 200),ReqsForChan = maps:get(Chan, AccChannels, [])++ [{sent, StateReqId, Peer}]++ [{sent, TimeReqId, Peer}],{AccNewReqs ++ [{StateReqId, StateMsg}, {TimeReqId, TimeMsg}],maps:update(Chan, ReqsForChan, AccChannels),PeerSent + StateSize + TimeSize}end,
{ok, {StateReqId, StateMsg, StateSize}} = send_channel_state_request(Peer, Chan, true),End = 0,Start = os:system_time(1000) - (12 * 60 * 60 * 1000),{ok, {TimeReqId, TimeMsg, TimeSize}} = send_channel_time_range_request(Peer, Chan, Start, End, 200),ReqsForChan =maps:get(Chan, AccChannels, []) ++[{sent, StateReqId, Peer}] ++[{sent, TimeReqId, Peer}],{AccNewReqs ++ [{StateReqId, StateMsg}, {TimeReqId, TimeMsg}],maps:update(Chan, ReqsForChan, AccChannels),PeerSent + StateSize + TimeSize}end,
NewOut = lists:foldl(fun({ReqId, Msg}, AccActiveOut) ->maps:put(ReqId, {Peer, Msg}, AccActiveOut)end, ActiveOut, NewRequests),
NewOut = lists:foldl(fun({ReqId, Msg}, AccActiveOut) ->maps:put(ReqId, {Peer, Msg}, AccActiveOut)end,ActiveOut,NewRequests),
handle_channel_messages(setOwnNick, {From, Nick}, State = #state{db = Db, keyPair = KeyPair, activeIn = ActiveIn, peers = Peers}) ->
handle_channel_messages(setOwnNick,{From, Nick},State = #state{db = Db, keyPair = KeyPair, activeIn = ActiveIn, peers = Peers}) ->
case proplists:get_value(type, Header) of5 ->{ok, {_, _, Size}} = send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);_ -> AccPeersendend,
case proplists:get_value(type, Header) of5 ->{ok, {_, _, Size}} = send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);_ ->AccPeersendend,
handle_channel_messages(channelsJoin, {Chan}, State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeOut = ActiveOut}) ->
handle_channel_messages(channelsJoin,{Chan},State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeOut = ActiveOut}) ->
%% TODO: handle send fail{ok, {ReqId, Msg, Size}} = send_channel_state_request(Peer, Chan, true),{{sent, ReqId, Peer},{maps:put(ReqId, {Peer, Msg}, AccActiveOuts),update_peer_sent(AccPeers, Peer, Size)}}end,{Reqs, {NewPeers, NewActiveOut}} = lists:mapfoldl(EachPeer, {Peers, ActiveOut}, maps:keys(Peers)),
%% TODO: handle send fail{ok, {ReqId, Msg, Size}} = send_channel_state_request(Peer, Chan, true),{{sent, ReqId, Peer},{maps:put(ReqId, {Peer, Msg}, AccActiveOuts),update_peer_sent(AccPeers, Peer, Size)}}end,{Reqs, {NewPeers, NewActiveOut}} = lists:mapfoldl(EachPeer, {Peers, ActiveOut}, maps:keys(Peers)),
handle_channel_messages(channelsLeave, {Chan}, State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeOut = ActiveOut}) ->
handle_channel_messages(channelsLeave,{Chan},State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeOut = ActiveOut}) ->
{{Peer, _}, NewActiveOuts} = maps:take(ReqId, AccActiveOuts),%% TODO: handle send fail{ok, {_, _, Size}} = send_cancel_request(Peer, ReqId),{NewActiveOuts,update_peer_sent(AccPeers, Peer, Size)}end,
{{Peer, _}, NewActiveOuts} = maps:take(ReqId, AccActiveOuts),%% TODO: handle send fail{ok, {_, _, Size}} = send_cancel_request(Peer, ReqId),{NewActiveOuts,update_peer_sent(AccPeers, Peer, Size)}end,
handle_channel_messages(channelsSetTopic, {From, Chan, Topic}, State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeIn = ActiveIn}) ->
handle_channel_messages(channelsSetTopic,{From, Chan, Topic},State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeIn = ActiveIn}) ->
SentPeers = lists:foldl(fun({received, ReqId, Peer}, AccPeers) ->{Peer, [Header, _]} = maps:get(ReqId, ActiveIn),case proplists:get_value(type, Header) of4 ->{ok, {_, _, Size}} = send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);_ -> AccPeers % ignoredendend, Peers, maps:get(Chan, Chans)),
SentPeers = lists:foldl(fun({received, ReqId, Peer}, AccPeers) ->{Peer, [Header, _]} = maps:get(ReqId, ActiveIn),case proplists:get_value(type, Header) of4 ->{ok, {_, _, Size}} = send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);% ignored_ ->AccPeersendend,Peers,maps:get(Chan, Chans)),
handle_database_messages(writeTextToChannel, {From, Chan, Text}, State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeIn = ActiveIn}) ->
handle_database_messages(writeTextToChannel,{From, Chan, Text},State = #state{channels = Chans, db = Db, keyPair = KeyPair, peers = Peers, activeIn = ActiveIn}) ->
case Direction ofreceived ->{Peer, [Header, _]} = maps:get(ReqId, ActiveIn),case proplists:get_value(msgType, Header) of4 ->{ok, {_, _, Size}}= send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);_ -> AccPeersend;sent -> AccPeersendend,
case Direction ofreceived ->{Peer, [Header, _]} = maps:get(ReqId, ActiveIn),case proplists:get_value(msgType, Header) of4 ->{ok, {_, _, Size}} =send_hash_response(Peer, ReqId, [PostHash]),update_peer_sent(AccPeers, Peer, Size);_ ->AccPeersend;sent ->AccPeersendend,
io:format("[DEBUG] incoming message ReqId:~p Type:~p Size:~p\tIsActiveOut:~p~n",[ReqIdStr, MsgType, MsgSize, IsActiveOut]),
io:format("[DEBUG] incoming message ReqId:~p Type:~p Size:~p\tIsActiveOut:~p~n",[ReqIdStr, MsgType, MsgSize, IsActiveOut]),
%% TODO: avoid double decode by pushing this into the db module[NewHeader, _] = posts:decode(Bin),H = proplists:get_value(hash, NewHeader),HasNot = not db:has_post(Db, H),%%io:format("[TEMP] got new(~p) post reply:~n~p~n", [HasNot, NewHeader]),HasNotend,
%% TODO: avoid double decode by pushing this into the db module[NewHeader, _] = posts:decode(Bin),H = proplists:get_value(hash, NewHeader),HasNot = not db:has_post(Db, H),%%io:format("[TEMP] got new(~p) post reply:~n~p~n", [HasNot, NewHeader]),HasNotend,
case db:load_post(Db, H) ofnotFound -> false;{ok, [_, _, Bin]} -> {true, Bin}endend,
case db:load_post(Db, H) ofnotFound -> false;{ok, [_, _, Bin]} -> {true, Bin}endend,
F = fun({received, ChanReqId, _}) ->CancelId =/= ChanReqIdend,UpdatedReqs = lists:filter(F, ChanReqs),{true, UpdatedReqs}end,
F = fun({received, ChanReqId, _}) ->CancelId =/= ChanReqIdend,UpdatedReqs = lists:filter(F, ChanReqs),{true, UpdatedReqs}end,
peers = update_peer_sent(NewPeers, Peer, RespSize),channels = maps:put(RequestedChan, NewChanReqs, TmpChans),activeIn = maps:put(ReqId, {Peer, Msg}, ActiveIn)}
peers = update_peer_sent(NewPeers, Peer, RespSize),channels = maps:put(RequestedChan, NewChanReqs, TmpChans),activeIn = maps:put(ReqId, {Peer, Msg}, ActiveIn)}
peers = update_peer_sent(NewPeers, Peer, RespSize),channels = maps:put(RequestedChan, NewChanReqs, TmpChans),activeIn = maps:put(ReqId, {Peer, Msg}, ActiveIn)}
peers = update_peer_sent(NewPeers, Peer, RespSize),channels = maps:put(RequestedChan, NewChanReqs, TmpChans),activeIn = maps:put(ReqId, {Peer, Msg}, ActiveIn)}
Header = [{requestId, ReqId},{circuitId, <<0,0,0,0>>},{ttl, 0}],
Header = [{requestId, ReqId},{circuitId, <<0, 0, 0, 0>>},{ttl, 0}],
Header = [{requestId, ReqId},{circuitId, <<0,0,0,0>>},{ttl, 3}],
Header = [{requestId, ReqId},{circuitId, <<0, 0, 0, 0>>},{ttl, 3}],
io:format("[DEBUG] sending post request(~p) for ~p hashes to ~p~n", [hex:bin_to_hexstr(ReqId), length(Hashes), Peer]),Header = [{requestId, ReqId},{circuitId, <<0,0,0,0>>},{ttl, 3}],
io:format("[DEBUG] sending post request(~p) for ~p hashes to ~p~n",[hex:bin_to_hexstr(ReqId), length(Hashes), Peer]),Header = [{requestId, ReqId},{circuitId, <<0, 0, 0, 0>>},{ttl, 3}],
io:format("[DEBUG] sending post response(~p) for ~p posts to ~p~n", [hex:bin_to_hexstr(ReqId), length(Posts), Peer]),Header = [{requestId, ReqId},{circuitId, <<0,0,0,0>>}],
io:format("[DEBUG] sending post response(~p) for ~p posts to ~p~n",[hex:bin_to_hexstr(ReqId), length(Posts), Peer]),Header = [{requestId, ReqId},{circuitId, <<0, 0, 0, 0>>}],
io:format("[DEBUG] sending hash response(~p) for ~p hashes to ~p~n", [hex:bin_to_hexstr(ReqId), length(Hashes), Peer]),Header = [{requestId, ReqId},{circuitId, <<0,0,0,0>>}],
io:format("[DEBUG] sending hash response(~p) for ~p hashes to ~p~n",[hex:bin_to_hexstr(ReqId), length(Hashes), Peer]),Header = [{requestId, ReqId},{circuitId, <<0, 0, 0, 0>>}],
Seed = case file:read_file(Fname) of%% load existing{ok, Content} when byte_size(Content) =:= 32 ->Content;%% generate new keypair{error, enoent} ->S = crypto:strong_rand_bytes(32),ok = file:write_file(Fname, S),ok = file:change_mode(Fname, 8#0400),io:format("[Cable] Created fresh keypair~n"),Send,
Seed =case file:read_file(Fname) of%% load existing{ok, Content} when byte_size(Content) =:= 32 ->Content;%% generate new keypair{error, enoent} ->S = crypto:strong_rand_bytes(32),ok = file:write_file(Fname, S),ok = file:change_mode(Fname, 8#0400),io:format("[Cable] Created fresh keypair~n"),Send,
human_bytesize(Size, ["b","kb","mb","gb"]).human_bytesize(S, [_|[_|_] = L]) when S >= 1024 ->human_bytesize(S/1024, L);human_bytesize(S, [M|_]) ->
human_bytesize(Size, ["b", "kb", "mb", "gb"]).human_bytesize(S, [_ | [_ | _] = L]) when S >= 1024 ->human_bytesize(S / 1024, L);human_bytesize(S, [M | _]) ->
post_request/0, cancel_request/0, channel_time_range_request/0,channel_state_request/0, channel_list_request/0,hash_response/0, post_response/0, channel_list_response/0,post_text/0, post_delete/0, post_info/0, post_topic/0, post_join/0, post_leave/0
post_request/0,cancel_request/0,channel_time_range_request/0,channel_state_request/0,channel_list_request/0,hash_response/0,post_response/0,channel_list_response/0,post_text/0,post_delete/0,post_info/0,post_topic/0,post_join/0,post_leave/0
{ok, J } = get_data(Index),#{ <<"binary">> := Bin, <<"obj">> := Obj, <<"name">> := Name } = J,{ok, [{name, Name},{binary, hex:hexstr_to_bin(binary_to_list(Bin))},{obj, Obj}]}.
{ok, J} = get_data(Index),#{<<"binary">> := Bin, <<"obj">> := Obj, <<"name">> := Name} = J,{ok, [{name, Name},{binary, hex:hexstr_to_bin(binary_to_list(Bin))},{obj, Obj}]}.
ActualEnd = case End of%% 00:00:00 01.01.21800 -> 6626966400000;_ -> Endend,ActualLimit = case Limit of0 -> 9001;_ -> Limitend,
ActualEnd =case End of%% 00:00:00 01.01.21800 -> 6626966400000;_ -> Endend,ActualLimit =case Limit of0 -> 9001;_ -> Limitend,
lists:foreach(fun(Link) ->Row = [{source, {blob, PostHash}}, {parent, {blob, Link}}]++ case LinkChan ofnull -> [];_ -> [{channel, LinkChan}]end,{rowid, _EdgeId} = sqlite3:write(Db, links, Row),io:format("[DB/Debug] Inserted Edge: ~p~n", [_EdgeId])end, Links),
lists:foreach(fun(Link) ->Row =[{source, {blob, PostHash}}, {parent, {blob, Link}}] ++case LinkChan ofnull -> [];_ -> [{channel, LinkChan}]end,{rowid, _EdgeId} = sqlite3:write(Db, links, Row),io:format("[DB/Debug] Inserted Edge: ~p~n", [_EdgeId])end,Links),
PostInsert = [{user_id, UserId}, {raw_post, Binary}] ++ HeaderTrimmed++ case proplists:get_value(type, Header) of1 -> % deleteConcat = iolist_to_binary(proplists:get_value(hashes, Body)),[{deletedHashes, Concat}];2 -> %% infosChan = proplists:get_value(channel, Body),Infos = proplists:get_value(infos, Body),[{channel, Chan}, {infos, {blob, jsone:encode(Infos)}}];%% text, topic, join or leave_ -> Bodyend,
PostInsert =[{user_id, UserId}, {raw_post, Binary}] ++ HeaderTrimmed ++case proplists:get_value(type, Header) of% delete1 ->Concat = iolist_to_binary(proplists:get_value(hashes, Body)),[{deletedHashes, Concat}];%% infos2 ->Chan = proplists:get_value(channel, Body),Infos = proplists:get_value(infos, Body),[{channel, Chan}, {infos, {blob, jsone:encode(Infos)}}];%% text, topic, join or leave_ ->Bodyend,
Prepared = lists:map(fun({Col, Val}) ->X = case Val of_ when is_binary(Val) -> {blob, Val};_ -> Valend,{Col, X}end, PostInsert),
Prepared = lists:map(fun({Col, Val}) ->X =case Val of_ when is_binary(Val) -> {blob, Val};_ -> Valend,{Col, X}end,PostInsert),
handle_call({loadPostById, IntId}, _From, [{sql, Db}, _] = State) when is_integer(IntId), IntId >= 0 ->
handle_call({loadPostById, IntId}, _From, [{sql, Db}, _] = State) whenis_integer(IntId), IntId >= 0->
[{columns, ["count"]},{rows, [{Count}]}] = sqlite3:sql_exec(Db, Qry, [{blob, Hash}]),Result = case Count of0 -> false;1 -> true;Other -> throw({unexpectedCount, Other})end,
[{columns, ["count"]},{rows, [{Count}]}] = sqlite3:sql_exec(Db, Qry, [{blob, Hash}]),Result =case Count of0 -> false;1 -> true;Other -> throw({unexpectedCount, Other})end,
[{columns, ["user_id", "timestamp", "text"]}, {rows, Rows}] = sqlite3:sql_exec(Db, Qry, [QryChan]), %%, Start, End, Limit]),
%%, Start, End, Limit]),[{columns, ["user_id", "timestamp", "text"]}, {rows, Rows}] = sqlite3:sql_exec(Db, Qry, [QryChan]),
NewUsers = case maps:is_key(UserId, Users) oftrue -> Users;false ->[{columns, ["name", "public_key"]},{rows, [{NameBin, {blob, PubKey}}]}] = sqlite3:sql_exec(Db, QryUser, [UserId]),Name = unicode:characters_to_list(NameBin),Users#{UserId => {Name, PubKey}}end,Text = unicode:characters_to_list(TextBin),{Texts ++ [{UserId, Ts, Text}], NewUsers}end,
NewUsers =case maps:is_key(UserId, Users) oftrue ->Users;false ->[{columns, ["name", "public_key"]},{rows, [{NameBin, {blob, PubKey}}]}] = sqlite3:sql_exec(Db, QryUser, [UserId]),Name = unicode:characters_to_list(NameBin),Users#{UserId => {Name, PubKey}}end,Text = unicode:characters_to_list(TextBin),{Texts ++ [{UserId, Ts, Text}], NewUsers}end,
Qry = "SELECT DISTINCT source FROM links "++ "WHERE source NOT IN ("++ " SELECT DISTINCT parent FROM links"++ " WHERE channel = ? or channel is null)",QryChan = case Chan ofnull -> null;_ when is_list(Chan) -> {blob, list_to_binary(Chan)}end,
Qry ="SELECT DISTINCT source FROM links " ++"WHERE source NOT IN (" ++" SELECT DISTINCT parent FROM links" ++" WHERE channel = ? or channel is null)",QryChan =case Chan ofnull -> null;_ when is_list(Chan) -> {blob, list_to_binary(Chan)}end,
InfoPostQry = "SELECT hash from posts where user_id = ? and type = 2 order by timestamp desc limit 1",InfosHashes = lists:foldl(fun({_, UserId}, Acc) ->[{columns, ["hash"]},{rows, InfoRows}] = sqlite3:sql_exec(Db, InfoPostQry, [UserId]),case InfoRows of[{{blob, H}}] -> sets:add_element(H, Acc);[] -> Accendend, sets:new(), Rows),
InfoPostQry ="SELECT hash from posts where user_id = ? and type = 2 order by timestamp desc limit 1",InfosHashes = lists:foldl(fun({_, UserId}, Acc) ->[{columns, ["hash"]},{rows, InfoRows}] = sqlite3:sql_exec(Db, InfoPostQry, [UserId]),case InfoRows of[{{blob, H}}] -> sets:add_element(H, Acc);[] -> Accendend,sets:new(),Rows),
MembersQry = "SELECT u.name as name, u.public_key as pub_key, p.timestamp as timestamp, p.type as type "++ "FROM channel_members m "++ "JOIN posts p ON m.last_hash = p.hash "++ "JOIN users u ON m.user_id = u.id "++ "WHERE m.channel = ?",
MembersQry ="SELECT u.name as name, u.public_key as pub_key, p.timestamp as timestamp, p.type as type " ++"FROM channel_members m " ++"JOIN posts p ON m.last_hash = p.hash " ++"JOIN users u ON m.user_id = u.id " ++"WHERE m.channel = ?",
[ {columns, ["name", "pub_key", "timestamp", "type"]}, {rows, Rows}] = sqlite3:sql_exec(Db, MembersQry, [Chan]),
[{columns, ["name", "pub_key", "timestamp", "type"]},{rows, Rows}] = sqlite3:sql_exec(Db, MembersQry, [Chan]),
M = lists:foldl(fun(Row, Acc) ->{NameBin, {blob, PubKey}, When, PostType} = Row,Name = unicode:characters_to_list(NameBin),HasLeft = case PostType of4 -> false;5 -> trueend,Acc#{PubKey => {Name, When, HasLeft}}end, #{}, Rows),
M = lists:foldl(fun(Row, Acc) ->{NameBin, {blob, PubKey}, When, PostType} = Row,Name = unicode:characters_to_list(NameBin),HasLeft =case PostType of4 -> false;5 -> trueend,Acc#{PubKey => {Name, When, HasLeft}}end,#{},Rows),
Res = case sqlite3:write(Db, channels, [{name, Name}, {topic, ""}]) of{rowid, _RowId} -> ok;{error, 19, _Msg} -> ok %% constraint violation => already joinedend,
Res =case sqlite3:write(Db, channels, [{name, Name}, {topic, ""}]) of{rowid, _RowId} -> ok;%% constraint violation => already joined{error, 19, _Msg} -> okend,
0 -> ok; %% text: nothing to do1 -> %% delete[ {hashes, Hashes} ] = Body,lists:foreach(fun(H) ->ok = sqlite3:delete(Db, posts, {hash, {blob, H}})end, Hashes),
%% text: nothing to do0 ->ok;%% delete1 ->[{hashes, Hashes}] = Body,lists:foreach(fun(H) ->ok = sqlite3:delete(Db, posts, {hash, {blob, H}})end,Hashes),
[ {channel, Chan} ] = Body,Qry = "INSERT INTO channel_members(channel, user_id, last_hash) VALUES (?, ?, ?) "++ "ON CONFLICT(user_id) DO UPDATE SET last_hash=excluded.last_hash",Res = sqlite3:sql_exec(Db, Qry, [ Chan, UserId, {blob, Hash}]),
[{channel, Chan}] = Body,Qry ="INSERT INTO channel_members(channel, user_id, last_hash) VALUES (?, ?, ?) " ++"ON CONFLICT(user_id) DO UPDATE SET last_hash=excluded.last_hash",Res = sqlite3:sql_exec(Db, Qry, [Chan, UserId, {blob, Hash}]),
[{columns, ["id","public_key","name"]},{rows, [{UserId, {blob, PubKey}, _Name}]}] = sqlite3:read(Db, users, {public_key, {blob, PubKey}}),
[{columns, ["id", "public_key", "name"]},{rows, [{UserId, {blob, PubKey}, _Name}]}] = sqlite3:read(Db, users, {public_key, {blob, PubKey}}),
ExpectedCols = ["id", "hash", "type", "timestamp", "user_id", "raw_post","channel", "text", "topic", "deletedHashes", "infos"],case ReadResult of[{columns, ExpectedCols},{rows, [Row]}] -> {ok, unpackPostRow(Db, Row)};_Other -> notFoundend.
ExpectedCols = ["id","hash","type","timestamp","user_id","raw_post","channel","text","topic","deletedHashes","infos"],case ReadResult of[{columns, ExpectedCols},{rows, [Row]}] ->{ok, unpackPostRow(Db, Row)};_Other ->notFoundend.
{_RowId, {blob, Hash}, Type, Timestamp, UserId, {blob, RawPost},ChannelBlob, TextBlob, TopicBlob, Deleteds, InfosBlob} = Row,
{_RowId, {blob, Hash}, Type, Timestamp, UserId, {blob, RawPost}, ChannelBlob, TextBlob,TopicBlob, Deleteds, InfosBlob} = Row,
[{columns, ["channel", "source", "parent"]},{rows, LinkRows}] = sqlite3:read(Db, links, {source, {blob, Hash}}),Links = [Parent || {_, {blob, _Source}, {blob, Parent}} <-LinkRows],
[{columns, ["channel", "source", "parent"]},{rows, LinkRows}] = sqlite3:read(Db, links, {source, {blob, Hash}}),Links = [Parent || {_, {blob, _Source}, {blob, Parent}} <- LinkRows],
Qry = "SELECT public_key from users where id = ?",[{columns, ["public_key"]},{rows, [{{blob, PubKey}}]}] = sqlite3:sql_exec(Db, Qry, [UserId]),
Qry = "SELECT public_key from users where id = ?",[{columns, ["public_key"]},{rows, [{{blob, PubKey}}]}] = sqlite3:sql_exec(Db, Qry, [UserId]),
Header = [ {public_key, PubKey}, {links, Links}, {type, Type}, {timestamp, Timestamp}, {hash, Hash}],
Header = [{public_key, PubKey},{links, Links},{type, Type},{timestamp, Timestamp},{hash, Hash}],
Body = case Type of0 ->{blob, Text} = TextBlob,{blob, Channel} = ChannelBlob,[{channel, Channel}, {text, Text}];1 ->{blob, DeletedConcat} = Deleteds,[{hashes, split_concated_hashes(DeletedConcat)}];2 ->{blob, InfosJson} = InfosBlob,[{infos, jsone:decode(InfosJson)}];3 ->{blob, Topic} = TopicBlob,{blob, Channel} = ChannelBlob,[{channel, Channel}, {topic, Topic}];4 ->{blob, Channel} = ChannelBlob,[{channel, Channel}];5 ->{blob, Channel} = ChannelBlob,[{channel, Channel}]end,
Body =case Type of0 ->{blob, Text} = TextBlob,{blob, Channel} = ChannelBlob,[{channel, Channel}, {text, Text}];1 ->{blob, DeletedConcat} = Deleteds,[{hashes, split_concated_hashes(DeletedConcat)}];2 ->{blob, InfosJson} = InfosBlob,[{infos, jsone:decode(InfosJson)}];3 ->{blob, Topic} = TopicBlob,{blob, Channel} = ChannelBlob,[{channel, Channel}, {topic, Topic}];4 ->{blob, Channel} = ChannelBlob,[{channel, Channel}];5 ->{blob, Channel} = ChannelBlob,[{channel, Channel}]end,
channels => [{name, text, [primary_key]}, {topic, text, not_null}],channel_members => [ColId, {channel, text, not_null}, {user_id, integer, [unique, not_null]}, {last_hash, blob, not_null}],users => [ColId, {public_key, blob, [unique, not_null]}, {name, text, not_null}]},
channels => [{name, text, [primary_key]}, {topic, text, not_null}],channel_members => [ColId,{channel, text, not_null},{user_id, integer, [unique, not_null]},{last_hash, blob, not_null}],users => [ColId, {public_key, blob, [unique, not_null]}, {name, text, not_null}]},