aboutsummaryrefslogtreecommitdiff
path: root/src/social/gnunet-social.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/social/gnunet-social.c')
-rw-r--r--src/social/gnunet-social.c1411
1 files changed, 1411 insertions, 0 deletions
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}