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 
38 enum CallState {
42  CS_LOOKUP = 0,
43 
48 
53 
58 
63 
68 
73 };
74 
75 
84 
89 
93  char *callee;
94 
99 
104 
109 
114 
119 
124 
129 
134 
139 };
140 
141 
147 static void
149 
150 
158 static void
160  size_t data_size,
161  const void *data)
162 {
163  struct GNUNET_CONVERSATION_Call *call = cls;
164  struct GNUNET_MQ_Envelope *e;
165  struct ClientAudioMessage *am;
166 
167  GNUNET_assert(CS_ACTIVE == call->state);
168  e = GNUNET_MQ_msg_extra(am,
169  data_size,
171  GNUNET_memcpy(&am[1],
172  data,
173  data_size);
174  GNUNET_MQ_send(call->mq,
175  e);
176 }
177 
178 
185 static void
187  const struct ClientPhoneSuspendMessage *msg)
188 {
189  struct GNUNET_CONVERSATION_Call *call = cls;
190 
191  (void)msg;
192  switch (call->state)
193  {
194  case CS_LOOKUP:
195  GNUNET_break(0);
196  fail_call(call);
197  break;
198 
199  case CS_RINGING:
200  GNUNET_break_op(0);
201  fail_call(call);
202  break;
203 
204  case CS_SUSPENDED_CALLER:
205  call->state = CS_SUSPENDED_BOTH;
206  call->event_handler(call->event_handler_cls,
208  break;
209 
210  case CS_SUSPENDED_CALLEE:
211  case CS_SUSPENDED_BOTH:
212  GNUNET_break_op(0);
213  break;
214 
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 
223  case CS_SHUTDOWN:
225  break;
226  }
227 }
228 
229 
236 static void
238  const struct ClientPhoneResumeMessage *msg)
239 {
240  struct GNUNET_CONVERSATION_Call *call = cls;
241 
242  (void)msg;
243  switch (call->state)
244  {
245  case CS_LOOKUP:
246  GNUNET_break(0);
247  fail_call(call);
248  break;
249 
250  case CS_RINGING:
251  GNUNET_break_op(0);
252  fail_call(call);
253  break;
254 
255  case CS_SUSPENDED_CALLER:
256  GNUNET_break_op(0);
257  break;
258 
259  case CS_SUSPENDED_CALLEE:
260  call->state = CS_ACTIVE;
261  call->speaker->enable_speaker(call->speaker->cls);
262  call->mic->enable_microphone(call->mic->cls,
264  call);
265  call->event_handler(call->event_handler_cls,
267  break;
268 
269  case CS_SUSPENDED_BOTH:
270  call->state = CS_SUSPENDED_CALLER;
271  call->event_handler(call->event_handler_cls,
273  break;
274 
275  case CS_ACTIVE:
276  GNUNET_break_op(0);
277  break;
278 
279  case CS_SHUTDOWN:
281  break;
282  }
283 }
284 
285 
292 static void
294  const struct ClientPhonePickedupMessage *msg)
295 {
296  struct GNUNET_CONVERSATION_Call *call = cls;
297 
298  (void)msg;
299  switch (call->state)
300  {
301  case CS_LOOKUP:
302  GNUNET_break(0);
303  fail_call(call);
304  break;
305 
306  case CS_RINGING:
307  call->state = CS_ACTIVE;
308  call->speaker->enable_speaker(call->speaker->cls);
309  call->mic->enable_microphone(call->mic->cls,
311  call);
312  call->event_handler(call->event_handler_cls,
314  break;
315 
316  case CS_SUSPENDED_CALLER:
317  case CS_SUSPENDED_CALLEE:
318  case CS_SUSPENDED_BOTH:
319  case CS_ACTIVE:
320  GNUNET_break(0);
321  fail_call(call);
322  break;
323 
324  case CS_SHUTDOWN:
326  break;
327  }
328 }
329 
330 
337 static void
339  const struct ClientPhoneHangupMessage *msg)
340 {
341  struct GNUNET_CONVERSATION_Call *call = cls;
343  void *eh_cls;
344 
345  (void)msg;
346  switch (call->state)
347  {
348  case CS_LOOKUP:
349  GNUNET_break(0);
350  fail_call(call);
351  break;
352 
353  case CS_RINGING:
354  case CS_SUSPENDED_CALLER:
355  case CS_SUSPENDED_CALLEE:
356  case CS_SUSPENDED_BOTH:
357  case CS_ACTIVE:
358  eh = call->event_handler;
359  eh_cls = call->event_handler_cls;
361  eh(eh_cls,
363  return;
364 
365  case CS_SHUTDOWN:
367  break;
368  }
369 }
370 
371 
379 static int
381  const struct ClientAudioMessage *am)
382 {
383  (void)cls;
384  (void)am;
385  /* any payload is OK */
386  return GNUNET_OK;
387 }
388 
389 
396 static void
398  const struct ClientAudioMessage *am)
399 {
400  struct GNUNET_CONVERSATION_Call *call = cls;
401 
402  switch (call->state)
403  {
404  case CS_LOOKUP:
405  GNUNET_break(0);
406  fail_call(call);
407  break;
408 
409  case CS_RINGING:
410  GNUNET_break(0);
411  fail_call(call);
412  break;
413 
414  case CS_SUSPENDED_CALLER:
415  /* can happen: we suspended, other peer did not yet
416  learn about this. */
417  break;
418 
419  case CS_SUSPENDED_CALLEE:
420  case CS_SUSPENDED_BOTH:
421  /* can (rarely) also happen: other peer suspended, but cadet might
422  have had delayed data on the unreliable channel */
423  break;
424 
425  case CS_ACTIVE:
426  call->speaker->play(call->speaker->cls,
427  ntohs(am->header.size) - sizeof(struct ClientAudioMessage),
428  &am[1]);
429  break;
430 
431  case CS_SHUTDOWN:
433  break;
434  }
435 }
436 
437 
446 static void
448  int was_gns,
449  uint32_t rd_count,
450  const struct GNUNET_GNSRECORD_Data *rd)
451 {
452  struct GNUNET_CONVERSATION_Call *call = cls;
453  struct GNUNET_MQ_Envelope *e;
454  struct ClientCallMessage *ccm;
455 
456  (void)was_gns;
457  GNUNET_break(NULL != call->gns_lookup);
458  GNUNET_break(CS_LOOKUP == call->state);
459  call->gns_lookup = NULL;
460  for (uint32_t i = 0; i < rd_count; i++)
461  {
462  if (GNUNET_GNSRECORD_TYPE_PHONE == rd[i].record_type)
463  {
464  if (rd[i].data_size != sizeof(struct GNUNET_CONVERSATION_PhoneRecord))
465  {
466  GNUNET_break_op(0);
467  continue;
468  }
470  rd[i].data,
471  rd[i].data_size);
472  e = GNUNET_MQ_msg(ccm,
474  ccm->line_port = call->phone_record.line_port;
475  ccm->target = call->phone_record.peer;
477  GNUNET_MQ_send(call->mq,
478  e);
479  call->state = CS_RINGING;
480  call->event_handler(call->event_handler_cls,
482  return;
483  }
484  }
485  /* not found */
486  call->event_handler(call->event_handler_cls,
489 }
490 
491 
498 static void
500  enum GNUNET_MQ_Error error)
501 {
502  struct GNUNET_CONVERSATION_Call *call = cls;
503 
504  (void)error;
505  if (CS_SHUTDOWN == call->state)
506  {
508  return;
509  }
511  _("Connection to conversation service lost, trying to reconnect\n"));
512  fail_call(call);
513 }
514 
515 
521 static void
523 {
524  if (CS_ACTIVE == call->state)
525  {
526  call->speaker->disable_speaker(call->speaker->cls);
527  call->mic->disable_microphone(call->mic->cls);
528  }
529  if (NULL != call->mq)
530  {
531  GNUNET_MQ_destroy(call->mq);
532  call->mq = NULL;
533  }
534  call->state = CS_SHUTDOWN;
535  call->event_handler(call->event_handler_cls,
538 }
539 
540 
559  const char *callee,
563  void *event_handler_cls)
564 {
567  struct GNUNET_MQ_MessageHandler handlers[] = {
568  GNUNET_MQ_hd_fixed_size(call_suspend,
571  call),
572  GNUNET_MQ_hd_fixed_size(call_resume,
575  call),
576  GNUNET_MQ_hd_fixed_size(call_picked_up,
579  call),
580  GNUNET_MQ_hd_fixed_size(call_hangup,
583  call),
584  GNUNET_MQ_hd_var_size(call_audio,
586  struct ClientAudioMessage,
587  call),
589  };
590 
591  call->mq = GNUNET_CLIENT_connect(cfg,
592  "conversation",
593  handlers,
595  call);
596  if (NULL == call->mq)
597  {
598  GNUNET_break(0);
599  GNUNET_free(call);
600  return NULL;
601  }
602  call->cfg = cfg;
603  call->caller_id = caller_id;
604  call->callee = GNUNET_strdup(callee);
605  call->speaker = speaker;
606  call->mic = mic;
609  call->gns = GNUNET_GNS_connect(cfg);
610  if (NULL == call->gns)
611  {
613  return NULL;
614  }
615  call->state = CS_LOOKUP;
617  call->callee,
619  GNUNET_NO,
621  call);
622  if (NULL == call->gns_lookup)
623  {
625  return NULL;
626  }
627  return call;
628 }
629 
630 
636 void
638 {
639  if ((NULL != call->speaker) &&
640  (CS_ACTIVE == call->state))
641  call->speaker->disable_speaker(call->speaker->cls);
642  if ((NULL != call->mic) &&
643  (CS_ACTIVE == call->state))
644  call->mic->disable_microphone(call->mic->cls);
645  if (CS_SHUTDOWN != call->state)
646  {
647  call->state = CS_SHUTDOWN;
648  }
649  if (NULL != call->mq)
650  {
651  GNUNET_MQ_destroy(call->mq);
652  call->mq = NULL;
653  }
654  if (NULL != call->gns_lookup)
655  {
657  call->gns_lookup = NULL;
658  }
659  if (NULL != call->gns)
660  {
661  GNUNET_GNS_disconnect(call->gns);
662  call->gns = NULL;
663  }
664  GNUNET_free(call->callee);
665  GNUNET_free(call);
666 }
667 
668 
675 void
677 {
678  struct GNUNET_MQ_Envelope *e;
679  struct ClientPhoneSuspendMessage *suspend;
680 
682  (CS_ACTIVE == call->state));
683  if (CS_ACTIVE == call->state)
684  {
685  call->speaker->disable_speaker(call->speaker->cls);
686  call->mic->disable_microphone(call->mic->cls);
687  }
688  call->speaker = NULL;
689  call->mic = NULL;
690  e = GNUNET_MQ_msg(suspend,
692  GNUNET_MQ_send(call->mq,
693  e);
694  if (CS_SUSPENDED_CALLER == call->state)
695  call->state = CS_SUSPENDED_BOTH;
696  else
697  call->state = CS_SUSPENDED_CALLER;
698 }
699 
700 
709 void
713 {
714  struct GNUNET_MQ_Envelope *e;
715  struct ClientPhoneResumeMessage *resume;
716 
718  (CS_SUSPENDED_BOTH == call->state));
720  GNUNET_MQ_send(call->mq, e);
721  call->speaker = speaker;
722  call->mic = mic;
723  if (CS_SUSPENDED_CALLER == call->state)
724  {
725  call->state = CS_ACTIVE;
726  call->speaker->enable_speaker(call->speaker->cls);
727  call->mic->enable_microphone(call->mic->cls,
729  call);
730  }
731  else
732  {
733  call->state = CS_SUSPENDED_CALLEE;
734  }
735 }
736 
737 
738 /* end of conversation_api_call.c */
Client -> Service message to call a phone.
Definition: conversation.h:199
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:900
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:328
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:553
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:240
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:213
#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:223
#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:237
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:163
#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:282
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:260
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:128
Service -> Client: other peer has picked up the phone, we are now talking.
Definition: conversation.h:231
The call was suspended by the callee.
Handle to a message queue.
Definition: mq.c:84
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:218
The call was suspended by both caller and callee.
configuration data
Definition: configuration.c:83
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:180
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:821
struct GNUNET_MessageHeader header
Type is GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO.
Definition: conversation.h:184
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:351
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:111
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.