-ensure external symbols have proper prefix for 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   return;
215 }
216
217 /**
218 * Check if the gns txt record for conversation exits
219 */
220 static void
221 check_gns (struct GNUNET_CONVERSATION_Handle *h)
222 {
223   GNUNET_GNS_lookup (h->gns, "conversation.gads", 
224                      NULL /* FIXME_ZONE */,
225                      GNUNET_DNSPARSER_TYPE_TXT,
226                      GNUNET_NO, 
227                      NULL, 
228                      &check_gns_cb, h);
229
230   return;
231 }
232
233 /******************************************************************************/
234 /***********************      RECEIVE HANDLERS     ****************************/
235 /******************************************************************************/
236
237 /**
238  * Function to process all messages received from the service
239  *
240  * @param cls closure
241  * @param msg message received, NULL on timeout or fatal error
242  */
243 static void
244 receive_message_cb (void *cls, const struct GNUNET_MessageHeader *msg)
245 {
246   struct GNUNET_CONVERSATION_Handle *h = cls;
247   struct ServerClientSessionInitiateMessage *imsg;
248   struct ServerClientSessionRejectMessage *rmsg;
249   struct GNUNET_CONVERSATION_MissedCallNotification *missed_calls;
250
251   if (NULL != msg)
252     {
253       switch (ntohs (msg->type))
254         {
255         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_ACCEPT:
256           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
257                       _("%s has accepted your call.\n"),
258                       GNUNET_i2s_full (&(h->call->peer)));
259
260           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_ACCEPTED,
261                                    &(h->call->peer));
262           h->call->type = CALLEE;
263
264           break;
265
266         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_REJECT:
267           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
268                       _("%s has rejected your call.\n"),
269                       GNUNET_i2s_full (&(h->call->peer)));
270
271           rmsg = (struct ServerClientSessionRejectMessage *) msg;
272           h->reject_handler (NULL, h, ntohs (rmsg->reason), &(h->call->peer));
273           GNUNET_free (h->call);
274           h->call = NULL;
275
276           break;
277
278         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_TERMINATE:
279           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
280                       _("%s has terminated the call.\n"),
281                       GNUNET_i2s_full (&(h->call->peer)));
282
283           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_CALL_TERMINATED,
284                                    &(h->call->peer));
285           GNUNET_free (h->call);
286           h->call = NULL;
287
288           break;
289
290         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SESSION_INITIATE:
291           imsg = (struct ServerClientSessionInitiateMessage *) msg;
292
293           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("%s wants to call you.\n"),
294                       GNUNET_i2s_full (&(imsg->peer)));
295
296           h->call =
297             (struct GNUNET_CONVERSATION_CallInformation *)
298             GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
299           memcpy (&(h->call->peer), &(imsg->peer),
300                   sizeof (struct GNUNET_PeerIdentity));
301           h->call_handler (NULL, h, &(h->call->peer));
302           h->call->type = CALLEE;
303
304           break;
305
306         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_MISSED_CALL:
307           missed_calls =
308             (struct GNUNET_CONVERSATION_MissedCallNotification *) (msg +
309                                                            (sizeof
310                                                             (struct
311                                                              GNUNET_MessageHeader)));
312           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
313                       _("You &d have missed a calls.\n"),
314                       missed_calls->number);
315           h->missed_call_handler (NULL, h, missed_calls);
316           break;
317
318         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_SERVICE_BLOCKED:
319           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("The service is blocked.\n"));
320           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_SERVICE_BLOCKED,
321                                    NULL);
322           break;
323
324         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_PEER_NOT_CONNECTED:
325           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
326                       _("The peer you are calling is not connected.\n"));
327           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL);
328           break;
329
330         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_NO_ANSWER:
331           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
332                       _("The peer you are calling does not answer.\n"));
333           h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_ANSWER,
334                                    &(h->call->peer));
335           break;
336
337         case GNUNET_MESSAGE_TYPE_CONVERSATION_SC_ERROR:
338           GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Generic error occured.\n"));
339           break;
340
341         default:
342           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
343                       _("Got unknown message type.\n"));
344           break;
345         }
346
347     }
348
349   GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
350                          GNUNET_TIME_UNIT_FOREVER_REL);
351 }
352
353 /******************************************************************************/
354 /************************       SEND FUNCTIONS     ****************************/
355 /******************************************************************************/
356
357 /**
358  * Function called to send a session initiate message to the service.
359  * "buf" will be NULL and "size" zero if the socket was closed for writing in
360  * the meantime.
361  *
362  * @param cls closure, NULL
363  * @param size number of bytes available in buf
364  * @param buf where the callee should write the initiate message
365  * @return number of bytes written to buf
366  */
367 static size_t
368 transmit_session_initiate_message (void *cls, size_t size, void *buf)
369 {
370   size_t msg_size;
371   struct ClientServerSessionInitiateMessage *msg;
372   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
373
374   msg_size = sizeof (struct ClientServerSessionInitiateMessage);
375
376   GNUNET_assert (size >= msg_size);
377   msg = buf;
378   msg->header.size = htons (msg_size);
379   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_INITIATE);
380   memcpy (&msg->peer, &(h->call->peer), sizeof (struct GNUNET_PeerIdentity));
381
382   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
383               _
384               ("Sending ClientServerSessionInitiateMessage to the service for peer: %s\n"),
385               GNUNET_i2s_full (&(h->call->peer)));
386
387   h->call->type = CALLER;
388
389   return msg_size;
390 }
391
392 /**
393  * Function called to send a session accept message to the service.
394  * "buf" will be NULL and "size" zero if the socket was closed for writing in
395  * the meantime.
396  *
397  * @param cls closure, NULL
398  * @param size number of bytes available in buf
399  * @param buf where the callee should write the accept message
400  * @return number of bytes written to buf
401  */
402 static size_t
403 transmit_session_accept_message (void *cls, size_t size, void *buf)
404 {
405   size_t msg_size;
406   struct ClientServerSessionAcceptMessage *msg;
407   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
408
409   msg_size = sizeof (struct ClientServerSessionAcceptMessage);
410
411   GNUNET_assert (size >= msg_size);
412   msg = buf;
413   msg->header.size = htons (msg_size);
414   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_ACCEPT);
415
416   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
417               _
418               ("Sending ClienServerSessionAcceptMessage to the service for peer: %s\n"),
419               GNUNET_i2s_full (&(h->call->peer)));
420
421   h->call->established = GNUNET_YES;
422
423   return msg_size;
424 }
425
426 /**
427  * Function called to send a session reject message to the service.
428  * "buf" will be NULL and "size" zero if the socket was closed for writing in
429  * the meantime.
430  *
431  * @param cls closure, NULL
432  * @param size number of bytes available in buf
433  * @param buf where the callee should write the reject message
434  * @return number of bytes written to buf
435  */
436 static size_t
437 transmit_session_reject_message (void *cls, size_t size, void *buf)
438 {
439   size_t msg_size;
440   struct ClientServerSessionRejectMessage *msg;
441   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
442
443   msg_size = sizeof (struct ClientServerSessionRejectMessage);
444
445   GNUNET_assert (size >= msg_size);
446   msg = buf;
447   msg->header.size = htons (msg_size);
448   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_REJECT);
449   msg->reason = htons (GNUNET_CONVERSATION_REJECT_REASON_NOT_WANTED);
450
451   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
452               _
453               ("Sending ClientServerSessionRejectMessage to the service for peer: %s\n"),
454               GNUNET_i2s_full (&(h->call->peer)));
455
456   GNUNET_free (h->call);
457   h->call = NULL;
458
459   return msg_size;
460 }
461
462 /**
463  * Function called to send a session terminate message to the service.
464  * "buf" will be NULL and "size" zero if the socket was closed for writing in
465  * the meantime.
466  *
467  * @param cls closure, NULL
468  * @param size number of bytes available in buf
469  * @param buf where the callee should write the terminate message
470  * @return number of bytes written to buf
471  */
472 static size_t
473 transmit_session_terminate_message (void *cls, size_t size, void *buf)
474 {
475   size_t msg_size;
476   struct ClientServerSessionTerminateMessage *msg;
477   struct GNUNET_CONVERSATION_Handle *h = (struct GNUNET_CONVERSATION_Handle *) cls;
478
479   msg_size = sizeof (struct ClientServerSessionTerminateMessage);
480
481   GNUNET_assert (size >= msg_size);
482   msg = buf;
483   msg->header.size = htons (msg_size);
484   msg->header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_SESSION_TERMINATE);
485
486   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
487               _
488               ("Sending ClientServerSessionTerminateMessage to the service for peer: %s\n"),
489               GNUNET_i2s_full (&(h->call->peer)));
490
491   GNUNET_free (h->call);
492   h->call = NULL;
493
494   return msg_size;
495 }
496
497 /**
498  * Auxiliary function to call a peer.
499  * 
500  * @param h conversation handle
501  * @return 
502  */
503 static void
504 initiate_call (struct GNUNET_CONVERSATION_Handle *h, struct GNUNET_PeerIdentity peer)
505 {
506   h->call =
507     (struct GNUNET_CONVERSATION_CallInformation *)
508     GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_CallInformation));
509   memcpy (&(h->call->peer), &peer, sizeof (struct GNUNET_PeerIdentity));
510
511   GNUNET_CLIENT_notify_transmit_ready (h->client,
512                                        sizeof (struct
513                                                ClientServerSessionInitiateMessage),
514                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
515                                        &transmit_session_initiate_message, h);
516
517   return;
518 }
519
520 /**
521  * Auxiliary function to accept a call.
522  * 
523  * @param h conversation handle
524  */
525 static void
526 accept_call (struct GNUNET_CONVERSATION_Handle *h)
527 {
528   GNUNET_CLIENT_notify_transmit_ready (h->client,
529                                        sizeof (struct
530                                                ClientServerSessionAcceptMessage),
531                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
532                                        &transmit_session_accept_message, h);
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  * Auxiliary function to terminate a call.
552  * 
553  * @param h conversation handle
554  */
555 static void
556 terminate_call (struct GNUNET_CONVERSATION_Handle *h)
557 {
558   GNUNET_CLIENT_notify_transmit_ready (h->client,
559                                        sizeof (struct
560                                                ClientServerSessionTerminateMessage),
561                                        MAX_TRANSMIT_DELAY, GNUNET_YES,
562                                        &transmit_session_terminate_message,
563                                        h);
564 }
565
566 /**
567 *
568 */
569 static void
570 gns_call_cb (void *cls, uint32_t rd_count,
571              const struct GNUNET_NAMESTORE_RecordData *rd)
572 {
573   struct GNUNET_CONVERSATION_Handle *handle = cls;
574   struct GNUNET_PeerIdentity peer;
575   unsigned int i;
576
577   for (i=0;i<rd_count;i++)
578   {
579     switch (rd[i].record_type)
580     {
581     case GNUNET_DNSPARSER_TYPE_TXT: /* FIXME:  use fresh record type for voide... */
582       if (GNUNET_OK !=
583           GNUNET_CRYPTO_ecc_public_sign_key_from_string (rd[i].data,
584                                                          rd[i].data_size,
585                                                          &peer.public_key))
586       {
587         GNUNET_break_op (0);
588         continue;
589       }      
590       initiate_call (handle, peer);
591       return;
592     default:
593       break;
594     }
595   }
596   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
597               "Lookup failed\n");
598   handle->notification_handler (NULL, handle, 
599                                 GNUNET_CONVERSATION_NT_NO_PEER,
600                                 NULL);
601 }
602
603
604 /**
605 * GNS lookup
606 */
607 static void
608 gns_lookup_and_call (struct GNUNET_CONVERSATION_Handle *h, const char *callee)
609 {
610   char domain[GNUNET_DNSPARSER_MAX_NAME_LENGTH];
611   char *pos;
612
613   pos = domain;
614   strcpy (pos, "conversation");
615   pos += strlen ("conversation");
616   strcpy (pos, ".");
617   pos++;
618   strcpy (pos, callee);
619   pos += strlen (callee);
620
621   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Lookup for %s\n", domain);
622
623   GNUNET_GNS_lookup (h->gns,
624                      domain,
625                      NULL /* FIXME: ZONE! */,
626                      GNUNET_DNSPARSER_TYPE_TXT,
627                      GNUNET_NO, 
628                      NULL,
629                      &gns_call_cb, h);
630 }
631
632
633 /******************************************************************************/
634 /**********************      API CALL DEFINITIONS     *************************/
635 /******************************************************************************/
636
637 struct GNUNET_CONVERSATION_Handle *
638 GNUNET_CONVERSATION_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, void *cls,
639                      GNUNET_CONVERSATION_CallHandler * call_handler,
640                      GNUNET_CONVERSATION_RejectHandler * reject_handler,
641                      GNUNET_CONVERSATION_NotificationHandler * notification_handler,
642                      GNUNET_CONVERSATION_MissedCallHandler * missed_call_handler)
643 {
644   struct GNUNET_CONVERSATION_Handle *h;
645
646   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "GNUNET_CONVERSATION_connect()\n");
647   h = GNUNET_malloc (sizeof (struct GNUNET_CONVERSATION_Handle));
648
649   h->cfg = cfg;
650   h->call_handler = call_handler;
651   h->reject_handler = reject_handler;
652   h->notification_handler = notification_handler;
653   h->missed_call_handler = missed_call_handler;
654
655   if (NULL == (h->client = GNUNET_CLIENT_connect ("conversation", cfg)))
656     {
657       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access CONVERSATION service\n");
658       GNUNET_break (0);
659       GNUNET_free (h);
660
661       return NULL;
662     }
663
664   if (NULL == (h->gns = GNUNET_GNS_connect (cfg)))
665     {
666       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Could not access GNS service\n");
667       GNUNET_break (0);
668       GNUNET_CLIENT_disconnect (h->client);
669       GNUNET_free (h);
670
671       return NULL;
672     }
673
674   if (NULL == (h->namestore = GNUNET_NAMESTORE_connect (cfg)))
675     {
676       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
677                   "Could not access NAMESTORE service\n");
678       GNUNET_break (0);
679       GNUNET_CLIENT_disconnect (h->client);
680       GNUNET_GNS_disconnect (h->gns);
681       GNUNET_free (h);
682
683       return NULL;
684     }
685
686   check_gns (h);
687   GNUNET_CLIENT_receive (h->client, &receive_message_cb, h,
688                          GNUNET_TIME_UNIT_FOREVER_REL);
689
690   return h;
691 }
692
693 void
694 GNUNET_CONVERSATION_disconnect (struct GNUNET_CONVERSATION_Handle *handle)
695 {
696   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "CONVERSATION DISCONNECT\n");
697
698   GNUNET_CLIENT_disconnect (handle->client);
699   GNUNET_GNS_disconnect (handle->gns);
700
701   GNUNET_free (handle);
702   handle = NULL;
703 }
704
705
706 void
707 GNUNET_CONVERSATION_call (struct GNUNET_CONVERSATION_Handle *h, 
708                           const char *callee,
709                           int doGnsLookup)
710 {
711   struct GNUNET_PeerIdentity peer;
712
713   if (NULL == h || NULL == h->client)
714     return;
715
716   if (GNUNET_YES == doGnsLookup)
717   {
718     gns_lookup_and_call (h, callee);
719     return;
720   }
721   if (GNUNET_OK !=
722       GNUNET_CRYPTO_ecc_public_sign_key_from_string (callee, 
723                                                      strlen (callee),
724                                                      &peer.public_key))
725   {
726     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
727                 _("`%s'  is not a valid public key\n"),
728                 callee);
729     h->notification_handler (NULL, h, GNUNET_CONVERSATION_NT_NO_PEER, NULL);
730     return;
731   }  
732   initiate_call (h, peer);
733 }
734
735 void
736 GNUNET_CONVERSATION_hangup (struct GNUNET_CONVERSATION_Handle *h)
737 {
738   if (NULL == h || NULL == h->client)
739     return;
740
741   terminate_call (h);
742 }
743
744 void
745 GNUNET_CONVERSATION_accept (struct GNUNET_CONVERSATION_Handle *h)
746 {
747   if (NULL == h || NULL == h->client)
748     return;
749
750   accept_call (h);
751 }
752
753 void
754 GNUNET_CONVERSATION_reject (struct GNUNET_CONVERSATION_Handle *h)
755 {
756   if (NULL == h || NULL == h->client)
757     return;
758
759   reject_call (h);
760 }
761
762 /* end of conversation_api.c */