Compare commits
167 Commits
52ae52ca9f
...
master
Author | SHA1 | Date | |
---|---|---|---|
f8537aad6d | |||
8d589505e5 | |||
511e260157 | |||
839fb7c0f9 | |||
3af112b860 | |||
7e68a12adb | |||
ec8924d05a | |||
d2bd4deb82 | |||
9b6caf4e62 | |||
050de1a0b3 | |||
606c42cd5e | |||
af55df1ff5 | |||
c0fd74f1e6 | |||
24cc151938 | |||
cc663d88c8 | |||
118eb29d17 | |||
e9624e4576 | |||
78a8b797e6 | |||
9179021544 | |||
d049f87cdc | |||
e674a0cb33 | |||
c1883f1524 | |||
c284b15788 | |||
17c991f442 | |||
d657e64ae4 | |||
2e2ebad364 | |||
428eda0ab7 | |||
a3557d5183 | |||
5491244d01 | |||
04d2b0246a | |||
4dfec86279 | |||
d14cd161da | |||
7c17a11426 | |||
6cfc54d943 | |||
5aec7b3ad4 | |||
d709cb9454 | |||
05df08efcb | |||
813611bde7 | |||
7fa997d443 | |||
2513f0303a | |||
99a9aa14af | |||
09892709ec | |||
3ac6b02e56 | |||
8fca09d853 | |||
aaa4d88a2f | |||
3bf75eb990 | |||
54c36c68ad | |||
caab80f346 | |||
dfa2b5fa83 | |||
2ac70d5448 | |||
2a246744db | |||
b4f7071990 | |||
903702c719 | |||
54b932e9c1 | |||
13cfda928d | |||
b556cd0361 | |||
657fdbbf48 | |||
a65d4f1a69 | |||
a5cfbf854d | |||
385c5f3298 | |||
31df45e771 | |||
0b8e3c4c90 | |||
ce479cc5b9 | |||
bdfa8c7bb1 | |||
8300a699a3 | |||
729ba7e02a | |||
b15f571938 | |||
ead810e666 | |||
4b412ae0f3 | |||
e4efff1824 | |||
788512c391 | |||
6bf6fadaaa | |||
0070a64d5f | |||
1398c6040a | |||
12ad5ced49 | |||
53145f1c5e | |||
6867086c4f | |||
f80411bf21 | |||
0a70206b11 | |||
f40f6520d2 | |||
b47ef2480c | |||
cba13ad91a | |||
1ba84dcefc | |||
0c0aa6e807 | |||
3467ea15d9 | |||
aa63bb745f | |||
f20681adab | |||
4b3d7548bd | |||
c4b61e16c5 | |||
9dcb579d93 | |||
defafcf996 | |||
db4c3cbbc8 | |||
07dfae8f0e | |||
05cc2ee218 | |||
034dcf5215 | |||
c58199385e | |||
b6b9dc238a | |||
9ec682d708 | |||
465a366e79 | |||
2969227656 | |||
9561531c7c | |||
8c8326780f | |||
0466b1fe05 | |||
69a07d77d5 | |||
a19f228c8e | |||
bcb3489de4 | |||
0fd7548ba4 | |||
24183ff581 | |||
df9c6b5d46 | |||
423ef5c4b1 | |||
b87c0bff3e | |||
978b6fdfd1 | |||
93e972900f | |||
f8a1cb6f68 | |||
44661de993 | |||
48b2e78b41 | |||
a9f3b548e5 | |||
379b40b2fb | |||
cbedad7178 | |||
9283764f42 | |||
25cf1808e3 | |||
6788487368 | |||
e406010374 | |||
289e39c677 | |||
6511ff6280 | |||
206dda0761 | |||
7a6c1cd085 | |||
04e81fcef1 | |||
0998845817 | |||
922668e2a3 | |||
5eb6be1415 | |||
a3b2473eed | |||
043980042d | |||
58118bd6bb | |||
86d51d6dfb | |||
6a42d261f1 | |||
1dda1b27a8 | |||
535181d669 | |||
c3ad8d5d2d | |||
4cd928fdc6 | |||
420b7d0af0 | |||
432f449558 | |||
65f9ee2e07 | |||
eeb39338e2 | |||
c203431ede | |||
04a390d558 | |||
08ff9d58b8 | |||
cd41a59518 | |||
4750915b49 | |||
e0faaf8cef | |||
539e2c528e | |||
d082724432 | |||
5eb06ebcc5 | |||
753cd30f38 | |||
4a009b69eb | |||
9d5ba42dfc | |||
71df3a792b | |||
d748735ded | |||
698740e20a | |||
940a8d395c | |||
6f2a65dac9 | |||
d9155bac51 | |||
0f8bdf5f66 | |||
ce758c5bb1 | |||
c4bb8c5693 | |||
61958593a1 | |||
5ac92ce3a8 |
103
.VSCodeCounter/2024-08-27_08-23-38/details.md
Normal file
103
.VSCodeCounter/2024-08-27_08-23-38/details.md
Normal file
@ -0,0 +1,103 @@
|
||||
# Details
|
||||
|
||||
Date : 2024-08-27 08:23:38
|
||||
|
||||
Directory /home/yves/Documents/code/go/meow/meowlib
|
||||
|
||||
Total : 88 files, 10488 codes, 836 comments, 1073 blanks, all 12397 lines
|
||||
|
||||
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
|
||||
|
||||
## Files
|
||||
| filename | language | code | comment | blank | total |
|
||||
| :--- | :--- | ---: | ---: | ---: | ---: |
|
||||
| [.drone.yml](/.drone.yml) | YAML | 9 | 0 | 3 | 12 |
|
||||
| [README.md](/README.md) | Markdown | 21 | 0 | 8 | 29 |
|
||||
| [asymcrypt.go](/asymcrypt.go) | Go | 237 | 36 | 41 | 314 |
|
||||
| [asymcrypt_test.go](/asymcrypt_test.go) | Go | 101 | 65 | 17 | 183 |
|
||||
| [buffer.go](/buffer.go) | Go | 62 | 0 | 5 | 67 |
|
||||
| [buffer_test.go](/buffer_test.go) | Go | 20 | 0 | 5 | 25 |
|
||||
| [clean.sh](/clean.sh) | Shell Script | 9 | 1 | 1 | 11 |
|
||||
| [client/avatar.go](/client/avatar.go) | Go | 6 | 0 | 3 | 9 |
|
||||
| [client/config.go](/client/config.go) | Go | 99 | 7 | 15 | 121 |
|
||||
| [client/config_test.go](/client/config_test.go) | Go | 17 | 0 | 4 | 21 |
|
||||
| [client/dbmessage.go](/client/dbmessage.go) | Go | 46 | 0 | 6 | 52 |
|
||||
| [client/helpers/backgroundHelper.go](/client/helpers/backgroundHelper.go) | Go | 140 | 28 | 23 | 191 |
|
||||
| [client/helpers/call.go](/client/helpers/call.go) | Go | 42 | 3 | 11 | 56 |
|
||||
| [client/helpers/contactHelper.go](/client/helpers/contactHelper.go) | Go | 1 | 0 | 1 | 2 |
|
||||
| [client/helpers/invitationAnswerHelper.go](/client/helpers/invitationAnswerHelper.go) | Go | 106 | 27 | 23 | 156 |
|
||||
| [client/helpers/invitationCheckHelper.go](/client/helpers/invitationCheckHelper.go) | Go | 52 | 65 | 10 | 127 |
|
||||
| [client/helpers/invitationCreateHelper.go](/client/helpers/invitationCreateHelper.go) | Go | 77 | 50 | 17 | 144 |
|
||||
| [client/helpers/invitationFinalizeHelper.go](/client/helpers/invitationFinalizeHelper.go) | Go | 34 | 12 | 8 | 54 |
|
||||
| [client/helpers/logger.go](/client/helpers/logger.go) | Go | 8 | 1 | 4 | 13 |
|
||||
| [client/helpers/messageHelper.go](/client/helpers/messageHelper.go) | Go | 66 | 9 | 12 | 87 |
|
||||
| [client/helpers/networkHelper.go](/client/helpers/networkHelper.go) | Go | 25 | 2 | 4 | 31 |
|
||||
| [client/helpers/serverHelper.go](/client/helpers/serverHelper.go) | Go | 1 | 0 | 1 | 2 |
|
||||
| [client/helpers/storageHelper.go](/client/helpers/storageHelper.go) | Go | 13 | 0 | 3 | 16 |
|
||||
| [client/identity.go](/client/identity.go) | Go | 254 | 81 | 27 | 362 |
|
||||
| [client/identity_test.go](/client/identity_test.go) | Go | 91 | 27 | 11 | 129 |
|
||||
| [client/internalusermessage.go](/client/internalusermessage.go) | Go | 41 | 5 | 5 | 51 |
|
||||
| [client/logger.go](/client/logger.go) | Go | 8 | 1 | 4 | 13 |
|
||||
| [client/matriochka.go](/client/matriochka.go) | Go | 44 | 4 | 4 | 52 |
|
||||
| [client/messagestorage.go](/client/messagestorage.go) | Go | 314 | 30 | 23 | 367 |
|
||||
| [client/messagestorage_test.go](/client/messagestorage_test.go) | Go | 74 | 5 | 5 | 84 |
|
||||
| [client/peer.go](/client/peer.go) | Go | 249 | 42 | 35 | 326 |
|
||||
| [client/peer_test.go](/client/peer_test.go) | Go | 26 | 0 | 4 | 30 |
|
||||
| [client/peerlist.go](/client/peerlist.go) | Go | 34 | 11 | 7 | 52 |
|
||||
| [client/peerstorage.go](/client/peerstorage.go) | Go | 209 | 20 | 20 | 249 |
|
||||
| [client/peerstorage_test.go](/client/peerstorage_test.go) | Go | 50 | 1 | 10 | 61 |
|
||||
| [client/server.go](/client/server.go) | Go | 249 | 33 | 26 | 308 |
|
||||
| [client/serverlist.go](/client/serverlist.go) | Go | 37 | 9 | 7 | 53 |
|
||||
| [client/serverstorage.go](/client/serverstorage.go) | Go | 234 | 17 | 18 | 269 |
|
||||
| [client/serverstorage_test.go](/client/serverstorage_test.go) | Go | 177 | 21 | 27 | 225 |
|
||||
| [contactcard.go](/contactcard.go) | Go | 147 | 7 | 24 | 178 |
|
||||
| [contactcard_test.go](/contactcard_test.go) | Go | 61 | 0 | 5 | 66 |
|
||||
| [crypt.go](/crypt.go) | Go | 5 | 0 | 2 | 7 |
|
||||
| [doc/act_01_send_msg.puml](/doc/act_01_send_msg.puml) | PlantUML | 21 | 0 | 1 | 22 |
|
||||
| [doc/act_02_srv_recv_msg.puml](/doc/act_02_srv_recv_msg.puml) | PlantUML | 21 | 0 | 1 | 22 |
|
||||
| [doc/act_03_srv_proc_msg.puml](/doc/act_03_srv_proc_msg.puml) | PlantUML | 20 | 0 | 0 | 20 |
|
||||
| [doc/architecture.tex](/doc/architecture.tex) | LaTeX | 0 | 0 | 1 | 1 |
|
||||
| [doc/class_messages01.puml](/doc/class_messages01.puml) | PlantUML | 70 | 0 | 14 | 84 |
|
||||
| [doc/docgen.sh](/doc/docgen.sh) | Shell Script | 13 | 1 | 4 | 18 |
|
||||
| [doc/endpoints/company_endpoint.puml](/doc/endpoints/company_endpoint.puml) | PlantUML | 7 | 0 | 0 | 7 |
|
||||
| [doc/endpoints/public_endpoint.puml](/doc/endpoints/public_endpoint.puml) | PlantUML | 10 | 0 | 0 | 10 |
|
||||
| [doc/general_deployment.puml](/doc/general_deployment.puml) | PlantUML | 19 | 0 | 3 | 22 |
|
||||
| [doc/invitation/sq_invitation.puml](/doc/invitation/sq_invitation.puml) | PlantUML | 7 | 0 | 0 | 7 |
|
||||
| [doc/invitation/sq_srvinv01.puml](/doc/invitation/sq_srvinv01.puml) | PlantUML | 12 | 0 | 0 | 12 |
|
||||
| [doc/invitation/sq_srvinv02.puml](/doc/invitation/sq_srvinv02.puml) | PlantUML | 11 | 0 | 0 | 11 |
|
||||
| [doc/invitation/sq_srvinv03.puml](/doc/invitation/sq_srvinv03.puml) | PlantUML | 11 | 0 | 1 | 12 |
|
||||
| [doc/invitation/sq_srvinv04.puml](/doc/invitation/sq_srvinv04.puml) | PlantUML | 8 | 0 | 1 | 9 |
|
||||
| [doc/meow.svg](/doc/meow.svg) | SVG | 2,814 | 1 | 2 | 2,817 |
|
||||
| [doc/meow.tex](/doc/meow.tex) | LaTeX | 175 | 0 | 50 | 225 |
|
||||
| [doc/messaging/sq_msg01.puml](/doc/messaging/sq_msg01.puml) | PlantUML | 9 | 0 | 1 | 10 |
|
||||
| [doc/messaging/wbs_messages_encapsulation.puml](/doc/messaging/wbs_messages_encapsulation.puml) | PlantUML | 13 | 0 | 2 | 15 |
|
||||
| [doc/protocol.tex](/doc/protocol.tex) | LaTeX | 60 | 0 | 24 | 84 |
|
||||
| [doc/server/server_deployment.puml](/doc/server/server_deployment.puml) | PlantUML | 33 | 0 | 3 | 36 |
|
||||
| [doc/server/server_messaging.puml](/doc/server/server_messaging.puml) | PlantUML | 18 | 0 | 6 | 24 |
|
||||
| [doc/server/sq_01_srvmessaging.puml](/doc/server/sq_01_srvmessaging.puml) | PlantUML | 18 | 0 | 2 | 20 |
|
||||
| [doc/server/sq_02_srvmessaging.puml](/doc/server/sq_02_srvmessaging.puml) | PlantUML | 27 | 0 | 3 | 30 |
|
||||
| [doc/usecase01.puml](/doc/usecase01.puml) | PlantUML | 9 | 0 | 7 | 16 |
|
||||
| [doc/usecase02.puml](/doc/usecase02.puml) | PlantUML | 10 | 0 | 6 | 16 |
|
||||
| [doc/usecase03.puml](/doc/usecase03.puml) | PlantUML | 33 | 0 | 7 | 40 |
|
||||
| [endtoend_test.go](/endtoend_test.go) | Go | 125 | 55 | 18 | 198 |
|
||||
| [go.mod](/go.mod) | Go Module File | 45 | 0 | 5 | 50 |
|
||||
| [go.sum](/go.sum) | Go Checksum File | 344 | 0 | 1 | 345 |
|
||||
| [http.go](/http.go) | Go | 41 | 0 | 4 | 45 |
|
||||
| [lokiwriter.go](/lokiwriter.go) | Go | 74 | 5 | 19 | 98 |
|
||||
| [message.go](/message.go) | Go | 37 | 0 | 7 | 44 |
|
||||
| [messages.pb.go](/messages.pb.go) | Go | 2,058 | 45 | 265 | 2,368 |
|
||||
| [pb/messages.proto](/pb/messages.proto) | Protocol Buffers | 194 | 0 | 40 | 234 |
|
||||
| [pb/messages.py](/pb/messages.py) | Python | 11 | 1 | 5 | 17 |
|
||||
| [pb/protogen.bat](/pb/protogen.bat) | Batch | 4 | 0 | 1 | 5 |
|
||||
| [pb/protogen.sh](/pb/protogen.sh) | Shell Script | 12 | 1 | 1 | 14 |
|
||||
| [proto_test.go](/proto_test.go) | Go | 33 | 0 | 5 | 38 |
|
||||
| [server/identity.go](/server/identity.go) | Go | 135 | 15 | 20 | 170 |
|
||||
| [server/invitation.go](/server/invitation.go) | Go | 62 | 1 | 8 | 71 |
|
||||
| [server/logger.go](/server/logger.go) | Go | 8 | 1 | 4 | 13 |
|
||||
| [server/router.go](/server/router.go) | Go | 271 | 54 | 21 | 346 |
|
||||
| [server/videoserver.go](/server/videoserver.go) | Go | 35 | 0 | 7 | 42 |
|
||||
| [servercard.go](/servercard.go) | Go | 10 | 0 | 3 | 13 |
|
||||
| [symcrypt.go](/symcrypt.go) | Go | 26 | 36 | 7 | 69 |
|
||||
| [symcrypt_test.go](/symcrypt_test.go) | Go | 18 | 0 | 4 | 22 |
|
||||
|
||||
[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
|
15
.VSCodeCounter/2024-08-27_08-23-38/diff-details.md
Normal file
15
.VSCodeCounter/2024-08-27_08-23-38/diff-details.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Diff Details
|
||||
|
||||
Date : 2024-08-27 08:23:38
|
||||
|
||||
Directory /home/yves/Documents/code/go/meow/meowlib
|
||||
|
||||
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
|
||||
|
||||
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
|
||||
|
||||
## Files
|
||||
| filename | language | code | comment | blank | total |
|
||||
| :--- | :--- | ---: | ---: | ---: | ---: |
|
||||
|
||||
[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details
|
2
.VSCodeCounter/2024-08-27_08-23-38/diff.csv
Normal file
2
.VSCodeCounter/2024-08-27_08-23-38/diff.csv
Normal file
@ -0,0 +1,2 @@
|
||||
"filename", "language", "", "comment", "blank", "total"
|
||||
"Total", "-", , 0, 0, 0
|
|
19
.VSCodeCounter/2024-08-27_08-23-38/diff.md
Normal file
19
.VSCodeCounter/2024-08-27_08-23-38/diff.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Diff Summary
|
||||
|
||||
Date : 2024-08-27 08:23:38
|
||||
|
||||
Directory /home/yves/Documents/code/go/meow/meowlib
|
||||
|
||||
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
|
||||
|
||||
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
|
||||
|
||||
## Languages
|
||||
| language | files | code | comment | blank | total |
|
||||
| :--- | ---: | ---: | ---: | ---: | ---: |
|
||||
|
||||
## Directories
|
||||
| path | files | code | comment | blank | total |
|
||||
| :--- | ---: | ---: | ---: | ---: | ---: |
|
||||
|
||||
[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md)
|
22
.VSCodeCounter/2024-08-27_08-23-38/diff.txt
Normal file
22
.VSCodeCounter/2024-08-27_08-23-38/diff.txt
Normal file
@ -0,0 +1,22 @@
|
||||
Date : 2024-08-27 08:23:38
|
||||
Directory : /home/yves/Documents/code/go/meow/meowlib
|
||||
Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines
|
||||
|
||||
Languages
|
||||
+----------+------------+------------+------------+------------+------------+
|
||||
| language | files | code | comment | blank | total |
|
||||
+----------+------------+------------+------------+------------+------------+
|
||||
+----------+------------+------------+------------+------------+------------+
|
||||
|
||||
Directories
|
||||
+------+------------+------------+------------+------------+------------+
|
||||
| path | files | code | comment | blank | total |
|
||||
+------+------------+------------+------------+------------+------------+
|
||||
+------+------------+------------+------------+------------+------------+
|
||||
|
||||
Files
|
||||
+----------+----------+------------+------------+------------+------------+
|
||||
| filename | language | code | comment | blank | total |
|
||||
+----------+----------+------------+------------+------------+------------+
|
||||
| Total | | 0 | 0 | 0 | 0 |
|
||||
+----------+----------+------------+------------+------------+------------+
|
90
.VSCodeCounter/2024-08-27_08-23-38/results.csv
Normal file
90
.VSCodeCounter/2024-08-27_08-23-38/results.csv
Normal file
@ -0,0 +1,90 @@
|
||||
"filename", "language", "Go", "Go Module File", "Markdown", "YAML", "Go Checksum File", "Shell Script", "Batch", "Protocol Buffers", "Python", "LaTeX", "PlantUML", "SVG", "comment", "blank", "total"
|
||||
"/home/yves/Documents/code/go/meow/meowlib/.drone.yml", "YAML", 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 12
|
||||
"/home/yves/Documents/code/go/meow/meowlib/README.md", "Markdown", 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 29
|
||||
"/home/yves/Documents/code/go/meow/meowlib/asymcrypt.go", "Go", 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 41, 314
|
||||
"/home/yves/Documents/code/go/meow/meowlib/asymcrypt_test.go", "Go", 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 17, 183
|
||||
"/home/yves/Documents/code/go/meow/meowlib/buffer.go", "Go", 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 67
|
||||
"/home/yves/Documents/code/go/meow/meowlib/buffer_test.go", "Go", 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 25
|
||||
"/home/yves/Documents/code/go/meow/meowlib/clean.sh", "Shell Script", 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 1, 1, 11
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/avatar.go", "Go", 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 9
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/config.go", "Go", 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 15, 121
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/config_test.go", "Go", 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 21
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/dbmessage.go", "Go", 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 52
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/backgroundHelper.go", "Go", 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 23, 191
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/call.go", "Go", 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 11, 56
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/contactHelper.go", "Go", 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationAnswerHelper.go", "Go", 106, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 23, 156
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationCheckHelper.go", "Go", 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 10, 127
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationCreateHelper.go", "Go", 77, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 17, 144
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationFinalizeHelper.go", "Go", 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 8, 54
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/logger.go", "Go", 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 13
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/messageHelper.go", "Go", 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 12, 87
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/networkHelper.go", "Go", 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 31
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/serverHelper.go", "Go", 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/helpers/storageHelper.go", "Go", 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 16
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/identity.go", "Go", 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 81, 27, 362
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/identity_test.go", "Go", 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 11, 129
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/internalusermessage.go", "Go", 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 51
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/logger.go", "Go", 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 13
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/matriochka.go", "Go", 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 52
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/messagestorage.go", "Go", 314, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 23, 367
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/messagestorage_test.go", "Go", 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 84
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/peer.go", "Go", 249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 35, 326
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/peer_test.go", "Go", 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 30
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/peerlist.go", "Go", 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 7, 52
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/peerstorage.go", "Go", 209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20, 249
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/peerstorage_test.go", "Go", 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 10, 61
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/server.go", "Go", 249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 26, 308
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/serverlist.go", "Go", 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 7, 53
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/serverstorage.go", "Go", 234, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 269
|
||||
"/home/yves/Documents/code/go/meow/meowlib/client/serverstorage_test.go", "Go", 177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 27, 225
|
||||
"/home/yves/Documents/code/go/meow/meowlib/contactcard.go", "Go", 147, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 24, 178
|
||||
"/home/yves/Documents/code/go/meow/meowlib/contactcard_test.go", "Go", 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 66
|
||||
"/home/yves/Documents/code/go/meow/meowlib/crypt.go", "Go", 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 7
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/act_01_send_msg.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 1, 22
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/act_02_srv_recv_msg.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 1, 22
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/act_03_srv_proc_msg.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 20
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/architecture.tex", "LaTeX", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/class_messages01.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70, 0, 0, 14, 84
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/docgen.sh", "Shell Script", 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, 0, 1, 4, 18
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/endpoints/company_endpoint.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/endpoints/public_endpoint.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/general_deployment.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 3, 22
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_invitation.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 7
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv01.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 12
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv02.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 11
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv03.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 1, 12
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv04.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 1, 9
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/meow.svg", "SVG", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2814, 1, 2, 2817
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/meow.tex", "LaTeX", 0, 0, 0, 0, 0, 0, 0, 0, 0, 175, 0, 0, 0, 50, 225
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/messaging/sq_msg01.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 1, 10
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/messaging/wbs_messages_encapsulation.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 2, 15
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/protocol.tex", "LaTeX", 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 24, 84
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/server/server_deployment.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 3, 36
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/server/server_messaging.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 6, 24
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/server/sq_01_srvmessaging.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 2, 20
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/server/sq_02_srvmessaging.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 3, 30
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/usecase01.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 7, 16
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/usecase02.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 6, 16
|
||||
"/home/yves/Documents/code/go/meow/meowlib/doc/usecase03.puml", "PlantUML", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 7, 40
|
||||
"/home/yves/Documents/code/go/meow/meowlib/endtoend_test.go", "Go", 125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 18, 198
|
||||
"/home/yves/Documents/code/go/meow/meowlib/go.mod", "Go Module File", 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 50
|
||||
"/home/yves/Documents/code/go/meow/meowlib/go.sum", "Go Checksum File", 0, 0, 0, 0, 344, 0, 0, 0, 0, 0, 0, 0, 0, 1, 345
|
||||
"/home/yves/Documents/code/go/meow/meowlib/http.go", "Go", 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 45
|
||||
"/home/yves/Documents/code/go/meow/meowlib/lokiwriter.go", "Go", 74, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 19, 98
|
||||
"/home/yves/Documents/code/go/meow/meowlib/message.go", "Go", 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 44
|
||||
"/home/yves/Documents/code/go/meow/meowlib/messages.pb.go", "Go", 2058, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 265, 2368
|
||||
"/home/yves/Documents/code/go/meow/meowlib/pb/messages.proto", "Protocol Buffers", 0, 0, 0, 0, 0, 0, 0, 194, 0, 0, 0, 0, 0, 40, 234
|
||||
"/home/yves/Documents/code/go/meow/meowlib/pb/messages.py", "Python", 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 1, 5, 17
|
||||
"/home/yves/Documents/code/go/meow/meowlib/pb/protogen.bat", "Batch", 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 5
|
||||
"/home/yves/Documents/code/go/meow/meowlib/pb/protogen.sh", "Shell Script", 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 1, 1, 14
|
||||
"/home/yves/Documents/code/go/meow/meowlib/proto_test.go", "Go", 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 38
|
||||
"/home/yves/Documents/code/go/meow/meowlib/server/identity.go", "Go", 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 20, 170
|
||||
"/home/yves/Documents/code/go/meow/meowlib/server/invitation.go", "Go", 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 8, 71
|
||||
"/home/yves/Documents/code/go/meow/meowlib/server/logger.go", "Go", 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 13
|
||||
"/home/yves/Documents/code/go/meow/meowlib/server/router.go", "Go", 271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 21, 346
|
||||
"/home/yves/Documents/code/go/meow/meowlib/server/videoserver.go", "Go", 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 42
|
||||
"/home/yves/Documents/code/go/meow/meowlib/servercard.go", "Go", 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 13
|
||||
"/home/yves/Documents/code/go/meow/meowlib/symcrypt.go", "Go", 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 7, 69
|
||||
"/home/yves/Documents/code/go/meow/meowlib/symcrypt_test.go", "Go", 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 22
|
||||
"Total", "-", 6390, 45, 21, 9, 344, 34, 4, 194, 11, 235, 387, 2814, 836, 1073, 12397
|
|
44
.VSCodeCounter/2024-08-27_08-23-38/results.md
Normal file
44
.VSCodeCounter/2024-08-27_08-23-38/results.md
Normal file
@ -0,0 +1,44 @@
|
||||
# Summary
|
||||
|
||||
Date : 2024-08-27 08:23:38
|
||||
|
||||
Directory /home/yves/Documents/code/go/meow/meowlib
|
||||
|
||||
Total : 88 files, 10488 codes, 836 comments, 1073 blanks, all 12397 lines
|
||||
|
||||
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
|
||||
|
||||
## Languages
|
||||
| language | files | code | comment | blank | total |
|
||||
| :--- | ---: | ---: | ---: | ---: | ---: |
|
||||
| Go | 53 | 6,390 | 831 | 869 | 8,090 |
|
||||
| SVG | 1 | 2,814 | 1 | 2 | 2,817 |
|
||||
| PlantUML | 21 | 387 | 0 | 58 | 445 |
|
||||
| Go Checksum File | 1 | 344 | 0 | 1 | 345 |
|
||||
| LaTeX | 3 | 235 | 0 | 75 | 310 |
|
||||
| Protocol Buffers | 1 | 194 | 0 | 40 | 234 |
|
||||
| Go Module File | 1 | 45 | 0 | 5 | 50 |
|
||||
| Shell Script | 3 | 34 | 3 | 6 | 43 |
|
||||
| Markdown | 1 | 21 | 0 | 8 | 29 |
|
||||
| Python | 1 | 11 | 1 | 5 | 17 |
|
||||
| YAML | 1 | 9 | 0 | 3 | 12 |
|
||||
| Batch | 1 | 4 | 0 | 1 | 5 |
|
||||
|
||||
## Directories
|
||||
| path | files | code | comment | blank | total |
|
||||
| :--- | ---: | ---: | ---: | ---: | ---: |
|
||||
| . | 88 | 10,488 | 836 | 1,073 | 12,397 |
|
||||
| . (Files) | 21 | 3,483 | 250 | 449 | 4,182 |
|
||||
| client | 32 | 2,824 | 511 | 378 | 3,713 |
|
||||
| client (Files) | 20 | 2,259 | 314 | 261 | 2,834 |
|
||||
| client/helpers | 12 | 565 | 197 | 117 | 879 |
|
||||
| doc | 26 | 3,449 | 2 | 139 | 3,590 |
|
||||
| doc (Files) | 13 | 3,265 | 2 | 120 | 3,387 |
|
||||
| doc/endpoints | 2 | 17 | 0 | 0 | 17 |
|
||||
| doc/invitation | 5 | 49 | 0 | 2 | 51 |
|
||||
| doc/messaging | 2 | 22 | 0 | 3 | 25 |
|
||||
| doc/server | 4 | 96 | 0 | 14 | 110 |
|
||||
| pb | 4 | 221 | 2 | 47 | 270 |
|
||||
| server | 5 | 511 | 71 | 60 | 642 |
|
||||
|
||||
Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md)
|
135
.VSCodeCounter/2024-08-27_08-23-38/results.txt
Normal file
135
.VSCodeCounter/2024-08-27_08-23-38/results.txt
Normal file
@ -0,0 +1,135 @@
|
||||
Date : 2024-08-27 08:23:38
|
||||
Directory : /home/yves/Documents/code/go/meow/meowlib
|
||||
Total : 88 files, 10488 codes, 836 comments, 1073 blanks, all 12397 lines
|
||||
|
||||
Languages
|
||||
+------------------+------------+------------+------------+------------+------------+
|
||||
| language | files | code | comment | blank | total |
|
||||
+------------------+------------+------------+------------+------------+------------+
|
||||
| Go | 53 | 6,390 | 831 | 869 | 8,090 |
|
||||
| SVG | 1 | 2,814 | 1 | 2 | 2,817 |
|
||||
| PlantUML | 21 | 387 | 0 | 58 | 445 |
|
||||
| Go Checksum File | 1 | 344 | 0 | 1 | 345 |
|
||||
| LaTeX | 3 | 235 | 0 | 75 | 310 |
|
||||
| Protocol Buffers | 1 | 194 | 0 | 40 | 234 |
|
||||
| Go Module File | 1 | 45 | 0 | 5 | 50 |
|
||||
| Shell Script | 3 | 34 | 3 | 6 | 43 |
|
||||
| Markdown | 1 | 21 | 0 | 8 | 29 |
|
||||
| Python | 1 | 11 | 1 | 5 | 17 |
|
||||
| YAML | 1 | 9 | 0 | 3 | 12 |
|
||||
| Batch | 1 | 4 | 0 | 1 | 5 |
|
||||
+------------------+------------+------------+------------+------------+------------+
|
||||
|
||||
Directories
|
||||
+-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
|
||||
| path | files | code | comment | blank | total |
|
||||
+-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
|
||||
| . | 88 | 10,488 | 836 | 1,073 | 12,397 |
|
||||
| . (Files) | 21 | 3,483 | 250 | 449 | 4,182 |
|
||||
| client | 32 | 2,824 | 511 | 378 | 3,713 |
|
||||
| client (Files) | 20 | 2,259 | 314 | 261 | 2,834 |
|
||||
| client/helpers | 12 | 565 | 197 | 117 | 879 |
|
||||
| doc | 26 | 3,449 | 2 | 139 | 3,590 |
|
||||
| doc (Files) | 13 | 3,265 | 2 | 120 | 3,387 |
|
||||
| doc/endpoints | 2 | 17 | 0 | 0 | 17 |
|
||||
| doc/invitation | 5 | 49 | 0 | 2 | 51 |
|
||||
| doc/messaging | 2 | 22 | 0 | 3 | 25 |
|
||||
| doc/server | 4 | 96 | 0 | 14 | 110 |
|
||||
| pb | 4 | 221 | 2 | 47 | 270 |
|
||||
| server | 5 | 511 | 71 | 60 | 642 |
|
||||
+-----------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+
|
||||
|
||||
Files
|
||||
+-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
|
||||
| filename | language | code | comment | blank | total |
|
||||
+-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
|
||||
| /home/yves/Documents/code/go/meow/meowlib/.drone.yml | YAML | 9 | 0 | 3 | 12 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/README.md | Markdown | 21 | 0 | 8 | 29 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/asymcrypt.go | Go | 237 | 36 | 41 | 314 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/asymcrypt_test.go | Go | 101 | 65 | 17 | 183 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/buffer.go | Go | 62 | 0 | 5 | 67 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/buffer_test.go | Go | 20 | 0 | 5 | 25 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/clean.sh | Shell Script | 9 | 1 | 1 | 11 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/avatar.go | Go | 6 | 0 | 3 | 9 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/config.go | Go | 99 | 7 | 15 | 121 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/config_test.go | Go | 17 | 0 | 4 | 21 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/dbmessage.go | Go | 46 | 0 | 6 | 52 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/backgroundHelper.go | Go | 140 | 28 | 23 | 191 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/call.go | Go | 42 | 3 | 11 | 56 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/contactHelper.go | Go | 1 | 0 | 1 | 2 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationAnswerHelper.go | Go | 106 | 27 | 23 | 156 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationCheckHelper.go | Go | 52 | 65 | 10 | 127 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationCreateHelper.go | Go | 77 | 50 | 17 | 144 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/invitationFinalizeHelper.go | Go | 34 | 12 | 8 | 54 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/logger.go | Go | 8 | 1 | 4 | 13 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/messageHelper.go | Go | 66 | 9 | 12 | 87 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/networkHelper.go | Go | 25 | 2 | 4 | 31 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/serverHelper.go | Go | 1 | 0 | 1 | 2 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/helpers/storageHelper.go | Go | 13 | 0 | 3 | 16 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/identity.go | Go | 254 | 81 | 27 | 362 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/identity_test.go | Go | 91 | 27 | 11 | 129 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/internalusermessage.go | Go | 41 | 5 | 5 | 51 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/logger.go | Go | 8 | 1 | 4 | 13 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/matriochka.go | Go | 44 | 4 | 4 | 52 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/messagestorage.go | Go | 314 | 30 | 23 | 367 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/messagestorage_test.go | Go | 74 | 5 | 5 | 84 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/peer.go | Go | 249 | 42 | 35 | 326 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/peer_test.go | Go | 26 | 0 | 4 | 30 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/peerlist.go | Go | 34 | 11 | 7 | 52 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/peerstorage.go | Go | 209 | 20 | 20 | 249 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/peerstorage_test.go | Go | 50 | 1 | 10 | 61 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/server.go | Go | 249 | 33 | 26 | 308 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/serverlist.go | Go | 37 | 9 | 7 | 53 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/serverstorage.go | Go | 234 | 17 | 18 | 269 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/client/serverstorage_test.go | Go | 177 | 21 | 27 | 225 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/contactcard.go | Go | 147 | 7 | 24 | 178 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/contactcard_test.go | Go | 61 | 0 | 5 | 66 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/crypt.go | Go | 5 | 0 | 2 | 7 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/act_01_send_msg.puml | PlantUML | 21 | 0 | 1 | 22 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/act_02_srv_recv_msg.puml | PlantUML | 21 | 0 | 1 | 22 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/act_03_srv_proc_msg.puml | PlantUML | 20 | 0 | 0 | 20 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/architecture.tex | LaTeX | 0 | 0 | 1 | 1 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/class_messages01.puml | PlantUML | 70 | 0 | 14 | 84 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/docgen.sh | Shell Script | 13 | 1 | 4 | 18 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/endpoints/company_endpoint.puml | PlantUML | 7 | 0 | 0 | 7 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/endpoints/public_endpoint.puml | PlantUML | 10 | 0 | 0 | 10 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/general_deployment.puml | PlantUML | 19 | 0 | 3 | 22 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_invitation.puml | PlantUML | 7 | 0 | 0 | 7 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv01.puml | PlantUML | 12 | 0 | 0 | 12 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv02.puml | PlantUML | 11 | 0 | 0 | 11 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv03.puml | PlantUML | 11 | 0 | 1 | 12 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/invitation/sq_srvinv04.puml | PlantUML | 8 | 0 | 1 | 9 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/meow.svg | SVG | 2,814 | 1 | 2 | 2,817 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/meow.tex | LaTeX | 175 | 0 | 50 | 225 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/messaging/sq_msg01.puml | PlantUML | 9 | 0 | 1 | 10 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/messaging/wbs_messages_encapsulation.puml | PlantUML | 13 | 0 | 2 | 15 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/protocol.tex | LaTeX | 60 | 0 | 24 | 84 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/server/server_deployment.puml | PlantUML | 33 | 0 | 3 | 36 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/server/server_messaging.puml | PlantUML | 18 | 0 | 6 | 24 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/server/sq_01_srvmessaging.puml | PlantUML | 18 | 0 | 2 | 20 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/server/sq_02_srvmessaging.puml | PlantUML | 27 | 0 | 3 | 30 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/usecase01.puml | PlantUML | 9 | 0 | 7 | 16 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/usecase02.puml | PlantUML | 10 | 0 | 6 | 16 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/doc/usecase03.puml | PlantUML | 33 | 0 | 7 | 40 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/endtoend_test.go | Go | 125 | 55 | 18 | 198 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/go.mod | Go Module File | 45 | 0 | 5 | 50 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/go.sum | Go Checksum File | 344 | 0 | 1 | 345 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/http.go | Go | 41 | 0 | 4 | 45 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/lokiwriter.go | Go | 74 | 5 | 19 | 98 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/message.go | Go | 37 | 0 | 7 | 44 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/messages.pb.go | Go | 2,058 | 45 | 265 | 2,368 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/pb/messages.proto | Protocol Buffers | 194 | 0 | 40 | 234 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/pb/messages.py | Python | 11 | 1 | 5 | 17 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/pb/protogen.bat | Batch | 4 | 0 | 1 | 5 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/pb/protogen.sh | Shell Script | 12 | 1 | 1 | 14 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/proto_test.go | Go | 33 | 0 | 5 | 38 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/server/identity.go | Go | 135 | 15 | 20 | 170 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/server/invitation.go | Go | 62 | 1 | 8 | 71 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/server/logger.go | Go | 8 | 1 | 4 | 13 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/server/router.go | Go | 271 | 54 | 21 | 346 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/server/videoserver.go | Go | 35 | 0 | 7 | 42 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/servercard.go | Go | 10 | 0 | 3 | 13 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/symcrypt.go | Go | 26 | 36 | 7 | 69 |
|
||||
| /home/yves/Documents/code/go/meow/meowlib/symcrypt_test.go | Go | 18 | 0 | 4 | 22 |
|
||||
| Total | | 10,488 | 836 | 1,073 | 12,397 |
|
||||
+-----------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+
|
11
.drone.yml
Normal file
11
.drone.yml
Normal file
@ -0,0 +1,11 @@
|
||||
type: docker
|
||||
kind: pipeline
|
||||
name: unit
|
||||
|
||||
|
||||
steps:
|
||||
- name: test
|
||||
image: golang
|
||||
commands:
|
||||
- go test
|
||||
- go build
|
14
.gitignore
vendored
14
.gitignore
vendored
@ -1,9 +1,10 @@
|
||||
doc/protocol.aux
|
||||
doc/protocol.fdb_latexmk
|
||||
doc/protocol.fls
|
||||
doc/protocol.log
|
||||
doc/protocol.pdf
|
||||
doc/protocol.synctex.gz
|
||||
doc/*.aux
|
||||
doc/*.fdb_latexmk
|
||||
doc/*.fls
|
||||
doc/*.log
|
||||
doc/*.pdf
|
||||
doc/*.synctex.gz
|
||||
doc/generated
|
||||
out/doc/general_deployment/general_deployment.png
|
||||
out/doc/server_deployment/server_deployment.png
|
||||
*.json
|
||||
@ -14,3 +15,4 @@ client/test.cfg
|
||||
.VSCodeCouter/
|
||||
meowlib-sources.jar
|
||||
meowlib.aar
|
||||
client/test.db
|
31
README.md
31
README.md
@ -1,9 +1,28 @@
|
||||
# MEOW lib
|
||||
|
||||
## Go Mobile setup
|
||||
go install golang.org/x/mobile/cmd/gobind@latest
|
||||
go install golang.org/x/mobile/cmd/gomobile@latest
|
||||
go get golang.org/x/mobile/bind
|
||||
Replace "Get" by "get" in messages.pb.go
|
||||
# Documentation generation
|
||||
## required tools
|
||||
* protoc
|
||||
* protoc-gen-doc
|
||||
* protoc-gen-uml
|
||||
* go-plantuml
|
||||
* plantuml (plantuml command is as shell script : `java -jar /<path-to-plantuml>/plantuml-mit-<version>.jar "$@"`)
|
||||
## generation
|
||||
run the shell scripts
|
||||
|
||||
cd pb
|
||||
./protogen.sh
|
||||
|
||||
cd doc
|
||||
./docgen.sh
|
||||
|
||||
# Tests
|
||||
|
||||
|
||||
# Design notes
|
||||
Config is written as a json file
|
||||
Identity is stored as an encrypted json file
|
||||
Message servers (messaging and my contact's messaging) are stored in an encrypted badger db with server url as key
|
||||
Received servers are stored in a sqlite db for selective searches, with storage limits
|
||||
Messages are stored in several badger? or sqlite? db per user with send/receive time as key
|
||||
|
||||
gomobile bind -target android -androidapi=19
|
@ -4,7 +4,6 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -122,6 +121,7 @@ func TestFlutterCompat(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestFlutterDecode(t *testing.T) {
|
||||
pub, err := os.ReadFile("/home/yves/Documents/code/flutter/meowlib/pub.key")
|
||||
if err != nil {
|
||||
@ -179,3 +179,4 @@ func TestFlutterEncode(t *testing.T) {
|
||||
println(base64.StdEncoding.EncodeToString(pub))
|
||||
|
||||
}
|
||||
*/
|
||||
|
8
client/avatar.go
Normal file
8
client/avatar.go
Normal file
@ -0,0 +1,8 @@
|
||||
package client
|
||||
|
||||
import "time"
|
||||
|
||||
type Avatar struct {
|
||||
File string
|
||||
Date time.Time
|
||||
}
|
@ -8,30 +8,48 @@ import (
|
||||
|
||||
type Config struct {
|
||||
// UserConfig
|
||||
SavePassword bool `json:"save_password,omitempty"`
|
||||
SavedPassword string `json:"saved_password,omitempty"`
|
||||
SavePassword bool `json:"save_password,omitempty"`
|
||||
SavedPassword string `json:"saved_password,omitempty"`
|
||||
PasswordTip string `json:"password_tip,omitempty"`
|
||||
PasswordTipUnlock string `json:"password_tip_unlock,omitempty"`
|
||||
// Technical
|
||||
IdentityFile string `json:"identity_file,omitempty"`
|
||||
StoragePath string `json:"storage_path,omitempty"`
|
||||
MaxIdsPerUser int `json:"max_ids_per_user,omitempty"`
|
||||
MsgDbRollingPeriod int `json:"msg_db_rolling_period,omitempty"`
|
||||
Chunksize int64 `json:"chunksize,omitempty"`
|
||||
ServerPollInterval int `json:"server_poll_interval,omitempty"`
|
||||
DbSize int `json:"db_size,omitempty"`
|
||||
UserAgent string `json:"user_agent,omitempty"`
|
||||
// Network
|
||||
ServerPollInterval int `json:"server_poll_interval,omitempty"`
|
||||
HttpTimeOut int `json:"http_timeout,omitempty"`
|
||||
HttpLongPoll int `json:"http_long_poll,omitempty"`
|
||||
// GUI
|
||||
LastOpenChat string `json:"last_open_chat,omitempty"`
|
||||
SoundNotification bool `json:"sound_notification,omitempty"`
|
||||
DefaultNotificationSound string `json:"default_notification_sound,omitempty"`
|
||||
NotificationVibe string `json:"notification_vibe,omitempty"`
|
||||
DefaultNotificationVibe string `json:"default_notification_vibe,omitempty"`
|
||||
NotificationLED string `json:"notification_led,omitempty"`
|
||||
DefaultNotificationLEDColor string `json:"default_notification_led_color,omitempty"`
|
||||
VisualNotification bool `json:"visual_notification,omitempty"`
|
||||
VisualNotifiactionModes string `json:"visual_notifiaction_modes,omitempty"`
|
||||
PrivateChatNotifiactions bool `json:"private_chat_notifiactions,omitempty"`
|
||||
GroupChatNotifiactions bool `json:"group_chat_notifiactions,omitempty"`
|
||||
ChannelNotifications bool `json:"channel_notifications,omitempty"`
|
||||
LastOpenChat string `json:"last_open_chat,omitempty"`
|
||||
SoundNotificationEnable bool `json:"sound_notification_enable,omitempty"`
|
||||
DefaultNotificationSound string `json:"default_notification_sound,omitempty"`
|
||||
NotificationVibeEnable bool `json:"notification_vibe_enable,omitempty"`
|
||||
DefaultNotificationVibe string `json:"default_notification_vibe,omitempty"`
|
||||
NotificationLEDEnable bool `json:"notification_led_enable,omitempty"`
|
||||
DefaultNotificationLEDColor string `json:"default_notification_led_color,omitempty"`
|
||||
VisualNotificationEnable bool `json:"visual_notification_enable,omitempty"`
|
||||
VisualNotificationModes string `json:"visual_notifiaction_modes,omitempty"`
|
||||
PrivateChatNotificationsEnable bool `json:"private_chat_notifiactions_enable,omitempty"`
|
||||
GroupChatNotificationsEnable bool `json:"group_chat_notifiactions_enable,omitempty"`
|
||||
ChannelNotificationsEnable bool `json:"channel_notifications_enable,omitempty"`
|
||||
Language string `json:"language,omitempty"`
|
||||
Theme string `json:"theme,omitempty"`
|
||||
FingerprintEnable bool `json:"fingerprint_enable,omitempty"`
|
||||
ShowFavoriteContacts bool `json:"show_favorite_contacts,omitempty"`
|
||||
NightModeEnable bool `json:"night_mode_enable,omitempty"`
|
||||
|
||||
// Debug
|
||||
DbSuffix string `json:"db_suffix,omitempty"`
|
||||
|
||||
// Inner
|
||||
memoryPassword string
|
||||
identityFile string
|
||||
memoryPassword string
|
||||
additionalPasswords []string
|
||||
me *Identity
|
||||
}
|
||||
|
||||
var instance *Config
|
||||
@ -53,6 +71,15 @@ func (c *Config) Load(filename string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// override values if not set or wrong
|
||||
if c.HttpTimeOut <= 0 {
|
||||
c.HttpTimeOut = 10
|
||||
c.Save(filename)
|
||||
}
|
||||
if c.HttpLongPoll <= 1 {
|
||||
c.HttpLongPoll = 300
|
||||
c.Save(filename)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -61,7 +88,7 @@ func (c *Config) Save(filename string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.WriteFile(filename, data, 0644)
|
||||
os.WriteFile(filename, data, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -75,3 +102,19 @@ func (c *Config) SetMemPass(pass string) {
|
||||
func (c *Config) GetMemPass() string {
|
||||
return c.memoryPassword
|
||||
}
|
||||
|
||||
func (c *Config) GetIdentity() *Identity {
|
||||
return c.me
|
||||
}
|
||||
|
||||
func (c *Config) SetIdentity(id *Identity) {
|
||||
c.me = id
|
||||
}
|
||||
|
||||
func (c *Config) SaveIdentity() error {
|
||||
return c.me.Save()
|
||||
}
|
||||
|
||||
func (c *Config) Clean() {
|
||||
c.additionalPasswords = []string{}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
func TestConfigSave(t *testing.T) {
|
||||
c := GetConfig()
|
||||
c.memoryPassword = "hideme"
|
||||
c.IdentityFile = "test.id"
|
||||
c.Chunksize = 10000000
|
||||
c.SavePassword = true
|
||||
c.SavedPassword = "stupid"
|
||||
|
51
client/dbmessage.go
Normal file
51
client/dbmessage.go
Normal file
@ -0,0 +1,51 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
)
|
||||
|
||||
func DbMessageToInternalUserMessage(id int64, dbFile string, dbm *meowlib.DbMessage) *InternalUserMessage {
|
||||
var ium InternalUserMessage
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = dbFile
|
||||
if dbm.Outbound {
|
||||
ium.Outbound = true
|
||||
} else {
|
||||
ium.Outbound = false
|
||||
}
|
||||
ium.Message = string(dbm.Data)
|
||||
ium.Status = dbm.Status
|
||||
ium.Contact = dbm.Contact
|
||||
ium.CurrentLocation = dbm.CurrentLocation
|
||||
ium.Messagetype = dbm.Type
|
||||
ium.Appdata = dbm.Appdata
|
||||
ium.FilePaths = dbm.FilePaths
|
||||
return &ium
|
||||
}
|
||||
|
||||
func InternalUserMessageToDbMessage(ium *InternalUserMessage) *meowlib.DbMessage {
|
||||
var dbm meowlib.DbMessage
|
||||
dbm.Outbound = ium.Outbound
|
||||
dbm.Type = ium.Messagetype
|
||||
dbm.Data = []byte(ium.Message)
|
||||
dbm.Appdata = ium.Appdata
|
||||
dbm.Contact = ium.Contact
|
||||
dbm.CurrentLocation = ium.CurrentLocation
|
||||
dbm.Status = ium.Status
|
||||
dbm.FilePaths = ium.FilePaths
|
||||
return &dbm
|
||||
}
|
||||
|
||||
func UserMessageToDbMessage(outbound bool, um *meowlib.UserMessage, filepaths []string) *meowlib.DbMessage {
|
||||
var dbm meowlib.DbMessage
|
||||
dbm.Outbound = outbound
|
||||
dbm.Type = um.Type
|
||||
dbm.Data = um.Data
|
||||
dbm.Appdata = um.Appdata
|
||||
dbm.Contact = um.Contact
|
||||
dbm.CurrentLocation = um.CurrentLocation
|
||||
dbm.Status = um.Status
|
||||
dbm.FilePaths = filepaths
|
||||
return &dbm
|
||||
|
||||
}
|
241
client/helpers/backgroundHelper.go
Normal file
241
client/helpers/backgroundHelper.go
Normal file
@ -0,0 +1,241 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
"github.com/google/uuid"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type ReceivedMessage struct {
|
||||
Text string
|
||||
files []string
|
||||
Server string
|
||||
Sent uint64
|
||||
Received uint64
|
||||
LocalUuid string
|
||||
LocalSequence uint64
|
||||
AppData string
|
||||
Location meowlib.Location
|
||||
}
|
||||
|
||||
// CheckForMessages checks for messages on a single server
|
||||
func CheckForMessages(storage_path string, job *client.RequestsJob, timeout int, longPoll bool) (int, string, error) {
|
||||
|
||||
count := 0
|
||||
|
||||
// if folder does not exist, create it
|
||||
if _, err := os.Stat(filepath.Join(storage_path, "inbox")); os.IsNotExist(err) {
|
||||
err := os.MkdirAll(filepath.Join(storage_path, "inbox"), 0700)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: MkdirAll", err
|
||||
}
|
||||
}
|
||||
//convert server to a server object
|
||||
|
||||
var crl []*meowlib.ConversationRequest
|
||||
// build conversation requests
|
||||
if job.LookupKeys != nil {
|
||||
for _, key := range job.LookupKeys {
|
||||
var cr meowlib.ConversationRequest
|
||||
cr.LookupKey = key.Public
|
||||
cr.SendTimestamp = time.Now().UTC().Unix()
|
||||
// todo sign it
|
||||
//cr.LookupSignature =
|
||||
crl = append(crl, &cr)
|
||||
}
|
||||
// get server public key
|
||||
if job.Server.PublicKey == "" {
|
||||
key, err := meowlib.HttpGetId(job.Server.Url)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: HttpGetId", err
|
||||
}
|
||||
job.Server.PublicKey = key["publicKey"]
|
||||
}
|
||||
// build server message
|
||||
var toSrv meowlib.ToServerMessage
|
||||
toSrv.PullRequest = crl
|
||||
toSrv.From = job.Server.UserKp.Public
|
||||
|
||||
if longPoll {
|
||||
toSrv.Timeout = int64(timeout)
|
||||
}
|
||||
|
||||
data, err := job.Server.ProcessOutboundMessage(&toSrv)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: ProcessOutboundMessage", err
|
||||
}
|
||||
|
||||
response, err := meowlib.HttpPostMessage(job.Server.Url, data, timeout)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: httpPostMessage", err
|
||||
}
|
||||
fs_msg, err := job.Server.ProcessInboundServerResponse(response)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: ProcessInboundServerResponse", err
|
||||
}
|
||||
if len(fs_msg.Chat) > 0 || (fs_msg.Invitation != nil && fs_msg.Invitation.Step == 3) {
|
||||
// chat or invitation answer => save the server message
|
||||
|
||||
out, err := proto.Marshal(fs_msg)
|
||||
if err != nil {
|
||||
return -1, "CheckMessages: protobuf marshal", err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(storage_path, "inbox", strconv.FormatInt(time.Now().UTC().UnixNano(), 10)), out, 0644); err != nil {
|
||||
return -1, "CheckMessages: WriteFile", err
|
||||
}
|
||||
}
|
||||
count = len(fs_msg.Chat)
|
||||
} else {
|
||||
// manage non uszer messages like devices or server
|
||||
}
|
||||
|
||||
return count, "", nil
|
||||
}
|
||||
|
||||
// SaveCheckJobs
|
||||
func SaveCheckJobs() (string, error) {
|
||||
me := client.GetConfig().GetIdentity()
|
||||
err := me.SaveBackgroundJob()
|
||||
if err != nil {
|
||||
|
||||
return "CheckMessages: json.Marshal", err
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// ReadMessage
|
||||
func ReadMessage(messageFilename string) ([]string, []string, string, error) {
|
||||
|
||||
messagesOverview := []string{}
|
||||
filenames := []string{}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
// read message file
|
||||
msg, err := os.ReadFile(messageFilename)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: ReadFile", err
|
||||
}
|
||||
// protobuf unmarshal message
|
||||
var fromServerMessage meowlib.FromServerMessage
|
||||
err = proto.Unmarshal(msg, &fromServerMessage)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: Unmarshal FromServerMessage", err
|
||||
}
|
||||
// check if invitation answer
|
||||
if fromServerMessage.Invitation != nil {
|
||||
invitationGetAnswerReadResponse(fromServerMessage.Invitation)
|
||||
}
|
||||
// Chat messages
|
||||
if len(fromServerMessage.Chat) > 0 {
|
||||
for _, packedUserMessage := range fromServerMessage.Chat {
|
||||
|
||||
// find the peer with that lookup key
|
||||
peer := identity.Peers.GetFromMyLookupKey(packedUserMessage.Destination)
|
||||
if peer == nil {
|
||||
return nil, nil, "ReadMessage: GetFromMyLookupKey", errors.New("no visible peer for that message")
|
||||
}
|
||||
// Unpack the message
|
||||
usermsg, err := peer.ProcessInboundUserMessage(packedUserMessage.Payload, packedUserMessage.Signature)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: ProcessInboundUserMessage", err
|
||||
}
|
||||
|
||||
//fmt.Println("From:", usermsg.From)
|
||||
//jsonUserMessage, _ := json.Marshal(usermsg)
|
||||
//fmt.Println(string(jsonUserMessage))
|
||||
peer = client.GetConfig().GetIdentity().Peers.GetFromPublicKey(usermsg.From)
|
||||
|
||||
// detach files
|
||||
if usermsg.Files != nil {
|
||||
// create files folder
|
||||
if _, err := os.Stat(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files")); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files"), 0700)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
for _, file := range usermsg.Files {
|
||||
filename := uuid.New().String() + "_" + file.Filename
|
||||
filenames = append(filenames, peer.Name+" sent: "+filename)
|
||||
// detach file
|
||||
os.WriteFile(filepath.Join(client.GetConfig().StoragePath, identity.Uuid, "files", filename), file.Data, 0600)
|
||||
}
|
||||
//? result["invitation finalized"] = peer.Name
|
||||
}
|
||||
// user message
|
||||
|
||||
messagesOverview = append(messagesOverview, peer.Name+" > "+string(usermsg.Data))
|
||||
// add message to storage
|
||||
err = peer.StoreMessage(usermsg, filenames)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: StoreMessage", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Remove(messageFilename)
|
||||
if err != nil {
|
||||
return nil, nil, "ReadMessage: Remove", err
|
||||
}
|
||||
|
||||
// list of messages & detached files
|
||||
return messagesOverview, filenames, "", nil
|
||||
}
|
||||
|
||||
// CheckForMessages checks for messages on a single server
|
||||
func LongPollForMessages(storage_path string, jobs []client.RequestsJob, timeout int, longPoll bool) (int, string, error) {
|
||||
|
||||
// Channel to collect results
|
||||
resultChan := make(chan int, len(jobs))
|
||||
errChan := make(chan error, len(jobs))
|
||||
|
||||
// WaitGroup to sync goroutines
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// Loop through each job (server)
|
||||
for _, job := range jobs {
|
||||
wg.Add(1)
|
||||
|
||||
go func(job client.RequestsJob) {
|
||||
defer wg.Done()
|
||||
|
||||
// Long-polling call to the server
|
||||
cnt, _, err := CheckForMessages(storage_path, &job, timeout, true)
|
||||
|
||||
if err == nil && cnt > 0 {
|
||||
select {
|
||||
case resultChan <- cnt:
|
||||
default:
|
||||
}
|
||||
|
||||
// Close the error channel to notify all goroutines
|
||||
close(errChan)
|
||||
|
||||
}
|
||||
}(job)
|
||||
}
|
||||
|
||||
// Close the result channel when all workers are done
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(resultChan)
|
||||
}()
|
||||
|
||||
// Wait for the first message or all timeouts
|
||||
select {
|
||||
case cnt := <-resultChan:
|
||||
return cnt, "", nil
|
||||
case <-errChan:
|
||||
// If one fails and exitOnMessage is true
|
||||
return 0, "", nil
|
||||
}
|
||||
|
||||
}
|
55
client/helpers/call.go
Normal file
55
client/helpers/call.go
Normal file
@ -0,0 +1,55 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func BuildCallRequestMessage(users []string, expiry uint64, srvuid string) ([]byte, string, error) {
|
||||
// Server: get the invitation server
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srvuid)
|
||||
if err != nil {
|
||||
return nil, "BuildCallRequestMessage: LoadServer", err
|
||||
}
|
||||
|
||||
toSrvMsg, err := server.BuildVideoRoomRequestMessage(users, expiry)
|
||||
if err != nil {
|
||||
return nil, "BuildCallRequestMessage: BuildVideoRoomRequestMessage", err
|
||||
}
|
||||
msg, err := server.ProcessOutboundMessage(toSrvMsg)
|
||||
if err != nil {
|
||||
return nil, "BuildCallRequestMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
return msg, "", nil
|
||||
}
|
||||
|
||||
func ReadCallRequestResponseMessage(data []byte, srvuid string) (*meowlib.VideoData, string, error) {
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srvuid)
|
||||
if err != nil {
|
||||
return nil, "ReadCallRequestResponseMessage: LoadServer", err
|
||||
}
|
||||
// Server inbound processing : get the invitation server
|
||||
serverMsg, err := server.ProcessInboundServerResponse(data)
|
||||
if err != nil {
|
||||
return nil, "ReadCallRequestResponseMessage: ProcessInboundServerResponse", err
|
||||
}
|
||||
return serverMsg.VideoData, "", nil
|
||||
}
|
||||
|
||||
func BuildCallMessage(videodata *meowlib.VideoData, srvuid string, peer_uid string, replyToUid string, filelist []string) ([]byte, string, error) {
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromUid(peer_uid)
|
||||
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage(nil)
|
||||
if err != nil {
|
||||
return nil, "BuildCallMessage : BuildSimpleUserMessage", err
|
||||
}
|
||||
|
||||
usermessage.Status.AnswerToUuid = replyToUid
|
||||
|
||||
return messageBuildPostprocess(usermessage, srvuid, peer)
|
||||
}
|
||||
|
||||
func BuildCancelCallMessage() {
|
||||
|
||||
}
|
1
client/helpers/contactHelper.go
Normal file
1
client/helpers/contactHelper.go
Normal file
@ -0,0 +1 @@
|
||||
package helpers
|
155
client/helpers/invitationAnswerHelper.go
Normal file
155
client/helpers/invitationAnswerHelper.go
Normal file
@ -0,0 +1,155 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"C"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// InvitationAnswer
|
||||
func InvitationAnswer(cc *meowlib.ContactCard, nickname string, myNickname string, serverUids []string) (*client.Peer, string, error) {
|
||||
|
||||
mynick := myNickname
|
||||
// my nickname for that contact
|
||||
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
// build my contact card for that friend
|
||||
peer := client.GetConfig().GetIdentity().AnswerInvitation(mynick, nickname, serverUids, cc)
|
||||
|
||||
//peerstr, err := json.Marshal(peer)
|
||||
//fmt.Println("InvitationAnswer: " + string(peerstr))
|
||||
c := client.GetConfig()
|
||||
c.GetIdentity().Save()
|
||||
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerFile
|
||||
func InvitationAnswerFile(invitationFile string, nickname string, myNickname string, serverUids []string) (string, error) {
|
||||
format := "qr"
|
||||
var filename string = ""
|
||||
var cc *meowlib.ContactCard
|
||||
c := client.GetConfig()
|
||||
if _, err := os.Stat(invitationFile); os.IsNotExist(err) {
|
||||
return "InvitationAnswerFile : os.Stat", err
|
||||
}
|
||||
if strings.HasSuffix(invitationFile, ".mwiv") {
|
||||
format = "mwiv"
|
||||
data, err := os.ReadFile(invitationFile)
|
||||
if err != nil {
|
||||
return "InvitationAnswerFile : os.ReadFile", err
|
||||
}
|
||||
cc, err = meowlib.NewContactCardFromCompressed(data)
|
||||
if err != nil {
|
||||
return "InvitationAnswerFile : NewContactCardFromCompressed", err
|
||||
}
|
||||
}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
if cc != nil {
|
||||
isAnswer, proposed, received, _ := identity.CheckInvitation(cc)
|
||||
if isAnswer {
|
||||
fmt.Fprintln(os.Stdout, "This is already a response "+proposed+" to your invitation.")
|
||||
fmt.Fprintln(os.Stdout, "You cannot answer again.")
|
||||
fmt.Fprintln(os.Stdout, "You should finalize it by importing "+proposed+" contact card to your meow.")
|
||||
fmt.Fprintln(os.Stdout, "Use : 'meow invitation finalize "+invitationFile+"' to do it.")
|
||||
|
||||
} else {
|
||||
mynick := myNickname
|
||||
// my nickname for that contact
|
||||
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
response := identity.AnswerInvitation(mynick, nickname, serverUids, cc)
|
||||
fmt.Fprintln(os.Stdout, "Invitation sent by "+received)
|
||||
if format == "qr" {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".png"
|
||||
response.GetMyContact().WriteQr(filename)
|
||||
} else {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + mynick + "-" + nickname + ".mwiv"
|
||||
response.GetMyContact().WriteCompressed(filename)
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
}
|
||||
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerMessage
|
||||
func InvitationAnswerMessage(invitationId string, invitationServerUid string, timeout int) ([]byte, string, error) {
|
||||
|
||||
// find the peer with that invitation id
|
||||
/*var peer *client.Peer
|
||||
for i := len(client.GetConfig().GetIdentity().Peers) - 1; i >= 0; i-- { //! to allow self invitation : testing only, findinc the received peer before myself
|
||||
// for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
peer = client.GetConfig().GetIdentity().Peers[i]
|
||||
break
|
||||
}
|
||||
}*/
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
||||
|
||||
if peer == nil {
|
||||
// declare a custom go error for no peer found
|
||||
return nil, "InvitationAnswerMessage: loop for peer", errors.New("no peer with that invitation id")
|
||||
}
|
||||
answermsg, err := peer.BuildInvitationAnswerMessage(peer.GetMyContact())
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: BuildInvitationAnswserMessage", err
|
||||
}
|
||||
// Server: get the invitation server
|
||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: LoadServer", err
|
||||
}
|
||||
|
||||
// this will be the invitation's payload
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(answermsg)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: ProcessOutboundUserMessage", err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage, err := invitationServer.BuildToServerMessageInvitationAnswer(packedMsg, peer.MyIdentity.Public, invitationId, timeout)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: BuildToServerMessageInvitationAnswer", err
|
||||
}
|
||||
|
||||
// Server outbound processing
|
||||
bytemsg, err := invitationServer.ProcessOutboundMessage(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationAnswerMessageReadResponse
|
||||
// Called by the invitation receiver
|
||||
// invitationData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func InvitationAnswerMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.Invitation, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessageReadResponse: LoadServer", err
|
||||
}
|
||||
// Server inbound processing : get the invitation server
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
||||
if err != nil {
|
||||
return nil, "InvitationAnswerMessageReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
|
||||
return serverMsg.Invitation, "", nil
|
||||
|
||||
}
|
126
client/helpers/invitationCheckHelper.go
Normal file
126
client/helpers/invitationCheckHelper.go
Normal file
@ -0,0 +1,126 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
// InvitationCheck
|
||||
// todo
|
||||
/*
|
||||
func InvitationCheck(invitationdata []byte) *C.char {
|
||||
var jsoninv map[string]interface{}
|
||||
err := json.Unmarshal([]byte(C.GoString(invitationdata)), &jsoninv)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
var cc *meowlib.ContactCard
|
||||
if _, err := os.Stat(jsoninv["filename"].(string)); os.IsNotExist(err) {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
if strings.HasSuffix(jsoninv["filename"].(string), ".mwiv") {
|
||||
data, err := os.ReadFile(jsoninv["filename"].(string))
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
cc, err = meowlib.NewContactCardFromCompressed(data)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
}
|
||||
identity := client.GetConfig().GetIdentity()
|
||||
result := map[string]string{}
|
||||
if cc != nil {
|
||||
isAnswer, proposed, received, invitationMessage := identity.CheckInvitation(cc)
|
||||
if isAnswer { // answer to infitation
|
||||
result["type"] = "answer"
|
||||
result["to"] = proposed
|
||||
result["from"] = received
|
||||
result["invitation_message"] = invitationMessage
|
||||
//fmt.Fprintln(os.Stdout, "Invitation sent to "+proposed+" received with "+received+" as suggested nickname")
|
||||
} else { // finalization message
|
||||
result["type"] = "finalize"
|
||||
result["from"] = received
|
||||
//fmt.Fprintln(os.Stdout, "Invitation sent by "+received)
|
||||
}
|
||||
|
||||
}
|
||||
val, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return C.CString(errorToJson(err, "InvitationCheck: "))
|
||||
}
|
||||
return C.CString(string(val))
|
||||
}*/
|
||||
|
||||
// InvitationGetMessage
|
||||
// Called by the invitation receiver
|
||||
// invitationUrl: the url of server holding the invitation
|
||||
// serverPublicKey: the public key of the server holding the invitation
|
||||
// invitationPassword: the password of the invitation
|
||||
func InvitationGetMessage(invitationUrl string, serverPublicKey string, invitationPassword string) ([]byte, string, error) {
|
||||
|
||||
meowurl := strings.Split(invitationUrl, "?")
|
||||
|
||||
shortcode := meowurl[1]
|
||||
srv := client.CreateServerFromMeowUrl(meowurl[0])
|
||||
// check if already in msg servers
|
||||
dbsrv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srv.Url)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: LoadServer", err
|
||||
}
|
||||
if dbsrv == nil {
|
||||
// create a server object with url & pubkey
|
||||
srv.PublicKey = serverPublicKey
|
||||
srv.UserKp = meowlib.NewKeyPair()
|
||||
// save it
|
||||
err = client.GetConfig().GetIdentity().MessageServers.StoreServer(srv)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: StoreServer", err
|
||||
}
|
||||
} else {
|
||||
if dbsrv.PublicKey != serverPublicKey {
|
||||
dbsrv.PublicKey = serverPublicKey
|
||||
}
|
||||
srv = dbsrv
|
||||
}
|
||||
// buildserver message
|
||||
toSrvMsg, err := srv.BuildToServerMessageInvitationRequest(shortcode, invitationPassword)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: BuildToServerMessageInvitationRequest", err
|
||||
}
|
||||
// processoutbound
|
||||
bytemsg, err := srv.ProcessOutboundMessage(toSrvMsg)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationGetMessageReadResponse
|
||||
// Called by the invitation receiver
|
||||
// invitationData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func InvitationGetMessageReadResponse(invitationData []byte, invitationServerUid string) (*meowlib.ContactCard, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: LoadServer", err
|
||||
}
|
||||
// Server inbound processing : get the invitation server
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationData)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
// fmt.Println("Inbound OK, Invitation Step: ", serverMsg.Invitation.Step, len(serverMsg.Invitation.Payload))
|
||||
// fmt.Println("Invitation Check")
|
||||
// fmt.Println(hex.EncodeToString(serverMsg.Invitation.Payload))
|
||||
// contactCard decode
|
||||
cc, err := meowlib.NewContactCardFromCompressed(serverMsg.Invitation.Payload)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetMessageReadResponse: NewContactCardFromCompressed", err
|
||||
}
|
||||
return cc, "", nil
|
||||
}
|
143
client/helpers/invitationCreateHelper.go
Normal file
143
client/helpers/invitationCreateHelper.go
Normal file
@ -0,0 +1,143 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
// InvitationCreatePeer creates a new peer and returns it
|
||||
// Called by invitation initiator
|
||||
// name: the name of the peer
|
||||
// myNickname: my nickname for that peer
|
||||
// invitationMessage: the message to send to the peer
|
||||
// serverUids: the list of server uids
|
||||
func InvitationCreatePeer(name string, myNickname string, invitationMessage string, serverUids []string) (*client.Peer, string, error) {
|
||||
|
||||
mynick := myNickname
|
||||
if myNickname == "" {
|
||||
mynick = client.GetConfig().GetIdentity().Nickname
|
||||
}
|
||||
|
||||
// build my contact card for that friend
|
||||
peer, err := client.GetConfig().GetIdentity().InvitePeer(mynick, name, serverUids, invitationMessage)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreate: InvitePeer", err
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateFile creates a new peer and writes the invitation to a file
|
||||
// Called by invitation initiator
|
||||
// name: the name of the peer
|
||||
// myNickname: my nickname for that peer
|
||||
// invitationMessage: the message to send to the peer
|
||||
// serverUids: the list of server uids
|
||||
// format: the format of the file (qr or mwiv)
|
||||
func InvitationCreateFile(name string, myNickname string, invitationMessage string, serverUids []string, format string) (*client.Peer, string, error) {
|
||||
|
||||
peer, errdata, err := InvitationCreatePeer(name, myNickname, invitationMessage, serverUids)
|
||||
if err != nil {
|
||||
return nil, errdata, err
|
||||
}
|
||||
c := client.GetConfig()
|
||||
var filename string = ""
|
||||
if format == "qr" {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".png"
|
||||
err := peer.GetMyContact().WriteQr(filename)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateFile: WriteQr", err
|
||||
}
|
||||
} else {
|
||||
filename = c.StoragePath + string(os.PathSeparator) + peer.MyName + "-" + peer.Name + ".mwiv"
|
||||
err := peer.GetMyContact().WriteCompressed(filename)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateFile: WriteCompressed", err
|
||||
}
|
||||
}
|
||||
return peer, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateMessage creates a new invitation message for an invited peer
|
||||
// Called by invitation initiator
|
||||
// invitationId: the invitation id of the peer
|
||||
// invitationServerUid: the uid of the server for sending the invitation
|
||||
// timeOut: the timeout for the invitation
|
||||
// urlLen: the length of the invitation url
|
||||
// password: the password for the invitation
|
||||
func InvitationCreateMessage(invitationId string, invitationServerUid string, timeOut int, urlLen int, password string) ([]byte, string, error) {
|
||||
|
||||
// lookup for peer with "invitation_id"
|
||||
var myContact *meowlib.ContactCard
|
||||
/* for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
myContact = client.GetConfig().GetIdentity().Peers[i].GetMyContact()
|
||||
break
|
||||
}
|
||||
}*/
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitationId)
|
||||
myContact = peer.GetMyContact()
|
||||
// todo handle not found !!
|
||||
// lookup for message server with "invitation_server"
|
||||
invitationServer, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid) //.GetServerByIdx(int(jsoninv["invitation_server"].(float64)))
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: LoadServer", err
|
||||
}
|
||||
// call server.buildinviattion
|
||||
msg, err := invitationServer.BuildToServerMessageInvitationCreation(myContact, password, timeOut, urlLen)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: BuildToServerMessageInvitationCreation", err
|
||||
}
|
||||
// fmt.Println("Invitation Create")
|
||||
// fmt.Println(hex.EncodeToString(msg.Invitation.Payload))
|
||||
bytemsg, err := invitationServer.ProcessOutboundMessage(msg)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateMessage: ProcessOutboundMessage", err
|
||||
}
|
||||
return bytemsg, "", nil
|
||||
}
|
||||
|
||||
// InvitationCreateReadResponse reads the response of an invitation creation (url, expiry)
|
||||
// Called by invitation initiator
|
||||
// invitationServerUid: the uid of the server where we sent the invitation
|
||||
// invitationResponse: the response we got from the server
|
||||
func InvitationCreateReadResponse(invitationServerUid string, invitationResponse []byte) (*meowlib.Invitation, string, error) {
|
||||
|
||||
server, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(invitationServerUid)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateReadResponse: LoadServer", err
|
||||
}
|
||||
serverMsg, err := server.ProcessInboundServerResponse(invitationResponse)
|
||||
if err != nil {
|
||||
return nil, "InvitationCreateReadResponse: ProcessInboundServerResponse", err
|
||||
}
|
||||
|
||||
return serverMsg.Invitation, "", nil
|
||||
}
|
||||
|
||||
// InvitationSetUrlInfo sets the url info for an invitation
|
||||
// Called by invitation initiator
|
||||
// invitationId: the invitation id of the peer
|
||||
// url: the url of the invitation we got from the server
|
||||
func InvitationSetUrlInfo(invitationId string, url string, expiry int64) {
|
||||
id := client.GetConfig().GetIdentity()
|
||||
// lookup for peer with "invitation_id"
|
||||
peer := id.Peers.GetFromInvitationId(invitationId)
|
||||
peer.InvitationUrl = url
|
||||
peer.InvitationExpiry = time.Unix(expiry, 0)
|
||||
id.Peers.StorePeer(peer)
|
||||
|
||||
/* for i := 0; i < len(client.GetConfig().GetIdentity().Peers); i++ {
|
||||
if client.GetConfig().GetIdentity().Peers[i].InvitationId == invitationId {
|
||||
client.GetConfig().GetIdentity().Peers[i].InvitationUrl = url
|
||||
client.GetConfig().GetIdentity().Peers[i].InvitationExpiry = time.Unix(expiry, 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()*/
|
||||
|
||||
}
|
53
client/helpers/invitationFinalizeHelper.go
Normal file
53
client/helpers/invitationFinalizeHelper.go
Normal file
@ -0,0 +1,53 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Got it by the message background check
|
||||
// => noInvitationGetAnswer
|
||||
|
||||
// invitationGetAnswerReadResponse
|
||||
// Called by the initiator's background service only
|
||||
// invitationAnswerData: the data received from the server
|
||||
// invitationServerUid: the uid of the server holding the invitation
|
||||
func invitationGetAnswerReadResponse(invitation *meowlib.Invitation) (*client.Peer, string, error) {
|
||||
|
||||
// decode the payload
|
||||
var invitationAnswer meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal(invitation.Payload, &invitationAnswer)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: Unmarshal", err
|
||||
}
|
||||
// retreive user public key to check usermessage signature
|
||||
// contactPublikKey := serverMsg.Invitation.From
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromInvitationId(invitation.Uuid)
|
||||
peer.ContactPublicKey = invitation.From
|
||||
if peer != nil {
|
||||
|
||||
// process the packed user message
|
||||
usermsg, err := peer.ProcessInboundUserMessage(invitationAnswer.Payload, invitationAnswer.Signature)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: ProcessInboundUserMessage", err
|
||||
}
|
||||
decodedInvitation := usermsg.Invitation
|
||||
var cc meowlib.ContactCard
|
||||
err = proto.Unmarshal(decodedInvitation.Payload, &cc)
|
||||
if err != nil {
|
||||
return nil, "InvitationGetAnswerReadResponse: Unmarshal", err
|
||||
}
|
||||
|
||||
// finalize the invitation
|
||||
// id := client.GetConfig().GetIdentity()
|
||||
peer.ContactLookupKey = cc.ContactPublicKey
|
||||
peer.ContactEncryption = cc.EncryptionPublicKey
|
||||
for _, server := range cc.PullServers {
|
||||
peer.ContactPullServers = append(peer.ContactPullServers, server.GetUid())
|
||||
}
|
||||
client.GetConfig().GetIdentity().Save()
|
||||
|
||||
}
|
||||
return peer, "", nil
|
||||
}
|
12
client/helpers/logger.go
Normal file
12
client/helpers/logger.go
Normal file
@ -0,0 +1,12 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var logger zerolog.Logger
|
||||
|
||||
// AddLogger sets the logger for the sublibrary
|
||||
func AddLogger(l zerolog.Logger) {
|
||||
logger = l
|
||||
}
|
86
client/helpers/messageHelper.go
Normal file
86
client/helpers/messageHelper.go
Normal file
@ -0,0 +1,86 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func messageBuildPostprocess(msg *meowlib.UserMessage, srvuid string, peer *client.Peer) ([]byte, string, error) {
|
||||
// Get the message server
|
||||
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srvuid)
|
||||
if err != nil {
|
||||
return nil, "messageBuildPostprocess : LoadServer", err
|
||||
}
|
||||
// Prepare cyphered + packed user message
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(msg)
|
||||
if err != nil {
|
||||
return nil, "messageBuildPostprocess : ProcessOutboundUserMessage", err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||
data, err := srv.ProcessOutboundMessage(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, "messageBuildPostprocess : ProcessOutboundMessage", err
|
||||
}
|
||||
// Store message
|
||||
err = peer.StoreMessage(msg, nil)
|
||||
if err != nil {
|
||||
return nil, "messageBuildPostprocess : StoreMessage", err
|
||||
}
|
||||
return data, "", nil
|
||||
}
|
||||
|
||||
func PrepareUserMessage(message string, srvuid string, peer_uid string, replyToUid string, filelist []string) ([]byte, string, error) {
|
||||
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromUid(peer_uid)
|
||||
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage([]byte(message))
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : BuildSimpleUserMessage", err
|
||||
}
|
||||
for _, file := range filelist {
|
||||
err = usermessage.AddFile(file, client.GetConfig().Chunksize)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : AddFile", err
|
||||
}
|
||||
}
|
||||
usermessage.Status.AnswerToUuid = replyToUid
|
||||
|
||||
return messageBuildPostprocess(usermessage, srvuid, peer)
|
||||
|
||||
}
|
||||
|
||||
func BuildAckMessage(messageUid string, srvuid string, peer_uid string, received int64, processed int64) ([]byte, string, error) {
|
||||
|
||||
peer := client.GetConfig().GetIdentity().Peers.GetFromUid(peer_uid)
|
||||
srv, err := client.GetConfig().GetIdentity().MessageServers.LoadServer(srvuid)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : LoadServer", err
|
||||
}
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage(nil)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : BuildSimpleUserMessage", err
|
||||
}
|
||||
usermessage.Status.Uuid = messageUid
|
||||
usermessage.Status.Received = uint64(received)
|
||||
usermessage.Status.Processed = uint64(processed)
|
||||
// Prepare cyphered + packed user message
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(usermessage)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : ProcessOutboundUserMessage", err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||
data, err := srv.ProcessOutboundMessage(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, "PrepareServerMessage : ProcessOutboundMessage", err
|
||||
}
|
||||
|
||||
return data, "", nil
|
||||
}
|
||||
|
||||
func ReadAckMessageResponse() {
|
||||
//! update the status in message store
|
||||
}
|
30
client/helpers/networkHelper.go
Normal file
30
client/helpers/networkHelper.go
Normal file
@ -0,0 +1,30 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func HttpSendMessage(serverUid string, message []byte, timeout int) ([]byte, error) {
|
||||
id := client.GetConfig().GetIdentity()
|
||||
srv, err := id.MessageServers.LoadServer(serverUid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// gettig server Public key if missing
|
||||
if srv.PublicKey == "" {
|
||||
srvdata, err := meowlib.HttpGetId(srv.Url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//print(srvdata["publicKey"])
|
||||
srv.PublicKey = srvdata["publicKey"]
|
||||
id.MessageServers.StoreServer(srv)
|
||||
}
|
||||
|
||||
response, err := meowlib.HttpPostMessage(srv.Url, message, timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return response, nil
|
||||
}
|
1
client/helpers/serverHelper.go
Normal file
1
client/helpers/serverHelper.go
Normal file
@ -0,0 +1 @@
|
||||
package helpers
|
15
client/helpers/storageHelper.go
Normal file
15
client/helpers/storageHelper.go
Normal file
@ -0,0 +1,15 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"forge.redroom.link/yves/meowlib/client"
|
||||
)
|
||||
|
||||
func LoadMessagesHistory(peer_uid string) ([]client.InternalUserMessage, string, error) {
|
||||
id := client.GetConfig().GetIdentity()
|
||||
peer := id.Peers.GetFromUid(peer_uid)
|
||||
msgs, err := peer.LoadMessagesHistory(0, 0, 50)
|
||||
if err != nil {
|
||||
return nil, "LoadLastMessages: LoadMessagesHistory", err
|
||||
}
|
||||
return msgs, "", nil
|
||||
}
|
@ -3,80 +3,123 @@ package client
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/ProtonMail/gopenpgp/v2/helper"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const maxHiddenCount = 30
|
||||
|
||||
type Identity struct {
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
RootKp meowlib.KeyPair `json:"id_kp,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Peers PeerList `json:"peers,omitempty"`
|
||||
HiddenPeers [][]byte `json:"hiddend_peers,omitempty"`
|
||||
Device meowlib.KeyPair `json:"device,omitempty"`
|
||||
KnownServers InternalServerList `json:"known_servers,omitempty"`
|
||||
MessageServers InternalServerList `json:"message_servers,omitempty"`
|
||||
ArchiveServers InternalServerList `json:"archive_servers,omitempty"`
|
||||
OwnedServers InternalServerList `json:"owned_servers,omitempty"`
|
||||
DefaultDbPassword string `json:"default_db_password,omitempty"`
|
||||
DbPasswordStore bool `json:"db_password_store,omitempty"`
|
||||
OwnedDevices PeerList `json:"owned_devices,omitempty"`
|
||||
StaticMtkServerPaths []InternalServerList `json:"static_mtk_server_paths,omitempty"`
|
||||
DynamicMtkServeRules []string `json:"dynamic_mtk_serve_rules,omitempty"`
|
||||
Nickname string `json:"nickname,omitempty"`
|
||||
DefaultAvatar string `json:"default_avatar,omitempty"`
|
||||
Avatars []Avatar `json:"avatars,omitempty"`
|
||||
RootKp meowlib.KeyPair `json:"id_kp,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
Peers PeerStorage `json:"peers,omitempty"`
|
||||
HiddenPeers [][]byte `json:"hidden_peers,omitempty"`
|
||||
Personae PeerList `json:"faces,omitempty"`
|
||||
Device meowlib.KeyPair `json:"device,omitempty"`
|
||||
KnownServers ServerList `json:"known_servers,omitempty"`
|
||||
MessageServers ServerStorage `json:"message_servers,omitempty"`
|
||||
DefaultDbPassword string `json:"default_db_password,omitempty"`
|
||||
DbPasswordStore bool `json:"db_password_store,omitempty"`
|
||||
OwnedDevices PeerList `json:"owned_devices,omitempty"`
|
||||
StaticMtkServerPaths []ServerList `json:"static_mtk_server_paths,omitempty"`
|
||||
DynamicMtkServeRules []string `json:"dynamic_mtk_serve_rules,omitempty"`
|
||||
InvitationTimeout int `json:"invitation_timeout,omitempty"`
|
||||
Uuid string `json:"uuid,omitempty"`
|
||||
unlockedHiddenPeers PeerList
|
||||
}
|
||||
|
||||
func CreateIdentity(nickname string) *Identity {
|
||||
func CreateIdentity(nickname string) (*Identity, error) {
|
||||
var id Identity
|
||||
id.Nickname = nickname
|
||||
id.Uuid = uuid.New().String()
|
||||
id.RootKp = meowlib.NewKeyPair()
|
||||
return &id
|
||||
GetConfig().me = &id
|
||||
id.MessageServers = ServerStorage{DbFile: uuid.NewString()}
|
||||
id.generateRandomHiddenStuff()
|
||||
err := id.CreateFolder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &id, nil
|
||||
}
|
||||
|
||||
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerIdxs []int) (*meowlib.ContactCard, error) {
|
||||
func (id *Identity) CreateFolder() error {
|
||||
err := os.MkdirAll(filepath.Join(GetConfig().StoragePath, id.Uuid), 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (id *Identity) WipeFolder() error {
|
||||
err := os.RemoveAll(filepath.Join(GetConfig().StoragePath, id.Uuid))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Creates an invitation for a peer, returns the newly created peer including infos to provide a ContactCard
|
||||
func (id *Identity) InvitePeer(MyName string, ContactName string, MessageServerUids []string, InvitationMessage string) (*Peer, error) {
|
||||
var peer Peer
|
||||
var myContactCard meowlib.ContactCard
|
||||
peer.Uid = uuid.New().String()
|
||||
peer.MyIdentity = meowlib.NewKeyPair()
|
||||
peer.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
peer.MyLookupKp = meowlib.NewKeyPair()
|
||||
peer.Name = ContactName
|
||||
peer.InvitationId = uuid.New().String()
|
||||
if id.MessageServers.Servers == nil {
|
||||
return nil, errors.New("no message servers defined in your identity")
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
if i > len(id.MessageServers.Servers)-1 {
|
||||
return nil, errors.New("requested server out of range of defined message servers")
|
||||
peer.InvitationId = uuid.New().String() // todo as param to identify then update url
|
||||
/* if id.MessageServers.Servers == nil {
|
||||
return nil, errors.New("no message servers defined in your identity")
|
||||
}
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := &id.MessageServers.Servers[i].ServerData
|
||||
myContactCard.PullServers = append(myContactCard.PullServers, srv)
|
||||
}
|
||||
myContactCard.Name = MyName
|
||||
myContactCard.ContactPublicKey = peer.MyIdentity.Public
|
||||
myContactCard.EncryptionPublicKey = peer.MyEncryptionKp.Public
|
||||
myContactCard.LookupPublicKey = peer.MyLookupKp.Public
|
||||
myContactCard.InvitationId = peer.InvitationId
|
||||
id.Peers = append(id.Peers, peer)
|
||||
for _, i := range MessageServerIdxs {
|
||||
if i > len(id.MessageServers.Servers)-1 {
|
||||
return nil, errors.New("requested server out of range of defined message servers")
|
||||
}
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].GetServerCard()
|
||||
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
|
||||
}*/
|
||||
/* pullServers, err := id.MessageServers.LoadServerCardsFromUids(MessageServerUids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}*/
|
||||
peer.MyPullServers = MessageServerUids
|
||||
peer.MyName = MyName
|
||||
peer.InvitationMessage = InvitationMessage
|
||||
id.Peers.StorePeer(&peer)
|
||||
|
||||
return &myContactCard, nil
|
||||
return &peer, nil
|
||||
}
|
||||
|
||||
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string) {
|
||||
for _, p := range id.Peers {
|
||||
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames
|
||||
func (id *Identity) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
|
||||
// invitation Id found, this is an answer to an invitation
|
||||
/*for _, p := range id.Peers {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
return true, p.Name, ReceivedContact.Name
|
||||
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||
}
|
||||
}
|
||||
return false, "", ReceivedContact.Name
|
||||
|
||||
// it's an invitation
|
||||
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage*/
|
||||
return id.Peers.CheckInvitation(ReceivedContact)
|
||||
}
|
||||
|
||||
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []int, ReceivedContact *meowlib.ContactCard) *meowlib.ContactCard {
|
||||
// Answers an invitation, returns the newly created peer including infos to provide a ContactCard
|
||||
func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageServerIdxs []string, ReceivedContact *meowlib.ContactCard) *Peer {
|
||||
var peer Peer
|
||||
var myContactCard meowlib.ContactCard
|
||||
//var myContactCard meowlib.ContactCard
|
||||
peer.Uid = uuid.New().String()
|
||||
peer.MyIdentity = meowlib.NewKeyPair()
|
||||
peer.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
peer.MyLookupKp = meowlib.NewKeyPair()
|
||||
@ -85,36 +128,59 @@ func (id *Identity) AnswerInvitation(MyName string, ContactName string, MessageS
|
||||
} else {
|
||||
peer.Name = ReceivedContact.Name
|
||||
}
|
||||
peer.Contact = *ReceivedContact
|
||||
for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].ServerData
|
||||
myContactCard.PullServers = append(myContactCard.PullServers, &srv)
|
||||
peer.ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||
peer.ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||
peer.ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||
peer.InvitationId = ReceivedContact.InvitationId
|
||||
peer.InvitationMessage = ReceivedContact.InvitationMessage
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
peer.ContactPullServers = append(peer.ContactPullServers, ReceivedContact.PullServers[srv].GetUid())
|
||||
id.MessageServers.StoreServerIfNotExists(CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid()))
|
||||
}
|
||||
myContactCard.Name = MyName
|
||||
myContactCard.ContactPublicKey = peer.MyIdentity.Public
|
||||
myContactCard.EncryptionPublicKey = peer.MyEncryptionKp.Public
|
||||
myContactCard.LookupPublicKey = peer.MyLookupKp.Public
|
||||
myContactCard.InvitationId = ReceivedContact.InvitationId
|
||||
/* for _, i := range MessageServerIdxs {
|
||||
srv := id.MessageServers.Servers[i].GetServerCard()
|
||||
peer.MyContact.PullServers = append(peer.MyContact.PullServers, srv)
|
||||
}*/
|
||||
/* srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(MessageServerIdxs)
|
||||
if err != nil {
|
||||
peer.MyContact.PullServers = srvCards
|
||||
}*/
|
||||
peer.MyPullServers = MessageServerIdxs
|
||||
peer.MyName = MyName
|
||||
peer.InvitationId = ReceivedContact.InvitationId
|
||||
id.Peers.StorePeer(&peer)
|
||||
|
||||
id.Peers = append(id.Peers, peer)
|
||||
|
||||
return &myContactCard
|
||||
return &peer
|
||||
}
|
||||
|
||||
// Finalizes an invitation, returns nil if successful
|
||||
func (id *Identity) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
|
||||
for i, p := range id.Peers {
|
||||
/*for i, p := range id.Peers {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
id.Peers[i].Contact = *ReceivedContact
|
||||
//id.Peers[i].Name = ReceivedContact.Name
|
||||
id.Peers[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||
id.Peers[i].ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||
id.Peers[i].ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||
srvs := []string{}
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
|
||||
}
|
||||
id.Peers[i].ContactPullServers = srvs
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
|
||||
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)*/
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
id.MessageServers.StoreServerIfNotExists(CreateServerFromUid(ReceivedContact.PullServers[srv].GetUid()))
|
||||
}
|
||||
return id.Peers.FinalizeInvitation(ReceivedContact)
|
||||
}
|
||||
|
||||
// LoadIdentity loads an identity from an encrypted file
|
||||
func LoadIdentity(filename string, password string) (*Identity, error) {
|
||||
var id Identity
|
||||
GetConfig().memoryPassword = password
|
||||
GetConfig().identityFile = filename
|
||||
GetConfig().IdentityFile = filename
|
||||
indata, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -124,15 +190,172 @@ func LoadIdentity(filename string, password string) (*Identity, error) {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(pass), &id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
GetConfig().me = &id
|
||||
if id.Peers.DbFile != "" {
|
||||
id.Peers.LoadPeers(password)
|
||||
}
|
||||
return &id, err
|
||||
}
|
||||
|
||||
func (id *Identity) Save() error {
|
||||
if GetConfig().IdentityFile == "" {
|
||||
return errors.New("identity filename empty")
|
||||
}
|
||||
b, _ := json.Marshal(id)
|
||||
armor, err := helper.EncryptMessageWithPassword([]byte(GetConfig().memoryPassword), string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(GetConfig().identityFile, []byte(armor), 0600)
|
||||
err = os.WriteFile(GetConfig().IdentityFile, []byte(armor), 0600)
|
||||
return err
|
||||
}
|
||||
|
||||
func (id *Identity) TryUnlockHidden(password string) error {
|
||||
found := false
|
||||
for _, encPeer := range id.HiddenPeers {
|
||||
p := Peer{}
|
||||
jsonPeer, err := meowlib.SymDecrypt(password, encPeer)
|
||||
if err == nil {
|
||||
err = json.Unmarshal(jsonPeer, &p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.dbPassword = password
|
||||
id.unlockedHiddenPeers = append(id.unlockedHiddenPeers, &p)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if found {
|
||||
return nil
|
||||
}
|
||||
return errors.New("no peer found")
|
||||
}
|
||||
|
||||
/*
|
||||
func (id *Identity) HidePeer(peerIdx int, password string) error {
|
||||
serializedPeer, err := json.Marshal(id.Peers[peerIdx])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encrypted, err := meowlib.SymEncrypt(password, serializedPeer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// add encrypted peer data
|
||||
id.HiddenPeers = append(id.HiddenPeers, encrypted)
|
||||
// remove clear text peer
|
||||
id.Peers = append(id.Peers[:peerIdx], id.Peers[peerIdx+1:]...)
|
||||
return nil
|
||||
}*/
|
||||
|
||||
func (id *Identity) generateRandomHiddenStuff() {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
count := r.Intn(maxHiddenCount) + 1
|
||||
for i := 1; i < count; i++ {
|
||||
var p Peer
|
||||
p.Uid = uuid.New().String()
|
||||
p.Name = randomLenString(4, 20)
|
||||
p.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
p.MyIdentity = meowlib.NewKeyPair()
|
||||
p.MyLookupKp = meowlib.NewKeyPair()
|
||||
p.Name = randomLenString(4, 20)
|
||||
p.ContactPublicKey = p.MyLookupKp.Public
|
||||
p.ContactEncryption = p.MyIdentity.Public
|
||||
p.ContactLookupKey = p.MyEncryptionKp.Public
|
||||
p.dbPassword = randomLenString(8, 14)
|
||||
// p.Contact.AddUrls([]string{randomLenString(14, 60), randomLenString(14, 60)}) // todo add servers
|
||||
id.Peers.StorePeer(&p)
|
||||
//id.HidePeer(0, randomLenString(8, 14))
|
||||
// TODO Add random conversations
|
||||
}
|
||||
}
|
||||
|
||||
type BackgroundJob struct {
|
||||
RootPublic string `json:"root_public,omitempty"`
|
||||
Device meowlib.KeyPair `json:"device,omitempty"`
|
||||
Jobs []RequestsJob `json:"jobs,omitempty"`
|
||||
}
|
||||
|
||||
type RequestsJob struct {
|
||||
Server *Server `json:"server,omitempty"`
|
||||
LookupKeys []meowlib.KeyPair `json:"lookup_keys,omitempty"`
|
||||
}
|
||||
|
||||
func (id *Identity) GetRequestJobs() []RequestsJob {
|
||||
var list []RequestsJob
|
||||
srvs := map[string]*RequestsJob{}
|
||||
// get all servers
|
||||
servers, err := id.MessageServers.LoadAllServers()
|
||||
if err == nil {
|
||||
// build a server map
|
||||
for _, server := range servers {
|
||||
var rj RequestsJob
|
||||
rj.Server = server
|
||||
srvs[server.GetServerCard().GetUid()] = &rj
|
||||
}
|
||||
// add ids to the map
|
||||
peers, err := id.Peers.GetPeers()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, peer := range peers {
|
||||
// check if peer inviation is accepted
|
||||
for _, server := range peer.MyPullServers {
|
||||
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
|
||||
}
|
||||
}
|
||||
// add hidden peers
|
||||
for _, peer := range id.unlockedHiddenPeers {
|
||||
for _, server := range peer.MyPullServers {
|
||||
srvs[server].LookupKeys = append(srvs[server].LookupKeys, peer.MyLookupKp)
|
||||
}
|
||||
}
|
||||
// todo add garbage
|
||||
|
||||
// todo random reorder
|
||||
|
||||
// build list
|
||||
for _, srv := range srvs {
|
||||
if len(srv.LookupKeys) > 0 {
|
||||
list = append(list, *srv)
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (id *Identity) SaveBackgroundJob() error {
|
||||
var bj BackgroundJob
|
||||
bj.Jobs = id.GetRequestJobs()
|
||||
bj.RootPublic = id.RootKp.Public
|
||||
bj.Device = id.Device
|
||||
jsonjobs, err := json.Marshal(bj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id.CreateFolder()
|
||||
err = os.WriteFile(filepath.Join(GetConfig().StoragePath, id.Uuid, ".jobs"), jsonjobs, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func randomLenString(min int, max int) string {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
n := r.Intn(max-min) + min
|
||||
return randomString(n)
|
||||
}
|
||||
|
||||
func randomString(n int) string {
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
|
||||
s := make([]rune, n)
|
||||
for i := range s {
|
||||
s[i] = letters[r.Intn(len(letters))]
|
||||
}
|
||||
return string(s)
|
||||
}
|
||||
|
@ -3,8 +3,11 @@ package client
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -16,14 +19,51 @@ func exists(filename string) bool {
|
||||
}
|
||||
}
|
||||
|
||||
func createId() *Identity {
|
||||
config := GetConfig()
|
||||
config.IdentityFile = "test.id"
|
||||
config.memoryPassword = "generalPassword"
|
||||
// ! Extension to quickly open db : Debug only !
|
||||
config.DbSuffix = ".sqlite"
|
||||
id, err := CreateIdentity("myname")
|
||||
if err != nil {
|
||||
log.Fatal("CreateIdentity failed")
|
||||
}
|
||||
err = id.Save()
|
||||
if err != nil {
|
||||
log.Fatal("Save failed")
|
||||
}
|
||||
for i := range 10 {
|
||||
var p Peer
|
||||
p.Uid = uuid.New().String()
|
||||
p.Name = "testName_" + strconv.Itoa(i)
|
||||
p.MyEncryptionKp = meowlib.NewKeyPair()
|
||||
p.MyIdentity = meowlib.NewKeyPair()
|
||||
p.MyLookupKp = meowlib.NewKeyPair()
|
||||
p.Name = "foo_" + strconv.Itoa(i)
|
||||
p.ContactPublicKey = meowlib.NewKeyPair().Public
|
||||
p.ContactEncryption = meowlib.NewKeyPair().Public
|
||||
p.ContactLookupKey = meowlib.NewKeyPair().Public
|
||||
p.MyPullServers = []string{"server1", "server2"}
|
||||
//p.Contact.AddUrls([]string{"http:/127.0.0.1/meow", "tcp://localhost:1234"}) //todo add servers
|
||||
id.Peers.StorePeer(&p)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
if exists("test.id") {
|
||||
os.Remove("test.id")
|
||||
}
|
||||
id, err := LoadIdentity("test.id", "toto")
|
||||
if err != nil {
|
||||
id := CreateIdentity("myname")
|
||||
id, err1 := CreateIdentity("myname")
|
||||
if err1 != nil {
|
||||
log.Fatal("CreateIdentity failed")
|
||||
}
|
||||
id.Save()
|
||||
} else {
|
||||
log.Println(id.Nickname)
|
||||
}
|
||||
id, err = LoadIdentity("test.id", "toto")
|
||||
if err != nil {
|
||||
@ -36,6 +76,53 @@ func TestLoad(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
func TestHidePeer(t *testing.T) {
|
||||
/*
|
||||
id := createId()
|
||||
name := id.Peers[0].Name
|
||||
assert.Equal(t, len(id.Peers), 1)
|
||||
h := len(id.HiddenPeers)
|
||||
id.HidePeer(0, "mypassword")
|
||||
assert.Equal(t, len(id.Peers), 0)
|
||||
assert.Equal(t, len(id.HiddenPeers), h+1)
|
||||
id.TryUnlockHidden("mypassword")
|
||||
assert.Equal(t, len(id.unlockedHiddenPeers), 1)
|
||||
assert.Equal(t, id.unlockedHiddenPeers[0].Name, name)
|
||||
|
||||
if exists("test.id") {
|
||||
os.Remove("test.id")
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// test GetRequestJobs
|
||||
func TestGetRequestJobs(t *testing.T) {
|
||||
// Create a mock Identity object
|
||||
id := createId()
|
||||
id.MessageServers = ServerStorage{
|
||||
DbFile: "test.db",
|
||||
}
|
||||
GetConfig().SetMemPass("test")
|
||||
GetConfig().SetIdentity(id)
|
||||
for i := 1; i < 10; i++ {
|
||||
// initialize a Server with name "server+i"
|
||||
srv := CreateServerFromUrl("server" + strconv.Itoa(i))
|
||||
id.MessageServers.StoreServer(srv)
|
||||
}
|
||||
// Call GetRequestJobs
|
||||
jobs := id.GetRequestJobs()
|
||||
|
||||
// Check that the returned list is as expected
|
||||
assert.Equal(t, 6, len(jobs), "Expected 6 jobs")
|
||||
|
||||
// Check that each job has the correct server and lookup keys
|
||||
for _, job := range jobs {
|
||||
//fmt.Println(job.Server.GetUid(), job.LookupKeys)
|
||||
assert.Contains(t, []string{"server1", "server2", "server3", "server4", "server5", "server6"}, job.Server.GetUid(), "Unexpected server UID")
|
||||
assert.Len(t, job.LookupKeys, 1, "Expected 1 lookup key per job")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
50
client/internalusermessage.go
Normal file
50
client/internalusermessage.go
Normal file
@ -0,0 +1,50 @@
|
||||
package client
|
||||
|
||||
import "forge.redroom.link/yves/meowlib"
|
||||
|
||||
type InternalUserMessage struct {
|
||||
Outbound bool `json:"outbound"`
|
||||
Messagetype string `json:"messagetype,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
Status *meowlib.ConversationStatus `json:"conversation_status,omitempty"`
|
||||
Contact *meowlib.ContactCard `json:"contact,omitempty"`
|
||||
ServerDeliveryUuid string `json:"server_delivery_uuid,omitempty"`
|
||||
ServerDeliveryTimestamp int64 `json:"server_delivery_timestamp,omitempty"`
|
||||
//Group group
|
||||
FilePaths []string `json:"file_paths,omitempty"`
|
||||
CurrentLocation *meowlib.Location `json:"current_location,omitempty"`
|
||||
Appdata []byte `json:"appdata,omitempty"`
|
||||
Dbfile string `json:"dbfile,omitempty"`
|
||||
Dbid int64 `json:"dbid,omitempty"`
|
||||
}
|
||||
|
||||
// InternalUserMessageFromUserMessage creates an InternalUserMessage from a UserMessage
|
||||
func InternalUserMessageFromUserMessage(peer *Peer, msg *meowlib.UserMessage) *InternalUserMessage {
|
||||
iu := new(InternalUserMessage)
|
||||
if peer.ContactPublicKey == msg.From {
|
||||
iu.Outbound = false
|
||||
} else {
|
||||
iu.Outbound = true
|
||||
}
|
||||
iu.Messagetype = msg.Type
|
||||
iu.Message = string(msg.Data)
|
||||
iu.Status = msg.Status
|
||||
iu.Contact = msg.Contact
|
||||
return iu
|
||||
}
|
||||
|
||||
func ProcessOutboundTextMessage(peer *Peer, text string, srv *Server) ([]byte, error) {
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage([]byte(text))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Prepare cyphered + packed user message
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(usermessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||
return srv.ProcessOutboundMessage(toServerMessage)
|
||||
}
|
12
client/logger.go
Normal file
12
client/logger.go
Normal file
@ -0,0 +1,12 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var logger zerolog.Logger
|
||||
|
||||
// AddLogger sets the logger for the sublibrary
|
||||
func AddLogger(l zerolog.Logger) {
|
||||
logger = l
|
||||
}
|
@ -5,7 +5,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func ProcessForOutput(usermessage *meowlib.UserMessage, peer *Peer, servers *InternalServerList, trackingLookupKey string) ([]byte, error) {
|
||||
func ProcessForOutput(usermessage *meowlib.UserMessage, peer *Peer, servers *ServerList, trackingLookupKey string) ([]byte, error) {
|
||||
lastIdx := len(servers.Servers) - 1
|
||||
// LAST SERVER : Message delivery as usual
|
||||
srv := &servers.Servers[lastIdx]
|
||||
@ -26,14 +26,14 @@ func ProcessForOutput(usermessage *meowlib.UserMessage, peer *Peer, servers *Int
|
||||
srv = &servers.Servers[i]
|
||||
var toServerMessage meowlib.ToServerMessage
|
||||
toServerMessage.MatriochkaMessage.Data = lastmsg
|
||||
toServerMessage.MatriochkaMessage.Next.Url = servers.Servers[i+1].ServerData.Url
|
||||
toServerMessage.MatriochkaMessage.Next.PublicKey = servers.Servers[i+1].ServerData.PublicKey
|
||||
toServerMessage.MatriochkaMessage.Next.Url = servers.Servers[i+1].Url
|
||||
toServerMessage.MatriochkaMessage.Next.PublicKey = servers.Servers[i+1].PublicKey
|
||||
toServerMessage.MatriochkaMessage.Next.Delay = int32(servers.Servers[i+1].AllowedDelay)
|
||||
if trackingLookupKey != "" {
|
||||
toServerMessage.MatriochkaMessage.Next.Uuid = lastuuid // change tracking uuid at each server
|
||||
if i > 0 {
|
||||
toServerMessage.MatriochkaMessage.Prev.Url = servers.Servers[i-1].ServerData.Url
|
||||
toServerMessage.MatriochkaMessage.Prev.PublicKey = servers.Servers[i+1].ServerData.PublicKey
|
||||
toServerMessage.MatriochkaMessage.Prev.Url = servers.Servers[i-1].Url
|
||||
toServerMessage.MatriochkaMessage.Prev.PublicKey = servers.Servers[i+1].PublicKey
|
||||
toServerMessage.MatriochkaMessage.Prev.Delay = int32(servers.Servers[i-1].AllowedDelay)
|
||||
toServerMessage.MatriochkaMessage.Prev.Uuid = uuid.NewString()
|
||||
lastuuid = toServerMessage.MatriochkaMessage.Prev.Uuid
|
||||
|
@ -1,17 +0,0 @@
|
||||
package client
|
||||
|
||||
func ProcessOutboundTextMessage(peer *Peer, text string, srv *InternalServer) ([]byte, error) {
|
||||
// Creating User message
|
||||
usermessage, err := peer.BuildSimpleUserMessage([]byte(text))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Prepare cyphered + packed user message
|
||||
packedMsg, err := peer.ProcessOutboundUserMessage(usermessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage := srv.BuildToServerMessageFromUserMessage(packedMsg)
|
||||
return srv.ProcessOutboundMessage(toServerMessage)
|
||||
}
|
366
client/messagestorage.go
Normal file
366
client/messagestorage.go
Normal file
@ -0,0 +1,366 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/google/uuid"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func StoreMessage(peer *Peer, usermessage *meowlib.UserMessage, filenames []string, password string) error {
|
||||
var dbid string
|
||||
cfg := GetConfig()
|
||||
identity := cfg.GetIdentity()
|
||||
// If no db/no ID create DB + Tablz
|
||||
// TODO : if file size > X new db
|
||||
if len(peer.DbIds) == 0 {
|
||||
dbid = uuid.NewString()
|
||||
peer.DbIds = []string{dbid}
|
||||
|
||||
identity.Peers.StorePeer(peer)
|
||||
identity.CreateFolder()
|
||||
file, err := os.Create(filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
peer.DbIds = append(peer.DbIds, dbid)
|
||||
sqliteDatabase, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
err = createMessageTable(sqliteDatabase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sqliteDatabase.Close()
|
||||
} else {
|
||||
dbid = peer.DbIds[len(peer.DbIds)-1]
|
||||
}
|
||||
// Open Db
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// Detach Files
|
||||
hiddenFilenames := []string{}
|
||||
if len(usermessage.Files) > 0 {
|
||||
for _, f := range usermessage.Files {
|
||||
hiddenFilename := uuid.NewString()
|
||||
// Cypher file
|
||||
encData, err := meowlib.SymEncrypt(password, f.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles")); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles"), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
os.WriteFile(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename), encData, 0600)
|
||||
hiddenFilenames = append(hiddenFilenames, filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
||||
// replace f.Data by uuid filename
|
||||
f.Data = []byte(filepath.Join(cfg.StoragePath, identity.Uuid, "securefiles", hiddenFilename))
|
||||
}
|
||||
}
|
||||
outbound := true
|
||||
if usermessage.From == peer.ContactPublicKey {
|
||||
outbound = false
|
||||
}
|
||||
// Convert UserMessage to DbMessage
|
||||
dbm := UserMessageToDbMessage(outbound, usermessage, hiddenFilenames)
|
||||
// Encrypt message
|
||||
out, err := proto.Marshal(dbm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encData, err := meowlib.SymEncrypt(password, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert message
|
||||
insertMessageSQL := `INSERT INTO message(m) VALUES (?) RETURNING ID`
|
||||
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := statement.Exec(encData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ium := DbMessageToInternalUserMessage(id, dbid, dbm)
|
||||
peer.LastMessage = ium
|
||||
identity.Peers.StorePeer(peer)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get new messages from a peer
|
||||
func GetNewMessages(peer *Peer, lastDbId int, password string) ([]*InternalUserMessage, error) {
|
||||
var messages []*InternalUserMessage
|
||||
cfg := GetConfig()
|
||||
identity := cfg.GetIdentity()
|
||||
// handle no db yet
|
||||
if len(peer.DbIds) == 0 {
|
||||
return messages, nil
|
||||
}
|
||||
fileidx := len(peer.DbIds) - 1
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(cfg.StoragePath, identity.Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
||||
if lastDbId == 0 {
|
||||
lastDbId = math.MaxInt64
|
||||
}
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id > ? ORDER BY id DESC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(lastDbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var ium *InternalUserMessage
|
||||
var dbm meowlib.DbMessage
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = peer.DbIds[fileidx]
|
||||
messages = append(messages, ium)
|
||||
}
|
||||
// TODO DB overlap
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
// Get old messages from a peer
|
||||
func GetMessagesHistory(peer *Peer, inAppMsgCount int, lastDbId int, wantMore int, password string) ([]InternalUserMessage, error) {
|
||||
var messages []InternalUserMessage
|
||||
// handle no db yet
|
||||
if len(peer.DbIds) == 0 {
|
||||
return messages, nil
|
||||
}
|
||||
fileidx := len(peer.DbIds) - 1
|
||||
// initialize count with last db message count
|
||||
countStack, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// while the db message count < what we already have in app, step to next db file
|
||||
for inAppMsgCount > countStack {
|
||||
fileidx--
|
||||
if fileidx < 0 {
|
||||
return nil, nil
|
||||
}
|
||||
newCount, err := getMessageCount(peer.DbIds[fileidx])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
countStack += newCount
|
||||
}
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, peer.DbIds[fileidx]+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// if it's first app query, it won't hold a lastIndex, so let's start from end
|
||||
if lastDbId == 0 {
|
||||
lastDbId = math.MaxInt64
|
||||
}
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id < ? ORDER BY id DESC LIMIT ?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(lastDbId, wantMore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var ium *InternalUserMessage
|
||||
var dbm meowlib.DbMessage
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ium = DbMessageToInternalUserMessage(id, peer.DbIds[fileidx], &dbm)
|
||||
ium.Dbid = id
|
||||
ium.Dbfile = peer.DbIds[fileidx]
|
||||
|
||||
messages = append(messages, *ium)
|
||||
}
|
||||
// TODO DB overlap
|
||||
return messages, nil
|
||||
}
|
||||
|
||||
func GetDbMessage(dbFile string, dbId int64, password string) (*meowlib.DbMessage, error) {
|
||||
// There fileidx should provide the db that we need (unless wantMore overlaps the next DB)
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
|
||||
stm, err := db.Prepare("SELECT id, m FROM message WHERE id=?")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer stm.Close()
|
||||
rows, err := stm.Query(dbId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var dbm meowlib.DbMessage
|
||||
for rows.Next() {
|
||||
|
||||
var id int64
|
||||
var m []byte
|
||||
err = rows.Scan(&id, &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
decdata, err := meowlib.SymDecrypt(password, m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = proto.Unmarshal(decdata, &dbm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
return &dbm, nil
|
||||
}
|
||||
|
||||
func UpdateDbMessage(dbm *meowlib.DbMessage, dbFile string, dbId int64, password string) error {
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbFile+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
// Encrypt message
|
||||
out, err := proto.Marshal(dbm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
encData, err := meowlib.SymEncrypt(password, out)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Insert message
|
||||
updateMessageSQL := `UPDATE message SET m=? WHERE id=?`
|
||||
statement, err := db.Prepare(updateMessageSQL) // Prepare statement.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = statement.Exec(encData, dbId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get old messages from a peer
|
||||
func GetMessagePreview(dbFile string, dbId int64, password string) ([]byte, error) {
|
||||
dbm, err := GetDbMessage(dbFile, dbId, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FilePreview(dbm.FilePaths[0], password)
|
||||
}
|
||||
|
||||
// decrypt the a file and returns the raw content
|
||||
func FilePreview(filename string, password string) ([]byte, error) {
|
||||
// get the hidden file
|
||||
encData, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// decrypt the file
|
||||
data, err := meowlib.SymDecrypt(password, encData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// return the raw content from the files content (loads the first image, or build a more complex view)
|
||||
func InternalUserMessagePreview(msg *InternalUserMessage, password string) ([]byte, error) {
|
||||
// get the hidden file name
|
||||
if len(msg.FilePaths) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return FilePreview(msg.FilePaths[0], password)
|
||||
}
|
||||
|
||||
func getMessageCount(dbid string) (int, error) {
|
||||
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, dbid+GetConfig().DbSuffix)) // Open the created SQLite File
|
||||
defer db.Close()
|
||||
var count int
|
||||
query := "SELECT COUNT(*) FROM message"
|
||||
err := db.QueryRow(query).Scan(&count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func createMessageTable(db *sql.DB) error {
|
||||
createMessageTableSQL := `CREATE TABLE message (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"m" BLOB);` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createMessageTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
||||
|
||||
func createServerTable(db *sql.DB) error {
|
||||
createServerTableSQL := `CREATE TABLE servers (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"country" varchar(2),
|
||||
"public" bool,
|
||||
"uptime" int,
|
||||
"bandwith" float,
|
||||
"load" float,
|
||||
"url" varchar(2000)
|
||||
"name" varchar(255);
|
||||
"description" varchar(5000)
|
||||
"publickey" varchar(10000)
|
||||
)` // SQL Statement for Create Table
|
||||
statement, err := db.Prepare(createServerTableSQL) // Prepare SQL Statement
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statement.Exec() // Execute SQL Statements
|
||||
return nil
|
||||
}
|
83
client/messagestorage_test.go
Normal file
83
client/messagestorage_test.go
Normal file
@ -0,0 +1,83 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStoreMessage(t *testing.T) {
|
||||
id := createId()
|
||||
var um meowlib.UserMessage
|
||||
um.Data = []byte("blabla")
|
||||
peers, err := id.Peers.GetPeers()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = StoreMessage(peers[0], &um, []string{}, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
messages, err := GetMessagesHistory(peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Checks
|
||||
assert.Equal(t, len(messages), 1, "not 1 message")
|
||||
assert.Equal(t, messages[0].Message, string(um.Data), "not 1 message")
|
||||
// Cleanup
|
||||
if exists("test.id") {
|
||||
os.Remove("test.id")
|
||||
}
|
||||
files, err := filepath.Glob("*.sqlite")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := os.Remove(f); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestManyStoreMessage(t *testing.T) {
|
||||
id := createId()
|
||||
peers, err := id.Peers.GetPeers()
|
||||
// test with zero messages
|
||||
messages, err := GetMessagesHistory(peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
assert.Equal(t, len(messages), 0, "not 0 message")
|
||||
for i := 1; i < 100; i++ {
|
||||
var um meowlib.UserMessage
|
||||
um.Data = []byte(randomLenString(20, 200))
|
||||
err := StoreMessage(peers[0], &um, []string{}, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
messages, err = GetMessagesHistory(peers[0], 0, 0, 10, GetConfig().memoryPassword)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Checks
|
||||
assert.Equal(t, len(messages), 10, "not 10 message")
|
||||
// Cleanup
|
||||
if exists("test.id") {
|
||||
os.Remove("test.id")
|
||||
}
|
||||
files, err := filepath.Glob("*.sqlite")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, f := range files {
|
||||
if err := os.Remove(f); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
234
client/peer.go
234
client/peer.go
@ -1,7 +1,6 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
@ -11,82 +10,104 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Peer manages the peer messaging functions
|
||||
// - Building simple user messages
|
||||
// - Utility functions for packing/unpacking, encrypting/decrypting messages for peer communication
|
||||
// - Peer might be of type "contact" "group" "personnae" "channel" "device" "sensor"
|
||||
type Peer struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Uid string `json:"uid,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
Avatars []Avatar `json:"avatars,omitempty"`
|
||||
MyName string `json:"my_name,omitempty"`
|
||||
MyAvatar string `json:"my_avatar,omitempty"`
|
||||
// Conversation []InternalMessage `json:"conversation,omitempty"`
|
||||
// My own keys for that peer
|
||||
MyIdentity meowlib.KeyPair `json:"my_identity,omitempty"`
|
||||
MyEncryptionKp meowlib.KeyPair `json:"my_encryption_kp,omitempty"`
|
||||
MyLookupKp meowlib.KeyPair `json:"my_lookup_kp,omitempty"`
|
||||
MyPullServers []meowlib.Server `json:"my_pull_servers,omitempty"`
|
||||
MyIdentity meowlib.KeyPair `json:"my_identity,omitempty"`
|
||||
MyEncryptionKp meowlib.KeyPair `json:"my_encryption_kp,omitempty"`
|
||||
MyLookupKp meowlib.KeyPair `json:"my_lookup_kp,omitempty"`
|
||||
MyPullServers []string `json:"my_pull_servers,omitempty"`
|
||||
// Peer keys and infos
|
||||
Contact meowlib.ContactCard `json:"contact,omitempty"`
|
||||
InvitationId string `json:"invitation_id,omitempty"`
|
||||
//Contact meowlib.ContactCard `json:"contact,omitempty"` // todo : remove
|
||||
ContactPublicKey string `json:"contact_public_key,omitempty"`
|
||||
ContactLookupKey string `json:"contact_lookup_key,omitempty"`
|
||||
ContactEncryption string `json:"contact_encryption,omitempty"`
|
||||
ContactPullServers []string `json:"contact_pull_servers,omitempty"`
|
||||
InvitationId string `json:"invitation_id,omitempty"`
|
||||
InvitationUrl string `json:"invitation_url,omitempty"`
|
||||
InvitationMessage string `json:"invitation_message,omitempty"`
|
||||
InvitationExpiry time.Time `json:"invitation_expiry,omitempty"`
|
||||
LastMessage *InternalUserMessage `json:"last_message,omitempty"`
|
||||
// Internal management attributes
|
||||
Visible bool `json:"visible,omitempty"`
|
||||
VisiblePassword string `json:"visible_password,omitempty"`
|
||||
PasswordType string `json:"password_type,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
MessageNotification string `json:"message_notification,omitempty"`
|
||||
OnionMode bool `json:"onion_mode,omitempty"`
|
||||
LastMessage time.Time `json:"last_message,omitempty"`
|
||||
DbIds []string `json:"db_ids,omitempty"`
|
||||
DbPassword string `json:"db_password,omitempty"`
|
||||
Visible bool `json:"visible,omitempty"`
|
||||
VisiblePassword string `json:"visible_password,omitempty"`
|
||||
PasswordType string `json:"password_type,omitempty"`
|
||||
Blocked bool `json:"blocked,omitempty"`
|
||||
MessageNotification string `json:"message_notification,omitempty"`
|
||||
MatriochkaMode bool `json:"matriochka_mode,omitempty"`
|
||||
ServerDeliveryInfo bool `json:"server_delivery_info,omitempty"`
|
||||
CallsAllowed bool `json:"calls_allowed,omitempty"`
|
||||
DirectMode bool `json:"direct_mode,omitempty"`
|
||||
DbIds []string `json:"db_ids,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
PersonnaeDbId string `json:"personnae_db_id,omitempty"`
|
||||
dbPassword string
|
||||
}
|
||||
|
||||
type PeerList []Peer
|
||||
//
|
||||
// getters and setters
|
||||
//
|
||||
|
||||
type Group struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Members []Peer `json:"members,omitempty"`
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.Contact.ContactPublicKey == publickey {
|
||||
return &peer
|
||||
}
|
||||
func (p *Peer) GetMyContact() *meowlib.ContactCard {
|
||||
var c meowlib.ContactCard
|
||||
c.ContactPublicKey = p.MyIdentity.Public
|
||||
c.LookupPublicKey = p.MyLookupKp.Public
|
||||
c.EncryptionPublicKey = p.MyEncryptionKp.Public
|
||||
srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(p.MyPullServers)
|
||||
if err == nil {
|
||||
c.PullServers = srvCards
|
||||
}
|
||||
return nil
|
||||
c.InvitationId = p.InvitationId
|
||||
c.InvitationMessage = p.InvitationMessage
|
||||
c.Name = p.MyName
|
||||
return &c
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromMyLookupKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.MyLookupKp.Public == publickey {
|
||||
return &peer
|
||||
}
|
||||
func (p *Peer) GetContact() *meowlib.ContactCard {
|
||||
var c meowlib.ContactCard
|
||||
c.ContactPublicKey = p.ContactPublicKey
|
||||
c.LookupPublicKey = p.ContactLookupKey
|
||||
c.EncryptionPublicKey = p.ContactEncryption
|
||||
srvCards, err := GetConfig().GetIdentity().MessageServers.LoadServerCardsFromUids(p.ContactPullServers)
|
||||
if err == nil {
|
||||
c.PullServers = srvCards
|
||||
}
|
||||
return nil
|
||||
c.InvitationId = p.InvitationId
|
||||
c.InvitationMessage = p.InvitationMessage
|
||||
c.Name = p.Name
|
||||
return &c
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromName(name string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.Contact.Name == name {
|
||||
return &peer
|
||||
}
|
||||
func (p *Peer) InvitationPending() bool {
|
||||
if p.ContactPublicKey == "" {
|
||||
return true
|
||||
}
|
||||
return nil
|
||||
return false
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetConversationRequests() []*meowlib.ToServerMessage_ConversationRequest {
|
||||
var list []*meowlib.ToServerMessage_ConversationRequest
|
||||
for _, peer := range *pl {
|
||||
var cr meowlib.ToServerMessage_ConversationRequest
|
||||
cr.LookupKey = peer.MyLookupKp.Public
|
||||
// TODO Add key signature
|
||||
list = append(list, &cr)
|
||||
}
|
||||
return list
|
||||
}
|
||||
//
|
||||
// Messages building
|
||||
//
|
||||
|
||||
func (p *Peer) BuildSimpleUserMessage(message []byte) (*meowlib.UserMessage, error) {
|
||||
var msg meowlib.UserMessage
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.From = p.MyIdentity.Public
|
||||
msg.Data = message
|
||||
msg.Type = "1"
|
||||
msg.Status = &meowlib.UserMessage_ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status = &meowlib.ConversationStatus{}
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
@ -96,16 +117,13 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// declare chunk size
|
||||
maxSz := GetConfig().Chunksize
|
||||
|
||||
// create buffer
|
||||
b := make([]byte, maxSz)
|
||||
chunk := 0
|
||||
@ -120,17 +138,17 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
}
|
||||
var msg meowlib.UserMessage
|
||||
var file meowlib.File
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.From = p.MyIdentity.Public
|
||||
file.Filename = fi.Name()
|
||||
file.Chunk = uint32(chunk)
|
||||
file.Data = b[:readTotal]
|
||||
file.Size = uint64(fi.Size())
|
||||
msg.Files = append(msg.Files, &file)
|
||||
msg.Type = "2"
|
||||
msg.Type = "1"
|
||||
if chunk == 0 {
|
||||
msg.Status = &meowlib.UserMessage_ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status = &meowlib.ConversationStatus{}
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
}
|
||||
msgs = append(msgs, msg)
|
||||
chunk++
|
||||
@ -138,6 +156,29 @@ func (p *Peer) BuildSingleFileMessage(filename string, message []byte) ([]meowli
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
// Builds an invitation answer user message.
|
||||
// it takes as input a contactcard generated by Identity.AnswerInvitation
|
||||
func (p *Peer) BuildInvitationAnswerMessage(myContactCard *meowlib.ContactCard) (*meowlib.UserMessage, error) {
|
||||
var msg meowlib.UserMessage
|
||||
var invitation meowlib.Invitation
|
||||
invitation.Step = 3
|
||||
out, err := proto.Marshal(myContactCard)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
invitation.Uuid = p.InvitationId
|
||||
invitation.Payload = out
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.Invitation = &invitation
|
||||
msg.From = p.MyIdentity.Public
|
||||
msg.Type = "1"
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
//
|
||||
// Messages encryption and packaging
|
||||
//
|
||||
|
||||
func (p *Peer) SerializeUserMessage(msg *meowlib.UserMessage) ([]byte, error) {
|
||||
out, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
@ -158,9 +199,13 @@ func (p *Peer) DeserializeUserMessage(data []byte) (*meowlib.UserMessage, error)
|
||||
// AsymEncryptMessage prepares a message to send to a specific peer contact
|
||||
func (p *Peer) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
||||
var enc *meowlib.EncryptedMessage
|
||||
enc, err := meowlib.AsymEncryptAndSign(p.Contact.EncryptionPublicKey, p.MyIdentity.Private, Message)
|
||||
// fmt.Println("[AsymEncryptMessage] Destination is:", p.ContactLookupKey)
|
||||
// fmt.Println("[AsymEncryptMessage] Contact encryption key is:", p.ContactEncryption)
|
||||
// fmt.Println("[AsymEncryptMessage] My signing key is:", p.MyIdentity.Private)
|
||||
// fmt.Println("[AsymEncryptMessage] Signature should be verified with:", p.MyIdentity.Public)
|
||||
enc, err := meowlib.AsymEncryptAndSign(p.ContactEncryption, p.MyIdentity.Private, Message)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
logger.Error().Err(err).Msg("Peer.AsymEncryptMessage")
|
||||
return enc, err
|
||||
}
|
||||
return enc, err
|
||||
@ -168,23 +213,30 @@ func (p *Peer) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, er
|
||||
|
||||
// AsymDecryptMessage reads a message from a specific peer contact
|
||||
func (p *Peer) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(p.MyEncryptionKp.Private, p.Contact.ContactPublicKey, Message, Signature)
|
||||
// fmt.Println("[AsymDecryptMessage] Decrypting key is:", p.MyEncryptionKp.Private)
|
||||
// fmt.Println("[AsymDecryptMessage] Should have been encrypted with:", p.MyEncryptionKp.Public)
|
||||
// fmt.Println("[AsymDecryptMessage] Signature will be verified with:", p.ContactPublicKey)
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(p.MyEncryptionKp.Private, p.ContactPublicKey, Message, Signature)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
logger.Error().Err(err).Msg("Peer.AsymDecryptMessage")
|
||||
return nil, err
|
||||
}
|
||||
return DecryptedMessage, err
|
||||
}
|
||||
|
||||
// PackUserMessage will package the previously encrypted message for sending it to the peer in protobuff format
|
||||
// PackUserMessage will package the previously encrypted message
|
||||
func (p *Peer) PackUserMessage(message []byte, signature []byte) *meowlib.PackedUserMessage {
|
||||
var msg meowlib.PackedUserMessage
|
||||
msg.Destination = p.Contact.LookupPublicKey
|
||||
msg.Destination = p.ContactLookupKey
|
||||
msg.Payload = message
|
||||
msg.Signature = signature
|
||||
if p.ServerDeliveryInfo {
|
||||
msg.ServerDeliveryUuid = uuid.New().String()
|
||||
}
|
||||
return &msg
|
||||
}
|
||||
|
||||
// UnPackUserMessage unpacks a user message
|
||||
func (p *Peer) UnPackUserMessage(protoPackedMessage []byte) (payload []byte, signature []byte, err error) {
|
||||
msg := &meowlib.PackedServerMessage{}
|
||||
if err := proto.Unmarshal(protoPackedMessage, msg); err != nil {
|
||||
@ -193,11 +245,12 @@ func (p *Peer) UnPackUserMessage(protoPackedMessage []byte) (payload []byte, sig
|
||||
return msg.Payload, msg.Signature, nil
|
||||
}
|
||||
|
||||
func (p *Peer) GetConversationRequest() meowlib.ToServerMessage_ConversationRequest {
|
||||
var cr meowlib.ToServerMessage_ConversationRequest
|
||||
return cr
|
||||
func (p *Peer) GetConversationRequest() *meowlib.ConversationRequest {
|
||||
var cr meowlib.ConversationRequest
|
||||
return &cr
|
||||
}
|
||||
|
||||
// ProcessOutboundUserMessage is a helper function that serializes, encrypts and packs a user message
|
||||
func (p *Peer) ProcessOutboundUserMessage(usermessage *meowlib.UserMessage) (*meowlib.PackedUserMessage, error) {
|
||||
serializedMessage, err := p.SerializeUserMessage(usermessage)
|
||||
if err != nil {
|
||||
@ -210,10 +263,10 @@ func (p *Peer) ProcessOutboundUserMessage(usermessage *meowlib.UserMessage) (*me
|
||||
}
|
||||
// Packing it
|
||||
packedMsg := p.PackUserMessage(enc.Data, enc.Signature)
|
||||
|
||||
return packedMsg, nil
|
||||
}
|
||||
|
||||
// ProcessInboundUserMessage is a helper function that decrypts and deserializes a user message
|
||||
func (p *Peer) ProcessInboundUserMessage(message []byte, signature []byte) (*meowlib.UserMessage, error) {
|
||||
dec, err := p.AsymDecryptMessage(message, signature)
|
||||
if err != nil {
|
||||
@ -226,16 +279,45 @@ func (p *Peer) ProcessInboundUserMessage(message []byte, signature []byte) (*meo
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (p *Peer) StoreMessage(msg []byte) {
|
||||
//
|
||||
// Messages database
|
||||
//
|
||||
|
||||
// SetDbPassword sets a specific password for a hidden user, it won't be saved, but kept in memory only for the next operations
|
||||
func (p *Peer) SetDbPassword(password string) {
|
||||
p.dbPassword = password
|
||||
}
|
||||
|
||||
func (p *Peer) GetDbPassword() string {
|
||||
if p.dbPassword == "" {
|
||||
return GetConfig().memoryPassword
|
||||
}
|
||||
return p.dbPassword
|
||||
}
|
||||
|
||||
func (p *Peer) StoreMessage(msg *meowlib.UserMessage, filenames []string) error {
|
||||
return StoreMessage(p, msg, filenames, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) GetFilePreview(filename string) ([]byte, error) {
|
||||
return FilePreview(filename, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) UpdateMessage(msg InternalUserMessage) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Peer) LoadMessagesHistory(alreadyLoadedCount int, oldestMessageId int, qty int) ([]InternalUserMessage, error) {
|
||||
return GetMessagesHistory(p, alreadyLoadedCount, oldestMessageId, qty, p.GetDbPassword())
|
||||
|
||||
}
|
||||
|
||||
func (p *Peer) LoadMessage(uid string) {
|
||||
|
||||
func (p *Peer) LoadNewMessages(lastMessageId int) ([]*InternalUserMessage, error) {
|
||||
return GetNewMessages(p, lastMessageId, p.GetDbPassword())
|
||||
}
|
||||
|
||||
func (p *Peer) LoadLastMessages(qty int) {
|
||||
|
||||
func (p *Peer) LoadMessage(uid string) (*InternalUserMessage, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *Peer) GetLastMessageUuid(msg []byte) {
|
||||
|
@ -1,10 +1,29 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetFromPublicKey(t *testing.T) {
|
||||
id := CreateIdentity("test")
|
||||
id, err := CreateIdentity("test")
|
||||
if err != nil {
|
||||
t.Fatal("CreateIdentity failed")
|
||||
}
|
||||
id.Save()
|
||||
for i := 1; i < 10; i++ {
|
||||
var p Peer
|
||||
p.Uid = uuid.New().String()
|
||||
p.Name = "test" + strconv.Itoa(i)
|
||||
p.ContactPublicKey = "stringToFind" + strconv.Itoa(i)
|
||||
err := id.Peers.StorePeer(&p)
|
||||
if err != nil {
|
||||
t.Fatal("StorePeer failed")
|
||||
}
|
||||
}
|
||||
p5 := id.Peers.GetFromPublicKey("stringToFind5")
|
||||
assert.Equal(t, p5.Name, "test5")
|
||||
}
|
||||
|
51
client/peerlist.go
Normal file
51
client/peerlist.go
Normal file
@ -0,0 +1,51 @@
|
||||
package client
|
||||
|
||||
type PeerList []*Peer
|
||||
|
||||
func (pl *PeerList) GetFromPublicKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.ContactPublicKey == publickey {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromInvitationId(invitationId string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.InvitationId == invitationId {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromMyLookupKey(publickey string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.MyLookupKp.Public == publickey {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pl *PeerList) GetFromName(name string) *Peer {
|
||||
for _, peer := range *pl {
|
||||
if peer.Name == name {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ! Wrong implementation, does not discriminate on different servers
|
||||
/*func (pl *PeerList) GetConversationRequests() []*meowlib.ConversationRequest {
|
||||
var list []*meowlib.ConversationRequest
|
||||
for _, peer := range *pl {
|
||||
var cr meowlib.ConversationRequest
|
||||
cr.LookupKey = peer.MyLookupKp.Public
|
||||
// TODO Add key signature
|
||||
list = append(list, &cr)
|
||||
}
|
||||
return list
|
||||
}*/
|
248
client/peerstorage.go
Normal file
248
client/peerstorage.go
Normal file
@ -0,0 +1,248 @@
|
||||
package client
|
||||
|
||||
//
|
||||
// Storage
|
||||
//
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/dgraph-io/badger"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PeerStorage struct {
|
||||
DbFile string `json:"db_file,omitempty"`
|
||||
db *badger.DB
|
||||
cache map[string]*Peer
|
||||
}
|
||||
|
||||
// Open the badger database from struct PeerStorage
|
||||
func (ps *PeerStorage) open() error {
|
||||
if ps.DbFile == "" {
|
||||
ps.DbFile = uuid.New().String()
|
||||
GetConfig().GetIdentity().Save()
|
||||
}
|
||||
if ps.cache == nil {
|
||||
ps.cache = make(map[string]*Peer)
|
||||
}
|
||||
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ps.DbFile))
|
||||
opts.Logger = nil
|
||||
var err error
|
||||
ps.db, err = badger.Open(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store function StorePeer stores a peer in the badger database with Peer.Uid as key
|
||||
func (ps *PeerStorage) StorePeer(peer *Peer) error {
|
||||
err := ps.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ps.close()
|
||||
// first marshal the Peer to bytes with protobuf
|
||||
jsonsrv, err := json.Marshal(peer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
password := GetConfig().memoryPassword
|
||||
if peer.dbPassword != "" {
|
||||
password = peer.dbPassword
|
||||
}
|
||||
data, err := meowlib.SymEncrypt(password, jsonsrv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shakey := sha256.Sum256([]byte(peer.Uid))
|
||||
key := shakey[:]
|
||||
// add it to cache
|
||||
ps.cache[peer.Uid] = peer
|
||||
// then store it in the database
|
||||
return ps.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Set(key, data)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// LoadPeer function loads a Peer from the badger database with Peer.GetUid() as key
|
||||
func (ps *PeerStorage) LoadPeer(uid string, password string) (*Peer, error) {
|
||||
var peer Peer
|
||||
err := ps.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ps.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
err = ps.db.View(func(txn *badger.Txn) error {
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &peer)
|
||||
})
|
||||
})
|
||||
return &peer, err
|
||||
}
|
||||
|
||||
// DeletePeer function deletes a Peer from the badger database with Peer.GetUid() as key
|
||||
func (ps *PeerStorage) DeletePeer(uid string) error {
|
||||
err := ps.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ps.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
return ps.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
// LoadPeers function loads Peers from the badger database with a specific password
|
||||
func (ps *PeerStorage) LoadPeers(password string) ([]*Peer, error) {
|
||||
var peers []*Peer
|
||||
err := ps.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ps.close()
|
||||
err = ps.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.PrefetchSize = 10
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
item := it.Item()
|
||||
var sc Peer
|
||||
err := item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(password, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err == nil {
|
||||
peers = append(peers, &sc)
|
||||
ps.cache[sc.Uid] = &sc
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
// Sort peers based on peer.Name
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
return peers[i].Name < peers[j].Name
|
||||
})
|
||||
return peers, err
|
||||
}
|
||||
|
||||
// GetPeers function returns all peers from the cache as a sorted array
|
||||
func (ps *PeerStorage) GetPeers() ([]*Peer, error) {
|
||||
peers := make([]*Peer, 0, len(ps.cache))
|
||||
for _, peer := range ps.cache {
|
||||
peers = append(peers, peer)
|
||||
}
|
||||
// Sort peers based on peer.Name
|
||||
sort.Slice(peers, func(i, j int) bool {
|
||||
return peers[i].Name < peers[j].Name
|
||||
})
|
||||
return peers, nil
|
||||
}
|
||||
|
||||
// close the badger database
|
||||
func (ps *PeerStorage) close() {
|
||||
ps.db.Close()
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) GetFromPublicKey(publickey string) *Peer {
|
||||
for _, peer := range ps.cache {
|
||||
if peer.ContactPublicKey == publickey {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) GetFromInvitationId(invitationId string) *Peer {
|
||||
for _, peer := range ps.cache {
|
||||
if peer.InvitationId == invitationId {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) GetFromMyLookupKey(publickey string) *Peer {
|
||||
for _, peer := range ps.cache {
|
||||
if peer.MyLookupKp.Public == publickey {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) NameExists(name string) bool {
|
||||
for _, peer := range ps.cache {
|
||||
if peer.Name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) GetFromName(name string) *Peer {
|
||||
for _, peer := range ps.cache {
|
||||
if peer.Name == name {
|
||||
return peer
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *PeerStorage) GetFromUid(uid string) *Peer {
|
||||
return ps.cache[uid]
|
||||
}
|
||||
|
||||
// Checks if the received contact card is an answer to an invitation, returns true if it is, and the proposed and received nicknames
|
||||
func (ps *PeerStorage) CheckInvitation(ReceivedContact *meowlib.ContactCard) (isAnswer bool, proposedNick string, receivedNick string, invitationMessage string) {
|
||||
// invitation Id found, this is an answer to an invitation
|
||||
for _, p := range ps.cache {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
return true, p.Name, ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||
}
|
||||
}
|
||||
// it's an invitation
|
||||
return false, "", ReceivedContact.Name, ReceivedContact.InvitationMessage
|
||||
}
|
||||
|
||||
// Finalizes an invitation, returns nil if successful
|
||||
func (ps *PeerStorage) FinalizeInvitation(ReceivedContact *meowlib.ContactCard) error {
|
||||
for i, p := range ps.cache {
|
||||
if p.InvitationId == ReceivedContact.InvitationId {
|
||||
//id.Peers[i].Name = ReceivedContact.Name
|
||||
ps.cache[i].ContactEncryption = ReceivedContact.EncryptionPublicKey
|
||||
ps.cache[i].ContactLookupKey = ReceivedContact.LookupPublicKey
|
||||
ps.cache[i].ContactPublicKey = ReceivedContact.ContactPublicKey
|
||||
srvs := []string{}
|
||||
for srv := range ReceivedContact.PullServers {
|
||||
srvs = append(srvs, ReceivedContact.PullServers[srv].GetUid())
|
||||
}
|
||||
ps.cache[i].ContactPullServers = srvs
|
||||
ps.StorePeer(ps.cache[i])
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("no matching contact found for invitationId " + ReceivedContact.InvitationId)
|
||||
}
|
60
client/peerstorage_test.go
Normal file
60
client/peerstorage_test.go
Normal file
@ -0,0 +1,60 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
ps := &PeerStorage{
|
||||
DbFile: "peerdb.test",
|
||||
cache: nil,
|
||||
db: nil,
|
||||
}
|
||||
|
||||
err := ps.open()
|
||||
if err != nil {
|
||||
t.Errorf("Failed to open database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorePeer(t *testing.T) {
|
||||
id := createId()
|
||||
GetConfig().SetMemPass("test")
|
||||
GetConfig().SetIdentity(id)
|
||||
ps := &PeerStorage{
|
||||
DbFile: "peerdb.test",
|
||||
cache: nil,
|
||||
db: nil,
|
||||
}
|
||||
|
||||
peer := &Peer{
|
||||
Uid: uuid.New().String(),
|
||||
Name: "testName",
|
||||
InvitationId: "testInvitationId",
|
||||
MyName: "testMyName",
|
||||
ContactPublicKey: "testContactPublicKey",
|
||||
}
|
||||
|
||||
err := ps.StorePeer(peer)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to store peer: %v", err)
|
||||
}
|
||||
|
||||
// load the peer from the database
|
||||
peers, err := ps.LoadPeers(GetConfig().GetMemPass())
|
||||
if err != nil {
|
||||
t.Errorf("Failed to load peers: %v", err)
|
||||
}
|
||||
if len(peers) != 1 {
|
||||
t.Errorf("Expected 1 peer, got %d", len(peers))
|
||||
}
|
||||
if peers[0].Uid != peer.Uid {
|
||||
t.Errorf("Expected peer to have uid %s, got %s", peer.Uid, peers[0].Uid)
|
||||
}
|
||||
if peers[0].MyName != peer.MyName {
|
||||
t.Errorf("Expected peer to have MyName %s, got %s", peer.MyName, peers[0].MyName)
|
||||
}
|
||||
|
||||
}
|
240
client/server.go
240
client/server.go
@ -1,7 +1,7 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
@ -9,75 +9,145 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type InternalServer struct {
|
||||
ServerData meowlib.Server `json:"server_data,omitempty"`
|
||||
// Server manages server related operations
|
||||
// - Sending messages for server usage
|
||||
// - Two first steps of an invitation
|
||||
// - User message sending with UserKp identification
|
||||
// - Messages lookup requests
|
||||
// - Utility functions for packing/unpacking, encrypting/decrypting messages for server communication
|
||||
// - Server remote management if ManagerKp is available for that server
|
||||
type Server struct {
|
||||
//ServerCard meowlib.ServerCard `json:"server_data,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
PublicKey string `json:"public_key,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
Login string `json:"login,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Presence bool `json:"presence,omitempty"`
|
||||
LastCheck time.Time `json:"last_check,omitempty"`
|
||||
Uptime time.Duration `json:"uptime,omitempty"`
|
||||
Login string `json:"login,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Me meowlib.KeyPair `json:"me,omitempty"`
|
||||
UserKp meowlib.KeyPair `json:"user_kp,omitempty"`
|
||||
ManagerKp meowlib.KeyPair `json:"manager_kp,omitempty"`
|
||||
Country string `json:"country,omitempty"`
|
||||
AllowedDelay int `json:"allowed_delay,omitempty"`
|
||||
Backup bool `json:"backup,omitempty"`
|
||||
WebRTC bool `json:"webrtc,omitempty"`
|
||||
}
|
||||
|
||||
type InternalServerList struct {
|
||||
Name string
|
||||
Servers []InternalServer
|
||||
}
|
||||
|
||||
func InternalServerFromUrl(url string) *InternalServer {
|
||||
var is InternalServer
|
||||
is.ServerData.Url = url
|
||||
// CreateServerFromUrl creates a server from a basic url, ex : https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromUrl(url string) *Server {
|
||||
var is Server
|
||||
is.Name = url
|
||||
is.Url = url
|
||||
is.UserKp = meowlib.NewKeyPair()
|
||||
return &is
|
||||
}
|
||||
|
||||
func InternalServerFromServer(server *meowlib.Server) *InternalServer {
|
||||
var is InternalServer
|
||||
is.ServerData = *server
|
||||
is.Me = meowlib.NewKeyPair()
|
||||
return &is
|
||||
}
|
||||
|
||||
func (sl *InternalServerList) AddUrls(urls []string) {
|
||||
for _, url := range urls {
|
||||
sl.Servers = append(sl.Servers, *InternalServerFromUrl(url))
|
||||
// CreateServerFromUid creates a server from a uid string, ex : mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromUid(uid string) *Server {
|
||||
var is Server
|
||||
uidTable := strings.Split(uid, "@") //! Weak test, use regexp
|
||||
is.Name = uid
|
||||
is.UserKp = meowlib.NewKeyPair()
|
||||
if len(uidTable) == 2 {
|
||||
loginpw := strings.Split(uidTable[0], ":")
|
||||
is.Url = uidTable[1]
|
||||
is.Login = loginpw[0]
|
||||
is.Password = loginpw[1]
|
||||
} else {
|
||||
is.Url = uidTable[0]
|
||||
}
|
||||
return &is
|
||||
}
|
||||
|
||||
// CreateServerFromMeowUrl creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow/
|
||||
func CreateServerFromMeowUrl(meowurl string) *Server {
|
||||
uid := strings.Replace(meowurl[7:], "//", "://", 1)
|
||||
return CreateServerFromUid(uid)
|
||||
}
|
||||
|
||||
// CreateServerFromInvitationLink creates a server from a meow url, ex : meow://mylogin:mypassword@https://my.meowserver.example:8443/meow?invitationCode
|
||||
func CreateServerFromInvitationLink(meowurl string) *Server {
|
||||
// remove the invitation code, last token after a /
|
||||
meowurlTable := strings.Split(meowurl, "?")
|
||||
// join all elements with / except the last one
|
||||
meowSrvUrl := meowurlTable[0]
|
||||
return CreateServerFromMeowUrl(meowSrvUrl)
|
||||
}
|
||||
|
||||
// GetServerCard returns a server card from a server
|
||||
func (ints *Server) GetServerCard() *meowlib.ServerCard {
|
||||
var sc meowlib.ServerCard
|
||||
sc.Name = ints.Name
|
||||
sc.PublicKey = ints.PublicKey
|
||||
sc.Description = ints.Description
|
||||
sc.Url = ints.Url
|
||||
sc.Login = ints.Login
|
||||
sc.Password = ints.Password
|
||||
return &sc
|
||||
}
|
||||
|
||||
func (sc *Server) GetUid() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return sc.Url
|
||||
}
|
||||
|
||||
func (sc *Server) GetMeowUrl() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return "meow://" + sc.Url
|
||||
}
|
||||
|
||||
// Create a server from a server card
|
||||
func CreateServerFromServerCard(server *meowlib.ServerCard) *Server {
|
||||
var is Server
|
||||
is.Name = server.Name
|
||||
is.PublicKey = server.PublicKey
|
||||
is.Description = server.Description
|
||||
is.Url = server.Url
|
||||
is.Login = server.Login
|
||||
is.Password = server.Password
|
||||
is.UserKp = meowlib.NewKeyPair()
|
||||
return &is
|
||||
}
|
||||
|
||||
// AsymEncryptMessage prepares a message to send to a specific internal server
|
||||
func (ints *InternalServer) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
||||
func (ints *Server) AsymEncryptMessage(Message []byte) (*meowlib.EncryptedMessage, error) {
|
||||
var enc *meowlib.EncryptedMessage
|
||||
enc, err := meowlib.AsymEncryptAndSign(ints.ServerData.PublicKey, ints.Me.Private, Message)
|
||||
enc, err := meowlib.AsymEncryptAndSign(ints.PublicKey, ints.UserKp.Private, Message)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
logger.Error().Err(err).Msg("Server.AsymEncryptMessage")
|
||||
return nil, err
|
||||
}
|
||||
return enc, err
|
||||
}
|
||||
|
||||
// AsymDecryptMessage reads a message from a specific internal server
|
||||
func (ints *InternalServer) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.Me.Private, ints.ServerData.PublicKey, Message, Signature)
|
||||
func (ints *Server) AsymDecryptMessage(Message []byte, Signature []byte) (DecryptedMessage []byte, err error) {
|
||||
DecryptedMessage, err = meowlib.AsymDecryptAndCheck(ints.UserKp.Private, ints.PublicKey, Message, Signature)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
logger.Error().Err(err).Msg("Server.AsymDecryptMessage")
|
||||
return nil, err
|
||||
}
|
||||
return DecryptedMessage, err
|
||||
}
|
||||
|
||||
// Creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array
|
||||
func (ints *InternalServer) BuildToServerMessageFromUserMessage(usermsg *meowlib.PackedUserMessage) *meowlib.ToServerMessage {
|
||||
// BuildToServerMessageFromUserMessage creates a basic message to server from a single packed user message and returns a meowlib.ToServerMessage
|
||||
func (ints *Server) BuildToServerMessageFromUserMessage(usermsg *meowlib.PackedUserMessage) *meowlib.ToServerMessage {
|
||||
var msg meowlib.ToServerMessage
|
||||
msg.Uuid = uuid.New().String()
|
||||
msg.Type = "1"
|
||||
msg.From = ints.Me.Public
|
||||
msg.From = ints.UserKp.Public
|
||||
msg.Messages = append(msg.Messages, usermsg)
|
||||
return &msg
|
||||
}
|
||||
|
||||
// Creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array
|
||||
func (ints *InternalServer) BuildMessageSendingMessage(usermsg *meowlib.PackedUserMessage) ([]byte, error) {
|
||||
// BuildMessageSendingMessage creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array
|
||||
func (ints *Server) BuildMessageSendingMessage(usermsg *meowlib.PackedUserMessage) ([]byte, error) {
|
||||
msg := ints.BuildToServerMessageFromUserMessage(usermsg)
|
||||
out, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
@ -86,13 +156,13 @@ func (ints *InternalServer) BuildMessageSendingMessage(usermsg *meowlib.PackedUs
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Creates a basic message to server from a single packed user message and returns it as protobuf serialized byte array
|
||||
func (ints *InternalServer) BuildMessageRequestMessage(lookupKeys []string) ([]byte, error) {
|
||||
// ! Unfinished unused ?
|
||||
// BuildMessageRequestMessage creates a message lookup message to server and returns it as protobuf serialized byte array
|
||||
func (ints *Server) BuildMessageRequestMessage(lookupKeys []string) ([]byte, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
msg.Uuid = uuid.New().String()
|
||||
msg.Type = "1"
|
||||
msg.From = ints.Me.Public
|
||||
|
||||
msg.From = ints.UserKp.Public
|
||||
out, err := proto.Marshal(&msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -100,9 +170,91 @@ func (ints *InternalServer) BuildMessageRequestMessage(lookupKeys []string) ([]b
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (ints *InternalServer) PackServerMessage(payload []byte, signature []byte) (protoPackedMessage []byte, err error) {
|
||||
// BuildVideoRoomRequestMessage creates a video room request to server and returns it as protobuf serialized byte array
|
||||
func (ints *Server) BuildVideoRoomRequestMessage(users []string, expiry uint64) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
msg.Uuid = uuid.New().String()
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
// declare an array of meow.VideoCredential
|
||||
videocreds := make([]*meowlib.VideoCredential, len(users))
|
||||
for idx := range users {
|
||||
videocreds[idx] = &meowlib.VideoCredential{
|
||||
Username: users[idx],
|
||||
}
|
||||
}
|
||||
msg.VideoData.Credentials = videocreds
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitation creates an invitation message to server and returns it as a meowlib.ToServerMessage
|
||||
// it takes as input a contactcard generated by Identity.InvitePeer
|
||||
func (ints *Server) BuildToServerMessageInvitationCreation(invitation *meowlib.ContactCard, password string, timeout int, shortCodeLen int) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
payload, err := invitation.Compress()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.Step = 1
|
||||
inv.Password = password
|
||||
inv.Timeout = int32(timeout)
|
||||
inv.ShortcodeLen = int32(shortCodeLen)
|
||||
inv.Payload = payload
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitationRequest requests invitation with provided id from server and returns it as a meowlib.ToServerMessage
|
||||
func (ints *Server) BuildToServerMessageInvitationRequest(shortcode string, password string) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.Step = 2
|
||||
inv.Password = password
|
||||
inv.Shortcode = shortcode
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitationAnswer creates an invitation answer to server and returns it as a meowlib.ToServerMessage
|
||||
// it takes as input a contactcard generated by Identity.InvitePeer
|
||||
func (ints *Server) BuildToServerMessageInvitationAnswer(invitationAnswer *meowlib.PackedUserMessage, myPublicKeyForThatPeer string, invitation_id string, timeout int) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
invitationPayload, err := proto.Marshal(invitationAnswer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inv.Step = 3
|
||||
inv.Uuid = invitation_id
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.From = myPublicKeyForThatPeer
|
||||
inv.Payload = invitationPayload
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// BuildToServerMessageInvitationAnswerRequest requests invitation answer with provided id from server and returns it as a meowlib.ToServerMessage
|
||||
func (ints *Server) BuildToServerMessageInvitationAnswerRequest(invitationId string) (*meowlib.ToServerMessage, error) {
|
||||
var msg meowlib.ToServerMessage
|
||||
var inv meowlib.Invitation
|
||||
msg.Type = "1"
|
||||
msg.From = ints.UserKp.Public
|
||||
inv.Step = 4
|
||||
inv.Uuid = invitationId
|
||||
msg.Invitation = &inv
|
||||
return &msg, nil
|
||||
}
|
||||
|
||||
// PackServerMessage
|
||||
func (ints *Server) PackServerMessage(payload []byte, signature []byte) (protoPackedMessage []byte, err error) {
|
||||
var msg meowlib.PackedServerMessage
|
||||
msg.From = ints.Me.Public
|
||||
msg.From = ints.UserKp.Public
|
||||
msg.Payload = payload
|
||||
msg.Signature = signature
|
||||
out, err := proto.Marshal(&msg)
|
||||
@ -112,7 +264,7 @@ func (ints *InternalServer) PackServerMessage(payload []byte, signature []byte)
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (ints *InternalServer) UnPackServerMessage(protoPackedMessage []byte) (payload []byte, signature []byte, err error) {
|
||||
func (ints *Server) UnPackServerMessage(protoPackedMessage []byte) (payload []byte, signature []byte, err error) {
|
||||
msg := &meowlib.PackedServerMessage{}
|
||||
if err := proto.Unmarshal(protoPackedMessage, msg); err != nil {
|
||||
return nil, nil, err
|
||||
@ -120,7 +272,7 @@ func (ints *InternalServer) UnPackServerMessage(protoPackedMessage []byte) (payl
|
||||
return msg.Payload, msg.Signature, nil
|
||||
}
|
||||
|
||||
func (srv *InternalServer) ProcessOutboundMessage(toServerMessage *meowlib.ToServerMessage) ([]byte, error) {
|
||||
func (srv *Server) ProcessOutboundMessage(toServerMessage *meowlib.ToServerMessage) ([]byte, error) {
|
||||
byteToServerMessage, err := proto.Marshal(toServerMessage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -138,7 +290,7 @@ func (srv *InternalServer) ProcessOutboundMessage(toServerMessage *meowlib.ToSer
|
||||
return protoPackedServerMsg, nil
|
||||
}
|
||||
|
||||
func (srv *InternalServer) ProcessInboundServerResponse(msg []byte) (*meowlib.FromServerMessage, error) {
|
||||
func (srv *Server) ProcessInboundServerResponse(msg []byte) (*meowlib.FromServerMessage, error) {
|
||||
fsmsg := &meowlib.FromServerMessage{}
|
||||
payload, signature, err := srv.UnPackServerMessage(msg)
|
||||
if err != nil {
|
||||
|
52
client/serverlist.go
Normal file
52
client/serverlist.go
Normal file
@ -0,0 +1,52 @@
|
||||
package client
|
||||
|
||||
import "errors"
|
||||
|
||||
// ServerList manages lists that are used in Identity
|
||||
// - Message servers list
|
||||
// - ArchiveServers lists
|
||||
// - Owned servers lists
|
||||
// - Matriochka paths
|
||||
type ServerList struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Servers []Server `json:"servers,omitempty"`
|
||||
}
|
||||
|
||||
// FilterByIdxs returns a filtered server list filtered according to an index list
|
||||
func (sl *ServerList) FilterByIdxs(MessageServerIdxs []int) (filtered *ServerList, err error) {
|
||||
filtered.Servers = []Server{}
|
||||
for _, i := range MessageServerIdxs {
|
||||
if i > len(sl.Servers)-1 {
|
||||
return nil, errors.New("requested server out of range of defined message servers")
|
||||
}
|
||||
}
|
||||
for _, i := range MessageServerIdxs {
|
||||
filtered.Servers = append(filtered.Servers, sl.Servers[i])
|
||||
}
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
// GetServerByIdx returns a server from it's index
|
||||
func (sl *ServerList) GetServerByIdx(idx int) (server *Server, err error) {
|
||||
if idx > len(sl.Servers)-1 {
|
||||
return nil, errors.New("requested server out of range of defined message servers")
|
||||
}
|
||||
return &sl.Servers[idx], nil
|
||||
}
|
||||
|
||||
// GetServerByPubkey returns a server from it's public key
|
||||
func (sl *ServerList) GetServerByPubkey(pubkey string) (filtered *Server) {
|
||||
for _, srv := range sl.Servers {
|
||||
if srv.PublicKey == pubkey {
|
||||
return &srv
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddUrls is a simple utility functon used mainly as a shortcut for testing purposes
|
||||
func (sl *ServerList) AddUrls(urls []string) {
|
||||
for _, url := range urls {
|
||||
sl.Servers = append(sl.Servers, *CreateServerFromUrl(url))
|
||||
}
|
||||
}
|
268
client/serverstorage.go
Normal file
268
client/serverstorage.go
Normal file
@ -0,0 +1,268 @@
|
||||
package client
|
||||
|
||||
//
|
||||
// Storage
|
||||
//
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/dgraph-io/badger"
|
||||
)
|
||||
|
||||
type ServerStorage struct {
|
||||
DbFile string `json:"db_file,omitempty"`
|
||||
db *badger.DB
|
||||
}
|
||||
|
||||
// Open a badger database from struct ServerStorage
|
||||
func (ss *ServerStorage) open() error {
|
||||
|
||||
opts := badger.DefaultOptions(filepath.Join(GetConfig().StoragePath, GetConfig().GetIdentity().Uuid, ss.DbFile))
|
||||
opts.Logger = nil
|
||||
var err error
|
||||
ss.db, err = badger.Open(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Store function StoreServer stores a server in a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) StoreServer(sc *Server) error {
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ss.close()
|
||||
// first marshal the Server to bytes with protobuf
|
||||
jsonsrv, err := json.Marshal(sc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, err := meowlib.SymEncrypt(GetConfig().memoryPassword, jsonsrv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
||||
key := shakey[:]
|
||||
// then store it in the database
|
||||
return ss.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Set(key, data)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// Check if a server exists in a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) ServerExists(sc *Server) (bool, error) {
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer ss.close()
|
||||
|
||||
shakey := sha256.Sum256([]byte(sc.GetServerCard().GetUid()))
|
||||
key := shakey[:]
|
||||
// check if key exists in badger database
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
_, err := txn.Get(key)
|
||||
return err
|
||||
}) // Add a comma here
|
||||
if err != nil { // key does not exist
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Store a server in a badger database with Server.GetUid() as key if it is not already there
|
||||
func (ss *ServerStorage) StoreServerIfNotExists(sc *Server) error {
|
||||
exists, err := ss.ServerExists(sc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
return ss.StoreServer(sc)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadServer function loads a Server from a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) LoadServer(uid string) (*Server, error) {
|
||||
var sc Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
})
|
||||
return &sc, err
|
||||
}
|
||||
|
||||
// DeleteServer function deletes a Server from a badger database with Server.GetUid() as key
|
||||
func (ss *ServerStorage) DeleteServer(uid string) error {
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer ss.close()
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
return ss.db.Update(func(txn *badger.Txn) error {
|
||||
return txn.Delete(key)
|
||||
})
|
||||
}
|
||||
|
||||
// LoadAllServers function loads all Servers from a badger database
|
||||
func (ss *ServerStorage) LoadAllServers() ([]*Server, error) {
|
||||
var scs []*Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.PrefetchSize = 10
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
item := it.Item()
|
||||
var sc Server
|
||||
err := item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, &sc)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadAllServers function loads all ServersCards from a badger database
|
||||
func (ss *ServerStorage) LoadAllServerCards() ([]*meowlib.ServerCard, error) {
|
||||
var scs []*meowlib.ServerCard
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
opts := badger.DefaultIteratorOptions
|
||||
opts.PrefetchSize = 10
|
||||
it := txn.NewIterator(opts)
|
||||
defer it.Close()
|
||||
for it.Rewind(); it.Valid(); it.Next() {
|
||||
item := it.Item()
|
||||
var sc Server
|
||||
err := item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, sc.GetServerCard())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
||||
func (ss *ServerStorage) LoadServersFromUids(uids []string) ([]*Server, error) {
|
||||
var scs []*Server
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
for _, uid := range uids {
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sc Server
|
||||
err = item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, &sc)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// LoadServersFromUids function loads Servers with id in []Uid parameter from a badger database
|
||||
func (ss *ServerStorage) LoadServerCardsFromUids(uids []string) ([]*meowlib.ServerCard, error) {
|
||||
var scs []*meowlib.ServerCard
|
||||
err := ss.open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer ss.close()
|
||||
err = ss.db.View(func(txn *badger.Txn) error {
|
||||
for _, uid := range uids {
|
||||
shakey := sha256.Sum256([]byte(uid))
|
||||
key := shakey[:]
|
||||
item, err := txn.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var sc Server
|
||||
err = item.Value(func(val []byte) error {
|
||||
jsonsrv, err := meowlib.SymDecrypt(GetConfig().memoryPassword, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(jsonsrv, &sc)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
scs = append(scs, sc.GetServerCard())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return scs, err
|
||||
}
|
||||
|
||||
// close a badger database
|
||||
func (ss *ServerStorage) close() {
|
||||
ss.db.Close()
|
||||
}
|
224
client/serverstorage_test.go
Normal file
224
client/serverstorage_test.go
Normal file
@ -0,0 +1,224 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
)
|
||||
|
||||
func TestGetUid(t *testing.T) {
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://127.0.0.1:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
uid := srv.GetUid()
|
||||
if uid != "http://127.0.0.1:8080" {
|
||||
log.Fatal("uid not correct")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreServer(t *testing.T) {
|
||||
createId()
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://127.0.0.1",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
sout, err := ss.LoadServer(srv.GetServerCard().GetUid())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout.Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestLoadServersFromUids(t *testing.T) {
|
||||
createId()
|
||||
GetConfig().SetMemPass("test")
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sout, err := ss.LoadServersFromUids([]string{srv.GetServerCard().GetUid()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout[0].Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestLoadServerCardsFromUids(t *testing.T) {
|
||||
createId()
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
srv := Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
err := ss.StoreServer(&srv)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
sout, err := ss.LoadServerCardsFromUids([]string{srv.GetServerCard().GetUid()})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if sout == nil {
|
||||
log.Fatal("server not found")
|
||||
}
|
||||
if sout[0].Name != srv.Name {
|
||||
log.Fatal("name not found")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestServerExists(t *testing.T) {
|
||||
createId()
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
|
||||
server := &Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
|
||||
// Check if server exists before storing it
|
||||
exists, err := ss.ServerExists(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to check if server exists: %v", err)
|
||||
}
|
||||
if exists {
|
||||
t.Errorf("Server exists before storing it")
|
||||
}
|
||||
|
||||
// Store the server
|
||||
err = ss.StoreServer(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to store server: %v", err)
|
||||
}
|
||||
|
||||
// Check if server exists after storing it
|
||||
exists, err = ss.ServerExists(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to check if server exists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("Server does not exist after storing it")
|
||||
}
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestStoreServerIfNotExists(t *testing.T) {
|
||||
createId()
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
|
||||
server := &Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
|
||||
// Check if server exists before storing it
|
||||
exists, err := ss.ServerExists(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to check if server exists: %v", err)
|
||||
}
|
||||
if exists {
|
||||
t.Errorf("Server exists before storing it")
|
||||
}
|
||||
|
||||
// Store the server if it does not exist
|
||||
err = ss.StoreServerIfNotExists(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to store server: %v", err)
|
||||
}
|
||||
|
||||
// Check if server exists after storing it
|
||||
exists, err = ss.ServerExists(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to check if server exists: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Errorf("Server does not exist after storing it")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
||||
|
||||
func TestStoreServerIfNotExists_ServerExists(t *testing.T) {
|
||||
createId()
|
||||
ss := ServerStorage{DbFile: "test.db"}
|
||||
|
||||
server := &Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
|
||||
// Store the server
|
||||
err := ss.StoreServer(server)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to store server: %v", err)
|
||||
}
|
||||
|
||||
// Store the server again with a different public key
|
||||
newServer := &Server{
|
||||
Name: "test",
|
||||
Url: "http://localhost:8080",
|
||||
PublicKey: meowlib.NewKeyPair().Public,
|
||||
}
|
||||
|
||||
err = ss.StoreServerIfNotExists(newServer)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to store server: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve the server and check if the public key has not changed
|
||||
storedServer, err := ss.LoadServer(server.GetServerCard().GetUid())
|
||||
if err != nil {
|
||||
t.Errorf("Failed to get server: %v", err)
|
||||
}
|
||||
|
||||
if storedServer.PublicKey != server.PublicKey {
|
||||
t.Errorf("Public key was modified")
|
||||
}
|
||||
|
||||
// Clean up
|
||||
// recursively remove the test.db folder
|
||||
os.RemoveAll("test.db")
|
||||
}
|
@ -17,8 +17,8 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func ServerFromUrl(url string) *Server {
|
||||
var s Server
|
||||
func ServerFromUrl(url string) *ServerCard {
|
||||
var s ServerCard
|
||||
s.Url = url
|
||||
return &s
|
||||
}
|
||||
|
@ -1,10 +1,13 @@
|
||||
package meowlib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCompress(t *testing.T) {
|
||||
func TestCompressAndJson(t *testing.T) {
|
||||
kp1 := NewKeyPair()
|
||||
kp2 := NewKeyPair()
|
||||
kp3 := NewKeyPair()
|
||||
@ -13,9 +16,50 @@ func TestCompress(t *testing.T) {
|
||||
cc.ContactPublicKey = kp1.Public
|
||||
cc.EncryptionPublicKey = kp2.Public
|
||||
cc.LookupPublicKey = kp3.Public
|
||||
cc.InvitationMessage = "hello, it's me"
|
||||
cc.AddUrls([]string{"https://meow.myfirstdomain.com/services/meow:8080", "https://meow.myseconddomain.com/services/meow:8080", "http://meow.mythirddomain.com/services/meow:8080"})
|
||||
serialized, _ := cc.Serialize()
|
||||
println(len(serialized))
|
||||
compressed, _ := cc.Compress()
|
||||
println(len(compressed))
|
||||
ncc, err := NewContactCardFromCompressed(compressed)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
assert.Equal(t, ncc.Name, cc.Name)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
jsoncc, err := json.Marshal(cc)
|
||||
var cc1 ContactCard
|
||||
err = json.Unmarshal(jsoncc, &cc1)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticJson(t *testing.T) {
|
||||
ccsrt := `
|
||||
{
|
||||
"contact_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNy40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBZy9iRnR4WG5hRjZLOFEzdGVWcmt3Y3YvRTV6TE94WnVZaXVvCm5MVkdtdWZOQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUhmcmlVWXZsWnhyRmlFRXFFMDIKVVRmOTRTY1hJWVdjZCt1SlJpK1ZuR3NDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBYmN3QgpBT1VsTHJEbWpCM0pKeGNWUFNHaU1KTlZrem1idlhMTDVSSnh4aTNuNVVrMUFRQ2NHN29QeDYwTUdHRVNhc0V0CnBTS2VqUGFmNjNTVXhMelRoRFFacTlqOUI4NDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEJXeXMvaFZHSGcKRFN0V2Jid3VnbnlCdTFWdUlJbVBZMDRsKzRCQWd4QUFZQU1CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRZCt1SgpSaStWbkdzV0lRU29UVFpSTi8zaEp4Y2hoWngzNjRsR0w1V2Nhd0liREFBQWkyTUEvMTdUYksyT3FMdzZDSWZmCkE3YnlwYitxNzdHVmZlQmtmY2l3aXlCM2xRSGxBUUROZzJONisxcklEbG40cXRRc0pFSWR1OUlMMzVlMjR6cWwKbEJMSVR0YVBBQT09Cj1KeHZrCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"encryption_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tCkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnClZlcnNpb246IEdvcGVuUEdQIDIuNy40Cgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBY3FocFM5dnFzVGE2WXJCWVEra1JaY1VDWnFSRUhVUTMrbWQyClZOdlNLYS9OQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUNHb3JhR2xRMVVoRmlFRVcrZVUKRzJjZnVSaU5CVDNwSWFpdG9hVkRWU0VDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBNmRRQgpBSVN3OUFKeVphem83TWs2R2NVZzR6ZDROR1p2dVorZnNxMThoTmtNd0EwRUFRQzNaNmNUT2kraGlURjJLazVGClBtSnI4aHlQWlREVGgwa1I5NE14TzhBa0NzNDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEQzYWZpeS9ZT3YKZFRxMXJ0UTZhVTVLNS9COTFKTW5SaVptSGtTYW9YRDZYd01CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRSWFpdApvYVZEVlNFV0lRUmI1NVFiWngrNUdJMEZQZWtocUsyaHBVTlZJUUliREFBQVpxZ0EvMElaRTR2MmRZbFB5NURJCnNPeTlDbTNFakRJSy80SElJK1VuRm5qeGFtVENBUDQwbUJqaEJKZVRNbzV5ZWpDN2xlYXliUlNMcE1yY1NIeEcKSmpnOGJ0eDVBdz09Cj1rd1VTCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"invitation_id": "38373ab8-f423-48bc-9136-628f2a7e0e18",
|
||||
"invitation_message": "Hi ! it's me !",
|
||||
"lookup_public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNy40CkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWlpST1pCWUpLd1lCQkFIYVJ3OEJBUWRBWmh2NGY3WG55aGhoU3JPQkUrc0VteGsrWkFGNFdjTjY4KythCnAwV25FU0xOQzI1aGJXVWdQRzFoYVd3K3dvOEVFeFlJQUVFRkFtV1VUbVFKRUVPTnRxN202L2h3RmlFRU9EaWMKL1cyMVJqNmJMWkdXUTQyMnJ1YnIrSEFDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdVbkNRSUhBZ0FBbi84QQovMWRjRHVIY3Fjd3JrNW9sclVPMUlIbVhSYWg1aC9wbm9HSDZMM0JSdlphbkFRRHozZTc0TmxZWFpnank3SVBFCittbnM5MDR6TnVqdnpFYks2SHg2dTh1VkJNNDRCR1dVVG1RU0Npc0dBUVFCbDFVQkJRRUJCMEFzVnBZd000TEMKK0JNT201WXFRWUEzRlFiTXI4alp3Wk5IelFvQ29URDRJZ01CQ2duQ2VBUVlGZ2dBS2dVQ1paUk9aQWtRUTQyMgpydWJyK0hBV0lRUTRPSno5YmJWR1Bwc3RrWlpEamJhdTV1djRjQUliREFBQUJxb0EvaUFOMGpIV0FHTXV2MTYxCkdxcXF6aGxPQXFzVjVKNW5iMy9LMk43TU9Pek1BUURzZEVaMlU5VmxXY3ljWDFuZGFoMnkzUnEvQ1QwWkJ0R2IKUzM1dU5HbDFBZz09Cj1kT2lnCi0tLS0tRU5EIFBHUCBQVUJMSUMgS0VZIEJMT0NLLS0tLS0=",
|
||||
"name": "me",
|
||||
"pull_servers": [
|
||||
{
|
||||
"name": "local",
|
||||
"public_key": "LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdvcGVuUEdQIDIuNS4wCkNvbW1lbnQ6IGh0dHBzOi8vZ29wZW5wZ3Aub3JnCgp4ak1FWSs2TUd4WUpLd1lCQkFIYVJ3OEJBUWRBVmk1ZnQyTmlmSzByVmVmYzNQd3JTRXhxNHRhVUtaTzhQeXprClB4SmNTT1BOQzI1aGJXVWdQRzFoYVd3K3dvd0VFeFlJQUQ0RkFtUHVqQnNKa0M4aTlzQjVvVjNyRmlFRWpNeEgKZldiZmxMblRhZzFRTHlMMndIbWhYZXNDR3dNQ0hnRUNHUUVEQ3drSEFoVUlBeFlBQWdJaUFRQUF4L0lCQU5kMgpVK3hZM09LQVk5elFSbmlXQXdlVEpoMWxySEpaMHd6RmVQS3JycUJvQVAwYjRISHBoT2dJWWx2TXlqajZ0TXZRCk01RTIzY3ZiWjRPZXRjNmNmeWxIQ2M0NEJHUHVqQnNTQ2lzR0FRUUJsMVVCQlFFQkIwQ2t1bWlndUpNT003Sy8KNEl1NVppYkJUYXAwSzBkNXNybkNCN2tIU2pObGV3TUJDZ25DZUFRWUZnZ0FLZ1VDWSs2TUd3bVFMeUwyd0htaApYZXNXSVFTTXpFZDladCtVdWROcURWQXZJdmJBZWFGZDZ3SWJEQUFBMlcwQS9qY2pZTUtQY3ZXcTA2QVpVKzRHClQwUmQxU2VNUXpzNndCUU9ZejEwQkVMWEFRQ3hTQ2kvN2RqRjZUWFl0SFpBSytrVUEvUHpYaW14bnRvVFpKbjMKV3l1Z0NnPT0KPTFjU20KLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQ==",
|
||||
"url": "http://localhost:8080"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
var cc1 ContactCard
|
||||
jsoncc := []byte(ccsrt)
|
||||
err := json.Unmarshal(jsoncc, &cc1)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
}
|
||||
|
17
doc/docgen.sh
Executable file
17
doc/docgen.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
go-plantuml generate -o generated/meowlib.puml -d ..
|
||||
sed -i 's/\.\./meowlib\ /g' generated/meowlib.puml
|
||||
go-plantuml generate -o generated/client.puml -d ../client
|
||||
sed -i 's/\.\.\/client/client/g' generated/client.puml
|
||||
go-plantuml generate -o generated/server.puml -d ../server
|
||||
sed -i 's/\.\.\/server/server/g' generated/server.puml
|
||||
cp *.puml generated/
|
||||
cd generated
|
||||
plantuml .
|
||||
plantuml -latex .
|
||||
mv *.tex tex/
|
||||
mv *.png png/
|
||||
rm *.log *.aux *.fdb_latexmk *.fls *.gz *.puml
|
||||
|
||||
|
||||
|
7
doc/endpoints/company_endpoint.puml
Normal file
7
doc/endpoints/company_endpoint.puml
Normal file
@ -0,0 +1,7 @@
|
||||
@startuml Company Company
|
||||
Employee -> Company: Join company with provided credentials
|
||||
Company -> Employee: provide a full featured validated peer
|
||||
Company -> All: Publishes Employee ContactCard and info
|
||||
Company -> Employee: notify publication done, service active
|
||||
Employee -> Company: search for contacts
|
||||
@enduml
|
10
doc/endpoints/public_endpoint.puml
Normal file
10
doc/endpoints/public_endpoint.puml
Normal file
@ -0,0 +1,10 @@
|
||||
@startuml Public endpoint (free for chat)
|
||||
User -> Endpoint: Create & send invitation for endpoint (Generate User ContactCard and create endpoint pending contact)
|
||||
Endpoint -> User: Auto-accept invitation and answer (Generate Endpoint ContactCard and create finalized User contact)
|
||||
User -> Endpoint: [auto]validate Answer, invitation finalize (Finalize Endpoint contact and notify Endpoint that communication is possible)
|
||||
Endpoint -> User: query mandatory/optional info for profile publication
|
||||
User -> Endpoint: provide info
|
||||
Endpoint -> All: Publishes User ContactCard and info
|
||||
Endpoint -> User: notify publication done, service active
|
||||
User -> Endpoint: search for contacts
|
||||
@enduml
|
7
doc/invitation/sq_invitation.puml
Normal file
7
doc/invitation/sq_invitation.puml
Normal file
@ -0,0 +1,7 @@
|
||||
@startuml Server Invitation Step 01
|
||||
Bob -> MeowBob: Create invitation for alice (Generate Bob ContactCard and create Alice pending contact)
|
||||
Bob -> Alice: Send invitation (Bob ContactCard)
|
||||
Alice -> MeowAlice: Accept Invitation and create answer (Generate Alice ContactCard and create finalized Bob contact)
|
||||
Alice -> Bob: Send answer (Alice ContactCard)
|
||||
Bob -> MeowBob: Review Answer, invitation finalize (Finalize Alice contact and notify Alice that communication is possible)
|
||||
@enduml
|
12
doc/invitation/sq_srvinv01.puml
Normal file
12
doc/invitation/sq_srvinv01.puml
Normal file
@ -0,0 +1,12 @@
|
||||
@startuml Server Invitation Step 01
|
||||
User -> Bastet : fill invitation
|
||||
User -> Bastet : select servers
|
||||
Bastet -> NativeLib : get server cards for selected uids
|
||||
NativeLib -> Bastet : server cards
|
||||
Bastet -> NativeLib : invitationCreateMessage
|
||||
NativeLib -> Bastet : invitationMessage
|
||||
Bastet -> Server : send invitation
|
||||
Server -> Redis : Store invitation
|
||||
Server -> Bastet : invitation URL
|
||||
Bastet -> User : invitation URL
|
||||
@enduml
|
11
doc/invitation/sq_srvinv02.puml
Normal file
11
doc/invitation/sq_srvinv02.puml
Normal file
@ -0,0 +1,11 @@
|
||||
@startuml Server Invitation Step 02
|
||||
User -> Bastet : paste URL
|
||||
Bastet -> NativeLib : build invitationGetMessage
|
||||
NativeLib -> Bastet : invitationGetMessage
|
||||
Bastet -> Server : send invitationGetMessage
|
||||
Redis -> Server : retrieve invitation
|
||||
Server -> Bastet : invitation message
|
||||
Bastet -> NativeLib : decode invitation message
|
||||
NativeLib -> Bastet : invitation data
|
||||
Bastet -> User : invitation data
|
||||
@enduml
|
12
doc/invitation/sq_srvinv03.puml
Normal file
12
doc/invitation/sq_srvinv03.puml
Normal file
@ -0,0 +1,12 @@
|
||||
|
||||
@startuml Server Invitation Step 03
|
||||
User -> Bastet : select servers
|
||||
User -> Bastet : accept invitation
|
||||
Bastet -> NativeLib : accept invitation
|
||||
Bastet -> NativeLib : build accept message
|
||||
NativeLib -> Bastet : invitationGetMessage
|
||||
Bastet -> Server : send accept message
|
||||
Server -> Redis : store accept message
|
||||
Server -> Bastet : accept message ok
|
||||
Bastet -> User : accept msg sent
|
||||
@enduml
|
9
doc/invitation/sq_srvinv04.puml
Normal file
9
doc/invitation/sq_srvinv04.puml
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
@startuml Server Invitation Step 03
|
||||
Bastet -> NativeLib : periodic message check
|
||||
NativeLib -> Server : get new messages
|
||||
Server -> NativeLib : send invitation message
|
||||
Server -> Redis : store accept message
|
||||
Server -> Bastet : accept message ok
|
||||
Bastet -> User : invitation sent is accepted
|
||||
@enduml
|
2816
doc/meow.svg
Normal file
2816
doc/meow.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 467 KiB |
225
doc/meow.tex
Normal file
225
doc/meow.tex
Normal file
@ -0,0 +1,225 @@
|
||||
\documentclass{article}
|
||||
\usepackage{fetamont}
|
||||
\begin{document}
|
||||
\title{
|
||||
\textffm{Meow} messaging protocol}
|
||||
\author{Author
|
||||
\texttt{meow@redroom.link}}
|
||||
\date{\today}
|
||||
|
||||
\maketitle
|
||||
|
||||
\begin{abstract}
|
||||
|
||||
|
||||
The \textffm{Meow} protocol is a privacy driven instant messaging protocol.
|
||||
That protocol might be used for creating secure and distributed chat services or allowing machine to machine communication.
|
||||
This document describes the services provided by the protocol.
|
||||
\begin{quote}
|
||||
|
||||
\centering
|
||||
\emph{"Nous ne vivrons pas d'utopie collective, nous arrivons trop tard, le grand marché est déjà là.
|
||||
Nous devons élaborer des stratégies de survie et de contamination, par la prolifération d'utopies privées, cryptées, qui se substitueront à l'ancien ordre social.
|
||||
Tout ce que je sais, c'est que nous vivons dans un monde dont on ne s'évade pas"}\\
|
||||
\footnotesize{Maurice G. Dantec for NOII (1997)}
|
||||
\end{quote}
|
||||
\end{abstract}
|
||||
|
||||
|
||||
\section{Services}
|
||||
\subsection{Unregulated identities}
|
||||
The only requirement to get a valid \textffm{Meow} identity is to generate a user key pair.
|
||||
No phone number or email check will be performed, unlike main instant messaging protocols, there is no central administration.
|
||||
|
||||
\subsection{Fine grained privacy control}
|
||||
|
||||
\subsubsection{Trustable server based communication}
|
||||
Like most widely available messaging softwares, (Whatsapp, Signal, Viber, Telegram...), \textffm{Meow} provides a simple server based messaging.
|
||||
The main difference is that \textffm{Meow} allows you to explicitly choose which server you want to use.
|
||||
The server code being open source, we strongly encourage you to run your own server at home or in your company.
|
||||
The server requires very few ressources and will run on any low cost single board computer.
|
||||
|
||||
\subsubsection{Anonymized message transfer}
|
||||
\textffm{Meow} also provides an anonymizing transfer service very similar to the Tor Onion protocol, we call it the Matriochka protocol.
|
||||
Any server can be used for building the transfer chain.
|
||||
Some of them might be marked as trusted.
|
||||
Random delays and random size payload padding might be set for each forwarding step, making the overall message tracking much more difficult, even for organizations having capabilities of global network surveillance.
|
||||
It is strongly advised to use trusted servers as your first node and message server (the one that holds your incoming messages).
|
||||
|
||||
|
||||
\subsubsection{Message lookup obfuscation}
|
||||
Your device will request for messages using conversation keys on a very regular basis to your messaging(s) server(s).
|
||||
The device will check for conversation keys for all your contacts. If you check that option, it will also check for hidden contact keys.
|
||||
In case of data interception on your device link, in order to prevent statistical analysis, every request might be answered with size useful data (server's known server list).
|
||||
Moreover, some random keys will be added to your requests list.
|
||||
|
||||
\subsubsection{Presence protocol for direct messaging TBC}
|
||||
A presence service associating your conversation keys to your IP address for direct peer to peer connection is also provided.
|
||||
The presence protocol is simply activated by setting a flag in the message poll requests.
|
||||
If that flag is set, your encrypted IP will be published on the server, allowing your only your peer(s) to decrypt it and directly communicate with your terminal.
|
||||
|
||||
\subsubsection{Peer based privacy settings}
|
||||
You might define specific communication privacy preferences for each of your contacts:
|
||||
\begin{itemize}
|
||||
\item simple server based communication allowed for Sarah,
|
||||
\item preferred direct communication with Julian, fallback to my own server,
|
||||
\item required matriochka protocol for Edward, first node is one of my trusted servers, my message node is my own server, randomly switch from trusted server lists for others.
|
||||
\item ...
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Resistance to device requisition / forensic}
|
||||
All your contact information and discussion are encrypted on the device and password protected.
|
||||
Password shall be asked on application startup and allows your identity file and contact decrytion.
|
||||
That password is not recoverable, so you can't forget it, or you'll loose your whole configuration and identity.
|
||||
Real security implies some constraints.
|
||||
You might configure the app to save your password, but that is a security flaw.
|
||||
In many authoritarian countries, you are required by law to provide your device passwords to authorities.
|
||||
In a \textffm{Meow} device, you might set a specific password for some contacts.
|
||||
Those contacts won't be visible when entering your main identity password.
|
||||
You'll have to type their specific password in order to make them visible.
|
||||
The \textffm{Meow} application will by default create a random set of fake hidden contacts and conversations.
|
||||
Even in case of device storage analysis, authorities won't be able to differentiate a real hidden contact from an normal fake generated one.
|
||||
It could be argued that this feature puts every user at risk, because authorities might think you're hiding something, even if you're not.
|
||||
As every \textffm{Meow} user has the same constraint, users are not responsible for that. Moreover solidarity is also a requirement for real security.
|
||||
|
||||
\subsection{Multiple devices support}
|
||||
\textffm{Meow} allows you to be connected from multiple devices and offers chat synchronization capability.
|
||||
A device might be revoqued anytime from any other one. Proof of your identity (password or other) shall be provided in order to grant device revocation.
|
||||
|
||||
\subsection{Adding contacts}
|
||||
If you want to add a new contact, keys will be generated, then a contact card will be created.
|
||||
That contact card might be sent by any trustable communication means, or preferably from hand to hand, as a file on a flash disk or a QR code.\\
|
||||
In return your contact will provide a similar contact card as an answer to your invitation.
|
||||
|
||||
\subsection{Contacts forwarding}
|
||||
By using the \textffm{Meow} protocol a user won't be able to forward your contact information without your consent.
|
||||
Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless. Any message to that identity signed by another user than you would be discarded.
|
||||
|
||||
|
||||
\subsection{Group conversation}
|
||||
A very basic group messaging service is available. It allows to exchange group information between users. After that, a message to a group will send a copy of the message to each member.
|
||||
|
||||
|
||||
\subsection{Emergency broadcast}
|
||||
A local (server based) emergency broadcast service will be provided. It will provide the ability to send/receive broadcast messages to all users connected to the current server.
|
||||
|
||||
|
||||
\subsection{Public networks shortage resilience}
|
||||
\textffm{Meow} may run without Internet connection, either on an isolated wifi access point, or on a meshed network of wifi routers or even via serial IOT transport layers (LoRa,...)
|
||||
|
||||
|
||||
\subsection{User directory service}
|
||||
This service allows restoring a lost functionality of Internet historic chat services (like ICQ). You could simply set a "Free for chat" status that would allow other people to contact you, either randomly or based on a short description that you might provide.
|
||||
Why providing that service while the internet is suffocating due to the abundance of social networks ?\\
|
||||
Well, that option offers a few advantages :
|
||||
\begin{itemize}
|
||||
\item you are still an anonymous user chatting with other anonymous users;
|
||||
\item no social network algorithm will select people that think/behave/vote/eat... just like you. Diversity makes a better world;
|
||||
\item a smaller community of users, skilled enough to operate a \textffm{Meow} chat app... that might provide a first filter;
|
||||
It's a bit like in the early ages, when people had to be able to start a win98 computer, connect it to internet, then download and install ICQ...
|
||||
If you lost some time in social networks today, and experienced ICQ in the 2000's, you'll understand what we'd like to revive.
|
||||
\end{itemize}
|
||||
|
||||
\section{Identities and keys}
|
||||
|
||||
\subsection{User identity}
|
||||
Each \textffm{Meow} user has a unique identity. That identity is strictly private, only used to manage your own data (local encryption, devices, ...)
|
||||
Let's call that one the User Key Pair (Ukp)
|
||||
|
||||
\subsection{Contact identity}
|
||||
Each of your contacts will know you under a different identity, we'll call that one the Contact Key Pair (Ckp)
|
||||
That contact Key Pair will not change once it's agreed between both peers: an initial key will be exchanged as part of the peer invitation process.
|
||||
As other people might have seen your key, this means that :
|
||||
\begin{itemize}
|
||||
\item none of your contacts will be able to forward your id to another person without your consent;
|
||||
\item any message to that Ckp, not signed by its associated user, will be discarded.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Conversation encryption}
|
||||
Each conversation with one of your contacts will be encrypted using an encryption keypair (Ekp) allowing cyphering your conversation.
|
||||
The Ekp might be changed anytime by its owner and the new public key will be sent along the last message.
|
||||
|
||||
\subsection{Conversation lookup}
|
||||
A contact conversation Lookup Key Pair(Lkp) is also associated with your conversation. The Lkp public key is used to identify your conversation on a server.
|
||||
The private key allows you to sign your request and prove the server that you are the legitimate recipient for a message.
|
||||
This Lkp can be changed anytime by its owner and the new public key will be sent along the last message.
|
||||
The Lkp and the Ekp are only changed once the change has been acknowledged by your contact.
|
||||
|
||||
\subsection{Server identity}
|
||||
Each server has a Server key (Skp). That key allows you to cypher the messages that you're sending to the server.
|
||||
|
||||
\subsection{Device identity}
|
||||
Each device is identified by a device key (Dkp) that allows you to perform secured exchanges between your devices for synchronization/revocation purposes.
|
||||
Communication between devices is achieved using the same principle as the user to user communication. A device might be considered as any another user. The messages content is based on a synchronization protocol.
|
||||
|
||||
\section{Contact management}
|
||||
\subsection{Adding a contact}
|
||||
Rendez-vous card, containing :
|
||||
\begin{itemize}
|
||||
\item Your public key for that contact;
|
||||
\item An initial conversation public key for getting encrypted messages from that contact;
|
||||
\item An initial conversation uuid that you'll use to lookup for incoming messages on the servers;
|
||||
\item A list of your preferred message servers;
|
||||
\item A signature to prevent transmission of tampered data.
|
||||
\end{itemize}
|
||||
\subsection{Sharing a contact}
|
||||
If a user wants to forward one of his contacts to you, it will be handled as a double request:
|
||||
\begin{enumerate}
|
||||
\item I'm receiving a contact name, without any key
|
||||
\item
|
||||
\end{enumerate}
|
||||
|
||||
\section{Messaging}
|
||||
\subsection{User messages}
|
||||
TODO
|
||||
|
||||
\subsection{Server stored message}
|
||||
TODO
|
||||
|
||||
\subsection{Matriochka message packing}
|
||||
TODO
|
||||
|
||||
\subsection{Synchronization messages}
|
||||
TODO
|
||||
|
||||
|
||||
\section{Server Features}
|
||||
\subsection{Server catalog}
|
||||
Each server will cache a list of all the servers that it is aware of.
|
||||
This server list will be shared between servers in a lazy exchange mode.
|
||||
|
||||
\subsection{Antispam}
|
||||
|
||||
\subsection{Self defense}
|
||||
The servers do integrate self defense mechanisms. Any threat to the \textffm{Meow} network by any computer,
|
||||
computer group or organization, might result in a distributed response from volunteering \textffm{Meow} servers and clients.
|
||||
An information about threat, desired defense action and request for assitance, might be submitted by any server or group of servers.
|
||||
Server owners and client users might accept or refuse to participate to the response action.
|
||||
|
||||
TODO : Request and actions definition consensus mechanism
|
||||
\section{Backup}
|
||||
\section{Recovery}
|
||||
|
||||
\section{Very secure devices}
|
||||
You don't trust your phone ?
|
||||
We're planning to provide very secured minimal devices dedicated to very sensitive \textffm{Meow} communication.
|
||||
|
||||
\section{Roadmap}
|
||||
\subsection{Nations}
|
||||
Beyond the scope of user directories, we plan to implement the concept of virtual Nations.
|
||||
Nation will allow people to regroup around common political funding values.
|
||||
They're not exclusive, you might be a citizen of several virtual nations.
|
||||
|
||||
Today still, most people don't really choose the nation they live in.
|
||||
You just have to live with the goverment decisions.
|
||||
In the best scenario that government was elected, and might represent at most 25\% of the population.
|
||||
In most case, they will vote laws to satisfy the powerful people who supported their election, and the most powerful lobbies.
|
||||
|
||||
\textffm{Meow} Nations aims to be the next lobbying power to influence real life politics, "the poor man's lobby".
|
||||
|
||||
Virtual nation in that perspective will be probably quickly flagged as terrorist nation by the old world media, but well,
|
||||
one man's terrorist is another man's freedom fighter.
|
||||
If requiring more democracy, using the same technique that is preventing it from happening, has to qualified that way, so be it.
|
||||
|
||||
|
||||
\end{document}
|
10
doc/messaging/sq_msg01.puml
Normal file
10
doc/messaging/sq_msg01.puml
Normal file
@ -0,0 +1,10 @@
|
||||
@startuml
|
||||
Client1 -> Server: Send Message (PubKeyClient2 + Payload)
|
||||
Server -> Client1: Ack Message (Server UUID + DateReceived)
|
||||
Client1 --> Server: Message delivery ? (PubKeyClient2 + Payload)
|
||||
Server --> Client1: Message delivered (Server UUID + DateReceived)
|
||||
|
||||
Client2 -> Server: Get Messages (PubKeyClient2)
|
||||
Server -> Client2: Get Messages [](PubKeyClient2 + Payload)
|
||||
Client2 --> Server: Ack Messages Processed
|
||||
@enduml
|
155
doc/protocol.tex
155
doc/protocol.tex
@ -1,8 +1,11 @@
|
||||
\documentclass{article}
|
||||
\usepackage{fetamont}
|
||||
\usepackage{listings}
|
||||
\usepackage{protobuf/lang} % include language definition for protobuf
|
||||
\usepackage{protobuf/style} % include custom style for proto declarations.
|
||||
\begin{document}
|
||||
\title{
|
||||
\textffm{Meow} messaging protocol}
|
||||
\textffm{Meow} messaging protocol description}
|
||||
\author{Author
|
||||
\texttt{meow@redroom.link}}
|
||||
\date{\today}
|
||||
@ -25,155 +28,13 @@ Tout ce que je sais, c'est que nous vivons dans un monde dont on ne s'évade pas
|
||||
\end{quote}
|
||||
\end{abstract}
|
||||
|
||||
\section{Identity creation}
|
||||
|
||||
\section{Services}
|
||||
\subsection{Unregulated identities}
|
||||
The only requirement to get a valid \textffm{Meow} identity is to generate a user key pair.
|
||||
No phone number or email check will be performed, unlike main instant messaging protocols, there is no central administration.
|
||||
\section{Messages structure}
|
||||
\lstinputlisting[language=protobuf2,style=protobuf]{../pb/messages.proto}
|
||||
|
||||
\subsection{Fine grained privacy control}
|
||||
\section{Invitation process}
|
||||
|
||||
\subsubsection{Trustable server based communication}
|
||||
Like most widely available messaging softwares, (Whatsapp, Signal, Viber, Telegram...), \textffm{Meow} provides a simple server based messaging.
|
||||
The main difference is that \textffm{Meow} allows you to explicitly choose which server you want to use.
|
||||
The server code being open source, we strongly encourage you to run your own server at home or in your company.
|
||||
The server requires very few ressources and will run on any low cost single board computer.
|
||||
|
||||
\subsubsection{Anonymized message transfer}
|
||||
\textffm{Meow} also provides an anonymizing transfer service very similar to the Tor Onion protocol, we call it the Matriochka protocol.
|
||||
Any server can be used for building the transfer chain.
|
||||
Some of them might be marked as trusted.
|
||||
Random delays might be set for each forwarding step, making the overall message tracking much more difficult, even with a global network audit.
|
||||
It is strongly advised to use trusted servers as your first node and message server (the one that holds your incoming messages).
|
||||
|
||||
\subsubsection{Presence protocol for direct messaging}
|
||||
A presence service associating your conversation keys to your IP address for direct peer to peer connection is also provided.
|
||||
The presence protocol is simply activated by setting a flag in the message poll requests.
|
||||
If that flag is set, your encrypted IP will be published on the server, allowing your only your peer(s) to decrypt it and directly communicate with your terminal.
|
||||
|
||||
\subsubsection{Peer based privacy settings}
|
||||
You might define specific communication privacy preferences for each of your contacts:
|
||||
\begin{itemize}
|
||||
\item simple server based communication allowed for Joe,
|
||||
\item preferred direct communication with Julian, fallback to my own server,
|
||||
\item required matriochka protocol for Edward, first node is one of my trusted servers, my message node is my own server, randomly switch from trusted server lists for others.
|
||||
\item ...
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Resistance to device requisition}
|
||||
All your contact information and discussion are encrypted on the device and password protected.
|
||||
Password shall be asked on application startup and allows your identity file and contact decrytion.
|
||||
That password is not recoverable, so you can't forget it, or you'll loose your whole configuration and identity.
|
||||
Real security implies some constraints.
|
||||
You might configure the app to save your password, but that is a security flaw.
|
||||
In many authoritarian countries, you are required by law to provide your device passwords to authorities.
|
||||
In a \textffm{Meow} device, you might set a special password for specific contacts.
|
||||
Those contacts won't be visible when entering your main identity password.
|
||||
You'll have to type their specific password in order to make them visible.
|
||||
The \textffm{Meow} application will by default create a random set of fake hidden contacts and conversations.
|
||||
Even in case of device storage analysis, authorities won't be able to differentiate a real hidden contact from an normal fake generated one.
|
||||
It could be argued that this feature puts every user at risk, because authorities might think you're hiding something, even if you're not.
|
||||
As every \textffm{Meow} user has the same constraint, users are not responsible for that. Moreover solidarity is also a requirement for real security.
|
||||
|
||||
\subsection{Multiple devices support}
|
||||
\textffm{Meow} allows you to be connected from multiple devices and offers chat synchronization capability.
|
||||
A device might be revoqued anytime from any other one. Proof of your identity (password or other) shall be provided in order to grant device revocation.
|
||||
|
||||
\subsection{Adding contacts}
|
||||
If you want to add a new contact, keys and uuids will be generated, then a rendez-vous card will be created.
|
||||
That rendez-vous card might be sent by any trustable communication means, or preferably from hand to hand, as a file on a flash disk or a QR code.\\
|
||||
In return your contact will provide the exact same data, encrypted with your public key and delivered to the address specified in the initial rendez-vous card.
|
||||
|
||||
\subsection{Contacts forwarding}
|
||||
By using the \textffm{Meow} protocol a user won't be able to forward your contact information without your consent.
|
||||
Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless. Any message to that identity signed by another user than you would be discarded.
|
||||
|
||||
|
||||
\subsection{Group conversation}
|
||||
A very basic group messaging service is available. It allows to exchange group information between users. After that, a message to a group will send a copy of the message to each member.
|
||||
|
||||
|
||||
\subsection{Emergency broadcast}
|
||||
A local (server based) emergency broadcast service will be provided. It will provide the ability to send/receive broadcast messages to all users connected to the current server.
|
||||
|
||||
|
||||
\subsection{Public networks shortage resilience}
|
||||
\textffm{Meow} may run without Internet connection, either on an isolated wifi access point, or on a meshed network of wifi routers or even via serial IOT transport layers (LoRa,...)
|
||||
|
||||
|
||||
\subsection{User directory service}
|
||||
This service allows to restore a lost functionality of Internet historic chat services (like ICQ). You could simply set a "Free for chat" status that would allow other people to contact you, either randomly or based on a short description that you might provide.
|
||||
Why providing that service while the internet is suffocating due to the abundance of social networks ?\\
|
||||
Well, that option offers a few advantages :
|
||||
\begin{itemize}
|
||||
\item you are still an anonymous user chatting with other anonymous users;
|
||||
\item no social network algorithm will select people that think/behave/vote/eat... just like you. Diversity makes a better world;
|
||||
\item a smaller community of users, skilled enough to operate a \textffm{Meow} chat app... that might provide a first filter;
|
||||
It's a bit like in the old times, when people had to be able to start a win98 computer, connect it to internet, then download and install ICQ...
|
||||
If you lost some time in social networks, and experienced ICQ in the 2000's, you'll understand.
|
||||
\end{itemize}
|
||||
|
||||
\section{Identities and keys}
|
||||
|
||||
\subsection{User identity}
|
||||
Each \textffm{Meow} user has a unique identity. That identity is strictly private, only used to manage your own data (local encryption, devices, ...)
|
||||
Let's call that one the User Key Pair (Ukp)
|
||||
|
||||
\subsection{Contact identity}
|
||||
Each of your contacts will know you under a different identity, we'll call that one the Contact Key Pair (Ckp)
|
||||
That contact Key Pair will not change once it's agreed between both peers: an initial key will be exchanged as part of the peer invitation process.
|
||||
As other people might have seen your key, this means that :
|
||||
\begin{itemize}
|
||||
\item none of your contacts will be able to forward your id to another person without your consent;
|
||||
\item any message to that Ckp, not signed by its associated user, will be discarded.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Conversation encryption}
|
||||
Each conversation with one of your contacts will be encrypted using an encryption keypair (Ekp) allowing cyphering your conversation.
|
||||
The Ekp might be changed anytime by its owner and the new public key will be sent along the last message.
|
||||
|
||||
\subsection{Conversation lookup}
|
||||
A contact conversation Lookup Key Pair(Lkp) is also associated with your conversation. The Lkp public key is used to identify your conversation on a server.
|
||||
The private key allows you to sign your request and prove the server that you are the legitimate recipient for a message.
|
||||
This Lkp can be changed anytime by its owner and the new public key will be sent along the last message.
|
||||
The Lkp and the Ekp are only changed once the change has beeen acknowledged by your contact.
|
||||
|
||||
\subsection{Server identity}
|
||||
Each server has a Server key (Skp). That key allows you to cypher the messages that you're sending to the server.
|
||||
|
||||
\subsection{Device identity}
|
||||
Each device is identified by a device key (Dkp) that allows you to perform secured exchanges between your devices for synchronization/revocation purposes.
|
||||
Communication between devices is achieved using the same principle as the user to user communication. A device might be considered as any another user. The messages content is based on a synchronization protocol.
|
||||
|
||||
\section{Contact management}
|
||||
\subsection{Adding a contact}
|
||||
Rendez-vous card, containing :
|
||||
\begin{itemize}
|
||||
\item Your public key for that contact;
|
||||
\item An initial conversation public key for getting encrypted messages from that contact;
|
||||
\item An initial conversation uuid that you'll use to lookup for incoming messages on the servers;
|
||||
\item A list of your preferred message servers;
|
||||
\item A signature to prevent transmission of tampered data.
|
||||
\end{itemize}
|
||||
\subsection{Sharing a contact}
|
||||
If a user wants to forward one of his contacts to you, it will be handled as a double request:
|
||||
\begin{enumerate}
|
||||
\item I'm receiving a contact name, without any key
|
||||
\item
|
||||
\end{enumerate}
|
||||
|
||||
\section{Messaging}
|
||||
\subsection{User messages}
|
||||
TODO
|
||||
|
||||
\subsection{Server stored message}
|
||||
TODO
|
||||
|
||||
\subsection{Matriochka message packing}
|
||||
TODO
|
||||
|
||||
\subsection{Synchronization messages}
|
||||
TODO
|
||||
|
||||
\section{Transport protocols}
|
||||
\subsection{URLs}
|
||||
|
24
doc/server/server_messaging.puml
Normal file
24
doc/server/server_messaging.puml
Normal file
@ -0,0 +1,24 @@
|
||||
@startuml
|
||||
|
||||
|
||||
actor Sender
|
||||
actor Receiver
|
||||
component Server
|
||||
component Router
|
||||
queue msgch
|
||||
queue dvych
|
||||
|
||||
collections msg
|
||||
collections dvyrq
|
||||
collections dvy
|
||||
|
||||
UserSender -> Server : mesg
|
||||
Server -> Router : mesg
|
||||
Router -> msg : store
|
||||
Router -> dvyrq : store
|
||||
Router -> msgch : publish
|
||||
msgch -> Receiver : notifiaction
|
||||
msg -> Receiver : mesg
|
||||
|
||||
|
||||
@enduml
|
20
doc/server/sq_01_srvmessaging.puml
Normal file
20
doc/server/sq_01_srvmessaging.puml
Normal file
@ -0,0 +1,20 @@
|
||||
@startuml "Simple messaging"
|
||||
actor Sender as snd
|
||||
actor Receiver as rcv
|
||||
control Server as srv
|
||||
collections msg as msg
|
||||
queue msgch as msgch
|
||||
collections dvyrq as dvyrq
|
||||
collections dvy as dvy
|
||||
queue dvych as dvych
|
||||
|
||||
rcv->srv: Listen
|
||||
srv->msgch: Subscribe
|
||||
snd->srv: Send message
|
||||
srv->msg: Store message
|
||||
srv->msgch: Notify listening receivers
|
||||
msgch->srv: Notify
|
||||
msg->srv: Grab message
|
||||
srv->rcv: Send message
|
||||
|
||||
@enduml
|
30
doc/server/sq_02_srvmessaging.puml
Normal file
30
doc/server/sq_02_srvmessaging.puml
Normal file
@ -0,0 +1,30 @@
|
||||
@startuml "Messaging with server delivery"
|
||||
actor Sender as snd
|
||||
actor Receiver as rcv
|
||||
control Server as srv
|
||||
collections msg as msg
|
||||
queue msgch as msgch
|
||||
collections dvyrq as dvyrq
|
||||
collections dvy as dvy
|
||||
queue dvych as dvych
|
||||
|
||||
|
||||
rcv->srv: Listen for messages
|
||||
srv->msgch: Subscribe
|
||||
snd->srv: Send message with delivery uid
|
||||
snd->msg: Listen for delivery
|
||||
|
||||
group storeMessage
|
||||
srv->msg: Store message
|
||||
srv->dvyrq: Store delivery request uid=>Receiver
|
||||
srv->msgch: Notify listening receivers
|
||||
end
|
||||
msgch->srv: Notify
|
||||
msg->srv: Grab message
|
||||
srv->rcv: Cuts listening with message
|
||||
srv->dvy: Store delivery done Receiver=>uid
|
||||
srv->dvych: Notify listening Sender
|
||||
dvych->srv: Notify
|
||||
dvy->srv: Grab delivery
|
||||
srv->snd: Cuts listening with delivery
|
||||
@enduml
|
@ -1,8 +0,0 @@
|
||||
@startuml
|
||||
Client1 -> Server: Send Message (PubKeyClient2 + Payload)
|
||||
Server --> Client1: Ack Message (Server UUID + DateReceived)
|
||||
Client2 -> Server: Get Messages (PubKeyClient2 + IpPublish)
|
||||
Server --> Client2: Available Messages list (UUID list cyphered with PubKeyClient2)
|
||||
Client2 <- Server: Get Messages (decoded UUID signed with PK)
|
||||
Server --> Client2: Messages [](PubKeyClient2 + Payload)
|
||||
@enduml
|
@ -22,28 +22,34 @@ func TestEndToEnd(t *testing.T) {
|
||||
///////////////////////////
|
||||
// Creating New Identity //
|
||||
///////////////////////////
|
||||
Me = client.CreateIdentity("myname")
|
||||
Me, err = client.CreateIdentity("myname")
|
||||
|
||||
// define my preferences (servers)
|
||||
Me.MessageServers.Name = "Message Servers"
|
||||
Me.MessageServers.AddUrls([]string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1", "meow://127.0.0.1"})
|
||||
srv := client.Server{Name: "MyServer", Url: "http://127.0.0.1/meow/"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
srv = client.Server{Name: "MyServer", Url: "mqtt://127.0.0.1"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
srv = client.Server{Name: "MyServer", Url: "meow://127.0.0.1"}
|
||||
Me.MessageServers.StoreServer(&srv)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Create an invitation for a friend, I want him/her to know me as Bender //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
fmt.Println("Creating an invitation for the first friend...")
|
||||
invitation, err := Me.InvitePeer("Bender", "myfirstfriend", []int{1, 2})
|
||||
peer, err := Me.InvitePeer("Bender", "myfirstfriend", []string{"http://127.0.0.1/meow/", "mqtt://127.0.0.1"}, "welcome, it's me!")
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
println(peer.Name)
|
||||
// print my invitation
|
||||
a, _ := json.Marshal(invitation)
|
||||
a, _ := json.Marshal(peer.GetMyContact())
|
||||
fmt.Println(string(a))
|
||||
// TODO : Convert invitation to QR Code
|
||||
invitation.WritePng("invitation.png")
|
||||
data, err := invitation.Compress()
|
||||
peer.GetMyContact().WritePng("invitation.png")
|
||||
data, err := peer.GetMyContact().Compress()
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
invitation.WriteQr("qrcode.png")
|
||||
peer.GetMyContact().WriteQr("qrcode.png")
|
||||
println("Compressed contact card :", len(data))
|
||||
///////////////////////////////////////
|
||||
// Simulate peer invitation response //
|
||||
@ -59,9 +65,9 @@ func TestEndToEnd(t *testing.T) {
|
||||
ReceivedContact.ContactPublicKey = FirstFriendContactKp.Public
|
||||
ReceivedContact.EncryptionPublicKey = FirstFriendEncryptionKp.Public
|
||||
ReceivedContact.LookupPublicKey = FirstFriendLookupKp.Public
|
||||
ReceivedContact.InvitationId = invitation.InvitationId
|
||||
ReceivedContact.InvitationId = peer.GetMyContact().InvitationId
|
||||
FriendServer1KP := meowlib.NewKeyPair()
|
||||
FriendServer1 := meowlib.Server{Name: "FriendServer1", Url: "http://myfriend.org/meow/", PublicKey: FriendServer1KP.Public, Description: "Fancy description", ConfidenceLevel: 1}
|
||||
FriendServer1 := meowlib.ServerCard{Name: "FriendServer1", Url: "http://myfriend.org/meow/", PublicKey: FriendServer1KP.Public, Description: "Fancy description"}
|
||||
ReceivedContact.PullServers = append(ReceivedContact.PullServers, &FriendServer1)
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
@ -79,7 +85,8 @@ func TestEndToEnd(t *testing.T) {
|
||||
/////////////////////////////////////
|
||||
// Create a message to that friend //
|
||||
/////////////////////////////////////
|
||||
MyFirstFriend := Me.Peers[0]
|
||||
peers, _ := Me.Peers.GetPeers()
|
||||
MyFirstFriend := peers[0]
|
||||
textmessage := "Hello friend!"
|
||||
// Creating User message
|
||||
usermessage, err := MyFirstFriend.BuildSimpleUserMessage([]byte(textmessage))
|
||||
@ -99,9 +106,10 @@ func TestEndToEnd(t *testing.T) {
|
||||
// Packing it
|
||||
packedMsg := MyFirstFriend.PackUserMessage(enc.Data, enc.Signature)
|
||||
|
||||
srv := MyFirstFriend.Contact.PullServers[0]
|
||||
intS1 := client.InternalServerFromServer(srv)
|
||||
|
||||
intS1, err := Me.MessageServers.LoadServer("http://127.0.0.1/meow/")
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
// Creating Server message for transporting the user message
|
||||
toServerMessage, err := intS1.BuildMessageSendingMessage(packedMsg)
|
||||
if err != nil {
|
||||
@ -129,9 +137,9 @@ func TestEndToEnd(t *testing.T) {
|
||||
// Simulating server side processing //
|
||||
///////////////////////////////////////
|
||||
var server1 server.Identity
|
||||
server1.ServerName = intS1.ServerData.Name
|
||||
server1.ServerName = intS1.Name
|
||||
server1.ServerKp = FriendServer1KP
|
||||
server1.ServerDesc = intS1.ServerData.Description
|
||||
server1.ServerDesc = intS1.Description
|
||||
// Unpack
|
||||
srv_from, srv_encmsg, srv_signature, err := server1.UnpackReceived(protoPackedServerMsg)
|
||||
if err != nil {
|
||||
@ -178,11 +186,11 @@ func TestEndToEnd(t *testing.T) {
|
||||
// user unpack
|
||||
|
||||
// user decrypt
|
||||
decMess, err2 := MyFirstFriend.AsymDecryptMessage([]byte(enc.Data), enc.Signature)
|
||||
/*decMess, err2 := MyFirstFriend.AsymDecryptMessage([]byte(enc.Data), enc.Signature)
|
||||
if err2 != nil {
|
||||
fmt.Println(err2.Error())
|
||||
}
|
||||
fmt.Println(decMess)
|
||||
fmt.Println(decMess)*/
|
||||
// user decode protobuf
|
||||
|
||||
}
|
||||
|
51
go.mod
51
go.mod
@ -1,18 +1,49 @@
|
||||
module forge.redroom.link/yves/meowlib
|
||||
|
||||
go 1.16
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.2.4
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.8.3
|
||||
github.com/dgraph-io/badger v1.6.2
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/livekit/protocol v1.16.0
|
||||
github.com/makiuchi-d/gozxing v0.1.1
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.22.1 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.16
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/rs/zerolog v1.25.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 // indirect
|
||||
golang.org/x/net v0.3.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.2.0 // indirect
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgraph-io/ristretto v0.0.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.30.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
golang.org/x/net v0.23.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c // indirect
|
||||
google.golang.org/grpc v1.62.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
355
go.sum
355
go.sum
@ -1,25 +1,76 @@
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210920160938-87db9fbc61c7 h1:DSqTh6nEes/uO8BlNcGk8PzZsxY2sN9ZL//veWBdTRI=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210920160938-87db9fbc61c7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a h1:W6RrgN/sTxg1msqzFFb+G80MFmpjMw61IU+slm+wln4=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20190923161245-9b5a4261663a/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.2.4 h1:PEke+LAMLH9CplflEl8WqGyz2IiDoiiipKkB+3cEWFQ=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.2.4/go.mod h1:ygdaHbrbWFPhKjmXii0zOs3/xlSR/01GaVePKqv19Hc=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
|
||||
github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
|
||||
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5 h1:STOY3vgES59gNgoOt2w0nyHBjKViB/qSg7NjbQWPJkA=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.7.5/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.8.3 h1:1jHlELwCR00qovx2B50DkL/FjYwt/P91RnlsqeOp2Hs=
|
||||
github.com/ProtonMail/gopenpgp/v2 v2.8.3/go.mod h1:LiuOTbnJit8w9ZzOoLscj0kmdALY7hfoCVh5Qlb0bcg=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
|
||||
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
||||
github.com/dgraph-io/ristretto v0.0.2 h1:a5WaUrDa0qm0YrAAS1tUykT5El3kt62KNZZeMxQn3po=
|
||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/eapache/channels v1.1.0 h1:F1taHcn7/F0i8DYqKXJnyhJcVpp2kgFcNePxXtnyu4k=
|
||||
github.com/eapache/channels v1.1.0/go.mod h1:jMm2qB5Ubtg9zLd+inMZd2/NUvXgzmWXsDaLyQIGfH0=
|
||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/frostbyte73/core v0.0.10 h1:D4DQXdPb8ICayz0n75rs4UYTXrUSdxzUfeleuNJORsU=
|
||||
github.com/frostbyte73/core v0.0.10/go.mod h1:XsOGqrqe/VEV7+8vJ+3a8qnCIXNbKsoEiu/czs7nrcU=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
|
||||
github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
@ -27,152 +78,266 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw=
|
||||
github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0 h1:QRbbVkfgNippHOS8PXDkti4NaWeyYfcBTHtw7k08o4c=
|
||||
github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
|
||||
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58=
|
||||
github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ=
|
||||
github.com/livekit/protocol v1.16.0 h1:TkUuirvfF1xIfpo5szXqAEEgg7QyML8d0O7+4NQpM7w=
|
||||
github.com/livekit/protocol v1.16.0/go.mod h1:pnn0Dv+/0K0OFqKHX6J6SreYO1dZxl6tDuAZ1ns8L/w=
|
||||
github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4 h1:253WtQ2VGVHzIIzW9MUZj7vUDDILESU3zsEbiRdxYF0=
|
||||
github.com/livekit/psrpc v0.5.3-0.20240228172457-3724cb4adbc4/go.mod h1:CQUBSPfYYAaevg1TNCc6/aYsa8DJH4jSRFdCeSZk5u0=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
|
||||
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/nats-io/nats.go v1.31.0 h1:/WFBHEc/dOKBF6qf1TZhrdEfTmOZ5JzdJ+Y3m6Y/p7E=
|
||||
github.com/nats-io/nats.go v1.31.0/go.mod h1:di3Bm5MLsoB4Bx61CBTsxuarI36WbhAwOm8QrW39+i8=
|
||||
github.com/nats-io/nkeys v0.4.6 h1:IzVe95ru2CT6ta874rt9saQRkWfe2nFj1NtvYSLqMzY=
|
||||
github.com/nats-io/nkeys v0.4.6/go.mod h1:4DxZNzenSVd1cYQoAa8948QY3QDjrHfcfVADymtkpts=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
|
||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/onsi/gomega v1.22.1 h1:pY8O4lBfsHKZHM/6nrxkhVPUznOlIu3quZcKP/M20KI=
|
||||
github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
|
||||
github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
|
||||
github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
|
||||
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
|
||||
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
github.com/pion/ice/v2 v2.3.13 h1:xOxP+4V9nSDlUaGFRf/LvAuGHDXRcjIdsbbXPK/w7c8=
|
||||
github.com/pion/ice/v2 v2.3.13/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw=
|
||||
github.com/pion/interceptor v0.1.25 h1:pwY9r7P6ToQ3+IF0bajN0xmk/fNw/suTgaTdlwTDmhc=
|
||||
github.com/pion/interceptor v0.1.25/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxCxPImLOg3Y=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
|
||||
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
|
||||
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
|
||||
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
|
||||
github.com/pion/rtcp v1.2.12 h1:bKWiX93XKgDZENEXCijvHRU/wRifm6JV5DGcH6twtSM=
|
||||
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
|
||||
github.com/pion/rtp v1.8.3 h1:VEHxqzSVQxCkKDSHro5/4IUUG1ea+MFdqR2R3xSpNU8=
|
||||
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
|
||||
github.com/pion/sctp v1.8.12 h1:2VX50pedElH+is6FI+OKyRTeN5oy4mrk2HjnGa3UCmY=
|
||||
github.com/pion/sctp v1.8.12/go.mod h1:cMLT45jqw3+jiJCrtHVwfQLnfR0MGZ4rgOJwUOIqLkI=
|
||||
github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw=
|
||||
github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw=
|
||||
github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo=
|
||||
github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
|
||||
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
|
||||
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
|
||||
github.com/pion/transport/v2 v2.2.3 h1:XcOE3/x41HOSKbl1BfyY1TF1dERx7lVvlMCbXU7kfvA=
|
||||
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
|
||||
github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA=
|
||||
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
|
||||
github.com/pion/webrtc/v3 v3.2.28 h1:ienStxZ6HcjtH2UlmnFpMM0loENiYjaX437uIUpQSKo=
|
||||
github.com/pion/webrtc/v3 v3.2.28/go.mod h1:PNRCEuQlibrmuBhOTnol9j6KkIbUG11aHLEfNpUYey0=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II=
|
||||
github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8=
|
||||
github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJXP61mNV3/7iuU=
|
||||
github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.2.0 h1:FtGenNNeCATRB3CmB/yEUnjEFeJWpB/pMcy7e2bKPYs=
|
||||
go.uber.org/zap/exp v0.2.0/go.mod h1:t0gqAIdh1MfKv9EwN/dLwfZnJxe9ITAZN78HEWPFWDQ=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099 h1:aIu0lKmfdgtn2uTj7JI2oN4TUrQvgB+wzTPO23bCKt8=
|
||||
golang.org/x/mobile v0.0.0-20221110043201-43a038452099/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk=
|
||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
|
||||
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c h1:NUsgEN92SQQqzfA+YtqYNqYmB3DMMYLlIwUZAQFVFbo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240221002015-b0ce06bbee7c/go.mod h1:H4O17MA/PE9BsGx3w+a+W2VOLLD1Qf7oJneAoU6WktY=
|
||||
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
|
||||
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -181,18 +346,20 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
44
http.go
Normal file
44
http.go
Normal file
@ -0,0 +1,44 @@
|
||||
package meowlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HttpGetId(url string) (response map[string]string, err error) {
|
||||
srvId := make(map[string]string)
|
||||
resp, err := http.Get(url + "/id")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = json.Unmarshal(body, &srvId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return srvId, nil
|
||||
}
|
||||
|
||||
func HttpPostMessage(url string, msg []byte, timeout int) (response []byte, err error) {
|
||||
client := http.Client{
|
||||
Timeout: time.Duration(timeout) * time.Second,
|
||||
}
|
||||
resp, err := client.Post(url+"/msg",
|
||||
"application/octet-stream", bytes.NewBuffer(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return body, nil
|
||||
}
|
97
lokiwriter.go
Normal file
97
lokiwriter.go
Normal file
@ -0,0 +1,97 @@
|
||||
package meowlib
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LokiWriter struct {
|
||||
url string
|
||||
labels map[string]string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
type LokiPayload struct {
|
||||
Streams []LokiStream `json:"streams"`
|
||||
}
|
||||
|
||||
type LokiStream struct {
|
||||
Stream map[string]string `json:"stream"`
|
||||
Values [][]string `json:"values"`
|
||||
}
|
||||
|
||||
func NewLokiWriter(url string, labels map[string]string) *LokiWriter {
|
||||
return &LokiWriter{
|
||||
url: url,
|
||||
labels: labels,
|
||||
httpClient: &http.Client{},
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LokiWriter) Write(p []byte) (n int, err error) {
|
||||
// Use zerolog to parse the log level
|
||||
var event map[string]interface{}
|
||||
if err := json.Unmarshal(p, &event); err != nil {
|
||||
return 0, fmt.Errorf("failed to unmarshal log event: %w", err)
|
||||
}
|
||||
|
||||
level := ""
|
||||
if l, ok := event["level"].(string); ok {
|
||||
level = l
|
||||
}
|
||||
|
||||
message := ""
|
||||
if m, ok := event["message"].(string); ok {
|
||||
message = m
|
||||
}
|
||||
|
||||
// Add log level to labels
|
||||
labels := make(map[string]string)
|
||||
for k, v := range w.labels {
|
||||
labels[k] = v
|
||||
}
|
||||
labels["level"] = level
|
||||
|
||||
// Format the timestamp in nanoseconds
|
||||
timestamp := fmt.Sprintf("%d000000", time.Now().UnixNano()/int64(time.Millisecond))
|
||||
|
||||
stream := LokiStream{
|
||||
Stream: labels,
|
||||
Values: [][]string{
|
||||
{timestamp, message},
|
||||
},
|
||||
}
|
||||
|
||||
payload := LokiPayload{
|
||||
Streams: []LokiStream{stream},
|
||||
}
|
||||
|
||||
payloadBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to marshal payload: %w", err)
|
||||
}
|
||||
|
||||
//fmt.Printf("Sending payload to Loki: %s\n", string(payloadBytes))
|
||||
|
||||
req, err := http.NewRequest("POST", w.url, bytes.NewReader(payloadBytes))
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to create HTTP request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := w.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to send log to Loki: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
//fmt.Printf("Loki response status: %d\n", resp.StatusCode)
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
return 0, fmt.Errorf("received non-204 response from Loki: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
@ -3,6 +3,7 @@ package meowlib
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@ -13,7 +14,7 @@ func (msg *UserMessage) AddFile(filename string, maxMessageSize int64) error {
|
||||
return err
|
||||
}
|
||||
if fi.Size() > maxMessageSize {
|
||||
return errors.New("cannot add file, file bigger than messge size")
|
||||
return errors.New("cannot add file, file bigger than message size")
|
||||
}
|
||||
var totalsize int64
|
||||
totalsize = 0
|
||||
@ -30,13 +31,13 @@ func (msg *UserMessage) AddFile(filename string, maxMessageSize int64) error {
|
||||
}
|
||||
|
||||
var file File
|
||||
file.Filename = filename
|
||||
file.Filename = filepath.Base(filename)
|
||||
file.Size = uint64(fi.Size())
|
||||
file.Data = data
|
||||
msg.Files = append(msg.Files, &file)
|
||||
|
||||
msg.Status = &UserMessage_ConversationStatus{}
|
||||
msg.Status.LocalUuid = uuid.New().String()
|
||||
msg.Status = &ConversationStatus{}
|
||||
msg.Status.Uuid = uuid.New().String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
1836
messages.pb.go
1836
messages.pb.go
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,14 @@
|
||||
// You should use the field numbers 1 through 15
|
||||
// for the most-frequently-set fields.
|
||||
// Lower field number values take less space in the wire format.
|
||||
// For example, field numbers in the range 1 through 15 take one byte to encode.
|
||||
// Field numbers in the range 16 through 2047 take two bytes.
|
||||
/**
|
||||
* Meow messages
|
||||
*
|
||||
* This is the Meow protocol protobuf messages description.
|
||||
*
|
||||
*/
|
||||
syntax = "proto3";
|
||||
package meowlib;
|
||||
option go_package = "forge.redroom.link/yves/meowlib";
|
||||
@ -9,48 +20,87 @@ message PackedServerMessage {
|
||||
bytes signature = 3; // The message signature with the client public key |eo| the reference to teh symetrical key used
|
||||
}
|
||||
|
||||
// structure to hold an invitation through a server
|
||||
message Invitation {
|
||||
bytes payload = 1; // invitation payload, encrypted after step 2
|
||||
int32 timeout = 2; // how long do I want the invitation to remain available on the server
|
||||
int32 shortcodeLen = 3; // len of the shortcode you wish for short url transmission
|
||||
string shortcode = 4; // shortcode that the friend shall request to get the invitation
|
||||
string password = 5; // password to set for accessing invitation (optional)
|
||||
string uuid = 6; // id that the friend gave you, that you should include to your reply to get recognized
|
||||
int64 expiry = 7; // the server allowed expiry date, it may be samller than the requested timeout according to server policy
|
||||
int32 step = 8; // progress in the inviattion process : 1=invite friend, 2=friend requests invitation, 3=friend's answer
|
||||
string from=9; // used in step 3 the answer public key to check the signature in user message
|
||||
}
|
||||
|
||||
// structure for requesting incoming messages
|
||||
message ConversationRequest {
|
||||
string lookup_key = 1; // lookup key for a conversation
|
||||
bool delivery_request = 2; // look for for delivery tracking, key is implicit, "from" field is used
|
||||
int64 send_timestamp = 3;
|
||||
string lookup_signature = 4; // prove that I own the private key by signing that block
|
||||
}
|
||||
|
||||
message Meet {
|
||||
string public_status = 1; // Publish my online status, if the server is a meeting server
|
||||
ContactCard contact_card = 2; // mine or the requester
|
||||
string message = 3; // short description
|
||||
}
|
||||
|
||||
message Credentials {
|
||||
string login = 1; // login
|
||||
string password = 2; // password
|
||||
string public_key = 3; // public key
|
||||
string private_key = 4; // private key
|
||||
}
|
||||
|
||||
// structure defining a message for a server, that will be encrypted, then sent in a "packedmessage" payload
|
||||
message ToServerMessage {
|
||||
string type = 1; // Type 1 : final destination / 2 : forward
|
||||
string from = 2 ; // My pub key for the server to send me an encrypter answer
|
||||
bytes payload = 3 ; // optional payload for server
|
||||
|
||||
// structure for requesting incoming messages
|
||||
message ConversationRequest {
|
||||
string lookupKey = 1; // lookup key for a conversation
|
||||
string lastServerUuidOK = 2; // Last Server message UUID received (send me all after that one)
|
||||
bool publishOnline = 3; // ?? Publish my online status for that contact ?
|
||||
string lookupSignature = 4; // prove that I own the private key by signing that block
|
||||
}
|
||||
|
||||
repeated ConversationRequest pullRequest = 4;
|
||||
repeated ConversationRequest pull_request = 4;
|
||||
|
||||
repeated PackedUserMessage messages = 5;
|
||||
|
||||
repeated Server knownServers = 6;
|
||||
repeated ServerCard known_servers = 6;
|
||||
|
||||
Matriochka matriochkaMessage = 7;
|
||||
Matriochka matriochka_message = 7;
|
||||
|
||||
string uuid = 8;
|
||||
|
||||
Invitation invitation = 9; // invitation for the 2 first steps of a "through server" invitation process
|
||||
|
||||
repeated PackedUserMessage device_messages = 10; // messages to another device belonging to the same user
|
||||
|
||||
int64 timeout = 11; // timeout expected by the client for the server to answer (long polling)
|
||||
|
||||
VideoData video_data = 12; // video call data
|
||||
|
||||
Credentials credentials = 13; // credentials for a new user or mandatory server creds
|
||||
}
|
||||
|
||||
|
||||
// structure defining a from server receiver message decrypted from a "packedmessage" payload
|
||||
message FromServerMessage {
|
||||
string type = 1; // Type
|
||||
string serverPublicKey = 2 ; // Pub key from the server
|
||||
string server_public_key = 2 ; // Pub key from the server
|
||||
bytes payload = 3 ; //
|
||||
string uuidAck = 4 ; // Ack for the last received ToServerMessage Uuid
|
||||
string serverUuid = 5 ; // Provides the server uuid that replaced the client uuid
|
||||
|
||||
message ConversationResponse {
|
||||
repeated string messageUuids = 1;
|
||||
}
|
||||
string uuid_ack = 4 ; // Ack for the last received ToServerMessage Uuid
|
||||
string server_uuid = 5 ; // Provides the server uuid that replaced the client uuid
|
||||
|
||||
repeated PackedUserMessage chat = 6;
|
||||
|
||||
repeated Server knownServers = 7;
|
||||
repeated ServerCard known_servers = 7;
|
||||
|
||||
Invitation invitation = 8; // invitation answer, for the third steps of any invitation
|
||||
|
||||
repeated PackedUserMessage device_messages = 9; // messages from other devices belonging to the same user
|
||||
|
||||
VideoData video_data = 10; // video call data
|
||||
|
||||
repeated ContactCard contact_card = 11; // contact list for a personae
|
||||
}
|
||||
|
||||
message MatriochkaServer {
|
||||
@ -68,25 +118,26 @@ message Matriochka {
|
||||
}
|
||||
|
||||
// structure describing required server attributes
|
||||
message Server {
|
||||
string name = 1;
|
||||
string description=2;
|
||||
string publicKey = 3;
|
||||
string url = 4;
|
||||
bool publish = 5; // publish this server when asked for a list by server
|
||||
bytes signature = 6; // signature of all previous fields by the server itself
|
||||
int32 confidenceLevel = 7; // additional info from the user
|
||||
}
|
||||
message ServerCard {
|
||||
string name = 1; // friendly server name
|
||||
string description=2; // description : owner type (company/private/university...),
|
||||
string public_key = 3; // public key you must use to send encrypted messages to that server
|
||||
string url = 4; // meow server url
|
||||
string login = 5; // required login to access the server
|
||||
string password = 6; // password associated to the login
|
||||
string signature = 7; // signature of all previous fields by the server itself
|
||||
}
|
||||
|
||||
// structure describing a user contact card ie the minimum set of attributes for exchanging identities
|
||||
message ContactCard {
|
||||
string name=1;
|
||||
string contactPublicKey =2;
|
||||
string encryptionPublicKey= 3;
|
||||
string lookupPublicKey =4;
|
||||
repeated Server pullServers =5;
|
||||
string name=1; // contact nickname
|
||||
string contact_public_key =2; // contact public key, will be used to authenticate her/his messages
|
||||
string encryption_public_key= 3; // public key you must use to to write encrypted messages to that contact
|
||||
string lookup_public_key =4; // public key you will use as "destination identifier" for her/him to lookup for your messages on the servers
|
||||
repeated ServerCard pull_servers =5; // list the servers where the contact will look for messages from you
|
||||
uint32 version = 6;
|
||||
string invitationId=7;
|
||||
string invitation_id=7;
|
||||
string invitation_message=8;
|
||||
}
|
||||
|
||||
// structure for sending a message to be forwarded to another user in protobuf format
|
||||
@ -95,6 +146,23 @@ message PackedUserMessage {
|
||||
bytes payload=2; // the message UserMessage encrypted with the destination peer's public key
|
||||
bytes signature=3; // the payload signature with the client identity private key
|
||||
repeated int64 serverTimestamp=4; // server time stamp, might be several in matriochka mode
|
||||
string server_delivery_uuid=5; // message uuid, for server delivery tracking, omitted if not delivery tracking desired
|
||||
}
|
||||
|
||||
message ConversationStatus {
|
||||
string uuid = 1;
|
||||
string answer_to_uuid=2; // message is an answer to another one, specify uuid here
|
||||
uint64 localSequence = 3 ; // seq number in local conversation for custom reordering
|
||||
uint64 sent = 4 ; // timestamp of the message sent
|
||||
uint64 received = 5; // timestamp of the message received
|
||||
uint64 processed = 6; // timestamp of the message processed
|
||||
ContactCard my_next_identity = 7;
|
||||
int32 peer_next_identityAck = 8; // version of the new peer accepted id
|
||||
}
|
||||
|
||||
message Group{
|
||||
string name=1;
|
||||
repeated ContactCard members = 2;
|
||||
}
|
||||
|
||||
|
||||
@ -102,35 +170,65 @@ message PackedUserMessage {
|
||||
message UserMessage {
|
||||
string destination = 1; // Lookupkey
|
||||
string from = 2; // My public key for that contact
|
||||
string type = 3;
|
||||
string type = 3; // Message type
|
||||
bytes data = 4;
|
||||
message ConversationStatus {
|
||||
string localUuid = 1;
|
||||
uint64 localSequence = 2 ;
|
||||
uint64 sent = 3 ;
|
||||
uint64 received = 4;
|
||||
uint64 processed = 5;
|
||||
ContactCard myNextIdentity = 6;
|
||||
int32 peerNextIdentityAck = 7; // version of the new peer accepted id
|
||||
}
|
||||
ConversationStatus Status = 5;
|
||||
|
||||
ConversationStatus status = 5;
|
||||
ContactCard contact = 6;
|
||||
|
||||
Server knownServers = 7;
|
||||
|
||||
message Group{
|
||||
string name=1;
|
||||
repeated ContactCard members = 2;
|
||||
}
|
||||
ServerCard knownServers = 7;
|
||||
Group group = 8;
|
||||
|
||||
repeated File files = 9;
|
||||
Location current_location = 10;
|
||||
bytes appdata = 11;
|
||||
Invitation invitation = 12;
|
||||
VideoData video_data = 13;
|
||||
}
|
||||
|
||||
// UserMessage types :
|
||||
// 1 : normal message (test, image, video, audio, file, etc)
|
||||
// 2 : status message (online, offline, busy, etc)
|
||||
// 3 : send avatar as file[0]
|
||||
// 4 : location request
|
||||
// 5 : location response
|
||||
|
||||
|
||||
message File {
|
||||
string filename=1;
|
||||
uint64 size=2;
|
||||
uint32 chunk=3;
|
||||
bytes data=4;
|
||||
string filename=1; // the proposed filename
|
||||
uint64 size=2; // the file size
|
||||
uint32 chunk=3; // the chunk counter if file is sent by chunks
|
||||
bytes data=4; // the file/chunk content
|
||||
}
|
||||
|
||||
message Location {
|
||||
uint64 time=1;
|
||||
float latitude=2;
|
||||
float longitude=3;
|
||||
int32 altitude=4;
|
||||
}
|
||||
|
||||
message DbMessage {
|
||||
bool outbound = 1; // direction of the message
|
||||
string type = 2;
|
||||
bytes data = 3; // text data
|
||||
ConversationStatus status = 4;
|
||||
ContactCard contact = 5;
|
||||
Group group = 6;
|
||||
repeated string file_paths = 7;
|
||||
Location current_location = 8;
|
||||
bytes appdata = 9;
|
||||
Invitation invitation = 10;
|
||||
string from = 11; // source peer uid, used when storing group conversations with more than one peer
|
||||
}
|
||||
|
||||
message VideoData {
|
||||
string url = 1;
|
||||
string room = 2;
|
||||
uint64 duration = 3;
|
||||
repeated VideoCredential credentials = 4;
|
||||
repeated string media_query = 5;
|
||||
}
|
||||
|
||||
message VideoCredential {
|
||||
string username = 1;
|
||||
string shared_key = 2;
|
||||
string token = 3;
|
||||
}
|
@ -1,6 +1,13 @@
|
||||
#!/bin/bash
|
||||
echo Generating Golang
|
||||
protoc -I=. --go_out=.. messages.proto
|
||||
mv ../forge.redroom.link/yves/meowlib/messages.pb.go ../
|
||||
rm -rf ../forge.redroom.link
|
||||
|
||||
protoc -I=. --dart_out=../../../flutter/meowlib/lib/ messages.proto
|
||||
echo Generating HTML doc
|
||||
protoc --plugin=protoc-gen-doc=/usr/bin/protoc-gen-doc \
|
||||
--doc_out=../doc/generated \
|
||||
--doc_opt=html,index.html \
|
||||
*.proto
|
||||
echo Generating UML
|
||||
protoc --plugin=protoc-gen-uml=/usr/bin/protoc-gen-uml \
|
||||
--uml_out=../doc/generated -I=. *.proto
|
||||
|
@ -14,14 +14,15 @@ import (
|
||||
const key = "3pw0c8#6ZG8{75b5;3?fe80$2"
|
||||
|
||||
type Identity struct {
|
||||
ServerName string `json:"servername,omitempty"`
|
||||
ServerDesc string `json:"serverdesc,omitempty"`
|
||||
ServerKp meowlib.KeyPair `json:"server_kp,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
OwnerPublicKey string `json:"owner_public_key,omitempty"`
|
||||
ArchiveClients []string `json:"archive_clients,omitempty"`
|
||||
KnownServers []meowlib.Server `json:"known_servers,omitempty"`
|
||||
ServerName string `json:"servername,omitempty"`
|
||||
ServerDesc string `json:"serverdesc,omitempty"`
|
||||
ServerKp meowlib.KeyPair `json:"server_kp,omitempty"`
|
||||
Status string `json:"status,omitempty"`
|
||||
OwnerName string `json:"owner_name,omitempty"`
|
||||
OwnerPublicKey string `json:"owner_public_key,omitempty"`
|
||||
ArchiveClients []string `json:"archive_clients,omitempty"`
|
||||
KnownServers []meowlib.ServerCard `json:"known_servers,omitempty"`
|
||||
VideoServer VideoServer `json:"video_server,omitempty"`
|
||||
}
|
||||
|
||||
func CreateIdentity(ServerName string, ServerDesc string) *Identity {
|
||||
@ -52,7 +53,7 @@ func (id *Identity) Save(file string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.WriteFile(file, []byte(armor), 0644)
|
||||
err = os.WriteFile(file, []byte(armor), 0600)
|
||||
return err
|
||||
}
|
||||
|
||||
|
70
server/invitation.go
Normal file
70
server/invitation.go
Normal file
@ -0,0 +1,70 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis"
|
||||
)
|
||||
|
||||
func (r *RedisRouter) StoreInvitation(invitation []byte, timeout int, password string, serverTimeout int, urlLen int) (string, time.Time) {
|
||||
id := r.createShortId(urlLen)
|
||||
if timeout > serverTimeout {
|
||||
timeout = serverTimeout
|
||||
}
|
||||
r.Client.Set("mwiv:"+id, invitation, 0) //, time.Duration(timeout*1000000))
|
||||
if len(password) > 0 {
|
||||
r.Client.Set("mwpw:"+id, password, 0) //, time.Duration(timeout*1000000))
|
||||
}
|
||||
return id, time.Now().Add(time.Duration(timeout * 1000000)).UTC()
|
||||
}
|
||||
|
||||
func (r *RedisRouter) GetInvitation(id string, password string) ([]byte, error) {
|
||||
passRequired := false
|
||||
expectedpass, err := r.Client.Get("mwpw:" + id).Result()
|
||||
if err != nil {
|
||||
passRequired = false
|
||||
} else {
|
||||
passRequired = true
|
||||
}
|
||||
if passRequired && password != expectedpass {
|
||||
return nil, errors.New("auth failed")
|
||||
}
|
||||
mwiv, err := r.Client.Get("mwiv:" + id).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(mwiv), nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) StoreAnswerToInvitation(id string, timeout int, invitation []byte, serverTimeout int) time.Time {
|
||||
if timeout > serverTimeout {
|
||||
timeout = serverTimeout
|
||||
}
|
||||
r.Client.Set("mwan:"+id, invitation, time.Duration(timeout*1000000))
|
||||
return time.Now().Add(time.Duration(timeout * 1000000)).UTC()
|
||||
}
|
||||
|
||||
func (r *RedisRouter) GetAnswerToInvitation(id string) ([]byte, error) {
|
||||
mwan, err := r.Client.Get("mwan:" + id).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(mwan), nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) createShortId(length int) string {
|
||||
id := ""
|
||||
alphabet := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
// for not in redis
|
||||
for {
|
||||
for i := 1; i <= length; i++ {
|
||||
id += string(alphabet[rand.Intn(61)])
|
||||
}
|
||||
if r.Client.Get("mwiv:"+id).Err() == redis.Nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
12
server/logger.go
Normal file
12
server/logger.go
Normal file
@ -0,0 +1,12 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var logger zerolog.Logger
|
||||
|
||||
// AddLogger sets the logger for the sublibrary
|
||||
func AddLogger(l zerolog.Logger) {
|
||||
logger = l
|
||||
}
|
349
server/router.go
349
server/router.go
@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
@ -10,80 +11,90 @@ import (
|
||||
)
|
||||
|
||||
type RedisRouter struct {
|
||||
Name string
|
||||
ServerIdentity *Identity
|
||||
Client *redis.Client
|
||||
Context context.Context
|
||||
Name string
|
||||
ServerIdentity *Identity
|
||||
Client *redis.Client
|
||||
InvitationTimeout int
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
func NewRedisRouter(server *Identity, url string, password string, db int) *RedisRouter {
|
||||
func NewRedisRouter(server *Identity, redisUrl string, password string, db int, invitationTimeout int) *RedisRouter {
|
||||
var r RedisRouter
|
||||
r.ServerIdentity = server
|
||||
r.Name = "Redis"
|
||||
r.Client = redis.NewClient(&redis.Options{
|
||||
Addr: url,
|
||||
Addr: redisUrl,
|
||||
Password: password,
|
||||
DB: db,
|
||||
})
|
||||
r.InvitationTimeout = invitationTimeout
|
||||
r.Context = context.Background()
|
||||
// set start for uptime
|
||||
err := r.Client.Set("statistics:start", time.Now().UTC().Format(time.RFC3339), 0).Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
func (r *RedisRouter) Route(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
if len(msg.Messages) > 0 { // user message
|
||||
for _, usrmsg := range msg.Messages {
|
||||
// serialize the message to store it as byte array into redis
|
||||
out, err := proto.Marshal(usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd(usrmsg.Destination, redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
var from_server *meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:total").Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if len(msg.PullRequest) > 0 {
|
||||
for _, rq := range msg.PullRequest {
|
||||
msgcnt, err := r.Client.ZCount(rq.LookupKey, "-inf", "+inf").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.Client.ZPopMin(rq.LookupKey, msgcnt).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, redismsg := range res {
|
||||
//println(redismsg.Score)
|
||||
val := redismsg.Member
|
||||
test := val.(string)
|
||||
var usrmsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal([]byte(test), &usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add server timestamp
|
||||
usrmsg.ServerTimestamp = append(usrmsg.ServerTimestamp, int64(redismsg.Score))
|
||||
|
||||
from_server.Chat = append(from_server.Chat, &usrmsg)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if msg.MatriochkaMessage != nil {
|
||||
out, err := proto.Marshal(msg)
|
||||
// user message => store
|
||||
if len(msg.Messages) > 0 {
|
||||
logger.Info().Msg("storing message")
|
||||
from_server, err = r.storeMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd("mtk", redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
if msg.MatriochkaMessage.LookupKey != "" {
|
||||
//r.Client.ZAdd("trk:" + msg.MatriochkaMessage.Next.Uuid,{})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
}
|
||||
|
||||
// check for messages
|
||||
if len(msg.PullRequest) > 0 {
|
||||
logger.Info().Msg("checking for messages")
|
||||
from_server, err = r.checkForMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if msg.Timeout > 0 {
|
||||
logger.Info().Msg("long poll, subscribing for messages")
|
||||
// set timeout for the lookup
|
||||
from_server, err = r.subscribe(msg, int(msg.Timeout))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// initiate video
|
||||
if msg.VideoData != nil {
|
||||
logger.Info().Msg("handling video")
|
||||
from_server, err = r.handleVideo(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// manage Matriochka
|
||||
if msg.MatriochkaMessage != nil {
|
||||
logger.Info().Msg("handling matriochka")
|
||||
from_server, err = r.handleMatriochka(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Server list exchange
|
||||
if len(msg.KnownServers) > 0 {
|
||||
|
||||
}
|
||||
// Through server invitation process
|
||||
if msg.Invitation != nil {
|
||||
logger.Info().Msg("handling invitation")
|
||||
from_server, err = r.handleInvitation(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
@ -98,5 +109,237 @@ func (r *RedisRouter) Route(msg *meowlib.ToServerMessage) (*meowlib.FromServerMe
|
||||
break
|
||||
}
|
||||
*/
|
||||
return from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) storeMessage(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:usermessages").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, usrmsg := range msg.Messages {
|
||||
// serialize the message to store it as byte array into redis
|
||||
out, err := proto.Marshal(usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd("msg:"+usrmsg.Destination, redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
r.Client.Publish("msgch:"+usrmsg.Destination, "!")
|
||||
// if delivery tracking resquested, store the uid for the sender's key in delivery tracking
|
||||
if usrmsg.ServerDeliveryUuid != "" {
|
||||
r.Client.SAdd("dvyrq:"+usrmsg.ServerDeliveryUuid, redis.Z{Score: float64(time.Now().Unix()), Member: msg.From})
|
||||
}
|
||||
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) checkForMessage(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
//dataFound := false
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:messagelookups").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// todo check pull requests signature
|
||||
// iterate over pull requests
|
||||
for _, rq := range msg.PullRequest {
|
||||
// get messages from redis
|
||||
msgcnt, err := r.Client.ZCount("msg:"+rq.LookupKey, "-inf", "+inf").Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.Client.ZPopMin("msg:"+rq.LookupKey, msgcnt).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// iterate over messages
|
||||
for _, redismsg := range res {
|
||||
//println(redismsg.Score)
|
||||
val := redismsg.Member
|
||||
test := val.(string)
|
||||
var usrmsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal([]byte(test), &usrmsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// add server timestamp
|
||||
usrmsg.ServerTimestamp = append(usrmsg.ServerTimestamp, int64(redismsg.Score))
|
||||
from_server.Chat = append(from_server.Chat, &usrmsg)
|
||||
|
||||
// if delivery for that pick up requested, create, store and publish delivery message
|
||||
deliveryRequester, err := r.Client.SPop("msg:" + usrmsg.ServerDeliveryUuid).Result()
|
||||
if err != nil {
|
||||
if err != redis.Nil { // exit only if real error
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err != redis.Nil {
|
||||
// create a delivery record
|
||||
r.Client.ZAdd("dvy:"+deliveryRequester, redis.Z{Score: float64(time.Now().Unix()), Member: usrmsg.ServerDeliveryUuid})
|
||||
// publish it in case of listener
|
||||
r.Client.Publish("dvych:"+usrmsg.ServerDeliveryUuid, "!")
|
||||
}
|
||||
}
|
||||
// if no messages check for invitationanswer payload
|
||||
if msgcnt == 0 {
|
||||
// get invitation answer
|
||||
var answer meowlib.Invitation
|
||||
storedAnswer, _ := r.GetAnswerToInvitation(rq.LookupKey)
|
||||
if storedAnswer != nil {
|
||||
err := proto.Unmarshal(storedAnswer, &answer)
|
||||
if err != nil {
|
||||
from_server.Invitation.Payload = []byte("invitation answer corrupted")
|
||||
}
|
||||
from_server.Invitation = &answer
|
||||
// exit loop if invitation found, cannot store several in a message
|
||||
return &from_server, nil
|
||||
}
|
||||
// add invitation answer to the response
|
||||
}
|
||||
|
||||
}
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func goSubscribeAndListen(client *redis.Client, key string, messages chan<- string, wg *sync.WaitGroup, done <-chan struct{}) {
|
||||
defer wg.Done()
|
||||
pubsub := client.Subscribe("msgch:" + key)
|
||||
defer pubsub.Close()
|
||||
|
||||
// Create a new channel for the messages from this subscription
|
||||
myMessages := make(chan *redis.Message)
|
||||
go func() {
|
||||
for {
|
||||
msg, err := pubsub.ReceiveMessage()
|
||||
if err != nil {
|
||||
close(myMessages)
|
||||
return
|
||||
}
|
||||
myMessages <- msg
|
||||
}
|
||||
}()
|
||||
|
||||
// Wait for a message or for the done signal
|
||||
select {
|
||||
case msg := <-myMessages:
|
||||
messages <- msg.Payload
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (r *RedisRouter) subscribe(msg *meowlib.ToServerMessage, timeout int) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:messagessubscription").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
messages := make(chan string)
|
||||
var wg sync.WaitGroup
|
||||
done := make(chan struct{})
|
||||
// extract lookup keys and subscribe
|
||||
// iterate over pull requests
|
||||
for _, rq := range msg.PullRequest {
|
||||
wg.Add(1)
|
||||
// subscribe to the lookup key
|
||||
go goSubscribeAndListen(r.Client, rq.LookupKey, messages, &wg, done)
|
||||
}
|
||||
// wait for timeout or message
|
||||
select {
|
||||
case <-messages:
|
||||
close(done)
|
||||
return r.checkForMessage(msg)
|
||||
case <-time.After(time.Duration(timeout) * time.Second): // 10 seconds timeout
|
||||
close(done)
|
||||
}
|
||||
wg.Wait()
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) handleInvitation(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:invitation").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch msg.Invitation.Step {
|
||||
// create invitation => provide shortcode and expiry
|
||||
case 1:
|
||||
url, expiry := r.StoreInvitation(msg.Invitation.Payload, int(msg.Invitation.Timeout), msg.Invitation.Password, r.InvitationTimeout, int(msg.Invitation.ShortcodeLen))
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
from_server.Invitation.Shortcode = url
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
// get invitation => retrieve invitation from redis and send
|
||||
case 2:
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
invitation, err := r.GetInvitation(msg.Invitation.Shortcode, msg.Invitation.Password)
|
||||
|
||||
if err != nil {
|
||||
if err.Error() == "auth failed" {
|
||||
from_server.Invitation.Payload = []byte("authentication failure")
|
||||
} else {
|
||||
from_server.Invitation.Payload = []byte("invitation expired")
|
||||
}
|
||||
} else {
|
||||
from_server.Invitation.Payload = invitation // protobuf invitation
|
||||
}
|
||||
|
||||
// accept invitation => store accepted invitation for initiator
|
||||
case 3:
|
||||
var usermsg meowlib.PackedUserMessage
|
||||
err := proto.Unmarshal(msg.Invitation.Payload, &usermsg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := proto.Marshal(msg.Invitation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expiry := r.StoreAnswerToInvitation(usermsg.Destination, int(msg.Invitation.Timeout), data, r.InvitationTimeout)
|
||||
from_server.Invitation = &meowlib.Invitation{}
|
||||
from_server.Invitation.Expiry = expiry.UTC().Unix()
|
||||
}
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) handleVideo(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:video").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
videoData, err := r.ServerIdentity.VideoServer.UpdateVideoData(msg.VideoData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
from_server.VideoData = videoData
|
||||
from_server.UuidAck = msg.Uuid
|
||||
return &from_server, nil
|
||||
}
|
||||
|
||||
func (r *RedisRouter) handleMatriochka(msg *meowlib.ToServerMessage) (*meowlib.FromServerMessage, error) {
|
||||
var from_server meowlib.FromServerMessage
|
||||
// update messages counter
|
||||
err := r.Client.Incr("statistics:messages:matriochka").Err()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out, err := proto.Marshal(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.Client.ZAdd("mtk", redis.Z{Score: float64(time.Now().Unix()), Member: out})
|
||||
if msg.MatriochkaMessage.LookupKey != "" {
|
||||
//r.Client.ZAdd("trk:" + msg.MatriochkaMessage.Next.Uuid,{})
|
||||
}
|
||||
from_server.UuidAck = msg.Uuid
|
||||
return &from_server, nil
|
||||
}
|
||||
|
41
server/videoserver.go
Normal file
41
server/videoserver.go
Normal file
@ -0,0 +1,41 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"forge.redroom.link/yves/meowlib"
|
||||
"github.com/livekit/protocol/auth"
|
||||
)
|
||||
|
||||
type VideoServer struct {
|
||||
Url string `json:"url,omitempty"`
|
||||
ApiKey string `json:"api_key,omitempty"`
|
||||
ApiSecret string `json:"api_secret,omitempty"`
|
||||
}
|
||||
|
||||
func (s *VideoServer) GetJoinToken(room, username string, validity time.Duration) (string, error) {
|
||||
at := auth.NewAccessToken(s.ApiKey, s.ApiSecret)
|
||||
grant := &auth.VideoGrant{
|
||||
RoomJoin: true,
|
||||
Room: room,
|
||||
}
|
||||
at.AddGrant(grant).
|
||||
SetIdentity(username).
|
||||
SetValidFor(validity)
|
||||
|
||||
return at.ToJWT()
|
||||
}
|
||||
|
||||
func (s *VideoServer) UpdateVideoData(vd *meowlib.VideoData) (*meowlib.VideoData, error) {
|
||||
vd.Url = s.Url
|
||||
vd.Credentials = []*meowlib.VideoCredential{}
|
||||
for idx := range len(vd.Credentials) {
|
||||
token, err := s.GetJoinToken(vd.Room, vd.Credentials[idx].Username, time.Duration(vd.Duration*uint64(time.Second)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vd.Credentials[idx].Token = token
|
||||
vd.Credentials[idx].SharedKey = s.ApiKey
|
||||
}
|
||||
return vd, nil
|
||||
}
|
12
servercard.go
Normal file
12
servercard.go
Normal file
@ -0,0 +1,12 @@
|
||||
package meowlib
|
||||
|
||||
func (sc *ServerCard) GetUid() string {
|
||||
if len(sc.Login) > 0 || len(sc.Password) > 0 {
|
||||
return sc.Login + ":" + sc.Password + "@" + sc.Url
|
||||
}
|
||||
return sc.Url
|
||||
}
|
||||
|
||||
func (sc *ServerCard) IsSame(sc1 *ServerCard) bool {
|
||||
return sc.GetUid() == sc1.GetUid()
|
||||
}
|
Reference in New Issue
Block a user