/*
This file is part of GNUnet.
- (C) 2013 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2013 Christian Grothoff (and other contributing authors)
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
#include "gnunet_gnsrecord_lib.h"
#include "gnunet_conversation_service.h"
#include "gnunet_namestore_service.h"
-
+#ifdef WINDOWS
+#include "../util/gnunet-helper-w32-console.h"
+#endif
/**
* Maximum length allowed for the command line input.
*/
#define MAX_MESSAGE_LENGTH 1024
+#define XSTRINGIFY(x) STRINGIFY(x)
+
+#define STRINGIFY(x) (#x)
+
+#ifdef WINDOWS
+/**
+ * Helper that reads the console for us.
+ */
+struct GNUNET_HELPER_Handle *stdin_hlp;
+#endif
/**
* Possible states of the phone.
};
-
/**
* List of incoming calls
*/
struct GNUNET_CONVERSATION_Caller *caller;
/**
- * String identifying the caller.
+ * Public key identifying the caller.
*/
- char *caller_id;
+ struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
/**
* Unique number of the call.
/**
* Caller handle (for active incoming call).
+ * This call handler is NOT in the #cl_head / #cl_tail list.
*/
static struct CallList *cl_active;
/**
* Task which handles the commands
*/
-static GNUNET_SCHEDULER_TaskIdentifier handle_cmd_task;
+static struct GNUNET_SCHEDULER_Task * handle_cmd_task;
/**
* Our speaker.
/**
* Our ego.
*/
-static struct GNUNET_IDENTITY_Ego *caller_id;
+static struct GNUNET_IDENTITY_Ego *my_caller_id;
/**
* Handle to identity service.
static char *ego_name;
/**
- * Name of conversation partner (if any).
+ * Public key of active conversation partner (if any).
+ */
+static struct GNUNET_CRYPTO_EcdsaPublicKey peer_key;
+
+/**
+ * Name of active conversation partner (if any).
*/
static char *peer_name;
* @param cls closure
* @param code type of the event
* @param caller handle for the caller
- * @param caller_id name of the caller in GNS
+ * @param caller_id public key of the caller (in GNS)
*/
static void
phone_event_handler (void *cls,
enum GNUNET_CONVERSATION_PhoneEventCode code,
struct GNUNET_CONVERSATION_Caller *caller,
- const char *caller_id)
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *caller_id)
{
struct CallList *cl;
{
case GNUNET_CONVERSATION_EC_PHONE_RING:
FPRINTF (stdout,
- _("Incoming call from `%s'.\nPlease /accept #%u or /cancel %u the call.\n"),
- caller_id,
+ _("Incoming call from `%s'. Please /accept %u or /cancel %u the call.\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (caller_id),
caller_num_gen,
caller_num_gen);
cl = GNUNET_new (struct CallList);
cl->caller = caller;
- cl->caller_id = GNUNET_strdup (caller_id);
+ cl->caller_id = *caller_id;
cl->caller_num = caller_num_gen++;
GNUNET_CONTAINER_DLL_insert (cl_head,
cl_tail,
for (cl = cl_head; NULL != cl; cl = cl->next)
if (caller == cl->caller)
break;
+ if ( (NULL == cl) &&
+ (caller == cl_active->caller) )
+ cl = cl_active;
if (NULL == cl)
{
GNUNET_break (0);
}
FPRINTF (stdout,
_("Call from `%s' terminated\n"),
- cl->caller_id);
- GNUNET_CONTAINER_DLL_remove (cl_head,
- cl_tail,
- cl);
- GNUNET_free (cl->caller_id);
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
if (cl == cl_active)
{
cl_active = NULL;
phone_state = PS_LISTEN;
}
+ else
+ {
+ GNUNET_CONTAINER_DLL_remove (cl_head,
+ cl_tail,
+ cl);
+ }
GNUNET_free (cl);
break;
}
case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
FPRINTF (stdout,
_("Call from `%s' suspended by other user\n"),
- cl->caller_id);
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
break;
case GNUNET_CONVERSATION_EC_CALLER_RESUME:
FPRINTF (stdout,
_("Call from `%s' resumed by other user\n"),
- cl->caller_id);
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
break;
}
}
{
struct GNUNET_GNSRECORD_Data rd;
- if (NULL == caller_id)
+ if (NULL == my_caller_id)
{
FPRINTF (stderr,
_("Ego `%s' no longer available, phone is now down.\n"),
phone_state = PS_LOOKUP_EGO;
return;
}
+ GNUNET_assert (NULL == phone);
phone = GNUNET_CONVERSATION_phone_create (cfg,
- caller_id,
+ my_caller_id,
&phone_event_handler, NULL);
/* FIXME: get record and print full GNS record info later here... */
if (NULL == phone)
address = GNUNET_GNSRECORD_value_to_string (rd.record_type,
rd.data,
rd.data_size);
- if (verbose)
- FPRINTF (stdout,
- _("Phone active on line %u\n"),
- (unsigned int) line);
+ FPRINTF (stdout,
+ _("Phone active on line %u. Type `/help' for a list of available commands\n"),
+ (unsigned int) line);
phone_state = PS_LISTEN;
}
}
{
case GNUNET_CONVERSATION_EC_CALL_RINGING:
GNUNET_break (CS_RESOLVING == call_state);
- if (verbose)
- FPRINTF (stdout,
- "%s",
- _("Resolved address. Now ringing other party.\n"));
+ FPRINTF (stdout,
+ _("Resolved address of `%s'. Now ringing other party.\n"),
+ peer_name);
call_state = CS_RINGING;
break;
case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
GNUNET_break (CS_RESOLVING == call_state);
FPRINTF (stdout,
_("Failed to resolve `%s'\n"),
- ego_name);
+ peer_name);
+ GNUNET_free (peer_name);
+ peer_name = NULL;
call = NULL;
break;
case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
FPRINTF (stdout,
- "%s",
- _("Call terminated\n"));
+ _("Call to `%s' terminated\n"),
+ peer_name);
+ GNUNET_free (peer_name);
+ peer_name = NULL;
call = NULL;
break;
case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
_("Connection to `%s' resumed (by other user)\n"),
peer_name);
break;
+ case GNUNET_CONVERSATION_EC_CALL_ERROR:
+ FPRINTF (stdout,
+ _("Error with the call, restarting it\n"));
+ GNUNET_free (peer_name);
+ peer_name = NULL;
+ call = NULL;
+ break;
}
}
static void
do_call (const char *arg)
{
- if (NULL == caller_id)
+ if (NULL == my_caller_id)
{
FPRINTF (stderr,
_("Ego `%s' not available\n"),
case PS_ACCEPTED:
FPRINTF (stderr,
_("You are answering call from `%s', hang up or suspend that call first!\n"),
- peer_name);
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
return;
case PS_ERROR:
/* ok to call */
break;
}
- GNUNET_free_non_null (peer_name);
+ if (NULL == arg)
+ {
+ FPRINTF (stderr,
+ _("Call recipient missing.\n"));
+ do_help ("/call");
+ return;
+ }
peer_name = GNUNET_strdup (arg);
call_state = CS_RESOLVING;
+ GNUNET_assert (NULL == call);
call = GNUNET_CONVERSATION_call_start (cfg,
- caller_id,
+ my_caller_id,
+ my_caller_id,
arg,
speaker,
mic,
case PS_ACCEPTED:
FPRINTF (stderr,
_("You are answering call from `%s', hang up or suspend that call first!\n"),
- peer_name);
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
return;
case PS_ERROR:
GNUNET_break (0);
args);
return;
}
+ GNUNET_CONTAINER_DLL_remove (cl_head,
+ cl_tail,
+ cl);
cl_active = cl;
- GNUNET_free_non_null (peer_name);
- peer_name = GNUNET_strdup (cl->caller_id);
+ peer_key = cl->caller_id;
phone_state = PS_ACCEPTED;
GNUNET_CONVERSATION_caller_pick_up (cl->caller,
&caller_event_handler,
case PS_ACCEPTED:
FPRINTF (stdout,
_("You are having a conversation with `%s'.\n"),
- peer_name);
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));;
break;
case PS_ERROR:
FPRINTF (stdout,
FPRINTF (stdout,
_("#%u: `%s'\n"),
cl->caller_num,
- cl->caller_id);
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
}
FPRINTF (stdout,
"%s",
case PS_ERROR:
FPRINTF (stderr,
"%s",
- _("There is no call that could be suspended right now.\n"));
+ _("There is no call that could be resumed right now.\n"));
return;
case PS_LISTEN:
/* expected state, do resume logic */
case PS_ACCEPTED:
FPRINTF (stderr,
_("Already talking with `%s', cannot resume a call right now.\n"),
- peer_name);
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
return;
}
GNUNET_assert (NULL == cl_active);
GNUNET_CONTAINER_DLL_remove (cl_head,
cl_tail,
cl);
- GNUNET_free (cl->caller_id);
GNUNET_free (cl);
break;
case PS_ACCEPTED:
do_stop_task (void *cls,
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
+#ifdef WINDOWS
+ if (NULL != stdin_hlp)
+ {
+ GNUNET_HELPER_stop (stdin_hlp, GNUNET_NO);
+ stdin_hlp = NULL;
+ }
+#endif
if (NULL != call)
{
GNUNET_CONVERSATION_call_stop (call);
GNUNET_CONVERSATION_phone_destroy (phone);
phone = NULL;
}
- if (GNUNET_SCHEDULER_NO_TASK != handle_cmd_task)
+ if (NULL != handle_cmd_task)
{
GNUNET_SCHEDULER_cancel (handle_cmd_task);
- handle_cmd_task = GNUNET_SCHEDULER_NO_TASK;
+ handle_cmd_task = NULL;
}
if (NULL != id)
{
mic = NULL;
GNUNET_free (ego_name);
ego_name = NULL;
- GNUNET_CONFIGURATION_destroy (cfg);
- cfg = NULL;
GNUNET_free_non_null (peer_name);
+ peer_name = NULL;
phone_state = PS_ERROR;
}
+/**
+ * Handle user command.
+ *
+ * @param message command the user typed in
+ * @param str_len number of bytes to process in @a message
+ */
+static void
+handle_command_string (char *message,
+ size_t str_len)
+{
+ size_t i;
+ const char *ptr;
+
+ if (0 == str_len)
+ return;
+ if (message[str_len - 1] == '\n')
+ message[str_len - 1] = '\0';
+ if (message[str_len - 2] == '\r')
+ message[str_len - 2] = '\0';
+ if (0 == strlen (message))
+ return;
+ i = 0;
+ while ((NULL != commands[i].command) &&
+ (0 != strncasecmp (commands[i].command, message,
+ strlen (commands[i].command))))
+ i++;
+ ptr = &message[strlen (commands[i].command)];
+ while (isspace ((int) *ptr))
+ ptr++;
+ if ('\0' == *ptr)
+ ptr = NULL;
+ commands[i].Action (ptr);
+}
+
+
+#ifdef WINDOWS
+static int
+console_reader_chars (void *cls,
+ void *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ char *chars;
+ size_t str_size;
+ switch (ntohs (message->type))
+ {
+ case GNUNET_MESSAGE_TYPE_W32_CONSOLE_HELPER_CHARS:
+ chars = (char *) &message[1];
+ str_size = ntohs (message->size) - sizeof (struct GNUNET_MessageHeader);
+ if (chars[str_size - 1] != '\0')
+ return GNUNET_SYSERR;
+ /* FIXME: is it ok that we pass part of a const struct to
+ * this function that may mangle the contents?
+ */
+ handle_command_string (chars, str_size - 1);
+ break;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+ return GNUNET_OK;
+}
+#endif
+
/**
* Task to handle commands from the terminal.
*
const struct GNUNET_SCHEDULER_TaskContext *tc)
{
char message[MAX_MESSAGE_LENGTH + 1];
- const char *ptr;
- size_t i;
handle_cmd_task =
GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
memset (message, 0, MAX_MESSAGE_LENGTH + 1);
if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
return;
- if (0 == strlen (message))
- return;
- if (message[strlen (message) - 1] == '\n')
- message[strlen (message) - 1] = '\0';
- if (0 == strlen (message))
- return;
- i = 0;
- while ((NULL != commands[i].command) &&
- (0 != strncasecmp (commands[i].command, message,
- strlen (commands[i].command))))
- i++;
- ptr = &message[strlen (commands[i].command)];
- while (isspace ((int) *ptr))
- ptr++;
- commands[i].Action (ptr);
+ handle_command_string (message, strlen (message));
}
{
if (NULL == name)
return;
- if (ego == caller_id)
+ if (ego == my_caller_id)
{
if (verbose)
FPRINTF (stdout,
FPRINTF (stdout,
_("Our ego `%s' was deleted!\n"),
ego_name);
- caller_id = NULL;
+ my_caller_id = NULL;
return;
}
- caller_id = ego;
+ my_caller_id = ego;
GNUNET_CONFIGURATION_set_value_number (cfg,
"CONVERSATION",
"LINE",
id = GNUNET_IDENTITY_connect (cfg,
&identity_cb,
NULL);
+#ifdef WINDOWS
+ if (stdin_fh == NULL)
+ {
+ static char cpid[64];
+ static char *args[] = {"gnunet-helper-w32-console.exe", "chars",
+ XSTRINGIFY (MAX_MESSAGE_LENGTH), cpid, NULL};
+ snprintf (cpid, 64, "%d", GetCurrentProcessId ());
+ stdin_hlp = GNUNET_HELPER_start (
+ GNUNET_NO,
+ "gnunet-helper-w32-console",
+ args,
+ console_reader_chars,
+ NULL,
+ NULL);
+ if (NULL == stdin_hlp)
+ {
+ FPRINTF (stderr,
+ "%s",
+ _("Failed to start gnunet-helper-w32-console\n"));
+ return;
+ }
+ }
+ else
+#endif
handle_cmd_task =
GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI,
&handle_command, NULL);
1, &GNUNET_GETOPT_set_uint, &line},
GNUNET_GETOPT_OPTION_END
};
- int flags;
int ret;
-
+#ifndef WINDOWS
+ int flags;
flags = fcntl (0, F_GETFL, 0);
flags |= O_NONBLOCK;
- fcntl (0, F_SETFL, flags);
+ if (0 != fcntl (0, F_SETFL, flags))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "fcntl");
stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
+#else
+ if (FILE_TYPE_CHAR == GetFileType ((HANDLE) _get_osfhandle (0)))
+ {
+ stdin_fh = NULL;
+ }
+ else
+ stdin_fh = GNUNET_DISK_get_handle_from_int_fd (0);
+#endif
+
if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
return 2;
ret = GNUNET_PROGRAM_run (argc, argv,
gettext_noop ("Enables having a conversation with other GNUnet users."),
options, &run, NULL);
GNUNET_free ((void *) argv);
+ if (NULL != cfg)
+ {
+ GNUNET_CONFIGURATION_destroy (cfg);
+ cfg = NULL;
+ }
return (GNUNET_OK == ret) ? 0 : 1;
}