cmdline usability for gnunet-social
[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 #include "gnunet_core_service.h"
33
34 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
35
36 #define DATA2ARG(data) data, sizeof (data)
37
38 /* operations corresponding to API calls */
39
40 /** --status */
41 static int op_status;
42
43 /** --host-enter */
44 static int op_host_enter;
45
46 /** --host-reconnect */
47 static int op_host_reconnect;
48
49 /** --host-leave */
50 static int op_host_leave;
51
52 /** --host-announce */
53 static int op_host_announce;
54
55 /** --guest-enter */
56 static int op_guest_enter;
57
58 /** --guest-reconnect */
59 static int op_guest_reconnect;
60
61 /** --guest-leave */
62 static int op_guest_leave;
63
64 /** --guest-talk */
65 static int op_guest_talk;
66
67 /** --replay */
68 static char *op_replay;
69
70 /** --replay-latest */
71 static char *op_replay_latest;
72
73 /** --look-at */
74 static int op_look_at;
75
76 /** --look-for */
77 static int op_look_for;
78
79
80 /* options */
81
82 /** --app */
83 static char *opt_app = "cli";
84
85 /** --place */
86 static char *opt_place;
87
88 /** --ego */
89 static char *opt_ego;
90
91 /** --gns */
92 static char *opt_gns;
93
94 /** --peer */
95 static char *opt_peer;
96
97 /** --follow */
98 static int opt_follow;
99
100 /** --welcome */
101 static int opt_welcome;
102
103 /** --deny */
104 static int opt_deny;
105
106 /** --method */
107 static char *opt_method;
108
109 /** --body */
110 // FIXME: should come from STDIN
111 static char *opt_body;
112
113 /** --name */
114 static char *opt_name;
115
116 /** --start */
117 static uint64_t opt_start;
118
119 /** --until */
120 static uint64_t opt_until;
121
122 /** --limit */
123 static int opt_limit;
124
125
126 /* global vars */
127
128 /** exit code */
129 static int ret = 1;
130
131 /** Task handle for timeout termination. */
132 struct GNUNET_SCHEDULER_Task *timeout_task;
133
134 const struct GNUNET_CONFIGURATION_Handle *cfg;
135
136 struct GNUNET_CORE_Handle *core;
137 struct GNUNET_PeerIdentity peer, this_peer;
138
139 struct GNUNET_SOCIAL_App *app;
140
141 /** public key of connected place */
142 struct GNUNET_CRYPTO_EddsaPublicKey place_pub_key;
143
144 struct GNUNET_PSYC_Slicer *slicer;
145
146 struct GNUNET_SOCIAL_Ego *ego;
147 struct GNUNET_CRYPTO_EcdsaPublicKey ego_pub_key;
148
149 struct GNUNET_SOCIAL_Host *hst;
150 struct GNUNET_SOCIAL_Guest *gst;
151 struct GNUNET_SOCIAL_Place *plc;
152
153
154 /* DISCONNECT */
155
156
157 static void
158 disconnect ()
159 {
160   if (hst)
161   {
162     GNUNET_SOCIAL_host_disconnect (hst, NULL, NULL);
163   }
164   if (gst)
165   {
166     GNUNET_SOCIAL_guest_disconnect (gst, NULL, NULL);
167   }
168
169   GNUNET_SOCIAL_app_disconnect (app);
170   GNUNET_CORE_disconnect (core);
171   GNUNET_SCHEDULER_shutdown ();
172 }
173
174 /**
175  * Terminate the test case (failure).
176  *
177  * @param cls NULL
178  */
179 static void
180 timeout (void *cls)
181 {
182   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Timeout.\n");
183   disconnect ();
184 }
185
186 static void
187 schedule_success (void *cls)
188 {
189   ret = 0;
190   disconnect ();
191 }
192
193
194 static void
195 schedule_fail (void *cls)
196 {
197   disconnect ();
198 }
199
200
201 static void
202 exit_success ()
203 {
204   if (timeout_task != NULL)
205   {
206     GNUNET_SCHEDULER_cancel (timeout_task);
207     timeout_task = NULL;
208   }
209   GNUNET_SCHEDULER_add_now (&schedule_success, NULL);
210 }
211
212
213 static void
214 exit_fail ()
215 {
216   if (timeout_task != NULL)
217   {
218     GNUNET_SCHEDULER_cancel (timeout_task);
219     timeout_task = NULL;
220   }
221   GNUNET_SCHEDULER_add_now (&schedule_fail, NULL);
222 }
223
224
225 /* LEAVE */
226
227
228 static void
229 host_left ()
230 {
231   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
232               "The host has left the place.\n");
233   exit_success ();
234 }
235
236
237 static void
238 host_leave ()
239 {
240   GNUNET_SOCIAL_host_leave (hst, NULL, &host_left, NULL);
241   hst = NULL;
242   plc = NULL;
243 }
244
245
246 static void
247 guest_left (void *cls)
248 {
249   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
250               "The guest has left the place.\n");
251 }
252
253
254 static void
255 guest_leave ()
256 {
257   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
258     // method in the middle of vars? FIXME
259   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
260                        "_notice_place_leave", DATA2ARG ("Leaving."));
261   GNUNET_SOCIAL_guest_leave (gst, env, &guest_left, NULL);
262   GNUNET_PSYC_env_destroy (env);
263   gst = NULL;
264   plc = NULL;
265 }
266
267
268 /* ANNOUNCE / TALK */
269
270
271 struct TransmitClosure
272 {
273   const char *data;
274   size_t size;
275 } tmit;
276
277
278 static int
279 notify_data (void *cls, uint16_t *data_size, void *data)
280 {
281   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
282               "Transmit notify data: %u bytes available\n",
283               *data_size);
284
285   struct TransmitClosure *tmit = cls;
286   uint16_t size = tmit->size < *data_size ? tmit->size : *data_size;
287   *data_size = size;
288   memcpy (data, tmit->data, size);
289
290   tmit->size -= size;
291   tmit->data += size;
292
293   if (0 == tmit->size)
294   {
295     if (op_host_announce || op_guest_talk)
296     {
297       exit_success ();
298     }
299     return GNUNET_NO;
300   }
301   else
302   {
303     return GNUNET_YES;
304   }
305 }
306
307
308 static void
309 host_announce (const char *method, const char *data, size_t data_size)
310 {
311   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
312   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
313                        "_foo", DATA2ARG ("bar baz"));
314
315   tmit = (struct TransmitClosure) {};
316   tmit.data = data;
317   tmit.size = data_size;
318
319   GNUNET_SOCIAL_host_announce (hst, method, env,
320                                &notify_data, &tmit,
321                                GNUNET_SOCIAL_ANNOUNCE_NONE);
322 }
323
324
325 static void
326 guest_talk (const char *method,
327             const char *data, size_t data_size)
328 {
329   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
330   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
331                        "_foo", DATA2ARG ("bar baz"));
332
333   tmit = (struct TransmitClosure) {};
334   tmit.data = data;
335   tmit.size = data_size;
336
337   GNUNET_SOCIAL_guest_talk (gst, method, env,
338                             &notify_data, &tmit,
339                             GNUNET_SOCIAL_TALK_NONE);
340 }
341
342
343 /* HISTORY REPLAY */
344
345
346 static void
347 recv_history_replay_result (void *cls, int64_t result,
348                             const void *data, uint16_t data_size)
349 {
350   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
351               "Received history replay result: %" PRId64 "\n"
352               "%.*s\n",
353               result, data_size, (const char *) data);
354
355   if (op_replay || op_replay_latest)
356   {
357     exit_success ();
358   }
359 }
360
361
362 static void
363 history_replay (uint64_t start, uint64_t end, const char *prefix)
364 {
365   GNUNET_SOCIAL_place_history_replay (plc, start, end, prefix,
366                                       GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
367                                       slicer,
368                                       &recv_history_replay_result,
369                                       NULL);
370 }
371
372
373 static void
374 history_replay_latest (uint64_t limit, const char *prefix)
375 {
376   GNUNET_SOCIAL_place_history_replay_latest (plc, limit, prefix,
377                                              GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
378                                              slicer,
379                                              &recv_history_replay_result,
380                                              NULL);
381 }
382
383
384 /* LOOK AT/FOR */
385
386
387 static void
388 look_result (void *cls, int64_t result_code,
389              const void *data, uint16_t data_size)
390 {
391   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
392               "Received look result: %" PRId64 "\n", result_code);
393
394   if (op_look_at || op_look_for)
395   {
396     exit_success ();
397   }
398 }
399
400
401 static void
402 look_var (void *cls,
403           const struct GNUNET_MessageHeader *mod,
404           const char *name,
405           const void *value,
406           uint32_t value_size,
407           uint32_t full_value_size)
408 {
409   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
410               "look_at_var: %s\n%.*s\n",
411               name, value_size, (const char *) value);
412 }
413
414
415 static void
416 look_at (const char *name)
417 {
418   GNUNET_SOCIAL_place_look_at (plc, name, look_var, look_result, NULL);
419 }
420
421
422 static void
423 look_for (const char *name)
424 {
425   GNUNET_SOCIAL_place_look_for (plc, name, look_var, look_result, NULL);
426 }
427
428
429 /* SLICER */
430
431
432 static void
433 slicer_recv_method (void *cls,
434                     const struct GNUNET_PSYC_MessageHeader *msg,
435                     const struct GNUNET_PSYC_MessageMethod *meth,
436                     uint64_t message_id,
437                     const char *method_name)
438 {
439   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
440               "Received method for message ID %" PRIu64 ":\n"
441               "%s (flags: %x)\n",
442               message_id, method_name, ntohl (meth->flags));
443 }
444
445
446 static void
447 slicer_recv_modifier (void *cls,
448                       const struct GNUNET_PSYC_MessageHeader *msg,
449                       const struct GNUNET_MessageHeader *pmsg,
450                       uint64_t message_id,
451                       enum GNUNET_PSYC_Operator oper,
452                       const char *name,
453                       const void *value,
454                       uint16_t value_size,
455                       uint16_t full_value_size)
456 {
457   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
458               "Received modifier for message ID %" PRIu64 ":\n"
459               "%c%s: %.*s (size: %u)\n",
460               message_id, oper, name, value_size, (const char *) value, value_size);
461 }
462
463
464 static void
465 slicer_recv_data (void *cls,
466                   const struct GNUNET_PSYC_MessageHeader *msg,
467                   const struct GNUNET_MessageHeader *pmsg,
468                   uint64_t message_id,
469                   const void *data,
470                   uint16_t data_size)
471 {
472   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
473               "Received data for message ID %" PRIu64 ":\n"
474               "%.*s\n",
475               message_id, data_size, (const char *) data);
476 }
477
478
479 static void
480 slicer_recv_eom (void *cls,
481                 const struct GNUNET_PSYC_MessageHeader *msg,
482                 const struct GNUNET_MessageHeader *pmsg,
483                 uint64_t message_id,
484                 uint8_t is_cancelled)
485 {
486   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
487               "Received end of message ID %" PRIu64
488               ", cancelled: %u\n",
489               message_id, is_cancelled);
490 }
491
492
493 static struct GNUNET_PSYC_Slicer *
494 slicer_create ()
495 {
496   slicer = GNUNET_PSYC_slicer_create ();
497
498   /* register slicer to receive incoming messages with any method name */
499   GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
500                                  slicer_recv_method, slicer_recv_modifier,
501                                  slicer_recv_data, slicer_recv_eom, NULL);
502   return slicer;
503 }
504
505
506 /* GUEST ENTER */
507
508
509 static void
510 guest_recv_entry_decision (void *cls,
511                            int is_admitted,
512                            const struct GNUNET_PSYC_Message *entry_msg)
513 {
514   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515               "Guest received entry decision %d\n",
516               is_admitted);
517
518   if (NULL != entry_msg)
519   {
520     struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
521     const char *method_name = NULL;
522     const void *data = NULL;
523     uint16_t data_size = 0;
524     struct GNUNET_PSYC_MessageHeader *
525       pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
526     GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
527     GNUNET_free (pmsg);
528
529     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
530                 "%s\n%.*s\n",
531                 method_name, data_size, (const char *) data);
532   }
533
534   if (op_guest_enter && !opt_follow)
535   {
536     exit_success ();
537   }
538 }
539
540
541 static void
542 guest_recv_local_enter (void *cls, int result,
543                         const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
544                         uint64_t max_message_id)
545 {
546   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
547   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
548               "Guest entered to local place: %s, max_message_id: %" PRIu64 "\n",
549               pub_str, max_message_id);
550   GNUNET_assert (0 <= result);
551
552   if (op_guest_enter && !opt_follow)
553   {
554     exit_success ();
555   }
556 }
557
558
559 static struct GNUNET_PSYC_Message *
560 guest_enter_msg_create ()
561 {
562   const char *method_name = "_request_enter";
563   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
564   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
565                        "_foo", DATA2ARG ("bar"));
566   void *data = "let me in";
567   uint16_t data_size = strlen (data) + 1;
568
569   return GNUNET_PSYC_message_create (method_name, env, data, data_size);
570 }
571
572
573 static void
574 guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
575              const struct GNUNET_PeerIdentity *peer)
576 {
577   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
578               "Entering to place as guest.\n");
579
580   if (NULL == ego)
581   {
582     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
583     exit_fail ();
584     return;
585   }
586
587   gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
588                                    GNUNET_PSYC_SLAVE_JOIN_NONE,
589                                    peer, 0, NULL, guest_enter_msg_create (),
590                                    slicer_create (),
591                                    guest_recv_local_enter,
592                                    guest_recv_entry_decision, NULL);
593   plc = GNUNET_SOCIAL_guest_get_place (gst);
594 }
595
596
597 static void
598 guest_enter_by_name (const char *gns_name)
599 {
600   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601               "Entering to place by name as guest.\n");
602
603   gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
604                                            guest_enter_msg_create (), slicer,
605                                            guest_recv_local_enter,
606                                            guest_recv_entry_decision, NULL);
607   plc = GNUNET_SOCIAL_guest_get_place (gst);
608 }
609
610
611 /* HOST ENTER */
612
613
614 static void
615 host_answer_door (void *cls,
616                   struct GNUNET_SOCIAL_Nym *nym,
617                   const char *method_name,
618                   struct GNUNET_PSYC_Environment *env,
619                   const void *data,
620                   size_t data_size)
621 {
622   const struct GNUNET_CRYPTO_EcdsaPublicKey *
623     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
624   char *
625     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
626
627   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
628               "Entry request: %s\n", nym_str);
629   GNUNET_free (nym_str);
630
631   if (opt_welcome)
632   {
633     struct GNUNET_PSYC_Message *
634       resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
635                                          DATA2ARG ("Welcome, nym!"));
636     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
637     GNUNET_free (resp);
638   }
639   else if (opt_deny)
640   {
641     struct GNUNET_PSYC_Message *
642       resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
643                                          DATA2ARG ("Go away!"));
644     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
645     GNUNET_free (resp);
646   }
647
648
649 }
650
651
652 static void
653 host_farewell (void *cls,
654                const struct GNUNET_SOCIAL_Nym *nym,
655                struct GNUNET_PSYC_Environment *env)
656 {
657   const struct GNUNET_CRYPTO_EcdsaPublicKey *
658     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
659   char *
660     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
661
662   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
663               "Farewell: %s\n", nym_str);
664   GNUNET_free (nym_str);
665 }
666
667
668 static void
669 host_entered (void *cls, int result,
670               const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
671               uint64_t max_message_id)
672 {
673   place_pub_key = *pub_key;
674   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
675   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
676               "Host entered: %s, max_message_id: %" PRIu64 "\n",
677               pub_str, max_message_id);
678
679   if (op_host_enter && !opt_follow)
680   {
681     exit_success ();
682   }
683 }
684
685
686 static void
687 host_enter ()
688 {
689   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
690
691   if (NULL == ego)
692   {
693     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
694     exit_fail ();
695     return;
696   }
697
698   hst = GNUNET_SOCIAL_host_enter (app, ego,
699                                   GNUNET_PSYC_CHANNEL_PRIVATE,
700                                   slicer_create (), host_entered,
701                                   host_answer_door, host_farewell, NULL);
702   plc = GNUNET_SOCIAL_host_get_place (hst);
703 }
704
705
706 /* PLACE RECONNECT */
707
708
709 static void
710 place_reconnected ()
711 {
712   if (op_replay) {
713     history_replay (opt_start, opt_until, opt_method);
714   }
715   else if (op_replay_latest) {
716     history_replay_latest (opt_limit, opt_method);
717   }
718   else if (op_look_at) {
719     look_at (opt_name);
720   }
721   else if (op_look_for) {
722     look_for (opt_name);
723   }
724 }
725
726
727 static void
728 host_reconnected (void *cls, int result,
729                   const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
730                   uint64_t max_message_id)
731 {
732   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
733               "Host reconnected.\n");
734
735   if (op_host_leave) {
736     host_leave ();
737   }
738   else if (op_host_announce) {
739     host_announce (opt_method, opt_body, strlen (opt_body));
740   }
741   else {
742     place_reconnected ();
743   }
744 }
745
746
747 static void
748 guest_reconnected (void *cls, int result,
749                    const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
750                    uint64_t max_message_id)
751 {
752   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
753               "Guest reconnected.\n");
754
755   if (op_guest_leave) {
756     guest_leave ();
757   }
758   else if (op_guest_talk) {
759     guest_talk (opt_method, opt_body, strlen (opt_body));
760   }
761   else {
762     place_reconnected ();
763   }
764 }
765
766
767 /* APP */
768
769
770 static void
771 app_connected (void *cls)
772 {
773   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774               "App connected: %p\n", cls);
775
776   if (op_status)
777   {
778     exit_success ();
779   }
780   else if (op_host_enter)
781   {
782     host_enter ();
783   }
784   else if (op_guest_enter)
785   {
786     if (opt_gns)
787     {
788       guest_enter_by_name (opt_gns);
789     }
790     else
791     {
792       if (opt_peer)
793       {
794         if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
795                                                                      strlen (opt_peer),
796                                                                      &peer.public_key))
797         {
798           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
799                       "--peer invalid");
800           exit_fail ();
801           return;
802         }
803       }
804       else
805       {
806         peer = this_peer;
807       }
808       guest_enter (&place_pub_key, &peer);
809     }
810   }
811 }
812
813
814 static void
815 app_recv_host (void *cls,
816                struct GNUNET_SOCIAL_HostConnection *hconn,
817                struct GNUNET_SOCIAL_Ego *ego,
818                const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
819                enum GNUNET_SOCIAL_AppPlaceState place_state)
820 {
821   char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
822   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
823               "Host:  %s\n", host_pub_str);
824   GNUNET_free (host_pub_str);
825
826   if ((op_host_reconnect || op_host_leave || op_host_announce
827        || op_replay || op_replay_latest
828        || op_look_at || op_look_for)
829       && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
830   {
831     hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
832                                               host_answer_door, host_farewell, NULL);
833     plc = GNUNET_SOCIAL_host_get_place (hst);
834   }
835 }
836
837
838 static void
839 app_recv_guest (void *cls,
840                 struct GNUNET_SOCIAL_GuestConnection *gconn,
841                 struct GNUNET_SOCIAL_Ego *ego,
842                 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
843                 enum GNUNET_SOCIAL_AppPlaceState place_state)
844 {
845   char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
846   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
847               "Guest: %s\n", guest_pub_str);
848   GNUNET_free (guest_pub_str);
849
850   if ((op_guest_reconnect || op_guest_leave || op_guest_talk
851        || op_replay || op_replay_latest
852        || op_look_at || op_look_for)
853       && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
854   {
855     gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
856                                                slicer_create (), guest_reconnected, NULL);
857     plc = GNUNET_SOCIAL_guest_get_place (gst);
858   }
859 }
860
861
862 static void
863 app_recv_ego (void *cls,
864               struct GNUNET_SOCIAL_Ego *e,
865               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
866               const char *name)
867 {
868   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
869               "Ego:   %s\t%s\n",
870               GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key), name);
871
872   if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
873       || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
874   {
875     ego = e;
876   }
877
878 }
879
880
881 static void
882 app_connect ()
883 {
884   app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
885                                    app_recv_ego,
886                                    app_recv_host,
887                                    app_recv_guest,
888                                    app_connected,
889                                    NULL);
890 }
891
892
893 /* CORE */
894
895
896 static void
897 core_connected (void *cls, const struct GNUNET_PeerIdentity *my_identity)
898 {
899   this_peer = *my_identity;
900   app_connect ();
901 }
902
903
904 /* RUN */
905
906
907 /**
908  * Main function run by the scheduler.
909  *
910  * @param cls closure
911  * @param args remaining command-line arguments
912  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
913  * @param cfg configuration
914  */
915 static void
916 run (void *cls, char *const *args, const char *cfgfile,
917      const struct GNUNET_CONFIGURATION_Handle *c)
918 {
919   cfg = c;
920
921   if (!opt_method)
922     opt_method = "message";
923   if (!opt_body)
924     opt_body = "";
925   if (!opt_name)
926     opt_name = "";
927
928   if (! (op_status
929          || op_host_enter || op_host_reconnect || op_host_leave || op_host_announce
930          || op_guest_enter || op_guest_reconnect || op_guest_leave || op_guest_talk
931          || op_replay || op_replay_latest
932          || op_look_at || op_look_for))
933   {
934     op_status = 1;
935   }
936
937   if (!opt_follow)
938   {
939     timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &timeout, NULL);
940   }
941
942   if ((op_host_reconnect || op_host_leave || op_host_announce
943        || op_guest_reconnect || (op_guest_enter && !opt_gns)
944        || op_guest_leave || op_guest_talk
945        || op_replay || op_replay_latest
946        || op_look_at || op_look_for)
947       && (!opt_place
948           || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
949                                                                       strlen (opt_place),
950                                                                       &place_pub_key)))
951   {
952     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
953                 _("--place missing or invalid.\n"));
954     exit_fail ();
955     return;
956   }
957
958   if (opt_ego)
959   {
960     GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
961                                                 strlen (opt_ego),
962                                                 &ego_pub_key);
963   }
964
965   core = GNUNET_CORE_connect (cfg, NULL, &core_connected, NULL, NULL,
966                               NULL, GNUNET_NO, NULL, GNUNET_NO, NULL);
967 }
968
969
970 /**
971  * The main function to obtain peer information.
972  *
973  * @param argc number of arguments from the command line
974  * @param argv command line arguments
975  * @return 0 ok, 1 on error
976  */
977 int
978 main (int argc, char *const *argv)
979 {
980   int res;
981   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
982     /*
983      * gnunet program options in addition to the ones below:
984      *
985      * -c, --config=FILENAME
986      * -l, --logfile=LOGFILE
987      * -L, --log=LOGLEVEL
988      * -h, --help
989      * -v, --version
990      */
991
992     /* operations */
993
994     { 'B', "guest-leave", NULL,
995       gettext_noop ("say good-bye and leave somebody else's place"),
996       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
997
998     { 'C', "host-enter", NULL,
999       gettext_noop ("create a place"),
1000       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_enter },
1001
1002     { 'D', "host-leave", NULL,
1003       gettext_noop ("destroy a place we were hosting"),
1004       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_leave },
1005
1006     { 'E', "guest-enter", NULL,
1007       gettext_noop ("enter somebody else's place"),
1008       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
1009
1010     { 'F', "look-for", NULL,
1011       gettext_noop ("find state matching name prefix"),
1012       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_for },
1013
1014     { 'H', "replay-latest", NULL,
1015       gettext_noop ("replay history of messages up to the given --limit"),
1016       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay_latest },
1017
1018     { 'N', "host-reconnect", NULL,
1019       gettext_noop ("reconnect to a previously created place"),
1020       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_reconnect },
1021
1022     { 'P', "host-announce", NULL,
1023       gettext_noop ("publish something to a place we are hosting"),
1024       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_announce },
1025
1026     { 'R', "guest-reconnect", NULL,
1027       gettext_noop ("reconnect to a previously entered place"),
1028       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_reconnect },
1029
1030     { 'S', "look-at", NULL,
1031       gettext_noop ("search for state matching exact name"),
1032       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_at },
1033
1034     { 'T', "guest-talk", NULL,
1035       gettext_noop ("submit something to somebody's place"),
1036       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_talk },
1037
1038     { 'U', "status", NULL,
1039       gettext_noop ("list of egos and subscribed places"),
1040       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_status },
1041
1042     { 'X', "replay", NULL,
1043       gettext_noop ("extract and replay history between message IDs --start and --until"),
1044       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay },
1045
1046
1047     /* options */
1048
1049     { 'a', "app", "APPLICATION_ID",
1050       gettext_noop ("application ID to use when connecting"),
1051       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_app },
1052
1053     { 'b', "body", "MESSAGE_BODY",
1054       gettext_noop ("message body to transmit"),
1055       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_body },
1056
1057     { 'e', "ego", "NAME|PUBKEY",
1058       gettext_noop ("name or public key of ego"),
1059       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_ego },
1060
1061     { 'f', "follow", NULL,
1062       gettext_noop ("wait for incoming messages"),
1063       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_follow },
1064
1065     { 'g', "gns", "GNS_NAME",
1066       gettext_noop ("GNS name"),
1067       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_gns },
1068
1069     { 'i', "peer", "PEER_ID",
1070       gettext_noop ("peer ID for --guest-enter"),
1071       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_peer },
1072
1073     { 'k', "name", "VAR_NAME",
1074       gettext_noop ("state variable name (key) to query"),
1075       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_name },
1076
1077     { 'm', "method", "METHOD_NAME",
1078       gettext_noop ("method name"),
1079       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_method },
1080
1081     { 'n', "limit", NULL,
1082       gettext_noop ("number of messages to replay from history"),
1083       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_limit },
1084
1085     { 'p', "place", "PUBKEY",
1086       gettext_noop ("key address of place"),
1087       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_place },
1088
1089     { 's', "start", NULL,
1090       gettext_noop ("start message ID for history replay"),
1091       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_start },
1092
1093     { 'w', "welcome", NULL,
1094       gettext_noop ("respond to entry requests by admitting all guests"),
1095       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_welcome },
1096
1097     { 'u', "until", NULL,
1098       gettext_noop ("end message ID for history replay"),
1099       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_until },
1100
1101     { 'y', "deny", NULL,
1102       gettext_noop ("respond to entry requests by refusing all guests"),
1103       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_deny },
1104
1105     GNUNET_GETOPT_OPTION_END
1106   };
1107
1108   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1109     return 2;
1110
1111   const char *help =
1112     _ ("gnunet-social - Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
1113   const char *usage =
1114     "gnunet-social [--status]\n"
1115     "\n"
1116     "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | --deny]\n"
1117     "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | --deny]\n"
1118     "gnunet-social --host-leave --place <PUBKEY>\n"
1119     "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --body <MESSAGE_BODY>\n"
1120     "\n"
1121     "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME or PUBKEY> [--follow]\n"
1122     "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> [--follow]\n"
1123     "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
1124     "gnunet-social --guest-leave --place <PUBKEY>\n"
1125     "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --body <MESSAGE_BODY>\n"
1126     "\n"
1127     "gnunet-social --history-replay --place <PUBKEY> --start <MSGID> --until <MSGID>  [--method <METHOD_PREFIX>]\n"
1128     "gnunet-social --history-replay-latest --place <PUBKEY> --limit <MSG_LIMIT> [--method <METHOD_PREFIX>]\n"
1129     "\n"
1130     "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"
1131     "gnunet-social --look-for --place <PUBKEY> --name <NAME_PREFIX>\n";
1132
1133   res = GNUNET_PROGRAM_run (argc, argv, help, usage, options, &run, NULL);
1134
1135   GNUNET_free ((void *) argv);
1136
1137   if (GNUNET_OK == res)
1138     return ret;
1139   else
1140     return 1;
1141 }