aboutsummaryrefslogtreecommitdiff
path: root/src/messenger/gnunet-service-messenger_member_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/messenger/gnunet-service-messenger_member_session.c')
-rw-r--r--src/messenger/gnunet-service-messenger_member_session.c707
1 files changed, 707 insertions, 0 deletions
diff --git a/src/messenger/gnunet-service-messenger_member_session.c b/src/messenger/gnunet-service-messenger_member_session.c
new file mode 100644
index 000000000..636285d93
--- /dev/null
+++ b/src/messenger/gnunet-service-messenger_member_session.c
@@ -0,0 +1,707 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @author Tobias Frisch
22 * @file src/messenger/gnunet-service-messenger_member_session.c
23 * @brief GNUnet MESSENGER service
24 */
25
26#include "gnunet-service-messenger_member_session.h"
27
28#include "gnunet-service-messenger_room.h"
29#include "gnunet-service-messenger_message_store.h"
30
31#include "messenger_api_contact_store.h"
32
33struct GNUNET_MESSENGER_MemberSession*
34create_member_session (struct GNUNET_MESSENGER_Member *member,
35 const struct GNUNET_IDENTITY_PublicKey *pubkey)
36{
37 if ((!member) || (!pubkey) || (!(member->store)))
38 return NULL;
39
40 struct GNUNET_MESSENGER_MemberSession *session = GNUNET_new(struct GNUNET_MESSENGER_MemberSession);
41 session->member = member;
42
43 GNUNET_memcpy(&(session->public_key), pubkey, sizeof(session->public_key));
44
45 get_context_from_member (
46 get_member_session_key (session),
47 get_member_session_id (session),
48 &(session->context)
49 );
50
51 struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store);
52
53 session->contact = get_store_contact(
54 store,
55 get_member_session_context (session),
56 get_member_session_public_key (session)
57 );
58
59 if (!(session->contact))
60 {
61 GNUNET_free(session);
62 return NULL;
63 }
64
65 increase_contact_rc (session->contact);
66
67 session->history = GNUNET_CONTAINER_multihashmap_create(8, GNUNET_NO);
68
69 init_list_messages(&(session->messages));
70
71 session->next = NULL;
72
73 session->closed = GNUNET_NO;
74 session->completed = GNUNET_NO;
75
76 return session;
77}
78
79static void
80check_member_session_completion (struct GNUNET_MESSENGER_MemberSession *session)
81{
82 GNUNET_assert (session);
83
84 if (!session->messages.tail)
85 {
86 session->completed = GNUNET_YES;
87 goto completion;
88 }
89
90 const struct GNUNET_HashCode* start = &(session->messages.head->hash);
91 const struct GNUNET_HashCode* end = &(session->messages.tail->hash);
92
93 struct GNUNET_MESSENGER_ListMessages level;
94 init_list_messages(&level);
95
96 add_to_list_messages(&level, end);
97
98 struct GNUNET_MESSENGER_MessageStore *store = get_room_message_store(session->member->store->room);
99
100 struct GNUNET_MESSENGER_ListMessages list;
101 init_list_messages(&list);
102
103 while (level.head)
104 {
105 struct GNUNET_MESSENGER_ListMessage *element;
106
107 for (element = level.head; element; element = element->next)
108 {
109 const struct GNUNET_MESSENGER_MessageLink *link = get_store_message_link(
110 store, &(element->hash), GNUNET_NO
111 );
112
113 if (!link)
114 continue;
115
116 add_to_list_messages(&list, &(link->first));
117
118 if (GNUNET_YES == link->multiple)
119 add_to_list_messages(&list, &(link->second));
120 }
121
122 clear_list_messages(&level);
123
124 for (element = list.head; element; element = element->next)
125 if (GNUNET_YES == check_member_session_history(session, &(element->hash), GNUNET_YES))
126 break;
127
128 if (element)
129 if (0 != GNUNET_CRYPTO_hash_cmp(&(element->hash), start))
130 add_to_list_messages(&level, &(element->hash));
131 else
132 session->completed = GNUNET_YES;
133 else
134 copy_list_messages(&level, &list);
135
136 clear_list_messages(&list);
137 }
138
139completion:
140 if (GNUNET_YES == is_member_session_completed(session))
141 {
142 GNUNET_CONTAINER_multihashmap_destroy (session->history);
143
144 struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store);
145
146 if ((session->contact) && (GNUNET_YES == decrease_contact_rc (session->contact)))
147 remove_store_contact (
148 store,
149 session->contact,
150 get_member_session_context(session)
151 );
152
153 session->contact = NULL;
154 }
155}
156
157static int
158iterate_copy_history (void *cls, const struct GNUNET_HashCode *key, void *value)
159{
160 struct GNUNET_MESSENGER_MemberSession *next = cls;
161
162 GNUNET_CONTAINER_multihashmap_put(next->history, key, (value? next : NULL),
163 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
164
165 return GNUNET_YES;
166}
167
168struct GNUNET_MESSENGER_MemberSession*
169switch_member_session (struct GNUNET_MESSENGER_MemberSession *session,
170 const struct GNUNET_MESSENGER_Message *message,
171 const struct GNUNET_HashCode *hash)
172{
173 if ((!session) || (!message) || (!hash))
174 return NULL;
175
176 GNUNET_assert((GNUNET_MESSENGER_KIND_ID == message->header.kind) ||
177 (GNUNET_MESSENGER_KIND_KEY == message->header.kind));
178
179 struct GNUNET_MESSENGER_MemberSession *next = GNUNET_new(struct GNUNET_MESSENGER_MemberSession);
180
181 if (GNUNET_MESSENGER_KIND_ID == message->header.kind)
182 next->member = add_store_member(session->member->store, &(message->body.id.id));
183 else
184 next->member = session->member;
185
186 if (GNUNET_MESSENGER_KIND_KEY == message->header.kind)
187 GNUNET_memcpy(&(next->public_key), &(message->body.key.key), sizeof(next->public_key));
188 else
189 GNUNET_memcpy(&(next->public_key), get_member_session_public_key(session), sizeof(next->public_key));
190
191 get_context_from_member (
192 get_member_session_key (next),
193 get_member_session_id (next),
194 &(next->context)
195 );
196
197 update_store_contact(
198 get_member_contact_store(next->member->store),
199 get_member_session_contact(session),
200 get_member_session_context(session),
201 get_member_session_context(next),
202 get_member_session_public_key(next)
203 );
204
205 next->contact = get_member_session_contact(session);
206
207 if (!(next->contact))
208 {
209 GNUNET_free(next);
210 return NULL;
211 }
212
213 increase_contact_rc (next->contact);
214
215 next->history = GNUNET_CONTAINER_multihashmap_create(
216 GNUNET_CONTAINER_multihashmap_size(session->history), GNUNET_NO
217 );
218
219 GNUNET_CONTAINER_multihashmap_iterate(session->history, iterate_copy_history, next);
220
221 init_list_messages(&(next->messages));
222 copy_list_messages(&(next->messages), &(session->messages));
223
224 session->next = next;
225 next->next = NULL;
226
227 session->closed = GNUNET_YES;
228 next->closed = GNUNET_NO;
229 next->completed = GNUNET_NO;
230
231 check_member_session_completion (session);
232
233 return next;
234}
235
236void
237destroy_member_session(struct GNUNET_MESSENGER_MemberSession* session)
238{
239 GNUNET_assert (session);
240
241 GNUNET_CONTAINER_multihashmap_destroy (session->history);
242
243 clear_list_messages (&(session->messages));
244
245 struct GNUNET_MESSENGER_Contact *contact = get_member_session_contact (session);
246
247 if ((contact) && (GNUNET_YES == decrease_contact_rc (contact)))
248 remove_store_contact (
249 get_member_contact_store(session->member->store),
250 contact,
251 get_member_session_context(session)
252 );
253
254 GNUNET_free(session);
255}
256
257int
258reset_member_session (struct GNUNET_MESSENGER_MemberSession* session,
259 const struct GNUNET_HashCode *hash)
260{
261 GNUNET_assert ((session) && (hash));
262
263 struct GNUNET_MESSENGER_ContactStore *store = get_member_contact_store(session->member->store);
264 struct GNUNET_MESSENGER_Contact *contact = get_store_contact(
265 store,
266 get_member_session_context (session),
267 get_member_session_public_key (session)
268 );
269
270 if (!contact)
271 return GNUNET_SYSERR;
272
273 if (contact == session->contact)
274 goto clear_messages;
275
276 session->contact = contact;
277 increase_contact_rc (session->contact);
278
279clear_messages:
280 clear_list_messages(&(session->messages));
281 add_to_list_messages(&(session->messages), hash);
282
283 session->next = NULL;
284 session->closed = GNUNET_NO;
285 session->completed = GNUNET_NO;
286
287 return GNUNET_OK;
288}
289
290void
291close_member_session (struct GNUNET_MESSENGER_MemberSession* session)
292{
293 GNUNET_assert (session);
294
295 session->closed = GNUNET_YES;
296 check_member_session_completion (session);
297}
298
299int
300is_member_session_closed (const struct GNUNET_MESSENGER_MemberSession* session)
301{
302 GNUNET_assert(session);
303
304 return session->closed;
305}
306
307int
308is_member_session_completed (const struct GNUNET_MESSENGER_MemberSession* session)
309{
310 GNUNET_assert(session);
311
312 return session->completed;
313}
314
315const struct GNUNET_HashCode*
316get_member_session_key (const struct GNUNET_MESSENGER_MemberSession* session)
317{
318 GNUNET_assert((session) && (session->member));
319
320 return get_member_store_key(session->member->store);
321}
322
323const struct GNUNET_ShortHashCode*
324get_member_session_id (const struct GNUNET_MESSENGER_MemberSession* session)
325{
326 GNUNET_assert(session);
327
328 return get_member_id(session->member);
329}
330
331const struct GNUNET_IDENTITY_PublicKey*
332get_member_session_public_key (const struct GNUNET_MESSENGER_MemberSession* session)
333{
334 GNUNET_assert(session);
335
336 return &(session->public_key);
337}
338
339const struct GNUNET_HashCode*
340get_member_session_context (const struct GNUNET_MESSENGER_MemberSession* session)
341{
342 GNUNET_assert(session);
343
344 return &(session->context);
345}
346
347struct GNUNET_MESSENGER_Contact*
348get_member_session_contact (struct GNUNET_MESSENGER_MemberSession* session)
349{
350 GNUNET_assert (session);
351
352 return session->contact;
353}
354
355int verify_member_session_as_sender (const struct GNUNET_MESSENGER_MemberSession *session,
356 const struct GNUNET_MESSENGER_Message *message,
357 const struct GNUNET_HashCode *hash)
358{
359 GNUNET_assert((session) && (message) && (hash));
360
361 if (GNUNET_YES == is_member_session_completed(session))
362 return GNUNET_SYSERR;
363
364 if (0 != GNUNET_memcmp(get_member_session_id(session), &(message->header.sender_id)))
365 return GNUNET_SYSERR;
366
367 return verify_message(message, hash, get_member_session_public_key(session));
368}
369
370int
371check_member_session_history (const struct GNUNET_MESSENGER_MemberSession *session,
372 const struct GNUNET_HashCode *hash, int ownership)
373{
374 GNUNET_assert((session) && (hash));
375
376 if (GNUNET_YES == ownership)
377 return (NULL != GNUNET_CONTAINER_multihashmap_get(session->history, hash)? GNUNET_YES : GNUNET_NO);
378 else
379 return GNUNET_CONTAINER_multihashmap_contains(session->history, hash);
380}
381
382static void
383update_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session,
384 const struct GNUNET_HashCode *hash, int ownership)
385{
386 GNUNET_assert ((session) && (hash));
387
388 if ((GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(session->history, hash, (GNUNET_YES == ownership? session : NULL),
389 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST)) && (session->next))
390 update_member_chain_history (session->next, hash, ownership);
391}
392
393void
394update_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
395 const struct GNUNET_MESSENGER_Message *message,
396 const struct GNUNET_HashCode *hash)
397{
398 GNUNET_assert((session) && (message) && (hash));
399
400 if (GNUNET_YES == is_member_session_completed(session))
401 return;
402
403 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Updating sessions history (%s) += (%s)\n",
404 GNUNET_sh2s(get_member_session_id(session)), GNUNET_h2s(hash));
405
406 if (GNUNET_OK == verify_member_session_as_sender (session, message, hash))
407 {
408 if (GNUNET_YES == is_message_session_bound (message))
409 add_to_list_messages(&(session->messages), hash);
410
411 update_member_chain_history (session, hash, GNUNET_YES);
412 }
413 else
414 update_member_chain_history (session, hash, GNUNET_NO);
415
416 if (GNUNET_YES == session->closed)
417 check_member_session_completion(session);
418}
419
420static void
421clear_member_chain_history (struct GNUNET_MESSENGER_MemberSession *session,
422 const struct GNUNET_HashCode *hash)
423{
424 GNUNET_assert ((session) && (hash));
425
426 if ((0 < GNUNET_CONTAINER_multihashmap_remove_all(session->history, hash)) && (session->next))
427 clear_member_session_history(session->next, hash);
428}
429
430void
431clear_member_session_history (struct GNUNET_MESSENGER_MemberSession *session,
432 const struct GNUNET_HashCode *hash)
433{
434 GNUNET_assert((session) && (hash));
435
436 clear_member_chain_history (session, hash);
437}
438
439struct GNUNET_MESSENGER_MemberSessionHistoryEntry
440{
441 struct GNUNET_HashCode hash;
442 unsigned char ownership;
443};
444
445static void
446load_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, const char *path)
447{
448 GNUNET_assert((session) && (path));
449
450 if (GNUNET_YES != GNUNET_DISK_file_test (path))
451 return;
452
453 enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
454
455 struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
456 path, GNUNET_DISK_OPEN_READ, permission
457 );
458
459 if (!handle)
460 return;
461
462 GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
463
464 struct GNUNET_MESSENGER_MemberSessionHistoryEntry entry;
465 ssize_t len;
466
467 int status;
468
469 do {
470 len = GNUNET_DISK_file_read(handle, &(entry.hash), sizeof(entry.hash));
471
472 if (len != sizeof(entry.hash))
473 break;
474
475 len = GNUNET_DISK_file_read(handle, &(entry.ownership), sizeof(entry.ownership));
476
477 if (len != sizeof(entry.ownership))
478 break;
479
480 status = GNUNET_CONTAINER_multihashmap_put(session->history, &(entry.hash), (entry.ownership? session : NULL),
481 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
482 } while (status == GNUNET_OK);
483
484 GNUNET_DISK_file_close(handle);
485}
486
487void
488load_member_session (struct GNUNET_MESSENGER_Member *member, const char *directory)
489{
490 GNUNET_assert ((member) && (directory));
491
492 char *config_file;
493 GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
494
495 struct GNUNET_MESSENGER_MemberSession *session = NULL;
496
497 if (GNUNET_YES != GNUNET_DISK_file_test (config_file))
498 goto free_config;
499
500 struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
501
502 if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file))
503 {
504 char *key_data;
505
506 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", "key", &key_data))
507 goto destroy_config;
508
509 struct GNUNET_IDENTITY_PublicKey key;
510
511 enum GNUNET_GenericReturnValue key_return = GNUNET_IDENTITY_public_key_from_string(key_data, &key);
512
513 GNUNET_free(key_data);
514
515 if (GNUNET_OK != key_return)
516 goto destroy_config;
517
518 session = create_member_session(member, &key);
519 }
520
521destroy_config:
522 GNUNET_CONFIGURATION_destroy (cfg);
523
524free_config:
525 GNUNET_free(config_file);
526
527 if (!session)
528 return;
529
530 char *history_file;
531 GNUNET_asprintf (&history_file, "%s%s", directory, "history.map");
532
533 load_member_session_history (session, history_file);
534 GNUNET_free(history_file);
535
536 char *messages_file;
537 GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list");
538
539 load_list_messages(&(session->messages), messages_file);
540 GNUNET_free(messages_file);
541
542 add_member_session(member, session);
543}
544
545static struct GNUNET_MESSENGER_MemberSession*
546get_cycle_safe_next_session (struct GNUNET_MESSENGER_MemberSession *session, struct GNUNET_MESSENGER_MemberSession *next)
547{
548 if (!next)
549 return NULL;
550
551 struct GNUNET_MESSENGER_MemberSession *check = next;
552
553 do {
554 if (check == session)
555 return NULL;
556
557 check = check->next;
558 } while (check);
559
560 return next;
561}
562
563void
564load_member_session_next (struct GNUNET_MESSENGER_MemberSession *session, const char *directory)
565{
566 GNUNET_assert ((session) && (directory));
567
568 char *config_file;
569 GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
570
571 if (GNUNET_YES != GNUNET_DISK_file_test (config_file))
572 goto free_config;
573
574 struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
575
576 if (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, config_file))
577 {
578 char *key_data;
579
580 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string(cfg, "session", "next_key", &key_data))
581 goto destroy_config;
582
583 struct GNUNET_IDENTITY_PublicKey next_key;
584
585 enum GNUNET_GenericReturnValue key_return = GNUNET_IDENTITY_public_key_from_string(key_data, &next_key);
586
587 GNUNET_free(key_data);
588
589 if (GNUNET_OK != key_return)
590 goto destroy_config;
591
592 struct GNUNET_ShortHashCode next_id;
593
594 if (GNUNET_OK != GNUNET_CONFIGURATION_get_data (cfg, "session", "next_id", &next_id, sizeof(next_id)))
595 goto destroy_config;
596
597 struct GNUNET_MESSENGER_Member *member = get_store_member(session->member->store, &next_id);
598
599 session->next = get_cycle_safe_next_session(
600 session, member? get_member_session (member, &next_key) : NULL
601 );
602 }
603
604destroy_config:
605 GNUNET_CONFIGURATION_destroy (cfg);
606
607free_config:
608 GNUNET_free(config_file);
609}
610
611static int
612iterate_save_member_session_history_hentries (void *cls, const struct GNUNET_HashCode *key, void *value)
613{
614 struct GNUNET_DISK_FileHandle *handle = cls;
615 unsigned char ownership = value? GNUNET_YES : GNUNET_NO;
616
617 GNUNET_DISK_file_write(handle, key, sizeof(*key));
618 GNUNET_DISK_file_write(handle, &ownership, sizeof(ownership));
619
620 return GNUNET_YES;
621}
622
623static void
624save_member_session_history (struct GNUNET_MESSENGER_MemberSession *session, const char *path)
625{
626 GNUNET_assert((session) && (path));
627
628 enum GNUNET_DISK_AccessPermissions permission = (GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
629
630 struct GNUNET_DISK_FileHandle *handle = GNUNET_DISK_file_open(
631 path, GNUNET_DISK_OPEN_CREATE | GNUNET_DISK_OPEN_WRITE, permission
632 );
633
634 if (!handle)
635 return;
636
637 GNUNET_DISK_file_seek(handle, 0, GNUNET_DISK_SEEK_SET);
638
639 GNUNET_CONTAINER_multihashmap_iterate(
640 session->history,
641 iterate_save_member_session_history_hentries,
642 handle
643 );
644
645 GNUNET_DISK_file_sync(handle);
646 GNUNET_DISK_file_close(handle);
647}
648
649void
650save_member_session (struct GNUNET_MESSENGER_MemberSession *session, const char *directory)
651{
652 GNUNET_assert ((session) && (directory));
653
654 char *config_file;
655 GNUNET_asprintf (&config_file, "%s%s", directory, "session.cfg");
656
657 struct GNUNET_CONFIGURATION_Handle *cfg = GNUNET_CONFIGURATION_create ();
658
659 char *key_data = GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session));
660
661 if (key_data)
662 {
663 GNUNET_CONFIGURATION_set_value_string (cfg, "session", "key", key_data);
664
665 GNUNET_free(key_data);
666 }
667
668 if (session->next)
669 {
670 const struct GNUNET_ShortHashCode *next_id = get_member_session_id(session->next);
671
672 char *next_id_data = GNUNET_STRINGS_data_to_string_alloc (next_id, sizeof(*next_id));
673
674 if (next_id_data)
675 {
676 GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_id", next_id_data);
677
678 GNUNET_free(next_id_data);
679 }
680
681 key_data = GNUNET_IDENTITY_public_key_to_string(get_member_session_public_key(session->next));
682
683 if (key_data)
684 {
685 GNUNET_CONFIGURATION_set_value_string (cfg, "session", "next_key", key_data);
686
687 GNUNET_free(key_data);
688 }
689 }
690
691 GNUNET_CONFIGURATION_write (cfg, config_file);
692 GNUNET_CONFIGURATION_destroy (cfg);
693
694 GNUNET_free(config_file);
695
696 char *history_file;
697 GNUNET_asprintf (&history_file, "%s%s", directory, "history.map");
698
699 save_member_session_history (session, history_file);
700 GNUNET_free(history_file);
701
702 char *messages_file;
703 GNUNET_asprintf (&messages_file, "%s%s", directory, "messages.list");
704
705 save_list_messages(&(session->messages), messages_file);
706 GNUNET_free(messages_file);
707}