GNUnet  0.10.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 {
82 
87 
92 
96  char *callee;
97 
102 
107 
112 
117 
122 
127 
132 
137 
142 
143 };
144 
145 
151 static void
153 
154 
162 static void
164  size_t data_size,
165  const void *data)
166 {
167  struct GNUNET_CONVERSATION_Call *call = cls;
168  struct GNUNET_MQ_Envelope *e;
169  struct ClientAudioMessage *am;
170 
171  GNUNET_assert (CS_ACTIVE == call->state);
172  e = GNUNET_MQ_msg_extra (am,
173  data_size,
175  GNUNET_memcpy (&am[1],
176  data,
177  data_size);
178  GNUNET_MQ_send (call->mq,
179  e);
180 }
181 
182 
189 static void
191  const struct ClientPhoneSuspendMessage *msg)
192 {
193  struct GNUNET_CONVERSATION_Call *call = cls;
194 
195  (void) msg;
196  switch (call->state)
197  {
198  case CS_LOOKUP:
199  GNUNET_break (0);
200  fail_call (call);
201  break;
202  case CS_RINGING:
203  GNUNET_break_op (0);
204  fail_call (call);
205  break;
206  case CS_SUSPENDED_CALLER:
207  call->state = CS_SUSPENDED_BOTH;
208  call->event_handler (call->event_handler_cls,
210  break;
211  case CS_SUSPENDED_CALLEE:
212  case CS_SUSPENDED_BOTH:
213  GNUNET_break_op (0);
214  break;
215  case CS_ACTIVE:
216  call->state = CS_SUSPENDED_CALLEE;
217  call->speaker->disable_speaker (call->speaker->cls);
218  call->mic->disable_microphone (call->mic->cls);
219  call->event_handler (call->event_handler_cls,
221  break;
222  case CS_SHUTDOWN:
224  break;
225  }
226 }
227 
228 
235 static void
237  const struct ClientPhoneResumeMessage *msg)
238 {
239  struct GNUNET_CONVERSATION_Call *call = cls;
240 
241  (void) msg;
242  switch (call->state)
243  {
244  case CS_LOOKUP:
245  GNUNET_break (0);
246  fail_call (call);
247  break;
248  case CS_RINGING:
249  GNUNET_break_op (0);
250  fail_call (call);
251  break;
252  case CS_SUSPENDED_CALLER:
253  GNUNET_break_op (0);
254  break;
255  case CS_SUSPENDED_CALLEE:
256  call->state = CS_ACTIVE;
257  call->speaker->enable_speaker (call->speaker->cls);
258  call->mic->enable_microphone (call->mic->cls,
260  call);
261  call->event_handler (call->event_handler_cls,
263  break;
264  case CS_SUSPENDED_BOTH:
265  call->state = CS_SUSPENDED_CALLER;
266  call->event_handler (call->event_handler_cls,
268  break;
269  case CS_ACTIVE:
270  GNUNET_break_op (0);
271  break;
272  case CS_SHUTDOWN:
274  break;
275  }
276 }
277 
278 
285 static void
287  const struct ClientPhonePickedupMessage *msg)
288 {
289  struct GNUNET_CONVERSATION_Call *call = cls;
290 
291  (void) msg;
292  switch (call->state)
293  {
294  case CS_LOOKUP:
295  GNUNET_break (0);
296  fail_call (call);
297  break;
298  case CS_RINGING:
299  call->state = CS_ACTIVE;
300  call->speaker->enable_speaker (call->speaker->cls);
301  call->mic->enable_microphone (call->mic->cls,
303  call);
304  call->event_handler (call->event_handler_cls,
306  break;
307  case CS_SUSPENDED_CALLER:
308  case CS_SUSPENDED_CALLEE:
309  case CS_SUSPENDED_BOTH:
310  case CS_ACTIVE:
311  GNUNET_break (0);
312  fail_call (call);
313  break;
314  case CS_SHUTDOWN:
316  break;
317  }
318 }
319 
320 
327 static void
329  const struct ClientPhoneHangupMessage *msg)
330 {
331  struct GNUNET_CONVERSATION_Call *call = cls;
333  void *eh_cls;
334 
335  (void) msg;
336  switch (call->state)
337  {
338  case CS_LOOKUP:
339  GNUNET_break (0);
340  fail_call (call);
341  break;
342  case CS_RINGING:
343  case CS_SUSPENDED_CALLER:
344  case CS_SUSPENDED_CALLEE:
345  case CS_SUSPENDED_BOTH:
346  case CS_ACTIVE:
347  eh = call->event_handler;
348  eh_cls = call->event_handler_cls;
350  eh (eh_cls,
352  return;
353  case CS_SHUTDOWN:
355  break;
356  }
357 }
358 
359 
367 static int
368 check_call_audio (void *cls,
369  const struct ClientAudioMessage *am)
370 {
371  (void) cls;
372  (void) am;
373  /* any payload is OK */
374  return GNUNET_OK;
375 }
376 
377 
384 static void
385 handle_call_audio (void *cls,
386  const struct ClientAudioMessage *am)
387 {
388  struct GNUNET_CONVERSATION_Call *call = cls;
389 
390  switch (call->state)
391  {
392  case CS_LOOKUP:
393  GNUNET_break (0);
394  fail_call (call);
395  break;
396  case CS_RINGING:
397  GNUNET_break (0);
398  fail_call (call);
399  break;
400  case CS_SUSPENDED_CALLER:
401  /* can happen: we suspended, other peer did not yet
402  learn about this. */
403  break;
404  case CS_SUSPENDED_CALLEE:
405  case CS_SUSPENDED_BOTH:
406  /* can (rarely) also happen: other peer suspended, but cadet might
407  have had delayed data on the unreliable channel */
408  break;
409  case CS_ACTIVE:
410  call->speaker->play (call->speaker->cls,
411  ntohs (am->header.size) - sizeof (struct ClientAudioMessage),
412  &am[1]);
413  break;
414  case CS_SHUTDOWN:
416  break;
417  }
418 }
419 
420 
429 static void
431  int was_gns,
432  uint32_t rd_count,
433  const struct GNUNET_GNSRECORD_Data *rd)
434 {
435  struct GNUNET_CONVERSATION_Call *call = cls;
436  struct GNUNET_MQ_Envelope *e;
437  struct ClientCallMessage *ccm;
438 
439  (void) was_gns;
440  GNUNET_break (NULL != call->gns_lookup);
441  GNUNET_break (CS_LOOKUP == call->state);
442  call->gns_lookup = NULL;
443  for (uint32_t i=0;i<rd_count;i++)
444  {
445  if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
446  {
447  if (rd[i].data_size != sizeof (struct GNUNET_CONVERSATION_PhoneRecord))
448  {
449  GNUNET_break_op (0);
450  continue;
451  }
452  GNUNET_memcpy (&call->phone_record,
453  rd[i].data,
454  rd[i].data_size);
455  e = GNUNET_MQ_msg (ccm,
457  ccm->line_port = call->phone_record.line_port;
458  ccm->target = call->phone_record.peer;
460  GNUNET_MQ_send (call->mq,
461  e);
462  call->state = CS_RINGING;
463  call->event_handler (call->event_handler_cls,
465  return;
466  }
467  }
468  /* not found */
469  call->event_handler (call->event_handler_cls,
472 }
473 
474 
481 static void
483  enum GNUNET_MQ_Error error)
484 {
485  struct GNUNET_CONVERSATION_Call *call = cls;
486 
487  (void) error;
488  if (CS_SHUTDOWN == call->state)
489  {
491  return;
492  }
494  _("Connection to conversation service lost, trying to reconnect\n"));
495  fail_call (call);
496 }
497 
498 
504 static void
506 {
507  if (CS_ACTIVE == call->state)
508  {
509  call->speaker->disable_speaker (call->speaker->cls);
510  call->mic->disable_microphone (call->mic->cls);
511  }
512  if (NULL != call->mq)
513  {
514  GNUNET_MQ_destroy (call->mq);
515  call->mq = NULL;
516  }
517  call->state = CS_SHUTDOWN;
518  call->event_handler (call->event_handler_cls,
521 }
522 
523 
542  const char *callee,
546  void *event_handler_cls)
547 {
550  struct GNUNET_MQ_MessageHandler handlers[] = {
551  GNUNET_MQ_hd_fixed_size (call_suspend,
554  call),
555  GNUNET_MQ_hd_fixed_size (call_resume,
558  call),
559  GNUNET_MQ_hd_fixed_size (call_picked_up,
562  call),
563  GNUNET_MQ_hd_fixed_size (call_hangup,
566  call),
567  GNUNET_MQ_hd_var_size (call_audio,
569  struct ClientAudioMessage,
570  call),
572  };
573 
574  call->mq = GNUNET_CLIENT_connect (cfg,
575  "conversation",
576  handlers,
578  call);
579  if (NULL == call->mq)
580  {
581  GNUNET_break (0);
582  GNUNET_free (call);
583  return NULL;
584  }
585  call->cfg = cfg;
586  call->caller_id = caller_id;
587  call->callee = GNUNET_strdup (callee);
588  call->speaker = speaker;
589  call->mic = mic;
592  call->gns = GNUNET_GNS_connect (cfg);
593  if (NULL == call->gns)
594  {
596  return NULL;
597  }
598  call->state = CS_LOOKUP;
600  call->callee,
602  GNUNET_NO,
604  call);
605  if (NULL == call->gns_lookup)
606  {
608  return NULL;
609  }
610  return call;
611 }
612 
613 
619 void
621 {
622  if ( (NULL != call->speaker) &&
623  (CS_ACTIVE == call->state) )
624  call->speaker->disable_speaker (call->speaker->cls);
625  if ( (NULL != call->mic) &&
626  (CS_ACTIVE == call->state) )
627  call->mic->disable_microphone (call->mic->cls);
628  if (CS_SHUTDOWN != call->state)
629  {
630  call->state = CS_SHUTDOWN;
631  }
632  if (NULL != call->mq)
633  {
634  GNUNET_MQ_destroy (call->mq);
635  call->mq = NULL;
636  }
637  if (NULL != call->gns_lookup)
638  {
640  call->gns_lookup = NULL;
641  }
642  if (NULL != call->gns)
643  {
644  GNUNET_GNS_disconnect (call->gns);
645  call->gns = NULL;
646  }
647  GNUNET_free (call->callee);
648  GNUNET_free (call);
649 }
650 
651 
658 void
660 {
661  struct GNUNET_MQ_Envelope *e;
662  struct ClientPhoneSuspendMessage *suspend;
663 
664  GNUNET_assert ( (CS_SUSPENDED_CALLEE == call->state) ||
665  (CS_ACTIVE == call->state) );
666  if (CS_ACTIVE == call->state)
667  {
668  call->speaker->disable_speaker (call->speaker->cls);
669  call->mic->disable_microphone (call->mic->cls);
670  }
671  call->speaker = NULL;
672  call->mic = NULL;
673  e = GNUNET_MQ_msg (suspend,
675  GNUNET_MQ_send (call->mq,
676  e);
677  if (CS_SUSPENDED_CALLER == call->state)
678  call->state = CS_SUSPENDED_BOTH;
679  else
680  call->state = CS_SUSPENDED_CALLER;
681 }
682 
683 
692 void
696 {
697  struct GNUNET_MQ_Envelope *e;
698  struct ClientPhoneResumeMessage *resume;
699 
700  GNUNET_assert ( (CS_SUSPENDED_CALLER == call->state) ||
701  (CS_SUSPENDED_BOTH == call->state) );
703  GNUNET_MQ_send (call->mq, e);
704  call->speaker = speaker;
705  call->mic = mic;
706  if (CS_SUSPENDED_CALLER == call->state)
707  {
708  call->state = CS_ACTIVE;
709  call->speaker->enable_speaker (call->speaker->cls);
710  call->mic->enable_microphone (call->mic->cls,
712  call);
713  }
714  else
715  {
716  call->state = CS_SUSPENDED_CALLEE;
717  }
718 }
719 
720 
721 /* end of conversation_api_call.c */
Client -> Service message to call a phone.
Definition: conversation.h:214
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:901
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:330
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.
#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:556
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:242
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:229
#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:81
#define GNUNET_OK
Named constants for return values.
Definition: gnunet_common.h:78
#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:239
#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:208
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.
#define GNUNET_memcpy(dst, src, n)
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:174
#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:285
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:263
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:135
Service -> Client: other peer has picked up the phone, we are now talking.
Definition: conversation.h:247
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:234
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:193
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:198
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:116
We still need to lookup the callee.
Handle to a lookup request.
Definition: gns_tld_api.c:44
uint32_t data
The data value.
static size_t data_size
Number of bytes in data.
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.