2 This file is part of GNUnet.
3 Copyright (C) 2016 GNUnet e.V.
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
22 * CLI tool to interact with the social service.
24 * @author Gabor X Toth
30 #include "gnunet_util_lib.h"
31 #include "gnunet_social_service.h"
33 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
35 /* operations corresponding to API calls */
38 static int op_host_enter;
41 static int op_host_leave;
43 /** --host-announce */
44 static int op_host_announce;
47 static int op_guest_enter;
50 static int op_guest_leave;
53 static int op_guest_talk;
55 /** --history-replay */
56 static char *op_history_replay;
58 /** --history-replay-latest */
59 static char *op_history_replay_latest;
62 static int op_look_at;
65 static int op_look_for;
71 static char *opt_app = "cli";
74 static char *opt_place;
80 static int opt_follow;
83 static char *opt_method;
86 // FIXME: could also come from STDIN
87 static char *opt_data;
90 static char *opt_name;
93 static uint64_t opt_start;
96 static uint64_t opt_end;
107 /** Task handle for timeout termination. */
108 struct GNUNET_SCHEDULER_Task *timeout_task;
110 const struct GNUNET_CONFIGURATION_Handle *cfg;
112 struct GNUNET_CORE_Handle *core;
113 struct GNUNET_PeerIdentity peer;
115 struct GNUNET_SOCIAL_App *app;
117 /** public key of connected place */
118 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
120 /** hash of @a place_pub_key */
121 struct GNUNET_HashCode place_pub_hash;
123 struct GNUNET_PSYC_Slicer *slicer;
125 struct GNUNET_SOCIAL_Ego *ego;
126 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
128 struct GNUNET_SOCIAL_Host *hst;
129 struct GNUNET_SOCIAL_Guest *gst;
130 struct GNUNET_SOCIAL_Place *plc;
141 * Terminate the test case (failure).
148 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout\n");
154 host_leave (struct GNUNET_SOCIAL_Host *host)
161 host_announce (struct GNUNET_SOCIAL_Host *host,
170 guest_leave (struct GNUNET_SOCIAL_Guest *guest)
177 guest_talk (struct GNUNET_SOCIAL_Guest *guest,
186 history_replay (struct GNUNET_SOCIAL_Place *place,
187 uint64_t start, uint64_t end, const char *prefix)
194 history_replay_latest (struct GNUNET_SOCIAL_Place *place,
195 uint64_t limit, const char *prefix)
202 look_at (struct GNUNET_SOCIAL_Place *place,
210 look_for (struct GNUNET_SOCIAL_Place *place,
216 /* SLICER + CALLBACKS */
220 slicer_recv_method (void *cls,
221 const struct GNUNET_PSYC_MessageHeader *msg,
222 const struct GNUNET_PSYC_MessageMethod *meth,
224 const char *method_name)
226 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
227 "Received method for message ID %" PRIu64 ":\n"
229 message_id, method_name, ntohl (meth->flags));
234 slicer_recv_modifier (void *cls,
235 const struct GNUNET_PSYC_MessageHeader *msg,
236 const struct GNUNET_MessageHeader *pmsg,
238 enum GNUNET_PSYC_Operator oper,
242 uint16_t full_value_size)
244 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
245 "Received modifier for message ID %" PRIu64 ":\n"
246 "%c%s: %.*s (size: %u)\n",
247 message_id, oper, name, value_size, value, value_size);
252 slicer_recv_data (void *cls,
253 const struct GNUNET_PSYC_MessageHeader *msg,
254 const struct GNUNET_MessageHeader *pmsg,
259 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
260 "Received data for message ID %" PRIu64 ":\n"
262 message_id, data_size, data);
267 slicer_recv_eom (void *cls,
268 const struct GNUNET_PSYC_MessageHeader *msg,
269 const struct GNUNET_MessageHeader *pmsg,
271 uint8_t is_cancelled)
273 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
274 "Received end of message ID %" PRIu64
276 message_id, is_cancelled);
280 static struct GNUNET_PSYC_Slicer *
283 slicer = GNUNET_PSYC_slicer_create ();
285 /* register slicer to receive incoming messages with any method name */
286 GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
287 slicer_recv_method, slicer_recv_modifier,
288 slicer_recv_data, slicer_recv_eom, NULL);
293 /* GUEST ENTER + CALLBACKS */
297 guest_recv_entry_decision (void *cls,
299 const struct GNUNET_PSYC_Message *entry_msg)
301 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
302 "Guest received entry decision %d\n",
305 if (NULL != entry_msg)
307 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
308 const char *method_name = NULL;
309 const void *data = NULL;
310 uint16_t data_size = 0;
311 struct GNUNET_PSYC_MessageHeader *
312 pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
313 GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318 method_name, data_size, data);
324 guest_recv_local_enter (void *cls, int result,
325 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
326 uint64_t max_message_id)
328 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
329 "Guest entered to local place: %d, max_message_id: %" PRIu64 "\n",
330 result, max_message_id);
331 GNUNET_assert (0 <= result);
335 static struct GNUNET_PSYC_Message *
336 guest_enter_msg_create ()
338 const char *method_name = "_request_enter";
339 struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
340 GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
341 "_foo", "bar", sizeof ("bar"));
342 void *data = "let me in";
343 uint16_t data_size = strlen (data) + 1;
345 return GNUNET_PSYC_message_create (method_name, env, data, data_size);
350 guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
351 struct GNUNET_PeerIdentity *peer)
353 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
354 "Entering to place as guest.\n");
356 gst = GNUNET_SOCIAL_guest_enter (app, ego, &place_pub_key,
357 GNUNET_PSYC_SLAVE_JOIN_NONE,
358 peer, 0, NULL, guest_enter_msg_create (),
360 guest_recv_local_enter,
361 guest_recv_entry_decision, NULL);
362 plc = GNUNET_SOCIAL_guest_get_place (gst);
367 guest_enter_by_name (const char *gns_name)
369 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
370 "Entering to place by name as guest.\n");
372 gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
373 guest_enter_msg_create (), slicer,
374 guest_recv_local_enter,
375 guest_recv_entry_decision, NULL);
376 plc = GNUNET_SOCIAL_guest_get_place (gst);
381 /* HOST ENTER + CALLBACKS */
385 host_answer_door (void *cls,
386 struct GNUNET_SOCIAL_Nym *nym,
387 const char *method_name,
388 struct GNUNET_PSYC_Environment *env,
392 const struct GNUNET_CRYPTO_EcdsaPublicKey *
393 nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
395 nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
397 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
398 "Entry request: %s\n", nym_str);
399 GNUNET_free (nym_str);
404 host_farewell (void *cls,
405 const struct GNUNET_SOCIAL_Nym *nym,
406 struct GNUNET_PSYC_Environment *env)
408 const struct GNUNET_CRYPTO_EcdsaPublicKey *
409 nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
411 nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
413 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
414 "Farewell: %s\n", nym_str);
415 GNUNET_free (nym_str);
420 host_entered (void *cls, int result,
421 const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
422 uint64_t max_message_id)
424 place_pub_key = *pub_key;
425 GNUNET_CRYPTO_hash (&place_pub_key, sizeof (place_pub_key), &place_pub_hash);
426 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
427 "Entered: %s, max_message_id: %" PRIu64 "\n",
428 GNUNET_h2s_full (&place_pub_hash), max_message_id);
435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
437 hst = GNUNET_SOCIAL_host_enter (app, ego,
438 GNUNET_PSYC_CHANNEL_PRIVATE,
439 slicer_create (), host_entered,
440 host_answer_door, host_farewell, NULL);
441 plc = GNUNET_SOCIAL_host_get_place (hst);
445 /* RECONNECT CALLBACKS */
450 if (op_history_replay) {
451 history_replay (plc, opt_start, opt_end, opt_method);
453 else if (op_history_replay_latest) {
454 history_replay_latest (plc, opt_limit, opt_method);
456 else if (op_look_at) {
457 look_at (plc, opt_name);
459 else if (op_look_for) {
460 look_for (plc, opt_name);
466 host_reconnected (void *cls, int result,
467 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
468 uint64_t max_message_id)
470 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
471 "Host reconnected\n");
476 else if (op_host_announce) {
477 host_announce (hst, opt_method, opt_data);
480 place_reconnected ();
486 guest_reconnected (void *cls, int result,
487 const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
488 uint64_t max_message_id)
490 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
491 "Guest reconnected\n");
493 if (op_guest_leave) {
496 else if (op_guest_talk) {
497 guest_talk (gst, opt_method, opt_data);
500 place_reconnected ();
509 app_connected (void *cls)
511 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
512 "App connected: %p\n", cls);
517 else if (op_guest_enter) {
518 guest_enter (&place_pub_key);
524 app_recv_host (void *cls,
525 struct GNUNET_SOCIAL_HostConnection *hconn,
526 struct GNUNET_SOCIAL_Ego *ego,
527 const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
528 enum GNUNET_SOCIAL_AppPlaceState place_state)
530 struct GNUNET_HashCode host_pub_hash;
531 GNUNET_CRYPTO_hash (host_pub_key, sizeof (*host_pub_key), &host_pub_hash);
533 host_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (guest_pub_key);
535 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
537 GNUNET_h2s_full (&host_pub_hash), host_pub_str);
538 GNUNET_free (host_pub_str);
540 if (0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
542 hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
543 host_answer_door, host_farewell, NULL);
544 plc = GNUNET_SOCIAL_host_get_place (hst);
550 app_recv_guest (void *cls,
551 struct GNUNET_SOCIAL_GuestConnection *gconn,
552 struct GNUNET_SOCIAL_Ego *ego,
553 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
554 enum GNUNET_SOCIAL_AppPlaceState place_state)
556 struct GNUNET_HashCode guest_pub_hash;
557 GNUNET_CRYPTO_hash (guest_pub_key, sizeof (*guest_pub_key), &guest_pub_hash);
559 guest_pub_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (guest_pub_key);
561 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
563 GNUNET_h2s_full (&guest_pub_hash), guest_pub_str);
564 GNUNET_free (guest_pub_str);
566 if (0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
568 gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
569 slicer_create (), guest_reconnected, NULL);
570 plc = GNUNET_SOCIAL_guest_get_place (gst);
576 app_recv_ego (void *cls,
577 struct GNUNET_SOCIAL_Ego *ego,
578 const struct GNUNET_CRYPTO_EcdsaPublicKey *ego_pub_key,
581 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
583 name, GNUNET_CRYPTO_ecdsa_public_key_to_string (ego_pub_key));
590 app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
602 core_connected (void *cls, const struct GNUNET_PeerIdentity *my_identity)
610 * Main function run by the scheduler.
613 * @param args remaining command-line arguments
614 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
615 * @param cfg configuration
618 run (void *cls, char *const *args, const char *cfgfile,
619 const struct GNUNET_CONFIGURATION_Handle *c)
625 timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout, NULL);
628 if (op_host_enter && NULL != opt_place)
630 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
631 _ ("--place must not be specified when using --host-enter\n"));
636 || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
640 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
641 _ ("--place missing or invalid.\n"));
647 GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
654 // FIXME: peer ID from string
657 core = GNUNET_CORE_connect (cfg, NULL, &core_connected, NULL, NULL,
658 NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
663 * The main function to obtain peer information.
665 * @param argc number of arguments from the command line
666 * @param argv command line arguments
667 * @return 0 ok, 1 on error
670 main (int argc, char *const *argv)
673 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
676 { 'E', "host-enter", NULL,
677 _ ("create a place for nyms to join"),
678 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_enter },
680 { 'L', "host-leave", NULL,
681 _ ("destroy a place we were hosting"),
682 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_leave },
684 { 'A', "host-announce", NULL,
685 _ ("publish something to a place we are hosting"),
686 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_announce },
688 { 'e', "guest-enter", NULL,
689 _ ("join somebody else's place"),
690 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
692 { 'l', "guest-leave", NULL,
693 _ ("leave somebody else's place"),
694 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
696 { 't', "guest-talk", NULL,
697 _ ("submit something to somebody's place"),
698 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_talk },
700 { 'R', "history-replay", NULL,
701 _ ("replay history of messages between message IDs --start and --end"),
702 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_history_replay },
704 { 'r', "history-replay-latest", NULL,
705 _ ("replay history of latest messages up to the given --limit"),
706 GNUNET_NO, &GNUNET_GETOPT_set_one, &op_history_replay_latest },
710 { 'A', "app", "application ID",
711 _ ("application ID to use when connecting"),
712 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_app },
714 { 'p', "place", "PUBKEY",
715 _ ("public key of place"),
716 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_place },
718 { 'g', "ego", "PUBKEY",
719 _ ("public key of ego"),
720 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_place },
722 { 'f', "follow", NULL,
723 _ ("wait for incoming messages"),
724 GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_follow },
726 { 'm', "method", "METHOD_NAME",
728 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_method },
730 { 'd', "data", "DATA",
731 _ ("message body to transmit"),
732 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_data },
734 { 'n', "name", "VAR_NAME",
735 _ ("state var name to query"),
736 GNUNET_NO, &GNUNET_GETOPT_set_string, &opt_name },
738 { 'a', "start", NULL,
739 _ ("start message ID for history replay"),
740 GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_start },
743 _ ("end message ID for history replay"),
744 GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_end },
746 { 'n', "limit", NULL,
747 _ ("number of messages to replay from history"),
748 GNUNET_NO, &GNUNET_GETOPT_set_ulong, &opt_limit },
750 GNUNET_GETOPT_OPTION_END
753 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
757 _ ("interact with the social service: enter/leave, send/receive messages, access history and state")m;
759 "gnunet-social --host-enter --ego <name or pubkey> [--listen]\n"
760 "gnunet-social --host-leave --place <pubkey>\n"
761 "gnunet-social --host-announce --place <pubkey> --method <method_name> --data <message body>\n"
763 "gnunet-social --guest-enter --place <pubkey> --ego <name or pubkey> [--listen]\n"
764 "gnunet-social --guest-leave --place <pubkey>\n"
765 "gnunet-social --guest-talk --place <pubkey> --method <method_nmae> --data <data>\n"
767 "gnunet-social --history-replay --place <pubkey> --start <msgid> --end <msgid> [--method <method_prefix>]\n"
768 "gnunet-social --history-replay-latest --place <pubkey> --limit <msg_limit> [--method <method_prefix>]\n"
770 "gnunet-social --look-at --place <pubkey> --name <full_name>\n"
771 "gnunet-social --look-for --place <pubkey> --name <name_prefix>\n";
773 res = GNUNET_PROGRAM_run (argc, argv, usage,
774 help, options, &run, NULL);
776 GNUNET_free ((void *) argv);
778 if (GNUNET_OK == res)