/*
This file is part of GNUnet.
- (C) 2013 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2013 GNUnet e.V.
GNUnet is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published
You should have received a copy of the GNU General Public License
along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
*/
/**
* @file conversation/gnunet-conversation.c
#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;
static struct CallList *cl_tail;
/**
- * Desired phone line.
+ * Desired phone line (string to be converted to a hash).
*/
-static unsigned int line;
+static char *line;
/**
* 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'. Please /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"),
}
GNUNET_assert (NULL == phone);
phone = GNUNET_CONVERSATION_phone_create (cfg,
- caller_id,
- &phone_event_handler, NULL);
+ my_caller_id,
+ &phone_event_handler,
+ NULL);
/* FIXME: get record and print full GNS record info later here... */
if (NULL == phone)
{
rd.data,
rd.data_size);
FPRINTF (stdout,
- _("Phone active on line %u. Type `/help' for a list of available commands\n"),
- (unsigned int) line);
+ _("Phone active at `%s'. Type `/help' for a list of available commands\n"),
+ address);
phone_state = PS_LISTEN;
}
}
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:
case GNUNET_CONVERSATION_EC_CALL_ERROR:
FPRINTF (stdout,
_("Error with the call, restarting it\n"));
- call_state = CS_RESOLVING;
+ 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_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,
break;
case PS_LISTEN:
FPRINTF (stdout,
- _("We are listening for incoming calls for ego `%s' on line %u.\n"),
+ _("We are listening for incoming calls for ego `%s' on line `%s'.\n"),
ego_name,
line);
break;
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_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:
* Task run during shutdown.
*
* @param cls NULL
- * @param tc unused
*/
static void
-do_stop_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+do_stop_task (void *cls)
{
+#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)
{
GNUNET_free (ego_name);
ego_name = NULL;
GNUNET_free_non_null (peer_name);
+ peer_name = NULL;
phone_state = PS_ERROR;
}
/**
- * Task to handle commands from the terminal.
+ * Handle user command.
*
- * @param cls NULL
- * @param tc scheduler context
+ * @param message command the user typed in
+ * @param str_len number of bytes to process in @a message
*/
static void
-handle_command (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
+handle_command_string (char *message,
+ size_t str_len)
{
- char message[MAX_MESSAGE_LENGTH + 1];
- const char *ptr;
size_t i;
+ const char *ptr;
- handle_cmd_task =
- GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
- stdin_fh,
- &handle_command, NULL);
- /* read message from command line and handle it */
- memset (message, 0, MAX_MESSAGE_LENGTH + 1);
- if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
+ if (0 == str_len)
return;
- if (0 == strlen (message))
- return;
- if (message[strlen (message) - 1] == '\n')
- message[strlen (message) - 1] = '\0';
+ 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;
strlen (commands[i].command))))
i++;
ptr = &message[strlen (commands[i].command)];
- while (isspace ((int) *ptr))
+ while (isspace ((unsigned char) *ptr))
ptr++;
if ('\0' == *ptr)
ptr = NULL;
}
+#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.
+ *
+ * @param cls NULL
+ */
+static void
+handle_command (void *cls)
+{
+ char message[MAX_MESSAGE_LENGTH + 1];
+
+ handle_cmd_task =
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ stdin_fh,
+ &handle_command, NULL);
+ /* read message from command line and handle it */
+ memset (message, 0, MAX_MESSAGE_LENGTH + 1);
+ if (NULL == fgets (message, MAX_MESSAGE_LENGTH, stdin))
+ return;
+ handle_command_string (message, strlen (message));
+}
+
+
/**
* Function called by identity service with information about egos.
*
{
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;
- GNUNET_CONFIGURATION_set_value_number (cfg,
+ my_caller_id = ego;
+ GNUNET_CONFIGURATION_set_value_string (cfg,
"CONVERSATION",
"LINE",
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);
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
- NULL);
+ GNUNET_SCHEDULER_add_shutdown (&do_stop_task,
+ NULL);
}
int
main (int argc, char *const *argv)
{
- static const struct GNUNET_GETOPT_CommandLineOption options[] = {
- {'e', "ego", "NAME",
- gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
- 1, &GNUNET_GETOPT_set_string, &ego_name},
- {'p', "phone", "LINE",
- gettext_noop ("sets the LINE to use for the phone"),
- 1, &GNUNET_GETOPT_set_uint, &line},
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+
+ GNUNET_GETOPT_option_string ('e',
+ "ego",
+ "NAME",
+ gettext_noop ("sets the NAME of the ego to use for the phone (and name resolution)"),
+ &ego_name),
+
+ GNUNET_GETOPT_option_string ('p',
+ "phone",
+ "LINE",
+ gettext_noop ("sets the LINE to use for the phone"),
+ &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,