/*
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 program.
+ * Possible states of the phone.
*/
-enum ConversationState
+enum PhoneState
{
/**
* We're waiting for our own idenitty.
*/
- CS_LOOKUP_EGO,
+ PS_LOOKUP_EGO,
/**
* We're listening for calls
*/
- CS_LISTEN,
+ PS_LISTEN,
/**
- * Our phone is ringing.
+ * We accepted an incoming phone call.
*/
- CS_RING,
+ PS_ACCEPTED,
/**
- * We accepted an incoming phone call.
+ * Internal error
*/
- CS_ACCEPTED,
+ PS_ERROR
+};
+
+/**
+ * States for current outgoing call.
+ */
+enum CallState
+{
/**
* We are looking up some other participant.
*/
CS_CONNECTED,
/**
- * Internal error
+ * The call is currently suspended (by us).
+ */
+ CS_SUSPENDED
+
+};
+
+
+/**
+ * List of incoming calls
+ */
+struct CallList
+{
+
+ /**
+ * A DLL.
+ */
+ struct CallList *prev;
+
+ /**
+ * A DLL.
+ */
+ struct CallList *next;
+
+ /**
+ * Handle to hang up or activate.
*/
- CS_ERROR
+ struct GNUNET_CONVERSATION_Caller *caller;
+
+ /**
+ * Public key identifying the caller.
+ */
+ struct GNUNET_CRYPTO_EcdsaPublicKey caller_id;
+
+ /**
+ * Unique number of the call.
+ */
+ unsigned int caller_num;
};
+
/**
* Phone handle
*/
static struct GNUNET_CONVERSATION_Phone *phone;
/**
- * Call handle
+ * Call handle (for active outgoing call).
*/
static struct GNUNET_CONVERSATION_Call *call;
/**
- * Desired phone line.
+ * Caller handle (for active incoming call).
+ * This call handler is NOT in the #cl_head / #cl_tail list.
+ */
+static struct CallList *cl_active;
+
+/**
+ * Head of calls waiting to be accepted.
*/
-static unsigned int line;
+static struct CallList *cl_head;
+
+/**
+ * Tail of calls waiting to be accepted.
+ */
+static struct CallList *cl_tail;
+
+/**
+ * Desired phone line (string to be converted to a hash).
+ */
+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;
static struct GNUNET_DISK_FileHandle *stdin_fh;
/**
- * Our current state.
+ * Our phone's current state.
+ */
+static enum PhoneState phone_state;
+
+/**
+ * Our call's current state.
+ */
+static enum CallState call_state;
+
+/**
+ * Counts the number of incoming calls we have had so far.
*/
-static enum ConversationState state;
+static unsigned int caller_num_gen;
/**
* GNS address for this phone.
* Function called with an event emitted by a phone.
*
* @param cls closure
- * @param code type of the event on the phone
- * @param ... additional information, depends on @a code
+ * @param code type of the event
+ * @param caller handle for the caller
+ * @param caller_id public key of the caller (in GNS)
*/
static void
phone_event_handler (void *cls,
- enum GNUNET_CONVERSATION_EventCode code,
- ...)
+ enum GNUNET_CONVERSATION_PhoneEventCode code,
+ struct GNUNET_CONVERSATION_Caller *caller,
+ const struct GNUNET_CRYPTO_EcdsaPublicKey *caller_id)
{
- va_list va;
+ struct CallList *cl;
- va_start (va, code);
switch (code)
{
- case GNUNET_CONVERSATION_EC_RING:
- GNUNET_break (CS_LISTEN == state);
- GNUNET_free_non_null (peer_name);
- peer_name = GNUNET_strdup (va_arg (va, const char *));
+ case GNUNET_CONVERSATION_EC_PHONE_RING:
FPRINTF (stdout,
- _("Incoming call from `%s'.\nPlease /accept or /cancel the call.\n"),
- peer_name);
- state = CS_RING;
- break;
- case GNUNET_CONVERSATION_EC_RINGING:
- GNUNET_break (0);
- break;
- case GNUNET_CONVERSATION_EC_READY:
- GNUNET_break (0);
+ _("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 = *caller_id;
+ cl->caller_num = caller_num_gen++;
+ GNUNET_CONTAINER_DLL_insert (cl_head,
+ cl_tail,
+ cl);
break;
- case GNUNET_CONVERSATION_EC_GNS_FAIL:
- GNUNET_break (0);
+ case GNUNET_CONVERSATION_EC_PHONE_HUNG_UP:
+ 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);
+ return;
+ }
+ FPRINTF (stdout,
+ _("Call from `%s' terminated\n"),
+ 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_BUSY:
- GNUNET_break (0);
+ }
+}
+
+
+/**
+ * Function called with an event emitted by a caller.
+ *
+ * @param cls closure with the `struct CallList` of the caller
+ * @param code type of the event issued by the caller
+ */
+static void
+caller_event_handler (void *cls,
+ enum GNUNET_CONVERSATION_CallerEventCode code)
+{
+ struct CallList *cl = cls;
+
+ switch (code)
+ {
+ case GNUNET_CONVERSATION_EC_CALLER_SUSPEND:
+ FPRINTF (stdout,
+ _("Call from `%s' suspended by other user\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
break;
- case GNUNET_CONVERSATION_EC_TERMINATED:
- GNUNET_break ( (CS_RING == state) ||
- (CS_ACCEPTED == state) );
+ case GNUNET_CONVERSATION_EC_CALLER_RESUME:
FPRINTF (stdout,
- _("Call terminated: %s\n"),
- va_arg (va, const char *));
- state = CS_LISTEN;
+ _("Call from `%s' resumed by other user\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
break;
}
- va_end (va);
}
{
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"),
ego_name);
- state = CS_LOOKUP_EGO;
+ phone_state = PS_LOOKUP_EGO;
return;
}
+ 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)
{
FPRINTF (stderr,
"%s",
_("Failed to setup phone (internal error)\n"));
- state = CS_ERROR;
+ phone_state = PS_ERROR;
}
else
{
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);
- state = CS_LISTEN;
+ FPRINTF (stdout,
+ _("Phone active at `%s'. Type `/help' for a list of available commands\n"),
+ address);
+ phone_state = PS_LISTEN;
}
}
/**
- * Function called with an event emitted by a phone.
+ * Function called with an event emitted by a call.
*
- * @param cls closure
- * @param code type of the event on the phone
- * @param ... additional information, depends on @a code
+ * @param cls closure, NULL
+ * @param code type of the event on the call
*/
static void
call_event_handler (void *cls,
- enum GNUNET_CONVERSATION_EventCode code,
- ...)
+ enum GNUNET_CONVERSATION_CallEventCode code)
{
- va_list va;
-
- va_start (va, code);
switch (code)
{
- case GNUNET_CONVERSATION_EC_RING:
- GNUNET_break (0);
- break;
- case GNUNET_CONVERSATION_EC_RINGING:
- GNUNET_break (CS_RESOLVING == state);
- if (verbose)
- FPRINTF (stdout,
- "%s",
- _("Resolved address. Now ringing other party.\n"));
- state = CS_RINGING;
+ case GNUNET_CONVERSATION_EC_CALL_RINGING:
+ GNUNET_break (CS_RESOLVING == call_state);
+ FPRINTF (stdout,
+ _("Resolved address of `%s'. Now ringing other party.\n"),
+ peer_name);
+ call_state = CS_RINGING;
break;
- case GNUNET_CONVERSATION_EC_READY:
- GNUNET_break (CS_RINGING == state);
+ case GNUNET_CONVERSATION_EC_CALL_PICKED_UP:
+ GNUNET_break (CS_RINGING == call_state);
FPRINTF (stdout,
- _("Connection established to `%s': %s\n"),
- peer_name,
- va_arg (va, const char *));
- state = CS_CONNECTED;
+ _("Connection established to `%s'\n"),
+ peer_name);
+ call_state = CS_CONNECTED;
break;
- case GNUNET_CONVERSATION_EC_GNS_FAIL:
- GNUNET_break (CS_RESOLVING == state);
+ case GNUNET_CONVERSATION_EC_CALL_GNS_FAIL:
+ 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;
- start_phone ();
break;
- case GNUNET_CONVERSATION_EC_BUSY:
- GNUNET_break (CS_RINGING == state);
+ case GNUNET_CONVERSATION_EC_CALL_HUNG_UP:
FPRINTF (stdout,
- "%s",
- _("Line busy\n"));
+ _("Call to `%s' terminated\n"),
+ peer_name);
+ GNUNET_free (peer_name);
+ peer_name = NULL;
call = NULL;
- start_phone ();
break;
- case GNUNET_CONVERSATION_EC_TERMINATED:
- GNUNET_break ( (CS_RINGING == state) ||
- (CS_CONNECTED == state) );
+ case GNUNET_CONVERSATION_EC_CALL_SUSPENDED:
+ GNUNET_break (CS_CONNECTED == call_state);
+ FPRINTF (stdout,
+ _("Connection to `%s' suspended (by other user)\n"),
+ peer_name);
+ break;
+ case GNUNET_CONVERSATION_EC_CALL_RESUMED:
+ GNUNET_break (CS_CONNECTED == call_state);
+ FPRINTF (stdout,
+ _("Connection to `%s' resumed (by other user)\n"),
+ peer_name);
+ break;
+ case GNUNET_CONVERSATION_EC_CALL_ERROR:
FPRINTF (stdout,
- _("Call terminated: %s\n"),
- va_arg (va, const char *));
+ _("Error with the call, restarting it\n"));
+ GNUNET_free (peer_name);
+ peer_name = NULL;
call = NULL;
- start_phone ();
break;
}
- va_end (va);
}
static void
do_call (const char *arg)
{
- if (NULL == caller_id)
+ if (NULL == my_caller_id)
{
FPRINTF (stderr,
_("Ego `%s' not available\n"),
ego_name);
return;
}
- switch (state)
+ if (NULL != call)
+ {
+ FPRINTF (stderr,
+ _("You are calling someone else already, hang up first!\n"));
+ return;
+ }
+ switch (phone_state)
{
- case CS_LOOKUP_EGO:
+ case PS_LOOKUP_EGO:
FPRINTF (stderr,
_("Ego `%s' not available\n"),
ego_name);
return;
- case CS_LISTEN:
+ case PS_LISTEN:
/* ok to call! */
break;
- case CS_RING:
- FPRINTF (stdout,
- _("Hanging up on incoming phone call from `%s' to call `%s'.\n"),
- peer_name,
- arg);
- GNUNET_CONVERSATION_phone_hang_up (phone, NULL);
- break;
- case CS_ACCEPTED:
- FPRINTF (stderr,
- _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
- peer_name,
- arg);
- return;
- case CS_RESOLVING:
- case CS_RINGING:
- FPRINTF (stderr,
- _("Aborting call to `%s'\n"),
- peer_name);
- GNUNET_CONVERSATION_call_stop (call, NULL);
- call = NULL;
- break;
- case CS_CONNECTED:
+ case PS_ACCEPTED:
FPRINTF (stderr,
- _("You are already in a conversation with `%s', refusing to call `%s'.\n"),
- peer_name,
- arg);
+ _("You are answering call from `%s', hang up or suspend that call first!\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
return;
- case CS_ERROR:
+ case PS_ERROR:
/* ok to call */
break;
}
- GNUNET_assert (NULL == call);
- if (NULL != phone)
+ if (NULL == arg)
{
- GNUNET_CONVERSATION_phone_destroy (phone);
- phone = NULL;
+ FPRINTF (stderr,
+ _("Call recipient missing.\n"));
+ do_help ("/call");
+ return;
}
- GNUNET_free_non_null (peer_name);
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,
&call_event_handler, NULL);
- state = CS_RESOLVING;
}
static void
do_accept (const char *args)
{
- switch (state)
+ struct CallList *cl;
+ char buf[32];
+
+ if ( (NULL != call) &&
+ (CS_SUSPENDED != call_state) )
{
- case CS_LOOKUP_EGO:
- case CS_LISTEN:
- case CS_ERROR:
FPRINTF (stderr,
- _("There is no incoming call to be accepted!\n"));
+ _("You are calling someone else already, hang up first!\n"));
return;
- case CS_RING:
+ }
+ switch (phone_state)
+ {
+ case PS_LOOKUP_EGO:
+ GNUNET_break (0);
+ break;
+ case PS_LISTEN:
/* this is the expected state */
break;
- case CS_ACCEPTED:
+ case PS_ACCEPTED:
FPRINTF (stderr,
- _("You are already in a conversation with `%s'.\n"),
- peer_name);
+ _("You are answering call from `%s', hang up or suspend that call first!\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
return;
- case CS_RESOLVING:
- case CS_RINGING:
+ case PS_ERROR:
+ GNUNET_break (0);
+ break;
+ }
+ cl = cl_head;
+ if (NULL == cl)
+ {
FPRINTF (stderr,
- _("You are trying to call `%s', cannot accept incoming calls right now.\n"),
- peer_name);
+ _("There is no incoming call to accept here!\n"));
return;
- case CS_CONNECTED:
+ }
+ if ( (NULL != cl->next) || (NULL != args) )
+ {
+ for (cl = cl_head; NULL != cl; cl = cl->next)
+ {
+ GNUNET_snprintf (buf, sizeof (buf),
+ "%u",
+ cl->caller_num);
+ if (0 == strcmp (buf, args))
+ break;
+ }
+ }
+ if (NULL == cl)
+ {
FPRINTF (stderr,
- _("You are already in a conversation with `%s'.\n"),
- peer_name);
+ _("There is no incoming call `%s' to accept right now!\n"),
+ args);
return;
}
- GNUNET_assert (NULL != phone);
- GNUNET_CONVERSATION_phone_pick_up (phone,
- args,
- speaker,
- mic);
- state = CS_ACCEPTED;
+ GNUNET_CONTAINER_DLL_remove (cl_head,
+ cl_tail,
+ cl);
+ cl_active = cl;
+ peer_key = cl->caller_id;
+ phone_state = PS_ACCEPTED;
+ GNUNET_CONVERSATION_caller_pick_up (cl->caller,
+ &caller_event_handler,
+ cl,
+ speaker,
+ mic);
}
static void
do_status (const char *args)
{
- switch (state)
+ struct CallList *cl;
+
+ switch (phone_state)
{
- case CS_LOOKUP_EGO:
+ case PS_LOOKUP_EGO:
FPRINTF (stdout,
_("We are currently trying to locate the private key for the ego `%s'.\n"),
ego_name);
break;
- case CS_LISTEN:
+ 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 CS_RING:
- FPRINTF (stdout,
- _("The phone is rining. `%s' is trying to call us.\n"),
- peer_name);
- break;
- case CS_ACCEPTED:
- case CS_CONNECTED:
+ case PS_ACCEPTED:
FPRINTF (stdout,
_("You are having a conversation with `%s'.\n"),
- peer_name);
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));;
break;
- case CS_RESOLVING:
+ case PS_ERROR:
FPRINTF (stdout,
- _("We are trying to find the network address to call `%s'.\n"),
- peer_name);
+ _("We had an internal error setting up our phone line. You can still make calls.\n"));
break;
- case CS_RINGING:
+ }
+ if (NULL != call)
+ {
+ switch (call_state)
+ {
+ case CS_RESOLVING:
+ FPRINTF (stdout,
+ _("We are trying to find the network address to call `%s'.\n"),
+ peer_name);
+ break;
+ case CS_RINGING:
+ FPRINTF (stdout,
+ _("We are calling `%s', his phone should be ringing.\n"),
+ peer_name);
+ break;
+ case CS_CONNECTED:
+ FPRINTF (stdout,
+ _("You are having a conversation with `%s'.\n"),
+ peer_name);
+ break;
+ case CS_SUSPENDED:
+ /* ok to accept incoming call right now */
+ break;
+ }
+ }
+ if ( (NULL != cl_head) &&
+ ( (cl_head != cl_active) ||
+ (cl_head != cl_tail) ) )
+ {
FPRINTF (stdout,
- _("We are calling `%s', his phone should be ringing.\n"),
- peer_name);
- break;
- case CS_ERROR:
+ "%s",
+ _("Calls waiting:\n"));
+ for (cl = cl_head; NULL != cl; cl = cl->next)
+ {
+ if (cl == cl_active)
+ continue;
+ FPRINTF (stdout,
+ _("#%u: `%s'\n"),
+ cl->caller_num,
+ GNUNET_GNSRECORD_pkey_to_zkey (&cl->caller_id));
+ }
FPRINTF (stdout,
- _("We had an internal error setting up our phone line. You can still make calls.\n"));
- break;
+ "%s",
+ "\n");
}
}
/**
- * Rejecting a call
+ * Suspending a call
*
* @param args arguments given to the command
*/
static void
-do_reject (const char *args)
+do_suspend (const char *args)
{
- switch (state)
+ if (NULL != call)
+ {
+ switch (call_state)
+ {
+ case CS_RESOLVING:
+ case CS_RINGING:
+ case CS_SUSPENDED:
+ FPRINTF (stderr,
+ "%s",
+ _("There is no call that could be suspended right now.\n"));
+ return;
+ case CS_CONNECTED:
+ call_state = CS_SUSPENDED;
+ GNUNET_CONVERSATION_call_suspend (call);
+ return;
+ }
+ }
+ switch (phone_state)
{
- case CS_LOOKUP_EGO:
- case CS_LISTEN:
- case CS_ERROR:
+ case PS_LOOKUP_EGO:
+ case PS_LISTEN:
+ case PS_ERROR:
FPRINTF (stderr,
"%s",
- _("There is no call that could be cancelled right now.\n"));
+ _("There is no call that could be suspended right now.\n"));
return;
- case CS_RING:
- case CS_ACCEPTED:
- case CS_RESOLVING:
- case CS_RINGING:
- case CS_CONNECTED:
+ case PS_ACCEPTED:
/* expected state, do rejection logic */
break;
}
- if (NULL == call)
+ GNUNET_assert (NULL != cl_active);
+ GNUNET_CONVERSATION_caller_suspend (cl_active->caller);
+ cl_active = NULL;
+ phone_state = PS_LISTEN;
+}
+
+
+/**
+ * Resuming a call
+ *
+ * @param args arguments given to the command
+ */
+static void
+do_resume (const char *args)
+{
+ struct CallList *cl;
+ char buf[32];
+
+ if (NULL != call)
+ {
+ switch (call_state)
+ {
+ case CS_RESOLVING:
+ case CS_RINGING:
+ case CS_CONNECTED:
+ FPRINTF (stderr,
+ "%s",
+ _("There is no call that could be resumed right now.\n"));
+ return;
+ case CS_SUSPENDED:
+ call_state = CS_CONNECTED;
+ GNUNET_CONVERSATION_call_resume (call,
+ speaker,
+ mic);
+ return;
+ }
+ }
+ switch (phone_state)
+ {
+ case PS_LOOKUP_EGO:
+ case PS_ERROR:
+ FPRINTF (stderr,
+ "%s",
+ _("There is no call that could be resumed right now.\n"));
+ return;
+ case PS_LISTEN:
+ /* expected state, do resume logic */
+ break;
+ case PS_ACCEPTED:
+ FPRINTF (stderr,
+ _("Already talking with `%s', cannot resume a call right now.\n"),
+ GNUNET_GNSRECORD_pkey_to_zkey (&peer_key));
+ return;
+ }
+ GNUNET_assert (NULL == cl_active);
+ cl = cl_head;
+ if (NULL == cl)
{
- GNUNET_assert (NULL != phone);
- GNUNET_CONVERSATION_phone_hang_up (phone,
- args);
- state = CS_LISTEN;
+ FPRINTF (stderr,
+ _("There is no incoming call to resume here!\n"));
+ return;
}
- else
+ if ( (NULL != cl->next) || (NULL != args) )
+ {
+ for (cl = cl_head; NULL != cl; cl = cl->next)
+ {
+ GNUNET_snprintf (buf, sizeof (buf),
+ "%u",
+ cl->caller_num);
+ if (0 == strcmp (buf, args))
+ break;
+ }
+ }
+ if (NULL == cl)
+ {
+ FPRINTF (stderr,
+ _("There is no incoming call `%s' to resume right now!\n"),
+ args);
+ return;
+ }
+ cl_active = cl;
+ GNUNET_CONVERSATION_caller_resume (cl_active->caller,
+ speaker,
+ mic);
+ phone_state = PS_ACCEPTED;
+}
+
+
+/**
+ * Rejecting a call
+ *
+ * @param args arguments given to the command
+ */
+static void
+do_reject (const char *args)
+{
+ struct CallList *cl;
+ char buf[32];
+
+ if (NULL != call)
{
- GNUNET_CONVERSATION_call_stop (call, args);
+ GNUNET_CONVERSATION_call_stop (call);
call = NULL;
- start_phone ();
+ return;
+ }
+ switch (phone_state)
+ {
+ case PS_LOOKUP_EGO:
+ case PS_ERROR:
+ FPRINTF (stderr,
+ "%s",
+ _("There is no call that could be cancelled right now.\n"));
+ return;
+ case PS_LISTEN:
+ /* look for active incoming calls to refuse */
+ cl = cl_head;
+ if (NULL == cl)
+ {
+ FPRINTF (stderr,
+ _("There is no incoming call to refuse here!\n"));
+ return;
+ }
+ if ( (NULL != cl->next) || (NULL != args) )
+ {
+ for (cl = cl_head; NULL != cl; cl = cl->next)
+ {
+ GNUNET_snprintf (buf, sizeof (buf),
+ "%u",
+ cl->caller_num);
+ if (0 == strcmp (buf, args))
+ break;
+ }
+ }
+ if (NULL == cl)
+ {
+ FPRINTF (stderr,
+ _("There is no incoming call `%s' to refuse right now!\n"),
+ args);
+ return;
+ }
+ GNUNET_CONVERSATION_caller_hang_up (cl->caller);
+ GNUNET_CONTAINER_DLL_remove (cl_head,
+ cl_tail,
+ cl);
+ GNUNET_free (cl);
+ break;
+ case PS_ACCEPTED:
+ /* expected state, do rejection logic */
+ GNUNET_assert (NULL != cl_active);
+ GNUNET_CONVERSATION_caller_hang_up (cl_active->caller);
+ cl_active = NULL;
+ phone_state = PS_LISTEN;
+ break;
}
}
{"/call", &do_call,
gettext_noop ("Use `/call USER.gnu' to call USER")},
{"/accept", &do_accept,
- gettext_noop ("Use `/accept MESSAGE' to accept an incoming call")},
+ gettext_noop ("Use `/accept #NUM' to accept incoming call #NUM")},
+ {"/suspend", &do_suspend,
+ gettext_noop ("Use `/suspend' to suspend the active call")},
+ {"/resume", &do_resume,
+ gettext_noop ("Use `/resume [#NUM]' to resume a call, #NUM is needed to resume incoming calls, no argument is needed to resume the current outgoing call.")},
{"/cancel", &do_reject,
- gettext_noop ("Use `/cancel MESSAGE' to reject or terminate a call")},
+ gettext_noop ("Use `/cancel' to reject or terminate a call")},
{"/status", &do_status,
gettext_noop ("Use `/status' to print status information")},
{"/quit", &do_quit,
/**
* Action function to print help for the command shell.
*
- * @param arguments arguments given to the command
+ * @param args arguments given to the command
*/
static void
do_help (const char *args)
* 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, NULL);
+ GNUNET_CONVERSATION_call_stop (call);
call = NULL;
}
if (NULL != phone)
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);
- state = CS_ERROR;
+ 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;
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.
+ *
+ * @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,
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;
}