Link libgnunetblockgroup to libgnunetblock
[oweals/gnunet.git] / src / social / gnunet-social.c
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
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.
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      General Public License for more details.
14
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.
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 */
40 static int op_status;
41
42 /** --host-enter */
43 static int op_host_enter;
44
45 /** --host-reconnect */
46 static int op_host_reconnect;
47
48 /** --host-leave */
49 static int op_host_leave;
50
51 /** --host-announce */
52 static int op_host_announce;
53
54 /** --host-assign */
55 static int op_host_assign;
56
57 /** --guest-enter */
58 static int op_guest_enter;
59
60 /** --guest-reconnect */
61 static int op_guest_reconnect;
62
63 /** --guest-leave */
64 static int op_guest_leave;
65
66 /** --guest-talk */
67 static int op_guest_talk;
68
69 /** --replay */
70 static char *op_replay;
71
72 /** --replay-latest */
73 static char *op_replay_latest;
74
75 /** --look-at */
76 static int op_look_at;
77
78 /** --look-for */
79 static int op_look_for;
80
81
82 /* options */
83
84 /** --app */
85 static char *opt_app = "cli";
86
87 /** --place */
88 static char *opt_place;
89
90 /** --ego */
91 static char *opt_ego;
92
93 /** --gns */
94 static char *opt_gns;
95
96 /** --peer */
97 static char *opt_peer;
98
99 /** --follow */
100 static int opt_follow;
101
102 /** --welcome */
103 static int opt_welcome;
104
105 /** --deny */
106 static int opt_deny;
107
108 /** --method */
109 static char *opt_method;
110
111 /** --data */
112 // FIXME: should come from STDIN
113 static char *opt_data;
114
115 /** --name */
116 static char *opt_name;
117
118 /** --start */
119 static uint64_t opt_start;
120
121 /** --until */
122 static uint64_t opt_until;
123
124 /** --limit */
125 static int opt_limit;
126
127
128 /* global vars */
129
130 /** exit code */
131 static int ret = 1;
132
133 /** are we waiting for service to close our connection */
134 static char is_disconnecting = 0;
135
136 /** Task handle for timeout termination. */
137 struct GNUNET_SCHEDULER_Task *timeout_task;
138
139 const struct GNUNET_CONFIGURATION_Handle *cfg;
140
141 struct GNUNET_PeerIdentity peer, this_peer;
142
143 struct GNUNET_SOCIAL_App *app;
144
145 /** public key of connected place */
146 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
147
148 struct GNUNET_PSYC_Slicer *slicer;
149
150 struct GNUNET_SOCIAL_Ego *ego;
151 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
152
153 struct GNUNET_SOCIAL_Host *hst;
154 struct GNUNET_SOCIAL_Guest *gst;
155 struct GNUNET_SOCIAL_Place *plc;
156
157 const char *method_received;
158
159
160 /* DISCONNECT */
161
162
163 /**
164  * Callback called after the host or guest place disconnected.
165  */
166 static void
167 disconnected (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  */
177 static void
178 app_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  */
202 static void
203 disconnect ()
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
213 static void
214 scheduler_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  */
223 static void
224 timeout (void *cls)
225 {
226   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "timeout()\n");
227   disconnect ();
228 }
229
230 static void
231 schedule_success (void *cls)
232 {
233   ret = 0;
234   disconnect ();
235 }
236
237
238 static void
239 schedule_fail (void *cls)
240 {
241   disconnect ();
242 }
243
244
245 /**
246  * Schedule exit with success result.
247  */
248 static void
249 exit_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  */
263 static void
264 exit_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  */
283 static void
284 host_left ()
285 {
286   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
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  */
295 static void
296 host_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  */
309 static void
310 guest_left (void *cls)
311 {
312   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
313               "Guest has left the place.\n");
314 }
315
316
317 /**
318  * Leave a place permanently as guest.
319  */
320 static void
321 guest_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
337 struct 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  */
348 static int
349 notify_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  */
381 static void
382 host_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  */
402 static void
403 host_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  */
420 static void
421 guest_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  */
445 static void
446 recv_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  */
465 static void
466 history_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  */
479 static void
480 history_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  */
496 static void
497 look_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  */
513 static void
514 look_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_WARNING,
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  */
530 static void
531 look_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  */
540 static void
541 look_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  */
553 static void
554 slicer_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_WARNING,
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\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  */
575 static void
576 slicer_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_WARNING,
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 #endif
592   /* obviously not binary safe */
593   printf("%c%s\t%.*s\n",
594               oper, name, value_size, (const char *) value);
595 }
596
597
598 /**
599  * Callback notifying about an incoming data fragment.
600  */
601 static void
602 slicer_recv_data (void *cls,
603                   const struct GNUNET_PSYC_MessageHeader *msg,
604                   const struct GNUNET_MessageHeader *pmsg,
605                   uint64_t message_id,
606                   const void *data,
607                   uint16_t data_size)
608 {
609 #if 0
610   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
611               "Received data for message ID %" PRIu64 ":\n"
612               "%.*s\n",
613               message_id, data_size, (const char *) data);
614 #endif
615   /* obviously not binary safe */
616   printf("%s\n%.*s\n",
617               method_received, data_size, (const char *) data);
618 }
619
620
621 /**
622  * Callback notifying about the end of a message.
623  */
624 static void
625 slicer_recv_eom (void *cls,
626                 const struct GNUNET_PSYC_MessageHeader *msg,
627                 const struct GNUNET_MessageHeader *pmsg,
628                 uint64_t message_id,
629                 uint8_t is_cancelled)
630 {
631   printf(".\n");
632   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
633               "Received end of message ID %" PRIu64
634               ", cancelled: %u\n",
635               message_id, is_cancelled);
636 }
637
638
639 /**
640  * Create a slicer for receiving message parts.
641  */
642 static struct GNUNET_PSYC_Slicer *
643 slicer_create ()
644 {
645   slicer = GNUNET_PSYC_slicer_create ();
646
647   /* register slicer to receive incoming messages with any method name */
648   GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
649                                  slicer_recv_method, slicer_recv_modifier,
650                                  slicer_recv_data, slicer_recv_eom, NULL);
651   return slicer;
652 }
653
654
655 /* GUEST ENTER */
656
657
658 /**
659  * Callback called when the guest receives an entry decision from the host.
660  *
661  * It is called once after using guest_enter() or guest_enter_by_name(),
662  * in case of a reconnection only the local enter callback is called.
663  */
664 static void
665 guest_recv_entry_decision (void *cls,
666                            int is_admitted,
667                            const struct GNUNET_PSYC_Message *entry_msg)
668 {
669   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
670               "Guest received entry decision %d\n",
671               is_admitted);
672
673   if (NULL != entry_msg)
674   {
675     struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
676     const char *method_name = NULL;
677     const void *data = NULL;
678     uint16_t data_size = 0;
679     struct GNUNET_PSYC_MessageHeader *
680       pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
681     GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
682     GNUNET_free (pmsg);
683
684     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
685                 "%s\n%.*s\n",
686                 method_name, data_size, (const char *) data);
687   }
688
689   if (op_guest_enter && !opt_follow)
690   {
691     exit_success ();
692   }
693 }
694
695
696 /**
697  * Callback called after a guest connection is established to the local service.
698  */
699 static void
700 guest_recv_local_enter (void *cls, int result,
701                         const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
702                         uint64_t max_message_id)
703 {
704   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
705   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
706               "Guest entered local place: %s, max_message_id: %" PRIu64 "\n",
707               pub_str, max_message_id);
708   GNUNET_free (pub_str);
709   GNUNET_assert (0 <= result);
710
711   if (op_guest_enter && !opt_follow)
712   {
713     exit_success ();
714   }
715 }
716
717
718 /**
719  * Create entry requset message.
720  */
721 static struct GNUNET_PSYC_Message *
722 guest_enter_msg_create ()
723 {
724   const char *method_name = "_request_enter";
725   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
726   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
727                        "_foo", DATA2ARG ("bar"));
728   void *data = "let me in";
729   uint16_t data_size = strlen (data) + 1;
730
731   return GNUNET_PSYC_message_create (method_name, env, data, data_size);
732 }
733
734
735 /**
736  * Enter a place as guest, using its public key and peer ID.
737  */
738 static void
739 guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
740              const struct GNUNET_PeerIdentity *peer)
741 {
742   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
743               "Entering to place as guest.\n");
744
745   if (NULL == ego)
746   {
747     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
748     exit_fail ();
749     return;
750   }
751
752   struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
753   gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
754                                    GNUNET_PSYC_SLAVE_JOIN_NONE,
755                                    peer, 0, NULL, join_msg, slicer_create (),
756                                    guest_recv_local_enter,
757                                    guest_recv_entry_decision, NULL);
758   GNUNET_free (join_msg);
759   plc = GNUNET_SOCIAL_guest_get_place (gst);
760 }
761
762
763 /**
764  * Enter a place as guest using its GNS address.
765  */
766 static void
767 guest_enter_by_name (const char *gns_name)
768 {
769   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
770               "Entering to place by name as guest.\n");
771
772   struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
773   gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
774                                            join_msg, slicer,
775                                            guest_recv_local_enter,
776                                            guest_recv_entry_decision, NULL);
777   GNUNET_free (join_msg);
778   plc = GNUNET_SOCIAL_guest_get_place (gst);
779 }
780
781
782 /* HOST ENTER */
783
784
785 /**
786  * Callback called when a @a nym wants to enter the place.
787  *
788  * The request needs to be replied with an entry decision.
789  */
790 static void
791 host_answer_door (void *cls,
792                   struct GNUNET_SOCIAL_Nym *nym,
793                   const char *method_name,
794                   struct GNUNET_PSYC_Environment *env,
795                   const void *data,
796                   size_t data_size)
797 {
798   const struct GNUNET_CRYPTO_EcdsaPublicKey *
799     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
800   char *
801     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
802
803   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
804               "Entry request: %s\n", nym_str);
805   GNUNET_free (nym_str);
806
807   if (opt_welcome)
808   {
809     struct GNUNET_PSYC_Message *
810       resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
811                                          DATA2ARG ("Welcome, nym!"));
812     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
813     GNUNET_free (resp);
814   }
815   else if (opt_deny)
816   {
817     struct GNUNET_PSYC_Message *
818       resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
819                                          DATA2ARG ("Go away!"));
820     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
821     GNUNET_free (resp);
822   }
823
824
825 }
826
827
828 /**
829  * Callback called when a @a nym has left the place.
830  */
831 static void
832 host_farewell (void *cls,
833                const struct GNUNET_SOCIAL_Nym *nym,
834                struct GNUNET_PSYC_Environment *env)
835 {
836   const struct GNUNET_CRYPTO_EcdsaPublicKey *
837     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
838   char *
839     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
840
841   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
842               "Farewell: %s\n", nym_str);
843   GNUNET_free (nym_str);
844 }
845
846
847 /**
848  * Callback called after the host entered the place.
849  */
850 static void
851 host_entered (void *cls, int result,
852               const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
853               uint64_t max_message_id)
854 {
855   place_pub_key = *pub_key;
856   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
857   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
858               "Host entered: %s, max_message_id: %" PRIu64 "\n",
859               pub_str, max_message_id);
860   GNUNET_free (pub_str);
861
862   if (op_host_enter && !opt_follow)
863   {
864     exit_success ();
865   }
866 }
867
868
869 /**
870  * Enter and start hosting a place.
871  */
872 static void
873 host_enter ()
874 {
875   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
876
877   if (NULL == ego)
878   {
879     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
880     exit_fail ();
881     return;
882   }
883
884   hst = GNUNET_SOCIAL_host_enter (app, ego,
885                                   GNUNET_PSYC_CHANNEL_PRIVATE,
886                                   slicer_create (), host_entered,
887                                   host_answer_door, host_farewell, NULL);
888   plc = GNUNET_SOCIAL_host_get_place (hst);
889 }
890
891
892 /* PLACE RECONNECT */
893
894
895 /**
896  * Perform operations common to both host & guest places.
897  */
898 static void
899 place_reconnected ()
900 {
901   static int first_run = GNUNET_YES;
902   if (GNUNET_NO == first_run)
903     return;
904   first_run = GNUNET_NO;
905
906   if (op_replay) {
907     history_replay (opt_start, opt_until, opt_method);
908   }
909   else if (op_replay_latest) {
910     history_replay_latest (opt_limit, opt_method);
911   }
912   else if (op_look_at) {
913     look_at (opt_name);
914   }
915   else if (op_look_for) {
916     look_for (opt_name);
917   }
918 }
919
920
921 /**
922  * Callback called after reconnecting to a host place.
923  */
924 static void
925 host_reconnected (void *cls, int result,
926                   const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
927                   uint64_t max_message_id)
928 {
929   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
930               "Host reconnected.\n");
931
932   if (op_host_leave) {
933     host_leave ();
934   }
935   else if (op_host_announce) {
936     host_announce (opt_method, opt_data, strlen (opt_data));
937   }
938   else if (op_host_assign) {
939     host_assign (opt_name, opt_data, strlen (opt_data) + 1);
940   }
941   else {
942     place_reconnected ();
943   }
944 }
945
946
947 /**
948  * Callback called after reconnecting to a guest place.
949  */
950 static void
951 guest_reconnected (void *cls, int result,
952                    const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
953                    uint64_t max_message_id)
954 {
955   char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (place_pub_key);
956   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
957               "Guest reconnected to place %s.\n", place_pub_str);
958   GNUNET_free (place_pub_str);
959
960   if (op_guest_leave) {
961     guest_leave ();
962   }
963   else if (op_guest_talk) {
964     guest_talk (opt_method, opt_data, strlen (opt_data));
965   }
966   else {
967     place_reconnected ();
968   }
969 }
970
971
972 /* APP */
973
974
975 /**
976  * Callback called after the ego and place callbacks.
977  */
978 static void
979 app_connected (void *cls)
980 {
981   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
982               "App connected: %p\n", cls);
983
984   if (op_status)
985   {
986     exit_success ();
987   }
988   else if (op_host_enter)
989   {
990     host_enter ();
991   }
992   else if (op_guest_enter)
993   {
994     if (opt_gns)
995     {
996       guest_enter_by_name (opt_gns);
997     }
998     else
999     {
1000       if (opt_peer)
1001       {
1002         if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
1003                                                                      strlen (opt_peer),
1004                                                                      &peer.public_key))
1005         {
1006           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1007                       "--peer invalid");
1008           exit_fail ();
1009           return;
1010         }
1011       }
1012       else
1013       {
1014         peer = this_peer;
1015       }
1016       guest_enter (&place_pub_key, &peer);
1017     }
1018   }
1019 }
1020
1021
1022 /**
1023  * Callback notifying about a host place available for reconnection.
1024  */
1025 static void
1026 app_recv_host (void *cls,
1027                struct GNUNET_SOCIAL_HostConnection *hconn,
1028                struct GNUNET_SOCIAL_Ego *ego,
1029                const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
1030                enum GNUNET_SOCIAL_AppPlaceState place_state)
1031 {
1032   char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
1033   printf ("Host\t%s\n", host_pub_str);
1034   GNUNET_free (host_pub_str);
1035
1036   if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1037        || op_replay || op_replay_latest
1038        || op_look_at || op_look_for)
1039       && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
1040   {
1041     hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
1042                                               host_answer_door, host_farewell, NULL);
1043     plc = GNUNET_SOCIAL_host_get_place (hst);
1044   }
1045 }
1046
1047
1048 /**
1049  * Callback notifying about a guest place available for reconnection.
1050  */
1051 static void
1052 app_recv_guest (void *cls,
1053                 struct GNUNET_SOCIAL_GuestConnection *gconn,
1054                 struct GNUNET_SOCIAL_Ego *ego,
1055                 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
1056                 enum GNUNET_SOCIAL_AppPlaceState place_state)
1057 {
1058   char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
1059   printf ("Guest\t%s\n", guest_pub_str);
1060   GNUNET_free (guest_pub_str);
1061
1062   if ((op_guest_reconnect || op_guest_leave || op_guest_talk
1063        || op_replay || op_replay_latest
1064        || op_look_at || op_look_for)
1065       && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
1066   {
1067     gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
1068                                                slicer_create (), guest_reconnected, NULL);
1069     plc = GNUNET_SOCIAL_guest_get_place (gst);
1070   }
1071 }
1072
1073
1074 /**
1075  * Callback notifying about an available ego.
1076  */
1077 static void
1078 app_recv_ego (void *cls,
1079               struct GNUNET_SOCIAL_Ego *e,
1080               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
1081               const char *name)
1082 {
1083   char *s = GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key);
1084   printf ("Ego\t%s\t%s\n", s, name);
1085   GNUNET_free (s);
1086
1087   if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
1088       || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
1089   {
1090     ego = e;
1091   }
1092
1093 }
1094
1095
1096
1097 /**
1098  * Establish application connection to receive available egos and places.
1099  */
1100 static void
1101 app_connect (void *cls)
1102 {
1103   app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
1104                                    app_recv_ego,
1105                                    app_recv_host,
1106                                    app_recv_guest,
1107                                    app_connected,
1108                                    NULL);
1109 }
1110
1111
1112 /**
1113  * Main function run by the scheduler.
1114  *
1115  * @param cls closure
1116  * @param args remaining command-line arguments
1117  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1118  * @param c configuration
1119  */
1120 static void
1121 run (void *cls, char *const *args, const char *cfgfile,
1122      const struct GNUNET_CONFIGURATION_Handle *c)
1123 {
1124   cfg = c;
1125   GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
1126
1127   if (!opt_method)
1128     opt_method = "message";
1129   if (!opt_data)
1130     opt_data = "";
1131   if (!opt_name)
1132     opt_name = "";
1133
1134   if (! (op_status
1135          || op_host_enter || op_host_reconnect || op_host_leave
1136          || op_host_announce || op_host_assign
1137          || op_guest_enter || op_guest_reconnect
1138          || op_guest_leave || op_guest_talk
1139          || op_replay || op_replay_latest
1140          || op_look_at || op_look_for))
1141   {
1142     op_status = 1;
1143     fputs("Caution: This tool does not produce correct binary safe PSYC syntax.\n\n", stderr);
1144   }
1145
1146   GNUNET_SCHEDULER_add_shutdown (scheduler_shutdown, NULL);
1147   if (!opt_follow)
1148   {
1149     timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, timeout, NULL);
1150   }
1151
1152   if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1153        || op_guest_reconnect || (op_guest_enter && !opt_gns)
1154        || op_guest_leave || op_guest_talk
1155        || op_replay || op_replay_latest
1156        || op_look_at || op_look_for)
1157       && (!opt_place
1158           || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
1159                                                                       strlen (opt_place),
1160                                                                       &place_pub_key)))
1161   {
1162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1163                 _("--place missing or invalid.\n"));
1164     exit_fail ();
1165     return;
1166   }
1167
1168   if (opt_ego)
1169   {
1170     if (GNUNET_OK !=
1171         GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
1172                                                 strlen (opt_ego),
1173                                                 &ego_pub_key))
1174     {
1175       FPRINTF (stderr,
1176                _("Public key `%s' malformed\n"),
1177                opt_ego);
1178       exit_fail ();
1179       return;
1180     }
1181   }
1182
1183   GNUNET_SCHEDULER_add_now (app_connect, NULL);
1184 }
1185
1186
1187 /**
1188  * The main function to obtain peer information.
1189  *
1190  * @param argc number of arguments from the command line
1191  * @param argv command line arguments
1192  * @return 0 ok, 1 on error
1193  */
1194 int
1195 main (int argc, char *const *argv)
1196 {
1197   int res;
1198   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1199     /*
1200      * gnunet program options in addition to the ones below:
1201      *
1202      * -c, --config=FILENAME
1203      * -l, --logfile=LOGFILE
1204      * -L, --log=LOGLEVEL
1205      * -h, --help
1206      * -v, --version
1207      */
1208
1209     /* operations */
1210
1211     { 'A', "host-assign", NULL,
1212       gettext_noop ("assign --name in state to --data"),
1213       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_assign },
1214
1215     { 'B', "guest-leave", NULL,
1216       gettext_noop ("say good-bye and leave somebody else's place"),
1217       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
1218
1219     { 'C', "host-enter", NULL,
1220       gettext_noop ("create a place"),
1221       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_enter },
1222
1223     { 'D', "host-leave", NULL,
1224       gettext_noop ("destroy a place we were hosting"),
1225       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_leave },
1226
1227     { 'E', "guest-enter", NULL,
1228       gettext_noop ("enter somebody else's place"),
1229       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
1230
1231     { 'F', "look-for", NULL,
1232       gettext_noop ("find state matching name prefix"),
1233       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_for },
1234
1235     { 'H', "replay-latest", NULL,
1236       gettext_noop ("replay history of messages up to the given --limit"),
1237       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay_latest },
1238
1239     { 'N', "host-reconnect", NULL,
1240       gettext_noop ("reconnect to a previously created place"),
1241       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_reconnect },
1242
1243     { 'P', "host-announce", NULL,
1244       gettext_noop ("publish something to a place we are hosting"),
1245       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_announce },
1246
1247     { 'R', "guest-reconnect", NULL,
1248       gettext_noop ("reconnect to a previously entered place"),
1249       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_reconnect },
1250
1251     { 'S', "look-at", NULL,
1252       gettext_noop ("search for state matching exact name"),
1253       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_at },
1254
1255     { 'T', "guest-talk", NULL,
1256       gettext_noop ("submit something to somebody's place"),
1257       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_talk },
1258
1259     { 'U', "status", NULL,
1260       gettext_noop ("list of egos and subscribed places"),
1261       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_status },
1262
1263     { 'X', "replay", NULL,
1264       gettext_noop ("extract and replay history between message IDs --start and --until"),
1265       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay },
1266
1267
1268     /* options */
1269
1270     { 'a', "app", "APPLICATION_ID",
1271       gettext_noop ("application ID to use when connecting"),
1272       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_app },
1273
1274     { 'd', "data", "DATA",
1275       gettext_noop ("message body or state value"),
1276       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_data },
1277
1278     { 'e', "ego", "NAME|PUBKEY",
1279       gettext_noop ("name or public key of ego"),
1280       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_ego },
1281
1282     { 'f', "follow", NULL,
1283       gettext_noop ("wait for incoming messages"),
1284       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_follow },
1285
1286     { 'g', "gns", "GNS_NAME",
1287       gettext_noop ("GNS name"),
1288       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_gns },
1289
1290     { 'i', "peer", "PEER_ID",
1291       gettext_noop ("peer ID for --guest-enter"),
1292       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_peer },
1293
1294     { 'k', "name", "VAR_NAME",
1295       gettext_noop ("name (key) to query from state"),
1296       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_name },
1297
1298     { 'm', "method", "METHOD_NAME",
1299       gettext_noop ("method name"),
1300       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_method },
1301
1302     { 'n', "limit", NULL,
1303       gettext_noop ("number of messages to replay from history"),
1304       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_limit },
1305
1306     { 'p', "place", "PUBKEY",
1307       gettext_noop ("key address of place"),
1308       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_place },
1309
1310     { 's', "start", NULL,
1311       gettext_noop ("start message ID for history replay"),
1312       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_start },
1313
1314     { 'w', "welcome", NULL,
1315       gettext_noop ("respond to entry requests by admitting all guests"),
1316       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_welcome },
1317
1318     { 'u', "until", NULL,
1319       gettext_noop ("end message ID for history replay"),
1320       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_until },
1321
1322     { 'y', "deny", NULL,
1323       gettext_noop ("respond to entry requests by refusing all guests"),
1324       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_deny },
1325
1326     GNUNET_GETOPT_OPTION_END
1327   };
1328
1329   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1330     return 2;
1331
1332   const char *help =
1333     _ ("gnunet-social - Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
1334   const char *usage =
1335     "gnunet-social [--status]\n"
1336     "\n"
1337     "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | --deny]\n"
1338     "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | --deny]\n"
1339     "gnunet-social --host-leave --place <PUBKEY>\n"
1340     "gnunet-social --host-assign --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1341 // FIXME: some state ops not implemented yet (no hurry)
1342 //  "gnunet-social --host-augment --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1343 //  "gnunet-social --host-diminish --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1344 //  "gnunet-social --host-set --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1345     "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1346     "\n"
1347     "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME or PUBKEY> [--follow]\n"
1348     "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> [--follow]\n"
1349     "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
1350     "gnunet-social --guest-leave --place <PUBKEY>\n"
1351     "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1352     "\n"
1353     "gnunet-social --history-replay --place <PUBKEY> --start <MSGID> --until <MSGID>  [--method <METHOD_PREFIX>]\n"
1354     "gnunet-social --history-replay-latest --place <PUBKEY> --limit <MSG_LIMIT> [--method <METHOD_PREFIX>]\n"
1355     "\n"
1356     "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"
1357     "gnunet-social --look-for --place <PUBKEY> --name <NAME_PREFIX>\n";
1358
1359   res = GNUNET_PROGRAM_run (argc, argv, help, usage, options, &run, NULL);
1360
1361   GNUNET_free ((void *) argv);
1362
1363   if (GNUNET_OK == res)
1364     return ret;
1365   else
1366     return 1;
1367 }