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