You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

1412 lines
37 KiB

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