GNUnet  0.11.x
conversation_api_call.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet
3  Copyright (C) 2013, 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 Affero 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  You should have received a copy of the GNU Affero General Public License
16  along with this program. If not, see <http://www.gnu.org/licenses/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 
28 #include "platform.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "gnunet_gns_service.h"
32 #include "conversation.h"
33 
34 
39 {
43  CS_LOOKUP = 0,
44 
49 
54 
59 
64 
69 
74 };
75 
76 
81 {
86 
91 
95  char *callee;
96 
101 
106 
111 
116 
121 
126 
131 
136 
141 };
142 
143 
149 static void
151 
152 
160 static void
162  size_t data_size,
163  const void *data)
164 {
165  struct GNUNET_CONVERSATION_Call *call = cls;
166  struct GNUNET_MQ_Envelope *e;
167  struct ClientAudioMessage *am;
168 
169  GNUNET_assert (CS_ACTIVE == call->state);
170  e = GNUNET_MQ_msg_extra (am,
171  data_size,
173  GNUNET_memcpy (&am[1],
174  data,
175  data_size);
176  GNUNET_MQ_send (call->mq,
177  e);
178 }
179 
180 
187 static void
189  const struct ClientPhoneSuspendMessage *msg)
190 {
191  struct GNUNET_CONVERSATION_Call *call = cls;
192 
193  (void) msg;
194  switch (call->state)
195  {
196  case CS_LOOKUP:
197  GNUNET_break (0);
198  fail_call (call);
199  break;
200 
201  case CS_RINGING:
202  GNUNET_break_op (0);
203  fail_call (call);
204  break;
205 
206  case CS_SUSPENDED_CALLER:
207  call->state = CS_SUSPENDED_BOTH;
208  call->event_handler (call->event_handler_cls,
210  break;
211 
212  case CS_SUSPENDED_CALLEE:
213  case CS_SUSPENDED_BOTH:
214  GNUNET_break_op (0);
215  break;
216 
217  case CS_ACTIVE:
218  call->state = CS_SUSPENDED_CALLEE;
219  call->speaker->disable_speaker (call->speaker->cls);
220  call->mic->disable_microphone (call->mic->cls);
221  call->event_handler (call->event_handler_cls,
223  break;
224 
225  case CS_SHUTDOWN:
227  break;
228  }
229 }
230 
231 
238 static void
240  const struct ClientPhoneResumeMessage *msg)
241 {
242  struct GNUNET_CONVERSATION_Call *call = cls;
243 
244  (void) msg;
245  switch (call->state)
246  {
247  case CS_LOOKUP:
248  GNUNET_break (0);
249  fail_call (call);
250  break;
251 
252  case CS_RINGING:
253  GNUNET_break_op (0);
254  fail_call (call);
255  break;
256 
257  case CS_SUSPENDED_CALLER:
258  GNUNET_break_op (0);
259  break;
260 
261  case CS_SUSPENDED_CALLEE:
262  call->state = CS_ACTIVE;
263  call->speaker->enable_speaker (call->speaker->cls);
264  call->mic->enable_microphone (call->mic->cls,
266  call);
267  call->event_handler (call->event_handler_cls,
269  break;
270 
271  case CS_SUSPENDED_BOTH:
272  call->state = CS_SUSPENDED_CALLER;
273  call->event_handler (call->event_handler_cls,
275  break;
276 
277  case CS_ACTIVE:
278  GNUNET_break_op (0);
279  break;
280 
281  case CS_SHUTDOWN:
283  break;
284  }
285 }
286 
287 
294 static void
296  const struct ClientPhonePickedupMessage *msg)
297 {
298  struct GNUNET_CONVERSATION_Call *call = cls;
299 
300  (void) msg;
301  switch (call->state)
302  {
303  case CS_LOOKUP:
304  GNUNET_break (0);
305  fail_call (call);
306  break;
307 
308  case CS_RINGING:
309  call->state = CS_ACTIVE;
310  call->speaker->enable_speaker (call->speaker->cls);
311  call->mic->enable_microphone (call->mic->cls,
313  call);
314  call->event_handler (call->event_handler_cls,
316  break;
317 
318  case CS_SUSPENDED_CALLER:
319  case CS_SUSPENDED_CALLEE:
320  case CS_SUSPENDED_BOTH:
321  case CS_ACTIVE:
322  GNUNET_break (0);
323  fail_call (call);
324  break;
325 
326  case CS_SHUTDOWN:
328  break;
329  }
330 }
331 
332 
339 static void
341  const struct ClientPhoneHangupMessage *msg)
342 {
343  struct GNUNET_CONVERSATION_Call *call = cls;
345  void *eh_cls;
346 
347  (void) msg;
348  switch (call->state)
349  {
350  case CS_LOOKUP:
351  GNUNET_break (0);
352  fail_call (call);
353  break;
354 
355  case CS_RINGING:
356  case CS_SUSPENDED_CALLER:
357  case CS_SUSPENDED_CALLEE:
358  case CS_SUSPENDED_BOTH:
359  case CS_ACTIVE:
360  eh = call->event_handler;
361  eh_cls = call->event_handler_cls;
363  eh (eh_cls,
365  return;
366 
367  case CS_SHUTDOWN:
369  break;
370  }
371 }
372 
373 
381 static int
382 check_call_audio (void *cls,
383  const struct ClientAudioMessage *am)
384 {
385  (void) cls;
386  (void) am;
387  /* any payload is OK */
388  return GNUNET_OK;
389 }
390 
391 
398 static void
399 handle_call_audio (void *cls,
400  const struct ClientAudioMessage *am)
401 {
402  struct GNUNET_CONVERSATION_Call *call = cls;
403 
404  switch (call->state)
405  {
406  case CS_LOOKUP:
407  GNUNET_break (0);
408  fail_call (call);
409  break;
410 
411  case CS_RINGING:
412  GNUNET_break (0);
413  fail_call (call);
414  break;
415 
416  case CS_SUSPENDED_CALLER:
417  /* can happen: we suspended, other peer did not yet
418  learn about this. */
419  break;
420 
421  case CS_SUSPENDED_CALLEE:
422  case CS_SUSPENDED_BOTH:
423  /* can (rarely) also happen: other peer suspended, but cadet might
424  have had delayed data on the unreliable channel */
425  break;
426 
427  case CS_ACTIVE:
428  call->speaker->play (call->speaker->cls,
429  ntohs (am->header.size) - sizeof(struct
431  &am[1]);
432  break;
433 
434  case CS_SHUTDOWN:
436  break;
437  }
438 }
439 
440 
449 static void
451  int was_gns,
452  uint32_t rd_count,
453  const struct GNUNET_GNSRECORD_Data *rd)
454 {
455  struct GNUNET_CONVERSATION_Call *call = cls;
456  struct GNUNET_MQ_Envelope *e;
457  struct ClientCallMessage *ccm;
458 
459  (void) was_gns;
460  GNUNET_break (NULL != call->gns_lookup);
461  GNUNET_break (CS_LOOKUP == call->state);
462  call->gns_lookup = NULL;
463  for (uint32_t i = 0; i < rd_count; i++)
464  {
465  if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
466  {
467  if (rd[i].data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
468  {
469  GNUNET_break_op (0);
470  continue;
471  }
472  GNUNET_memcpy (&call->phone_record,
473  rd[i].data,
474  rd[i].data_size);
475  e = GNUNET_MQ_msg (ccm,
477  ccm->line_port = call->phone_record.line_port;
478  ccm->target = call->phone_record.peer;
480  GNUNET_MQ_send (call->mq,
481  e);
482  call->state = CS_RINGING;
483  call->event_handler (call->event_handler_cls,
485  return;
486  }
487  }
488  /* not found */
489  call->event_handler (call->event_handler_cls,
492 }
493 
494 
501 static void
503  enum GNUNET_MQ_Error error)
504 {
505  struct GNUNET_CONVERSATION_Call *call = cls;
506 
507  (void) error;
508  if (CS_SHUTDOWN == call->state)
509  {
511  return;
512  }
514  _ (
515  "Connection to conversation service lost, trying to reconnect\n"));
516  fail_call (call);
517 }
518 
519 
525 static void
527 {
528  if (CS_ACTIVE == call->state)
529  {
530  call->speaker->disable_speaker (call->speaker->cls);
531  call->mic->disable_microphone (call->mic->cls);
532  }
533  if (NULL != call->mq)
534  {
535  GNUNET_MQ_destroy (call->mq);
536  call->mq = NULL;
537  }
538  call->state = CS_SHUTDOWN;
539  call->event_handler (call->event_handler_cls,
542 }
543 
544 
563  const char *callee,
568  void *event_handler_cls)
569 {
572  struct GNUNET_MQ_MessageHandler handlers[] = {
573  GNUNET_MQ_hd_fixed_size (call_suspend,
576  call),
577  GNUNET_MQ_hd_fixed_size (call_resume,
580  call),
581  GNUNET_MQ_hd_fixed_size (call_picked_up,
584  call),
585  GNUNET_MQ_hd_fixed_size (call_hangup,
588  call),
589  GNUNET_MQ_hd_var_size (call_audio,
591  struct ClientAudioMessage,
592  call),
594  };
595 
596  call->mq = GNUNET_CLIENT_connect (cfg,
597  "conversation",
598  handlers,
600  call);
601  if (NULL == call->mq)
602  {
603  GNUNET_break (0);
604  GNUNET_free (call);
605  return NULL;
606  }
607  call->cfg = cfg;
608  call->caller_id = caller_id;
609  call->callee = GNUNET_strdup (callee);
610  call->speaker = speaker;
611  call->mic = mic;
614  call->gns = GNUNET_GNS_connect (cfg);
615  if (NULL == call->gns)
616  {
618  return NULL;
619  }
620  call->state = CS_LOOKUP;
622  call->callee,
624  GNUNET_NO,
626  call);
627  if (NULL == call->gns_lookup)
628  {
630  return NULL;
631  }
632  return call;
633 }
634 
635 
641 void
643 {
644  if ((NULL != call->speaker) &&
645  (CS_ACTIVE == call->state))
646  call->speaker->disable_speaker (call->speaker->cls);
647  if ((NULL != call->mic) &&
648  (CS_ACTIVE == call->state))
649  call->mic->disable_microphone (call->mic->cls);
650  if (CS_SHUTDOWN != call->state)
651  {
652  call->state = CS_SHUTDOWN;
653  }
654  if (NULL != call->mq)
655  {
656  GNUNET_MQ_destroy (call->mq);
657  call->mq = NULL;
658  }
659  if (NULL != call->gns_lookup)
660  {
662  call->gns_lookup = NULL;
663  }
664  if (NULL != call->gns)
665  {
666  GNUNET_GNS_disconnect (call->gns);
667  call->gns = NULL;
668  }
669  GNUNET_free (call->callee);
670  GNUNET_free (call);
671 }
672 
673 
680 void
682 {
683  struct GNUNET_MQ_Envelope *e;
684  struct ClientPhoneSuspendMessage *suspend;
685 
687  (CS_ACTIVE == call->state));
688  if (CS_ACTIVE == call->state)
689  {
690  call->speaker->disable_speaker (call->speaker->cls);
691  call->mic->disable_microphone (call->mic->cls);
692  }
693  call->speaker = NULL;
694  call->mic = NULL;
695  e = GNUNET_MQ_msg (suspend,
697  GNUNET_MQ_send (call->mq,
698  e);
699  if (CS_SUSPENDED_CALLER == call->state)
700  call->state = CS_SUSPENDED_BOTH;
701  else
702  call->state = CS_SUSPENDED_CALLER;
703 }
704 
705 
714 void
718 {
719  struct GNUNET_MQ_Envelope *e;
720  struct ClientPhoneResumeMessage *resume;
721 
723  (CS_SUSPENDED_BOTH == call->state));
725  GNUNET_MQ_send (call->mq, e);
726  call->speaker = speaker;
727  call->mic = mic;
728  if (CS_SUSPENDED_CALLER == call->state)
729  {
730  call->state = CS_ACTIVE;
731  call->speaker->enable_speaker (call->speaker->cls);
732  call->mic->enable_microphone (call->mic->cls,
734  call);
735  }
736  else
737  {
738  call->state = CS_SUSPENDED_CALLEE;
739  }
740 }
741 
742 
743 /* end of conversation_api_call.c */
Client -> Service message to call a phone.
Definition: conversation.h:208
Connection to the GNS service.
Definition: gns_api.h:35
void(* GNUNET_CONVERSATION_CallEventHandler)(void *cls, enum GNUNET_CONVERSATION_CallEventCode code)
Function called with an event emitted for a call.
The call was suspended by the caller.
struct GNUNET_PeerIdentity peer
Identity of the peer hosting the phone service.
static void fail_call(struct GNUNET_CONVERSATION_Call *call)
The call got disconnected, reconnect to the service.
struct GNUNET_MessageHeader * msg
Definition: 005.c:2
The call is ringing.
struct GNUNET_MQ_Handle * GNUNET_CLIENT_connect(const struct GNUNET_CONFIGURATION_Handle *cfg, const char *service_name, const struct GNUNET_MQ_MessageHandler *handlers, GNUNET_MQ_ErrorHandler error_handler, void *error_handler_cls)
Create a message queue to connect to a GNUnet service.
Definition: client.c:1057
enum CallState state
State machine for the call.
GNUNET_MQ_Error
Error codes for the queue.
static void handle_call_suspend(void *cls, const struct ClientPhoneSuspendMessage *msg)
We received a GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND.
void * GNUNET_GNS_lookup_with_tld_cancel(struct GNUNET_GNS_LookupWithTldRequest *ltr)
Cancel pending lookup request.
Definition: gns_tld_api.c:329
constants for network protocols
GNUNET_MICROPHONE_DisableCallback disable_microphone
Turn the microphone off.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL
Client <- Server message to indicate a ringing phone.
static size_t data_size
Number of bytes in data.
Definition: gnunet-abd.c:187
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
We are the caller and the callee suspended the call.
const struct GNUNET_CRYPTO_EcdsaPrivateKey * GNUNET_IDENTITY_ego_get_private_key(const struct GNUNET_IDENTITY_Ego *ego)
Obtain the ECC key associated with a ego.
Definition: identity_api.c:555
GNUNET_SPEAKER_PlayCallback play
Play audio.
struct GNUNET_GNS_LookupWithTldRequest * GNUNET_GNS_lookup_with_tld(struct GNUNET_GNS_Handle *handle, const char *name, uint32_t type, enum GNUNET_GNS_LocalOptions options, GNUNET_GNS_LookupResultProcessor2 proc, void *proc_cls)
Perform an asynchronous lookup operation on the GNS, determining the zone using the TLD of the given ...
Definition: gns_tld_api.c:241
static void handle_call_hangup(void *cls, const struct ClientPhoneHangupMessage *msg)
We received a #GNUNET_MESSAGE_TYPE_CONVERSATION_CS_HANG_UP.
struct GNUNET_PeerIdentity target
Which peer is hosting the line?
Definition: conversation.h:223
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
#define GNUNET_MQ_hd_fixed_size(name, code, str, ctx)
#define GNUNET_MQ_msg(mvar, type)
Allocate a GNUNET_MQ_Envelope.
Definition: gnunet_mq_lib.h:67
static struct Experiment * e
struct GNUNET_CONVERSATION_PhoneRecord phone_record
Target phone record, only valid after the lookup is done.
GNUNET_MICROPHONE_EnableCallback enable_microphone
Turn on the microphone.
struct GNUNET_IDENTITY_Ego * caller_id
Our caller identity.
#define GNUNET_NO
Definition: gnunet_common.h:78
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:75
#define GNUNET_GNSRECORD_TYPE_PHONE
Record type for a phone (of CONVERSATION).
const struct GNUNET_CONFIGURATION_Handle * cfg
Our configuration.
size_t data_size
Number of bytes in data.
GNUNET_SPEAKER_DisableCallback disable_speaker
Turn the speaker off.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
uint16_t size
The length of the struct (in bytes, including the length field itself), in big-endian format...
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO
Client <-> Server message to send audio data.
struct GNUNET_GNS_Handle * gns
Connection to GNS (can be NULL).
#define GNUNET_strdup(a)
Wrapper around GNUNET_xstrdup_.
struct GNUNET_CRYPTO_EcdsaPrivateKey caller_id
Identity of the caller.
Definition: conversation.h:233
#define GNUNET_break(cond)
Use this for internal assertion violations that are not fatal (can be handled) but should not occur...
static void transmit_call_audio(void *cls, size_t data_size, const void *data)
Process recorded audio data.
We are the caller and are now ready to talk as the callee picked up.
The call is in an active conversation.
#define _(String)
GNU gettext support macro.
Definition: platform.h:181
A phone record specifies which peer is hosting a given user and may also specify the phone line that ...
static void handle_call_audio(void *cls, const struct ClientAudioMessage *am)
We received a struct ClientAudioMessage
#define GNUNET_MQ_msg_extra(mvar, esize, type)
Allocate an envelope, with extra space allocated after the space needed by the message struct...
Definition: gnunet_mq_lib.h:52
Handle for an ego.
Definition: identity.h:245
void * event_handler_cls
Closure for event_handler.
const void * data
Binary value stored in the DNS record.
Client <-> Service hang up phone that may or may not be ringing.
Definition: conversation.h:170
#define GNUNET_MQ_hd_var_size(name, code, str, ctx)
#define GNUNET_break_op(cond)
Use this for assertion violations caused by other peers (i.e.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP
Service -> Client message to notify that phone was picked up.
void * cls
Closure for the callbacks.
char * callee
Target callee as a GNS address/name.
void GNUNET_GNS_disconnect(struct GNUNET_GNS_Handle *handle)
Shutdown connection with the GNS service.
Definition: gns_api.c:284
struct GNUNET_MQ_Handle * mq
Handle for transmitting to the CONVERSATION service.
static void handle_call_resume(void *cls, const struct ClientPhoneResumeMessage *msg)
We received a GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME.
void GNUNET_CONVERSATION_call_resume(struct GNUNET_CONVERSATION_Call *call, struct GNUNET_SPEAKER_Handle *speaker, struct GNUNET_MICROPHONE_Handle *mic)
Resumes a call after GNUNET_CONVERSATION_call_suspend.
We are the caller and are now ringing the other party (GNS lookup succeeded).
We are the caller and the callee called GNUNET_CONVERSATION_caller_hang_up.
struct GNUNET_GNS_Handle * GNUNET_GNS_connect(const struct GNUNET_CONFIGURATION_Handle *cfg)
Initialize the connection with the GNS service.
Definition: gns_api.c:262
struct GNUNET_CONVERSATION_Call * GNUNET_CONVERSATION_call_start(const struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_IDENTITY_Ego *caller_id, const char *callee, struct GNUNET_SPEAKER_Handle *speaker, struct GNUNET_MICROPHONE_Handle *mic, GNUNET_CONVERSATION_CallEventHandler event_handler, void *event_handler_cls)
Call the phone of another user.
Message handler for a specific message type.
GNUNET_SPEAKER_EnableCallback enable_speaker
Turn on the speaker.
struct GNUNET_SPEAKER_Handle * speaker
Our speaker.
static int check_call_audio(void *cls, const struct ClientAudioMessage *am)
We received a struct ClientAudioMessage, check it is well-formed.
void * cls
Closure for the callbacks.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RESUME
Client <-> Server message to resume connection.
We are the caller and failed to locate a phone record in GNS.
CallState
Possible states of the phone.
Service <-> Client message for phone was resumed.
Definition: conversation.h:133
Service -> Client: other peer has picked up the phone, we are now talking.
Definition: conversation.h:241
The call was suspended by the callee.
Handle to a message queue.
Definition: mq.c:85
static void handle_call_picked_up(void *cls, const struct ClientPhonePickedupMessage *msg)
We received a GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICKED_UP.
static struct GNUNET_CONVERSATION_Call * call
Call handle (for active outgoing call).
struct GNUNET_HashCode line_port
Which phone line to call at the peer?
Definition: conversation.h:228
The call was suspended by both caller and callee.
configuration data
Definition: configuration.c:85
struct GNUNET_GNS_LookupWithTldRequest * gns_lookup
Active GNS lookup (or NULL).
static void handle_gns_response(void *cls, int was_gns, uint32_t rd_count, const struct GNUNET_GNSRECORD_Data *rd)
Iterator called on obtained result for a GNS lookup.
#define GNUNET_log(kind,...)
Message Client <-> Service to transmit the audio.
Definition: conversation.h:188
struct GNUNET_HashCode line_port
Phone line (CADET port) to connect to.
void GNUNET_CONVERSATION_call_stop(struct GNUNET_CONVERSATION_Call *call)
Terminate a call.
We are the caller and the callee suspended the call.
GNUNET_CONVERSATION_CallEventHandler event_handler
Function to call with events.
void GNUNET_MQ_destroy(struct GNUNET_MQ_Handle *mq)
Destroy the message queue.
Definition: mq.c:824
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO.
Definition: conversation.h:193
Handle for an outgoing call.
void GNUNET_MQ_send(struct GNUNET_MQ_Handle *mq, struct GNUNET_MQ_Envelope *ev)
Send a message with the given message queue.
Definition: mq.c:353
The call is in termination.
A microphone is a device that can capture or otherwise produce audio data.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_SUSPEND
Client <-> Server message to suspend connection.
We had an error handing the call, and are now restarting it (back to lookup).
static void call_error_handler(void *cls, enum GNUNET_MQ_Error error)
We encountered an error talking with the conversation service.
struct GNUNET_MICROPHONE_Handle * mic
Our microphone.
Service <-> Client message for phone was suspended.
Definition: conversation.h:115
We still need to lookup the callee.
Handle to a lookup request.
Definition: gns_tld_api.c:44
uint32_t data
The data value.
A speaker is a device that can play or record audio data.
void GNUNET_CONVERSATION_call_suspend(struct GNUNET_CONVERSATION_Call *call)
Pause a call.
#define GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP
Client -> Server message to reject/hangup a call.
#define GNUNET_MQ_handler_end()
End-marker for the handlers array.
#define GNUNET_free(ptr)
Wrapper around free.