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