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