-sync before server reboot, work on conversation service
[oweals/gnunet.git] / src / conversation / conversation_api.c
1 /*
2      This file is part of GNUnet.
3      (C  2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file conversation/conversation_api.c
23  * @brief API for conversation
24  * @author Simon Dieterle
25  * @author Andreas Fuchs
26  * STRUCTURE:
27  * - DATA STRUCTURES
28  * - DECLARATIONS
29  * - AUXILIARY FUNCTIONS
30  * - RECEIVE HANDLERS
31  * - SEND FUNCTIONS
32  * - API CALL DEFINITIONS
33  *
34  */
35
36 #include "platform.h"
37 #include "gnunet_util_lib.h"
38 #include "gnunet_dnsparser_lib.h"
39 #include "gnunet_gns_service.h"
40 #include "gnunet_protocols.h"
41 #include "conversation.h"
42 #include "gnunet_conversation_service.h"
43
44 #define MAX_TRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60)
45
46 /******************************************************************************/
47 /************************      DATA STRUCTURES     ****************************/
48 /******************************************************************************/
49
50 enum GNUNET_CONVERSATION_CallType
51 {
52   CALLER = 0,
53   CALLEE
54 };
55
56 /**
57 * Information about a call
58 */
59 struct GNUNET_CONVERSATION_CallInformation
60 {
61
62         /**
63         * Peer interacting with
64         */
65   struct GNUNET_PeerIdentity peer;
66
67         /**
68         * Type of call (incoming or outgoing)
69         */
70   int type;
71
72         /**
73         * Shows if the call ist fully established
74         */
75   int established;
76 };
77
78 /**
79  * Opaque handle to the service.
80  */
81 struct GNUNET_CONVERSATION_Handle
82 {
83
84   /**
85    * Our configuration.
86    */
87   const struct GNUNET_CONFIGURATION_Handle *cfg;
88
89   /**
90    * Handle to the server connection, to send messages later
91    */
92   struct GNUNET_CLIENT_Connection *client;
93
94   /**
95    * GNS handle
96    */
97   struct GNUNET_GNS_Handle *gns;
98
99   /**
100    * Namestore handle
101    */
102   struct GNUNET_NAMESTORE_Handle *namestore;
103
104   /**
105    * TXT record for gns
106    */
107   int txt_record_set;
108
109   /**
110    * Callback for incoming calls
111    */
112   GNUNET_CONVERSATION_CallHandler *call_handler;
113
114   /**
115    * Callback for rejected calls
116    */
117   GNUNET_CONVERSATION_RejectHandler *reject_handler;
118   
119   /**
120    * Callback for notifications
121    */
122   GNUNET_CONVERSATION_NotificationHandler *notification_handler;
123
124   /**
125    * Callback for missed calls
126    */
127   GNUNET_CONVERSATION_MissedCallHandler *missed_call_handler;
128
129   /**
130    * The pointer to the call
131    */
132   struct GNUNET_CONVERSATION_CallInformation *call;
133 };
134
135 /******************************************************************************/
136 /***********************     AUXILIARY FUNCTIONS      *************************/
137 /******************************************************************************/
138
139 /**
140 * Initialize the conversation txt record in GNS
141 */
142 static void
143 setup_gns_txt (struct GNUNET_CONVERSATION_Handle *handle)
144 {
145   struct GNUNET_CRYPTO_EccPublicSignKey zone_pkey;
146   struct GNUNET_CRYPTO_EccPrivateKey *zone_key;
147   struct GNUNET_CRYPTO_EccPrivateKey *peer_key;
148   struct GNUNET_NAMESTORE_RecordData rd;
149   struct GNUNET_PeerIdentity peer;
150
151   char *zone_keyfile;
152   char *peer_keyfile;
153
154   rd.expiration_time = UINT64_MAX;
155
156   if (GNUNET_OK !=
157       GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "gns", "ZONEKEY",
158                                                &zone_keyfile))
159     {
160       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n");
161       return;
162     }
163
164   if (GNUNET_OK !=
165       GNUNET_CONFIGURATION_get_value_filename (handle->cfg, "PEER",
166                                                "PRIVATE_KEY", &peer_keyfile))
167     {
168       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to get key from cfg\n");
169       return;
170     }
171
172   zone_key = GNUNET_CRYPTO_ecc_key_create_from_file (zone_keyfile);
173   GNUNET_CRYPTO_ecc_key_get_public_for_signature (zone_key, &zone_pkey);
174   peer_key = GNUNET_CRYPTO_ecc_key_create_from_file (peer_keyfile);
175   GNUNET_CRYPTO_ecc_key_get_public_for_signature (peer_key,
176                                                   &peer.public_key);
177   const char *h = GNUNET_i2s_full (&peer);
178
179   rd.data_size = strlen (h) + 1;
180   rd.data = h;
181   rd.record_type = GNUNET_DNSPARSER_TYPE_TXT;
182   rd.flags = GNUNET_NAMESTORE_RF_NONE;
183
184   /* FIXME: continuation? return value? */
185   GNUNET_NAMESTORE_records_store (handle->namestore, 
186                                   zone_key,
187                                   "conversation", 
188                                   1, &rd,
189                                   NULL, NULL);
190 }
191
192 /**
193 * Callback for checking the conversation txt gns record
194 *
195 * @param cls closure
196 * @param rd_count
197 * @param rd
198 */
199 static void
200 check_gns_cb (void *cls, uint32_t rd_count,
201               const struct GNUNET_NAMESTORE_RecordData *rd)
202 {
203   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
204
205   if (0 == rd_count)
206     {
207       setup_gns_txt (h);
208     }
209   else
210     {
211       h->txt_record_set = GNUNET_YES;
212     }
213 }
214
215
216 /**
217  * Check if the gns txt record for conversation exits
218  */
219 static void
220 check_gns (struct GNUNET_CONVERSATION_Handle *h)
221 {
222   GNUNET_GNS_lookup (h->gns, "conversation.gns", 
223                      NULL /* FIXME_ZONE */,
224                      GNUNET_DNSPARSER_TYPE_TXT,
225                      GNUNET_NO, 
226                      NULL, 
227                      &check_gns_cb, h);
228 }
229
230
231 /******************************************************************************/
232 /***********************      RECEIVE HANDLERS     ****************************/
233 /******************************************************************************/
234
235 /**
236  * Function to process all messages received from the service
237  *
238  * @param cls closure
239  * @param msg message received, NULL on timeout or fatal error
240  */
241 static void
242 receive_message_cb (void *cls, 
243                     const struct GNUNET_MessageHeader *msg)
244 {
245   struct GNUNET_CONVERSATION_Handle *h = cls;
246   struct ServerClientSessionInitiateMessage *imsg;
247   struct ServerClientSessionRejectMessage *rmsg;
248   struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls;
249
250   if (NULL != msg)
251     {
252       switch (ntohs (msg->type))
253         {
254         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT:
255           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
256                       _("%s has accepted your call.\n"),
257                       GNUNET_i2s_full (&(h->call->peer)));
258
259           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_ACCEPTED,
260                                    &(h->call->peer));
261           h->call->type = CALLEE;
262
263           break;
264
265         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT:
266           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
267                       _("%s has rejected your call.\n"),
268                       GNUNET_i2s_full (&(h->call->peer)));
269
270           rmsg = (struct ServerClientSessionRejectMessage *) msg;
271           h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer));
272           GNUNET_free (h->call);
273           h->call = NULL;
274
275           break;
276
277         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE:
278           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
279                       _("%s has terminated the call.\n"),
280                       GNUNET_i2s_full (&(h->call->peer)));
281
282           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_TERMINATED,
283                                    &(h->call->peer));
284           GNUNET_free (h->call);
285           h->call = NULL;
286
287           break;
288
289         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE:
290           imsg = (struct ServerClientSessionInitiateMessage *) msg;
291
292           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"),
293                       GNUNET_i2s_full (&(imsg->peer)));
294
295           h->call =
296             (struct GNUNET_CONVERSATION_CallInformation *)
297             GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
298           memcpy (&(h->call->peer), &(imsg->peer),
299                   sizeof (struct GNUNET_PeerIdentity));
300           h->call_handler (NULL, h, &(h->call->peer));
301           h->call->type = CALLEE;
302
303           break;
304
305         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL:
306           missed_calls =
307             (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg +
308                                                            (sizeof
309                                                             (struct
310                                                              GNUNET_MessageHeader)));
311           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312                       _("You &d have missed a calls.\n"),
313                       missed_calls->number);
314           h->missed_call_handler (NULL, h, missed_calls);
315           break;
316
317         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED:
318           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n"));
319           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_SERVICE_BLOCKED,
320                                    NULL);
321           break;
322
323         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED:
324           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
325                       _("The peer you are calling is not connected.\n"));
326           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL);
327           break;
328
329         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER:
330           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
331                       _("The peer you are calling does not answer.\n"));
332           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_ANSWER,
333                                    &(h->call->peer));
334           break;
335
336         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR:
337           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n"));
338           break;
339
340         default:
341           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342                       _("Got unknown message type.\n"));
343           break;
344         }
345
346     }
347
348   GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
349                          GNUNET_TIME_UNIT_FOREVER_REL);
350 }
351
352 /******************************************************************************/
353 /************************       SEND FUNCTIONS     ****************************/
354 /******************************************************************************/
355
356 /**
357  * Function called to send a session initiate message to the service.
358  * "buf" will be NULL and "size" zero if the socket was closed for writing in
359  * the meantime.
360  *
361  * @param cls closure, NULL
362  * @param size number of bytes available in buf
363  * @param buf where the callee should write the initiate message
364  * @return number of bytes written to buf
365  */
366 static size_t
367 transmit_session_initiate_message (void *cls, size_t size, void *buf)
368 {
369   size_t msg_size;
370   struct ClientServerSessionInitiateMessage *msg;
371   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
372
373   msg_size = sizeof (struct ClientServerSessionInitiateMessage);
374
375   GNUNET_assert (size >= msg_size);
376   msg = buf;
377   msg->header.size = htons (msg_size);
378   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE);
379   memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity));
380
381   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
382               _
383               ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"),
384               GNUNET_i2s_full (&(h->call->peer)));
385
386   h->call->type = CALLER;
387
388   return msg_size;
389 }
390
391 /**
392  * Function called to send a session accept message to the service.
393  * "buf" will be NULL and "size" zero if the socket was closed for writing in
394  * the meantime.
395  *
396  * @param cls closure, NULL
397  * @param size number of bytes available in buf
398  * @param buf where the callee should write the accept message
399  * @return number of bytes written to buf
400  */
401 static size_t
402 transmit_session_accept_message (void *cls, size_t size, void *buf)
403 {
404   size_t msg_size;
405   struct ClientServerSessionAcceptMessage *msg;
406   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
407
408   msg_size = sizeof (struct ClientServerSessionAcceptMessage);
409
410   GNUNET_assert (size >= msg_size);
411   msg = buf;
412   msg->header.size = htons (msg_size);
413   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT);
414
415   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
416               _
417               ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"),
418               GNUNET_i2s_full (&(h->call->peer)));
419
420   h->call->established = GNUNET_YES;
421
422   return msg_size;
423 }
424
425 /**
426  * Function called to send a session reject message to the service.
427  * "buf" will be NULL and "size" zero if the socket was closed for writing in
428  * the meantime.
429  *
430  * @param cls closure, NULL
431  * @param size number of bytes available in buf
432  * @param buf where the callee should write the reject message
433  * @return number of bytes written to buf
434  */
435 static size_t
436 transmit_session_reject_message (void *cls, size_t size, void *buf)
437 {
438   size_t msg_size;
439   struct ClientServerSessionRejectMessage *msg;
440   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
441
442   msg_size = sizeof (struct ClientServerSessionRejectMessage);
443
444   GNUNET_assert (size >= msg_size);
445   msg = buf;
446   msg->header.size = htons (msg_size);
447   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT);
448   msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_WANTED);
449
450   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
451               _
452               ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"),
453               GNUNET_i2s_full (&(h->call->peer)));
454
455   GNUNET_free (h->call);
456   h->call = NULL;
457
458   return msg_size;
459 }
460
461 /**
462  * Function called to send a session terminate message to the service.
463  * "buf" will be NULL and "size" zero if the socket was closed for writing in
464  * the meantime.
465  *
466  * @param cls closure, NULL
467  * @param size number of bytes available in buf
468  * @param buf where the callee should write the terminate message
469  * @return number of bytes written to buf
470  */
471 static size_t
472 transmit_session_terminate_message (void *cls, size_t size, void *buf)
473 {
474   size_t msg_size;
475   struct ClientServerSessionTerminateMessage *msg;
476   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
477
478   msg_size = sizeof (struct ClientServerSessionTerminateMessage);
479
480   GNUNET_assert (size >= msg_size);
481   msg = buf;
482   msg->header.size = htons (msg_size);
483   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE);
484
485   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
486               _
487               ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"),
488               GNUNET_i2s_full (&(h->call->peer)));
489
490   GNUNET_free (h->call);
491   h->call = NULL;
492
493   return msg_size;
494 }
495
496 /**
497  * Auxiliary function to call a peer.
498  * 
499  * @param h conversation handle
500  * @return 
501  */
502 static void
503 initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer)
504 {
505   h->call =
506     (struct GNUNET_CONVERSATION_CallInformation *)
507     GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
508   memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity));
509
510   GNUNET_CLIENT_notify_transmit_ready (h->client,
511                                        sizeof (struct
512                                                ClientServerSessionInitiateMessage),
513                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
514                                        &transmit_session_initiate_message, h);
515
516   return;
517 }
518
519 /**
520  * Auxiliary function to accept a call.
521  * 
522  * @param h conversation handle
523  */
524 static void
525 accept_call (struct GNUNET_CONVERSATION_Handle *h)
526 {
527   GNUNET_CLIENT_notify_transmit_ready (h->client,
528                                        sizeof (struct
529                                                ClientServerSessionAcceptMessage),
530                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
531                                        &transmit_session_accept_message, h);
532 }
533
534
535 /**
536  * Auxiliary function to reject a call.
537  * 
538  * @param h conversation handle
539  */
540 static void
541 reject_call (struct GNUNET_CONVERSATION_Handle *h)
542 {
543   GNUNET_CLIENT_notify_transmit_ready (h->client,
544                                        sizeof (struct
545                                                ClientServerSessionRejectMessage),
546                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
547                                        &transmit_session_reject_message, h);
548 }
549
550
551 /**
552  * Auxiliary function to terminate a call.
553  * 
554  * @param h conversation handle
555  */
556 static void
557 terminate_call (struct GNUNET_CONVERSATION_Handle *h)
558 {
559   GNUNET_CLIENT_notify_transmit_ready (h->client,
560                                        sizeof (struct
561                                                ClientServerSessionTerminateMessage),
562                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
563                                        &transmit_session_terminate_message,
564                                        h);
565 }
566
567
568 /**
569  *
570  */
571 static void
572 gns_call_cb (void *cls, uint32_t rd_count,
573              const struct GNUNET_NAMESTORE_RecordData *rd)
574 {
575   struct GNUNET_CONVERSATION_Handle *handle = cls;
576   struct GNUNET_PeerIdentity peer;
577   unsigned int i;
578
579   for (i=0;i<rd_count;i++)
580   {
581     switch (rd[i].record_type)
582     {
583     case GNUNET_DNSPARSER_TYPE_TXT: /* FIXME:  use fresh record type for voide... */
584       if (GNUNET_OK !=
585           GNUNET_CRYPTO_ecc_public_sign_key_from_string (rd[i].data,
586                                                          rd[i].data_size,
587                                                          &peer.public_key))
588       {
589         GNUNET_break_op (0);
590         continue;
591       }      
592       initiate_call (handle, peer);
593       return;
594     default:
595       break;
596     }
597   }
598   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
599               "Lookup failed\n");
600   handle->notification_handler (NULL, handle, 
601                                 GNUNET_CONVERSATION_NT_NO_PEER,
602                                 NULL);
603 }
604
605
606 /**
607 * GNS lookup
608 */
609 static void
610 gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee)
611 {
612   char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
613   char *pos;
614
615   pos = domain;
616   strcpy (pos, "conversation");
617   pos += strlen ("conversation");
618   strcpy (pos, ".");
619   pos++;
620   strcpy (pos, callee);
621   pos += strlen (callee);
622
623   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain);
624
625   GNUNET_GNS_lookup (h->gns,
626                      domain,
627                      NULL /* FIXME: ZONE! */,
628                      GNUNET_DNSPARSER_TYPE_TXT,
629                      GNUNET_NO, 
630                      NULL,
631                      &gns_call_cb, h);
632 }
633
634
635 /******************************************************************************/
636 /**********************      API CALL DEFINITIONS     *************************/
637 /******************************************************************************/
638
639 struct GNUNET_CONVERSATION_Handle *
640 GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 
641                              void *cls,
642                              GNUNET_CONVERSATION_CallHandler call_handler,
643                              GNUNET_CONVERSATION_RejectHandler reject_handler,
644                              GNUNET_CONVERSATION_NotificationHandler notification_handler,
645                              GNUNET_CONVERSATION_MissedCallHandler missed_call_handler)
646 {
647   struct GNUNET_CONVERSATION_Handle *h;
648
649   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
650               "GNUNET_CONVERSATION_connect()\n");
651   h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle));
652
653   h->cfg = cfg;
654   h->call_handler = call_handler;
655   h->reject_handler = reject_handler;
656   h->notification_handler = notification_handler;
657   h->missed_call_handler = missed_call_handler;
658
659   if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg)))
660     {
661       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n");
662       GNUNET_break (0);
663       GNUNET_free (h);
664
665       return NULL;
666     }
667
668   if (NULL == (h->gns = GNUNET_GNS_connect (cfg)))
669     {
670       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n");
671       GNUNET_break (0);
672       GNUNET_CLIENT_disconnect (h->client);
673       GNUNET_free (h);
674
675       return NULL;
676     }
677
678   if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg)))
679     {
680       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
681                   "Could not access NAMESTORE service\n");
682       GNUNET_break (0);
683       GNUNET_CLIENT_disconnect (h->client);
684       GNUNET_GNS_disconnect (h->gns);
685       GNUNET_free (h);
686
687       return NULL;
688     }
689
690   check_gns (h);
691   GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
692                          GNUNET_TIME_UNIT_FOREVER_REL);
693
694   return h;
695 }
696
697
698 void
699 GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle)
700 {
701   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n");
702
703   GNUNET_CLIENT_disconnect (handle->client);
704   GNUNET_GNS_disconnect (handle->gns);
705
706   GNUNET_free (handle);
707   handle = NULL;
708 }
709
710
711 void
712 GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, 
713                           const char *callee,
714                           int doGnsLookup)
715 {
716   struct GNUNET_PeerIdentity peer;
717
718   if (NULL == h || NULL == h->client)
719     return;
720
721   if (GNUNET_YES == doGnsLookup)
722   {
723     gns_lookup_and_call (h, callee);
724     return;
725   }
726   if (GNUNET_OK !=
727       GNUNET_CRYPTO_ecc_public_sign_key_from_string (callee, 
728                                                      strlen (callee),
729                                                      &peer.public_key))
730   {
731     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
732                 _("`%s'  is not a valid public key\n"),
733                 callee);
734     h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL);
735     return;
736   }  
737   initiate_call (h, peer);
738 }
739
740
741 void
742 GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h)
743 {
744   if (NULL == h || NULL == h->client)
745     return;
746
747   terminate_call (h);
748 }
749
750
751 void
752 GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h)
753 {
754   if (NULL == h || NULL == h->client)
755     return;
756
757   accept_call (h);
758 }
759
760
761 void
762 GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h)
763 {
764   if (NULL == h || NULL == h->client)
765     return;
766
767   reject_call (h);
768 }
769
770 /* end of conversation_api.c */