aboutsummaryrefslogtreecommitdiff
path: root/src/social
diff options
context:
space:
mode:
Diffstat (limited to 'src/social')
-rw-r--r--src/social/.gitignore3
-rw-r--r--src/social/Makefile.am79
-rw-r--r--src/social/gnunet-service-social.c3760
-rw-r--r--src/social/gnunet-social.c1411
-rw-r--r--src/social/social.conf.in15
-rw-r--r--src/social/social.h292
-rw-r--r--src/social/social_api.c2827
-rw-r--r--src/social/test_social.c1449
-rw-r--r--src/social/test_social.conf19
9 files changed, 9855 insertions, 0 deletions
diff --git a/src/social/.gitignore b/src/social/.gitignore
new file mode 100644
index 0000000..875aa11
--- /dev/null
+++ b/src/social/.gitignore
@@ -0,0 +1,3 @@
1gnunet-social
2gnunet-service-social
3test_social
diff --git a/src/social/Makefile.am b/src/social/Makefile.am
new file mode 100644
index 0000000..94a9ba1
--- /dev/null
+++ b/src/social/Makefile.am
@@ -0,0 +1,79 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4pkgcfgdir= $(pkgdatadir)/config.d/
5
6libexecdir= $(pkglibdir)/libexec/
7
8pkgcfg_DATA = \
9 social.conf
10
11
12if MINGW
13 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
14endif
15
16if USE_COVERAGE
17 AM_CFLAGS = --coverage -O0
18 XLIB = -lgcov
19endif
20
21lib_LTLIBRARIES = libgnunetsocial.la
22
23libgnunetsocial_la_SOURCES = \
24 social_api.c social.h
25libgnunetsocial_la_LIBADD = \
26 $(top_builddir)/src/util/libgnunetutil.la \
27 $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
28 $(GN_LIBINTL) $(XLIB)
29libgnunetsocial_la_LDFLAGS = \
30 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
31 -version-info 0:0:0
32
33bin_PROGRAMS = \
34 gnunet-social
35
36libexec_PROGRAMS = \
37 gnunet-service-social
38
39gnunet_social_SOURCES = \
40 gnunet-social.c
41gnunet_social_LDADD = \
42 libgnunetsocial.la \
43 $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
44 $(top_builddir)/src/util/libgnunetutil.la
45
46gnunet_service_social_SOURCES = \
47 gnunet-service-social.c
48gnunet_service_social_LDADD = \
49 $(top_builddir)/src/util/libgnunetutil.la \
50 $(top_builddir)/src/statistics/libgnunetstatistics.la \
51 $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
52 $(top_builddir)/src/psyc/libgnunetpsyc.la \
53 $(top_builddir)/src/identity/libgnunetidentity.la \
54 $(top_builddir)/src/gns/libgnunetgns.la \
55 $(top_builddir)/src/namestore/libgnunetnamestore.la \
56 $(GN_LIBINTL)
57
58
59if HAVE_TESTING
60check_PROGRAMS = \
61 test_social
62endif
63
64if ENABLE_TEST_RUN
65AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
66TESTS = $(check_PROGRAMS)
67endif
68
69test_social_SOURCES = \
70 test_social.c
71test_social_LDADD = \
72 libgnunetsocial.la \
73 $(top_builddir)/src/testing/libgnunettesting.la \
74 $(top_builddir)/src/util/libgnunetutil.la \
75 $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
76 $(top_builddir)/src/identity/libgnunetidentity.la
77
78EXTRA_DIST = \
79 test_social.conf
diff --git a/src/social/gnunet-service-social.c b/src/social/gnunet-service-social.c
new file mode 100644
index 0000000..33fabae
--- /dev/null
+++ b/src/social/gnunet-service-social.c
@@ -0,0 +1,3760 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file social/gnunet-service-social.c
23 * @brief Social service
24 * @author Gabor X Toth
25 */
26
27#include <inttypes.h>
28#include <strings.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_constants.h"
33#include "gnunet_protocols.h"
34#include "gnunet_identity_service.h"
35#include "gnunet_namestore_service.h"
36#include "gnunet_gns_service.h"
37#include "gnunet_statistics_service.h"
38#include "gnunet_psyc_service.h"
39#include "gnunet_psyc_util_lib.h"
40#include "gnunet_social_service.h"
41#include "social.h"
42
43
44/**
45 * Handle to our current configuration.
46 */
47static const struct GNUNET_CONFIGURATION_Handle *cfg;
48
49/**
50 * Service handle.
51 */
52static struct GNUNET_SERVICE_Handle *service;
53
54/* Handles to other services */
55static struct GNUNET_IDENTITY_Handle *id;
56static struct GNUNET_GNS_Handle *gns;
57static struct GNUNET_NAMESTORE_Handle *namestore;
58static struct GNUNET_STATISTICS_Handle *stats;
59
60/**
61 * ID of this peer.
62 */
63static struct GNUNET_PeerIdentity this_peer;
64
65/**
66 * All connected hosts.
67 * H(place_pub_key) -> struct Host
68 */
69static struct GNUNET_CONTAINER_MultiHashMap *hosts;
70
71/**
72 * All connected guests.
73 * H(place_pub_key) -> struct Guest
74 */
75static struct GNUNET_CONTAINER_MultiHashMap *guests;
76
77/**
78 * Connected guests per place.
79 * H(place_pub_key) -> ego_pub_key -> struct Guest
80 */
81static struct GNUNET_CONTAINER_MultiHashMap *place_guests;
82
83/**
84 * Places entered as host or guest.
85 * H(place_pub_key) -> struct HostEnterRequest OR struct GuestEnterRequest
86 */
87static struct GNUNET_CONTAINER_MultiHashMap *places;
88
89/**
90 * Places entered per application.
91 * H(app_id) -> H(place_pub_key) -> NULL
92 */
93static struct GNUNET_CONTAINER_MultiHashMap *apps_places;
94
95/**
96 * Application subscriptions per place.
97 * H(place_pub_key) -> H(app_id)
98 */
99//static struct GNUNET_CONTAINER_MultiHashMap *places_apps;
100
101/**
102 * Connected applications.
103 * H(app_id) -> struct Application
104 */
105static struct GNUNET_CONTAINER_MultiHashMap *apps;
106
107/**
108 * All egos.
109 * H(ego_pub_key) -> struct Ego
110 */
111static struct GNUNET_CONTAINER_MultiHashMap *egos;
112
113/**
114 * Directory for storing social data.
115 * Default: $GNUNET_DATA_HOME/social
116 */
117static char *dir_social;
118
119/**
120 * Directory for storing place data.
121 * $dir_social/places
122 */
123static char *dir_places;
124
125/**
126 * Directory for storing app data.
127 * $dir_social/apps
128 */
129static char *dir_apps;
130
131
132/**
133 * Message fragment transmission queue.
134 */
135struct FragmentTransmitQueue
136{
137 struct FragmentTransmitQueue *prev;
138 struct FragmentTransmitQueue *next;
139
140 struct GNUNET_SERVICE_Client *client;
141
142 /**
143 * Pointer to the next message part inside the data after this struct.
144 */
145 struct GNUNET_MessageHeader *next_part;
146
147 /**
148 * Size of message.
149 */
150 uint16_t size;
151
152 /**
153 * @see enum GNUNET_PSYC_MessageState
154 */
155 uint8_t state;
156
157 /* Followed by one or more message parts. */
158};
159
160
161/**
162 * Message transmission queue.
163 */
164struct MessageTransmitQueue
165{
166 struct MessageTransmitQueue *prev;
167 struct MessageTransmitQueue *next;
168
169 struct FragmentTransmitQueue *frags_head;
170 struct FragmentTransmitQueue *frags_tail;
171
172 struct GNUNET_SERVICE_Client *client;
173};
174
175/**
176 * List of connected clients.
177 */
178struct ClientListItem
179{
180 struct ClientListItem *prev;
181 struct ClientListItem *next;
182
183 struct GNUNET_SERVICE_Client *client;
184};
185
186
187/**
188 * Common part of the client context for both a host and guest.
189 */
190struct Place
191{
192 struct ClientListItem *clients_head;
193 struct ClientListItem *clients_tail;
194
195 struct MessageTransmitQueue *tmit_msgs_head;
196 struct MessageTransmitQueue *tmit_msgs_tail;
197
198 struct GNUNET_PSYC_Channel *channel;
199
200 /**
201 * Private key of home in case of a host.
202 */
203 struct GNUNET_CRYPTO_EddsaPublicKey key;
204
205 /**
206 * Public key of place.
207 */
208 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
209
210 /**
211 * Hash of @a pub_key.
212 */
213 struct GNUNET_HashCode pub_key_hash;
214
215 /**
216 * Private key of ego.
217 */
218 struct GNUNET_CRYPTO_EcdsaPrivateKey ego_key;
219
220 /**
221 * Public key of ego.
222 */
223 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
224
225 /**
226 * Hash of @a ego_pub_key.
227 */
228 struct GNUNET_HashCode ego_pub_hash;
229
230 /**
231 * Slicer for processing incoming messages.
232 */
233 struct GNUNET_PSYC_Slicer *slicer;
234
235 /**
236 * Last message ID received for the place.
237 * 0 if there is no such message.
238 */
239 uint64_t max_message_id;
240
241 /**
242 * Offset where the file is currently being written.
243 */
244 uint64_t file_offset;
245
246 /**
247 * Whether or not to save the file (#GNUNET_YES or #GNUNET_NO)
248 */
249 uint8_t file_save;
250
251 /**
252 * Is this place ready to receive messages from client?
253 * #GNUNET_YES or #GNUNET_NO
254 */
255 uint8_t is_ready;
256
257 /**
258 * Is the client disconnecting?
259 * #GNUNET_YES or #GNUNET_NO
260 */
261 uint8_t is_disconnecting;
262
263 /**
264 * Is this a host (#GNUNET_YES), or guest (#GNUNET_NO)?
265 */
266 uint8_t is_host;
267
268 union {
269 struct Host *host;
270 struct Guest *guest;
271 };
272};
273
274
275/**
276 * Client context for a host.
277 */
278struct Host
279{
280 /**
281 * Place struct common for Host and Guest
282 */
283 struct Place place;
284
285 /**
286 * Handle for the multicast origin.
287 */
288 struct GNUNET_PSYC_Master *master;
289
290 /**
291 * Transmit handle for multicast.
292 */
293 struct GNUNET_PSYC_MasterTransmitHandle *tmit_handle;
294
295 /**
296 * Incoming join requests.
297 * guest_key -> struct GNUNET_PSYC_JoinHandle *
298 */
299 struct GNUNET_CONTAINER_MultiHashMap *join_reqs;
300
301 /**
302 * Messages being relayed.
303 */
304 struct GNUNET_CONTAINER_MultiHashMap *relay_msgs;
305
306 /**
307 * @see enum GNUNET_PSYC_Policy
308 */
309 enum GNUNET_PSYC_Policy policy;
310};
311
312
313/**
314 * Client context for a guest.
315 */
316struct Guest
317{
318 /**
319 * Place struct common for Host and Guest.
320 */
321 struct Place place;
322
323 /**
324 * Handle for the PSYC slave.
325 */
326 struct GNUNET_PSYC_Slave *slave;
327
328 /**
329 * Transmit handle for multicast.
330 */
331 struct GNUNET_PSYC_SlaveTransmitHandle *tmit_handle;
332
333 /**
334 * Peer identity of the origin.
335 */
336 struct GNUNET_PeerIdentity origin;
337
338 /**
339 * Number of items in @a relays.
340 */
341 uint32_t relay_count;
342
343 /**
344 * Relays that multicast can use to connect.
345 */
346 struct GNUNET_PeerIdentity *relays;
347
348 /**
349 * Join request to be transmitted to the master on join.
350 */
351 struct GNUNET_MessageHeader *join_req; // FIXME: not used!
352
353 /**
354 * Join decision received from PSYC.
355 */
356 struct GNUNET_PSYC_JoinDecisionMessage *join_dcsn;
357
358 /**
359 * Join flags for the PSYC service.
360 */
361 enum GNUNET_PSYC_SlaveJoinFlags join_flags;
362};
363
364
365/**
366 * Context for a client.
367 */
368struct Client
369{
370 /**
371 * Client handle.
372 */
373 struct GNUNET_SERVICE_Client *client;
374
375 /**
376 * Place where the client entered.
377 */
378 struct Place *place;
379
380 /**
381 * Message queue for the message currently being transmitted
382 * by this client.
383 */
384 struct MessageTransmitQueue *tmit_msg;
385
386 /**
387 * ID for application clients.
388 */
389 char *app_id;
390};
391
392
393struct Application
394{
395 struct ClientListItem *clients_head;
396 struct ClientListItem *clients_tail;
397};
398
399
400struct Ego {
401 struct GNUNET_CRYPTO_EcdsaPrivateKey key;
402 char *name;
403};
404
405
406struct OperationClosure
407{
408 struct Client *client;
409 uint64_t op_id;
410 uint32_t flags;
411};
412
413
414static int
415psyc_transmit_message (struct Place *plc);
416
417
418/**
419 * Clean up place data structures after a client disconnected.
420 *
421 * @param cls the `struct Place` to clean up
422 */
423static void
424cleanup_place (void *cls);
425
426
427static struct MessageTransmitQueue *
428psyc_transmit_queue_message (struct Place *plc,
429 struct GNUNET_SERVICE_Client *client,
430 size_t data_size,
431 const void *data,
432 uint16_t first_ptype, uint16_t last_ptype,
433 struct MessageTransmitQueue *tmit_msg);
434
435
436static int
437place_entry_cleanup (void *cls,
438 const struct GNUNET_HashCode *key,
439 void *value)
440{
441 struct Place *plc = value;
442
443 cleanup_place (plc);
444 return GNUNET_YES;
445}
446
447
448/**
449 * Task run during shutdown.
450 *
451 * @param cls unused
452 */
453static void
454shutdown_task (void *cls)
455{
456 GNUNET_CONTAINER_multihashmap_iterate (hosts, place_entry_cleanup, NULL);
457 GNUNET_CONTAINER_multihashmap_iterate (guests, place_entry_cleanup, NULL);
458
459 if (NULL != id)
460 {
461 GNUNET_IDENTITY_disconnect (id);
462 id = NULL;
463 }
464 if (NULL != namestore)
465 {
466 GNUNET_NAMESTORE_disconnect (namestore);
467 namestore = NULL;
468 }
469 if (NULL != gns)
470 {
471 GNUNET_GNS_disconnect (gns);
472 gns = NULL;
473 }
474 if (NULL != stats)
475 {
476 GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
477 stats = NULL;
478 }
479}
480
481
482/**
483 * Clean up host data structures after a client disconnected.
484 */
485static void
486cleanup_host (struct Host *hst)
487{
488 struct Place *plc = &hst->place;
489
490 GNUNET_CONTAINER_multihashmap_destroy (hst->join_reqs);
491 GNUNET_CONTAINER_multihashmap_destroy (hst->relay_msgs);
492 GNUNET_CONTAINER_multihashmap_remove (hosts, &plc->pub_key_hash, plc);
493}
494
495
496/**
497 * Clean up guest data structures after a client disconnected.
498 */
499static void
500cleanup_guest (struct Guest *gst)
501{
502 struct Place *plc = &gst->place;
503 struct GNUNET_CONTAINER_MultiHashMap *
504 plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests,
505 &plc->pub_key_hash);
506 if (NULL != plc_gst)
507 {
508 GNUNET_CONTAINER_multihashmap_remove (plc_gst, &plc->ego_pub_hash, gst);
509
510 if (0 == GNUNET_CONTAINER_multihashmap_size (plc_gst))
511 {
512 GNUNET_CONTAINER_multihashmap_remove (place_guests, &plc->pub_key_hash,
513 plc_gst);
514 GNUNET_CONTAINER_multihashmap_destroy (plc_gst);
515 }
516 }
517 GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, gst);
518 if (NULL != gst->join_req)
519 GNUNET_free (gst->join_req);
520 if (NULL != gst->relays)
521 GNUNET_free (gst->relays);
522 GNUNET_CONTAINER_multihashmap_remove (guests, &plc->pub_key_hash, plc);
523}
524
525
526/**
527 * Clean up place data structures after a client disconnected.
528 *
529 * @param cls the `struct Place` to clean up
530 */
531static void
532cleanup_place (void *cls)
533{
534 struct Place *plc = cls;
535
536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537 "cleaning up place %s\n",
538 GNUNET_h2s (&plc->pub_key_hash));
539
540 (GNUNET_YES == plc->is_host)
541 ? cleanup_host ((struct Host *) plc)
542 : cleanup_guest ((struct Guest *) plc);
543
544 GNUNET_PSYC_slicer_destroy (plc->slicer);
545 GNUNET_free (plc);
546}
547
548
549/**
550 * Called whenever a client is disconnected.
551 * Frees our resources associated with that client.
552 *
553 * @param cls closure
554 * @param client identification of the client
555 * @param app_ctx must match @a client
556 */
557static void
558client_notify_disconnect (void *cls,
559 struct GNUNET_SERVICE_Client *client,
560 void *app_ctx)
561{
562 struct Client *c = app_ctx;
563 struct Place *plc = c->place;
564
565 if (NULL != c->app_id)
566 GNUNET_free (c->app_id);
567
568 GNUNET_free (c);
569
570 if (NULL == plc)
571 return; // application client, nothing to do
572
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "%p Client (%s) disconnected from place %s\n",
575 plc, (GNUNET_YES == plc->is_host) ? "host" : "guest",
576 GNUNET_h2s (&plc->pub_key_hash));
577
578 struct ClientListItem *cli = plc->clients_head;
579 while (NULL != cli)
580 {
581 if (cli->client == client)
582 {
583 GNUNET_CONTAINER_DLL_remove (plc->clients_head,
584 plc->clients_tail,
585 cli);
586 GNUNET_free (cli);
587 break;
588 }
589 cli = cli->next;
590 }
591 if (GNUNET_YES == plc->is_disconnecting)
592 {
593 GNUNET_PSYC_slicer_destroy (plc->slicer);
594 GNUNET_free (plc);
595 }
596}
597
598
599/**
600 * A new client connected.
601 *
602 * @param cls NULL
603 * @param client client to add
604 * @param mq message queue for @a client
605 * @return @a client
606 */
607static void *
608client_notify_connect (void *cls,
609 struct GNUNET_SERVICE_Client *client,
610 struct GNUNET_MQ_Handle *mq)
611{
612 struct Client *c = GNUNET_new (struct Client);
613
614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
615 "Client %p connected with queue %p\n",
616 client,
617 mq);
618 c->client = client;
619 return c;
620}
621
622
623/**
624 * Send message to all clients connected to a place and
625 * takes care of freeing @env.
626 */
627static void
628place_send_msg (const struct Place *plc,
629 struct GNUNET_MQ_Envelope *env)
630{
631 struct ClientListItem *cli = plc->clients_head;
632
633 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
634 "%p Sending message to clients of place.\n", plc);
635 while (NULL != cli)
636 {
637 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
638 "Sending message to client %p\n",
639 cli);
640 GNUNET_MQ_send_copy (GNUNET_SERVICE_client_get_mq (cli->client),
641 env);
642 cli = cli->next;
643 }
644 GNUNET_MQ_discard (env);
645}
646
647
648static void
649place_send_leave_ack (struct Place *plc)
650{
651 struct GNUNET_MQ_Envelope *env;
652
653 for (struct ClientListItem *cli = plc->clients_head;
654 NULL != cli;
655 cli = cli->next)
656 {
657 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK);
658 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (cli->client),
659 env);
660 }
661}
662
663
664/**
665 * Send a result code back to the client.
666 *
667 * @param client
668 * Client that should receive the result code.
669 * @param result_code
670 * Code to transmit.
671 * @param op_id
672 * Operation ID in network byte order.
673 * @param data
674 * Data payload or NULL.
675 * @param data_size
676 * Size of @a data.
677 */
678static void
679client_send_result (struct GNUNET_SERVICE_Client *client, uint64_t op_id,
680 int64_t result_code, const void *data, uint16_t data_size)
681{
682 struct GNUNET_MQ_Envelope *env;
683 struct GNUNET_OperationResultMessage *res;
684
685 env = GNUNET_MQ_msg_extra (res,
686 data_size,
687 GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE);
688 res->result_code = GNUNET_htonll (result_code);
689 res->op_id = op_id;
690 if (0 < data_size)
691 GNUNET_memcpy (&res[1], data, data_size);
692 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
693 "%p Sending result to client for operation #%" PRIu64 ": "
694 "%" PRId64 " (size: %u)\n",
695 client, GNUNET_ntohll (op_id), result_code, data_size);
696 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
697}
698
699
700static void
701client_send_host_enter_ack (struct GNUNET_SERVICE_Client *client,
702 struct Host *hst, uint32_t result)
703{
704 struct GNUNET_MQ_Envelope *env;
705 struct HostEnterAck *hack;
706 struct Place *plc = &hst->place;
707
708 env = GNUNET_MQ_msg (hack,
709 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK);
710 hack->result_code = htonl (result);
711 hack->max_message_id = GNUNET_htonll (plc->max_message_id);
712 hack->place_pub_key = plc->pub_key;
713
714 if (NULL != client)
715 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
716 env);
717 else
718 place_send_msg (plc, env);
719}
720
721
722/**
723 * Called after a PSYC master is started.
724 */
725static void
726psyc_master_started (void *cls, int result, uint64_t max_message_id)
727{
728 struct Host *hst = cls;
729 struct Place *plc = &hst->place;
730 plc->max_message_id = max_message_id;
731 plc->is_ready = GNUNET_YES;
732
733 client_send_host_enter_ack (NULL, hst, result);
734}
735
736
737/**
738 * Called when a PSYC master receives a join request.
739 */
740static void
741psyc_recv_join_request (void *cls,
742 const struct GNUNET_PSYC_JoinRequestMessage *req,
743 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
744 const struct GNUNET_PSYC_Message *join_msg,
745 struct GNUNET_PSYC_JoinHandle *jh)
746{
747 struct Host *hst = cls;
748 struct GNUNET_HashCode slave_key_hash;
749 GNUNET_CRYPTO_hash (slave_key, sizeof (*slave_key), &slave_key_hash);
750 GNUNET_CONTAINER_multihashmap_put (hst->join_reqs, &slave_key_hash, jh,
751 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
752 place_send_msg (&hst->place,
753 GNUNET_MQ_msg_copy (&req->header));
754}
755
756
757/**
758 * Called after a PSYC slave is connected.
759 */
760static void
761psyc_slave_connected (void *cls, int result, uint64_t max_message_id)
762{
763 struct GNUNET_PSYC_CountersResultMessage *res;
764 struct GNUNET_MQ_Envelope *env;
765 struct Guest *gst = cls;
766 struct Place *plc = &gst->place;
767
768 plc->max_message_id = max_message_id;
769 plc->is_ready = GNUNET_YES;
770 env = GNUNET_MQ_msg (res,
771 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
772 res->result_code =
773 (result != GNUNET_SYSERR) ? htonl (GNUNET_OK) : htonl (GNUNET_SYSERR);
774 res->max_message_id = GNUNET_htonll (plc->max_message_id);
775 place_send_msg (plc, env);
776}
777
778
779static void
780slave_parted_after_join_decision (void *cls)
781{
782 struct Guest *gst = cls;
783
784 GNUNET_assert (NULL != gst->join_dcsn);
785 place_send_msg (&gst->place, GNUNET_MQ_msg_copy (&gst->join_dcsn->header));
786}
787
788
789/**
790 * Called when a PSYC slave receives a join decision.
791 */
792static void
793psyc_recv_join_dcsn (void *cls,
794 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn,
795 int is_admitted,
796 const struct GNUNET_PSYC_Message *join_msg)
797{
798 struct Guest *gst = cls;
799
800 gst->join_dcsn = GNUNET_malloc (dcsn->header.size);
801 GNUNET_memcpy (gst->join_dcsn,
802 dcsn,
803 dcsn->header.size);
804 if (GNUNET_NO == is_admitted)
805 {
806 GNUNET_PSYC_slave_part (gst->slave,
807 GNUNET_NO,
808 &slave_parted_after_join_decision,
809 gst);
810 gst->slave = NULL;
811 return;
812 }
813 place_send_msg (&gst->place, GNUNET_MQ_msg_copy (&gst->join_dcsn->header));
814}
815
816
817/**
818 * Called when a PSYC master or slave receives a message.
819 */
820static void
821psyc_recv_message (void *cls,
822 const struct GNUNET_PSYC_MessageHeader *msg)
823{
824 struct Place *plc = cls;
825
826 char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&msg->slave_pub_key);
827 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
828 "%p Received PSYC message of size %u from %s.\n",
829 plc, ntohs (msg->header.size), str);
830 GNUNET_free (str);
831
832 GNUNET_PSYC_slicer_message (plc->slicer, msg);
833
834 place_send_msg (plc, GNUNET_MQ_msg_copy (&msg->header));
835}
836
837
838/**
839 * Relay a message part received from a guest to the the place.
840 *
841 * @param hst
842 * Host.
843 * @param pmsg
844 * Message part.
845 * @param nym_pub_key
846 * Nym the message is received from.
847 */
848static void
849host_relay_message_part (struct Host *hst,
850 const struct GNUNET_MessageHeader *pmsg,
851 const struct GNUNET_CRYPTO_EcdsaPublicKey *nym_pub_key)
852{
853 /* separate queue per nym */
854 struct GNUNET_HashCode nym_pub_hash;
855 GNUNET_CRYPTO_hash (nym_pub_key, sizeof (*nym_pub_key), &nym_pub_hash);
856
857 struct MessageTransmitQueue *
858 tmit_msg = GNUNET_CONTAINER_multihashmap_get (hst->relay_msgs, &nym_pub_hash);
859
860 uint16_t ptype = ntohs (pmsg->type);
861
862 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == ptype)
863 {
864 /* FIXME: last message was unfinished, cancel & remove from queue */
865 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
866 "FIXME: last message was unfinished.\n");
867 }
868
869 tmit_msg = psyc_transmit_queue_message (&hst->place, NULL, ntohs (pmsg->size),
870 pmsg, ptype, ptype, tmit_msg);
871
872 switch (ptype)
873 {
874 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
875 GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_put
876 (hst->relay_msgs, &nym_pub_hash, tmit_msg,
877 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
878 break;
879 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
880 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
881 GNUNET_assert (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove
882 (hst->relay_msgs, &nym_pub_hash, tmit_msg));
883 break;
884 }
885}
886
887
888/**
889 * Received a method to be relayed from a guest.
890 */
891static void
892place_recv_relay_method (void *cls,
893 const struct GNUNET_PSYC_MessageHeader *msg,
894 const struct GNUNET_PSYC_MessageMethod *meth,
895 uint64_t message_id,
896 const char *method_name)
897{
898 struct Place *plc = cls;
899
900 if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
901 && GNUNET_YES == plc->is_host)
902 {
903 struct Host *hst = cls;
904 host_relay_message_part (hst, &meth->header, &msg->slave_pub_key);
905 }
906}
907
908
909/**
910 * Received a modifier to be relayed from a guest.
911 */
912static void
913place_recv_relay_modifier (void *cls,
914 const struct GNUNET_PSYC_MessageHeader *msg,
915 const struct GNUNET_MessageHeader *pmsg,
916 uint64_t message_id,
917 enum GNUNET_PSYC_Operator oper,
918 const char *name,
919 const void *value,
920 uint16_t value_size,
921 uint16_t full_value_size)
922{
923 struct Place *plc = cls;
924
925 if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
926 && GNUNET_YES == plc->is_host)
927 {
928 struct Host *hst = cls;
929 host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
930 }
931}
932
933/**
934 * Received a data fragment to be relayed from a guest.
935 */
936static void
937place_recv_relay_data (void *cls,
938 const struct GNUNET_PSYC_MessageHeader *msg,
939 const struct GNUNET_MessageHeader *pmsg,
940 uint64_t message_id,
941 const void *data,
942 uint16_t data_size)
943{
944 struct Place *plc = cls;
945
946 if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
947 && GNUNET_YES == plc->is_host)
948 {
949 struct Host *hst = cls;
950 host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
951 }
952}
953
954
955/**
956 * Received end of message to be relayed from a guest.
957 */
958static void
959place_recv_relay_eom (void *cls,
960 const struct GNUNET_PSYC_MessageHeader *msg,
961 const struct GNUNET_MessageHeader *pmsg,
962 uint64_t message_id,
963 uint8_t is_cancelled)
964{
965 struct Place *plc = cls;
966
967 if (GNUNET_PSYC_MESSAGE_REQUEST & ntohs (msg->flags)
968 && GNUNET_YES == plc->is_host)
969 {
970 struct Host *hst = cls;
971 host_relay_message_part (hst, pmsg, &msg->slave_pub_key);
972 }
973}
974
975
976/**
977 * Received a method to be saved to disk.
978 *
979 * Create a new file for writing the data part of the message into,
980 * if the file does not yet exist.
981 */
982static void
983place_recv_save_method (void *cls,
984 const struct GNUNET_PSYC_MessageHeader *msg,
985 const struct GNUNET_PSYC_MessageMethod *meth,
986 uint64_t message_id,
987 const char *method_name)
988{
989 struct Place *plc = cls;
990 plc->file_offset = 0;
991 plc->file_save = GNUNET_NO;
992
993 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&plc->pub_key);
994 char *filename = NULL;
995 GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%" PRIu64 ".part",
996 dir_social, DIR_SEPARATOR,
997 "files", DIR_SEPARATOR,
998 place_pub_str, DIR_SEPARATOR,
999 GNUNET_ntohll (msg->message_id));
1000 GNUNET_free (place_pub_str);
1001
1002 /* save if does not already exist */
1003 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
1004 {
1005 if (0 == GNUNET_DISK_fn_write (filename, NULL, 0,
1006 GNUNET_DISK_PERM_USER_READ
1007 | GNUNET_DISK_PERM_USER_WRITE))
1008 {
1009 plc->file_save = GNUNET_YES;
1010 }
1011 else
1012 {
1013 GNUNET_break (0);
1014 }
1015 }
1016 GNUNET_free (filename);
1017}
1018
1019
1020/**
1021 * Received a data fragment to be saved to disk.
1022 *
1023 * Append data fragment to the file.
1024 */
1025static void
1026place_recv_save_data (void *cls,
1027 const struct GNUNET_PSYC_MessageHeader *msg,
1028 const struct GNUNET_MessageHeader *pmsg,
1029 uint64_t message_id,
1030 const void *data,
1031 uint16_t data_size)
1032{
1033 struct Place *plc = cls;
1034 if (GNUNET_YES != plc->file_save)
1035 return;
1036
1037 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&plc->pub_key);
1038 char *filename = NULL;
1039 GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%" PRIu64 ".part",
1040 dir_social, DIR_SEPARATOR,
1041 "files", DIR_SEPARATOR,
1042 place_pub_str, DIR_SEPARATOR,
1043 GNUNET_ntohll (msg->message_id));
1044 GNUNET_free (place_pub_str);
1045 if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
1046 {
1047 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "create", filename);
1048 GNUNET_free (filename);
1049 return;
1050 }
1051
1052 struct GNUNET_DISK_FileHandle *
1053 fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_WRITE,
1054 GNUNET_DISK_PERM_NONE);
1055 if (NULL != fh)
1056 {
1057 if (plc->file_offset != GNUNET_DISK_file_seek
1058 (fh, plc->file_offset, GNUNET_DISK_SEEK_SET)) {
1059 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "seek", filename);
1060 GNUNET_DISK_file_close (fh);
1061 GNUNET_free (filename);
1062 return;
1063 }
1064 GNUNET_DISK_file_write (fh, data, data_size);
1065 GNUNET_DISK_file_close (fh);
1066 GNUNET_free (filename);
1067 }
1068 else
1069 {
1070 GNUNET_free (filename);
1071 GNUNET_break (0);
1072 }
1073 plc->file_offset += data_size;
1074}
1075
1076
1077/**
1078 * Received end of message to be saved to disk.
1079 *
1080 * Remove .part ending from the filename.
1081 */
1082static void
1083place_recv_save_eom (void *cls,
1084 const struct GNUNET_PSYC_MessageHeader *msg,
1085 const struct GNUNET_MessageHeader *pmsg,
1086 uint64_t message_id,
1087 uint8_t is_cancelled)
1088{
1089 struct Place *plc = cls;
1090 if (GNUNET_YES != plc->file_save)
1091 return;
1092
1093 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&plc->pub_key);
1094 char *fn = NULL;
1095 GNUNET_asprintf (&fn, "%s%c%s%c%s%c%" PRIu64,
1096 dir_social, DIR_SEPARATOR,
1097 "files", DIR_SEPARATOR,
1098 place_pub_str, DIR_SEPARATOR,
1099 GNUNET_ntohll (msg->message_id));
1100 GNUNET_free (place_pub_str);
1101 char *fn_part = NULL;
1102 GNUNET_asprintf (&fn_part, "%s.part", fn);
1103
1104 if (rename (fn_part, fn)) {
1105 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1106 "Failed to rename %s into %s: %s (%d)\n",
1107 fn_part, fn, strerror (errno), errno);
1108 }
1109
1110 GNUNET_free (fn);
1111 GNUNET_free (fn_part);
1112}
1113
1114
1115/**
1116 * Initialize place data structure.
1117 */
1118static void
1119place_init (struct Place *plc)
1120{
1121 plc->slicer = GNUNET_PSYC_slicer_create ();
1122}
1123
1124
1125/**
1126 * Add a place to the @e places hash map.
1127 *
1128 * @param ereq
1129 * Entry request.
1130 *
1131 * @return #GNUNET_OK if the place was added
1132 * #GNUNET_NO if the place already exists in the hash map
1133 * #GNUNET_SYSERR on error
1134 */
1135static int
1136place_add (const struct PlaceEnterRequest *ereq)
1137{
1138 struct EgoPlacePublicKey ego_place_pub_key = {
1139 .ego_pub_key = ereq->ego_pub_key,
1140 .place_pub_key = ereq->place_pub_key,
1141 };
1142 struct GNUNET_HashCode ego_place_pub_hash;
1143 GNUNET_CRYPTO_hash (&ego_place_pub_key, sizeof (ego_place_pub_key), &ego_place_pub_hash);
1144
1145 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146 " ego_place_pub_hash = %s\n", GNUNET_h2s (&ego_place_pub_hash));
1147
1148 struct GNUNET_MessageHeader *
1149 place_msg = GNUNET_CONTAINER_multihashmap_get (places, &ego_place_pub_hash);
1150 if (NULL != place_msg)
1151 return GNUNET_NO;
1152
1153 place_msg = GNUNET_copy_message (&ereq->header);
1154 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (places, &ego_place_pub_hash, place_msg,
1155 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
1156 {
1157 GNUNET_break (0);
1158 GNUNET_free (place_msg);
1159 return GNUNET_SYSERR;
1160 }
1161
1162 return GNUNET_OK;
1163}
1164
1165/**
1166 * Add a place to the @e app_places hash map.
1167 *
1168 * @param app_id
1169 * Application ID.
1170 * @param ereq
1171 * Entry request.
1172 *
1173 * @return #GNUNET_OK if the place was added
1174 * #GNUNET_NO if the place already exists in the hash map
1175 * #GNUNET_SYSERR on error
1176 */
1177static int
1178app_place_add (const char *app_id,
1179 const struct PlaceEnterRequest *ereq)
1180{
1181 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1182 "Adding app place to hashmap:\n");
1183 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1184 " app_id = %s\n", app_id);
1185
1186 struct GNUNET_HashCode app_id_hash;
1187 GNUNET_CRYPTO_hash (app_id, strlen (app_id) + 1, &app_id_hash);
1188
1189 struct EgoPlacePublicKey ego_place_pub_key = {
1190 .ego_pub_key = ereq->ego_pub_key,
1191 .place_pub_key = ereq->place_pub_key,
1192 };
1193 struct GNUNET_HashCode ego_place_pub_hash;
1194 GNUNET_CRYPTO_hash (&ego_place_pub_key, sizeof (ego_place_pub_key), &ego_place_pub_hash);
1195
1196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1197 " ego_place_pub_hash = %s\n", GNUNET_h2s (&ego_place_pub_hash));
1198
1199 struct GNUNET_CONTAINER_MultiHashMap *
1200 app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
1201 if (NULL == app_places)
1202 {
1203 app_places = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1204 GNUNET_CONTAINER_multihashmap_put (apps_places, &app_id_hash, app_places,
1205 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1206 }
1207
1208 if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (app_places, &ego_place_pub_hash))
1209 return GNUNET_NO;
1210
1211 if (GNUNET_SYSERR == place_add (ereq))
1212 {
1213 return GNUNET_SYSERR;
1214 }
1215
1216 if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (app_places, &ego_place_pub_hash, NULL,
1217 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST))
1218 {
1219 GNUNET_break (0);
1220 return GNUNET_SYSERR;
1221 }
1222 return GNUNET_OK;
1223}
1224
1225
1226/**
1227 * Save place entry message to disk.
1228 *
1229 * @param app_id
1230 * Application ID.
1231 * @param ereq
1232 * Entry request message.
1233 */
1234static int
1235app_place_save (const char *app_id,
1236 const struct PlaceEnterRequest *ereq)
1237{
1238 if (GNUNET_SYSERR == app_place_add (app_id, ereq))
1239 {
1240 GNUNET_assert (0);
1241 }
1242
1243 if (NULL == dir_places)
1244 return GNUNET_SYSERR;
1245
1246 char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&ereq->ego_pub_key);
1247 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (&ereq->place_pub_key);
1248 char *filename = NULL;
1249 GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s",
1250 dir_social, DIR_SEPARATOR,
1251 "places", DIR_SEPARATOR,
1252 ego_pub_str, DIR_SEPARATOR,
1253 place_pub_str);
1254 int ret = GNUNET_DISK_directory_create_for_file (filename);
1255 if (GNUNET_OK != ret
1256 || 0 > GNUNET_DISK_fn_write (filename, ereq, ntohs (ereq->header.size),
1257 GNUNET_DISK_PERM_USER_READ
1258 | GNUNET_DISK_PERM_USER_WRITE))
1259 {
1260 GNUNET_break (0);
1261 ret = GNUNET_SYSERR;
1262 }
1263 GNUNET_free (filename);
1264
1265 if (ret == GNUNET_OK)
1266 {
1267 GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s%c" "%s",
1268 dir_social, DIR_SEPARATOR,
1269 "apps", DIR_SEPARATOR,
1270 app_id, DIR_SEPARATOR,
1271 ego_pub_str, DIR_SEPARATOR,
1272 place_pub_str);
1273 ret = GNUNET_DISK_directory_create_for_file (filename);
1274 if (GNUNET_OK != ret
1275 || 0 > GNUNET_DISK_fn_write (filename, "", 0,
1276 GNUNET_DISK_PERM_USER_READ
1277 | GNUNET_DISK_PERM_USER_WRITE))
1278 {
1279 GNUNET_break (0);
1280 ret = GNUNET_SYSERR;
1281 }
1282 GNUNET_free (filename);
1283 }
1284 GNUNET_free (ego_pub_str);
1285 GNUNET_free (place_pub_str);
1286 return ret;
1287}
1288
1289
1290int
1291app_place_remove (const char *app_id,
1292 const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
1293 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key)
1294{
1295 struct GNUNET_HashCode ego_pub_hash;
1296 struct GNUNET_HashCode place_pub_hash;
1297 GNUNET_CRYPTO_hash (ego_pub_key, sizeof (*ego_pub_key), &ego_pub_hash);
1298 GNUNET_CRYPTO_hash (place_pub_key, sizeof (*place_pub_key), &place_pub_hash);
1299
1300 char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (ego_pub_key);
1301 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (place_pub_key);
1302 char *app_place_filename = NULL;
1303 GNUNET_asprintf (&app_place_filename,
1304 "%s%c" "%s%c" "%s%c" "%s%c" "%s",
1305 dir_social, DIR_SEPARATOR,
1306 "apps", DIR_SEPARATOR,
1307 app_id, DIR_SEPARATOR,
1308 ego_pub_str, DIR_SEPARATOR,
1309 place_pub_str);
1310 GNUNET_free (ego_pub_str);
1311 GNUNET_free (place_pub_str);
1312
1313 struct GNUNET_HashCode app_id_hash;
1314 GNUNET_CRYPTO_hash (app_id, strlen (app_id) + 1, &app_id_hash);
1315
1316 struct GNUNET_CONTAINER_MultiHashMap *
1317 app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
1318
1319 if (NULL != app_places)
1320 GNUNET_CONTAINER_multihashmap_remove (app_places, &place_pub_hash, NULL);
1321
1322 int ret = GNUNET_OK;
1323
1324 if (0 != unlink (app_place_filename))
1325 {
1326 GNUNET_break (0);
1327 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1328 "Error removing app place file: %s: %s (%d)\n",
1329 app_place_filename, strerror (errno), errno);
1330 ret = GNUNET_SYSERR;
1331 }
1332 GNUNET_free (app_place_filename);
1333
1334 return ret;
1335}
1336
1337
1338/**
1339 * Enter place as host.
1340 *
1341 * @param hreq
1342 * Host entry request.
1343 * @param[out] ret_hst
1344 * Returned Host struct.
1345 *
1346 * @return #GNUNET_YES if the host entered the place just now,
1347 * #GNUNET_NO if the place is already entered,
1348 * #GNUNET_SYSERR if place_pub_key was set
1349 * but its private key was not found
1350 */
1351static int
1352host_enter (const struct HostEnterRequest *hreq, struct Host **ret_hst)
1353{
1354 int ret = GNUNET_NO;
1355 struct GNUNET_HashCode place_pub_hash;
1356 GNUNET_CRYPTO_hash (&hreq->place_pub_key, sizeof (hreq->place_pub_key),
1357 &place_pub_hash);
1358 struct Host *hst = GNUNET_CONTAINER_multihashmap_get (hosts, &place_pub_hash);
1359
1360 if (NULL == hst)
1361 {
1362 hst = GNUNET_new (struct Host);
1363 hst->policy = hreq->policy;
1364 hst->join_reqs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1365 hst->relay_msgs = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
1366
1367 struct Place *plc = &hst->place;
1368 place_init (plc);
1369 plc->is_host = GNUNET_YES;
1370 plc->pub_key = hreq->place_pub_key;
1371 plc->pub_key_hash = place_pub_hash;
1372
1373 GNUNET_CONTAINER_multihashmap_put (hosts, &plc->pub_key_hash, plc,
1374 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1375 hst->master = GNUNET_PSYC_master_start (cfg, &hreq->place_key, hst->policy,
1376 &psyc_master_started,
1377 &psyc_recv_join_request,
1378 &psyc_recv_message, NULL, hst);
1379 plc->channel = GNUNET_PSYC_master_get_channel (hst->master);
1380 ret = GNUNET_YES;
1381 }
1382
1383 if (NULL != ret_hst)
1384 *ret_hst = hst;
1385 return ret;
1386}
1387
1388
1389static int
1390msg_proc_parse (const struct MsgProcRequest *mpreq,
1391 uint32_t *flags,
1392 const char **method_prefix,
1393 struct GNUNET_HashCode *method_hash)
1394{
1395 ssize_t method_size = ntohs (mpreq->header.size) - sizeof (*mpreq);
1396 uint16_t offset;
1397
1398 if (method_size < 0)
1399 {
1400 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1401 "MsgProcRequest has invalid size\n");
1402 return GNUNET_SYSERR;
1403 }
1404
1405 offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &mpreq[1],
1406 method_size,
1407 1,
1408 method_prefix);
1409 if (0 == offset || offset != method_size || *method_prefix == NULL)
1410 {
1411 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1412 "MsgProcRequest contains invalid method\n");
1413 return GNUNET_SYSERR;
1414 }
1415 GNUNET_CRYPTO_hash (*method_prefix, (size_t) method_size, method_hash);
1416 *flags = ntohl (mpreq->flags);
1417 return GNUNET_OK;
1418}
1419
1420
1421void
1422app_notify_place (const struct GNUNET_MessageHeader *msg,
1423 struct GNUNET_SERVICE_Client *client)
1424{
1425 struct AppPlaceMessage *amsg;
1426 struct GNUNET_MQ_Envelope *env;
1427 uint16_t msg_size = ntohs (msg->size);
1428
1429 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1430 "%p Sending place notification of type %u to client.\n",
1431 client, ntohs (msg->type));
1432 switch (ntohs (msg->type))
1433 {
1434 case GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER:
1435 {
1436 struct HostEnterRequest *hreq = (struct HostEnterRequest *) msg;
1437 if (msg_size < sizeof (struct HostEnterRequest))
1438 return;
1439 env = GNUNET_MQ_msg (amsg,
1440 GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE);
1441 // FIXME: also notify about not entered places
1442 amsg->place_state = GNUNET_SOCIAL_PLACE_STATE_ENTERED;
1443 amsg->is_host = GNUNET_YES;
1444 amsg->ego_pub_key = hreq->ego_pub_key;
1445 amsg->place_pub_key = hreq->place_pub_key;
1446 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1447 env);
1448 break;
1449 }
1450 case GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER:
1451 {
1452 if (msg_size < sizeof (struct GuestEnterRequest))
1453 return;
1454 struct GuestEnterRequest *greq = (struct GuestEnterRequest *) msg;
1455 env = GNUNET_MQ_msg (amsg,
1456 GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE);
1457 // FIXME: also notify about not entered places
1458 amsg->place_state = GNUNET_SOCIAL_PLACE_STATE_ENTERED;
1459 amsg->is_host = GNUNET_NO;
1460 amsg->ego_pub_key = greq->ego_pub_key;
1461 amsg->place_pub_key = greq->place_pub_key;
1462 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1463 env);
1464 break;
1465 }
1466 default:
1467 return;
1468 }
1469}
1470
1471
1472void
1473app_notify_place_end (struct GNUNET_SERVICE_Client *client)
1474{
1475 struct GNUNET_MQ_Envelope *env;
1476
1477 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1478 "%p Sending end of place list notification to client\n",
1479 client);
1480 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END);
1481 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1482 env);
1483}
1484
1485
1486void
1487app_notify_ego (struct Ego *ego, struct GNUNET_SERVICE_Client *client)
1488{
1489 struct AppEgoMessage *emsg;
1490 struct GNUNET_MQ_Envelope *env;
1491 size_t name_size = strlen (ego->name) + 1;
1492
1493 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1494 "%p Sending ego notification to client: %s\n",
1495 client, ego->name);
1496 env = GNUNET_MQ_msg_extra (emsg,
1497 name_size,
1498 GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO);
1499 GNUNET_CRYPTO_ecdsa_key_get_public (&ego->key, &emsg->ego_pub_key);
1500 GNUNET_memcpy (&emsg[1], ego->name, name_size);
1501 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1502 env);
1503}
1504
1505
1506void
1507app_notify_ego_end (struct GNUNET_SERVICE_Client *client)
1508{
1509 struct GNUNET_MQ_Envelope *env;
1510
1511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1512 "%p Sending end of ego list notification to client\n",
1513 client);
1514 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END);
1515 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1516 env);
1517}
1518
1519
1520int
1521app_place_entry_notify (void *cls, const struct GNUNET_HashCode *key, void *value)
1522{
1523 struct GNUNET_MessageHeader *
1524 msg = GNUNET_CONTAINER_multihashmap_get (places, key);
1525 if (NULL != msg)
1526 app_notify_place (msg, cls);
1527 return GNUNET_YES;
1528}
1529
1530
1531int
1532ego_entry (void *cls, const struct GNUNET_HashCode *key, void *value)
1533{
1534 app_notify_ego (value, cls);
1535 return GNUNET_YES;
1536}
1537
1538
1539static int
1540check_client_msg_proc_set (void *cls,
1541 const struct MsgProcRequest *mpreq)
1542{
1543 return GNUNET_OK;
1544}
1545
1546
1547/**
1548 * Handle a client setting message proccesing flags for a method prefix.
1549 */
1550static void
1551handle_client_msg_proc_set (void *cls,
1552 const struct MsgProcRequest *mpreq)
1553{
1554 struct Client *c = cls;
1555 struct GNUNET_SERVICE_Client *client = c->client;
1556 struct Place *plc = c->place;
1557 if (NULL == plc)
1558 {
1559 GNUNET_break (0);
1560 GNUNET_SERVICE_client_drop (client);
1561 return;
1562 }
1563
1564 const char *method_prefix = NULL;
1565 uint32_t flags = 0;
1566 struct GNUNET_HashCode method_hash;
1567
1568 if (GNUNET_OK !=
1569 msg_proc_parse (mpreq, &flags, &method_prefix, &method_hash))
1570 {
1571 GNUNET_break (0);
1572 GNUNET_SERVICE_client_drop (client);
1573 return;
1574 }
1575#if 0
1576 GNUNET_PSYC_slicer_method_remove (plc->slicer, method_prefix,
1577 place_recv_relay_method,
1578 place_recv_relay_modifier,
1579 place_recv_relay_data,
1580 place_recv_relay_eom);
1581 GNUNET_PSYC_slicer_method_remove (plc->slicer, method_prefix,
1582 place_recv_save_method,
1583 NULL,
1584 place_recv_save_data,
1585 place_recv_save_eom);
1586#endif
1587 if (flags & GNUNET_SOCIAL_MSG_PROC_RELAY)
1588 {
1589 GNUNET_PSYC_slicer_method_add (plc->slicer, method_prefix, NULL,
1590 place_recv_relay_method,
1591 place_recv_relay_modifier,
1592 place_recv_relay_data,
1593 place_recv_relay_eom,
1594 plc);
1595 }
1596 if (flags & GNUNET_SOCIAL_MSG_PROC_SAVE)
1597 {
1598 GNUNET_PSYC_slicer_method_add (plc->slicer, method_prefix, NULL,
1599 place_recv_save_method,
1600 NULL,
1601 place_recv_save_data,
1602 place_recv_save_eom,
1603 plc);
1604 }
1605
1606 /** @todo Save flags to be able to resume relaying/saving after restart */
1607
1608 GNUNET_SERVICE_client_continue (client);
1609}
1610
1611
1612/**
1613 * Handle a connecting client requesting to clear all relay rules.
1614 */
1615static void
1616handle_client_msg_proc_clear (void *cls,
1617 const struct GNUNET_MessageHeader *msg)
1618{
1619 struct Client *c = cls;
1620 struct GNUNET_SERVICE_Client *client = c->client;
1621 struct Place *plc = c->place;
1622 if (NULL == plc)
1623 {
1624 GNUNET_break (0);
1625 GNUNET_SERVICE_client_drop (client);
1626 return;
1627 }
1628
1629 GNUNET_PSYC_slicer_clear (plc->slicer);
1630
1631 GNUNET_SERVICE_client_continue (client);
1632}
1633
1634
1635static int
1636check_client_host_enter (void *cls,
1637 const struct HostEnterRequest *hr)
1638{
1639 return GNUNET_OK;
1640}
1641
1642
1643/**
1644 * Handle a connecting client entering a place as host.
1645 */
1646static void
1647handle_client_host_enter (void *cls,
1648 const struct HostEnterRequest *hr)
1649{
1650 struct Client *c = cls;
1651 struct GNUNET_SERVICE_Client *client = c->client;
1652 struct HostEnterRequest *
1653 hreq = (struct HostEnterRequest *) GNUNET_copy_message (&hr->header);
1654
1655 uint8_t app_id_size = ntohs (hreq->header.size) - sizeof (*hreq);
1656 const char *app_id = NULL;
1657 uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &hreq[1],
1658 app_id_size, 1, &app_id);
1659 if (0 == offset || offset != app_id_size || app_id == NULL)
1660 {
1661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1662 "offset = %u, app_id_size = %u, app_id = %s\n",
1663 offset, app_id_size, app_id);
1664 GNUNET_break (0);
1665 GNUNET_SERVICE_client_drop (client);
1666 return;
1667 }
1668
1669 struct Host *hst = NULL;
1670 struct Place *plc = NULL;
1671 int ret = GNUNET_OK;
1672
1673 struct GNUNET_CRYPTO_EddsaPublicKey empty_pub_key;
1674 memset (&empty_pub_key, 0, sizeof (empty_pub_key));
1675
1676 if (0 == memcmp (&hreq->place_pub_key, &empty_pub_key, sizeof (empty_pub_key)))
1677 { // no public key set: create new private key & save the place
1678 struct GNUNET_CRYPTO_EddsaPrivateKey *
1679 place_key = GNUNET_CRYPTO_eddsa_key_create ();
1680 hreq->place_key = *place_key;
1681 GNUNET_CRYPTO_eddsa_key_get_public (place_key, &hreq->place_pub_key);
1682 GNUNET_CRYPTO_eddsa_key_clear (place_key);
1683 GNUNET_free (place_key);
1684
1685 app_place_save (app_id, (const struct PlaceEnterRequest *) hreq);
1686 }
1687
1688 switch (host_enter (hreq, &hst))
1689 {
1690 case GNUNET_YES:
1691 plc = c->place = &hst->place;
1692 plc->host = hst;
1693 break;
1694
1695 case GNUNET_NO:
1696 {
1697 plc = c->place = &hst->place;
1698 plc->host = hst;
1699 client_send_host_enter_ack (client, hst, GNUNET_OK);
1700 break;
1701 }
1702 case GNUNET_SYSERR:
1703 ret = GNUNET_SYSERR;
1704 }
1705
1706 if (ret != GNUNET_SYSERR)
1707 {
1708
1709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1710 "%p Client connected as host to place %s.\n",
1711 hst, GNUNET_h2s (&plc->pub_key_hash));
1712
1713 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
1714 cli->client = client;
1715 GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
1716 c->place = plc;
1717 app_notify_place (&hreq->header, client);
1718 }
1719
1720 GNUNET_CRYPTO_eddsa_key_clear (&hreq->place_key);
1721 GNUNET_free (hreq);
1722
1723 if (GNUNET_OK == ret)
1724 GNUNET_SERVICE_client_continue (client);
1725 else
1726 GNUNET_SERVICE_client_drop (client);
1727}
1728
1729
1730/**
1731 * Enter place as guest.
1732 *
1733 * @param greq
1734 * Guest entry request.
1735 * @param[out] ret_gst
1736 * Returned Guest struct.
1737 *
1738 * @return #GNUNET_YES if the guest entered the place just now,
1739 * #GNUNET_NO if the place is already entered,
1740 * #GNUNET_SYSERR on error.
1741 */
1742static int
1743guest_enter (const struct GuestEnterRequest *greq, struct Guest **ret_gst)
1744{
1745 int ret = GNUNET_NO;
1746 uint16_t greq_size = ntohs (greq->header.size);
1747
1748 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key = greq->ego_pub_key;
1749 struct GNUNET_HashCode ego_pub_hash;
1750 GNUNET_CRYPTO_hash (&ego_pub_key, sizeof (ego_pub_key), &ego_pub_hash);
1751 struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
1752
1753 if (NULL == ego)
1754 {
1755 return GNUNET_SYSERR;
1756 }
1757
1758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1759 "entering as guest\n");
1760 struct GNUNET_HashCode place_pub_hash;
1761 GNUNET_CRYPTO_hash (&greq->place_pub_key, sizeof (greq->place_pub_key),
1762 &place_pub_hash);
1763
1764 struct GNUNET_CONTAINER_MultiHashMap *
1765 plc_gst = GNUNET_CONTAINER_multihashmap_get (place_guests, &place_pub_hash);
1766 struct Guest *gst = NULL;
1767 int new_guest;
1768
1769 if (NULL != plc_gst)
1770 gst = GNUNET_CONTAINER_multihashmap_get (plc_gst, &ego_pub_hash);
1771
1772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1773 "plc_gst = %p, gst = %p\n",
1774 plc_gst,
1775 gst);
1776
1777 if (NULL == gst)
1778 {
1779 gst = GNUNET_new (struct Guest);
1780 new_guest = GNUNET_YES;
1781 }
1782 else new_guest = GNUNET_NO;
1783
1784 if (NULL == gst->slave)
1785 {
1786 gst->origin = greq->origin;
1787 gst->relay_count = ntohl (greq->relay_count);
1788
1789 uint16_t len;
1790 uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
1791 const char *app_id = (const char *) &greq[1];
1792 const char *p = app_id;
1793
1794 len = strnlen (app_id, remaining);
1795 if (len == remaining)
1796 {
1797 GNUNET_free (gst);
1798 GNUNET_break (0);
1799 return GNUNET_SYSERR;
1800 }
1801 p += len + 1;
1802 remaining -= len + 1;
1803
1804 const struct GNUNET_PeerIdentity *relays = NULL;
1805 uint16_t relay_size = gst->relay_count * sizeof (*relays);
1806 if (remaining < relay_size)
1807 {
1808 GNUNET_free (gst);
1809 GNUNET_break (0);
1810 return GNUNET_SYSERR;
1811 }
1812 if (0 < relay_size)
1813 relays = (const struct GNUNET_PeerIdentity *) p;
1814 p += relay_size;
1815 remaining -= relay_size;
1816
1817 struct GNUNET_PSYC_Message *join_msg = NULL;
1818 uint16_t join_msg_size = 0;
1819
1820 if (sizeof (struct GNUNET_MessageHeader) <= remaining)
1821 {
1822 join_msg = (struct GNUNET_PSYC_Message *) p;
1823 join_msg_size = ntohs (join_msg->header.size);
1824 p += join_msg_size;
1825 remaining -= join_msg_size;
1826 }
1827 if (0 != remaining)
1828 {
1829 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1830 "%zu + %u + %u != %u\n",
1831 sizeof (*greq), relay_size, join_msg_size, greq_size);
1832 GNUNET_free (gst);
1833 GNUNET_break (0);
1834 return GNUNET_SYSERR;
1835 }
1836 if (0 < relay_size)
1837 {
1838 gst->relays = GNUNET_malloc (relay_size);
1839 GNUNET_memcpy (gst->relays, relays, relay_size);
1840 }
1841
1842 gst->join_flags = ntohl (greq->flags);
1843
1844 struct Place *plc = &gst->place;
1845 place_init (plc);
1846 plc->is_host = GNUNET_NO;
1847 plc->pub_key = greq->place_pub_key;
1848 plc->pub_key_hash = place_pub_hash;
1849 plc->ego_pub_key = ego_pub_key;
1850 plc->ego_pub_hash = ego_pub_hash;
1851 plc->ego_key = ego->key;
1852
1853 if (NULL == plc_gst)
1854 {
1855 plc_gst = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
1856 (void) GNUNET_CONTAINER_multihashmap_put (place_guests, &plc->pub_key_hash, plc_gst,
1857 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1858 }
1859 if (GNUNET_YES == new_guest)
1860 {
1861 (void) GNUNET_CONTAINER_multihashmap_put (plc_gst, &plc->ego_pub_hash, gst,
1862 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
1863 (void) GNUNET_CONTAINER_multihashmap_put (guests, &plc->pub_key_hash, gst,
1864 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1865
1866 }
1867 gst->slave
1868 = GNUNET_PSYC_slave_join (cfg, &plc->pub_key, &plc->ego_key,
1869 gst->join_flags, &gst->origin,
1870 gst->relay_count, gst->relays,
1871 &psyc_recv_message, NULL,
1872 &psyc_slave_connected,
1873 &psyc_recv_join_dcsn,
1874 gst, join_msg);
1875 plc->channel = GNUNET_PSYC_slave_get_channel (gst->slave);
1876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1877 "slave entered channel %p\n",
1878 plc->channel);
1879 ret = GNUNET_YES;
1880 }
1881
1882 // TODO: explain to automatic code scanners why free(gst) not necessary
1883 if (NULL != ret_gst)
1884 *ret_gst = gst;
1885 return ret;
1886}
1887
1888
1889static int
1890client_guest_enter (struct Client *c,
1891 const struct GuestEnterRequest *greq)
1892{
1893 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1894 "client_guest_enter\n");
1895 struct GNUNET_PSYC_CountersResultMessage *result_msg;
1896 struct GNUNET_MQ_Envelope *env;
1897 struct GNUNET_SERVICE_Client *client = c->client;
1898 uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
1899 const char *app_id = NULL;
1900 uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &greq[1],
1901 remaining, 1, &app_id);
1902 struct Guest *gst = NULL;
1903 struct Place *plc = NULL;
1904
1905 if (0 == offset)
1906 {
1907 return GNUNET_SYSERR;
1908 }
1909 switch (guest_enter (greq, &gst))
1910 {
1911 case GNUNET_YES:
1912 {
1913 plc = c->place = &gst->place;
1914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1915 "guest entered successfully to local place %s\n",
1916 GNUNET_h2s (&plc->pub_key_hash));
1917 plc->guest = gst;
1918 app_place_save (app_id, (const struct PlaceEnterRequest *) greq);
1919 app_notify_place (&greq->header, client);
1920 break;
1921 }
1922 case GNUNET_NO:
1923 {
1924 plc = c->place = &gst->place;
1925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1926 "guest re-entered successfully to local place %s\n",
1927 GNUNET_h2s (&plc->pub_key_hash));
1928 plc->guest = gst;
1929 env = GNUNET_MQ_msg (result_msg,
1930 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK);
1931 result_msg->result_code = htonl (GNUNET_OK);
1932 result_msg->max_message_id = GNUNET_htonll (plc->max_message_id);
1933 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1934 env);
1935 if (NULL != gst->join_dcsn)
1936 {
1937 env = GNUNET_MQ_msg_copy (&gst->join_dcsn->header);
1938 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
1939 env);
1940 }
1941 break;
1942 }
1943 case GNUNET_SYSERR:
1944 {
1945 return GNUNET_SYSERR;
1946 }
1947 }
1948
1949 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
1950 cli->client = client;
1951 GNUNET_CONTAINER_DLL_insert (plc->clients_head, plc->clients_tail, cli);
1952 return GNUNET_OK;
1953}
1954
1955
1956static int
1957check_client_guest_enter (void *cls,
1958 const struct GuestEnterRequest *greq)
1959{
1960 return GNUNET_OK;
1961}
1962
1963
1964/**
1965 * Handle a connecting client entering a place as guest.
1966 */
1967static void
1968handle_client_guest_enter (void *cls,
1969 const struct GuestEnterRequest *greq)
1970{
1971 struct Client *c = cls;
1972
1973 if (GNUNET_SYSERR == client_guest_enter (c, greq))
1974 {
1975 GNUNET_break (0);
1976 GNUNET_SERVICE_client_drop (c->client);
1977 return;
1978 }
1979 GNUNET_SERVICE_client_continue (c->client);
1980}
1981
1982
1983struct GuestEnterByNameClosure
1984{
1985 struct Client *client;
1986 char *app_id;
1987 char *password;
1988 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
1989 struct GNUNET_MessageHeader *join_msg;
1990};
1991
1992
1993/**
1994 * Result of a GNS name lookup for entering a place.
1995 *
1996 * @see GNUNET_SOCIAL_guest_enter_by_name
1997 */
1998static void
1999gns_result_guest_enter (void *cls, uint32_t rd_count,
2000 const struct GNUNET_GNSRECORD_Data *rd)
2001{
2002 struct GuestEnterByNameClosure *gcls = cls;
2003 struct Client *c = gcls->client;
2004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2005 "%p GNS result: %u records.\n",
2006 c, rd_count);
2007
2008 const struct GNUNET_GNSRECORD_PlaceData *
2009 rec = (const struct GNUNET_GNSRECORD_PlaceData *) rd->data;
2010
2011 if (0 == rd_count || rd->data_size < sizeof (*rec))
2012 {
2013 GNUNET_break (0);
2014 GNUNET_SERVICE_client_drop (c->client);
2015 return;
2016 }
2017
2018 uint16_t relay_count = ntohl (rec->relay_count);
2019 struct GNUNET_PeerIdentity *relays = NULL;
2020
2021 if (0 < relay_count)
2022 {
2023 if (rd->data_size == sizeof (*rec) + relay_count * sizeof (struct GNUNET_PeerIdentity))
2024 {
2025 relays = (struct GNUNET_PeerIdentity *) &rec[1];
2026 }
2027 else
2028 {
2029 relay_count = 0;
2030 GNUNET_break_op (0);
2031 }
2032 }
2033
2034 uint16_t app_id_size = strlen (gcls->app_id) + 1;
2035 uint16_t relay_size = relay_count * sizeof (*relays);
2036 uint16_t join_msg_size = 0;
2037 if (NULL != gcls->join_msg)
2038 join_msg_size = ntohs (gcls->join_msg->size);
2039 uint16_t greq_size = sizeof (struct GuestEnterRequest)
2040 + app_id_size + relay_size + join_msg_size;
2041 struct GuestEnterRequest *greq = GNUNET_malloc (greq_size);
2042 greq->header.size = htons (greq_size);
2043 greq->header.type = htons (GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
2044 greq->ego_pub_key = gcls->ego_pub_key;
2045 greq->place_pub_key = rec->place_pub_key;
2046 greq->origin = rec->origin;
2047 greq->relay_count = rec->relay_count;
2048
2049 void *p = &greq[1];
2050 GNUNET_memcpy (p, gcls->app_id, app_id_size);
2051 p += app_id_size;
2052 GNUNET_memcpy (p, relays, relay_size);
2053 p += relay_size;
2054 GNUNET_memcpy (p, gcls->join_msg, join_msg_size);
2055
2056 client_guest_enter (c, greq);
2057
2058 GNUNET_free (gcls->app_id);
2059 if (NULL != gcls->password)
2060 GNUNET_free (gcls->password);
2061 if (NULL != gcls->join_msg)
2062 GNUNET_free (gcls->join_msg);
2063 GNUNET_free (gcls);
2064 GNUNET_free (greq);
2065}
2066
2067
2068static int
2069check_client_guest_enter_by_name (void *cls,
2070 const struct GuestEnterByNameRequest *greq)
2071{
2072 return GNUNET_OK;
2073}
2074
2075
2076/**
2077 * Handle a connecting client entering a place as guest using a GNS address.
2078 *
2079 * Look up GNS address and generate a GuestEnterRequest from that.
2080 */
2081static void
2082handle_client_guest_enter_by_name (void *cls,
2083 const struct GuestEnterByNameRequest *greq)
2084{
2085 struct Client *c = cls;
2086 struct GNUNET_SERVICE_Client *client = c->client;
2087
2088 struct GuestEnterByNameClosure *gcls = GNUNET_malloc (sizeof (*gcls));
2089 gcls->client = c;
2090 gcls->ego_pub_key = greq->ego_pub_key;
2091
2092 const char *p = (const char *) &greq[1];
2093 const char *app_id = NULL, *password = NULL, *gns_name = NULL;
2094 uint16_t remaining = ntohs (greq->header.size) - sizeof (*greq);
2095 uint16_t offset = GNUNET_STRINGS_buffer_tokenize (p, remaining, 3,
2096 &app_id,
2097 &gns_name,
2098 &password);
2099 p += offset;
2100 remaining -= offset;
2101
2102 if (0 != offset && sizeof (*gcls->join_msg) <= remaining)
2103 {
2104 gcls->join_msg = GNUNET_copy_message ((struct GNUNET_MessageHeader *) p);
2105 remaining -= ntohs (gcls->join_msg->size);
2106 }
2107
2108 if (0 == offset || 0 != remaining)
2109 {
2110 if (NULL != gcls->join_msg)
2111 GNUNET_free (gcls->join_msg);
2112 GNUNET_free (gcls);
2113 GNUNET_break (0);
2114 GNUNET_SERVICE_client_drop (client);
2115 return;
2116 }
2117
2118 uint16_t app_id_size = strlen (app_id) + 1;
2119 gcls->app_id = GNUNET_malloc (app_id_size);
2120 GNUNET_memcpy (gcls->app_id, app_id, app_id_size);
2121
2122 uint16_t password_size = strlen (password);
2123 if (0 < password_size++)
2124 {
2125 gcls->password = GNUNET_malloc (password_size);
2126 GNUNET_memcpy (gcls->password, password, password_size);
2127 }
2128
2129 GNUNET_GNS_lookup (gns, gns_name,
2130 &greq->ego_pub_key,
2131 GNUNET_GNSRECORD_TYPE_PLACE,
2132 GNUNET_GNS_LO_DEFAULT,
2133 &gns_result_guest_enter, gcls);
2134 GNUNET_SERVICE_client_continue (client);
2135}
2136
2137
2138static int
2139check_client_app_connect (void *cls,
2140 const struct AppConnectRequest *creq)
2141{
2142 return GNUNET_OK;
2143}
2144
2145
2146/**
2147 * Handle application connection.
2148 */
2149static void
2150handle_client_app_connect (void *cls,
2151 const struct AppConnectRequest *creq)
2152{
2153 struct Client *c = cls;
2154 struct GNUNET_SERVICE_Client *client = c->client;
2155 ssize_t app_id_size = ntohs (creq->header.size) - sizeof (*creq);
2156 const char *app_id = NULL;
2157 uint16_t offset;
2158
2159 if (app_id_size < 0)
2160 {
2161 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2162 "AppConnectRequest has invalid size\n");
2163 GNUNET_break (0);
2164 GNUNET_SERVICE_client_drop (client);
2165 return;
2166 }
2167
2168 offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &creq[1],
2169 (size_t) app_id_size,
2170 1,
2171 &app_id);
2172 if (0 == offset || offset != app_id_size)
2173 {
2174 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2175 "AppConnectRequest contains invalid app ID\n");
2176 GNUNET_break (0);
2177 GNUNET_SERVICE_client_drop (client);
2178 return;
2179 }
2180
2181 struct GNUNET_HashCode app_id_hash;
2182 GNUNET_CRYPTO_hash (app_id, (size_t) app_id_size, &app_id_hash);
2183
2184 GNUNET_CONTAINER_multihashmap_iterate (egos, ego_entry, client);
2185 app_notify_ego_end (client);
2186
2187 struct GNUNET_CONTAINER_MultiHashMap *
2188 app_places = GNUNET_CONTAINER_multihashmap_get (apps_places, &app_id_hash);
2189 if (NULL != app_places)
2190 GNUNET_CONTAINER_multihashmap_iterate (app_places, app_place_entry_notify, client);
2191 app_notify_place_end (client);
2192
2193 struct ClientListItem *cli = GNUNET_new (struct ClientListItem);
2194 cli->client = client;
2195 struct Application *app = GNUNET_CONTAINER_multihashmap_get (apps,
2196 &app_id_hash);
2197 if (NULL == app) {
2198 app = GNUNET_malloc (sizeof (*app));
2199 (void) GNUNET_CONTAINER_multihashmap_put (apps, &app_id_hash, app,
2200 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
2201 }
2202 GNUNET_CONTAINER_DLL_insert (app->clients_head, app->clients_tail, cli);
2203
2204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2205 "%p Application %s connected.\n", app, app_id);
2206
2207 c->app_id = GNUNET_malloc ((size_t) app_id_size);
2208 GNUNET_memcpy (c->app_id, app_id, (size_t) app_id_size);
2209
2210 GNUNET_SERVICE_client_continue (client);
2211}
2212
2213
2214/**
2215 * Handle application detach request.
2216 */
2217static void
2218handle_client_app_detach (void *cls,
2219 const struct AppDetachRequest *req)
2220{
2221 struct Client *c = cls;
2222 struct GNUNET_SERVICE_Client *client = c->client;
2223
2224 int ret = app_place_remove (c->app_id, &req->ego_pub_key, &req->place_pub_key);
2225 client_send_result (client, req->op_id, ret, NULL, 0);
2226
2227 GNUNET_SERVICE_client_continue (client);
2228}
2229
2230
2231static void
2232place_leave_cb (void *cls)
2233{
2234 struct Place *plc = cls;
2235
2236 place_send_leave_ack (plc);
2237 (GNUNET_YES == plc->is_host)
2238 ? cleanup_host ((struct Host *) plc)
2239 : cleanup_guest ((struct Guest *) plc);
2240}
2241
2242
2243/**
2244 * Handle application leave request.
2245 */
2246static void
2247handle_client_place_leave (void *cls,
2248 const struct GNUNET_MessageHeader *msg)
2249{
2250 struct Client *c = cls;
2251 struct GNUNET_SERVICE_Client *client = c->client;
2252 struct Place *plc = c->place;
2253
2254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2255 "got leave request from %s for place %s",
2256 plc->is_host? "host" : "slave",
2257 GNUNET_h2s (&plc->pub_key_hash));
2258 if (NULL == plc)
2259 {
2260 GNUNET_break (0);
2261 GNUNET_SERVICE_client_drop (client);
2262 return;
2263 }
2264
2265 if (GNUNET_YES != plc->is_disconnecting)
2266 {
2267 plc->is_disconnecting = GNUNET_YES;
2268 if (plc->is_host)
2269 {
2270 struct Host *host = plc->host;
2271 GNUNET_assert (NULL != host);
2272 GNUNET_PSYC_master_stop (host->master, GNUNET_NO, &place_leave_cb, plc);
2273 }
2274 else
2275 {
2276 struct Guest *guest = plc->guest;
2277 GNUNET_assert (NULL != guest);
2278 GNUNET_PSYC_slave_part (guest->slave, GNUNET_NO, &place_leave_cb, plc);
2279 }
2280 }
2281 else
2282 {
2283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2284 "got leave request but place is already leaving\n");
2285 }
2286 GNUNET_SERVICE_client_continue (client);
2287}
2288
2289
2290struct JoinDecisionClosure
2291{
2292 int32_t is_admitted;
2293 struct GNUNET_PSYC_Message *msg;
2294};
2295
2296
2297/**
2298 * Iterator callback for responding to join requests.
2299 */
2300static int
2301psyc_send_join_decision (void *cls, const struct GNUNET_HashCode *pub_key_hash,
2302 void *value)
2303{
2304 struct JoinDecisionClosure *jcls = cls;
2305 struct GNUNET_PSYC_JoinHandle *jh = value;
2306 // FIXME: add relays
2307 GNUNET_PSYC_join_decision (jh, jcls->is_admitted, 0, NULL, jcls->msg);
2308 return GNUNET_YES;
2309}
2310
2311
2312static int
2313check_client_join_decision (void *cls,
2314 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
2315{
2316 return GNUNET_OK;
2317}
2318
2319
2320/**
2321 * Handle an entry decision from a host client.
2322 */
2323static void
2324handle_client_join_decision (void *cls,
2325 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
2326{
2327 struct Client *c = cls;
2328 struct GNUNET_SERVICE_Client *client = c->client;
2329 struct Place *plc = c->place;
2330 if (NULL == plc || GNUNET_YES != plc->is_host)
2331 {
2332 GNUNET_break (0);
2333 GNUNET_SERVICE_client_drop (client);
2334 return;
2335 }
2336 struct Host *hst = plc->host;
2337
2338 struct JoinDecisionClosure jcls;
2339 jcls.is_admitted = ntohl (dcsn->is_admitted);
2340 jcls.msg
2341 = (sizeof (*dcsn) + sizeof (*jcls.msg) <= ntohs (dcsn->header.size))
2342 ? (struct GNUNET_PSYC_Message *) &dcsn[1]
2343 : NULL;
2344
2345 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2346 "jcls.msg = %p\n",
2347 jcls.msg);
2348 struct GNUNET_HashCode slave_pub_hash;
2349 GNUNET_CRYPTO_hash (&dcsn->slave_pub_key, sizeof (dcsn->slave_pub_key),
2350 &slave_pub_hash);
2351
2352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2353 "%p Got join decision (%d) from client for place %s..\n",
2354 hst, jcls.is_admitted, GNUNET_h2s (&plc->pub_key_hash));
2355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2356 "%p ..and slave %s.\n",
2357 hst, GNUNET_h2s (&slave_pub_hash));
2358
2359 GNUNET_CONTAINER_multihashmap_get_multiple (hst->join_reqs, &slave_pub_hash,
2360 &psyc_send_join_decision, &jcls);
2361 GNUNET_CONTAINER_multihashmap_remove_all (hst->join_reqs, &slave_pub_hash);
2362
2363 GNUNET_SERVICE_client_continue (client);
2364}
2365
2366
2367/**
2368 * Send acknowledgement to a client.
2369 *
2370 * Sent after a message fragment has been passed on to multicast.
2371 *
2372 * @param plc The place struct for the client.
2373 */
2374static void
2375send_message_ack (struct Place *plc, struct GNUNET_SERVICE_Client *client)
2376{
2377 struct GNUNET_MQ_Envelope *env;
2378
2379 env = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK);
2380 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client),
2381 env);
2382}
2383
2384
2385/**
2386 * Proceed to the next message part in the transmission queue.
2387 *
2388 * @param plc
2389 * Place where the transmission is going on.
2390 * @param tmit_msg
2391 * Currently transmitted message.
2392 * @param tmit_frag
2393 * Currently transmitted message fragment.
2394 *
2395 * @return @a tmit_frag, or NULL if reached the end of fragment.
2396 */
2397static struct FragmentTransmitQueue *
2398psyc_transmit_queue_next_part (struct Place *plc,
2399 struct MessageTransmitQueue *tmit_msg,
2400 struct FragmentTransmitQueue *tmit_frag)
2401{
2402 uint16_t psize = ntohs (tmit_frag->next_part->size);
2403 if ((char *) tmit_frag->next_part + psize - ((char *) &tmit_frag[1])
2404 < tmit_frag->size)
2405 {
2406 tmit_frag->next_part
2407 = (struct GNUNET_MessageHeader *) ((char *) tmit_frag->next_part + psize);
2408 }
2409 else /* Reached end of current fragment. */
2410 {
2411 if (NULL != tmit_frag->client)
2412 send_message_ack (plc, tmit_frag->client);
2413 GNUNET_CONTAINER_DLL_remove (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
2414 GNUNET_free (tmit_frag);
2415 tmit_frag = NULL;
2416 }
2417 return tmit_frag;
2418}
2419
2420
2421/**
2422 * Proceed to next message in transmission queue.
2423 *
2424 * @param plc
2425 * Place where the transmission is going on.
2426 * @param tmit_msg
2427 * Currently transmitted message.
2428 *
2429 * @return The next message in queue, or NULL if queue is empty.
2430 */
2431static struct MessageTransmitQueue *
2432psyc_transmit_queue_next_msg (struct Place *plc,
2433 struct MessageTransmitQueue *tmit_msg)
2434{
2435 GNUNET_CONTAINER_DLL_remove (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
2436 GNUNET_free (tmit_msg);
2437 return plc->tmit_msgs_head;
2438}
2439
2440
2441/**
2442 * Callback for data transmission to PSYC.
2443 */
2444static int
2445psyc_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
2446{
2447 struct Place *plc = cls;
2448 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
2449 GNUNET_assert (NULL != tmit_msg);
2450 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
2451 if (NULL == tmit_frag)
2452 { /* Rest of the message have not arrived yet, pause transmission */
2453 *data_size = 0;
2454 return GNUNET_NO;
2455 }
2456 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
2457 if (NULL == pmsg)
2458 {
2459 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2460 "%p psyc_transmit_notify_data: nothing to send.\n", plc);
2461 *data_size = 0;
2462 return GNUNET_NO;
2463 }
2464
2465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2466 "%p psyc_transmit_notify_data()\n", plc);
2467 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
2468
2469 uint16_t ptype = ntohs (pmsg->type);
2470 uint16_t pdata_size = ntohs (pmsg->size) - sizeof (*pmsg);
2471 int ret;
2472
2473 switch (ptype)
2474 {
2475 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
2476 if (*data_size < pdata_size)
2477 {
2478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2479 "%p psyc_transmit_notify_data: buffer size too small for data.\n", plc);
2480 *data_size = 0;
2481 return GNUNET_NO;
2482 }
2483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2484 "%p psyc_transmit_notify_data: sending %u bytes.\n",
2485 plc, pdata_size);
2486
2487 *data_size = pdata_size;
2488 GNUNET_memcpy (data, &pmsg[1], *data_size);
2489 ret = GNUNET_NO;
2490 break;
2491
2492 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
2493 *data_size = 0;
2494 ret = GNUNET_YES;
2495 break;
2496
2497 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
2498 *data_size = 0;
2499 ret = GNUNET_SYSERR;
2500 break;
2501
2502 default:
2503 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2504 "%p psyc_transmit_notify_data: unexpected message part of type %u.\n",
2505 plc, ptype);
2506 ret = GNUNET_SYSERR;
2507 }
2508
2509 if (GNUNET_SYSERR == ret && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL != ptype)
2510 {
2511 *data_size = 0;
2512 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
2513 GNUNET_SERVICE_client_drop (tmit_frag->client);
2514 GNUNET_SCHEDULER_add_now (&cleanup_place, plc);
2515 return ret;
2516 }
2517 else
2518 {
2519 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
2520 if (NULL != tmit_frag)
2521 {
2522 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
2523 ptype = ntohs (pmsg->type);
2524 switch (ptype)
2525 {
2526 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
2527 ret = GNUNET_YES;
2528 break;
2529 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
2530 ret = GNUNET_SYSERR;
2531 break;
2532 }
2533 switch (ptype)
2534 {
2535 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
2536 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
2537 tmit_frag = psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
2538 }
2539 }
2540
2541 if (NULL == tmit_msg->frags_head
2542 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
2543 { /* Reached end of current message. */
2544 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
2545 }
2546 }
2547
2548 if (ret != GNUNET_NO)
2549 {
2550 if (NULL != tmit_msg)
2551 {
2552 psyc_transmit_message (plc);
2553 }
2554 /* FIXME: handle partial message (when still in_transmit) */
2555 }
2556 return ret;
2557}
2558
2559
2560/**
2561 * Callback for modifier transmission to PSYC.
2562 */
2563static int
2564psyc_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
2565 uint8_t *oper, uint32_t *full_value_size)
2566{
2567 struct Place *plc = cls;
2568 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
2569 GNUNET_assert (NULL != tmit_msg);
2570 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
2571 if (NULL == tmit_frag)
2572 { /* Rest of the message have not arrived yet, pause transmission */
2573 *data_size = 0;
2574 return GNUNET_NO;
2575 }
2576 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
2577 if (NULL == pmsg)
2578 {
2579 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2580 "%p psyc_transmit_notify_mod: nothing to send.\n", plc);
2581 *data_size = 0;
2582 return GNUNET_NO;
2583 }
2584
2585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2586 "%p psyc_transmit_notify_mod()\n", plc);
2587 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, pmsg);
2588
2589 uint16_t ptype = ntohs (pmsg->type);
2590 int ret;
2591
2592 switch (ptype)
2593 {
2594 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
2595 {
2596 if (NULL == oper)
2597 {
2598 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2599 "%p psyc_transmit_notify_mod: oper is NULL.\n", plc);
2600 ret = GNUNET_SYSERR;
2601 break;
2602 }
2603 struct GNUNET_PSYC_MessageModifier *
2604 pmod = (struct GNUNET_PSYC_MessageModifier *) tmit_frag->next_part;
2605 uint16_t mod_size = ntohs (pmod->header.size) - sizeof (*pmod);
2606
2607 if (*data_size < mod_size)
2608 {
2609 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2610 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
2611 *data_size = 0;
2612 return GNUNET_NO;
2613 }
2614
2615 *full_value_size = ntohl (pmod->value_size);
2616 *oper = pmod->oper;
2617 *data_size = mod_size;
2618 GNUNET_memcpy (data, &pmod[1], mod_size);
2619 ret = GNUNET_NO;
2620 break;
2621 }
2622
2623 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
2624 {
2625 if (NULL != oper)
2626 {
2627 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2628 "%p psyc_transmit_notify_mod: oper is not NULL.\n", plc);
2629 ret = GNUNET_SYSERR;
2630 break;
2631 }
2632 uint16_t mod_size = ntohs (pmsg->size) - sizeof (*pmsg);
2633 if (*data_size < mod_size)
2634 {
2635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2636 "%p psyc_transmit_notify_mod: buffer size too small for data.\n", plc);
2637 *data_size = 0;
2638 return GNUNET_NO;
2639 }
2640 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2641 "%p psyc_transmit_notify_mod: sending %u bytes.\n", plc, mod_size);
2642
2643 *data_size = mod_size;
2644 GNUNET_memcpy (data, &pmsg[1], *data_size);
2645 ret = GNUNET_NO;
2646 break;
2647 }
2648
2649 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
2650 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
2651 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL:
2652 *data_size = 0;
2653 ret = GNUNET_YES;
2654 break;
2655
2656 default:
2657 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2658 "%p psyc_transmit_notify_mod: unexpected message part of type %u.\n",
2659 plc, ptype);
2660 ret = GNUNET_SYSERR;
2661 }
2662
2663 if (GNUNET_SYSERR == ret)
2664 {
2665 *data_size = 0;
2666 ret = GNUNET_SYSERR;
2667 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
2668 GNUNET_SERVICE_client_drop (tmit_frag->client);
2669 GNUNET_SCHEDULER_add_now (&cleanup_place, plc);
2670 }
2671 else
2672 {
2673 if (GNUNET_YES != ret)
2674 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
2675
2676 if (NULL == tmit_msg->frags_head
2677 && GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= ptype)
2678 { /* Reached end of current message. */
2679 tmit_msg = psyc_transmit_queue_next_msg (plc, tmit_msg);
2680 }
2681 }
2682 return ret;
2683}
2684
2685/**
2686 * Callback for data transmission from a host to PSYC.
2687 */
2688static int
2689host_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
2690{
2691 int ret = psyc_transmit_notify_data (cls, data_size, data);
2692
2693 if (GNUNET_NO != ret)
2694 {
2695 struct Host *hst = cls;
2696 hst->tmit_handle = NULL;
2697 }
2698 return ret;
2699}
2700
2701
2702/**
2703 * Callback for the transmit functions of multicast.
2704 */
2705static int
2706guest_transmit_notify_data (void *cls, uint16_t *data_size, void *data)
2707{
2708 int ret = psyc_transmit_notify_data (cls, data_size, data);
2709
2710 if (GNUNET_NO != ret)
2711 {
2712 struct Guest *gst = cls;
2713 gst->tmit_handle = NULL;
2714 }
2715 return ret;
2716}
2717
2718
2719/**
2720 * Callback for modifier transmission from a host to PSYC.
2721 */
2722static int
2723host_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
2724 uint8_t *oper, uint32_t *full_value_size)
2725{
2726 int ret = psyc_transmit_notify_mod (cls, data_size, data,
2727 oper, full_value_size);
2728 if (GNUNET_SYSERR == ret)
2729 {
2730 struct Host *hst = cls;
2731 hst->tmit_handle = NULL;
2732 }
2733 return ret;
2734}
2735
2736
2737/**
2738 * Callback for modifier transmission from a guest to PSYC.
2739 */
2740static int
2741guest_transmit_notify_mod (void *cls, uint16_t *data_size, void *data,
2742 uint8_t *oper, uint32_t *full_value_size)
2743{
2744 int ret = psyc_transmit_notify_mod (cls, data_size, data,
2745 oper, full_value_size);
2746 if (GNUNET_SYSERR == ret)
2747 {
2748 struct Guest *gst = cls;
2749 gst->tmit_handle = NULL;
2750 }
2751 return ret;
2752}
2753
2754
2755/**
2756 * Get method part of next message from transmission queue.
2757 *
2758 * @param plc
2759 * Place
2760 *
2761 * @return #GNUNET_OK on success
2762 * #GNUNET_NO if there are no more messages in queue.
2763 * #GNUNET_SYSERR if the next message is malformed.
2764 */
2765static struct GNUNET_PSYC_MessageMethod *
2766psyc_transmit_queue_next_method (struct Place *plc)
2767{
2768 struct MessageTransmitQueue *tmit_msg = plc->tmit_msgs_head;
2769 if (NULL == tmit_msg)
2770 return GNUNET_NO;
2771
2772 struct FragmentTransmitQueue *tmit_frag = tmit_msg->frags_head;
2773 if (NULL == tmit_frag)
2774 {
2775 GNUNET_break (0);
2776 return GNUNET_NO;
2777 }
2778
2779 struct GNUNET_MessageHeader *pmsg = tmit_frag->next_part;
2780 if (NULL == pmsg
2781 || GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD != ntohs (pmsg->type))
2782 {
2783 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2784 "%p psyc_transmit_queue_next_method: unexpected message part of type %u.\n",
2785 plc, NULL != pmsg ? ntohs (pmsg->type) : 0);
2786 GNUNET_break (0);
2787 return NULL;
2788 }
2789
2790 uint16_t psize = ntohs (pmsg->size);
2791 struct GNUNET_PSYC_MessageMethod *
2792 pmeth = (struct GNUNET_PSYC_MessageMethod *) GNUNET_copy_message (pmsg);
2793
2794 if (psize < sizeof (*pmeth) + 1 || '\0' != *((char *) pmeth + psize - 1))
2795 {
2796 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2797 "%p psyc_transmit_queue_next_method: invalid method name.\n",
2798 plc);
2799 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2800 "%zu <= %u || NUL != %u\n",
2801 sizeof (*pmeth), psize, *((char *) pmeth + psize - 1));
2802 GNUNET_break (0);
2803 GNUNET_free (pmeth);
2804 return NULL;
2805 }
2806
2807 psyc_transmit_queue_next_part (plc, tmit_msg, tmit_frag);
2808 return pmeth;
2809}
2810
2811
2812/**
2813 * Transmit the next message in queue from the host to the PSYC channel.
2814 */
2815static int
2816psyc_master_transmit_message (struct Host *hst)
2817{
2818 struct Place *plc = &hst->place;
2819
2820 if (NULL == hst->tmit_handle)
2821 {
2822 struct GNUNET_PSYC_MessageMethod *
2823 pmeth = psyc_transmit_queue_next_method (plc);
2824 if (NULL == pmeth)
2825 return GNUNET_SYSERR;
2826
2827 hst->tmit_handle = (void *) &hst->tmit_handle;
2828 struct GNUNET_PSYC_MasterTransmitHandle *
2829 tmit_handle = GNUNET_PSYC_master_transmit (hst->master, (const char *) &pmeth[1],
2830 &host_transmit_notify_mod,
2831 &host_transmit_notify_data, hst,
2832 pmeth->flags);
2833 if (NULL != hst->tmit_handle)
2834 hst->tmit_handle = tmit_handle;
2835 GNUNET_free (pmeth);
2836 }
2837 else
2838 {
2839 GNUNET_PSYC_master_transmit_resume (hst->tmit_handle);
2840 }
2841 return GNUNET_OK;
2842}
2843
2844
2845/**
2846 * Transmit the next message in queue from a guest to the PSYC channel.
2847 */
2848static int
2849psyc_slave_transmit_message (struct Guest *gst)
2850{
2851 struct Place *plc = &gst->place;
2852
2853 if (NULL == gst->tmit_handle)
2854 {
2855 struct GNUNET_PSYC_MessageMethod *
2856 pmeth = psyc_transmit_queue_next_method (plc);
2857 if (NULL == pmeth)
2858 return GNUNET_SYSERR;
2859
2860 gst->tmit_handle = (void *) &gst->tmit_handle;
2861 struct GNUNET_PSYC_SlaveTransmitHandle *
2862 tmit_handle = GNUNET_PSYC_slave_transmit (gst->slave, (const char *) &pmeth[1],
2863 &guest_transmit_notify_mod,
2864 &guest_transmit_notify_data, gst,
2865 pmeth->flags);
2866 if (NULL != gst->tmit_handle)
2867 gst->tmit_handle = tmit_handle;
2868 GNUNET_free (pmeth);
2869 }
2870 else
2871 {
2872 GNUNET_PSYC_slave_transmit_resume (gst->tmit_handle);
2873 }
2874 return GNUNET_OK;
2875}
2876
2877
2878/**
2879 * Transmit a message to PSYC.
2880 */
2881static int
2882psyc_transmit_message (struct Place *plc)
2883{
2884 return
2885 (plc->is_host)
2886 ? psyc_master_transmit_message ((struct Host *) plc)
2887 : psyc_slave_transmit_message ((struct Guest *) plc);
2888}
2889
2890
2891/**
2892 * Queue message parts for sending to PSYC.
2893 *
2894 * @param plc Place to send to.
2895 * @param client Client the message originates from.
2896 * @param data_size Size of @a data.
2897 * @param data Concatenated message parts.
2898 * @param first_ptype First message part type in @a data.
2899 * @param last_ptype Last message part type in @a data.
2900 */
2901static struct MessageTransmitQueue *
2902psyc_transmit_queue_message (struct Place *plc,
2903 struct GNUNET_SERVICE_Client *client,
2904 size_t data_size,
2905 const void *data,
2906 uint16_t first_ptype, uint16_t last_ptype,
2907 struct MessageTransmitQueue *tmit_msg)
2908{
2909 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD == first_ptype)
2910 {
2911 tmit_msg = GNUNET_malloc (sizeof (*tmit_msg));
2912 GNUNET_CONTAINER_DLL_insert_tail (plc->tmit_msgs_head, plc->tmit_msgs_tail, tmit_msg);
2913 }
2914 else if (NULL == tmit_msg)
2915 {
2916 return NULL;
2917 }
2918
2919 struct FragmentTransmitQueue *
2920 tmit_frag = GNUNET_malloc (sizeof (*tmit_frag) + data_size);
2921 GNUNET_memcpy (&tmit_frag[1], data, data_size);
2922 tmit_frag->next_part = (struct GNUNET_MessageHeader *) &tmit_frag[1];
2923 tmit_frag->client = client;
2924 tmit_frag->size = data_size;
2925
2926 GNUNET_CONTAINER_DLL_insert_tail (tmit_msg->frags_head, tmit_msg->frags_tail, tmit_frag);
2927 tmit_msg->client = client;
2928 return tmit_msg;
2929}
2930
2931
2932///**
2933// * Cancel transmission of current message to PSYC.
2934// *
2935// * @param plc Place to send to.
2936// * @param client Client the message originates from.
2937// */
2938//static void
2939//psyc_transmit_cancel (struct Place *plc, struct GNUNET_SERVICE_Client *client)
2940//{
2941// uint16_t type = GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_CANCEL;
2942//
2943// struct GNUNET_MessageHeader msg;
2944// msg.size = htons (sizeof (msg));
2945// msg.type = htons (type);
2946//
2947// psyc_transmit_queue_message (plc, client, sizeof (msg), &msg, type, type, NULL);
2948// psyc_transmit_message (plc);
2949//
2950// /* FIXME: cleanup */
2951//}
2952
2953
2954static int
2955check_client_psyc_message (void *cls,
2956 const struct GNUNET_MessageHeader *msg)
2957{
2958 return GNUNET_OK;
2959}
2960
2961
2962/**
2963 * Handle an incoming message from a client, to be transmitted to the place.
2964 */
2965static void
2966handle_client_psyc_message (void *cls,
2967 const struct GNUNET_MessageHeader *msg)
2968{
2969 struct Client *c = cls;
2970 struct GNUNET_SERVICE_Client *client = c->client;
2971 struct Place *plc = c->place;
2972 int ret;
2973
2974 if (NULL == plc)
2975 {
2976 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2977 "received PSYC message for non-existing client %p\n",
2978 client);
2979 GNUNET_break (0);
2980 GNUNET_SERVICE_client_drop (client);
2981 return;
2982 }
2983 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2984 "%p Received message of type %d from client.\n", plc, ntohs (msg->type));
2985 GNUNET_PSYC_log_message (GNUNET_ERROR_TYPE_DEBUG, msg);
2986
2987 if (GNUNET_YES != plc->is_ready)
2988 {
2989 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2990 "%p Place is not ready yet, disconnecting client.\n", plc);
2991 GNUNET_break (0);
2992 GNUNET_SERVICE_client_drop (client);
2993 return;
2994 }
2995
2996 uint16_t size = ntohs (msg->size);
2997 uint16_t psize = size - sizeof (*msg);
2998 if (psize < sizeof (struct GNUNET_MessageHeader)
2999 || GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < psize)
3000 {
3001 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3002 "%p Received message with invalid payload size (%u) from client.\n",
3003 plc, psize);
3004 GNUNET_break (0);
3005 GNUNET_SERVICE_client_drop (client);
3006 return;
3007 }
3008
3009 uint16_t first_ptype = 0;
3010 uint16_t last_ptype = 0;
3011 if (GNUNET_SYSERR ==
3012 GNUNET_PSYC_receive_check_parts (psize, (const char *) &msg[1],
3013 &first_ptype, &last_ptype))
3014 {
3015 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3016 "%p Received invalid message part from client.\n", plc);
3017 GNUNET_break (0);
3018 GNUNET_SERVICE_client_drop (client);
3019 return;
3020 }
3021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3022 "%p Received message with first part type %u and last part type %u.\n",
3023 plc, first_ptype, last_ptype);
3024
3025 c->tmit_msg
3026 = psyc_transmit_queue_message (plc, client, psize, &msg[1],
3027 first_ptype, last_ptype, c->tmit_msg);
3028 if (NULL != c->tmit_msg)
3029 {
3030 if (GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END <= last_ptype)
3031 c->tmit_msg = NULL;
3032 ret = psyc_transmit_message (plc);
3033 }
3034 else
3035 {
3036 ret = GNUNET_SYSERR;
3037 }
3038 if (GNUNET_OK != ret)
3039 {
3040 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3041 "%p Received invalid message part from client.\n", plc);
3042 GNUNET_break (0);
3043 GNUNET_SERVICE_client_drop (client);
3044 return;
3045 }
3046 GNUNET_SERVICE_client_continue (client);
3047}
3048
3049
3050/**
3051 * A historic message arrived from PSYC.
3052 */
3053static void
3054psyc_recv_history_message (void *cls, const struct GNUNET_PSYC_MessageHeader *msg)
3055{
3056 struct OperationClosure *opcls = cls;
3057 struct Client *c = opcls->client;
3058 struct Place *plc = c->place;
3059
3060 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3061 "%p Received historic message #%" PRId64 " (flags: %x)\n",
3062 plc, GNUNET_ntohll (msg->message_id), ntohl (msg->flags));
3063
3064 uint16_t size = ntohs (msg->header.size);
3065
3066 struct GNUNET_OperationResultMessage *
3067 res = GNUNET_malloc (sizeof (*res) + size);
3068 res->header.size = htons (sizeof (*res) + size);
3069 res->header.type = htons (GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT);
3070 res->op_id = opcls->op_id;
3071 res->result_code = GNUNET_htonll (GNUNET_OK);
3072
3073 GNUNET_memcpy (&res[1], msg, size);
3074
3075 /** @todo FIXME: send only to requesting client */
3076 place_send_msg (plc, GNUNET_MQ_msg_copy (&res->header));
3077
3078 GNUNET_free (res);
3079}
3080
3081
3082/**
3083 * Result of message history replay from PSYC.
3084 */
3085static void
3086psyc_recv_history_result (void *cls, int64_t result,
3087 const void *err_msg, uint16_t err_msg_size)
3088{
3089 struct OperationClosure *opcls = cls;
3090 struct Client *c = opcls->client;
3091 struct Place *plc = c->place;
3092
3093 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3094 "%p History replay #%" PRIu64 ": "
3095 "PSYCstore returned %" PRId64 " (%.*s)\n",
3096 plc, GNUNET_ntohll (opcls->op_id), result,
3097 err_msg_size, (const char *) err_msg);
3098
3099 // FIXME: place might have been destroyed
3100 client_send_result (c->client, opcls->op_id, result, err_msg, err_msg_size);
3101}
3102
3103
3104static int
3105check_client_history_replay (void *cls,
3106 const struct GNUNET_PSYC_HistoryRequestMessage *req)
3107{
3108 return GNUNET_OK;
3109}
3110
3111
3112/**
3113 * Client requests channel history.
3114 */
3115static void
3116handle_client_history_replay (void *cls,
3117 const struct GNUNET_PSYC_HistoryRequestMessage *req)
3118{
3119 struct Client *c = cls;
3120 struct GNUNET_SERVICE_Client *client = c->client;
3121 struct Place *plc = c->place;
3122 if (NULL == plc)
3123 {
3124 GNUNET_break (0);
3125 GNUNET_SERVICE_client_drop (client);
3126 return;
3127 }
3128
3129 uint16_t size = ntohs (req->header.size);
3130 const char *method_prefix = (const char *) &req[1];
3131
3132 if (size < sizeof (*req) + 1
3133 || '\0' != method_prefix[size - sizeof (*req) - 1])
3134 {
3135 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3136 "%p History replay #%" PRIu64 ": "
3137 "invalid method prefix. size: %u < %zu?\n",
3138 plc, GNUNET_ntohll (req->op_id), size, sizeof (*req) + 1);
3139 GNUNET_break (0);
3140 GNUNET_SERVICE_client_drop (client);
3141 return;
3142 }
3143
3144 struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
3145 opcls->client = c;
3146 opcls->op_id = req->op_id;
3147 opcls->flags = ntohl (req->flags);
3148
3149 if (0 == req->message_limit)
3150 GNUNET_PSYC_channel_history_replay (plc->channel,
3151 GNUNET_ntohll (req->start_message_id),
3152 GNUNET_ntohll (req->end_message_id),
3153 method_prefix, opcls->flags,
3154 psyc_recv_history_message, NULL,
3155 psyc_recv_history_result, opcls);
3156 else
3157 GNUNET_PSYC_channel_history_replay_latest (plc->channel,
3158 GNUNET_ntohll (req->message_limit),
3159 method_prefix, opcls->flags,
3160 psyc_recv_history_message, NULL,
3161 psyc_recv_history_result, opcls);
3162
3163 GNUNET_SERVICE_client_continue (client);
3164}
3165
3166
3167/**
3168 * A state variable part arrived from PSYC.
3169 */
3170void
3171psyc_recv_state_var (void *cls,
3172 const struct GNUNET_MessageHeader *mod,
3173 const char *name,
3174 const void *value,
3175 uint32_t value_size,
3176 uint32_t full_value_size)
3177{
3178 struct GNUNET_OperationResultMessage *result_msg;
3179 struct GNUNET_MQ_Envelope *env;
3180 struct OperationClosure *opcls = cls;
3181 struct Client *c = opcls->client;
3182 struct Place *plc = c->place;
3183 uint16_t size = ntohs (mod->size);
3184
3185 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3186 "%p Received state variable %s from PSYC\n",
3187 plc, name);
3188 env = GNUNET_MQ_msg_extra (result_msg,
3189 size,
3190 GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT);
3191 result_msg->op_id = opcls->op_id;
3192 result_msg->result_code = GNUNET_htonll (GNUNET_OK);
3193 GNUNET_memcpy (&result_msg[1], mod, size);
3194 /** @todo FIXME: send only to requesting client */
3195 place_send_msg (plc, env);
3196}
3197
3198
3199/**
3200 * Result of retrieving state variable from PSYC.
3201 */
3202static void
3203psyc_recv_state_result (void *cls, int64_t result,
3204 const void *err_msg, uint16_t err_msg_size)
3205{
3206 struct OperationClosure *opcls = cls;
3207 struct Client *c = opcls->client;
3208 struct Place *plc = c->place;
3209
3210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3211 "%p State get #%" PRIu64 ": "
3212 "PSYCstore returned %" PRId64 " (%.*s)\n",
3213 plc, GNUNET_ntohll (opcls->op_id), result,
3214 err_msg_size, (const char *) err_msg);
3215
3216 // FIXME: place might have been destroyed
3217 client_send_result (c->client, opcls->op_id, result, err_msg, err_msg_size);
3218}
3219
3220
3221static int
3222check_client_state_get (void *cls,
3223 const struct GNUNET_PSYC_StateRequestMessage *req)
3224{
3225 return GNUNET_OK;
3226}
3227
3228
3229/**
3230 * Client requests channel history.
3231 */
3232static void
3233handle_client_state_get (void *cls,
3234 const struct GNUNET_PSYC_StateRequestMessage *req)
3235{
3236 struct Client *c = cls;
3237 struct GNUNET_SERVICE_Client *client = c->client;
3238 struct Place *plc = c->place;
3239 if (NULL == plc)
3240 {
3241 GNUNET_break (0);
3242 GNUNET_SERVICE_client_drop (client);
3243 return;
3244 }
3245
3246 uint16_t size = ntohs (req->header.size);
3247 const char *name = (const char *) &req[1];
3248
3249 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3250 "%p State get #%" PRIu64 ": %s\n",
3251 plc, GNUNET_ntohll (req->op_id), name);
3252
3253 if (size < sizeof (*req) + 1
3254 || '\0' != name[size - sizeof (*req) - 1])
3255 {
3256 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3257 "%p State get #%" PRIu64 ": "
3258 "invalid name. size: %u < %zu?\n",
3259 plc, GNUNET_ntohll (req->op_id), size, sizeof (*req) + 1);
3260 GNUNET_break (0);
3261 GNUNET_SERVICE_client_drop (client);
3262 return;
3263 }
3264
3265 struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
3266 opcls->client = c;
3267 opcls->op_id = req->op_id;
3268
3269 switch (ntohs (req->header.type))
3270 {
3271 case GNUNET_MESSAGE_TYPE_PSYC_STATE_GET:
3272 GNUNET_PSYC_channel_state_get (plc->channel, name,
3273 psyc_recv_state_var,
3274 psyc_recv_state_result, opcls);
3275 break;
3276
3277 case GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX:
3278 GNUNET_PSYC_channel_state_get_prefix (plc->channel, name,
3279 psyc_recv_state_var,
3280 psyc_recv_state_result, opcls);
3281 break;
3282
3283 default:
3284 GNUNET_assert (0);
3285 }
3286
3287 GNUNET_SERVICE_client_continue (client);
3288}
3289
3290
3291#define check_client_state_get_prefix check_client_state_get
3292#define handle_client_state_get_prefix handle_client_state_get
3293
3294
3295static void
3296namestore_recv_records_store_result (void *cls, int32_t result,
3297 const char *err_msg)
3298{
3299 struct OperationClosure *opcls = cls;
3300 struct Client *c = opcls->client;
3301
3302 // FIXME: client might have been disconnected
3303 client_send_result (c->client, opcls->op_id, result, err_msg,
3304 (NULL != err_msg) ? strlen (err_msg) : 0);
3305 GNUNET_free (opcls);
3306}
3307
3308
3309static int
3310check_client_zone_add_place (void *cls,
3311 const struct ZoneAddPlaceRequest *preq)
3312{
3313 return GNUNET_OK;
3314}
3315
3316
3317/**
3318 * Handle request to add PLACE record to GNS zone.
3319 */
3320static void
3321handle_client_zone_add_place (void *cls,
3322 const struct ZoneAddPlaceRequest *preq)
3323{
3324 struct Client *c = cls;
3325 struct GNUNET_SERVICE_Client *client = c->client;
3326
3327 uint16_t remaining = ntohs (preq->header.size) - sizeof (*preq);
3328 const char *p = (const char *) &preq[1];
3329 const char *name = NULL, *password = NULL;
3330 uint16_t offset = GNUNET_STRINGS_buffer_tokenize (p, remaining, 2,
3331 &name, &password);
3332 remaining -= offset;
3333 p += offset;
3334 const struct GNUNET_PeerIdentity *
3335 relays = (const struct GNUNET_PeerIdentity *) p;
3336 uint16_t relay_size = ntohl (preq->relay_count) * sizeof (*relays);
3337
3338 if (0 == offset || remaining != relay_size)
3339 {
3340 GNUNET_break (0);
3341 client_send_result (client, preq->op_id, GNUNET_SYSERR, NULL, 0);
3342 GNUNET_SERVICE_client_drop (client);
3343 return;
3344 }
3345
3346 struct GNUNET_GNSRECORD_Data rd = { };
3347 rd.record_type = GNUNET_GNSRECORD_TYPE_PLACE;
3348 rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
3349 rd.expiration_time = GNUNET_ntohll (preq->expiration_time);
3350
3351 struct GNUNET_GNSRECORD_PlaceData *
3352 rec = GNUNET_malloc (sizeof (*rec) + relay_size);
3353 rec->place_pub_key = preq->place_pub_key;
3354 rec->origin = this_peer;
3355 rec->relay_count = preq->relay_count;
3356 GNUNET_memcpy (&rec[1], relays, relay_size);
3357
3358 rd.data = rec;
3359 rd.data_size = sizeof (*rec) + relay_size;
3360
3361 struct GNUNET_HashCode ego_pub_hash;
3362 GNUNET_CRYPTO_hash (&preq->ego_pub_key, sizeof (preq->ego_pub_key), &ego_pub_hash);
3363 struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
3364 if (NULL == ego)
3365 {
3366 client_send_result (client, preq->op_id, GNUNET_SYSERR, NULL, 0);
3367 }
3368 else
3369 {
3370 struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
3371 opcls->client = c;
3372 opcls->op_id = preq->op_id;
3373 GNUNET_NAMESTORE_records_store (namestore, &ego->key,
3374 name, 1, &rd,
3375 namestore_recv_records_store_result, opcls);
3376 /** @todo refresh stored records later */
3377 }
3378 GNUNET_SERVICE_client_continue (client);
3379}
3380
3381
3382static int
3383check_client_zone_add_nym (void *cls,
3384 const struct ZoneAddNymRequest *nreq)
3385{
3386 return GNUNET_OK;
3387}
3388
3389
3390/**
3391 * Handle request to add PLACE record to GNS zone.
3392 */
3393static void
3394handle_client_zone_add_nym (void *cls,
3395 const struct ZoneAddNymRequest *nreq)
3396{
3397 struct Client *c = cls;
3398 struct GNUNET_SERVICE_Client *client = c->client;
3399
3400 uint16_t name_size = ntohs (nreq->header.size) - sizeof (*nreq);
3401 const char *name = NULL;
3402 uint16_t offset = GNUNET_STRINGS_buffer_tokenize ((const char *) &nreq[1],
3403 name_size, 1, &name);
3404 if (0 == offset || offset != name_size)
3405 {
3406 GNUNET_break (0);
3407 client_send_result (client, nreq->op_id, GNUNET_SYSERR, NULL, 0);
3408 GNUNET_SERVICE_client_continue (client);
3409 return;
3410 }
3411
3412 struct GNUNET_GNSRECORD_Data rd = { };
3413 rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
3414 rd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
3415 rd.expiration_time = GNUNET_ntohll (nreq->expiration_time);
3416 rd.data = &nreq->nym_pub_key;
3417 rd.data_size = sizeof (nreq->nym_pub_key);
3418
3419 struct GNUNET_HashCode ego_pub_hash;
3420 GNUNET_CRYPTO_hash (&nreq->ego_pub_key, sizeof (nreq->ego_pub_key), &ego_pub_hash);
3421 struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
3422 if (NULL == ego)
3423 {
3424 client_send_result (client, nreq->op_id, GNUNET_SYSERR, NULL, 0);
3425 }
3426 else
3427 {
3428 struct OperationClosure *opcls = GNUNET_malloc (sizeof (*opcls));
3429 opcls->client = c;
3430 opcls->op_id = nreq->op_id;
3431 GNUNET_NAMESTORE_records_store (namestore, &ego->key,
3432 name, 1, &rd,
3433 namestore_recv_records_store_result, opcls);
3434 /** @todo refresh stored records later */
3435 }
3436 GNUNET_SERVICE_client_continue (client);
3437}
3438
3439
3440const char *
3441path_basename (const char *path)
3442{
3443 const char *basename = strrchr (path, DIR_SEPARATOR);
3444 if (NULL != basename)
3445 basename++;
3446
3447 if (NULL == basename || '\0' == *basename)
3448 return NULL;
3449
3450 return basename;
3451}
3452
3453
3454struct PlaceLoadClosure
3455{
3456 const char *app_id;
3457 const char *ego_pub_str;
3458};
3459
3460
3461/** Load a place file */
3462int
3463file_place_load (void *cls, const char *place_filename)
3464{
3465 struct PlaceLoadClosure *plcls = cls;
3466
3467 const char *place_pub_str = path_basename (place_filename);
3468 if (NULL == place_pub_str)
3469 {
3470 GNUNET_break (0);
3471 return GNUNET_OK;
3472 }
3473
3474 char *filename = NULL;
3475 GNUNET_asprintf (&filename, "%s%c" "%s%c" "%s%c" "%s",
3476 dir_social, DIR_SEPARATOR,
3477 "places", DIR_SEPARATOR,
3478 plcls->ego_pub_str, DIR_SEPARATOR,
3479 place_pub_str);
3480
3481 uint64_t file_size = 0;
3482 if (GNUNET_OK !=
3483 GNUNET_DISK_file_size (filename, &file_size, GNUNET_YES, GNUNET_YES)
3484 || file_size < sizeof (struct PlaceEnterRequest))
3485 {
3486 GNUNET_free (filename);
3487 return GNUNET_OK;
3488 }
3489
3490 struct PlaceEnterRequest *ereq = GNUNET_malloc (file_size);
3491 ssize_t read_size = GNUNET_DISK_fn_read (filename, ereq, file_size);
3492 GNUNET_free (filename);
3493 if (read_size < 0 || read_size < sizeof (*ereq))
3494 {
3495 GNUNET_free (ereq);
3496 return GNUNET_OK;
3497 }
3498
3499 uint16_t ereq_size = ntohs (ereq->header.size);
3500 if (read_size != ereq_size)
3501 {
3502 GNUNET_free (ereq);
3503 return GNUNET_OK;
3504 }
3505
3506 switch (ntohs (ereq->header.type))
3507 {
3508 case GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER:
3509 if (ereq_size < sizeof (struct HostEnterRequest))
3510 {
3511 GNUNET_free (ereq);
3512 return GNUNET_OK;
3513 }
3514 struct HostEnterRequest *hreq = (struct HostEnterRequest *) ereq;
3515 host_enter (hreq, NULL);
3516 break;
3517
3518 case GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER:
3519 if (ereq_size < sizeof (struct GuestEnterRequest))
3520 {
3521 GNUNET_free (ereq);
3522 return GNUNET_OK;
3523 }
3524 struct GuestEnterRequest *greq = (struct GuestEnterRequest *) ereq;
3525 guest_enter (greq, NULL);
3526 break;
3527
3528 default:
3529 GNUNET_free (ereq);
3530 return GNUNET_OK;
3531 }
3532
3533 if (GNUNET_SYSERR == app_place_add (plcls->app_id, ereq))
3534 {
3535 GNUNET_assert (0);
3536 }
3537 GNUNET_free (ereq);
3538 return GNUNET_OK;
3539}
3540
3541
3542/**
3543 * Read @e place_pub_str entries in @a dir_ego
3544 *
3545 * @param dir_ego
3546 * Data directory of an application ego.
3547 * $GNUNET_DATA_HOME/social/apps/$app_id/$ego_pub_str/
3548 */
3549int
3550scan_app_ego_dir (void *cls, const char *dir_ego)
3551{
3552 struct PlaceLoadClosure *plcls = cls;
3553 plcls->ego_pub_str = path_basename (dir_ego);
3554
3555 if (NULL != plcls->ego_pub_str)
3556 GNUNET_DISK_directory_scan (dir_ego, file_place_load, plcls);
3557
3558 return GNUNET_OK;
3559}
3560
3561/**
3562 * Read @e ego_pub_str entries in @a dir_app
3563 *
3564 * @param dir_app
3565 * Data directory of an application.
3566 * $GNUNET_DATA_HOME/social/apps/$app_id/
3567 */
3568int
3569scan_app_dir (void *cls, const char *dir_app)
3570{
3571 if (GNUNET_YES != GNUNET_DISK_directory_test (dir_app, GNUNET_YES))
3572 return GNUNET_OK;
3573
3574 struct PlaceLoadClosure plcls;
3575 plcls.app_id = path_basename (dir_app);
3576
3577 if (NULL != plcls.app_id)
3578 GNUNET_DISK_directory_scan (dir_app, scan_app_ego_dir, &plcls);
3579
3580 return GNUNET_OK;
3581}
3582
3583
3584static void
3585identity_recv_ego (void *cls, struct GNUNET_IDENTITY_Ego *id_ego,
3586 void **ctx, const char *name)
3587{
3588 if (NULL == id_ego) // end of initial list of egos
3589 return;
3590
3591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3592 "social service received ego %s\n",
3593 name);
3594
3595 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
3596 GNUNET_IDENTITY_ego_get_public_key (id_ego, &ego_pub_key);
3597
3598 struct GNUNET_HashCode ego_pub_hash;
3599 GNUNET_CRYPTO_hash (&ego_pub_key, sizeof (ego_pub_key), &ego_pub_hash);
3600
3601 struct Ego *ego = GNUNET_CONTAINER_multihashmap_get (egos, &ego_pub_hash);
3602 if (NULL == ego && NULL == name)
3603 {
3604 // an ego that is none of our business has been deleted
3605 return;
3606 }
3607 if (NULL != ego)
3608 {
3609 // one of our egos has been changed
3610 GNUNET_free (ego->name);
3611 if (NULL == name)
3612 {
3613 // one of our egos has been deleted
3614 GNUNET_CONTAINER_multihashmap_remove (egos, &ego_pub_hash, ego);
3615 GNUNET_free (ego);
3616 return;
3617 }
3618 }
3619 else
3620 {
3621 ego = GNUNET_malloc (sizeof (*ego));
3622 }
3623 ego->key = *(GNUNET_IDENTITY_ego_get_private_key (id_ego));
3624 size_t name_size = strlen (name) + 1;
3625 ego->name = GNUNET_malloc (name_size);
3626 GNUNET_memcpy (ego->name, name, name_size);
3627
3628 GNUNET_CONTAINER_multihashmap_put (egos, &ego_pub_hash, ego,
3629 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
3630
3631 // FIXME: notify clients about changed ego
3632}
3633
3634
3635/**
3636 * Initialize the PSYC service.
3637 *
3638 * @param cls Closure.
3639 * @param server The initialized server.
3640 * @param c Configuration to use.
3641 */
3642static void
3643run (void *cls,
3644 const struct GNUNET_CONFIGURATION_Handle *c,
3645 struct GNUNET_SERVICE_Handle *svc)
3646{
3647 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3648 "starting social service\n");
3649
3650 cfg = c;
3651 service = svc;
3652 GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
3653
3654 hosts = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
3655 guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
3656 place_guests = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
3657
3658 egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
3659 apps = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
3660 places = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
3661 apps_places = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
3662 //places_apps = GNUNET_CONTAINER_multihashmap_create(1, GNUNET_NO);
3663
3664 id = GNUNET_IDENTITY_connect (cfg, &identity_recv_ego, NULL);
3665 gns = GNUNET_GNS_connect (cfg);
3666 namestore = GNUNET_NAMESTORE_connect (cfg);
3667 stats = GNUNET_STATISTICS_create ("social", cfg);
3668
3669 if (GNUNET_OK !=
3670 GNUNET_CONFIGURATION_get_value_filename (cfg, "social", "DATA_HOME",
3671 &dir_social))
3672 {
3673 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
3674 "social", "DATA_HOME");
3675 GNUNET_break (0);
3676 return;
3677 }
3678 GNUNET_asprintf (&dir_places, "%s%c%s",
3679 dir_social, DIR_SEPARATOR, "places");
3680 GNUNET_asprintf (&dir_apps, "%s%c%s",
3681 dir_social, DIR_SEPARATOR, "apps");
3682
3683 GNUNET_DISK_directory_scan (dir_apps, scan_app_dir, NULL);
3684
3685 GNUNET_SCHEDULER_add_shutdown (shutdown_task, NULL);
3686}
3687
3688
3689/**
3690 * Define "main" method using service macro.
3691 */
3692GNUNET_SERVICE_MAIN
3693("social",
3694 GNUNET_SERVICE_OPTION_NONE,
3695 run,
3696 client_notify_connect,
3697 client_notify_disconnect,
3698 NULL,
3699 GNUNET_MQ_hd_var_size (client_host_enter,
3700 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER,
3701 struct HostEnterRequest,
3702 NULL),
3703 GNUNET_MQ_hd_var_size (client_guest_enter,
3704 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER,
3705 struct GuestEnterRequest,
3706 NULL),
3707 GNUNET_MQ_hd_var_size (client_guest_enter_by_name,
3708 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME,
3709 struct GuestEnterByNameRequest,
3710 NULL),
3711 GNUNET_MQ_hd_var_size (client_join_decision,
3712 GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
3713 struct GNUNET_PSYC_JoinDecisionMessage,
3714 NULL),
3715 GNUNET_MQ_hd_var_size (client_psyc_message,
3716 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
3717 struct GNUNET_MessageHeader,
3718 NULL),
3719 GNUNET_MQ_hd_var_size (client_history_replay,
3720 GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY,
3721 struct GNUNET_PSYC_HistoryRequestMessage,
3722 NULL),
3723 GNUNET_MQ_hd_var_size (client_state_get,
3724 GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
3725 struct GNUNET_PSYC_StateRequestMessage,
3726 NULL),
3727 GNUNET_MQ_hd_var_size (client_state_get_prefix,
3728 GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
3729 struct GNUNET_PSYC_StateRequestMessage,
3730 NULL),
3731 GNUNET_MQ_hd_var_size (client_zone_add_place,
3732 GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE,
3733 struct ZoneAddPlaceRequest,
3734 NULL),
3735 GNUNET_MQ_hd_var_size (client_zone_add_nym,
3736 GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM,
3737 struct ZoneAddNymRequest,
3738 NULL),
3739 GNUNET_MQ_hd_var_size (client_app_connect,
3740 GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT,
3741 struct AppConnectRequest,
3742 NULL),
3743 GNUNET_MQ_hd_fixed_size (client_app_detach,
3744 GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH,
3745 struct AppDetachRequest,
3746 NULL),
3747 GNUNET_MQ_hd_fixed_size (client_place_leave,
3748 GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE,
3749 struct GNUNET_MessageHeader,
3750 NULL),
3751 GNUNET_MQ_hd_var_size (client_msg_proc_set,
3752 GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET,
3753 struct MsgProcRequest,
3754 NULL),
3755 GNUNET_MQ_hd_fixed_size (client_msg_proc_clear,
3756 GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR,
3757 struct GNUNET_MessageHeader,
3758 NULL));
3759
3760/* end of gnunet-service-social.c */
diff --git a/src/social/gnunet-social.c b/src/social/gnunet-social.c
new file mode 100644
index 0000000..14701bf
--- /dev/null
+++ b/src/social/gnunet-social.c
@@ -0,0 +1,1411 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2016 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/**
22 * CLI tool to interact with the social service.
23 *
24 * @author Gabor X Toth
25 */
26
27#include <inttypes.h>
28
29#include "platform.h"
30#include "gnunet_util_lib.h"
31#include "gnunet_social_service.h"
32
33#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
34
35#define DATA2ARG(data) data, sizeof (data)
36
37/* operations corresponding to API calls */
38
39/** --status */
40static int op_status;
41
42/** --host-enter */
43static int op_host_enter;
44
45/** --host-reconnect */
46static int op_host_reconnect;
47
48/** --host-leave */
49static int op_host_leave;
50
51/** --host-announce */
52static int op_host_announce;
53
54/** --host-assign */
55static int op_host_assign;
56
57/** --guest-enter */
58static int op_guest_enter;
59
60/** --guest-reconnect */
61static int op_guest_reconnect;
62
63/** --guest-leave */
64static int op_guest_leave;
65
66/** --guest-talk */
67static int op_guest_talk;
68
69/** --replay */
70static int op_replay;
71
72/** --replay-latest */
73static int op_replay_latest;
74
75/** --look-at */
76static int op_look_at;
77
78/** --look-for */
79static int op_look_for;
80
81
82/* options */
83
84/** --app */
85static char *opt_app = "cli";
86
87/** --place */
88static char *opt_place;
89
90/** --ego */
91static char *opt_ego;
92
93/** --gns */
94static char *opt_gns;
95
96/** --peer */
97static char *opt_peer;
98
99/** --follow */
100static int opt_follow;
101
102/** --welcome */
103static int opt_welcome;
104
105/** --deny */
106static int opt_deny;
107
108/** --method */
109static char *opt_method;
110
111/** --data */
112// FIXME: should come from STDIN
113static char *opt_data;
114
115/** --name */
116static char *opt_name;
117
118/** --start */
119static unsigned long long opt_start;
120
121/** --until */
122static unsigned long long opt_until;
123
124/** --limit */
125static unsigned long long opt_limit;
126
127
128/* global vars */
129
130/** exit code */
131static int ret = 1;
132
133/** are we waiting for service to close our connection */
134static char is_disconnecting = 0;
135
136/** Task handle for timeout termination. */
137struct GNUNET_SCHEDULER_Task *timeout_task;
138
139const struct GNUNET_CONFIGURATION_Handle *cfg;
140
141struct GNUNET_PeerIdentity peer, this_peer;
142
143struct GNUNET_SOCIAL_App *app;
144
145/** public key of connected place */
146struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
147
148struct GNUNET_PSYC_Slicer *slicer;
149
150struct GNUNET_SOCIAL_Ego *ego;
151struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
152
153struct GNUNET_SOCIAL_Host *hst;
154struct GNUNET_SOCIAL_Guest *gst;
155struct GNUNET_SOCIAL_Place *plc;
156
157const char *method_received;
158
159
160/* DISCONNECT */
161
162
163/**
164 * Callback called after the host or guest place disconnected.
165 */
166static void
167disconnected (void *cls)
168{
169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnected()\n");
170 GNUNET_SCHEDULER_shutdown ();
171}
172
173
174/**
175 * Callback called after the application disconnected.
176 */
177static void
178app_disconnected (void *cls)
179{
180 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "app_disconnected()\n");
181 if (hst || gst)
182 {
183 if (hst)
184 {
185 GNUNET_SOCIAL_host_disconnect (hst, disconnected, NULL);
186 }
187 if (gst)
188 {
189 GNUNET_SOCIAL_guest_disconnect (gst, disconnected, NULL);
190 }
191 }
192 else
193 {
194 GNUNET_SCHEDULER_shutdown ();
195 }
196}
197
198
199/**
200 * Disconnect from connected GNUnet services.
201 */
202static void
203disconnect ()
204{
205 // handle that we get called several times from several places, but should we?
206 if (!is_disconnecting++) {
207 GNUNET_SOCIAL_app_disconnect (app, app_disconnected, NULL);
208 }
209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnect() called for the #%d time\n", is_disconnecting);
210}
211
212
213static void
214scheduler_shutdown (void *cls)
215{
216 disconnect ();
217}
218
219
220/**
221 * Callback called when the program failed to finish the requested operation in time.
222 */
223static void
224timeout (void *cls)
225{
226 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "timeout()\n");
227 disconnect ();
228}
229
230static void
231schedule_success (void *cls)
232{
233 ret = 0;
234 disconnect ();
235}
236
237
238static void
239schedule_fail (void *cls)
240{
241 disconnect ();
242}
243
244
245/**
246 * Schedule exit with success result.
247 */
248static void
249exit_success ()
250{
251 if (timeout_task != NULL)
252 {
253 GNUNET_SCHEDULER_cancel (timeout_task);
254 timeout_task = NULL;
255 }
256 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_success, NULL);
257}
258
259
260/**
261 * Schedule exit with failure result.
262 */
263static void
264exit_fail ()
265{
266 if (timeout_task != NULL)
267 {
268 GNUNET_SCHEDULER_cancel (timeout_task);
269 timeout_task = NULL;
270 }
271 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_fail, NULL);
272}
273
274
275/* LEAVE */
276
277
278/**
279 * Callback notifying about the host has left and stopped hosting the place.
280 *
281 * This also indicates the end of the connection to the service.
282 */
283static void
284host_left (void *cls)
285{
286 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
287 "The host has left the place.\n");
288 exit_success ();
289}
290
291
292/**
293 * Leave a place permanently and stop hosting a place.
294 */
295static void
296host_leave ()
297{
298 GNUNET_SOCIAL_host_leave (hst, NULL, host_left, NULL);
299 hst = NULL;
300 plc = NULL;
301}
302
303
304/**
305 * Callback notifying about the guest has left the place.
306 *
307 * This also indicates the end of the connection to the service.
308 */
309static void
310guest_left (void *cls)
311{
312 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
313 "Guest has left the place.\n");
314}
315
316
317/**
318 * Leave a place permanently as guest.
319 */
320static void
321guest_leave ()
322{
323 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
324 // FIXME: wrong use of vars
325 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
326 "_message", DATA2ARG ("Leaving."));
327 GNUNET_SOCIAL_guest_leave (gst, env, guest_left, NULL);
328 GNUNET_PSYC_env_destroy (env);
329 gst = NULL;
330 plc = NULL;
331}
332
333
334/* ANNOUNCE / ASSIGN / TALK */
335
336
337struct TransmitClosure
338{
339 const char *data;
340 size_t size;
341} tmit;
342
343
344/**
345 * Callback notifying about available buffer space to write message data
346 * when transmitting messages using host_announce() or guest_talk()
347 */
348static int
349notify_data (void *cls, uint16_t *data_size, void *data)
350{
351 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352 "Transmit notify data: %u bytes available\n",
353 *data_size);
354
355 struct TransmitClosure *tmit = cls;
356 uint16_t size = tmit->size < *data_size ? tmit->size : *data_size;
357 *data_size = size;
358 GNUNET_memcpy (data, tmit->data, size);
359
360 tmit->size -= size;
361 tmit->data += size;
362
363 if (0 == tmit->size)
364 {
365 if ((op_host_announce || op_host_assign || op_guest_talk) && !opt_follow)
366 {
367 exit_success ();
368 }
369 return GNUNET_YES;
370 }
371 else
372 {
373 return GNUNET_NO;
374 }
375}
376
377
378/**
379 * Host announcement - send a message to the place.
380 */
381static void
382host_announce (const char *method, const char *data, size_t data_size)
383{
384 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
385 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
386 "_foo", DATA2ARG ("bar baz"));
387
388 tmit = (struct TransmitClosure) {};
389 tmit.data = data;
390 tmit.size = data_size;
391
392 GNUNET_SOCIAL_host_announce (hst, method, env,
393 notify_data, &tmit,
394 GNUNET_SOCIAL_ANNOUNCE_NONE);
395 GNUNET_PSYC_env_destroy (env);
396}
397
398
399/**
400 * Assign a state var of @a name to the value of @a data.
401 */
402static void
403host_assign (const char *name, const char *data, size_t data_size)
404{
405 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
406 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
407 name, data, data_size);
408
409 tmit = (struct TransmitClosure) {};
410 GNUNET_SOCIAL_host_announce (hst, "_assign", env,
411 notify_data, &tmit,
412 GNUNET_SOCIAL_ANNOUNCE_NONE);
413 GNUNET_PSYC_env_destroy (env);
414}
415
416
417/**
418 * Guest talk request to host.
419 */
420static void
421guest_talk (const char *method,
422 const char *data, size_t data_size)
423{
424 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
425 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
426 "_foo", DATA2ARG ("bar baz"));
427
428 tmit = (struct TransmitClosure) {};
429 tmit.data = data;
430 tmit.size = data_size;
431
432 GNUNET_SOCIAL_guest_talk (gst, method, env,
433 notify_data, &tmit,
434 GNUNET_SOCIAL_TALK_NONE);
435 GNUNET_PSYC_env_destroy (env);
436}
437
438
439/* HISTORY REPLAY */
440
441
442/**
443 * Callback notifying about the end of history replay results.
444 */
445static void
446recv_history_replay_result (void *cls, int64_t result,
447 const void *data, uint16_t data_size)
448{
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 "Received history replay result: %" PRId64 "\n"
451 "%.*s\n",
452 result, data_size, (const char *) data);
453
454 if (op_replay || op_replay_latest)
455 {
456 exit_success ();
457 }
458}
459
460
461/**
462 * Replay history between a given @a start and @a end message IDs,
463 * optionally filtered by a method @a prefix.
464 */
465static void
466history_replay (uint64_t start, uint64_t end, const char *prefix)
467{
468 GNUNET_SOCIAL_place_history_replay (plc, start, end, prefix,
469 GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
470 slicer,
471 recv_history_replay_result,
472 NULL);
473}
474
475
476/**
477 * Replay latest @a limit messages.
478 */
479static void
480history_replay_latest (uint64_t limit, const char *prefix)
481{
482 GNUNET_SOCIAL_place_history_replay_latest (plc, limit, prefix,
483 GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
484 slicer,
485 recv_history_replay_result,
486 NULL);
487}
488
489
490/* LOOK AT/FOR */
491
492
493/**
494 * Callback notifying about the end of state var results.
495 */
496static void
497look_result (void *cls, int64_t result_code,
498 const void *data, uint16_t data_size)
499{
500 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
501 "Received look result: %" PRId64 "\n", result_code);
502
503 if (op_look_at || op_look_for)
504 {
505 exit_success ();
506 }
507}
508
509
510/**
511 * Callback notifying about a state var result.
512 */
513static void
514look_var (void *cls,
515 const struct GNUNET_MessageHeader *mod,
516 const char *name,
517 const void *value,
518 uint32_t value_size,
519 uint32_t full_value_size)
520{
521 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
522 "Received var: %s\n%.*s\n",
523 name, value_size, (const char *) value);
524}
525
526
527/**
528 * Look for a state var using exact match of the name.
529 */
530static void
531look_at (const char *full_name)
532{
533 GNUNET_SOCIAL_place_look_at (plc, full_name, look_var, look_result, NULL);
534}
535
536
537/**
538 * Look for state vars by name prefix.
539 */
540static void
541look_for (const char *name_prefix)
542{
543 GNUNET_SOCIAL_place_look_for (plc, name_prefix, look_var, look_result, NULL);
544}
545
546
547/* SLICER */
548
549
550/**
551 * Callback notifying about the start of a new incoming message.
552 */
553static void
554slicer_recv_method (void *cls,
555 const struct GNUNET_PSYC_MessageHeader *msg,
556 const struct GNUNET_PSYC_MessageMethod *meth,
557 uint64_t message_id,
558 const char *method_name)
559{
560 method_received = method_name;
561 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
562 "Received method for message ID %" PRIu64 ":\n"
563 "%s (flags: %x)\n",
564 message_id, method_name, ntohl (meth->flags));
565 /* routing header is missing, so we just print double newline */
566 printf("\n");
567 /* we output . instead of | to indicate that this is not proper PSYC syntax */
568 /* FIXME: use libpsyc here */
569}
570
571
572/**
573 * Callback notifying about an incoming modifier.
574 */
575static void
576slicer_recv_modifier (void *cls,
577 const struct GNUNET_PSYC_MessageHeader *msg,
578 const struct GNUNET_MessageHeader *pmsg,
579 uint64_t message_id,
580 enum GNUNET_PSYC_Operator oper,
581 const char *name,
582 const void *value,
583 uint16_t value_size,
584 uint16_t full_value_size)
585{
586#if 0
587 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
588 "Received modifier for message ID %" PRIu64 ":\n"
589 "%c%s: %.*s (size: %u)\n",
590 message_id, oper, name, value_size, (const char *) value, value_size);
591#else
592 /* obviously not binary safe */
593 printf("%c%s\t%.*s\n",
594 oper, name, value_size, (const char *) value);
595#endif
596}
597
598
599/**
600 * Callback notifying about an incoming data fragment.
601 */
602static void
603slicer_recv_data (void *cls,
604 const struct GNUNET_PSYC_MessageHeader *msg,
605 const struct GNUNET_MessageHeader *pmsg,
606 uint64_t message_id,
607 const void *data,
608 uint16_t data_size)
609{
610#if 0
611 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
612 "Received data for message ID %" PRIu64 ":\n"
613 "%.*s\n",
614 message_id, data_size, (const char *) data);
615#else
616 /* obviously not binary safe */
617 printf("%s\n%.*s\n",
618 method_received, data_size, (const char *) data);
619#endif
620}
621
622
623/**
624 * Callback notifying about the end of a message.
625 */
626static void
627slicer_recv_eom (void *cls,
628 const struct GNUNET_PSYC_MessageHeader *msg,
629 const struct GNUNET_MessageHeader *pmsg,
630 uint64_t message_id,
631 uint8_t is_cancelled)
632{
633 printf(".\n");
634 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
635 "Received end of message ID %" PRIu64
636 ", cancelled: %u\n",
637 message_id, is_cancelled);
638}
639
640
641/**
642 * Create a slicer for receiving message parts.
643 */
644static struct GNUNET_PSYC_Slicer *
645slicer_create ()
646{
647 slicer = GNUNET_PSYC_slicer_create ();
648
649 /* register slicer to receive incoming messages with any method name */
650 GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
651 slicer_recv_method, slicer_recv_modifier,
652 slicer_recv_data, slicer_recv_eom, NULL);
653 return slicer;
654}
655
656
657/* GUEST ENTER */
658
659
660/**
661 * Callback called when the guest receives an entry decision from the host.
662 *
663 * It is called once after using guest_enter() or guest_enter_by_name(),
664 * in case of a reconnection only the local enter callback is called.
665 */
666static void
667guest_recv_entry_decision (void *cls,
668 int is_admitted,
669 const struct GNUNET_PSYC_Message *entry_msg)
670{
671 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
672 "Guest received entry decision %d\n",
673 is_admitted);
674
675 if (NULL != entry_msg)
676 {
677 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
678 const char *method_name = NULL;
679 const void *data = NULL;
680 uint16_t data_size = 0;
681 struct GNUNET_PSYC_MessageHeader *
682 pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
683 GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
684 GNUNET_free (pmsg);
685
686 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
687 "%s\n%.*s\n",
688 method_name, data_size, (const char *) data);
689 }
690
691 if (op_guest_enter && !opt_follow)
692 {
693 exit_success ();
694 }
695}
696
697
698/**
699 * Callback called after a guest connection is established to the local service.
700 */
701static void
702guest_recv_local_enter (void *cls, int result,
703 const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
704 uint64_t max_message_id)
705{
706 char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
707 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
708 "Guest entered local place: %s, max_message_id: %" PRIu64 "\n",
709 pub_str, max_message_id);
710 GNUNET_free (pub_str);
711 GNUNET_assert (0 <= result);
712
713 if (op_guest_enter && !opt_follow)
714 {
715 exit_success ();
716 }
717}
718
719
720/**
721 * Create entry request message.
722 */
723static struct GNUNET_PSYC_Message *
724guest_enter_msg_create ()
725{
726 const char *method_name = "_request_enter";
727 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
728 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
729 "_foo", DATA2ARG ("bar"));
730 void *data = "let me in";
731 uint16_t data_size = strlen (data) + 1;
732
733 return GNUNET_PSYC_message_create (method_name, env, data, data_size);
734}
735
736
737/**
738 * Enter a place as guest, using its public key and peer ID.
739 */
740static void
741guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
742 const struct GNUNET_PeerIdentity *peer)
743{
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Entering to place as guest.\n");
746
747 if (NULL == ego)
748 {
749 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
750 exit_fail ();
751 return;
752 }
753
754 struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
755 gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
756 GNUNET_PSYC_SLAVE_JOIN_NONE,
757 peer, 0, NULL, join_msg, slicer_create (),
758 guest_recv_local_enter,
759 guest_recv_entry_decision, NULL);
760 GNUNET_free (join_msg);
761 plc = GNUNET_SOCIAL_guest_get_place (gst);
762}
763
764
765/**
766 * Enter a place as guest using its GNS address.
767 */
768static void
769guest_enter_by_name (const char *gns_name)
770{
771 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
772 "Entering to place by name as guest.\n");
773
774 struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
775 gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
776 join_msg, slicer,
777 guest_recv_local_enter,
778 guest_recv_entry_decision, NULL);
779 GNUNET_free (join_msg);
780 plc = GNUNET_SOCIAL_guest_get_place (gst);
781}
782
783
784/* HOST ENTER */
785
786
787/**
788 * Callback called when a @a nym wants to enter the place.
789 *
790 * The request needs to be replied with an entry decision.
791 */
792static void
793host_answer_door (void *cls,
794 struct GNUNET_SOCIAL_Nym *nym,
795 const char *method_name,
796 struct GNUNET_PSYC_Environment *env,
797 const void *data,
798 size_t data_size)
799{
800 const struct GNUNET_CRYPTO_EcdsaPublicKey *
801 nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
802 char *
803 nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
804
805 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
806 "Entry request: %s\n", nym_str);
807 GNUNET_free (nym_str);
808
809 if (opt_welcome)
810 {
811 struct GNUNET_PSYC_Message *
812 resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
813 DATA2ARG ("Welcome, nym!"));
814 GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
815 GNUNET_free (resp);
816 }
817 else if (opt_deny)
818 {
819 struct GNUNET_PSYC_Message *
820 resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
821 DATA2ARG ("Go away!"));
822 GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
823 GNUNET_free (resp);
824 }
825
826
827}
828
829
830/**
831 * Callback called when a @a nym has left the place.
832 */
833static void
834host_farewell (void *cls,
835 const struct GNUNET_SOCIAL_Nym *nym,
836 struct GNUNET_PSYC_Environment *env)
837{
838 const struct GNUNET_CRYPTO_EcdsaPublicKey *
839 nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
840 char *
841 nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
842
843 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
844 "Farewell: %s\n", nym_str);
845 GNUNET_free (nym_str);
846}
847
848
849/**
850 * Callback called after the host entered the place.
851 */
852static void
853host_entered (void *cls, int result,
854 const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
855 uint64_t max_message_id)
856{
857 place_pub_key = *pub_key;
858 char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
859 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
860 "Host entered: %s, max_message_id: %" PRIu64 "\n",
861 pub_str, max_message_id);
862 GNUNET_free (pub_str);
863
864 if (op_host_enter && !opt_follow)
865 {
866 exit_success ();
867 }
868}
869
870
871/**
872 * Enter and start hosting a place.
873 */
874static void
875host_enter ()
876{
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
878
879 if (NULL == ego)
880 {
881 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
882 exit_fail ();
883 return;
884 }
885
886 hst = GNUNET_SOCIAL_host_enter (app, ego,
887 GNUNET_PSYC_CHANNEL_PRIVATE,
888 slicer_create (), host_entered,
889 host_answer_door, host_farewell, NULL);
890 plc = GNUNET_SOCIAL_host_get_place (hst);
891}
892
893
894/* PLACE RECONNECT */
895
896
897/**
898 * Perform operations common to both host & guest places.
899 */
900static void
901place_reconnected ()
902{
903 static int first_run = GNUNET_YES;
904 if (GNUNET_NO == first_run)
905 return;
906 first_run = GNUNET_NO;
907
908 if (op_replay) {
909 history_replay (opt_start, opt_until, opt_method);
910 }
911 else if (op_replay_latest) {
912 history_replay_latest (opt_limit, opt_method);
913 }
914 else if (op_look_at) {
915 look_at (opt_name);
916 }
917 else if (op_look_for) {
918 look_for (opt_name);
919 }
920}
921
922
923/**
924 * Callback called after reconnecting to a host place.
925 */
926static void
927host_reconnected (void *cls, int result,
928 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
929 uint64_t max_message_id)
930{
931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
932 "Host reconnected.\n");
933
934 if (op_host_leave) {
935 host_leave ();
936 }
937 else if (op_host_announce) {
938 host_announce (opt_method, opt_data, strlen (opt_data));
939 }
940 else if (op_host_assign) {
941 host_assign (opt_name, opt_data, strlen (opt_data) + 1);
942 }
943 else {
944 place_reconnected ();
945 }
946}
947
948
949/**
950 * Callback called after reconnecting to a guest place.
951 */
952static void
953guest_reconnected (void *cls, int result,
954 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
955 uint64_t max_message_id)
956{
957 char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (place_pub_key);
958 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959 "Guest reconnected to place %s.\n", place_pub_str);
960 GNUNET_free (place_pub_str);
961
962 if (op_guest_leave) {
963 guest_leave ();
964 }
965 else if (op_guest_talk) {
966 guest_talk (opt_method, opt_data, strlen (opt_data));
967 }
968 else {
969 place_reconnected ();
970 }
971}
972
973
974/* APP */
975
976
977/**
978 * Callback called after the ego and place callbacks.
979 */
980static void
981app_connected (void *cls)
982{
983 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
984 "App connected: %p\n", cls);
985
986 if (op_status)
987 {
988 exit_success ();
989 }
990 else if (op_host_enter)
991 {
992 host_enter ();
993 }
994 else if (op_guest_enter)
995 {
996 if (opt_gns)
997 {
998 guest_enter_by_name (opt_gns);
999 }
1000 else
1001 {
1002 if (opt_peer)
1003 {
1004 if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
1005 strlen (opt_peer),
1006 &peer.public_key))
1007 {
1008 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1009 "--peer invalid");
1010 exit_fail ();
1011 return;
1012 }
1013 }
1014 else
1015 {
1016 peer = this_peer;
1017 }
1018 guest_enter (&place_pub_key, &peer);
1019 }
1020 }
1021 printf(".\n");
1022}
1023
1024
1025/**
1026 * Callback notifying about a host place available for reconnection.
1027 */
1028static void
1029app_recv_host (void *cls,
1030 struct GNUNET_SOCIAL_HostConnection *hconn,
1031 struct GNUNET_SOCIAL_Ego *ego,
1032 const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
1033 enum GNUNET_SOCIAL_AppPlaceState place_state)
1034{
1035 char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
1036 printf ("Host\t%s\n", host_pub_str);
1037 GNUNET_free (host_pub_str);
1038
1039 if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1040 || op_replay || op_replay_latest
1041 || op_look_at || op_look_for)
1042 && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
1043 {
1044 hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
1045 host_answer_door, host_farewell, NULL);
1046 plc = GNUNET_SOCIAL_host_get_place (hst);
1047 }
1048}
1049
1050
1051/**
1052 * Callback notifying about a guest place available for reconnection.
1053 */
1054static void
1055app_recv_guest (void *cls,
1056 struct GNUNET_SOCIAL_GuestConnection *gconn,
1057 struct GNUNET_SOCIAL_Ego *ego,
1058 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
1059 enum GNUNET_SOCIAL_AppPlaceState place_state)
1060{
1061 char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
1062 printf ("Guest\t%s\n", guest_pub_str);
1063 GNUNET_free (guest_pub_str);
1064
1065 if ((op_guest_reconnect || op_guest_leave || op_guest_talk
1066 || op_replay || op_replay_latest
1067 || op_look_at || op_look_for)
1068 && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
1069 {
1070 gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
1071 slicer_create (), guest_reconnected, NULL);
1072 plc = GNUNET_SOCIAL_guest_get_place (gst);
1073 }
1074}
1075
1076
1077/**
1078 * Callback notifying about an available ego.
1079 */
1080static void
1081app_recv_ego (void *cls,
1082 struct GNUNET_SOCIAL_Ego *e,
1083 const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
1084 const char *name)
1085{
1086 char *s = GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key);
1087 printf ("Ego\t%s\t%s\n", s, name);
1088 GNUNET_free (s);
1089
1090 if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
1091 || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
1092 {
1093 ego = e;
1094 }
1095
1096}
1097
1098
1099
1100/**
1101 * Establish application connection to receive available egos and places.
1102 */
1103static void
1104app_connect (void *cls)
1105{
1106 app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
1107 app_recv_ego,
1108 app_recv_host,
1109 app_recv_guest,
1110 app_connected,
1111 NULL);
1112}
1113
1114
1115/**
1116 * Main function run by the scheduler.
1117 *
1118 * @param cls closure
1119 * @param args remaining command-line arguments
1120 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1121 * @param c configuration
1122 */
1123static void
1124run (void *cls, char *const *args, const char *cfgfile,
1125 const struct GNUNET_CONFIGURATION_Handle *c)
1126{
1127 cfg = c;
1128 GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
1129
1130 if (!opt_method)
1131 opt_method = "message";
1132 if (!opt_data)
1133 opt_data = "";
1134 if (!opt_name)
1135 opt_name = "";
1136
1137 if (! (op_status
1138 || op_host_enter || op_host_reconnect || op_host_leave
1139 || op_host_announce || op_host_assign
1140 || op_guest_enter || op_guest_reconnect
1141 || op_guest_leave || op_guest_talk
1142 || op_replay || op_replay_latest
1143 || op_look_at || op_look_for))
1144 {
1145 op_status = 1;
1146 fputs("Caution: This tool does not produce correct binary safe PSYC syntax.\n\n", stderr);
1147 }
1148
1149 GNUNET_SCHEDULER_add_shutdown (scheduler_shutdown, NULL);
1150 if (!opt_follow)
1151 {
1152 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, timeout, NULL);
1153 }
1154
1155 if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1156 || op_guest_reconnect || (op_guest_enter && !opt_gns)
1157 || op_guest_leave || op_guest_talk
1158 || op_replay || op_replay_latest
1159 || op_look_at || op_look_for)
1160 && (!opt_place
1161 || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
1162 strlen (opt_place),
1163 &place_pub_key)))
1164 {
1165 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1166 _("--place missing or invalid.\n"));
1167 /* FIXME: why does it segfault here? */
1168 exit_fail ();
1169 return;
1170 }
1171
1172 if (opt_ego)
1173 {
1174 if (GNUNET_OK !=
1175 GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
1176 strlen (opt_ego),
1177 &ego_pub_key))
1178 {
1179 FPRINTF (stderr,
1180 _("Public key `%s' malformed\n"),
1181 opt_ego);
1182 exit_fail ();
1183 return;
1184 }
1185 }
1186
1187 GNUNET_SCHEDULER_add_now (app_connect, NULL);
1188}
1189
1190
1191/**
1192 * The main function to obtain peer information.
1193 *
1194 * @param argc number of arguments from the command line
1195 * @param argv command line arguments
1196 * @return 0 ok, 1 on error
1197 */
1198int
1199main (int argc, char *const *argv)
1200{
1201 int res;
1202 struct GNUNET_GETOPT_CommandLineOption options[] = {
1203 /*
1204 * gnunet program options in addition to the ones below:
1205 *
1206 * -c, --config=FILENAME
1207 * -l, --logfile=LOGFILE
1208 * -L, --log=LOGLEVEL
1209 * -h, --help
1210 * -v, --version
1211 */
1212
1213 /* operations */
1214
1215 GNUNET_GETOPT_option_flag ('A',
1216 "host-assign",
1217 gettext_noop ("assign --name in state to --data"),
1218 &op_host_assign),
1219
1220 GNUNET_GETOPT_option_flag ('B',
1221 "guest-leave",
1222 gettext_noop ("say good-bye and leave somebody else's place"),
1223 &op_guest_leave),
1224
1225 GNUNET_GETOPT_option_flag ('C',
1226 "host-enter",
1227 gettext_noop ("create a place"),
1228 &op_host_enter),
1229
1230 GNUNET_GETOPT_option_flag ('D',
1231 "host-leave",
1232 gettext_noop ("destroy a place we were hosting"),
1233 &op_host_leave),
1234
1235 GNUNET_GETOPT_option_flag ('E',
1236 "guest-enter",
1237 gettext_noop ("enter somebody else's place"),
1238 &op_guest_enter),
1239
1240
1241 GNUNET_GETOPT_option_flag ('F',
1242 "look-for",
1243 gettext_noop ("find state matching name prefix"),
1244 &op_look_for),
1245
1246 GNUNET_GETOPT_option_flag ('H',
1247 "replay-latest",
1248 gettext_noop ("replay history of messages up to the given --limit"),
1249 &op_replay_latest),
1250
1251 GNUNET_GETOPT_option_flag ('N',
1252 "host-reconnect",
1253 gettext_noop ("reconnect to a previously created place"),
1254 &op_host_reconnect),
1255
1256 GNUNET_GETOPT_option_flag ('P',
1257 "host-announce",
1258 gettext_noop ("publish something to a place we are hosting"),
1259 &op_host_announce),
1260
1261 GNUNET_GETOPT_option_flag ('R',
1262 "guest-reconnect",
1263 gettext_noop ("reconnect to a previously entered place"),
1264 &op_guest_reconnect),
1265
1266 GNUNET_GETOPT_option_flag ('S',
1267 "look-at",
1268 gettext_noop ("search for state matching exact name"),
1269 &op_look_at),
1270
1271 GNUNET_GETOPT_option_flag ('T',
1272 "guest-talk",
1273 gettext_noop ("submit something to somebody's place"),
1274 &op_guest_talk),
1275
1276 GNUNET_GETOPT_option_flag ('U',
1277 "status",
1278 gettext_noop ("list of egos and subscribed places"),
1279 &op_status),
1280
1281 GNUNET_GETOPT_option_flag ('X',
1282 "replay",
1283 gettext_noop ("extract and replay history between message IDs --start and --until"),
1284 &op_replay),
1285
1286
1287 /* options */
1288
1289 GNUNET_GETOPT_option_string ('a',
1290 "app",
1291 "APPLICATION_ID",
1292 gettext_noop ("application ID to use when connecting"),
1293 &opt_app),
1294
1295 GNUNET_GETOPT_option_string ('d',
1296 "data",
1297 "DATA",
1298 gettext_noop ("message body or state value"),
1299 &opt_data),
1300
1301 GNUNET_GETOPT_option_string ('e',
1302 "ego",
1303 "NAME|PUBKEY",
1304 gettext_noop ("name or public key of ego"),
1305 &opt_ego),
1306
1307 GNUNET_GETOPT_option_flag ('f',
1308 "follow",
1309 gettext_noop ("wait for incoming messages"),
1310 &opt_follow),
1311
1312 GNUNET_GETOPT_option_string ('g',
1313 "gns",
1314 "GNS_NAME",
1315 gettext_noop ("GNS name"),
1316 &opt_gns),
1317
1318 GNUNET_GETOPT_option_string ('i',
1319 "peer",
1320 "PEER_ID",
1321 gettext_noop ("peer ID for --guest-enter"),
1322 &opt_peer),
1323
1324 GNUNET_GETOPT_option_string ('k',
1325 "name",
1326 "VAR_NAME",
1327 gettext_noop ("name (key) to query from state"),
1328 &opt_name),
1329
1330 GNUNET_GETOPT_option_string ('m',
1331 "method",
1332 "METHOD_NAME",
1333 gettext_noop ("method name"),
1334 &opt_method),
1335
1336 GNUNET_GETOPT_option_ulong ('n',
1337 "limit",
1338 NULL,
1339 gettext_noop ("number of messages to replay from history"),
1340 &opt_limit),
1341
1342 GNUNET_GETOPT_option_string ('p',
1343 "place",
1344 "PUBKEY",
1345 gettext_noop ("key address of place"),
1346 &opt_place),
1347
1348 GNUNET_GETOPT_option_ulong ('s',
1349 "start",
1350 NULL,
1351 gettext_noop ("start message ID for history replay"),
1352 &opt_start),
1353
1354 GNUNET_GETOPT_option_flag ('w',
1355 "welcome",
1356 gettext_noop ("respond to entry requests by admitting all guests"),
1357 &opt_welcome),
1358
1359 GNUNET_GETOPT_option_ulong ('u',
1360 "until",
1361 NULL,
1362 gettext_noop ("end message ID for history replay"),
1363 &opt_until),
1364
1365 GNUNET_GETOPT_option_flag ('y',
1366 "deny",
1367 gettext_noop ("respond to entry requests by refusing all guests"),
1368 &opt_deny),
1369
1370 GNUNET_GETOPT_OPTION_END
1371 };
1372
1373 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1374 return 2;
1375
1376 const char *help =
1377 _ ("gnunet-social - Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
1378 const char *usage =
1379 "gnunet-social [--status]\n"
1380 "\n"
1381 "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | --deny]\n"
1382 "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | --deny]\n"
1383 "gnunet-social --host-leave --place <PUBKEY>\n"
1384 "gnunet-social --host-assign --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1385// FIXME: some state ops not implemented yet (no hurry)
1386// "gnunet-social --host-augment --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1387// "gnunet-social --host-diminish --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1388// "gnunet-social --host-set --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1389 "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1390 "\n"
1391 "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME or PUBKEY> [--follow]\n"
1392 "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> [--follow]\n"
1393 "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
1394 "gnunet-social --guest-leave --place <PUBKEY>\n"
1395 "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1396 "\n"
1397 "gnunet-social --replay --place <PUBKEY> --start <MSGID> --until <MSGID> [--method <METHOD_PREFIX>]\n"
1398 "gnunet-social --replay-latest --place <PUBKEY> --limit <MSG_LIMIT> [--method <METHOD_PREFIX>]\n"
1399 "\n"
1400 "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"
1401 "gnunet-social --look-for --place <PUBKEY> --name <NAME_PREFIX>\n";
1402
1403 res = GNUNET_PROGRAM_run (argc, argv, help, usage, options, &run, NULL);
1404
1405 GNUNET_free ((void *) argv);
1406
1407 if (GNUNET_OK == res)
1408 return ret;
1409 else
1410 return 1;
1411}
diff --git a/src/social/social.conf.in b/src/social/social.conf.in
new file mode 100644
index 0000000..3fe754c
--- /dev/null
+++ b/src/social/social.conf.in
@@ -0,0 +1,15 @@
1[social]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-social
4RUN_PER_USER = YES
5
6UNIXPATH = $GNUNET_USER_RUNTIME_DIR/gnunet-service-social.sock
7UNIX_MATCH_UID = YES
8UNIX_MATCH_GID = YES
9
10@UNIXONLY@PORT = 2116
11HOSTNAME = localhost
12ACCEPT_FROM = 127.0.0.1;
13ACCEPT_FROM6 = ::1;
14
15DATA_HOME = $GNUNET_DATA_HOME/social
diff --git a/src/social/social.h b/src/social/social.h
new file mode 100644
index 0000000..73f73f6
--- /dev/null
+++ b/src/social/social.h
@@ -0,0 +1,292 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file social/social.h
23 * @brief Common type definitions for the Social service and API.
24 * @author Gabor X Toth
25 */
26
27#ifndef SOCIAL_H
28#define SOCIAL_H
29
30#include "platform.h"
31#include "gnunet_social_service.h"
32
33enum MessageState
34{
35 MSG_STATE_START = 0,
36 MSG_STATE_HEADER = 1,
37 MSG_STATE_METHOD = 2,
38 MSG_STATE_MODIFIER = 3,
39 MSG_STATE_MOD_CONT = 4,
40 MSG_STATE_DATA = 5,
41 MSG_STATE_END = 6,
42 MSG_STATE_CANCEL = 7,
43 MSG_STATE_ERROR = 8,
44};
45
46
47GNUNET_NETWORK_STRUCT_BEGIN
48
49/**** library -> service ****/
50
51
52struct AppConnectRequest
53{
54 /**
55 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT
56 */
57 struct GNUNET_MessageHeader header;
58
59 /* Followed by char *app_id */
60};
61
62
63struct AppDetachRequest
64{
65 /**
66 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH
67 */
68 struct GNUNET_MessageHeader header;
69
70 /**
71 * Public key of place.
72 */
73 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
74
75 /**
76 * Public key of ego.
77 */
78 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
79
80 /**
81 * Operation ID.
82 */
83 uint64_t op_id GNUNET_PACKED;
84};
85
86
87struct MsgProcRequest
88{
89 /**
90 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET
91 */
92 struct GNUNET_MessageHeader header;
93
94 /**
95 * @see enum GNUNET_SOCIAL_MsgProcFlags
96 */
97 uint32_t flags;
98
99 /* Followed by char *method_prefix */
100};
101
102
103struct HostEnterRequest
104{
105 /**
106 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER
107 */
108 struct GNUNET_MessageHeader header;
109
110 uint32_t policy GNUNET_PACKED;
111
112 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
113
114 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
115
116 struct GNUNET_CRYPTO_EddsaPrivateKey place_key;
117
118 /* Followed by char *app_id */
119};
120
121
122struct GuestEnterRequest
123{
124 /**
125 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER
126 */
127 struct GNUNET_MessageHeader header;
128
129 uint32_t relay_count GNUNET_PACKED;
130
131 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
132
133 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
134
135 struct GNUNET_PeerIdentity origin;
136
137 uint32_t flags GNUNET_PACKED;
138
139 /* Followed by char *app_id */
140 /* Followed by struct GNUNET_PeerIdentity relays[relay_count] */
141 /* Followed by struct GNUNET_MessageHeader *join_msg */
142};
143
144
145/** Compatible parts of HostEnterRequest and GuestEnterRequest */
146struct PlaceEnterRequest
147{
148 struct GNUNET_MessageHeader header;
149
150 uint32_t reserved GNUNET_PACKED;
151
152 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
153
154 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
155};
156
157
158struct EgoPlacePublicKey
159{
160 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
161 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
162};
163
164
165struct GuestEnterByNameRequest
166{
167 /**
168 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME
169 */
170 struct GNUNET_MessageHeader header;
171
172 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
173
174 /* Followed by char *app_id */
175 /* Followed by char *gns_name */
176 /* Followed by char *password */
177 /* Followed by struct GNUNET_MessageHeader *join_msg */
178};
179
180
181struct ZoneAddPlaceRequest
182{
183 struct GNUNET_MessageHeader header;
184
185 uint32_t relay_count GNUNET_PACKED;
186
187 /**
188 * Operation ID.
189 */
190 uint64_t op_id;
191
192 /**
193 * Expiration time: absolute value in us.
194 */
195 uint64_t expiration_time;
196
197 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
198
199 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
200
201 struct GNUNET_PeerIdentity origin;
202
203 /* Followed by const char *name */
204 /* Followed by const char *password */
205 /* Followed by struct GNUNET_PeerIdentity *relays[relay_count] */
206};
207
208
209struct ZoneAddNymRequest
210{
211 struct GNUNET_MessageHeader header;
212
213 /**
214 * Operation ID.
215 */
216 uint64_t op_id;
217
218 /**
219 * Expiration time: absolute value in us.
220 */
221 uint64_t expiration_time;
222
223 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
224
225 struct GNUNET_CRYPTO_EcdsaPublicKey nym_pub_key;
226
227 /* Followed by const char *name */
228};
229
230
231/**** service -> library ****/
232
233
234struct AppEgoMessage
235{
236 /**
237 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO
238 */
239 struct GNUNET_MessageHeader header;
240
241 /**
242 * Public key of ego.
243 */
244 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
245
246 /* Followed by char *name */
247};
248
249
250struct AppPlaceMessage
251{
252 /**
253 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE
254 */
255 struct GNUNET_MessageHeader header;
256
257 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
258
259 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
260
261 uint8_t is_host;
262
263 uint8_t place_state;
264};
265
266
267struct HostEnterAck {
268 /**
269 * Type: GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK
270 */
271 struct GNUNET_MessageHeader header;
272
273 /**
274 * Status code for the operation.
275 */
276 uint32_t result_code GNUNET_PACKED;
277
278 /**
279 * Last message ID sent to the channel.
280 */
281 uint64_t max_message_id GNUNET_PACKED;
282
283 /**
284 * Public key of the place.
285 */
286 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
287};
288
289
290GNUNET_NETWORK_STRUCT_END
291
292#endif
diff --git a/src/social/social_api.c b/src/social/social_api.c
new file mode 100644
index 0000000..9b96580
--- /dev/null
+++ b/src/social/social_api.c
@@ -0,0 +1,2827 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @author Gabor X Toth
23 *
24 * @file
25 * Social service; implements social interactions using the PSYC service.
26 */
27
28#include <inttypes.h>
29#include <string.h>
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_psyc_service.h"
34#include "gnunet_psyc_util_lib.h"
35#include "gnunet_social_service.h"
36#include "social.h"
37
38#define LOG(kind,...) GNUNET_log_from (kind, "social-api",__VA_ARGS__)
39
40/**
41 * Handle for an ego.
42 */
43struct GNUNET_SOCIAL_Ego
44{
45 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
46 struct GNUNET_HashCode pub_key_hash;
47 char *name;
48};
49
50
51/**
52 * Handle for a pseudonym of another user in the network.
53 */
54struct GNUNET_SOCIAL_Nym
55{
56 struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
57 struct GNUNET_HashCode pub_key_hash;
58};
59
60
61/**
62 * Handle for an application.
63 */
64struct GNUNET_SOCIAL_App
65{
66 /**
67 * Configuration to use.
68 */
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
70
71 /**
72 * Client connection to the service.
73 */
74 struct GNUNET_MQ_Handle *mq;
75
76 /**
77 * Message to send on connect.
78 */
79 struct GNUNET_MQ_Envelope *connect_env;
80
81 /**
82 * Time to wait until we try to reconnect on failure.
83 */
84 struct GNUNET_TIME_Relative reconnect_delay;
85
86 /**
87 * Task for reconnecting when the listener fails.
88 */
89 struct GNUNET_SCHEDULER_Task *reconnect_task;
90
91 /**
92 * Async operations.
93 */
94 struct GNUNET_OP_Handle *op;
95
96 /**
97 * Function called after disconnected from the service.
98 */
99 GNUNET_ContinuationCallback disconnect_cb;
100
101 /**
102 * Closure for @a disconnect_cb.
103 */
104 void *disconnect_cls;
105
106 /**
107 * Application ID.
108 */
109 char *id;
110
111 /**
112 * Hash map of all egos.
113 * pub_key_hash -> struct GNUNET_SOCIAL_Ego *
114 */
115 struct GNUNET_CONTAINER_MultiHashMap *egos;
116
117 GNUNET_SOCIAL_AppEgoCallback ego_cb;
118 GNUNET_SOCIAL_AppHostPlaceCallback host_cb;
119 GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb;
120 GNUNET_SOCIAL_AppConnectedCallback connected_cb;
121 void *cb_cls;
122};
123
124
125struct GNUNET_SOCIAL_HostConnection
126{
127 struct GNUNET_SOCIAL_App *app;
128
129 struct AppPlaceMessage plc_msg;
130};
131
132
133struct GNUNET_SOCIAL_GuestConnection
134{
135 struct GNUNET_SOCIAL_App *app;
136
137 struct AppPlaceMessage plc_msg;
138};
139
140
141/**
142 * Handle for a place where social interactions happen.
143 */
144struct GNUNET_SOCIAL_Place
145{
146 /**
147 * Configuration to use.
148 */
149 const struct GNUNET_CONFIGURATION_Handle *cfg;
150
151 /**
152 * Client connection to the service.
153 */
154 struct GNUNET_MQ_Handle *mq;
155
156 /**
157 * Message to send on connect.
158 */
159 struct GNUNET_MQ_Envelope *connect_env;
160
161 /**
162 * Time to wait until we try to reconnect on failure.
163 */
164 struct GNUNET_TIME_Relative reconnect_delay;
165
166 /**
167 * Task for reconnecting when the listener fails.
168 */
169 struct GNUNET_SCHEDULER_Task *reconnect_task;
170
171 /**
172 * Async operations.
173 */
174 struct GNUNET_OP_Handle *op;
175
176 /**
177 * Transmission handle.
178 */
179 struct GNUNET_PSYC_TransmitHandle *tmit;
180
181 /**
182 * Slicer for processing incoming messages.
183 */
184 struct GNUNET_PSYC_Slicer *slicer;
185
186 // FIXME: do we need is_disconnecing like on the psyc and multicast APIs?
187 /**
188 * Function called after disconnected from the service.
189 */
190 GNUNET_ContinuationCallback disconnect_cb;
191
192 /**
193 * Closure for @a disconnect_cb.
194 */
195 void *disconnect_cls;
196
197 /**
198 * Public key of the place.
199 */
200 struct GNUNET_CRYPTO_EddsaPublicKey pub_key;
201
202 /**
203 * Public key of the ego.
204 */
205 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
206
207 /**
208 * Does this place belong to a host (#GNUNET_YES) or guest (#GNUNET_NO)?
209 */
210 uint8_t is_host;
211};
212
213
214/**
215 * Host handle for a place that we entered.
216 */
217struct GNUNET_SOCIAL_Host
218{
219 struct GNUNET_SOCIAL_Place plc;
220
221 /**
222 * Slicer for processing incoming messages from guests.
223 */
224 struct GNUNET_PSYC_Slicer *slicer;
225
226 GNUNET_SOCIAL_HostEnterCallback enter_cb;
227
228 GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb;
229
230 GNUNET_SOCIAL_FarewellCallback farewell_cb;
231
232 /**
233 * Closure for callbacks.
234 */
235 void *cb_cls;
236
237 struct GNUNET_SOCIAL_Nym *notice_place_leave_nym;
238 struct GNUNET_PSYC_Environment *notice_place_leave_env;
239};
240
241
242/**
243 * Guest handle for place that we entered.
244 */
245struct GNUNET_SOCIAL_Guest
246{
247 struct GNUNET_SOCIAL_Place plc;
248
249 GNUNET_SOCIAL_GuestEnterCallback enter_cb;
250
251 GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb;
252
253 /**
254 * Closure for callbacks.
255 */
256 void *cb_cls;
257};
258
259
260/**
261 * Hash map of all nyms.
262 * pub_key_hash -> struct GNUNET_SOCIAL_Nym *
263 */
264struct GNUNET_CONTAINER_MultiHashMap *nyms;
265
266
267/**
268 * Handle for an announcement request.
269 */
270struct GNUNET_SOCIAL_Announcement
271{
272
273};
274
275
276/**
277 * A talk request.
278 */
279struct GNUNET_SOCIAL_TalkRequest
280{
281
282};
283
284
285/**
286 * A history lesson.
287 */
288struct GNUNET_SOCIAL_HistoryRequest
289{
290 /**
291 * Place.
292 */
293 struct GNUNET_SOCIAL_Place *plc;
294
295 /**
296 * Operation ID.
297 */
298 uint64_t op_id;
299
300 /**
301 * Slicer for processing incoming messages.
302 */
303 struct GNUNET_PSYC_Slicer *slicer;
304
305 /**
306 * Function to call when the operation finished.
307 */
308 GNUNET_ResultCallback result_cb;
309
310 /**
311 * Closure for @a result_cb.
312 */
313 void *cls;
314};
315
316
317struct GNUNET_SOCIAL_LookHandle
318{
319 /**
320 * Place.
321 */
322 struct GNUNET_SOCIAL_Place *plc;
323
324 /**
325 * Operation ID.
326 */
327 uint64_t op_id;
328
329 /**
330 * State variable result callback.
331 */
332 GNUNET_PSYC_StateVarCallback var_cb;
333
334 /**
335 * Function to call when the operation finished.
336 */
337 GNUNET_ResultCallback result_cb;
338
339 /**
340 * Name of current modifier being received.
341 */
342 char *mod_name;
343
344 /**
345 * Size of current modifier value being received.
346 */
347 size_t mod_value_size;
348
349 /**
350 * Remaining size of current modifier value still to be received.
351 */
352 size_t mod_value_remaining;
353
354 /**
355 * Closure for @a result_cb.
356 */
357 void *cls;
358};
359
360
361struct ZoneAddPlaceHandle
362{
363 GNUNET_ResultCallback result_cb;
364 void *result_cls;
365};
366
367
368struct ZoneAddNymHandle
369{
370 GNUNET_ResultCallback result_cb;
371 void *result_cls;
372};
373
374
375/*** CLEANUP / DISCONNECT ***/
376
377
378static void
379host_cleanup (struct GNUNET_SOCIAL_Host *hst)
380{
381 if (NULL != hst->slicer)
382 {
383 GNUNET_PSYC_slicer_destroy (hst->slicer);
384 hst->slicer = NULL;
385 }
386 GNUNET_free (hst);
387}
388
389
390static void
391guest_cleanup (struct GNUNET_SOCIAL_Guest *gst)
392{
393 GNUNET_free (gst);
394}
395
396
397static void
398place_cleanup (struct GNUNET_SOCIAL_Place *plc)
399{
400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
401 "cleaning up place %p\n",
402 plc);
403 if (NULL != plc->tmit)
404 {
405 GNUNET_PSYC_transmit_destroy (plc->tmit);
406 plc->tmit = NULL;
407 }
408 if (NULL != plc->connect_env)
409 {
410 GNUNET_MQ_discard (plc->connect_env);
411 plc->connect_env = NULL;
412 }
413 if (NULL != plc->mq)
414 {
415 GNUNET_MQ_destroy (plc->mq);
416 plc->mq = NULL;
417 }
418 if (NULL != plc->disconnect_cb)
419 {
420 plc->disconnect_cb (plc->disconnect_cls);
421 plc->disconnect_cb = NULL;
422 }
423
424 (GNUNET_YES == plc->is_host)
425 ? host_cleanup ((struct GNUNET_SOCIAL_Host *) plc)
426 : guest_cleanup ((struct GNUNET_SOCIAL_Guest *) plc);
427}
428
429
430static void
431place_disconnect (struct GNUNET_SOCIAL_Place *plc)
432{
433 place_cleanup (plc);
434}
435
436
437/*** NYM ***/
438
439static struct GNUNET_SOCIAL_Nym *
440nym_get_or_create (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key)
441{
442 struct GNUNET_SOCIAL_Nym *nym = NULL;
443 struct GNUNET_HashCode pub_key_hash;
444
445 if (NULL == pub_key)
446 return NULL;
447
448 GNUNET_CRYPTO_hash (pub_key, sizeof (*pub_key), &pub_key_hash);
449
450 if (NULL == nyms)
451 nyms = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_YES);
452 else
453 nym = GNUNET_CONTAINER_multihashmap_get (nyms, &pub_key_hash);
454
455 if (NULL == nym)
456 {
457 nym = GNUNET_new (struct GNUNET_SOCIAL_Nym);
458 nym->pub_key = *pub_key;
459 nym->pub_key_hash = pub_key_hash;
460 GNUNET_CONTAINER_multihashmap_put (nyms, &nym->pub_key_hash, nym,
461 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST);
462 }
463 return nym;
464}
465
466
467static void
468nym_destroy (struct GNUNET_SOCIAL_Nym *nym)
469{
470 GNUNET_CONTAINER_multihashmap_remove (nyms, &nym->pub_key_hash, nym);
471 GNUNET_free (nym);
472}
473
474
475/*** MESSAGE HANDLERS ***/
476
477/** _notice_place_leave from guests */
478
479static void
480host_recv_notice_place_leave_method (void *cls,
481 const struct GNUNET_PSYC_MessageHeader *msg,
482 const struct GNUNET_PSYC_MessageMethod *meth,
483 uint64_t message_id,
484 const char *method_name)
485{
486 struct GNUNET_SOCIAL_Host *hst = cls;
487
488 if (0 == memcmp (&(struct GNUNET_CRYPTO_EcdsaPublicKey) {},
489 &msg->slave_pub_key, sizeof (msg->slave_pub_key)))
490 return;
491
492 struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&msg->slave_pub_key);
493
494 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495 "Host received method for message ID %" PRIu64 " from nym %s: %s\n",
496 message_id, GNUNET_h2s (&nym->pub_key_hash), method_name);
497
498 hst->notice_place_leave_nym = (struct GNUNET_SOCIAL_Nym *) nym;
499 hst->notice_place_leave_env = GNUNET_PSYC_env_create ();
500
501 char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
502 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
503 "_notice_place_leave: got method from nym %s (%s).\n",
504 GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
505 GNUNET_free (str);
506}
507
508
509static void
510host_recv_notice_place_leave_modifier (void *cls,
511 const struct GNUNET_PSYC_MessageHeader *msg,
512 const struct GNUNET_MessageHeader *pmsg,
513 uint64_t message_id,
514 enum GNUNET_PSYC_Operator oper,
515 const char *name,
516 const void *value,
517 uint16_t value_size,
518 uint16_t full_value_size)
519{
520 struct GNUNET_SOCIAL_Host *hst = cls;
521 if (NULL == hst->notice_place_leave_env)
522 return;
523
524 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
525 "Host received modifier for _notice_place_leave message with ID %" PRIu64 ":\n"
526 "%c%s: %.*s\n",
527 message_id, oper, name, value_size, (const char *) value);
528
529 /* skip _nym, it's added later in eom() */
530 if (0 == memcmp (name, "_nym", sizeof ("_nym"))
531 || 0 == memcmp (name, "_nym_", sizeof ("_nym_") - 1))
532 return;
533
534 GNUNET_PSYC_env_add (hst->notice_place_leave_env,
535 GNUNET_PSYC_OP_SET, name, value, value_size);
536}
537
538
539static void
540host_recv_notice_place_leave_eom (void *cls,
541 const struct GNUNET_PSYC_MessageHeader *msg,
542 const struct GNUNET_MessageHeader *pmsg,
543 uint64_t message_id,
544 uint8_t is_cancelled)
545{
546 struct GNUNET_SOCIAL_Host *hst = cls;
547 if (NULL == hst->notice_place_leave_env)
548 return;
549
550 char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&hst->notice_place_leave_nym->pub_key);
551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
552 "_notice_place_leave: got EOM from nym %s (%s).\n",
553 GNUNET_h2s (&hst->notice_place_leave_nym->pub_key_hash), str);
554 GNUNET_free (str);
555
556 if (GNUNET_YES != is_cancelled)
557 {
558 if (NULL != hst->farewell_cb)
559 hst->farewell_cb (hst->cb_cls, hst->notice_place_leave_nym,
560 hst->notice_place_leave_env);
561 /* announce leaving guest to place */
562 GNUNET_PSYC_env_add (hst->notice_place_leave_env, GNUNET_PSYC_OP_SET,
563 "_nym", hst->notice_place_leave_nym,
564 sizeof (*hst->notice_place_leave_nym));
565 GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave",
566 hst->notice_place_leave_env,
567 NULL, NULL, GNUNET_SOCIAL_ANNOUNCE_NONE);
568 nym_destroy (hst->notice_place_leave_nym);
569 }
570 GNUNET_PSYC_env_destroy (hst->notice_place_leave_env);
571 hst->notice_place_leave_env = NULL;
572}
573
574
575/*** PLACE ***/
576
577
578static int
579check_place_result (void *cls,
580 const struct GNUNET_OperationResultMessage *res)
581{
582 uint16_t size = ntohs (res->header.size);
583 if (size < sizeof (*res))
584 { /* Error, message too small. */
585 GNUNET_break (0);
586 return GNUNET_SYSERR;
587 }
588 return GNUNET_OK;
589}
590
591
592static void
593handle_place_result (void *cls,
594 const struct GNUNET_OperationResultMessage *res)
595{
596 struct GNUNET_SOCIAL_Place *plc = cls;
597
598 uint16_t size = ntohs (res->header.size);
599 uint16_t data_size = size - sizeof (*res);
600 const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
601
602 GNUNET_OP_result (plc->op, GNUNET_ntohll (res->op_id),
603 GNUNET_ntohll (res->result_code),
604 data, data_size, NULL);
605}
606
607
608static int
609check_app_result (void *cls,
610 const struct GNUNET_OperationResultMessage *res)
611{
612 uint16_t size = ntohs (res->header.size);
613 if (size < sizeof (*res))
614 { /* Error, message too small. */
615 GNUNET_break (0);
616 return GNUNET_SYSERR;
617 }
618 return GNUNET_OK;
619}
620
621
622static void
623handle_app_result (void *cls,
624 const struct GNUNET_OperationResultMessage *res)
625{
626 struct GNUNET_SOCIAL_App *app = cls;
627
628 uint16_t size = ntohs (res->header.size);
629 uint16_t data_size = size - sizeof (*res);
630 const char *data = (0 < data_size) ? (const char *) &res[1] : NULL;
631
632 GNUNET_OP_result (app->op, GNUNET_ntohll (res->op_id),
633 GNUNET_ntohll (res->result_code),
634 data, data_size, NULL);
635}
636
637
638static void
639op_recv_history_result (void *cls, int64_t result,
640 const void *err_msg, uint16_t err_msg_size)
641{
642 LOG (GNUNET_ERROR_TYPE_DEBUG,
643 "Received history replay result: %" PRId64 ".\n", result);
644
645 struct GNUNET_SOCIAL_HistoryRequest *hist = cls;
646
647 if (NULL != hist->result_cb)
648 hist->result_cb (hist->cls, result, err_msg, err_msg_size);
649
650 GNUNET_free (hist);
651}
652
653
654static void
655op_recv_state_result (void *cls, int64_t result,
656 const void *err_msg, uint16_t err_msg_size)
657{
658 LOG (GNUNET_ERROR_TYPE_DEBUG,
659 "Received state request result: %" PRId64 ".\n", result);
660
661 struct GNUNET_SOCIAL_LookHandle *look = cls;
662
663 if (NULL != look->result_cb)
664 look->result_cb (look->cls, result, err_msg, err_msg_size);
665
666 GNUNET_free (look);
667}
668
669
670static int
671check_place_history_result (void *cls,
672 const struct GNUNET_OperationResultMessage *res)
673{
674 struct GNUNET_PSYC_MessageHeader *
675 pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res);
676 uint16_t size = ntohs (res->header.size);
677
678 if (NULL == pmsg || size < sizeof (*res) + sizeof (*pmsg))
679 { /* Error, message too small. */
680 GNUNET_break (0);
681 return GNUNET_SYSERR;
682 }
683 return GNUNET_OK;
684}
685
686
687static void
688handle_place_history_result (void *cls,
689 const struct GNUNET_OperationResultMessage *res)
690{
691 struct GNUNET_SOCIAL_Place *plc = cls;
692 struct GNUNET_PSYC_MessageHeader *
693 pmsg = (struct GNUNET_PSYC_MessageHeader *) GNUNET_MQ_extract_nested_mh (res);
694
695 LOG (GNUNET_ERROR_TYPE_DEBUG,
696 "%p Received historic fragment for message #%" PRIu64 ".\n",
697 plc, GNUNET_ntohll (pmsg->message_id));
698
699 GNUNET_ResultCallback result_cb = NULL;
700 struct GNUNET_SOCIAL_HistoryRequest *hist = NULL;
701
702 if (GNUNET_YES != GNUNET_OP_get (plc->op,
703 GNUNET_ntohll (res->op_id),
704 &result_cb, (void *) &hist, NULL))
705 { /* Operation not found. */
706 LOG (GNUNET_ERROR_TYPE_WARNING,
707 "%p Replay operation not found for historic fragment of message #%"
708 PRIu64 ".\n",
709 plc, GNUNET_ntohll (pmsg->message_id));
710 return;
711 }
712
713 GNUNET_PSYC_slicer_message (hist->slicer,
714 (const struct GNUNET_PSYC_MessageHeader *) pmsg);
715}
716
717
718static int
719check_place_state_result (void *cls,
720 const struct GNUNET_OperationResultMessage *res)
721{
722 const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
723 if (NULL == mod)
724 {
725 GNUNET_break_op (0);
726 LOG (GNUNET_ERROR_TYPE_WARNING,
727 "Invalid modifier in state result\n");
728 return GNUNET_SYSERR;
729 }
730
731 uint16_t size = ntohs (res->header.size);
732 uint16_t mod_size = ntohs (mod->size);
733 if (size - sizeof (*res) != mod_size)
734 {
735 GNUNET_break_op (0);
736 LOG (GNUNET_ERROR_TYPE_WARNING,
737 "Invalid modifier size in state result: %u - %u != %u\n",
738 ntohs (res->header.size), sizeof (*res), mod_size);
739 return GNUNET_SYSERR;
740 }
741 return GNUNET_OK;
742}
743
744
745static void
746handle_place_state_result (void *cls,
747 const struct GNUNET_OperationResultMessage *res)
748{
749 struct GNUNET_SOCIAL_Place *plc = cls;
750
751 GNUNET_ResultCallback result_cb = NULL;
752 struct GNUNET_SOCIAL_LookHandle *look = NULL;
753
754 if (GNUNET_YES != GNUNET_OP_get (plc->op,
755 GNUNET_ntohll (res->op_id),
756 &result_cb, (void *) &look, NULL))
757 { /* Operation not found. */
758 return;
759 }
760
761 const struct GNUNET_MessageHeader *mod = GNUNET_MQ_extract_nested_mh (res);
762 uint16_t mod_size = ntohs (mod->size);
763
764 switch (ntohs (mod->type))
765 {
766 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
767 {
768 const struct GNUNET_PSYC_MessageModifier *
769 pmod = (const struct GNUNET_PSYC_MessageModifier *) mod;
770
771 const char *name = (const char *) &pmod[1];
772 uint16_t name_size = ntohs (pmod->name_size);
773 if (0 == name_size
774 || mod_size - sizeof (*pmod) < name_size
775 || '\0' != name[name_size - 1])
776 {
777 GNUNET_break_op (0);
778 LOG (GNUNET_ERROR_TYPE_WARNING,
779 "Invalid modifier name in state result\n");
780 return;
781 }
782 look->mod_value_size = ntohs (pmod->value_size);
783 look->var_cb (look->cls, mod, name, name + name_size,
784 mod_size - sizeof (*mod) - name_size,
785 look->mod_value_size);
786 if (look->mod_value_size > mod_size - sizeof (*mod) - name_size)
787 {
788 look->mod_value_remaining = look->mod_value_size;
789 look->mod_name = GNUNET_malloc (name_size);
790 GNUNET_memcpy (look->mod_name, name, name_size);
791 }
792 break;
793 }
794
795 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
796 look->var_cb (look->cls, mod, look->mod_name, (const char *) &mod[1],
797 mod_size - sizeof (*mod), look->mod_value_size);
798 look->mod_value_remaining -= mod_size - sizeof (*mod);
799 if (0 == look->mod_value_remaining)
800 {
801 GNUNET_free (look->mod_name);
802 }
803 break;
804 }
805}
806
807
808static void
809handle_place_message_ack (void *cls,
810 const struct GNUNET_MessageHeader *msg)
811{
812 struct GNUNET_SOCIAL_Place *plc = cls;
813
814 GNUNET_PSYC_transmit_got_ack (plc->tmit);
815}
816
817
818static int
819check_place_message (void *cls,
820 const struct GNUNET_PSYC_MessageHeader *pmsg)
821{
822 return GNUNET_OK;
823}
824
825
826static void
827handle_place_message (void *cls,
828 const struct GNUNET_PSYC_MessageHeader *pmsg)
829{
830 struct GNUNET_SOCIAL_Place *plc = cls;
831
832 GNUNET_PSYC_slicer_message (plc->slicer, pmsg);
833}
834
835
836static int
837check_host_message (void *cls,
838 const struct GNUNET_PSYC_MessageHeader *pmsg)
839{
840 return GNUNET_OK;
841}
842
843
844static void
845handle_host_message (void *cls,
846 const struct GNUNET_PSYC_MessageHeader *pmsg)
847{
848 struct GNUNET_SOCIAL_Host *hst = cls;
849
850 GNUNET_PSYC_slicer_message (hst->slicer, pmsg);
851 GNUNET_PSYC_slicer_message (hst->plc.slicer, pmsg);
852}
853
854
855static void
856handle_host_enter_ack (void *cls,
857 const struct HostEnterAck *hack)
858{
859 struct GNUNET_SOCIAL_Host *hst = cls;
860
861 hst->plc.pub_key = hack->place_pub_key;
862
863 int32_t result = ntohl (hack->result_code);
864 if (NULL != hst->enter_cb)
865 hst->enter_cb (hst->cb_cls, result, &hack->place_pub_key,
866 GNUNET_ntohll (hack->max_message_id));
867}
868
869
870static int
871check_host_enter_request (void *cls,
872 const struct GNUNET_PSYC_JoinRequestMessage *req)
873{
874 return GNUNET_OK;
875}
876
877
878static void
879handle_host_enter_request (void *cls,
880 const struct GNUNET_PSYC_JoinRequestMessage *req)
881{
882 struct GNUNET_SOCIAL_Host *hst = cls;
883
884 if (NULL == hst->answer_door_cb)
885 return;
886
887 const char *method_name = NULL;
888 struct GNUNET_PSYC_Environment *env = NULL;
889 struct GNUNET_PSYC_MessageHeader *entry_pmsg = NULL;
890 const void *data = NULL;
891 uint16_t data_size = 0;
892 char *str;
893 const struct GNUNET_PSYC_Message *join_msg = NULL;
894
895 do
896 {
897 if (sizeof (*req) + sizeof (*join_msg) <= ntohs (req->header.size))
898 {
899 join_msg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (req);
900 LOG (GNUNET_ERROR_TYPE_DEBUG,
901 "Received join_msg of type %u and size %u.\n",
902 ntohs (join_msg->header.type), ntohs (join_msg->header.size));
903
904 env = GNUNET_PSYC_env_create ();
905 entry_pmsg = GNUNET_PSYC_message_header_create_from_psyc (join_msg);
906 if (GNUNET_OK != GNUNET_PSYC_message_parse (entry_pmsg, &method_name, env,
907 &data, &data_size))
908 {
909 GNUNET_break_op (0);
910 str = GNUNET_CRYPTO_ecdsa_public_key_to_string (&req->slave_pub_key);
911 LOG (GNUNET_ERROR_TYPE_WARNING,
912 "Ignoring invalid entry request from nym %s.\n",
913 str);
914 GNUNET_free (str);
915 break;
916 }
917 }
918
919 struct GNUNET_SOCIAL_Nym *nym = nym_get_or_create (&req->slave_pub_key);
920 hst->answer_door_cb (hst->cb_cls, nym, method_name, env,
921 data, data_size);
922 } while (0);
923
924 if (NULL != env)
925 GNUNET_PSYC_env_destroy (env);
926 if (NULL != entry_pmsg)
927 GNUNET_free (entry_pmsg);
928}
929
930
931static void
932handle_guest_enter_ack (void *cls,
933 const struct GNUNET_PSYC_CountersResultMessage *cres)
934{
935 struct GNUNET_SOCIAL_Guest *gst = cls;
936
937 int32_t result = ntohl (cres->result_code);
938 if (NULL != gst->enter_cb)
939 gst->enter_cb (gst->cb_cls, result, &gst->plc.pub_key,
940 GNUNET_ntohll (cres->max_message_id));
941}
942
943
944static int
945check_guest_enter_decision (void *cls,
946 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
947{
948 return GNUNET_OK;
949}
950
951
952static void
953handle_guest_enter_decision (void *cls,
954 const struct GNUNET_PSYC_JoinDecisionMessage *dcsn)
955{
956 struct GNUNET_SOCIAL_Guest *gst = cls;
957
958 struct GNUNET_PSYC_Message *pmsg = NULL;
959 if (ntohs (dcsn->header.size) > sizeof (*dcsn))
960 pmsg = (struct GNUNET_PSYC_Message *) GNUNET_MQ_extract_nested_mh (dcsn);
961
962 if (NULL != gst->entry_dcsn_cb)
963 gst->entry_dcsn_cb (gst->cb_cls, ntohl (dcsn->is_admitted), pmsg);
964}
965
966
967static int
968check_app_ego (void *cls,
969 const struct AppEgoMessage *emsg)
970{
971 return GNUNET_OK;
972}
973
974
975static void
976handle_app_ego (void *cls,
977 const struct AppEgoMessage *emsg)
978{
979 struct GNUNET_SOCIAL_App *app = cls;
980
981 uint16_t name_size = ntohs (emsg->header.size) - sizeof (*emsg);
982
983 struct GNUNET_HashCode ego_pub_hash;
984 GNUNET_CRYPTO_hash (&emsg->ego_pub_key, sizeof (emsg->ego_pub_key),
985 &ego_pub_hash);
986
987 struct GNUNET_SOCIAL_Ego *
988 ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
989 if (NULL == ego)
990 {
991 ego = GNUNET_malloc (sizeof (*ego));
992 ego->pub_key = emsg->ego_pub_key;
993 ego->name = GNUNET_malloc (name_size);
994 GNUNET_memcpy (ego->name, &emsg[1], name_size);
995 }
996 else
997 {
998 ego->name = GNUNET_realloc (ego->name, name_size);
999 GNUNET_memcpy (ego->name, &emsg[1], name_size);
1000 }
1001
1002 GNUNET_CONTAINER_multihashmap_put (app->egos, &ego_pub_hash, ego,
1003 GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1004
1005 if (NULL != app->ego_cb)
1006 app->ego_cb (app->cb_cls, ego, &ego->pub_key, ego->name);
1007}
1008
1009
1010static void
1011handle_app_ego_end (void *cls,
1012 const struct GNUNET_MessageHeader *msg)
1013{
1014 //struct GNUNET_SOCIAL_App *app = cls;
1015}
1016
1017
1018static int
1019check_app_place (void *cls,
1020 const struct AppPlaceMessage *pmsg)
1021{
1022 return GNUNET_OK;
1023}
1024
1025
1026static void
1027handle_app_place (void *cls,
1028 const struct AppPlaceMessage *pmsg)
1029{
1030 struct GNUNET_SOCIAL_App *app = cls;
1031
1032 if ((GNUNET_YES == pmsg->is_host && NULL == app->host_cb)
1033 || (GNUNET_NO == pmsg->is_host && NULL == app->guest_cb))
1034 return;
1035
1036 struct GNUNET_HashCode ego_pub_hash;
1037 GNUNET_CRYPTO_hash (&pmsg->ego_pub_key, sizeof (pmsg->ego_pub_key),
1038 &ego_pub_hash);
1039 struct GNUNET_SOCIAL_Ego *
1040 ego = GNUNET_CONTAINER_multihashmap_get (app->egos, &ego_pub_hash);
1041 if (NULL == ego)
1042 {
1043 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failure to obtain ego %s.\n",
1044 GNUNET_h2s (&ego_pub_hash));
1045 GNUNET_break (0);
1046 return;
1047 }
1048
1049 if (GNUNET_YES == pmsg->is_host)
1050 {
1051 if (NULL != app->host_cb) {
1052 struct GNUNET_SOCIAL_HostConnection *hconn = GNUNET_malloc (sizeof (*hconn));
1053 hconn->app = app;
1054 hconn->plc_msg = *pmsg;
1055 app->host_cb (app->cb_cls, hconn, ego, &pmsg->place_pub_key, pmsg->place_state);
1056 GNUNET_free (hconn);
1057 }
1058 }
1059 else if (NULL != app->guest_cb)
1060 {
1061 struct GNUNET_SOCIAL_GuestConnection *gconn = GNUNET_malloc (sizeof (*gconn));
1062 gconn->app = app;
1063 gconn->plc_msg = *pmsg;
1064 app->guest_cb (app->cb_cls, gconn, ego, &pmsg->place_pub_key, pmsg->place_state);
1065 GNUNET_free (gconn);
1066 }
1067}
1068
1069
1070static void
1071handle_app_place_end (void *cls,
1072 const struct GNUNET_MessageHeader *msg)
1073{
1074 struct GNUNET_SOCIAL_App *app = cls;
1075
1076 if (NULL != app->connected_cb)
1077 app->connected_cb (app->cb_cls);
1078}
1079
1080
1081/**
1082 * Handler for a #GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK message received
1083 * from the social service.
1084 *
1085 * @param cls the place of type `struct GNUNET_SOCIAL_Place`
1086 * @param msg the message received from the service
1087 */
1088static void
1089handle_place_leave_ack (void *cls,
1090 const struct GNUNET_MessageHeader *msg)
1091{
1092 struct GNUNET_SOCIAL_Place *plc = cls;
1093
1094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1095 "%s left place %p\n",
1096 plc->is_host ? "host" : "guest",
1097 plc);
1098 place_disconnect (plc);
1099}
1100
1101
1102/*** HOST ***/
1103
1104
1105static void
1106host_connect (struct GNUNET_SOCIAL_Host *hst);
1107
1108
1109static void
1110host_reconnect (void *cls)
1111{
1112 host_connect (cls);
1113}
1114
1115
1116/**
1117 * Host client disconnected from service.
1118 *
1119 * Reconnect after backoff period.
1120 */
1121static void
1122host_disconnected (void *cls, enum GNUNET_MQ_Error error)
1123{
1124 struct GNUNET_SOCIAL_Host *hst = cls;
1125 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1126
1127 LOG (GNUNET_ERROR_TYPE_DEBUG,
1128 "Host client disconnected (%d), re-connecting\n",
1129 (int) error);
1130 if (NULL != plc->tmit)
1131 {
1132 GNUNET_PSYC_transmit_destroy (plc->tmit);
1133 plc->tmit = NULL;
1134 }
1135 if (NULL != plc->mq)
1136 {
1137 GNUNET_MQ_destroy (plc->mq);
1138 plc->mq = NULL;
1139 }
1140
1141 plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
1142 host_reconnect,
1143 hst);
1144 plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
1145}
1146
1147
1148static void
1149host_connect (struct GNUNET_SOCIAL_Host *hst)
1150{
1151 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1152
1153 struct GNUNET_MQ_MessageHandler handlers[] = {
1154 GNUNET_MQ_hd_fixed_size (host_enter_ack,
1155 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER_ACK,
1156 struct HostEnterAck,
1157 hst),
1158 GNUNET_MQ_hd_fixed_size (place_leave_ack,
1159 GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
1160 struct GNUNET_MessageHeader,
1161 plc),
1162 GNUNET_MQ_hd_var_size (host_enter_request,
1163 GNUNET_MESSAGE_TYPE_PSYC_JOIN_REQUEST,
1164 struct GNUNET_PSYC_JoinRequestMessage,
1165 hst),
1166 GNUNET_MQ_hd_var_size (host_message,
1167 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1168 struct GNUNET_PSYC_MessageHeader,
1169 hst),
1170 GNUNET_MQ_hd_fixed_size (place_message_ack,
1171 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1172 struct GNUNET_MessageHeader,
1173 plc),
1174 GNUNET_MQ_hd_var_size (place_history_result,
1175 GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1176 struct GNUNET_OperationResultMessage,
1177 plc),
1178 GNUNET_MQ_hd_var_size (place_state_result,
1179 GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1180 struct GNUNET_OperationResultMessage,
1181 plc),
1182 GNUNET_MQ_hd_var_size (place_result,
1183 GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1184 struct GNUNET_OperationResultMessage,
1185 plc),
1186 GNUNET_MQ_handler_end ()
1187 };
1188
1189 plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
1190 handlers, host_disconnected, hst);
1191 GNUNET_assert (NULL != plc->mq);
1192 plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
1193
1194 GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
1195}
1196
1197
1198/**
1199 * Enter a place as host.
1200 *
1201 * A place is created upon first entering, and it is active until permanently
1202 * left using GNUNET_SOCIAL_host_leave().
1203 *
1204 * @param app
1205 * Application handle.
1206 * @param ego
1207 * Identity of the host.
1208 * @param policy
1209 * Policy specifying entry and history restrictions for the place.
1210 * @param slicer
1211 * Slicer to handle incoming messages.
1212 * @param enter_cb
1213 * Function called when the place is entered and ready to use.
1214 * @param answer_door_cb
1215 * Function to handle new nyms that want to enter.
1216 * @param farewell_cb
1217 * Function to handle departing nyms.
1218 * @param cls
1219 * Closure for the callbacks.
1220 *
1221 * @return Handle for the host. This handle contains the pubkey.
1222 */
1223struct GNUNET_SOCIAL_Host *
1224GNUNET_SOCIAL_host_enter (const struct GNUNET_SOCIAL_App *app,
1225 const struct GNUNET_SOCIAL_Ego *ego,
1226 enum GNUNET_PSYC_Policy policy,
1227 struct GNUNET_PSYC_Slicer *slicer,
1228 GNUNET_SOCIAL_HostEnterCallback enter_cb,
1229 GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1230 GNUNET_SOCIAL_FarewellCallback farewell_cb,
1231 void *cls)
1232{
1233 struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
1234 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1235
1236 plc->cfg = app->cfg;
1237 plc->is_host = GNUNET_YES;
1238 plc->slicer = slicer;
1239
1240 hst->enter_cb = enter_cb;
1241 hst->answer_door_cb = answer_door_cb;
1242 hst->farewell_cb = farewell_cb;
1243 hst->cb_cls = cls;
1244
1245 plc->op = GNUNET_OP_create ();
1246
1247 hst->slicer = GNUNET_PSYC_slicer_create ();
1248 GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
1249 host_recv_notice_place_leave_method,
1250 host_recv_notice_place_leave_modifier,
1251 NULL, host_recv_notice_place_leave_eom, hst);
1252
1253 uint16_t app_id_size = strlen (app->id) + 1;
1254 struct HostEnterRequest *hreq;
1255 plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
1256 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
1257 hreq->policy = policy;
1258 hreq->ego_pub_key = ego->pub_key;
1259 GNUNET_memcpy (&hreq[1], app->id, app_id_size);
1260
1261 host_connect (hst);
1262 return hst;
1263}
1264
1265
1266/**
1267 * Reconnect to an already entered place as host.
1268 *
1269 * @param hconn
1270 * Host connection handle.
1271 * @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppHostPlaceCallback()
1272 * @param slicer
1273 * Slicer to handle incoming messages.
1274 * @param enter_cb
1275 * Function called when the place is entered and ready to use.
1276 * @param answer_door_cb
1277 * Function to handle new nyms that want to enter.
1278 * @param farewell_cb
1279 * Function to handle departing nyms.
1280 * @param cls
1281 * Closure for the callbacks.
1282 *
1283 * @return Handle for the host.
1284 */
1285 struct GNUNET_SOCIAL_Host *
1286GNUNET_SOCIAL_host_enter_reconnect (struct GNUNET_SOCIAL_HostConnection *hconn,
1287 struct GNUNET_PSYC_Slicer *slicer,
1288 GNUNET_SOCIAL_HostEnterCallback enter_cb,
1289 GNUNET_SOCIAL_AnswerDoorCallback answer_door_cb,
1290 GNUNET_SOCIAL_FarewellCallback farewell_cb,
1291 void *cls)
1292{
1293 struct GNUNET_SOCIAL_Host *hst = GNUNET_malloc (sizeof (*hst));
1294 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1295
1296 hst->enter_cb = enter_cb;
1297 hst->answer_door_cb = answer_door_cb;
1298 hst->farewell_cb = farewell_cb;
1299 hst->cb_cls = cls;
1300
1301 plc->cfg = hconn->app->cfg;
1302 plc->is_host = GNUNET_YES;
1303 plc->slicer = slicer;
1304 plc->pub_key = hconn->plc_msg.place_pub_key;
1305 plc->ego_pub_key = hconn->plc_msg.ego_pub_key;
1306
1307 plc->op = GNUNET_OP_create ();
1308
1309 hst->slicer = GNUNET_PSYC_slicer_create ();
1310 GNUNET_PSYC_slicer_method_add (hst->slicer, "_notice_place_leave", NULL,
1311 host_recv_notice_place_leave_method,
1312 host_recv_notice_place_leave_modifier,
1313 NULL, host_recv_notice_place_leave_eom, hst);
1314
1315 size_t app_id_size = strlen (hconn->app->id) + 1;
1316 struct HostEnterRequest *hreq;
1317 plc->connect_env = GNUNET_MQ_msg_extra (hreq, app_id_size,
1318 GNUNET_MESSAGE_TYPE_SOCIAL_HOST_ENTER);
1319 hreq->place_pub_key = hconn->plc_msg.place_pub_key;
1320 hreq->ego_pub_key = hconn->plc_msg.ego_pub_key;
1321 GNUNET_memcpy (&hreq[1], hconn->app->id, app_id_size);
1322
1323 host_connect (hst);
1324 return hst;
1325}
1326
1327
1328/**
1329 * Decision whether to admit @a nym into the place or refuse entry.
1330 *
1331 * @param hst
1332 * Host of the place.
1333 * @param nym
1334 * Handle for the entity that wanted to enter.
1335 * @param is_admitted
1336 * #GNUNET_YES if @a nym is admitted,
1337 * #GNUNET_NO if @a nym is refused entry,
1338 * #GNUNET_SYSERR if we cannot answer the request.
1339 * @param method_name
1340 * Method name for the rejection message.
1341 * @param env
1342 * Environment containing variables for the message, or NULL.
1343 * @param data
1344 * Data for the rejection message to send back.
1345 * @param data_size
1346 * Number of bytes in @a data for method.
1347 * @return #GNUNET_OK on success,
1348 * #GNUNET_SYSERR if the message is too large.
1349 */
1350int
1351GNUNET_SOCIAL_host_entry_decision (struct GNUNET_SOCIAL_Host *hst,
1352 struct GNUNET_SOCIAL_Nym *nym,
1353 int is_admitted,
1354 const struct GNUNET_PSYC_Message *entry_resp)
1355{
1356 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1357 struct GNUNET_PSYC_JoinDecisionMessage *dcsn;
1358 uint16_t entry_resp_size
1359 = (NULL != entry_resp) ? ntohs (entry_resp->header.size) : 0;
1360
1361 if (GNUNET_MULTICAST_FRAGMENT_MAX_PAYLOAD < sizeof (*dcsn) + entry_resp_size)
1362 return GNUNET_SYSERR;
1363
1364 struct GNUNET_MQ_Envelope *
1365 env = GNUNET_MQ_msg_extra (dcsn, entry_resp_size,
1366 GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION);
1367 dcsn->is_admitted = htonl (is_admitted);
1368 dcsn->slave_pub_key = nym->pub_key;
1369
1370 if (0 < entry_resp_size)
1371 GNUNET_memcpy (&dcsn[1], entry_resp, entry_resp_size);
1372
1373 GNUNET_MQ_send (plc->mq, env);
1374 return GNUNET_OK;
1375}
1376
1377
1378/**
1379 * Throw @a nym out of the place.
1380 *
1381 * The @a nym reference will remain valid until the
1382 * #GNUNET_SOCIAL_FarewellCallback is invoked,
1383 * which should be very soon after this call.
1384 *
1385 * @param host
1386 * Host of the place.
1387 * @param nym
1388 * Handle for the entity to be ejected.
1389 * @param env
1390 * Environment for the message or NULL.
1391 */
1392void
1393GNUNET_SOCIAL_host_eject (struct GNUNET_SOCIAL_Host *hst,
1394 const struct GNUNET_SOCIAL_Nym *nym,
1395 struct GNUNET_PSYC_Environment *e)
1396{
1397 struct GNUNET_PSYC_Environment *env = e;
1398 if (NULL == env)
1399 env = GNUNET_PSYC_env_create ();
1400 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
1401 "_nym", &nym->pub_key, sizeof (nym->pub_key));
1402 GNUNET_SOCIAL_host_announce (hst, "_notice_place_leave", env, NULL, NULL,
1403 GNUNET_SOCIAL_ANNOUNCE_NONE);
1404 if (NULL == e)
1405 GNUNET_PSYC_env_destroy (env);
1406}
1407
1408
1409/**
1410 * Get the public key of @a ego.
1411 *
1412 * @param ego
1413 * Ego.
1414 *
1415 * @return Public key of ego.
1416 */
1417const struct GNUNET_CRYPTO_EcdsaPublicKey *
1418GNUNET_SOCIAL_ego_get_pub_key (const struct GNUNET_SOCIAL_Ego *ego)
1419{
1420 return &ego->pub_key;
1421}
1422
1423
1424/**
1425 * Get the hash of the public key of @a ego.
1426 *
1427 * @param ego
1428 * Ego.
1429 *
1430 * @return Hash of the public key of @a ego.
1431 */
1432const struct GNUNET_HashCode *
1433GNUNET_SOCIAL_ego_get_pub_key_hash (const struct GNUNET_SOCIAL_Ego *ego)
1434{
1435 return &ego->pub_key_hash;
1436}
1437
1438
1439/**
1440 * Get the name of @a ego.
1441 *
1442 * @param ego
1443 * Ego.
1444 *
1445 * @return Public key of @a ego.
1446 */
1447const char *
1448GNUNET_SOCIAL_ego_get_name (const struct GNUNET_SOCIAL_Ego *ego)
1449{
1450 return ego->name;
1451}
1452
1453
1454/**
1455 * Get the public key of @a nym.
1456 *
1457 * Suitable, for example, to be used with GNUNET_SOCIAL_zone_add_nym().
1458 *
1459 * @param nym
1460 * Pseudonym.
1461 *
1462 * @return Public key of @a nym.
1463 */
1464const struct GNUNET_CRYPTO_EcdsaPublicKey *
1465GNUNET_SOCIAL_nym_get_pub_key (const struct GNUNET_SOCIAL_Nym *nym)
1466{
1467 return &nym->pub_key;
1468}
1469
1470
1471/**
1472 * Get the hash of the public key of @a nym.
1473 *
1474 * @param nym
1475 * Pseudonym.
1476 *
1477 * @return Hash of the public key of @a nym.
1478 */
1479const struct GNUNET_HashCode *
1480GNUNET_SOCIAL_nym_get_pub_key_hash (const struct GNUNET_SOCIAL_Nym *nym)
1481{
1482 return &nym->pub_key_hash;
1483}
1484
1485
1486/**
1487 * Send a message to all nyms that are present in the place.
1488 *
1489 * This function is restricted to the host. Nyms can only send requests
1490 * to the host who can decide to relay it to everyone in the place.
1491 *
1492 * @param host Host of the place.
1493 * @param method_name Method to use for the announcement.
1494 * @param env Environment containing variables for the message and operations
1495 * on objects of the place. Can be NULL.
1496 * @param notify Function to call to get the payload of the announcement.
1497 * @param notify_cls Closure for @a notify.
1498 * @param flags Flags for this announcement.
1499 *
1500 * @return NULL on error (announcement already in progress?).
1501 */
1502struct GNUNET_SOCIAL_Announcement *
1503GNUNET_SOCIAL_host_announce (struct GNUNET_SOCIAL_Host *hst,
1504 const char *method_name,
1505 const struct GNUNET_PSYC_Environment *env,
1506 GNUNET_PSYC_TransmitNotifyData notify_data,
1507 void *notify_data_cls,
1508 enum GNUNET_SOCIAL_AnnounceFlags flags)
1509{
1510 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1511 "PSYC_transmit_message for host, method: %s\n",
1512 method_name);
1513 if (GNUNET_OK ==
1514 GNUNET_PSYC_transmit_message (hst->plc.tmit, method_name, env,
1515 NULL, notify_data, notify_data_cls, flags))
1516 return (struct GNUNET_SOCIAL_Announcement *) hst->plc.tmit;
1517 else
1518 return NULL;
1519}
1520
1521
1522/**
1523 * Resume transmitting announcement.
1524 *
1525 * @param a
1526 * The announcement to resume.
1527 */
1528void
1529GNUNET_SOCIAL_host_announce_resume (struct GNUNET_SOCIAL_Announcement *a)
1530{
1531 GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) a);
1532}
1533
1534
1535/**
1536 * Cancel announcement.
1537 *
1538 * @param a
1539 * The announcement to cancel.
1540 */
1541void
1542GNUNET_SOCIAL_host_announce_cancel (struct GNUNET_SOCIAL_Announcement *a)
1543{
1544 GNUNET_PSYC_transmit_cancel ((struct GNUNET_PSYC_TransmitHandle *) a);
1545}
1546
1547
1548/**
1549 * Obtain handle for a hosted place.
1550 *
1551 * The returned handle can be used to access the place API.
1552 *
1553 * @param host Handle for the host.
1554 *
1555 * @return Handle for the hosted place, valid as long as @a host is valid.
1556 */
1557struct GNUNET_SOCIAL_Place *
1558GNUNET_SOCIAL_host_get_place (struct GNUNET_SOCIAL_Host *hst)
1559{
1560 return &hst->plc;
1561}
1562
1563
1564/**
1565 * Disconnect from a home.
1566 *
1567 * Invalidates host handle.
1568 *
1569 * @param hst
1570 * The host to disconnect.
1571 */
1572void
1573GNUNET_SOCIAL_host_disconnect (struct GNUNET_SOCIAL_Host *hst,
1574 GNUNET_ContinuationCallback disconnect_cb,
1575 void *cls)
1576{
1577 struct GNUNET_SOCIAL_Place *plc = &hst->plc;
1578
1579 plc->disconnect_cb = disconnect_cb;
1580 plc->disconnect_cls = cls;
1581 place_disconnect (plc);
1582}
1583
1584
1585/**
1586 * Stop hosting the home.
1587 *
1588 * Sends a _notice_place_closing announcement to the home.
1589 * Invalidates host handle.
1590 *
1591 * @param hst
1592 * The host leaving.
1593 * @param env
1594 * Environment for the message or NULL.
1595 * _nym is set to @e nym regardless whether an @e env is provided.
1596 * @param disconnect_cb
1597 * Function called after the host left the place
1598 * and disconnected from the social service.
1599 * @param cls
1600 * Closure for @a disconnect_cb.
1601 */
1602void
1603GNUNET_SOCIAL_host_leave (struct GNUNET_SOCIAL_Host *hst,
1604 const struct GNUNET_PSYC_Environment *env,
1605 GNUNET_ContinuationCallback disconnect_cb,
1606 void *cls)
1607{
1608 struct GNUNET_MQ_Envelope *envelope;
1609
1610 GNUNET_SOCIAL_host_announce (hst, "_notice_place_closing", env, NULL, NULL,
1611 GNUNET_SOCIAL_ANNOUNCE_NONE);
1612 hst->plc.disconnect_cb = disconnect_cb;
1613 hst->plc.disconnect_cls = cls;
1614 envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
1615 GNUNET_MQ_send (hst->plc.mq,
1616 envelope);
1617}
1618
1619
1620/*** GUEST ***/
1621
1622
1623static void
1624guest_connect (struct GNUNET_SOCIAL_Guest *gst);
1625
1626
1627static void
1628guest_reconnect (void *cls)
1629{
1630 guest_connect (cls);
1631}
1632
1633
1634/**
1635 * Guest client disconnected from service.
1636 *
1637 * Reconnect after backoff period.
1638 */
1639static void
1640guest_disconnected (void *cls, enum GNUNET_MQ_Error error)
1641{
1642 struct GNUNET_SOCIAL_Guest *gst = cls;
1643 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1644
1645 LOG (GNUNET_ERROR_TYPE_DEBUG,
1646 "Guest client disconnected (%d), re-connecting\n",
1647 (int) error);
1648 if (NULL != plc->tmit)
1649 {
1650 GNUNET_PSYC_transmit_destroy (plc->tmit);
1651 plc->tmit = NULL;
1652 }
1653 if (NULL != plc->mq)
1654 {
1655 GNUNET_MQ_destroy (plc->mq);
1656 plc->mq = NULL;
1657 }
1658
1659 plc->reconnect_task = GNUNET_SCHEDULER_add_delayed (plc->reconnect_delay,
1660 guest_reconnect,
1661 gst);
1662 plc->reconnect_delay = GNUNET_TIME_STD_BACKOFF (plc->reconnect_delay);
1663}
1664
1665
1666static void
1667guest_connect (struct GNUNET_SOCIAL_Guest *gst)
1668{
1669 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1670
1671 struct GNUNET_MQ_MessageHandler handlers[] = {
1672 GNUNET_MQ_hd_fixed_size (guest_enter_ack,
1673 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_ACK,
1674 struct GNUNET_PSYC_CountersResultMessage,
1675 gst),
1676 GNUNET_MQ_hd_fixed_size (place_leave_ack,
1677 GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE_ACK,
1678 struct GNUNET_MessageHeader,
1679 plc),
1680 GNUNET_MQ_hd_var_size (guest_enter_decision,
1681 GNUNET_MESSAGE_TYPE_PSYC_JOIN_DECISION,
1682 struct GNUNET_PSYC_JoinDecisionMessage,
1683 gst),
1684 GNUNET_MQ_hd_var_size (place_message,
1685 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE,
1686 struct GNUNET_PSYC_MessageHeader,
1687 plc),
1688 GNUNET_MQ_hd_fixed_size (place_message_ack,
1689 GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_ACK,
1690 struct GNUNET_MessageHeader,
1691 plc),
1692 GNUNET_MQ_hd_var_size (place_history_result,
1693 GNUNET_MESSAGE_TYPE_PSYC_HISTORY_RESULT,
1694 struct GNUNET_OperationResultMessage,
1695 plc),
1696 GNUNET_MQ_hd_var_size (place_state_result,
1697 GNUNET_MESSAGE_TYPE_PSYC_STATE_RESULT,
1698 struct GNUNET_OperationResultMessage,
1699 plc),
1700 GNUNET_MQ_hd_var_size (place_result,
1701 GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
1702 struct GNUNET_OperationResultMessage,
1703 plc),
1704 GNUNET_MQ_handler_end ()
1705 };
1706
1707 plc->mq = GNUNET_CLIENT_connect (plc->cfg, "social",
1708 handlers, guest_disconnected, gst);
1709 GNUNET_assert (NULL != plc->mq);
1710 plc->tmit = GNUNET_PSYC_transmit_create (plc->mq);
1711
1712 GNUNET_MQ_send_copy (plc->mq, plc->connect_env);
1713}
1714
1715
1716static struct GNUNET_MQ_Envelope *
1717guest_enter_request_create (const char *app_id,
1718 const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
1719 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1720 const struct GNUNET_PeerIdentity *origin,
1721 size_t relay_count,
1722 const struct GNUNET_PeerIdentity *relays,
1723 const struct GNUNET_PSYC_Message *join_msg)
1724{
1725 uint16_t app_id_size = strlen (app_id) + 1;
1726 uint16_t join_msg_size = ntohs (join_msg->header.size);
1727 uint16_t relay_size = relay_count * sizeof (*relays);
1728
1729 struct GuestEnterRequest *greq;
1730 struct GNUNET_MQ_Envelope *
1731 env = GNUNET_MQ_msg_extra (greq, app_id_size + relay_size + join_msg_size,
1732 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
1733 greq->place_pub_key = *place_pub_key;
1734 greq->ego_pub_key = *ego_pub_key;
1735 greq->origin = *origin;
1736 greq->relay_count = htonl (relay_count);
1737
1738 char *p = (char *) &greq[1];
1739 GNUNET_memcpy (p, app_id, app_id_size);
1740 p += app_id_size;
1741
1742 if (0 < relay_size)
1743 {
1744 GNUNET_memcpy (p, relays, relay_size);
1745 p += relay_size;
1746 }
1747
1748 GNUNET_memcpy (p, join_msg, join_msg_size);
1749 return env;
1750}
1751
1752
1753/**
1754 * Request entry to a place as a guest.
1755 *
1756 * @param app
1757 * Application handle.
1758 * @param ego
1759 * Identity of the guest.
1760 * @param place_pub_key
1761 * Public key of the place to enter.
1762 * @param flags
1763 * Flags for the entry.
1764 * @param origin
1765 * Peer identity of the origin of the underlying multicast group.
1766 * @param relay_count
1767 * Number of elements in the @a relays array.
1768 * @param relays
1769 * Relays for the underlying multicast group.
1770 * @param method_name
1771 * Method name for the message.
1772 * @param env
1773 * Environment containing variables for the message, or NULL.
1774 * @param data
1775 * Payload for the message to give to the enter callback.
1776 * @param data_size
1777 * Number of bytes in @a data.
1778 * @param slicer
1779 * Slicer to use for processing incoming requests from guests.
1780 *
1781 * @return NULL on errors, otherwise handle for the guest.
1782 */
1783struct GNUNET_SOCIAL_Guest *
1784GNUNET_SOCIAL_guest_enter (const struct GNUNET_SOCIAL_App *app,
1785 const struct GNUNET_SOCIAL_Ego *ego,
1786 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1787 enum GNUNET_PSYC_SlaveJoinFlags flags,
1788 const struct GNUNET_PeerIdentity *origin,
1789 uint32_t relay_count,
1790 const struct GNUNET_PeerIdentity *relays,
1791 const struct GNUNET_PSYC_Message *entry_msg,
1792 struct GNUNET_PSYC_Slicer *slicer,
1793 GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1794 GNUNET_SOCIAL_EntryDecisionCallback entry_dcsn_cb,
1795 void *cls)
1796{
1797 struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1798 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1799
1800 plc->ego_pub_key = ego->pub_key;
1801 plc->pub_key = *place_pub_key;
1802 plc->cfg = app->cfg;
1803 plc->is_host = GNUNET_NO;
1804 plc->slicer = slicer;
1805
1806 plc->op = GNUNET_OP_create ();
1807
1808 plc->connect_env
1809 = guest_enter_request_create (app->id, &ego->pub_key, &plc->pub_key,
1810 origin, relay_count, relays, entry_msg);
1811
1812 gst->enter_cb = local_enter_cb;
1813 gst->entry_dcsn_cb = entry_dcsn_cb;
1814 gst->cb_cls = cls;
1815
1816 guest_connect (gst);
1817 return gst;
1818}
1819
1820
1821/**
1822 * Request entry to a place by name as a guest.
1823 *
1824 * @param app
1825 * Application handle.
1826 * @param ego
1827 * Identity of the guest.
1828 * @param gns_name
1829 * GNS name of the place to enter. Either in the form of
1830 * 'room.friend.gnu', or 'NYMPUBKEY.zkey'. This latter case refers to
1831 * the 'PLACE' record of the empty label ("+") in the GNS zone with the
1832 * nym's public key 'NYMPUBKEY', and can be used to request entry to a
1833 * pseudonym's place directly.
1834 * @param password
1835 * Password to decrypt the record, or NULL for cleartext records.
1836 * @param join_msg
1837 * Entry request message or NULL.
1838 * @param slicer
1839 * Slicer to use for processing incoming requests from guests.
1840 * @param local_enter_cb
1841 * Called upon connection established to the social service.
1842 * @param entry_decision_cb
1843 * Called upon receiving entry decision.
1844 *
1845 * @return NULL on errors, otherwise handle for the guest.
1846 */
1847struct GNUNET_SOCIAL_Guest *
1848GNUNET_SOCIAL_guest_enter_by_name (const struct GNUNET_SOCIAL_App *app,
1849 const struct GNUNET_SOCIAL_Ego *ego,
1850 const char *gns_name,
1851 const char *password,
1852 const struct GNUNET_PSYC_Message *join_msg,
1853 struct GNUNET_PSYC_Slicer *slicer,
1854 GNUNET_SOCIAL_GuestEnterCallback local_enter_cb,
1855 GNUNET_SOCIAL_EntryDecisionCallback entry_decision_cb,
1856 void *cls)
1857{
1858 struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1859 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1860
1861 if (NULL == password)
1862 password = "";
1863
1864 uint16_t app_id_size = strlen (app->id) + 1;
1865 uint16_t gns_name_size = strlen (gns_name) + 1;
1866 uint16_t password_size = strlen (password) + 1;
1867
1868 uint16_t join_msg_size = 0;
1869 if (NULL != join_msg)
1870 join_msg_size = ntohs (join_msg->header.size);
1871
1872 struct GuestEnterByNameRequest *greq;
1873 plc->connect_env
1874 = GNUNET_MQ_msg_extra (greq, app_id_size + gns_name_size
1875 + password_size + join_msg_size,
1876 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER_BY_NAME);
1877
1878 greq->ego_pub_key = ego->pub_key;
1879
1880 char *p = (char *) &greq[1];
1881 GNUNET_memcpy (p, app->id, app_id_size);
1882 p += app_id_size;
1883 GNUNET_memcpy (p, gns_name, gns_name_size);
1884 p += gns_name_size;
1885 GNUNET_memcpy (p, password, password_size);
1886 p += password_size;
1887 if (NULL != join_msg)
1888 GNUNET_memcpy (p, join_msg, join_msg_size);
1889
1890 plc->ego_pub_key = ego->pub_key;
1891 plc->cfg = app->cfg;
1892 plc->is_host = GNUNET_NO;
1893 plc->slicer = slicer;
1894
1895 plc->op = GNUNET_OP_create ();
1896
1897 gst->enter_cb = local_enter_cb;
1898 gst->entry_dcsn_cb = entry_decision_cb;
1899 gst->cb_cls = cls;
1900
1901 guest_connect (gst);
1902 return gst;
1903}
1904
1905
1906struct ReconnectContext
1907{
1908 struct GNUNET_SOCIAL_Guest *guest;
1909 int *result;
1910 int64_t *max_message_id;
1911 GNUNET_SOCIAL_GuestEnterCallback enter_cb;
1912 void *enter_cls;
1913};
1914
1915
1916static void
1917guest_enter_reconnect_cb (void *cls,
1918 int result,
1919 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1920 uint64_t max_message_id)
1921{
1922 struct ReconnectContext *reconnect_ctx = cls;
1923
1924 GNUNET_assert (NULL != reconnect_ctx);
1925 reconnect_ctx->result = GNUNET_new (int);
1926 *(reconnect_ctx->result) = result;
1927 reconnect_ctx->max_message_id = GNUNET_new (int64_t);
1928 *(reconnect_ctx->max_message_id) = max_message_id;
1929}
1930
1931
1932static void
1933guest_entry_dcsn_reconnect_cb (void *cls,
1934 int is_admitted,
1935 const struct GNUNET_PSYC_Message *entry_resp)
1936{
1937 struct ReconnectContext *reconnect_ctx = cls;
1938 struct GNUNET_SOCIAL_Guest *gst = reconnect_ctx->guest;
1939
1940 GNUNET_assert (NULL != reconnect_ctx);
1941 GNUNET_assert (NULL != reconnect_ctx->result);
1942 GNUNET_assert (NULL != reconnect_ctx->max_message_id);
1943 if (GNUNET_YES != is_admitted)
1944 {
1945 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1946 "Guest was rejected after calling "
1947 "GNUNET_SOCIAL_guest_enter_reconnect ()\n");
1948 }
1949 else if (NULL != reconnect_ctx->enter_cb)
1950 {
1951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1952 "guest reconnected!\n");
1953 reconnect_ctx->enter_cb (reconnect_ctx->enter_cls,
1954 *(reconnect_ctx->result),
1955 &gst->plc.pub_key,
1956 *(reconnect_ctx->max_message_id));
1957 }
1958 GNUNET_free (reconnect_ctx->result);
1959 GNUNET_free (reconnect_ctx->max_message_id);
1960 GNUNET_free (reconnect_ctx);
1961}
1962
1963
1964/**
1965 * Reconnect to an already entered place as guest.
1966 *
1967 * @param gconn
1968 * Guest connection handle.
1969 * @see GNUNET_SOCIAL_app_connect() & GNUNET_SOCIAL_AppGuestPlaceCallback()
1970 * @param flags
1971 * Flags for the entry.
1972 * @param slicer
1973 * Slicer to use for processing incoming requests from guests.
1974 * @param enter_cb
1975 * Called upon re-entering is complete.
1976 * @param entry_decision_cb
1977 * Called upon receiving entry decision.
1978 *
1979 * @return NULL on errors, otherwise handle for the guest.
1980 */
1981struct GNUNET_SOCIAL_Guest *
1982GNUNET_SOCIAL_guest_enter_reconnect (struct GNUNET_SOCIAL_GuestConnection *gconn,
1983 enum GNUNET_PSYC_SlaveJoinFlags flags,
1984 struct GNUNET_PSYC_Slicer *slicer,
1985 GNUNET_SOCIAL_GuestEnterCallback enter_cb,
1986 void *cls)
1987{
1988 struct GNUNET_SOCIAL_Guest *gst = GNUNET_malloc (sizeof (*gst));
1989 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
1990 struct ReconnectContext *reconnect_ctx;
1991
1992 uint16_t app_id_size = strlen (gconn->app->id) + 1;
1993 struct GuestEnterRequest *greq;
1994 plc->connect_env
1995 = GNUNET_MQ_msg_extra (greq, app_id_size,
1996 GNUNET_MESSAGE_TYPE_SOCIAL_GUEST_ENTER);
1997 greq->ego_pub_key = gconn->plc_msg.ego_pub_key;
1998 greq->place_pub_key = gconn->plc_msg.place_pub_key;
1999 greq->flags = htonl (flags);
2000
2001 GNUNET_memcpy (&greq[1], gconn->app->id, app_id_size);
2002
2003 plc->cfg = gconn->app->cfg;
2004 plc->is_host = GNUNET_NO;
2005 plc->slicer = slicer;
2006 plc->pub_key = gconn->plc_msg.place_pub_key;
2007 plc->ego_pub_key = gconn->plc_msg.ego_pub_key;
2008
2009 reconnect_ctx = GNUNET_new (struct ReconnectContext);
2010 reconnect_ctx->guest = gst;
2011 reconnect_ctx->enter_cb = enter_cb;
2012 reconnect_ctx->enter_cls = cls;
2013
2014 plc->op = GNUNET_OP_create ();
2015 gst->enter_cb = &guest_enter_reconnect_cb;
2016 gst->entry_dcsn_cb = &guest_entry_dcsn_reconnect_cb;
2017 gst->cb_cls = reconnect_ctx;
2018
2019 guest_connect (gst);
2020 return gst;
2021}
2022
2023
2024/**
2025 * Talk to the host of the place.
2026 *
2027 * @param place
2028 * Place where we want to talk to the host.
2029 * @param method_name
2030 * Method to invoke on the host.
2031 * @param env
2032 * Environment containing variables for the message, or NULL.
2033 * @param notify_data
2034 * Function to use to get the payload for the method.
2035 * @param notify_data_cls
2036 * Closure for @a notify_data.
2037 * @param flags
2038 * Flags for the message being sent.
2039 *
2040 * @return NULL if we are already trying to talk to the host,
2041 * otherwise handle to cancel the request.
2042 */
2043struct GNUNET_SOCIAL_TalkRequest *
2044GNUNET_SOCIAL_guest_talk (struct GNUNET_SOCIAL_Guest *gst,
2045 const char *method_name,
2046 const struct GNUNET_PSYC_Environment *env,
2047 GNUNET_PSYC_TransmitNotifyData notify_data,
2048 void *notify_data_cls,
2049 enum GNUNET_SOCIAL_TalkFlags flags)
2050{
2051 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2052 GNUNET_assert (NULL != plc->tmit);
2053
2054 if (GNUNET_OK ==
2055 GNUNET_PSYC_transmit_message (plc->tmit, method_name, env,
2056 NULL, notify_data, notify_data_cls, flags))
2057 return (struct GNUNET_SOCIAL_TalkRequest *) plc->tmit;
2058 else
2059 return NULL;
2060}
2061
2062
2063/**
2064 * Resume talking to the host of the place.
2065 *
2066 * @param tr
2067 * Talk request to resume.
2068 */
2069void
2070GNUNET_SOCIAL_guest_talk_resume (struct GNUNET_SOCIAL_TalkRequest *tr)
2071{
2072 GNUNET_PSYC_transmit_resume ((struct GNUNET_PSYC_TransmitHandle *) tr);
2073}
2074
2075
2076/**
2077 * Cancel talking to the host of the place.
2078 *
2079 * @param tr
2080 * Talk request to cancel.
2081 */
2082void
2083GNUNET_SOCIAL_guest_talk_cancel (struct GNUNET_SOCIAL_TalkRequest *tr)
2084{
2085 GNUNET_PSYC_transmit_cancel ( (struct GNUNET_PSYC_TransmitHandle *) tr);
2086}
2087
2088
2089/**
2090 * Disconnect from a place.
2091 *
2092 * Invalidates guest handle.
2093 *
2094 * @param gst
2095 * The guest to disconnect.
2096 */
2097void
2098GNUNET_SOCIAL_guest_disconnect (struct GNUNET_SOCIAL_Guest *gst,
2099 GNUNET_ContinuationCallback disconnect_cb,
2100 void *cls)
2101{
2102 struct GNUNET_SOCIAL_Place *plc = &gst->plc;
2103
2104 plc->disconnect_cb = disconnect_cb;
2105 plc->disconnect_cls = cls;
2106 place_disconnect (plc);
2107}
2108
2109
2110/**
2111 * Leave a place temporarily or permanently.
2112 *
2113 * Notifies the owner of the place about leaving, and destroys the place handle.
2114 *
2115 * @param place
2116 * Place to leave.
2117 * @param keep_active
2118 * Keep place active after last application disconnected.
2119 * #GNUNET_YES or #GNUNET_NO
2120 * @param env
2121 * Optional environment for the leave message if @a keep_active
2122 * is #GNUNET_NO. NULL if not needed.
2123 * @param leave_cb
2124 * Called upon disconnecting from the social service.
2125 */
2126void
2127GNUNET_SOCIAL_guest_leave (struct GNUNET_SOCIAL_Guest *gst,
2128 struct GNUNET_PSYC_Environment *env,
2129 GNUNET_ContinuationCallback disconnect_cb,
2130 void *cls)
2131{
2132 struct GNUNET_MQ_Envelope *envelope;
2133
2134 GNUNET_SOCIAL_guest_talk (gst, "_notice_place_leave", env, NULL, NULL,
2135 GNUNET_SOCIAL_TALK_NONE);
2136 gst->plc.disconnect_cb = disconnect_cb;
2137 gst->plc.disconnect_cls = cls;
2138 envelope = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SOCIAL_PLACE_LEAVE);
2139 GNUNET_MQ_send (gst->plc.mq,
2140 envelope);
2141}
2142
2143
2144/**
2145 * Obtain handle for a place entered as guest.
2146 *
2147 * The returned handle can be used to access the place API.
2148 *
2149 * @param guest Handle for the guest.
2150 *
2151 * @return Handle for the place, valid as long as @a guest is valid.
2152 */
2153struct GNUNET_SOCIAL_Place *
2154GNUNET_SOCIAL_guest_get_place (struct GNUNET_SOCIAL_Guest *gst)
2155{
2156 return &gst->plc;
2157}
2158
2159
2160/**
2161 * Obtain the public key of a place.
2162 *
2163 * @param plc
2164 * Place.
2165 *
2166 * @return Public key of the place.
2167 */
2168const struct GNUNET_CRYPTO_EddsaPublicKey *
2169GNUNET_SOCIAL_place_get_pub_key (const struct GNUNET_SOCIAL_Place *plc)
2170{
2171 return &plc->pub_key;
2172}
2173
2174
2175/**
2176 * Set message processing @a flags for a @a method_prefix.
2177 *
2178 * @param plc
2179 * Place.
2180 * @param method_prefix
2181 * Method prefix @a flags apply to.
2182 * @param flags
2183 * The flags that apply to a matching @a method_prefix.
2184 */
2185void
2186GNUNET_SOCIAL_place_msg_proc_set (struct GNUNET_SOCIAL_Place *plc,
2187 const char *method_prefix,
2188 enum GNUNET_SOCIAL_MsgProcFlags flags)
2189{
2190 GNUNET_assert (NULL != method_prefix);
2191 struct MsgProcRequest *mpreq;
2192 uint16_t method_size = strnlen (method_prefix,
2193 GNUNET_MAX_MESSAGE_SIZE
2194 - sizeof (*mpreq)) + 1;
2195 GNUNET_assert ('\0' == method_prefix[method_size - 1]);
2196
2197 struct GNUNET_MQ_Envelope *
2198 env = GNUNET_MQ_msg_extra (mpreq, method_size,
2199 GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_SET);
2200 mpreq->flags = htonl (flags);
2201 GNUNET_memcpy (&mpreq[1], method_prefix, method_size);
2202
2203 GNUNET_MQ_send (plc->mq, env);
2204}
2205
2206
2207/**
2208 * Clear all message processing flags previously set for this place.
2209 */
2210void
2211GNUNET_SOCIAL_place_msg_proc_clear (struct GNUNET_SOCIAL_Place *plc)
2212{
2213 struct GNUNET_MessageHeader *req;
2214 struct GNUNET_MQ_Envelope *
2215 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_SOCIAL_MSG_PROC_CLEAR);
2216
2217 GNUNET_MQ_send (plc->mq, env);
2218}
2219
2220
2221static struct GNUNET_SOCIAL_HistoryRequest *
2222place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2223 uint64_t start_message_id,
2224 uint64_t end_message_id,
2225 uint64_t message_limit,
2226 const char *method_prefix,
2227 uint32_t flags,
2228 struct GNUNET_PSYC_Slicer *slicer,
2229 GNUNET_ResultCallback result_cb,
2230 void *cls)
2231{
2232 struct GNUNET_PSYC_HistoryRequestMessage *req;
2233 struct GNUNET_SOCIAL_HistoryRequest *hist = GNUNET_malloc (sizeof (*hist));
2234 hist->plc = plc;
2235 hist->slicer = slicer;
2236 hist->result_cb = result_cb;
2237 hist->cls = cls;
2238 hist->op_id = GNUNET_OP_add (plc->op, op_recv_history_result, hist, NULL);
2239
2240 GNUNET_assert (NULL != method_prefix);
2241 uint16_t method_size = strnlen (method_prefix,
2242 GNUNET_MAX_MESSAGE_SIZE
2243 - sizeof (*req)) + 1;
2244 GNUNET_assert ('\0' == method_prefix[method_size - 1]);
2245
2246 struct GNUNET_MQ_Envelope *
2247 env = GNUNET_MQ_msg_extra (req, method_size,
2248 GNUNET_MESSAGE_TYPE_PSYC_HISTORY_REPLAY);
2249 req->start_message_id = GNUNET_htonll (start_message_id);
2250 req->end_message_id = GNUNET_htonll (end_message_id);
2251 req->message_limit = GNUNET_htonll (message_limit);
2252 req->flags = htonl (flags);
2253 req->op_id = GNUNET_htonll (hist->op_id);
2254 GNUNET_memcpy (&req[1], method_prefix, method_size);
2255
2256 GNUNET_MQ_send (plc->mq, env);
2257 return hist;
2258}
2259
2260
2261/**
2262 * Learn about the history of a place.
2263 *
2264 * Messages are returned through the @a slicer function
2265 * and have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2266 *
2267 * @param place
2268 * Place we want to learn more about.
2269 * @param start_message_id
2270 * First historic message we are interested in.
2271 * @param end_message_id
2272 * Last historic message we are interested in (inclusive).
2273 * @param method_prefix
2274 * Only retrieve messages with this method prefix.
2275 * @param flags
2276 * OR'ed GNUNET_PSYC_HistoryReplayFlags
2277 * @param slicer
2278 * Slicer to use for retrieved messages.
2279 * Can be the same as the slicer of the place.
2280 * @param result_cb
2281 * Function called after all messages retrieved.
2282 * NULL if not needed.
2283 * @param cls Closure for @a result_cb.
2284 */
2285struct GNUNET_SOCIAL_HistoryRequest *
2286GNUNET_SOCIAL_place_history_replay (struct GNUNET_SOCIAL_Place *plc,
2287 uint64_t start_message_id,
2288 uint64_t end_message_id,
2289 const char *method_prefix,
2290 uint32_t flags,
2291 struct GNUNET_PSYC_Slicer *slicer,
2292 GNUNET_ResultCallback result_cb,
2293 void *cls)
2294{
2295 return place_history_replay (plc, start_message_id, end_message_id, 0,
2296 method_prefix, flags, slicer, result_cb, cls);
2297}
2298
2299
2300/**
2301 * Learn about the history of a place.
2302 *
2303 * Sends messages through the slicer function of the place where
2304 * start_message_id <= message_id <= end_message_id.
2305 * The messages will have the #GNUNET_PSYC_MESSAGE_HISTORIC flag set.
2306 *
2307 * To get the latest message, use 0 for both the start and end message ID.
2308 *
2309 * @param place
2310 * Place we want to learn more about.
2311 * @param message_limit
2312 * Maximum number of historic messages we are interested in.
2313 * @param method_prefix
2314 * Only retrieve messages with this method prefix.
2315 * @param flags
2316 * OR'ed GNUNET_PSYC_HistoryReplayFlags
2317 * @param result_cb
2318 * Function called after all messages retrieved.
2319 * NULL if not needed.
2320 * @param cls Closure for @a result_cb.
2321 */
2322struct GNUNET_SOCIAL_HistoryRequest *
2323GNUNET_SOCIAL_place_history_replay_latest (struct GNUNET_SOCIAL_Place *plc,
2324 uint64_t message_limit,
2325 const char *method_prefix,
2326 uint32_t flags,
2327 struct GNUNET_PSYC_Slicer *slicer,
2328 GNUNET_ResultCallback result_cb,
2329 void *cls)
2330{
2331 return place_history_replay (plc, 0, 0, message_limit, method_prefix, flags,
2332 slicer, result_cb, cls);
2333}
2334
2335
2336/**
2337 * Cancel learning about the history of a place.
2338 *
2339 * @param hist
2340 * History lesson to cancel.
2341 */
2342void
2343GNUNET_SOCIAL_place_history_replay_cancel (struct GNUNET_SOCIAL_HistoryRequest *hist)
2344{
2345 GNUNET_OP_remove (hist->plc->op, hist->op_id);
2346 GNUNET_free (hist);
2347}
2348
2349
2350/**
2351 * Request matching state variables.
2352 */
2353static struct GNUNET_SOCIAL_LookHandle *
2354place_state_get (struct GNUNET_SOCIAL_Place *plc,
2355 uint16_t type, const char *name,
2356 GNUNET_PSYC_StateVarCallback var_cb,
2357 GNUNET_ResultCallback result_cb, void *cls)
2358{
2359 struct GNUNET_PSYC_StateRequestMessage *req;
2360 struct GNUNET_SOCIAL_LookHandle *look = GNUNET_malloc (sizeof (*look));
2361 look->plc = plc;
2362 look->var_cb = var_cb;
2363 look->result_cb = result_cb;
2364 look->cls = cls;
2365 look->op_id = GNUNET_OP_add (plc->op, &op_recv_state_result, look, NULL);
2366
2367 GNUNET_assert (NULL != name);
2368 size_t name_size = strnlen (name, GNUNET_MAX_MESSAGE_SIZE
2369 - sizeof (*req)) + 1;
2370 struct GNUNET_MQ_Envelope *
2371 env = GNUNET_MQ_msg_extra (req, name_size, type);
2372 req->op_id = GNUNET_htonll (look->op_id);
2373 GNUNET_memcpy (&req[1], name, name_size);
2374
2375 GNUNET_MQ_send (plc->mq, env);
2376 return look;
2377}
2378
2379
2380/**
2381 * Look at a particular object in the place.
2382 *
2383 * The best matching object is returned (its name might be less specific than
2384 * what was requested).
2385 *
2386 * @param place
2387 * The place where to look.
2388 * @param full_name
2389 * Full name of the object.
2390 * @param value_size
2391 * Set to the size of the returned value.
2392 *
2393 * @return NULL if there is no such object at this place.
2394 */
2395struct GNUNET_SOCIAL_LookHandle *
2396GNUNET_SOCIAL_place_look_at (struct GNUNET_SOCIAL_Place *plc,
2397 const char *full_name,
2398 GNUNET_PSYC_StateVarCallback var_cb,
2399 GNUNET_ResultCallback result_cb,
2400 void *cls)
2401{
2402 return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET,
2403 full_name, var_cb, result_cb, cls);
2404}
2405
2406
2407/**
2408 * Look for objects in the place with a matching name prefix.
2409 *
2410 * @param place
2411 * The place where to look.
2412 * @param name_prefix
2413 * Look at objects with names beginning with this value.
2414 * @param var_cb
2415 * Function to call for each object found.
2416 * @param cls
2417 * Closure for callback function.
2418 *
2419 * @return Handle that can be used to stop looking at objects.
2420 */
2421struct GNUNET_SOCIAL_LookHandle *
2422GNUNET_SOCIAL_place_look_for (struct GNUNET_SOCIAL_Place *plc,
2423 const char *name_prefix,
2424 GNUNET_PSYC_StateVarCallback var_cb,
2425 GNUNET_ResultCallback result_cb,
2426 void *cls)
2427{
2428 return place_state_get (plc, GNUNET_MESSAGE_TYPE_PSYC_STATE_GET_PREFIX,
2429 name_prefix, var_cb, result_cb, cls);
2430}
2431
2432
2433/**
2434 * Cancel a state request operation.
2435 *
2436 * @param sr
2437 * Handle for the operation to cancel.
2438 */
2439void
2440GNUNET_SOCIAL_place_look_cancel (struct GNUNET_SOCIAL_LookHandle *look)
2441{
2442 GNUNET_OP_remove (look->plc->op, look->op_id);
2443 GNUNET_free (look);
2444}
2445
2446
2447static void
2448op_recv_zone_add_place_result (void *cls, int64_t result,
2449 const void *err_msg, uint16_t err_msg_size)
2450{
2451 LOG (GNUNET_ERROR_TYPE_DEBUG,
2452 "Received zone add place result: %" PRId64 ".\n", result);
2453
2454 struct ZoneAddPlaceHandle *add_plc = cls;
2455 if (NULL != add_plc->result_cb)
2456 add_plc->result_cb (add_plc->result_cls, result, err_msg, err_msg_size);
2457
2458 GNUNET_free (add_plc);
2459}
2460
2461
2462/**
2463 * Advertise @e place in the GNS zone of @e ego.
2464 *
2465 * @param app
2466 * Application handle.
2467 * @param ego
2468 * Ego.
2469 * @param place_pub_key
2470 * Public key of place to add.
2471 * @param name
2472 * The name for the PLACE record to put in the zone.
2473 * @param password
2474 * Password used to encrypt the record or NULL to keep it cleartext.
2475 * @param relay_count
2476 * Number of elements in the @a relays array.
2477 * @param relays
2478 * List of relays to put in the PLACE record to advertise
2479 * as entry points to the place in addition to the origin.
2480 * @param expiration_time
2481 * Expiration time of the record, use 0 to remove the record.
2482 * @param result_cb
2483 * Function called with the result of the operation.
2484 * @param result_cls
2485 * Closure for @a result_cb
2486 *
2487 * @return #GNUNET_OK if the request was sent,
2488 * #GNUNET_SYSERR on error, e.g. the name/password is too long.
2489 */
2490int
2491GNUNET_SOCIAL_zone_add_place (const struct GNUNET_SOCIAL_App *app,
2492 const struct GNUNET_SOCIAL_Ego *ego,
2493 const char *name,
2494 const char *password,
2495 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
2496 const struct GNUNET_PeerIdentity *origin,
2497 uint32_t relay_count,
2498 const struct GNUNET_PeerIdentity *relays,
2499 struct GNUNET_TIME_Absolute expiration_time,
2500 GNUNET_ResultCallback result_cb,
2501 void *result_cls)
2502{
2503 struct ZoneAddPlaceRequest *preq;
2504 size_t name_size = strlen (name) + 1;
2505 size_t password_size = strlen (password) + 1;
2506 size_t relay_size = relay_count * sizeof (*relays);
2507 size_t payload_size = name_size + password_size + relay_size;
2508
2509 if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*preq) + payload_size)
2510 return GNUNET_SYSERR;
2511
2512 struct GNUNET_MQ_Envelope *
2513 env = GNUNET_MQ_msg_extra (preq, payload_size,
2514 GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_PLACE);
2515 preq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
2516 preq->ego_pub_key = ego->pub_key;
2517 preq->place_pub_key = *place_pub_key;
2518 preq->origin = *origin;
2519 preq->relay_count = htonl (relay_count);
2520
2521 char *p = (char *) &preq[1];
2522 GNUNET_memcpy (p, name, name_size);
2523 p += name_size;
2524 GNUNET_memcpy (p, password, password_size);
2525 p += password_size;
2526 GNUNET_memcpy (p, relays, relay_size);
2527
2528 struct ZoneAddPlaceHandle * add_plc = GNUNET_malloc (sizeof (*add_plc));
2529 add_plc->result_cb = result_cb;
2530 add_plc->result_cls = result_cls;
2531
2532 preq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
2533 op_recv_zone_add_place_result,
2534 add_plc, NULL));
2535
2536 GNUNET_MQ_send (app->mq, env);
2537 return GNUNET_OK;
2538}
2539
2540
2541static void
2542op_recv_zone_add_nym_result (void *cls, int64_t result,
2543 const void *err_msg, uint16_t err_msg_size)
2544{
2545 LOG (GNUNET_ERROR_TYPE_DEBUG,
2546 "Received zone add nym result: %" PRId64 ".\n", result);
2547
2548 struct ZoneAddNymHandle *add_nym = cls;
2549 if (NULL != add_nym->result_cb)
2550 add_nym->result_cb (add_nym->result_cls, result, err_msg, err_msg_size);
2551
2552 GNUNET_free (add_nym);
2553}
2554
2555
2556/**
2557 * Add nym to the GNS zone of @e ego.
2558 *
2559 * @param cfg
2560 * Configuration.
2561 * @param ego
2562 * Ego.
2563 * @param name
2564 * The name for the PKEY record to put in the zone.
2565 * @param nym_pub_key
2566 * Public key of nym to add.
2567 * @param expiration_time
2568 * Expiration time of the record, use 0 to remove the record.
2569 * @param result_cb
2570 * Function called with the result of the operation.
2571 * @param result_cls
2572 * Closure for @a result_cb
2573 *
2574 * @return #GNUNET_OK if the request was sent,
2575 * #GNUNET_SYSERR on error, e.g. the name is too long.
2576 */
2577int
2578GNUNET_SOCIAL_zone_add_nym (const struct GNUNET_SOCIAL_App *app,
2579 const struct GNUNET_SOCIAL_Ego *ego,
2580 const char *name,
2581 const struct GNUNET_CRYPTO_EcdsaPublicKey *nym_pub_key,
2582 struct GNUNET_TIME_Absolute expiration_time,
2583 GNUNET_ResultCallback result_cb,
2584 void *result_cls)
2585{
2586 struct ZoneAddNymRequest *nreq;
2587
2588 size_t name_size = strlen (name) + 1;
2589 if (GNUNET_MAX_MESSAGE_SIZE < sizeof (*nreq) + name_size)
2590 return GNUNET_SYSERR;
2591
2592 struct GNUNET_MQ_Envelope *
2593 env = GNUNET_MQ_msg_extra (nreq, name_size,
2594 GNUNET_MESSAGE_TYPE_SOCIAL_ZONE_ADD_NYM);
2595 nreq->expiration_time = GNUNET_htonll (expiration_time.abs_value_us);
2596 nreq->ego_pub_key = ego->pub_key;
2597 nreq->nym_pub_key = *nym_pub_key;
2598 GNUNET_memcpy (&nreq[1], name, name_size);
2599
2600 struct ZoneAddNymHandle *add_nym = GNUNET_malloc (sizeof (*add_nym));
2601 add_nym->result_cb = result_cb;
2602 add_nym->result_cls = result_cls;
2603
2604 nreq->op_id = GNUNET_htonll (GNUNET_OP_add (app->op,
2605 op_recv_zone_add_nym_result,
2606 add_nym, NULL));
2607
2608 GNUNET_MQ_send (app->mq, env);
2609 return GNUNET_OK;
2610}
2611
2612
2613/*** APP ***/
2614
2615
2616static void
2617app_connect (struct GNUNET_SOCIAL_App *app);
2618
2619
2620static void
2621app_reconnect (void *cls)
2622{
2623 app_connect (cls);
2624}
2625
2626
2627/**
2628 * App client disconnected from service.
2629 *
2630 * Reconnect after backoff period.
2631 */
2632static void
2633app_disconnected (void *cls, enum GNUNET_MQ_Error error)
2634{
2635 struct GNUNET_SOCIAL_App *app = cls;
2636
2637 LOG (GNUNET_ERROR_TYPE_DEBUG,
2638 "App client disconnected (%d), re-connecting\n",
2639 (int) error);
2640 if (NULL != app->mq)
2641 {
2642 GNUNET_MQ_destroy (app->mq);
2643 app->mq = NULL;
2644 }
2645
2646 app->reconnect_task = GNUNET_SCHEDULER_add_delayed (app->reconnect_delay,
2647 app_reconnect,
2648 app);
2649 app->reconnect_delay = GNUNET_TIME_STD_BACKOFF (app->reconnect_delay);
2650}
2651
2652
2653static void
2654app_connect (struct GNUNET_SOCIAL_App *app)
2655{
2656 struct GNUNET_MQ_MessageHandler handlers[] = {
2657 GNUNET_MQ_hd_var_size (app_ego,
2658 GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO,
2659 struct AppEgoMessage,
2660 app),
2661 GNUNET_MQ_hd_fixed_size (app_ego_end,
2662 GNUNET_MESSAGE_TYPE_SOCIAL_APP_EGO_END,
2663 struct GNUNET_MessageHeader,
2664 app),
2665 GNUNET_MQ_hd_var_size (app_place,
2666 GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE,
2667 struct AppPlaceMessage,
2668 app),
2669 GNUNET_MQ_hd_fixed_size (app_place_end,
2670 GNUNET_MESSAGE_TYPE_SOCIAL_APP_PLACE_END,
2671 struct GNUNET_MessageHeader,
2672 app),
2673 GNUNET_MQ_hd_var_size (app_result,
2674 GNUNET_MESSAGE_TYPE_PSYC_RESULT_CODE,
2675 struct GNUNET_OperationResultMessage,
2676 app),
2677 GNUNET_MQ_handler_end ()
2678 };
2679
2680 app->mq = GNUNET_CLIENT_connect (app->cfg, "social",
2681 handlers, app_disconnected, app);
2682 GNUNET_assert (NULL != app->mq);
2683 GNUNET_MQ_send_copy (app->mq, app->connect_env);
2684}
2685
2686
2687/**
2688 * Connect application to the social service.
2689 *
2690 * The @host_place_cb and @guest_place_cb functions are
2691 * initially called for each entered places,
2692 * then later each time a new place is entered with the current application ID.
2693 *
2694 * @param cfg
2695 * Configuration.
2696 * @param id
2697 * Application ID.
2698 * @param ego_cb
2699 * Function to notify about an available ego.
2700 * @param host_cb
2701 * Function to notify about a place entered as host.
2702 * @param guest_cb
2703 * Function to notify about a place entered as guest.
2704 * @param cls
2705 * Closure for the callbacks.
2706 *
2707 * @return Handle that can be used to stop listening.
2708 */
2709struct GNUNET_SOCIAL_App *
2710GNUNET_SOCIAL_app_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
2711 const char *id,
2712 GNUNET_SOCIAL_AppEgoCallback ego_cb,
2713 GNUNET_SOCIAL_AppHostPlaceCallback host_cb,
2714 GNUNET_SOCIAL_AppGuestPlaceCallback guest_cb,
2715 GNUNET_SOCIAL_AppConnectedCallback connected_cb,
2716 void *cls)
2717{
2718 uint16_t app_id_size = strnlen (id, GNUNET_SOCIAL_APP_MAX_ID_SIZE);
2719 if (GNUNET_SOCIAL_APP_MAX_ID_SIZE == app_id_size)
2720 return NULL;
2721 app_id_size++;
2722
2723 struct GNUNET_SOCIAL_App *app = GNUNET_malloc (sizeof *app);
2724 app->cfg = cfg;
2725 app->ego_cb = ego_cb;
2726 app->host_cb = host_cb;
2727 app->guest_cb = guest_cb;
2728 app->connected_cb = connected_cb;
2729 app->cb_cls = cls;
2730 app->egos = GNUNET_CONTAINER_multihashmap_create (1, GNUNET_NO);
2731 app->op = GNUNET_OP_create ();
2732 app->id = GNUNET_malloc (app_id_size);
2733 GNUNET_memcpy (app->id, id, app_id_size);
2734
2735 struct AppConnectRequest *creq;
2736 app->connect_env = GNUNET_MQ_msg_extra (creq, app_id_size,
2737 GNUNET_MESSAGE_TYPE_SOCIAL_APP_CONNECT);
2738 GNUNET_memcpy (&creq[1], app->id, app_id_size);
2739
2740 app_connect (app);
2741 return app;
2742}
2743
2744
2745static void
2746app_cleanup (struct GNUNET_SOCIAL_App *app)
2747{
2748 if (NULL != app->mq)
2749 {
2750 GNUNET_MQ_destroy (app->mq);
2751 app->mq = NULL;
2752 }
2753 if (NULL != app->disconnect_cb)
2754 {
2755 app->disconnect_cb (app->disconnect_cls);
2756 app->disconnect_cb = NULL;
2757 }
2758 GNUNET_free (app);
2759}
2760
2761/**
2762 * Disconnect application.
2763 *
2764 * @param app
2765 * Application handle.
2766 * @param disconnect_cb
2767 * Disconnect callback.
2768 * @param disconnect_cls
2769 * Disconnect closure.
2770 */
2771void
2772GNUNET_SOCIAL_app_disconnect (struct GNUNET_SOCIAL_App *app,
2773 GNUNET_ContinuationCallback disconnect_cb,
2774 void *disconnect_cls)
2775{
2776 if (NULL == app) return;
2777
2778 app->disconnect_cb = disconnect_cb;
2779 app->disconnect_cls = disconnect_cls;
2780
2781 if (NULL != app->mq)
2782 {
2783 struct GNUNET_MQ_Envelope *env = GNUNET_MQ_get_last_envelope (app->mq);
2784 if (NULL != env)
2785 {
2786 GNUNET_MQ_notify_sent (env, (GNUNET_SCHEDULER_TaskCallback) app_cleanup, app);
2787 }
2788 else
2789 {
2790 app_cleanup (app);
2791 }
2792 }
2793 else
2794 {
2795 app_cleanup (app);
2796 }
2797}
2798
2799
2800/**
2801 * Detach application from a place.
2802 *
2803 * Removes the place from the entered places list for this application.
2804 * Note: this does not disconnect from the place.
2805 *
2806 * @see GNUNET_SOCIAL_host_disconnect() and GNUNET_SOCIAL_guest_disconnect()
2807 *
2808 * @param app
2809 * Application.
2810 * @param plc
2811 * Place.
2812 */
2813void
2814GNUNET_SOCIAL_app_detach (struct GNUNET_SOCIAL_App *app,
2815 struct GNUNET_SOCIAL_Place *plc)
2816{
2817 struct AppDetachRequest *dreq;
2818 struct GNUNET_MQ_Envelope *
2819 env = GNUNET_MQ_msg (dreq, GNUNET_MESSAGE_TYPE_SOCIAL_APP_DETACH);
2820 dreq->place_pub_key = plc->pub_key;
2821 dreq->ego_pub_key = plc->ego_pub_key;
2822
2823 GNUNET_MQ_send (app->mq, env);
2824}
2825
2826
2827/* end of social_api.c */
diff --git a/src/social/test_social.c b/src/social/test_social.c
new file mode 100644
index 0000000..feac3c5
--- /dev/null
+++ b/src/social/test_social.c
@@ -0,0 +1,1449 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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 * @file social/test_social.c
22 * @brief Tests for the Social API.
23 * @author Gabor X Toth
24 */
25
26#include <inttypes.h>
27
28#include "platform.h"
29#include "gnunet_crypto_lib.h"
30#include "gnunet_common.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_testing_lib.h"
33#include "gnunet_psyc_util_lib.h"
34#include "gnunet_social_service.h"
35#include "gnunet_identity_service.h"
36
37#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
38
39#define DATA2ARG(data) data, sizeof (data)
40
41/**
42 * Return value from 'main'.
43 */
44int res;
45
46struct GNUNET_SOCIAL_App *app;
47const char *app_id = "test";
48
49/**
50 * Handle for task for timeout termination.
51 */
52struct GNUNET_SCHEDULER_Task *end_badly_task;
53
54const struct GNUNET_CONFIGURATION_Handle *cfg;
55
56struct GNUNET_PeerIdentity this_peer;
57
58struct GNUNET_IDENTITY_Handle *id;
59
60const struct GNUNET_IDENTITY_Ego *identity_host_ego;
61const struct GNUNET_IDENTITY_Ego *identity_guest_ego;
62
63const struct GNUNET_SOCIAL_Ego *host_ego;
64const struct GNUNET_SOCIAL_Ego *guest_ego;
65
66const char *host_name = "Host One";
67const char *guest_name = "Guest One";
68
69struct GNUNET_CRYPTO_EddsaPrivateKey *place_key;
70struct GNUNET_CRYPTO_EcdsaPrivateKey *guest_key;
71
72struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
73struct GNUNET_HashCode place_pub_hash;
74
75const struct GNUNET_CRYPTO_EcdsaPublicKey *guest_pub_key;
76const struct GNUNET_CRYPTO_EcdsaPublicKey *host_pub_key;
77
78struct GNUNET_PSYC_Slicer *host_slicer;
79struct GNUNET_PSYC_Slicer *guest_slicer;
80
81struct GNUNET_SOCIAL_Host *hst;
82struct GNUNET_SOCIAL_Guest *gst;
83
84struct GNUNET_SOCIAL_Place *hst_plc;
85struct GNUNET_SOCIAL_Place *gst_plc;
86
87struct GNUNET_SOCIAL_Nym *nym_eject;
88
89struct GuestEnterMessage
90{
91 struct GNUNET_PSYC_Message *msg;
92 const char *method_name;
93 struct GNUNET_PSYC_Environment *env;
94 void *data;
95 uint16_t data_size;
96} guest_enter_msg;
97
98struct TransmitClosure
99{
100 struct GNUNET_SOCIAL_Announcement *host_ann;
101 struct GNUNET_SOCIAL_TalkRequest *guest_talk;
102 struct GNUNET_PSYC_Environment *env;
103 char *data[16];
104 uint8_t data_delay[16];
105 uint8_t data_count;
106 uint8_t paused;
107 uint8_t n;
108} tmit;
109
110struct ResultClosure {
111 uint32_t n;
112} mod_foo_bar_rcls;
113
114uint8_t join_req_count;
115struct GNUNET_PSYC_Message *join_resp;
116
117uint32_t counter;
118
119uint8_t is_guest_nym_added = GNUNET_NO;
120uint8_t is_host_reconnected = GNUNET_NO;
121uint8_t is_guest_reconnected = GNUNET_NO;
122
123enum
124{
125 TEST_NONE = 0,
126 TEST_IDENTITIES_CREATE = 1,
127 TEST_HOST_ENTER = 2,
128 TEST_GUEST_ENTER = 3,
129 TEST_HOST_ANSWER_DOOR_REFUSE = 4,
130 TEST_GUEST_RECV_ENTRY_DCSN_REFUSE = 5,
131 TEST_HOST_ANSWER_DOOR_ADMIT = 6,
132 TEST_GUEST_RECV_ENTRY_DCSN_ADMIT = 7,
133 TEST_HOST_ANNOUNCE = 8,
134 TEST_HOST_ANNOUNCE_END = 9,
135 TEST_GUEST_TALK = 10,
136 TEST_HOST_ANNOUNCE2 = 11,
137 TEST_HOST_ANNOUNCE2_END = 12,
138 TEST_GUEST_HISTORY_REPLAY = 13,
139 TEST_GUEST_HISTORY_REPLAY_LATEST = 14,
140 TEST_GUEST_LOOK_AT = 15,
141 TEST_GUEST_LOOK_FOR = 16,
142 TEST_GUEST_LEAVE = 17,
143 TEST_ZONE_ADD_PLACE = 18,
144 TEST_GUEST_ENTER_BY_NAME = 19,
145 TEST_RECONNECT = 20,
146 TEST_GUEST_LEAVE2 = 21,
147 TEST_HOST_LEAVE = 22,
148} test;
149
150
151static void
152schedule_guest_leave (void *cls);
153
154
155static void
156host_answer_door (void *cls,
157 struct GNUNET_SOCIAL_Nym *nym,
158 const char *method_name,
159 struct GNUNET_PSYC_Environment *env,
160 const void *data,
161 size_t data_size);
162
163static void
164host_enter ();
165
166static void
167guest_init ();
168
169static void
170guest_enter ();
171
172static void
173guest_enter_by_name ();
174
175static void
176guest_talk ();
177
178static void
179host_announce2 ();
180
181
182/**
183 * Terminate the test case (failure).
184 *
185 * @param cls NULL
186 */
187static void
188end_badly (void *cls)
189{
190 end_badly_task = NULL;
191 GNUNET_SCHEDULER_shutdown ();
192 res = 2;
193 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194 "Test FAILED.\n");
195}
196
197
198/**
199 * Terminate the test case (failure).
200 *
201 * @param cls NULL
202 */
203static void
204end_shutdown (void *cls)
205{
206 if (NULL != id)
207 {
208 GNUNET_IDENTITY_disconnect (id);
209 id = NULL;
210 }
211
212 if (NULL != guest_slicer)
213 {
214 GNUNET_PSYC_slicer_destroy (guest_slicer);
215 guest_slicer = NULL;
216 }
217
218 if (NULL != host_slicer)
219 {
220 GNUNET_PSYC_slicer_destroy (host_slicer);
221 host_slicer = NULL;
222 }
223 if (NULL != end_badly_task)
224 {
225 GNUNET_SCHEDULER_cancel (end_badly_task);
226 end_badly_task = NULL;
227 }
228 if (NULL != gst)
229 {
230 GNUNET_SOCIAL_guest_leave (gst, NULL, NULL, NULL);
231 gst = NULL;
232 gst_plc = NULL;
233 }
234 if (NULL != hst)
235 {
236 GNUNET_SOCIAL_host_leave (hst, NULL, NULL, NULL);
237 hst = NULL;
238 hst_plc = NULL;
239 }
240 GNUNET_SOCIAL_app_disconnect (app, NULL, NULL);
241}
242
243
244/**
245 * Terminate the test case (success).
246 *
247 * @param cls NULL
248 */
249static void
250end_normally (void *cls)
251{
252 GNUNET_SCHEDULER_shutdown ();
253 res = 0;
254 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Test PASSED.\n");
255}
256
257
258/**
259 * Finish the test case (successfully).
260 */
261static void
262end ()
263{
264 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
265 "Test #%u: Ending tests.\n", test);
266
267 if (end_badly_task != NULL)
268 {
269 GNUNET_SCHEDULER_cancel (end_badly_task);
270 end_badly_task = NULL;
271 }
272 GNUNET_SCHEDULER_add_now (&end_normally, NULL);
273}
274
275
276static void
277transmit_resume (void *cls)
278{
279 struct TransmitClosure *tmit = cls;
280
281 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
282 "Test #%u: Transmission resumed.\n", test);
283 if (NULL != tmit->host_ann)
284 GNUNET_SOCIAL_host_announce_resume (tmit->host_ann);
285 else
286 GNUNET_SOCIAL_guest_talk_resume (tmit->guest_talk);
287}
288
289
290static int
291notify_data (void *cls, uint16_t *data_size, void *data)
292{
293 struct TransmitClosure *tmit = cls;
294 if (NULL != tmit->env)
295 {
296 GNUNET_PSYC_env_destroy (tmit->env);
297 tmit->env = NULL;
298 }
299 if (0 == tmit->data_count)
300 {
301 *data_size = 0;
302 return GNUNET_YES;
303 }
304
305 uint16_t size = strlen (tmit->data[tmit->n]);
306 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
307 "Test #%u: Transmit notify data: %u bytes available, "
308 "processing fragment %u/%u (size %u).\n",
309 test, *data_size, tmit->n + 1, tmit->data_count, size);
310 if (*data_size < size)
311 {
312 *data_size = 0;
313 GNUNET_assert (0);
314 return GNUNET_SYSERR;
315 }
316
317 if (GNUNET_YES != tmit->paused && 0 < tmit->data_delay[tmit->n])
318 {
319 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
320 "Test #%u: Transmission paused.\n", test);
321 tmit->paused = GNUNET_YES;
322 GNUNET_SCHEDULER_add_delayed (
323 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
324 tmit->data_delay[tmit->n]),
325 &transmit_resume, tmit);
326 *data_size = 0;
327 return GNUNET_NO;
328 }
329 tmit->paused = GNUNET_NO;
330
331 *data_size = size;
332 GNUNET_memcpy (data, tmit->data[tmit->n], size);
333
334 return ++tmit->n < tmit->data_count ? GNUNET_NO : GNUNET_YES;
335}
336
337
338static void
339host_left ()
340{
341 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
342 "Test #%u: The host has left the place.\n", test);
343 end ();
344}
345
346
347static void
348schedule_host_leave (void *cls)
349{
350 test = TEST_HOST_LEAVE;
351 GNUNET_SOCIAL_host_leave (hst, NULL, &host_left, NULL);
352 hst = NULL;
353 hst_plc = NULL;
354}
355
356
357static void
358host_farewell2 (void *cls,
359 const struct GNUNET_SOCIAL_Nym *nym,
360 struct GNUNET_PSYC_Environment *env)
361{
362 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
363 "Nym left the place again.\n");
364 GNUNET_SCHEDULER_add_now (&schedule_host_leave, NULL);
365}
366
367
368static void
369host_reconnected (void *cls, int result,
370 const struct GNUNET_CRYPTO_EddsaPublicKey *home_pub_key,
371 uint64_t max_message_id)
372{
373 place_pub_key = *home_pub_key;
374 GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
375 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
376 "Test #%u: Host reconnected to place %s\n",
377 test, GNUNET_h2s (&place_pub_hash));
378
379 is_host_reconnected = GNUNET_YES;
380 if (GNUNET_YES == is_guest_reconnected)
381 {
382 GNUNET_assert (NULL != gst);
383 GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
384 }
385}
386
387
388static void
389guest_reconnected (void *cls, int result,
390 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
391 uint64_t max_message_id)
392{
393 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
394 "Test #%u: Guest reconnected to place: %d\n",
395 test, result);
396 GNUNET_assert (0 <= result);
397
398 is_guest_reconnected = GNUNET_YES;
399 if (GNUNET_YES == is_host_reconnected)
400 {
401 GNUNET_assert (NULL != gst);
402 GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
403 }
404}
405
406
407static void
408app_connected (void *cls)
409{
410 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
411 "Test #%u: App connected: %p\n", test, cls);
412}
413
414
415static void
416app_recv_host (void *cls,
417 struct GNUNET_SOCIAL_HostConnection *hconn,
418 struct GNUNET_SOCIAL_Ego *ego,
419 const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
420 enum GNUNET_SOCIAL_AppPlaceState place_state)
421{
422 struct GNUNET_HashCode host_pub_hash;
423
424 GNUNET_CRYPTO_hash (host_pub_key,
425 sizeof (*host_pub_key),
426 &host_pub_hash);
427
428 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
429 "Test #%u: Got app host place notification: %s\n",
430 test,
431 GNUNET_h2s (&host_pub_hash));
432
433 if (test == TEST_RECONNECT)
434 {
435 if (0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
436 {
437 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
438 "Test #%u: Reconnecting to host place: %s\n",
439 test, GNUNET_h2s (&host_pub_hash));
440 hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, host_slicer,
441 &host_reconnected,
442 &host_answer_door,
443 &host_farewell2,
444 NULL);
445 }
446 }
447}
448
449
450static void
451app_recv_guest (void *cls,
452 struct GNUNET_SOCIAL_GuestConnection *gconn,
453 struct GNUNET_SOCIAL_Ego *ego,
454 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
455 enum GNUNET_SOCIAL_AppPlaceState place_state)
456{
457 struct GNUNET_HashCode guest_pub_hash;
458
459 GNUNET_CRYPTO_hash (guest_pub_key,
460 sizeof (*guest_pub_key),
461 &guest_pub_hash);
462
463 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
464 "Test #%u: Got app guest place notification: %s\n",
465 test, GNUNET_h2s (&guest_pub_hash));
466
467 if (test == TEST_RECONNECT)
468 {
469 if (0 == memcmp (&place_pub_key,
470 guest_pub_key,
471 sizeof (*guest_pub_key)))
472 {
473 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
474 "Test #%u: Reconnecting to guest place: %s\n",
475 test, GNUNET_h2s (&guest_pub_hash));
476 gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn,
477 GNUNET_PSYC_SLAVE_JOIN_NONE,
478 guest_slicer,
479 &guest_reconnected,
480 NULL);
481 GNUNET_assert (NULL != gst);
482 }
483 }
484}
485
486
487static void
488enter_if_ready ()
489{
490 if (NULL == host_ego || NULL == guest_ego)
491 {
492 return;
493 }
494 host_enter ();
495 guest_init ();
496}
497
498
499static void
500app_recv_ego (void *cls,
501 struct GNUNET_SOCIAL_Ego *ego,
502 const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
503 const char *name)
504{
505 char *ego_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (ego_pub_key);
506 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
507 "Test #%u: Got app ego notification: %p %s %s\n",
508 test, ego, name, ego_pub_str);
509 GNUNET_free (ego_pub_str);
510
511 if (NULL != strstr (name, host_name))
512 {
513 host_ego = ego;
514 host_pub_key = ego_pub_key;
515 if (TEST_IDENTITIES_CREATE == test)
516 {
517 enter_if_ready ();
518 }
519 else
520 {
521 GNUNET_assert (TEST_RECONNECT == test);
522 }
523 }
524 else if (NULL != strstr (name, guest_name))
525 {
526 guest_ego = ego;
527 guest_pub_key = ego_pub_key;
528 if (TEST_IDENTITIES_CREATE == test)
529 {
530 enter_if_ready ();
531 }
532 else
533 {
534 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
535 "test = %d\n",
536 test);
537 GNUNET_assert (TEST_RECONNECT == test);
538 }
539 }
540}
541
542
543static void
544schedule_reconnect (void *cls)
545{
546 test = TEST_RECONNECT;
547 GNUNET_SOCIAL_host_disconnect (hst, NULL, NULL);
548 GNUNET_SOCIAL_guest_disconnect (gst, NULL, NULL);
549 hst = NULL;
550 gst = NULL;
551
552 GNUNET_SOCIAL_app_disconnect (app, NULL, NULL);
553 app = GNUNET_SOCIAL_app_connect (cfg, app_id,
554 &app_recv_ego,
555 &app_recv_host,
556 &app_recv_guest,
557 &app_connected,
558 NULL);
559}
560
561
562static void
563host_recv_zone_add_place_result (void *cls, int64_t result,
564 const void *data, uint16_t data_size)
565{
566 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
567 "Test #%u: Zone add place result: %" PRId64 " (%.*s).\n",
568 test, result, data_size, (const char *) data);
569 GNUNET_assert (GNUNET_YES == result);
570
571 GNUNET_assert (GNUNET_YES == is_guest_nym_added);
572 guest_enter_by_name ();
573}
574
575
576static void
577zone_add_place ()
578{
579 test = TEST_ZONE_ADD_PLACE;
580 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
581 "Test #%u: Adding place to zone.\n", test);
582
583 GNUNET_SOCIAL_zone_add_place (app, host_ego, "home", "let.me*in!",
584 &place_pub_key, &this_peer, 1, &this_peer,
585 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES),
586 host_recv_zone_add_place_result, app);
587}
588
589
590static void
591host_farewell (void *cls,
592 const struct GNUNET_SOCIAL_Nym *nym,
593 struct GNUNET_PSYC_Environment *env)
594{
595 const struct GNUNET_CRYPTO_EcdsaPublicKey *
596 nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
597
598 char *str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
599 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
600 "Test #%u: Farewell: nym %s (%s) has left the place.\n",
601 test, GNUNET_h2s (GNUNET_SOCIAL_nym_get_pub_key_hash (nym)), str);
602 GNUNET_free (str);
603 GNUNET_assert (1 == GNUNET_PSYC_env_get_count (env));
604 if (0 != memcmp (guest_pub_key, nym_key, sizeof (*nym_key)))
605 {
606 str = GNUNET_CRYPTO_ecdsa_public_key_to_string (guest_pub_key);
607 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
608 "Test #%u: Farewell: nym does not match guest: %s\n",
609 test, str);
610 GNUNET_free (str);
611 GNUNET_assert (0);
612 }
613 zone_add_place ();
614}
615
616
617static void
618guest_left (void *cls)
619{
620 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
621 "Test #%u: The guest has left the place.\n", test);
622}
623
624
625static void
626guest_leave ()
627{
628 if (test < TEST_RECONNECT)
629 test = TEST_GUEST_LEAVE;
630 else
631 test = TEST_GUEST_LEAVE2;
632
633 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
634 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
635 "_notice_place_leave", DATA2ARG ("Leaving."));
636 GNUNET_SOCIAL_guest_leave (gst, env, &guest_left, NULL);
637 GNUNET_PSYC_env_destroy (env);
638 gst = NULL;
639 gst_plc = NULL;
640}
641
642
643static void
644schedule_guest_leave (void *cls)
645{
646 guest_leave ();
647}
648
649
650static void
651guest_look_for_result (void *cls,
652 int64_t result_code,
653 const void *data,
654 uint16_t data_size)
655{
656 struct ResultClosure *rcls = cls;
657 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
658 "Test #%u: guest_look_for_result: %" PRId64 "\n",
659 test, result_code);
660 GNUNET_assert (GNUNET_OK == result_code);
661 GNUNET_assert (6 == rcls->n);
662 GNUNET_free (rcls);
663 GNUNET_SCHEDULER_add_now (&schedule_guest_leave, NULL);
664}
665
666
667static void
668guest_look_for_var (void *cls,
669 const struct GNUNET_MessageHeader *mod,
670 const char *name,
671 const void *value,
672 uint32_t value_size,
673 uint32_t full_value_size)
674{
675 struct ResultClosure *rcls = cls;
676 rcls->n++;
677 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
678 "Test #%u: guest_look_for_var: %s\n%.*s\n",
679 test, name, value_size, (const char *) value);
680}
681
682
683static void
684guest_look_for ()
685{
686 test = TEST_GUEST_LOOK_FOR;
687 struct ResultClosure *rcls = GNUNET_malloc (sizeof (*rcls));
688 GNUNET_SOCIAL_place_look_for (gst_plc, "_foo", guest_look_for_var, guest_look_for_result, rcls);
689}
690
691
692static void
693guest_look_at_result (void *cls, int64_t result_code,
694 const void *data, uint16_t data_size)
695{
696 struct ResultClosure *rcls = cls;
697
698 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
699 "Test #%u: guest_look_at_result: %" PRId64 "\n",
700 test, result_code);
701 GNUNET_assert (GNUNET_OK == result_code);
702 GNUNET_assert (1 == rcls->n);
703 GNUNET_free (rcls);
704 guest_look_for ();
705}
706
707
708static void
709guest_look_at_var (void *cls,
710 const struct GNUNET_MessageHeader *mod,
711 const char *name,
712 const void *value,
713 uint32_t value_size,
714 uint32_t full_value_size)
715{
716 struct ResultClosure *rcls = cls;
717 rcls->n++;
718
719 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
720 "Test #%u: guest_look_at_var: %s\n%.*s\n",
721 test ,name, value_size, (const char *) value);
722}
723
724
725static void
726guest_look_at ()
727{
728 test = TEST_GUEST_LOOK_AT;
729 struct ResultClosure *rcls = GNUNET_malloc (sizeof (*rcls));
730 GNUNET_SOCIAL_place_look_at (gst_plc, "_foo_bar", guest_look_at_var, guest_look_at_result, rcls);
731}
732
733
734static void
735guest_recv_history_replay_latest_result (void *cls, int64_t result,
736 const void *data, uint16_t data_size)
737{
738 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
739 "Test #%u: Guest received latest history replay result "
740 "(%" PRIu32 " messages, %" PRId64 " fragments):\n"
741 "%.*s\n",
742 test, counter, result, data_size, (const char *) data);
743 //GNUNET_assert (2 == counter); /* message count */
744 //GNUNET_assert (7 == result); /* fragment count */
745
746 guest_look_at ();
747}
748
749
750static void
751guest_history_replay_latest ()
752{
753 test = TEST_GUEST_HISTORY_REPLAY_LATEST;
754 counter = 0;
755 GNUNET_SOCIAL_place_history_replay_latest (gst_plc, 3, "",
756 GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
757 guest_slicer,
758 &guest_recv_history_replay_latest_result,
759 NULL);
760}
761
762
763static void
764guest_recv_history_replay_result (void *cls, int64_t result,
765 const void *data, uint16_t data_size)
766{
767 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
768 "Test #%u: Guest received history replay result: %" PRId64 "\n"
769 "%.*s\n",
770 test, result, data_size, (const char *) data);
771// GNUNET_assert (2 == counter); /* message count */
772// GNUNET_assert (7 == result); /* fragment count */
773
774 guest_history_replay_latest ();
775}
776
777
778static void
779guest_history_replay ()
780{
781 test = TEST_GUEST_HISTORY_REPLAY;
782 counter = 0;
783 GNUNET_SOCIAL_place_history_replay (gst_plc, 1, 3, "",
784 GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
785 guest_slicer,
786 &guest_recv_history_replay_result,
787 NULL);
788}
789
790
791static void
792guest_recv_method (void *cls,
793 const struct GNUNET_PSYC_MessageHeader *msg,
794 const struct GNUNET_PSYC_MessageMethod *meth,
795 uint64_t message_id,
796 const char *method_name)
797{
798 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
799 "Test #%u: Guest received method for message ID %" PRIu64 ":\n"
800 "%s (flags: %x)\n",
801 test, message_id, method_name, ntohl (meth->flags));
802 /** @todo FIXME: check message */
803}
804
805
806static void
807guest_recv_modifier (void *cls,
808 const struct GNUNET_PSYC_MessageHeader *msg,
809 const struct GNUNET_MessageHeader *pmsg,
810 uint64_t message_id,
811 enum GNUNET_PSYC_Operator oper,
812 const char *name,
813 const void *value,
814 uint16_t value_size,
815 uint16_t full_value_size)
816{
817 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
818 "Test #%u: Guest received modifier for message ID %" PRIu64 ":\n"
819 "%c%s: %.*s (size: %u)\n",
820 test, message_id, oper, name, value_size, (const char *) value, value_size);
821 /** @todo FIXME: check modifier */
822}
823
824static void
825guest_recv_mod_foo_bar (void *cls,
826 const struct GNUNET_PSYC_MessageHeader *msg,
827 const struct GNUNET_MessageHeader *pmsg,
828 uint64_t message_id,
829 enum GNUNET_PSYC_Operator oper,
830 const char *name,
831 const void *value,
832 uint16_t value_size,
833 uint16_t full_value_size)
834{
835 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
836 "Test #%u: Guest received modifier matching _foo_bar for message ID %" PRIu64 ":\n"
837 "%c%s: %.*s (size: %u)\n",
838 test, message_id, oper, name, value_size, (const char *) value, value_size);
839 struct ResultClosure *rc = cls;
840 rc->n++;
841 /** @todo FIXME: check modifier */
842}
843
844
845static void
846guest_recv_data (void *cls,
847 const struct GNUNET_PSYC_MessageHeader *msg,
848 const struct GNUNET_MessageHeader *pmsg,
849 uint64_t message_id,
850 const void *data,
851 uint16_t data_size)
852{
853 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
854 "Test #%u: Guest received data for message ID %" PRIu64 ":\n"
855 "%.*s\n",
856 test, message_id, data_size, (const char *) data);
857 /** @todo FIXME: check data */
858}
859
860
861static void
862guest_recv_eom (void *cls,
863 const struct GNUNET_PSYC_MessageHeader *msg,
864 const struct GNUNET_MessageHeader *pmsg,
865 uint64_t message_id,
866 uint8_t is_cancelled)
867{
868 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
869 "Test #%u: Guest received end of message ID %" PRIu64
870 ", cancelled: %u\n",
871 test, message_id, is_cancelled);
872
873 switch (test)
874 {
875 case TEST_HOST_ANNOUNCE:
876 test = TEST_HOST_ANNOUNCE_END;
877 break;
878
879 case TEST_HOST_ANNOUNCE_END:
880 guest_talk ();
881 break;
882
883 case TEST_HOST_ANNOUNCE2:
884 test = TEST_HOST_ANNOUNCE2_END;
885 break;
886
887 case TEST_HOST_ANNOUNCE2_END:
888 guest_history_replay ();
889 break;
890
891 case TEST_GUEST_HISTORY_REPLAY:
892 case TEST_GUEST_HISTORY_REPLAY_LATEST:
893 counter++;
894 break;
895
896 default:
897 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid test: %d\n", test);
898 GNUNET_assert (0);
899 }
900}
901
902
903static void
904host_recv_method (void *cls,
905 const struct GNUNET_PSYC_MessageHeader *msg,
906 const struct GNUNET_PSYC_MessageMethod *meth,
907 uint64_t message_id,
908 const char *method_name)
909{
910 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
911 "Test #%u: Host received method for message ID %" PRIu64 ":\n"
912 "%s\n",
913 test, message_id, method_name);
914 /** @todo FIXME: check message */
915}
916
917
918static void
919host_recv_modifier (void *cls,
920 const struct GNUNET_PSYC_MessageHeader *msg,
921 const struct GNUNET_MessageHeader *pmsg,
922 uint64_t message_id,
923 enum GNUNET_PSYC_Operator oper,
924 const char *name,
925 const void *value,
926 uint16_t value_size,
927 uint16_t full_value_size)
928{
929 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
930 "Test #%u: Host received modifier for message ID %" PRIu64 ":\n"
931 "%c%s: %.*s\n",
932 test, message_id, oper, name, value_size, (const char *) value);
933}
934
935
936static void
937host_recv_data (void *cls,
938 const struct GNUNET_PSYC_MessageHeader *msg,
939 const struct GNUNET_MessageHeader *pmsg,
940 uint64_t message_id,
941 const void *data,
942 uint16_t data_size)
943{
944 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
945 "Test #%u: Host received data for message ID %" PRIu64 ":\n"
946 "%.*s\n",
947 test, message_id, data_size, (const char *) data);
948}
949
950
951static void
952host_recv_eom (void *cls,
953 const struct GNUNET_PSYC_MessageHeader *msg,
954 const struct GNUNET_MessageHeader *pmsg,
955 uint64_t message_id,
956 uint8_t is_cancelled)
957{
958 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
959 "Test #%u: Host received end of message ID %" PRIu64
960 ", cancelled: %u\n",
961 test, message_id, is_cancelled);
962
963 switch (test)
964 {
965 case TEST_HOST_ANNOUNCE:
966 test = TEST_HOST_ANNOUNCE_END;
967 break;
968
969 case TEST_HOST_ANNOUNCE_END:
970 guest_talk ();
971 break;
972
973 case TEST_HOST_ANNOUNCE2:
974 test = TEST_HOST_ANNOUNCE2_END;
975 break;
976
977 case TEST_HOST_ANNOUNCE2_END:
978 guest_history_replay ();
979 break;
980
981 case TEST_GUEST_TALK:
982 host_announce2 ();
983 break;
984
985 default:
986 if (TEST_GUEST_LEAVE <= test)
987 break;
988 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid test: #%u\n", test);
989 GNUNET_assert (0);
990 }
991}
992
993
994static void
995guest_talk ()
996{
997 test = TEST_GUEST_TALK;
998
999 tmit = (struct TransmitClosure) {};
1000 tmit.env = GNUNET_PSYC_env_create ();
1001 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1002 "_bar_foo", DATA2ARG ("one two three"));
1003 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1004 "_bar_baz", DATA2ARG ("four five"));
1005 tmit.data[0] = "zzz xxx yyy ";
1006 tmit.data[1] = "zyx wvu tsr qpo.\n";
1007 tmit.data_delay[1] = 1;
1008 tmit.data[2] = "testing ten nine eight.\n";
1009 tmit.data_count = 3;
1010
1011 tmit.guest_talk
1012 = GNUNET_SOCIAL_guest_talk (gst, "_converse_guest", tmit.env,
1013 &notify_data, &tmit,
1014 GNUNET_SOCIAL_TALK_NONE);
1015}
1016
1017
1018static void
1019host_announce ()
1020{
1021 test = TEST_HOST_ANNOUNCE;
1022
1023 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1024 "Test #%u: Host announcement.\n", test);
1025
1026 tmit = (struct TransmitClosure) {};
1027 tmit.env = GNUNET_PSYC_env_create ();
1028 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1029 "_foo", DATA2ARG ("bar baz"));
1030 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1031 "_foo_bar", DATA2ARG ("foo bar"));
1032 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1033 "_foo_bar_baz", DATA2ARG ("foo bar baz"));
1034 tmit.data[0] = "aaa bbb ccc ";
1035 tmit.data[1] = "abc def ghi jkl.\n";
1036 tmit.data_delay[1] = 1;
1037 tmit.data[2] = "testing one two three ";
1038 tmit.data[3] = "four five.\n";
1039 tmit.data_count = 4;
1040
1041 tmit.host_ann
1042 = GNUNET_SOCIAL_host_announce (hst, "_converse_host", tmit.env,
1043 &notify_data, &tmit,
1044 GNUNET_SOCIAL_ANNOUNCE_NONE);
1045}
1046
1047
1048static void
1049host_announce2 ()
1050{
1051 GNUNET_assert (2 == mod_foo_bar_rcls.n);
1052 GNUNET_PSYC_slicer_modifier_remove (guest_slicer, "_foo_bar",
1053 guest_recv_mod_foo_bar);
1054
1055 test = TEST_HOST_ANNOUNCE2;
1056
1057 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1058 "Test #%u: Host announcement 2.\n", test);
1059
1060 tmit = (struct TransmitClosure) {};
1061 tmit.env = GNUNET_PSYC_env_create ();
1062 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1063 "_foo2", DATA2ARG ("BAR BAZ"));
1064 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1065 "_foo2_bar", DATA2ARG ("FOO BAR"));
1066 GNUNET_PSYC_env_add (tmit.env, GNUNET_PSYC_OP_ASSIGN,
1067 "_foo2_bar_baz", DATA2ARG ("FOO BAR BAZ"));
1068 tmit.data[0] = "AAA BBB CCC ";
1069 tmit.data[1] = "ABC DEF GHI JKL.\n";
1070 tmit.data[2] = "TESTING ONE TWO THREE.\n";
1071 tmit.data_count = 3;
1072
1073 tmit.host_ann
1074 = GNUNET_SOCIAL_host_announce (hst, "_converse_host_two", tmit.env,
1075 &notify_data, &tmit,
1076 GNUNET_SOCIAL_ANNOUNCE_NONE);
1077}
1078
1079
1080static void
1081guest_recv_entry_decision (void *cls,
1082 int is_admitted,
1083 const struct GNUNET_PSYC_Message *entry_msg)
1084{
1085 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1086 "Test #%u: Guest received entry decision (try %u): %d.\n",
1087 test, join_req_count, is_admitted);
1088
1089 if (NULL != entry_msg)
1090 {
1091 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
1092 const char *method_name = NULL;
1093 const void *data = NULL;
1094 uint16_t data_size = 0;
1095 struct GNUNET_PSYC_MessageHeader *
1096 pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
1097 GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
1098 GNUNET_free (pmsg);
1099
1100 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1101 "%s\n%.*s\n",
1102 method_name, data_size, (const char *) data);
1103 /** @todo FIXME: check response message */
1104 }
1105
1106 switch (test)
1107 {
1108 case TEST_GUEST_RECV_ENTRY_DCSN_REFUSE:
1109 GNUNET_assert (GNUNET_NO == is_admitted);
1110 test = TEST_HOST_ANSWER_DOOR_ADMIT;
1111 GNUNET_SOCIAL_guest_disconnect (gst, &guest_enter, NULL);
1112 break;
1113
1114 case TEST_GUEST_RECV_ENTRY_DCSN_ADMIT:
1115 GNUNET_assert (GNUNET_YES == is_admitted);
1116 host_announce ();
1117 break;
1118
1119 case TEST_GUEST_ENTER_BY_NAME:
1120 GNUNET_SCHEDULER_add_now (&schedule_reconnect, NULL);
1121 break;
1122
1123 default:
1124 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "invalid test: %d\n", test);
1125 GNUNET_assert (0);
1126 }
1127}
1128
1129
1130static void
1131host_answer_door (void *cls,
1132 struct GNUNET_SOCIAL_Nym *nym,
1133 const char *method_name,
1134 struct GNUNET_PSYC_Environment *env,
1135 const void *data,
1136 size_t data_size)
1137{
1138 join_req_count++;
1139
1140 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1141 "Test #%u: Host received entry request from guest (try %u).\n",
1142 (uint8_t) test, join_req_count);
1143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1144 "%s\n%.*s\n",
1145 method_name, (int) data_size, (const char *) data);
1146
1147 switch (test)
1148 {
1149 case TEST_HOST_ANSWER_DOOR_REFUSE:
1150 test = TEST_GUEST_RECV_ENTRY_DCSN_REFUSE;
1151 join_resp = GNUNET_PSYC_message_create ("_notice_place_refuse", env,
1152 DATA2ARG ("Go away!"));
1153 GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, join_resp);
1154 break;
1155
1156 case TEST_HOST_ANSWER_DOOR_ADMIT:
1157 test = TEST_GUEST_RECV_ENTRY_DCSN_ADMIT;
1158 // fall through
1159
1160 case TEST_GUEST_ENTER_BY_NAME:
1161 join_resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
1162 DATA2ARG ("Welcome, nym!"));
1163 GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, join_resp);
1164 break;
1165
1166 default:
1167 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Invalid test: #%u\n", test);
1168 GNUNET_assert (0);
1169 }
1170}
1171
1172
1173static void
1174guest_recv_local_enter (void *cls, int result,
1175 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
1176 uint64_t max_message_id)
1177{
1178 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1179 "Test #%u: Guest entered local place: %d\n",
1180 test, result);
1181 GNUNET_assert (GNUNET_OK == result);
1182}
1183
1184
1185static void
1186guest_enter ()
1187{
1188 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1189 "Test #%u: Entering place as guest.\n", test);
1190
1191 struct GuestEnterMessage *emsg = &guest_enter_msg;
1192
1193 emsg->method_name = "_request_enter";
1194 emsg->env = GNUNET_PSYC_env_create ();
1195 GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
1196 "_abc", "abc def", 7);
1197 GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
1198 "_abc_def", "abc def ghi", 11);
1199 emsg->data = "let me in";
1200 emsg->data_size = strlen (emsg->data) + 1;
1201 emsg->msg = GNUNET_PSYC_message_create (emsg->method_name, emsg->env,
1202 emsg->data, emsg->data_size);
1203
1204 gst = GNUNET_SOCIAL_guest_enter (app, guest_ego, &place_pub_key,
1205 GNUNET_PSYC_SLAVE_JOIN_NONE,
1206 &this_peer, 0, NULL, emsg->msg, guest_slicer,
1207 guest_recv_local_enter,
1208 guest_recv_entry_decision, NULL);
1209 gst_plc = GNUNET_SOCIAL_guest_get_place (gst);
1210
1211 GNUNET_SOCIAL_place_msg_proc_set (gst_plc, "_converse",
1212 GNUNET_SOCIAL_MSG_PROC_SAVE);
1213}
1214
1215
1216static void
1217guest_enter_by_name ()
1218{
1219 test = TEST_GUEST_ENTER_BY_NAME;
1220 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1221 "Test #%u: Entering place by name as guest.\n", test);
1222
1223 struct GuestEnterMessage *emsg = &guest_enter_msg;
1224
1225 emsg->method_name = "_request_enter";
1226 emsg->env = GNUNET_PSYC_env_create ();
1227 GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
1228 "_abc", "abc def", 7);
1229 GNUNET_PSYC_env_add (emsg->env, GNUNET_PSYC_OP_ASSIGN,
1230 "_abc_def", "abc def ghi", 11);
1231 emsg->data = "let me in";
1232 emsg->data_size = strlen (emsg->data) + 1;
1233 emsg->msg = GNUNET_PSYC_message_create (emsg->method_name, emsg->env,
1234 emsg->data, emsg->data_size);
1235
1236 gst = GNUNET_SOCIAL_guest_enter_by_name (app, guest_ego,
1237 "home.host.gnu", "let.me*in!",
1238 emsg->msg, guest_slicer,
1239 guest_recv_local_enter,
1240 guest_recv_entry_decision, NULL);
1241 gst_plc = GNUNET_SOCIAL_guest_get_place (gst);
1242}
1243
1244
1245static void
1246app_recv_zone_add_nym_result (void *cls, int64_t result,
1247 const void *data, uint16_t data_size)
1248{
1249 GNUNET_assert (GNUNET_YES == result);
1250 is_guest_nym_added = GNUNET_YES;
1251}
1252
1253
1254static void
1255guest_init ()
1256{
1257 guest_pub_key = GNUNET_SOCIAL_ego_get_pub_key (guest_ego);
1258
1259 guest_slicer = GNUNET_PSYC_slicer_create ();
1260 GNUNET_PSYC_slicer_method_add (guest_slicer, "", NULL,
1261 guest_recv_method, guest_recv_modifier,
1262 guest_recv_data, guest_recv_eom, NULL);
1263 GNUNET_PSYC_slicer_modifier_add (guest_slicer, "_foo_bar",
1264 guest_recv_mod_foo_bar, &mod_foo_bar_rcls);
1265 test = TEST_HOST_ANSWER_DOOR_REFUSE;
1266
1267 GNUNET_SOCIAL_zone_add_nym (app, guest_ego, "host", host_pub_key,
1268 GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES),
1269 app_recv_zone_add_nym_result, NULL);
1270}
1271
1272
1273static void
1274id_host_created (void *cls, const char *emsg)
1275{
1276 if (NULL != emsg)
1277 {
1278 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1279 "Test #%u: Could not create host identity: %s\n",
1280 test, emsg);
1281#if ! DEBUG_TEST_SOCIAL
1282 GNUNET_assert (0);
1283#endif
1284 }
1285
1286}
1287
1288
1289static void
1290id_guest_created (void *cls, const char *emsg)
1291{
1292 if (NULL != emsg)
1293 {
1294 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1295 "Test #%u: Could not create guest identity: %s\n",
1296 test, emsg);
1297#if ! DEBUG_TEST_SOCIAL
1298 GNUNET_assert (0);
1299#endif
1300 }
1301 //if (NULL != guest_ego)
1302 // guest_init ();
1303}
1304
1305
1306static void
1307host_entered (void *cls, int result,
1308 const struct GNUNET_CRYPTO_EddsaPublicKey *home_pub_key,
1309 uint64_t max_message_id)
1310{
1311 place_pub_key = *home_pub_key;
1312 GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
1313 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1314 "Test #%u: Host entered place %s\n",
1315 test, GNUNET_h2s (&place_pub_hash));
1316 guest_enter ();
1317}
1318
1319
1320static void
1321host_enter ()
1322{
1323 host_slicer = GNUNET_PSYC_slicer_create ();
1324 GNUNET_PSYC_slicer_method_add (host_slicer, "", NULL,
1325 host_recv_method, host_recv_modifier,
1326 host_recv_data, host_recv_eom, NULL);
1327
1328 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1329 "Test #%u: Entering place as host.\n", test);
1330 test = TEST_HOST_ENTER;
1331 hst = GNUNET_SOCIAL_host_enter (app, host_ego,
1332 GNUNET_PSYC_CHANNEL_PRIVATE,
1333 host_slicer, host_entered,
1334 host_answer_door, host_farewell, NULL);
1335 hst_plc = GNUNET_SOCIAL_host_get_place (hst);
1336
1337 GNUNET_SOCIAL_place_msg_proc_set (hst_plc, "_converse",
1338 GNUNET_SOCIAL_MSG_PROC_RELAY);
1339}
1340
1341
1342static void
1343start_app_if_ready ()
1344{
1345 if (NULL == identity_host_ego || NULL == identity_guest_ego)
1346 {
1347 return;
1348 }
1349 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1350 "starting app...\n");
1351 app = GNUNET_SOCIAL_app_connect (cfg,
1352 app_id,
1353 app_recv_ego,
1354 app_recv_host,
1355 app_recv_guest,
1356 app_connected,
1357 NULL);
1358}
1359
1360
1361static void
1362identity_ego_cb (void *cls, struct GNUNET_IDENTITY_Ego *ego,
1363 void **ctx, const char *name)
1364{
1365 if (NULL != ego)
1366 {
1367 if (ego == identity_host_ego)
1368 {
1369 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1370 "Host ego deleted\n");
1371 }
1372 else if (ego == identity_guest_ego)
1373 {
1374 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1375 "Guest ego deleted\n");
1376 }
1377 else if (0 == strcmp (name, host_name))
1378 {
1379 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1380 "Created ego %s\n",
1381 name);
1382 identity_host_ego = ego;
1383 start_app_if_ready ();
1384 }
1385 else if (0 == strcmp (name, guest_name))
1386 {
1387 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1388 "Created guest ego %s\n",
1389 name);
1390 identity_guest_ego = ego;
1391 start_app_if_ready ();
1392 }
1393 }
1394}
1395
1396
1397/**
1398 * Main function of the test, run from scheduler.
1399 *
1400 * @param cls NULL
1401 * @param cfg configuration we use (also to connect to Social service)
1402 * @param peer handle to access more of the peer (not used)
1403 */
1404static void
1405#if DEBUG_TEST_SOCIAL
1406run (void *cls, char *const *args, const char *cfgfile,
1407 const struct GNUNET_CONFIGURATION_Handle *c)
1408#else
1409run (void *cls,
1410 const struct GNUNET_CONFIGURATION_Handle *c,
1411 struct GNUNET_TESTING_Peer *peer)
1412#endif
1413{
1414 cfg = c;
1415 res = 1;
1416 end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
1417 &end_badly, NULL);
1418 GNUNET_SCHEDULER_add_shutdown (&end_shutdown,
1419 NULL);
1420 GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
1421
1422 id = GNUNET_IDENTITY_connect (cfg, &identity_ego_cb, NULL);
1423
1424 test = TEST_IDENTITIES_CREATE;
1425 GNUNET_IDENTITY_create (id, host_name, &id_host_created, NULL);
1426 GNUNET_IDENTITY_create (id, guest_name, &id_guest_created, NULL);
1427}
1428
1429
1430int
1431main (int argc, char *argv[])
1432{
1433 res = 1;
1434#if DEBUG_TEST_SOCIAL
1435 const struct GNUNET_GETOPT_CommandLineOption opts[] = {
1436 GNUNET_GETOPT_OPTION_END
1437 };
1438 if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-social",
1439 "test-social [options]",
1440 opts, &run, NULL))
1441 return 1;
1442#else
1443 if (0 != GNUNET_TESTING_peer_run ("test-social", "test_social.conf", &run, NULL))
1444 return 1;
1445#endif
1446 return res;
1447}
1448
1449/* end of test_social.c */
diff --git a/src/social/test_social.conf b/src/social/test_social.conf
new file mode 100644
index 0000000..bc48776
--- /dev/null
+++ b/src/social/test_social.conf
@@ -0,0 +1,19 @@
1@INLINE@ ../../contrib/conf/gnunet/no_forcestart.conf
2
3[PATHS]
4GNUNET_TEST_HOME = $GNUNET_TMP/gnunet-test-social/
5
6[social]
7IMMEDIATE_START = YES
8
9[transport]
10PLUGINS = tcp
11
12[nat]
13DISABLEV6 = YES
14ENABLE_UPNP = NO
15BEHIND_NAT = NO
16ALLOW_NAT = NO
17INTERNAL_ADDRESS = 127.0.0.1
18EXTERNAL_ADDRESS = 127.0.0.1
19