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