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