-eliminate dead code
[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
158 /* DISCONNECT */
159
160
161 /**
162  * Callback called after the host or guest place disconnected.
163  */
164 static void
165 disconnected (void *cls)
166 {
167   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnected()\n");
168   GNUNET_SCHEDULER_shutdown ();
169 }
170
171
172 /**
173  * Callback called after the application disconnected.
174  */
175 static void
176 app_disconnected (void *cls)
177 {
178   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "app_disconnected()\n");
179   if (hst || gst)
180   {
181     if (hst)
182     {
183       GNUNET_SOCIAL_host_disconnect (hst, disconnected, NULL);
184     }
185     if (gst)
186     {
187       GNUNET_SOCIAL_guest_disconnect (gst, disconnected, NULL);
188     }
189   }
190   else
191   {
192     GNUNET_SCHEDULER_shutdown ();
193   }
194 }
195
196
197 /**
198  * Disconnect from connected GNUnet services.
199  */
200 static void
201 disconnect ()
202 {
203   // handle that we get called several times from several places, but should we?
204   if (!is_disconnecting++) {
205     GNUNET_SOCIAL_app_disconnect (app, app_disconnected, NULL);
206   }
207   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "disconnect() called for the #%d time\n", is_disconnecting);
208 }
209
210
211 static void
212 scheduler_shutdown (void *cls)
213 {
214   disconnect ();
215 }
216
217
218 /**
219  * Callback called when the program failed to finish the requested operation in time.
220  */
221 static void
222 timeout (void *cls)
223 {
224   GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "timeout()\n");
225   disconnect ();
226 }
227
228 static void
229 schedule_success (void *cls)
230 {
231   ret = 0;
232   disconnect ();
233 }
234
235
236 static void
237 schedule_fail (void *cls)
238 {
239   disconnect ();
240 }
241
242
243 /**
244  * Schedule exit with success result.
245  */
246 static void
247 exit_success ()
248 {
249   if (timeout_task != NULL)
250   {
251     GNUNET_SCHEDULER_cancel (timeout_task);
252     timeout_task = NULL;
253   }
254   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_success, NULL);
255 }
256
257
258 /**
259  * Schedule exit with failure result.
260  */
261 static void
262 exit_fail ()
263 {
264   if (timeout_task != NULL)
265   {
266     GNUNET_SCHEDULER_cancel (timeout_task);
267     timeout_task = NULL;
268   }
269   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, schedule_fail, NULL);
270 }
271
272
273 /* LEAVE */
274
275
276 /**
277  * Callback notifying about the host has left and stopped hosting the place.
278  *
279  * This also indicates the end of the connection to the service.
280  */
281 static void
282 host_left ()
283 {
284   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
285               "The host has left the place.\n");
286   exit_success ();
287 }
288
289
290 /**
291  * Leave a place permanently and stop hosting a place.
292  */
293 static void
294 host_leave ()
295 {
296   GNUNET_SOCIAL_host_leave (hst, NULL, host_left, NULL);
297   hst = NULL;
298   plc = NULL;
299 }
300
301
302 /**
303  * Callback notifying about the guest has left the place.
304  *
305  * This also indicates the end of the connection to the service.
306  */
307 static void
308 guest_left (void *cls)
309 {
310   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
311               "The guest has left the place.\n");
312 }
313
314
315 /**
316  * Leave a place permanently as guest.
317  */
318 static void
319 guest_leave ()
320 {
321   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
322   // FIXME: wrong use of vars
323   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
324                        "_message", DATA2ARG ("Leaving."));
325   GNUNET_SOCIAL_guest_leave (gst, env, guest_left, NULL);
326   GNUNET_PSYC_env_destroy (env);
327   gst = NULL;
328   plc = NULL;
329 }
330
331
332 /* ANNOUNCE / ASSIGN / TALK */
333
334
335 struct TransmitClosure
336 {
337   const char *data;
338   size_t size;
339 } tmit;
340
341
342 /**
343  * Callback notifying about available buffer space to write message data
344  * when transmitting messages using host_announce() or guest_talk()
345  */
346 static int
347 notify_data (void *cls, uint16_t *data_size, void *data)
348 {
349   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350               "Transmit notify data: %u bytes available\n",
351               *data_size);
352
353   struct TransmitClosure *tmit = cls;
354   uint16_t size = tmit->size < *data_size ? tmit->size : *data_size;
355   *data_size = size;
356   GNUNET_memcpy (data, tmit->data, size);
357
358   tmit->size -= size;
359   tmit->data += size;
360
361   if (0 == tmit->size)
362   {
363     if (op_host_announce || op_host_assign || op_guest_talk)
364     {
365       exit_success ();
366     }
367     return GNUNET_YES;
368   }
369   else
370   {
371     return GNUNET_NO;
372   }
373 }
374
375
376 /**
377  * Host announcement - send a message to the place.
378  */
379 static void
380 host_announce (const char *method, const char *data, size_t data_size)
381 {
382   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
383   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
384                        "_foo", DATA2ARG ("bar baz"));
385
386   tmit = (struct TransmitClosure) {};
387   tmit.data = data;
388   tmit.size = data_size;
389
390   GNUNET_SOCIAL_host_announce (hst, method, env,
391                                notify_data, &tmit,
392                                GNUNET_SOCIAL_ANNOUNCE_NONE);
393   GNUNET_PSYC_env_destroy (env);
394 }
395
396
397 /**
398  * Assign a state var of @a name to the value of @a data.
399  */
400 static void
401 host_assign (const char *name, const char *data, size_t data_size)
402 {
403   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
404   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_ASSIGN,
405                        name, data, data_size);
406
407   tmit = (struct TransmitClosure) {};
408   GNUNET_SOCIAL_host_announce (hst, "_assign", env,
409                                notify_data, &tmit,
410                                GNUNET_SOCIAL_ANNOUNCE_NONE);
411   GNUNET_PSYC_env_destroy (env);
412 }
413
414
415 /**
416  * Guest talk request to host.
417  */
418 static void
419 guest_talk (const char *method,
420             const char *data, size_t data_size)
421 {
422   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
423   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
424                        "_foo", DATA2ARG ("bar baz"));
425
426   tmit = (struct TransmitClosure) {};
427   tmit.data = data;
428   tmit.size = data_size;
429
430   GNUNET_SOCIAL_guest_talk (gst, method, env,
431                             notify_data, &tmit,
432                             GNUNET_SOCIAL_TALK_NONE);
433   GNUNET_PSYC_env_destroy (env);
434 }
435
436
437 /* HISTORY REPLAY */
438
439
440 /**
441  * Callback notifying about the end of history replay results.
442  */
443 static void
444 recv_history_replay_result (void *cls, int64_t result,
445                             const void *data, uint16_t data_size)
446 {
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448               "Received history replay result: %" PRId64 "\n"
449               "%.*s\n",
450               result, data_size, (const char *) data);
451
452   if (op_replay || op_replay_latest)
453   {
454     exit_success ();
455   }
456 }
457
458
459 /**
460  * Replay history between a given @a start and @a end message IDs,
461  * optionally filtered by a method @a prefix.
462  */
463 static void
464 history_replay (uint64_t start, uint64_t end, const char *prefix)
465 {
466   GNUNET_SOCIAL_place_history_replay (plc, start, end, prefix,
467                                       GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
468                                       slicer,
469                                       recv_history_replay_result,
470                                       NULL);
471 }
472
473
474 /**
475  * Replay latest @a limit messages.
476  */
477 static void
478 history_replay_latest (uint64_t limit, const char *prefix)
479 {
480   GNUNET_SOCIAL_place_history_replay_latest (plc, limit, prefix,
481                                              GNUNET_PSYC_HISTORY_REPLAY_LOCAL,
482                                              slicer,
483                                              recv_history_replay_result,
484                                              NULL);
485 }
486
487
488 /* LOOK AT/FOR */
489
490
491 /**
492  * Callback notifying about the end of state var results.
493  */
494 static void
495 look_result (void *cls, int64_t result_code,
496              const void *data, uint16_t data_size)
497 {
498   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
499               "Received look result: %" PRId64 "\n", result_code);
500
501   if (op_look_at || op_look_for)
502   {
503     exit_success ();
504   }
505 }
506
507
508 /**
509  * Callback notifying about a state var result.
510  */
511 static void
512 look_var (void *cls,
513           const struct GNUNET_MessageHeader *mod,
514           const char *name,
515           const void *value,
516           uint32_t value_size,
517           uint32_t full_value_size)
518 {
519   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
520               "Received var: %s\n%.*s\n",
521               name, value_size, (const char *) value);
522 }
523
524
525 /**
526  * Look for a state var using exact match of the name.
527  */
528 static void
529 look_at (const char *full_name)
530 {
531   GNUNET_SOCIAL_place_look_at (plc, full_name, look_var, look_result, NULL);
532 }
533
534
535 /**
536  * Look for state vars by name prefix.
537  */
538 static void
539 look_for (const char *name_prefix)
540 {
541   GNUNET_SOCIAL_place_look_for (plc, name_prefix, look_var, look_result, NULL);
542 }
543
544
545 /* SLICER */
546
547
548 /**
549  * Callback notifying about the start of a new incoming message.
550  */
551 static void
552 slicer_recv_method (void *cls,
553                     const struct GNUNET_PSYC_MessageHeader *msg,
554                     const struct GNUNET_PSYC_MessageMethod *meth,
555                     uint64_t message_id,
556                     const char *method_name)
557 {
558   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
559               "Received method for message ID %" PRIu64 ":\n"
560               "%s (flags: %x)\n",
561               message_id, method_name, ntohl (meth->flags));
562 }
563
564
565 /**
566  * Callback notifying about an incoming modifier.
567  */
568 static void
569 slicer_recv_modifier (void *cls,
570                       const struct GNUNET_PSYC_MessageHeader *msg,
571                       const struct GNUNET_MessageHeader *pmsg,
572                       uint64_t message_id,
573                       enum GNUNET_PSYC_Operator oper,
574                       const char *name,
575                       const void *value,
576                       uint16_t value_size,
577                       uint16_t full_value_size)
578 {
579   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
580               "Received modifier for message ID %" PRIu64 ":\n"
581               "%c%s: %.*s (size: %u)\n",
582               message_id, oper, name, value_size, (const char *) value, value_size);
583 }
584
585
586 /**
587  * Callback notifying about an incoming data fragment.
588  */
589 static void
590 slicer_recv_data (void *cls,
591                   const struct GNUNET_PSYC_MessageHeader *msg,
592                   const struct GNUNET_MessageHeader *pmsg,
593                   uint64_t message_id,
594                   const void *data,
595                   uint16_t data_size)
596 {
597   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
598               "Received data for message ID %" PRIu64 ":\n"
599               "%.*s\n",
600               message_id, data_size, (const char *) data);
601 }
602
603
604 /**
605  * Callback notifying about the end of a message.
606  */
607 static void
608 slicer_recv_eom (void *cls,
609                 const struct GNUNET_PSYC_MessageHeader *msg,
610                 const struct GNUNET_MessageHeader *pmsg,
611                 uint64_t message_id,
612                 uint8_t is_cancelled)
613 {
614   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
615               "Received end of message ID %" PRIu64
616               ", cancelled: %u\n",
617               message_id, is_cancelled);
618 }
619
620
621 /**
622  * Create a slicer for receiving message parts.
623  */
624 static struct GNUNET_PSYC_Slicer *
625 slicer_create ()
626 {
627   slicer = GNUNET_PSYC_slicer_create ();
628
629   /* register slicer to receive incoming messages with any method name */
630   GNUNET_PSYC_slicer_method_add (slicer, "", NULL,
631                                  slicer_recv_method, slicer_recv_modifier,
632                                  slicer_recv_data, slicer_recv_eom, NULL);
633   return slicer;
634 }
635
636
637 /* GUEST ENTER */
638
639
640 /**
641  * Callback called when the guest receives an entry decision from the host.
642  *
643  * It is called once after using guest_enter() or guest_enter_by_name(),
644  * in case of a reconnection only the local enter callback is called.
645  */
646 static void
647 guest_recv_entry_decision (void *cls,
648                            int is_admitted,
649                            const struct GNUNET_PSYC_Message *entry_msg)
650 {
651   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
652               "Guest received entry decision %d\n",
653               is_admitted);
654
655   if (NULL != entry_msg)
656   {
657     struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
658     const char *method_name = NULL;
659     const void *data = NULL;
660     uint16_t data_size = 0;
661     struct GNUNET_PSYC_MessageHeader *
662       pmsg = GNUNET_PSYC_message_header_create_from_psyc (entry_msg);
663     GNUNET_PSYC_message_parse (pmsg, &method_name, env, &data, &data_size);
664     GNUNET_free (pmsg);
665
666     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
667                 "%s\n%.*s\n",
668                 method_name, data_size, (const char *) data);
669   }
670
671   if (op_guest_enter && !opt_follow)
672   {
673     exit_success ();
674   }
675 }
676
677
678 /**
679  * Callback called after a guest connection is established to the local service.
680  */
681 static void
682 guest_recv_local_enter (void *cls, int result,
683                         const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
684                         uint64_t max_message_id)
685 {
686   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
687   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
688               "Guest entered to local place: %s, max_message_id: %" PRIu64 "\n",
689               pub_str, max_message_id);
690   GNUNET_free (pub_str);
691   GNUNET_assert (0 <= result);
692
693   if (op_guest_enter && !opt_follow)
694   {
695     exit_success ();
696   }
697 }
698
699
700 /**
701  * Create entry requset message.
702  */
703 static struct GNUNET_PSYC_Message *
704 guest_enter_msg_create ()
705 {
706   const char *method_name = "_request_enter";
707   struct GNUNET_PSYC_Environment *env = GNUNET_PSYC_env_create ();
708   GNUNET_PSYC_env_add (env, GNUNET_PSYC_OP_SET,
709                        "_foo", DATA2ARG ("bar"));
710   void *data = "let me in";
711   uint16_t data_size = strlen (data) + 1;
712
713   return GNUNET_PSYC_message_create (method_name, env, data, data_size);
714 }
715
716
717 /**
718  * Enter a place as guest, using its public key and peer ID.
719  */
720 static void
721 guest_enter (const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
722              const struct GNUNET_PeerIdentity *peer)
723 {
724   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
725               "Entering to place as guest.\n");
726
727   if (NULL == ego)
728   {
729     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
730     exit_fail ();
731     return;
732   }
733
734   struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
735   gst = GNUNET_SOCIAL_guest_enter (app, ego, pub_key,
736                                    GNUNET_PSYC_SLAVE_JOIN_NONE,
737                                    peer, 0, NULL, join_msg, slicer_create (),
738                                    guest_recv_local_enter,
739                                    guest_recv_entry_decision, NULL);
740   GNUNET_free (join_msg);
741   plc = GNUNET_SOCIAL_guest_get_place (gst);
742 }
743
744
745 /**
746  * Enter a place as guest using its GNS address.
747  */
748 static void
749 guest_enter_by_name (const char *gns_name)
750 {
751   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
752               "Entering to place by name as guest.\n");
753
754   struct GNUNET_PSYC_Message *join_msg = guest_enter_msg_create ();
755   gst = GNUNET_SOCIAL_guest_enter_by_name (app, ego, gns_name, NULL,
756                                            join_msg, slicer,
757                                            guest_recv_local_enter,
758                                            guest_recv_entry_decision, NULL);
759   GNUNET_free (join_msg);
760   plc = GNUNET_SOCIAL_guest_get_place (gst);
761 }
762
763
764 /* HOST ENTER */
765
766
767 /**
768  * Callback called when a @a nym wants to enter the place.
769  *
770  * The request needs to be replied with an entry decision.
771  */
772 static void
773 host_answer_door (void *cls,
774                   struct GNUNET_SOCIAL_Nym *nym,
775                   const char *method_name,
776                   struct GNUNET_PSYC_Environment *env,
777                   const void *data,
778                   size_t data_size)
779 {
780   const struct GNUNET_CRYPTO_EcdsaPublicKey *
781     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
782   char *
783     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
784
785   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
786               "Entry request: %s\n", nym_str);
787   GNUNET_free (nym_str);
788
789   if (opt_welcome)
790   {
791     struct GNUNET_PSYC_Message *
792       resp = GNUNET_PSYC_message_create ("_notice_place_admit", env,
793                                          DATA2ARG ("Welcome, nym!"));
794     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_YES, resp);
795     GNUNET_free (resp);
796   }
797   else if (opt_deny)
798   {
799     struct GNUNET_PSYC_Message *
800       resp = GNUNET_PSYC_message_create ("_notice_place_refuse", NULL,
801                                          DATA2ARG ("Go away!"));
802     GNUNET_SOCIAL_host_entry_decision (hst, nym, GNUNET_NO, resp);
803     GNUNET_free (resp);
804   }
805
806
807 }
808
809
810 /**
811  * Callback called when a @a nym has left the place.
812  */
813 static void
814 host_farewell (void *cls,
815                const struct GNUNET_SOCIAL_Nym *nym,
816                struct GNUNET_PSYC_Environment *env)
817 {
818   const struct GNUNET_CRYPTO_EcdsaPublicKey *
819     nym_key = GNUNET_SOCIAL_nym_get_pub_key (nym);
820   char *
821     nym_str = GNUNET_CRYPTO_ecdsa_public_key_to_string (nym_key);
822
823   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
824               "Farewell: %s\n", nym_str);
825   GNUNET_free (nym_str);
826 }
827
828
829 /**
830  * Callback called after the host entered the place.
831  */
832 static void
833 host_entered (void *cls, int result,
834               const struct GNUNET_CRYPTO_EddsaPublicKey *pub_key,
835               uint64_t max_message_id)
836 {
837   place_pub_key = *pub_key;
838   char *pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (pub_key);
839   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
840               "Host entered: %s, max_message_id: %" PRIu64 "\n",
841               pub_str, max_message_id);
842   GNUNET_free (pub_str);
843
844   if (op_host_enter && !opt_follow)
845   {
846     exit_success ();
847   }
848 }
849
850
851 /**
852  * Enter and start hosting a place.
853  */
854 static void
855 host_enter ()
856 {
857   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "host_enter()\n");
858
859   if (NULL == ego)
860   {
861     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "--ego missing or invalid\n");
862     exit_fail ();
863     return;
864   }
865
866   hst = GNUNET_SOCIAL_host_enter (app, ego,
867                                   GNUNET_PSYC_CHANNEL_PRIVATE,
868                                   slicer_create (), host_entered,
869                                   host_answer_door, host_farewell, NULL);
870   plc = GNUNET_SOCIAL_host_get_place (hst);
871 }
872
873
874 /* PLACE RECONNECT */
875
876
877 /**
878  * Perform operations common to both host & guest places.
879  */
880 static void
881 place_reconnected ()
882 {
883   static int first_run = GNUNET_YES;
884   if (GNUNET_NO == first_run)
885     return;
886   first_run = GNUNET_NO;
887
888   if (op_replay) {
889     history_replay (opt_start, opt_until, opt_method);
890   }
891   else if (op_replay_latest) {
892     history_replay_latest (opt_limit, opt_method);
893   }
894   else if (op_look_at) {
895     look_at (opt_name);
896   }
897   else if (op_look_for) {
898     look_for (opt_name);
899   }
900 }
901
902
903 /**
904  * Callback called after reconnecting to a host place.
905  */
906 static void
907 host_reconnected (void *cls, int result,
908                   const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
909                   uint64_t max_message_id)
910 {
911   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
912               "Host reconnected.\n");
913
914   if (op_host_leave) {
915     host_leave ();
916   }
917   else if (op_host_announce) {
918     host_announce (opt_method, opt_data, strlen (opt_data));
919   }
920   else if (op_host_assign) {
921     host_assign (opt_name, opt_data, strlen (opt_data) + 1);
922   }
923   else {
924     place_reconnected ();
925   }
926 }
927
928
929 /**
930  * Callback called after reconnecting to a guest place.
931  */
932 static void
933 guest_reconnected (void *cls, int result,
934                    const struct GNUNET_CRYPTO_EddsaPublicKey *place_pub_key,
935                    uint64_t max_message_id)
936 {
937   char *place_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (place_pub_key);
938   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939               "Guest reconnected to place %s.\n", place_pub_str);
940   GNUNET_free (place_pub_str);
941
942   if (op_guest_leave) {
943     guest_leave ();
944   }
945   else if (op_guest_talk) {
946     guest_talk (opt_method, opt_data, strlen (opt_data));
947   }
948   else {
949     place_reconnected ();
950   }
951 }
952
953
954 /* APP */
955
956
957 /**
958  * Callback called after the ego and place callbacks.
959  */
960 static void
961 app_connected (void *cls)
962 {
963   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
964               "App connected: %p\n", cls);
965
966   if (op_status)
967   {
968     exit_success ();
969   }
970   else if (op_host_enter)
971   {
972     host_enter ();
973   }
974   else if (op_guest_enter)
975   {
976     if (opt_gns)
977     {
978       guest_enter_by_name (opt_gns);
979     }
980     else
981     {
982       if (opt_peer)
983       {
984         if (GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_peer,
985                                                                      strlen (opt_peer),
986                                                                      &peer.public_key))
987         {
988           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
989                       "--peer invalid");
990           exit_fail ();
991           return;
992         }
993       }
994       else
995       {
996         peer = this_peer;
997       }
998       guest_enter (&place_pub_key, &peer);
999     }
1000   }
1001 }
1002
1003
1004 /**
1005  * Callback notifying about a host place available for reconnection.
1006  */
1007 static void
1008 app_recv_host (void *cls,
1009                struct GNUNET_SOCIAL_HostConnection *hconn,
1010                struct GNUNET_SOCIAL_Ego *ego,
1011                const struct GNUNET_CRYPTO_EddsaPublicKey *host_pub_key,
1012                enum GNUNET_SOCIAL_AppPlaceState place_state)
1013 {
1014   char *host_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (host_pub_key);
1015   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1016               "Host:  %s\n", host_pub_str);
1017   GNUNET_free (host_pub_str);
1018
1019   if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1020        || op_replay || op_replay_latest
1021        || op_look_at || op_look_for)
1022       && 0 == memcmp (&place_pub_key, host_pub_key, sizeof (*host_pub_key)))
1023   {
1024     hst = GNUNET_SOCIAL_host_enter_reconnect (hconn, slicer_create (), host_reconnected,
1025                                               host_answer_door, host_farewell, NULL);
1026     plc = GNUNET_SOCIAL_host_get_place (hst);
1027   }
1028 }
1029
1030
1031 /**
1032  * Callback notifying about a guest place available for reconnection.
1033  */
1034 static void
1035 app_recv_guest (void *cls,
1036                 struct GNUNET_SOCIAL_GuestConnection *gconn,
1037                 struct GNUNET_SOCIAL_Ego *ego,
1038                 const struct GNUNET_CRYPTO_EddsaPublicKey *guest_pub_key,
1039                 enum GNUNET_SOCIAL_AppPlaceState place_state)
1040 {
1041   char *guest_pub_str = GNUNET_CRYPTO_eddsa_public_key_to_string (guest_pub_key);
1042   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1043               "Guest: %s\n", guest_pub_str);
1044   GNUNET_free (guest_pub_str);
1045
1046   if ((op_guest_reconnect || op_guest_leave || op_guest_talk
1047        || op_replay || op_replay_latest
1048        || op_look_at || op_look_for)
1049       && 0 == memcmp (&place_pub_key, guest_pub_key, sizeof (*guest_pub_key)))
1050   {
1051     gst = GNUNET_SOCIAL_guest_enter_reconnect (gconn, GNUNET_PSYC_SLAVE_JOIN_NONE,
1052                                                slicer_create (), guest_reconnected, NULL);
1053     plc = GNUNET_SOCIAL_guest_get_place (gst);
1054   }
1055 }
1056
1057
1058 /**
1059  * Callback notifying about an available ego.
1060  */
1061 static void
1062 app_recv_ego (void *cls,
1063               struct GNUNET_SOCIAL_Ego *e,
1064               const struct GNUNET_CRYPTO_EcdsaPublicKey *pub_key,
1065               const char *name)
1066 {
1067   char *s = GNUNET_CRYPTO_ecdsa_public_key_to_string (pub_key);
1068   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Ego:   %s\t%s\n", s, name);
1069   GNUNET_free (s);
1070
1071   if (0 == memcmp (&ego_pub_key, pub_key, sizeof (*pub_key))
1072       || (NULL != opt_ego && 0 == strcmp (opt_ego, name)))
1073   {
1074     ego = e;
1075   }
1076
1077 }
1078
1079
1080
1081 /**
1082  * Establish application connection to receive available egos and places.
1083  */
1084 static void
1085 app_connect (void *cls)
1086 {
1087   app = GNUNET_SOCIAL_app_connect (cfg, opt_app,
1088                                    app_recv_ego,
1089                                    app_recv_host,
1090                                    app_recv_guest,
1091                                    app_connected,
1092                                    NULL);
1093 }
1094
1095
1096 /**
1097  * Main function run by the scheduler.
1098  *
1099  * @param cls closure
1100  * @param args remaining command-line arguments
1101  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1102  * @param c configuration
1103  */
1104 static void
1105 run (void *cls, char *const *args, const char *cfgfile,
1106      const struct GNUNET_CONFIGURATION_Handle *c)
1107 {
1108   cfg = c;
1109   GNUNET_CRYPTO_get_peer_identity (cfg, &this_peer);
1110
1111   if (!opt_method)
1112     opt_method = "message";
1113   if (!opt_data)
1114     opt_data = "";
1115   if (!opt_name)
1116     opt_name = "";
1117
1118   if (! (op_status
1119          || op_host_enter || op_host_reconnect || op_host_leave
1120          || op_host_announce || op_host_assign
1121          || op_guest_enter || op_guest_reconnect
1122          || op_guest_leave || op_guest_talk
1123          || op_replay || op_replay_latest
1124          || op_look_at || op_look_for))
1125   {
1126     op_status = 1;
1127   }
1128
1129   GNUNET_SCHEDULER_add_shutdown (scheduler_shutdown, NULL);
1130   if (!opt_follow)
1131   {
1132     timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, timeout, NULL);
1133   }
1134
1135   if ((op_host_reconnect || op_host_leave || op_host_announce || op_host_assign
1136        || op_guest_reconnect || (op_guest_enter && !opt_gns)
1137        || op_guest_leave || op_guest_talk
1138        || op_replay || op_replay_latest
1139        || op_look_at || op_look_for)
1140       && (!opt_place
1141           || GNUNET_OK != GNUNET_CRYPTO_eddsa_public_key_from_string (opt_place,
1142                                                                       strlen (opt_place),
1143                                                                       &place_pub_key)))
1144   {
1145     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1146                 _("--place missing or invalid.\n"));
1147     exit_fail ();
1148     return;
1149   }
1150
1151   if (opt_ego)
1152   {
1153     if (GNUNET_OK !=
1154         GNUNET_CRYPTO_ecdsa_public_key_from_string (opt_ego,
1155                                                 strlen (opt_ego),
1156                                                 &ego_pub_key))
1157     {
1158       FPRINTF (stderr,
1159                _("Public key `%s' malformed\n"),
1160                opt_ego);
1161       exit_fail ();
1162       return;
1163     }
1164   }
1165
1166   GNUNET_SCHEDULER_add_now (app_connect, NULL);
1167 }
1168
1169
1170 /**
1171  * The main function to obtain peer information.
1172  *
1173  * @param argc number of arguments from the command line
1174  * @param argv command line arguments
1175  * @return 0 ok, 1 on error
1176  */
1177 int
1178 main (int argc, char *const *argv)
1179 {
1180   int res;
1181   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1182     /*
1183      * gnunet program options in addition to the ones below:
1184      *
1185      * -c, --config=FILENAME
1186      * -l, --logfile=LOGFILE
1187      * -L, --log=LOGLEVEL
1188      * -h, --help
1189      * -v, --version
1190      */
1191
1192     /* operations */
1193
1194     { 'A', "host-assign", NULL,
1195       gettext_noop ("assign --name in state to --data"),
1196       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_assign },
1197
1198     { 'B', "guest-leave", NULL,
1199       gettext_noop ("say good-bye and leave somebody else's place"),
1200       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_leave },
1201
1202     { 'C', "host-enter", NULL,
1203       gettext_noop ("create a place"),
1204       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_enter },
1205
1206     { 'D', "host-leave", NULL,
1207       gettext_noop ("destroy a place we were hosting"),
1208       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_leave },
1209
1210     { 'E', "guest-enter", NULL,
1211       gettext_noop ("enter somebody else's place"),
1212       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_enter },
1213
1214     { 'F', "look-for", NULL,
1215       gettext_noop ("find state matching name prefix"),
1216       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_for },
1217
1218     { 'H', "replay-latest", NULL,
1219       gettext_noop ("replay history of messages up to the given --limit"),
1220       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay_latest },
1221
1222     { 'N', "host-reconnect", NULL,
1223       gettext_noop ("reconnect to a previously created place"),
1224       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_reconnect },
1225
1226     { 'P', "host-announce", NULL,
1227       gettext_noop ("publish something to a place we are hosting"),
1228       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_host_announce },
1229
1230     { 'R', "guest-reconnect", NULL,
1231       gettext_noop ("reconnect to a previously entered place"),
1232       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_reconnect },
1233
1234     { 'S', "look-at", NULL,
1235       gettext_noop ("search for state matching exact name"),
1236       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_look_at },
1237
1238     { 'T', "guest-talk", NULL,
1239       gettext_noop ("submit something to somebody's place"),
1240       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_guest_talk },
1241
1242     { 'U', "status", NULL,
1243       gettext_noop ("list of egos and subscribed places"),
1244       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_status },
1245
1246     { 'X', "replay", NULL,
1247       gettext_noop ("extract and replay history between message IDs --start and --until"),
1248       GNUNET_NO, &GNUNET_GETOPT_set_one, &op_replay },
1249
1250
1251     /* options */
1252
1253     { 'a', "app", "APPLICATION_ID",
1254       gettext_noop ("application ID to use when connecting"),
1255       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_app },
1256
1257     { 'd', "data", "DATA",
1258       gettext_noop ("message body or state value"),
1259       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_data },
1260
1261     { 'e', "ego", "NAME|PUBKEY",
1262       gettext_noop ("name or public key of ego"),
1263       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_ego },
1264
1265     { 'f', "follow", NULL,
1266       gettext_noop ("wait for incoming messages"),
1267       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_follow },
1268
1269     { 'g', "gns", "GNS_NAME",
1270       gettext_noop ("GNS name"),
1271       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_gns },
1272
1273     { 'i', "peer", "PEER_ID",
1274       gettext_noop ("peer ID for --guest-enter"),
1275       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_peer },
1276
1277     { 'k', "name", "VAR_NAME",
1278       gettext_noop ("name (key) to query from state"),
1279       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_name },
1280
1281     { 'm', "method", "METHOD_NAME",
1282       gettext_noop ("method name"),
1283       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_method },
1284
1285     { 'n', "limit", NULL,
1286       gettext_noop ("number of messages to replay from history"),
1287       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_limit },
1288
1289     { 'p', "place", "PUBKEY",
1290       gettext_noop ("key address of place"),
1291       GNUNET_YES, &GNUNET_GETOPT_set_string, &opt_place },
1292
1293     { 's', "start", NULL,
1294       gettext_noop ("start message ID for history replay"),
1295       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_start },
1296
1297     { 'w', "welcome", NULL,
1298       gettext_noop ("respond to entry requests by admitting all guests"),
1299       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_welcome },
1300
1301     { 'u', "until", NULL,
1302       gettext_noop ("end message ID for history replay"),
1303       GNUNET_YES, &GNUNET_GETOPT_set_ulong, &opt_until },
1304
1305     { 'y', "deny", NULL,
1306       gettext_noop ("respond to entry requests by refusing all guests"),
1307       GNUNET_NO, &GNUNET_GETOPT_set_one, &opt_deny },
1308
1309     GNUNET_GETOPT_OPTION_END
1310   };
1311
1312   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1313     return 2;
1314
1315   const char *help =
1316     _ ("gnunet-social - Interact with the social service: enter/leave, send/receive messages, access history and state.\n");
1317   const char *usage =
1318     "gnunet-social [--status]\n"
1319     "\n"
1320     "gnunet-social --host-enter --ego <NAME or PUBKEY> [--follow] [--welcome | --deny]\n"
1321     "gnunet-social --host-reconnect --place <PUBKEY> [--follow] [--welcome | --deny]\n"
1322     "gnunet-social --host-leave --place <PUBKEY>\n"
1323     "gnunet-social --host-assign --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1324 // FIXME: some state ops not implemented yet (no hurry)
1325 //  "gnunet-social --host-augment --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1326 //  "gnunet-social --host-diminish --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1327 //  "gnunet-social --host-set --place <PUBKEY> --name <NAME> --data <VALUE>\n"
1328     "gnunet-social --host-announce --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1329     "\n"
1330     "gnunet-social --guest-enter --place <PUBKEY> --peer <PEERID> --ego <NAME or PUBKEY> [--follow]\n"
1331     "gnunet-social --guest-enter --gns <GNS_NAME> --ego <NAME or PUBKEY> [--follow]\n"
1332     "gnunet-social --guest-reconnect --place <PUBKEY> [--follow]\n"
1333     "gnunet-social --guest-leave --place <PUBKEY>\n"
1334     "gnunet-social --guest-talk --place <PUBKEY> --method <METHOD_NAME> --data <MESSAGE_BODY>\n"
1335     "\n"
1336     "gnunet-social --history-replay --place <PUBKEY> --start <MSGID> --until <MSGID>  [--method <METHOD_PREFIX>]\n"
1337     "gnunet-social --history-replay-latest --place <PUBKEY> --limit <MSG_LIMIT> [--method <METHOD_PREFIX>]\n"
1338     "\n"
1339     "gnunet-social --look-at --place <PUBKEY> --name <FULL_NAME>\n"
1340     "gnunet-social --look-for --place <PUBKEY> --name <NAME_PREFIX>\n";
1341
1342   res = GNUNET_PROGRAM_run (argc, argv, help, usage, options, &run, NULL);
1343
1344   GNUNET_free ((void *) argv);
1345
1346   if (GNUNET_OK == res)
1347     return ret;
1348   else
1349     return 1;
1350 }