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