Compare commits

..

165 Commits

Author SHA1 Message Date
ycc
f8537aad6d long poll exit fix 2025-05-07 22:53:34 +02:00
ycc
8d589505e5 change array of pointer to array of requestjobs 2025-05-07 20:44:41 +02:00
ycc
511e260157 change longpoll job array param type 2025-05-04 23:19:18 +02:00
ycc
839fb7c0f9 long poll helper first draft - untested 2025-05-04 22:34:20 +02:00
ycc
3af112b860 go 1.24 2025-05-04 21:12:55 +02:00
ycc
7e68a12adb refactor 2025-05-04 09:56:09 +02:00
ycc
ec8924d05a save peer status when db created or last message when received
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 23:18:25 +02:00
ycc
d2bd4deb82 fix server creatiuon with userkp
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 22:40:38 +02:00
ycc
9b6caf4e62 adding loggers
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 22:23:48 +02:00
ycc
050de1a0b3 clean logwriter
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 21:31:34 +02:00
ycc
606c42cd5e add name to server when creating from uid or url
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 21:07:00 +02:00
ycc
af55df1ff5 store unknown servers in the invitation process answer and finalize + server storage fixes and testing
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-05 14:45:01 +02:00
ycc
c0fd74f1e6 again
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-04 22:36:45 +02:00
ycc
24cc151938 lokiwriter fix
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-04 22:34:44 +02:00
ycc
cc663d88c8 added lokiwriter log, server logger setting function and some zerolog logs
Some checks failed
continuous-integration/drone/push Build is failing
2024-06-04 22:02:18 +02:00
ycc
118eb29d17 add nameexists to peers storage
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 20:19:58 +02:00
ycc
e9624e4576 write peer storage test + fix loading
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 18:39:32 +02:00
ycc
78a8b797e6 initial load peers fix
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 18:05:41 +02:00
ycc
9179021544 peer db default load fix
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 17:32:26 +02:00
ycc
d049f87cdc peers db alpha release
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 17:25:10 +02:00
ycc
e674a0cb33 peers separate
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 16:47:04 +02:00
ycc
c1883f1524 ready to switch to peer DB
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-28 14:28:10 +02:00
ycc
c284b15788 some video retated fixes
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-27 18:38:38 +02:00
ycc
17c991f442 some adjustmentsfor server delivery and peer storage study
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-23 14:14:12 +02:00
ycc
d657e64ae4 models and doc update
Some checks failed
continuous-integration/drone/push Build is failing
2024-05-22 10:06:00 +02:00
ycc
2e2ebad364 bugfix : return valur from subscribe
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-24 20:12:03 +02:00
ycc
428eda0ab7 Update RedisRouter.Publish method to use channel prefix for destination
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-24 19:39:12 +02:00
ycc
a3557d5183 Add RedisRouter.Publish method to store and publish messages
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-23 23:35:23 +02:00
ycc
5491244d01 Refactor RedisRouter subscribe function to include timeout parameter
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-23 22:48:37 +02:00
ycc
04d2b0246a Add longPoll parameter to CheckForMessages function
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-23 22:41:58 +02:00
ycc
4dfec86279 Update Config.Load function to override default values if not set or wrong
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-23 22:31:11 +02:00
ycc
d14cd161da manage http timeout to allow long poll
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-20 22:29:22 +02:00
ycc
7c17a11426 long poll routing first version
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-19 09:13:50 +02:00
ycc
6cfc54d943 change meowurl scheme to meow://http//url for safer uri parsing
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-12 14:37:20 +02:00
ycc
5aec7b3ad4 refactor router
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-10 11:18:42 +02:00
ycc
d709cb9454 Update PrepareUserMessage function to include replyToUid parameter 2024-04-09 20:58:29 +02:00
ycc
05df08efcb Add GetFilePreview method to Peer
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-06 16:04:41 +02:00
ycc
813611bde7 add preview
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-06 15:44:30 +02:00
ycc
7fa997d443 message preview in progress
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-06 12:55:27 +02:00
ycc
2513f0303a Refactor PrepareUserMessage function to store message after processing outbound user message, store is destructive for a usermessage
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-06 10:13:25 +02:00
ycc
99a9aa14af store securefiles in messages and not filenames
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-05 23:07:23 +02:00
ycc
09892709ec Fix function name and update file path handling
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-05 18:07:00 +02:00
ycc
3ac6b02e56 Refactor file detachment logic in ReadMessage function
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-01 20:56:45 +02:00
ycc
8fca09d853 Add securefiles directory for storing encrypted files
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 19:39:50 +02:00
ycc
aaa4d88a2f Refactor message storage and retrieval
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 19:04:37 +02:00
ycc
3bf75eb990 Fix invitationCreateHelper.go to use the correct name parameter
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 18:36:51 +02:00
ycc
54c36c68ad Fix invitation answer message building with inv_id
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 18:22:17 +02:00
ycc
caab80f346 Fix condition for saving server message in backgroundHelper.go for nil safety
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 17:58:41 +02:00
ycc
dfa2b5fa83 Set invitation UUID in BuildInvitationAnswerMessage function
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 17:24:21 +02:00
ycc
2ac70d5448 Fix error handling in GetAnswerToInvitation function
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 17:05:37 +02:00
ycc
2a246744db Fix invitation handling in RedisRouter
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 16:24:25 +02:00
ycc
b4f7071990 Update Redis key in GetAnswerToInvitation function
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 16:13:08 +02:00
ycc
903702c719 Fix server public key assignment and rename Wipe() to WipeFolder()
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-31 15:52:50 +02:00
ycc
54b932e9c1 Add Wipe function to Identity
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-30 22:44:59 +01:00
ycc
13cfda928d Remove message files after ReadMessage processing
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 21:08:09 +01:00
ycc
b556cd0361 Add logging and create files folder for detach files
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 20:49:18 +01:00
ycc
657fdbbf48 readmessage cleanup
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 19:31:48 +01:00
ycc
a65d4f1a69 bugfix empty messages saved...
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 19:25:15 +01:00
ycc
a5cfbf854d add identity folder creation
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 18:26:41 +01:00
ycc
385c5f3298 Remove temporary patch for generating UUID in Identity.Save() function
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 18:16:34 +01:00
ycc
31df45e771 store msg db and inbox by identity
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 18:07:06 +01:00
ycc
0b8e3c4c90 declare identity to ease debug
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 16:42:03 +01:00
ycc
ce479cc5b9 Remove unused variable in ReadMessage function
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 16:22:26 +01:00
ycc
bdfa8c7bb1 ReadMessage fix
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 16:18:24 +01:00
ycc
8300a699a3 background return improve
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 16:17:07 +01:00
ycc
729ba7e02a add server pub key to background process
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 15:59:33 +01:00
ycc
b15f571938 server invitatio
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-29 15:40:46 +01:00
ycc
ead810e666 server store invitation answer by lookupkey + refactor background check
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-28 18:41:30 +01:00
ycc
4b412ae0f3 InvitationAnswerMessageReadResponse
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-28 00:45:40 +01:00
ycc
e4efff1824 Server side invitation step 3/4 preserve From public key / Client finalize in progress
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 23:42:56 +01:00
ycc
788512c391 Server invitation step 3/4
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 20:22:56 +01:00
ycc
6bf6fadaaa fix invitation code separator
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 15:29:52 +01:00
ycc
0070a64d5f server from uid weak patch
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 15:23:24 +01:00
ycc
1398c6040a moewurl bugfixes
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 15:15:41 +01:00
ycc
12ad5ced49 meowurls
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 14:57:33 +01:00
ycc
53145f1c5e create server from uid added
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-26 14:12:32 +01:00
ycc
6867086c4f Http helper function added
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-25 13:24:43 +01:00
ycc
f80411bf21 add uid to peer + helper methods + http
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-23 20:09:14 +01:00
ycc
0a70206b11 handle no db yet in message retrieve
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-05 23:15:19 +01:00
ycc
f40f6520d2 add last message to peers
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-05 20:15:48 +01:00
ycc
b47ef2480c typo... useful line ?
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-02 18:11:15 +01:00
ycc
cba13ad91a adding usermessage answer_to_usermessage in status
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-02 18:05:13 +01:00
ycc
1ba84dcefc bugfix
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-02 14:10:16 +01:00
ycc
0c0aa6e807 try to avoid protobuf omitted
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-02 10:45:05 +01:00
ycc
3467ea15d9 Change Peers to * Peers in identity to retrieve pointer to the real peer
Some checks failed
continuous-integration/drone/push Build is failing
2024-03-02 10:07:59 +01:00
ycc
aa63bb745f refactor
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-29 21:14:46 +01:00
ycc
f20681adab Store messages with DbMessage Type
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-29 21:03:15 +01:00
ycc
4b3d7548bd store fix + add peer invitationPending Check
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-25 20:15:35 +01:00
ycc
c4b61e16c5 Add invitation message to CheckInvitation function
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-20 22:44:19 +01:00
ycc
9dcb579d93 Refactor GetMessagesHistory and LoadLastMessages functions to use slice of InternalUserMessage instead of pointer to InternalUserMessage.
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-20 20:30:55 +01:00
ycc
defafcf996 Refactor invitation handling in router.go
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-20 20:25:07 +01:00
ycc
db4c3cbbc8 From moved to invitation message
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-18 13:56:33 +01:00
ycc
07dfae8f0e message storage and from removed from packedusermessage (weakness)
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-18 13:46:11 +01:00
ycc
05cc2ee218 Refactor client message handling and storage
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-17 19:30:25 +01:00
ycc
034dcf5215 Add invitation to user message and comment out debug print statements
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-16 19:03:45 +01:00
ycc
c58199385e Addinf trace function for invitation debug
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-15 23:24:07 +01:00
ycc
b6b9dc238a protobug03
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 23:25:37 +01:00
ycc
9ec682d708 protobug 02
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 22:40:41 +01:00
ycc
465a366e79 protobug test01
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 22:26:52 +01:00
ycc
2969227656 new version of from to trick protobuf ?
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 22:15:25 +01:00
ycc
9561531c7c temporary disable from for cli debug
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 22:11:53 +01:00
ycc
8c8326780f Add 'from' field to PackedUserMessage
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-13 21:00:50 +01:00
ycc
0466b1fe05 Debug GetRequestJobs function and add unit tests
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-12 20:02:02 +01:00
ycc
69a07d77d5 Fix error handling in GetMyContact and GetContact functions
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 23:35:41 +01:00
ycc
a19f228c8e Fix issue with loading contact cards from peer
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 23:30:29 +01:00
ycc
bcb3489de4 BugFix pullservers allocation in finalize
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 22:58:25 +01:00
ycc
0fd7548ba4 Contact struct removed from peer
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 22:36:25 +01:00
ycc
24183ff581 Remove MyContact contactcard from peer
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 15:05:02 +01:00
ycc
df9c6b5d46 Refactor GetUid() method and add new tests for server storage
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-10 11:32:44 +01:00
ycc
423ef5c4b1 Update badger db path
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-09 23:28:43 +01:00
ycc
b87c0bff3e add LoadAllServerCards from badger
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-09 22:30:00 +01:00
ycc
978b6fdfd1 badgerdb messageservers storage
Some checks failed
continuous-integration/drone/push Build is failing
2024-02-08 22:17:16 +01:00
ycc
93e972900f Refactor structs with some getters/setters - Peers part
All checks were successful
continuous-integration/drone/push Build is passing
2024-02-07 16:08:24 +01:00
ycc
f8a1cb6f68 begin adding device to device communication
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-18 22:43:41 +01:00
ycc
44661de993 conversation_request cleanup
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-14 15:49:24 +01:00
ycc
48b2e78b41 request jobs generation
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-12 23:17:34 +01:00
ycc
a9f3b548e5 permission fixes
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-05 17:01:36 +01:00
ycc
379b40b2fb answer invitation bugfix
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-03 23:11:23 +01:00
ycc
cbedad7178 typo, + invitation return value changed from ContactCard to Peer
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-03 19:40:38 +01:00
ycc
9283764f42 proto contactcard to _
Some checks failed
continuous-integration/drone/push Build is failing
2024-01-01 22:45:54 +01:00
ycc
25cf1808e3 added an invitation message
All checks were successful
continuous-integration/drone/push Build is passing
2024-01-01 13:50:00 +01:00
ycc
6788487368 restoring, pb was in client code
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 23:46:24 +01:00
ycc
e406010374 #2
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 23:39:30 +01:00
ycc
289e39c677 invitation bytearray copy debug #1
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 23:28:10 +01:00
ycc
6511ff6280 add debug test code for redis binary store
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-31 22:44:21 +01:00
ycc
206dda0761 fix missing init
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 20:33:01 +01:00
ycc
7a6c1cd085 temporary remove invitation expiry
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-31 20:22:56 +01:00
ycc
04e81fcef1 lib invitation improvement for GUI
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-31 18:43:55 +01:00
ycc
0998845817 Invitation process store my server card+ server invitation fields
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-31 10:24:15 +01:00
ycc
922668e2a3 Invitation struct initt fix
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-30 19:08:44 +01:00
ycc
5eb6be1415 test changing cameCase for _ in protobuf servercard
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-28 16:57:34 +01:00
ycc
a3b2473eed avatar field rename
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-26 17:51:42 +01:00
ycc
043980042d peer cleanup
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-26 13:54:20 +01:00
ycc
58118bd6bb dependency upgrade
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-25 16:46:03 +01:00
ycc
86d51d6dfb cleanup
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-25 16:44:58 +01:00
ycc
6a42d261f1 doc update
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
2023-12-12 23:44:30 +01:00
ycc
1dda1b27a8 Server lists simplify
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-09 20:09:55 +01:00
ycc
535181d669 Server management keys, and user Kp rename
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-09 20:01:19 +01:00
ycc
c3ad8d5d2d start time format fix
Some checks failed
continuous-integration/drone/push Build is failing
2023-12-08 17:08:01 +01:00
ycc
4cd928fdc6 add basic server statistics to redis db
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-08 16:52:18 +01:00
ycc
420b7d0af0 Refactor and servercard ajustments
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-27 00:31:43 +01:00
ycc
432f449558 Server invitation process functions
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-14 16:32:50 +01:00
ycc
65f9ee2e07 out of bound errot detection
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-09 22:50:51 +01:00
ycc
eeb39338e2 Split server and serverlist + serverlist methods
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-09 22:46:39 +01:00
ycc
c203431ede InternalServer to Server refactor
All checks were successful
continuous-integration/drone Build is passing
2023-11-08 22:01:44 +01:00
ycc
04a390d558 Refactor and comment 2023-11-08 21:52:09 +01:00
ycc
08ff9d58b8 Doc update 2023-11-06 22:44:21 +01:00
ycc
cd41a59518 config params 2023-09-27 22:17:44 +02:00
ycc
4750915b49 config update 2023-09-27 17:16:23 +02:00
ycc
e0faaf8cef switch url to id in invitation process 2023-08-31 23:51:20 +02:00
ycc
539e2c528e fix router constructor 2023-08-31 23:43:36 +02:00
ycc
d082724432 Integrate invitation process in router 2023-08-31 23:38:03 +02:00
ycc
5eb06ebcc5 typo 2023-08-30 21:16:13 +02:00
ycc
753cd30f38 start through server invitation process 2023-08-29 23:40:30 +02:00
ycc
4a009b69eb start through server invitation process 2023-08-29 23:40:19 +02:00
ycc
9d5ba42dfc loaction and appdata added 2023-08-01 22:47:18 +02:00
ycc
71df3a792b add my name and myavatar to my identity for a peer 2023-07-27 15:48:06 +02:00
ycc
d748735ded Added avatar identifiers and servers table 2023-07-27 10:44:09 +02:00
ycc
698740e20a Identity accessors
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-17 22:30:13 +01:00
ycc
940a8d395c add identity accessors to config 2023-02-17 22:29:21 +01:00
ycc
6f2a65dac9 storage get messages + config improve + tests
Some checks failed
continuous-integration/drone/push Build is failing
2023-02-15 22:08:17 +01:00
ycc
d9155bac51 tests
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-11 22:43:08 +01:00
ycc
0f8bdf5f66 Store pass for db saving
Some checks reported errors
continuous-integration/drone/push Build encountered an error
2023-01-11 22:29:31 +01:00
ycc
ce758c5bb1 hide peers, add drone ci, doc update 2023-01-11 21:42:14 +01:00
ycc
c4bb8c5693 hiddens peers unlock 2023-01-08 23:19:08 +01:00
81 changed files with 9365 additions and 1191 deletions

View 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)

View 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

View File

@ -0,0 +1,2 @@
"filename", "language", "", "comment", "blank", "total"
"Total", "-", , 0, 0, 0
1 filename language comment blank total
2 Total - 0 0 0

View 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)

View 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 |
+----------+----------+------------+------------+------------+------------+

View 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
1 filename language Go Go Module File Markdown YAML Go Checksum File Shell Script Batch Protocol Buffers Python LaTeX PlantUML SVG comment blank total
2 /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
3 /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
4 /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
5 /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
6 /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
7 /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
8 /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
9 /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
10 /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
11 /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
12 /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
13 /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
14 /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
15 /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
16 /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
17 /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
18 /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
19 /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
20 /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
21 /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
22 /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
23 /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
24 /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
25 /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
26 /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
27 /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
28 /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
29 /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
30 /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
31 /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
32 /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
33 /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
34 /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
35 /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
36 /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
37 /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
38 /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
39 /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
40 /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
41 /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
42 /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
43 /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
44 /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
45 /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
46 /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
47 /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
48 /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
49 /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
50 /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
51 /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
52 /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
53 /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
54 /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
55 /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
56 /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
57 /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
58 /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
59 /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
60 /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
61 /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
62 /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
63 /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
64 /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
65 /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
66 /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
67 /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
68 /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
69 /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
70 /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
71 /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
72 /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
73 /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
74 /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
75 /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
76 /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
77 /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
78 /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
79 /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
80 /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
81 /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
82 /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
83 /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
84 /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
85 /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
86 /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
87 /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
88 /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
89 /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
90 Total - 6390 45 21 9 344 34 4 194 11 235 387 2814 836 1073 12397

View 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)

View 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
View File

@ -0,0 +1,11 @@
type: docker
kind: pipeline
name: unit
steps:
- name: test
image: golang
commands:
- go test
- go build

14
.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -0,0 +1,8 @@
package client
import "time"
type Avatar struct {
File string
Date time.Time
}

View File

@ -8,28 +8,43 @@ 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
@ -56,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
}
@ -64,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
}
@ -79,6 +103,14 @@ 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()
}

View File

@ -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
View 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
}

View 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
View 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() {
}

View File

@ -0,0 +1 @@
package helpers

View 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
}

View 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
}

View 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()*/
}

View 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
View 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
}

View 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
}

View 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
}

View File

@ -0,0 +1 @@
package helpers

View 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
}

View File

@ -3,81 +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()
GetConfig().me = &id
return &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()
@ -86,32 +128,55 @@ 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
@ -125,11 +190,20 @@ 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 {
@ -138,3 +212,150 @@ func (id *Identity) Save() error {
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)
}

View File

@ -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,13 +19,48 @@ 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)
@ -38,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")
}

View 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
View 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
}

View File

@ -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

View File

@ -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
View 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
}

View 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)
}
}
}

View File

@ -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"`
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
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,24 +279,45 @@ func (p *Peer) ProcessInboundUserMessage(message []byte, signature []byte) (*meo
return msg, nil
}
//
// 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 []byte) {
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) {

View File

@ -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
View 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
View 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)
}

View 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)
}
}

View File

@ -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
View 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
View 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()
}

View 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")
}

View File

@ -1,91 +0,0 @@
package client
import (
"database/sql"
"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, password string) error {
var dbid string
// If no db/no ID create DB + Tablz
if len(peer.DbIds) == 0 {
dbid = uuid.NewString()
file, err := os.Create(filepath.Join(GetConfig().StoragePath, dbid))
if err != nil {
return err
}
file.Close()
peer.DbIds = append(peer.DbIds, dbid)
sqliteDatabase, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid)) // Open the created SQLite File
err = createTable(sqliteDatabase)
if err != nil {
return err
}
sqliteDatabase.Close()
GetConfig().me.Save()
} else {
dbid = peer.DbIds[len(peer.DbIds)-1]
}
// Open Db
db, _ := sql.Open("sqlite3", filepath.Join(GetConfig().StoragePath, dbid)) // Open the created SQLite File
defer db.Close()
// Detach Files
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
}
os.WriteFile(hiddenFilename, encData, 0600)
// replace f.Data by uuid filename
f.Data = []byte(hiddenFilename)
}
}
// Encrypt message
out, err := proto.Marshal(usermessage)
if err != nil {
return err
}
encData, err := meowlib.SymEncrypt(password, out)
if err != nil {
return err
}
// Insert message
insertMessageSQL := `INSERT INTO message(m) VALUES (?)`
statement, err := db.Prepare(insertMessageSQL) // Prepare statement.
if err != nil {
return err
}
_, err = statement.Exec(encData)
if err != nil {
return err
}
return nil
}
// Get last messages from a peer
func GetLastMessages(peer *Peer, lastIdx int, count int, gotUuid string) {
// TODO Try to get count from last DB
// TODO Get missing from previous DB
}
func createTable(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
}

View File

@ -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
}

View File

@ -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
View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 467 KiB

225
doc/meow.tex Normal file
View 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}

View 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

View File

@ -1,16 +1,19 @@
\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}}
\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.
@ -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}

View 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

View 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

View 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

View File

@ -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

View File

@ -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
}

49
go.mod
View File

@ -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/mattn/go-sqlite3 v1.14.16
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.22.1 // indirect
github.com/pkg/errors v0.9.1
github.com/rs/zerolog v1.25.0
github.com/stretchr/testify v1.5.1
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
)

348
go.sum
View File

@ -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,149 +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/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/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.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/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=
@ -178,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
View 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
View 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
}

View File

@ -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
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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

View File

@ -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
View 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
View 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
}

View File

@ -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
View 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
View 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()
}