-transmit hangup
[oweals/gnunet.git] / src / conversation / gnunet-service-conversation-new.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  * @file conversation/gnunet-service-conversation.c
22  * @brief conversation service implementation
23  * @author Simon Dieterle
24  * @author Andreas Fuchs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_applications.h"
31 #include "gnunet_constants.h"
32 #include "gnunet_signatures.h"
33 #include "gnunet_mesh_service.h"
34 #include "gnunet_conversation_service.h"
35 #include "conversation.h"
36
37
38 /**
39  * How long is our signature on a call valid?  Needs to be long enough for time zone
40  * differences and network latency to not matter.  No strong need for it to be short,
41  * but we simply like all signatures to eventually expire.
42  */
43 #define RING_TIMEOUT GNUNET_TIME_UNIT_DAYS
44
45
46 /**
47  * The possible connection status
48  */
49 enum LineStatus
50 {
51   /**
52    * We are waiting for incoming calls.
53    */
54   LS_CALLEE_LISTEN,
55
56   /**
57    * Our phone is ringing, waiting for the client to pick up.
58    */
59   LS_CALLEE_RINGING,
60
61   /**
62    * We are talking!
63    */
64   LS_CALLEE_CONNECTED,
65
66   /**
67    * We are waiting for the phone to be picked up.
68    */
69   LS_CALLER_CALLING,
70
71   /**
72    * We are talking!
73    */
74   LS_CALLER_CONNECTED,
75
76   /**
77    * We're in shutdown, sending hangup messages before cleaning up.
78    */
79   LS_CALLER_SHUTDOWN
80 };
81
82
83 /**
84  * A line connects a local client with a mesh tunnel (or, if it is an
85  * open line, is waiting for a mesh tunnel).
86  */
87 struct Line
88 {
89   /**
90    * Kept in a DLL.
91    */
92   struct Line *next;
93
94   /**
95    * Kept in a DLL.
96    */
97   struct Line *prev;
98
99   /**
100    * Handle for the reliable tunnel (contol data)
101    */
102   struct GNUNET_MESH_Tunnel *tunnel_reliable;
103   
104   /**
105    * Handle for unreliable tunnel (audio data)
106    */
107   struct GNUNET_MESH_Tunnel *tunnel_unreliable;
108
109   /**
110    * Transmit handle for pending audio messages
111    */
112   struct GNUNET_MESH_TransmitHandle *unreliable_mth;
113
114   /**
115    * Message queue for control messages
116    */
117   struct GNUNET_MQ_Handle *reliable_mq;
118
119   /**
120    * Handle to the line client.
121    */
122   struct GNUNET_SERVER_Client *client;
123
124   /**
125    * Target of the line, if we are the caller.
126    */
127   struct GNUNET_PeerIdentity target;
128
129   /**
130    * Our line number.
131    */
132   uint32_t local_line;
133
134   /**
135    * Remote line number.
136    */
137   uint32_t remote_line;
138
139   /**
140    * Current status of this line.
141    */ 
142   enum LineStatus status;
143
144 };
145
146
147 /**
148  * Our configuration.
149  */
150 static const struct GNUNET_CONFIGURATION_Handle *cfg;
151
152 /**
153  * Notification context containing all connected clients.
154  */
155 static struct GNUNET_SERVER_NotificationContext *nc;
156
157 /**
158  * Handle for mesh
159  */
160 static struct GNUNET_MESH_Handle *mesh;
161
162 /**
163  * Identity of this peer.
164  */
165 static struct GNUNET_PeerIdentity my_identity;
166
167 /**
168  * Head of DLL of active lines.
169  */
170 static struct Line *lines_head;
171
172 /**
173  * Tail of DLL of active lines.
174  */
175 static struct Line *lines_tail;
176
177 /**
178  * Counter for generating local line numbers.
179  * FIXME: randomize generation in the future
180  * to eliminate information leakage.
181  */
182 static uint32_t local_line_cnt;
183
184
185 /**
186  * Function to register a phone.
187  *
188  * @param cls closure, NULL
189  * @param client the client from which the message is
190  * @param message the message from the client
191  */
192 static void
193 handle_client_register_message (void *cls,
194                                 struct GNUNET_SERVER_Client *client,
195                                 const struct GNUNET_MessageHeader *message)
196 {
197   const struct ClientPhoneRegisterMessage *msg;
198   struct Line *line;
199
200   msg = (struct ClientPhoneRegisterMessage *) message;
201   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
202   if (NULL != line)
203   {
204     GNUNET_break (0);
205     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
206     return;
207   }
208   line = GNUNET_new (struct Line);
209   line->client = client;
210   GNUNET_SERVER_notification_context_add (nc, client);
211   GNUNET_CONTAINER_DLL_insert (lines_head,
212                                lines_tail,
213                                line);
214   line->local_line = ntohl (msg->line);
215   GNUNET_SERVER_client_set_user_context (client, line);
216   GNUNET_SERVER_receive_done (client, GNUNET_OK);
217 }
218
219
220 /**
221  * Function to handle a pickup request message from the client
222  *
223  * @param cls closure, NULL
224  * @param client the client from which the message is
225  * @param message the message from the client
226  */
227 static void
228 handle_client_pickup_message (void *cls,
229                               struct GNUNET_SERVER_Client *client,
230                               const struct GNUNET_MessageHeader *message)
231 {
232   const struct ClientPhonePickupMessage *msg;
233   struct GNUNET_MQ_Envelope *e;
234   struct MeshPhonePickupMessage *mppm;
235   const char *meta;
236   struct Line *line;
237   size_t len;
238
239   msg = (struct ClientPhonePickupMessage *) message;
240   meta = (const char *) &msg[1];
241   len = ntohs (msg->header.size) - sizeof (struct ClientPhonePickupMessage);
242   if ( (0 == len) ||
243        ('\0' != meta[len - 1]) )
244   {
245     meta = NULL;
246     len = 0;
247   }
248   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
249   if (NULL == line)
250   {
251     GNUNET_break (0);
252     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
253     return;
254   }
255   line->status = LS_CALLEE_CONNECTED;
256   e = GNUNET_MQ_msg_extra (mppm,
257                            len,
258                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP);
259   memcpy (&mppm[1], meta, len);
260   GNUNET_MQ_send (line->reliable_mq, e);
261   GNUNET_SERVER_receive_done (client, GNUNET_OK);
262 }
263
264
265 /**
266  * Function to handle a hangup request message from the client
267  *
268  * @param cls closure, NULL
269  * @param client the client from which the message is
270  * @param message the message from the client
271  */
272 static void
273 handle_client_hangup_message (void *cls,
274                               struct GNUNET_SERVER_Client *client,
275                               const struct GNUNET_MessageHeader *message)
276 {
277   const struct ClientPhoneHangupMessage *msg;
278   struct GNUNET_MQ_Envelope *e;
279   struct MeshPhoneHangupMessage *mhum;
280   const char *meta;
281   struct Line *line;
282   size_t len;
283
284   msg = (struct ClientPhoneHangupMessage *) message;
285   meta = (const char *) &msg[1];
286   len = ntohs (msg->header.size) - sizeof (struct ClientPhoneHangupMessage);
287   if ( (0 == len) ||
288        ('\0' != meta[len - 1]) )
289   {
290     meta = NULL;
291     len = 0;
292   }
293   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
294   if (NULL == line)
295   {
296     GNUNET_break (0);
297     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
298     return;
299   }
300   line->status = LS_CALLEE_LISTEN;
301   e = GNUNET_MQ_msg_extra (mhum,
302                            len,
303                            GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP);
304   memcpy (&mhum[1], meta, len);
305   GNUNET_MQ_send (line->reliable_mq, e);
306   GNUNET_SERVER_receive_done (client, GNUNET_OK);
307 }
308
309
310 /**
311  * Function to handle call request the client
312  *
313  * @param cls closure, NULL
314  * @param client the client from which the message is
315  * @param message the message from the client
316  */
317 static void
318 handle_client_call_message (void *cls,
319                             struct GNUNET_SERVER_Client *client,
320                             const struct GNUNET_MessageHeader *message)
321 {
322   const struct ClientCallMessage *msg;
323   struct Line *line;
324   struct GNUNET_MQ_Envelope *e;
325   struct MeshPhoneRingMessage *ring;
326
327   msg = (struct ClientCallMessage *) message;
328   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
329   if (NULL != line)
330   {
331     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
332     return;
333   }
334   line = GNUNET_new (struct Line);
335   line->target = msg->target;
336   GNUNET_CONTAINER_DLL_insert (lines_head,
337                                lines_tail,
338                                line);
339   line->remote_line = ntohl (msg->line);
340   line->status = LS_CALLER_CALLING;
341   line->tunnel_reliable = GNUNET_MESH_tunnel_create (mesh,
342                                                      line,
343                                                      &msg->target,
344                                                      GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
345                                                      GNUNET_NO,
346                                                      GNUNET_YES);
347   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
348   line->local_line = local_line_cnt++;
349   e = GNUNET_MQ_msg (ring, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING);
350   ring->purpose.purpose = htonl (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING);
351   ring->purpose.size = htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
352                               sizeof (struct GNUNET_TIME_AbsoluteNBO) +
353                               sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
354                               sizeof (struct GNUNET_CRYPTO_EccPublicSignKey));
355   GNUNET_CRYPTO_ecc_key_get_public_for_signature (&msg->caller_id,
356                                                   &ring->caller_id);
357   ring->remote_line = msg->line;
358   ring->source_line = line->local_line;
359   ring->target = msg->target;
360   ring->source = my_identity;
361   ring->expiration_time = GNUNET_TIME_absolute_hton (GNUNET_TIME_relative_to_absolute (RING_TIMEOUT));
362   GNUNET_CRYPTO_ecc_sign (&msg->caller_id,
363                           &ring->purpose,
364                           &ring->signature);
365   GNUNET_MQ_send (line->reliable_mq, e);
366   GNUNET_SERVER_client_set_user_context (client, line);
367   GNUNET_SERVER_receive_done (client, GNUNET_OK);
368 }
369
370
371 /**
372  * Function to handle audio data from the client
373  *
374  * @param cls closure, NULL
375  * @param client the client from which the message is
376  * @param message the message from the client
377  */
378 static void
379 handle_client_audio_message (void *cls,
380                              struct GNUNET_SERVER_Client *client,
381                              const struct GNUNET_MessageHeader *message)
382 {
383   const struct ClientAudioMessage *msg;
384
385   msg = (struct ClientAudioMessage *) message;
386   GNUNET_break (0); // FIXME
387   GNUNET_SERVER_receive_done (client, GNUNET_OK);
388 }
389
390
391 /**
392  * Function to handle a ring message incoming over mesh
393  *
394  * @param cls closure, NULL
395  * @param tunnel the tunnel over which the message arrived
396  * @param tunnel_ctx the tunnel context, can be NULL
397  * @param message the incoming message
398  * @return #GNUNET_OK
399  */
400 static int
401 handle_mesh_ring_message (void *cls,
402                           struct GNUNET_MESH_Tunnel *tunnel,
403                           void **tunnel_ctx,
404                           const struct GNUNET_MessageHeader *message)
405 {
406   const struct MeshPhoneRingMessage *msg;
407   struct Line *line;
408   struct GNUNET_MQ_Envelope *e;
409   struct MeshPhoneBusyMessage *busy;
410   struct ClientPhoneRingMessage cring;
411   
412   msg = (const struct MeshPhoneRingMessage *) message;
413   if ( (msg->purpose.size != htonl (sizeof (struct GNUNET_PeerIdentity) * 2 +
414                                     sizeof (struct GNUNET_TIME_AbsoluteNBO) +
415                                     sizeof (struct GNUNET_CRYPTO_EccSignaturePurpose) +
416                                     sizeof (struct GNUNET_CRYPTO_EccPublicSignKey))) ||
417        (GNUNET_OK !=
418         GNUNET_CRYPTO_ecc_verify (GNUNET_SIGNATURE_PURPOSE_CONVERSATION_RING,
419                                   &msg->purpose,
420                                   &msg->signature,
421                                   &msg->caller_id)) )
422   {
423     GNUNET_break_op (0);
424     return GNUNET_SYSERR;
425   }
426   for (line = lines_head; NULL != line; line = line->next)  
427     if ( (line->local_line == ntohl (msg->remote_line)) &&
428          (LS_CALLEE_LISTEN == line->status) )
429       break;
430   if (NULL == line) 
431   {
432     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
433                 _("No available phone for incoming call on line %u, sending BUSY signal\n"),
434                 ntohl (msg->remote_line));
435     e = GNUNET_MQ_msg (busy, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY);
436     GNUNET_MQ_send (line->reliable_mq, e);
437     return GNUNET_OK;
438   }
439   line->status = LS_CALLEE_RINGING;
440   line->remote_line = ntohl (msg->source_line);
441   line->tunnel_reliable = tunnel;
442   line->reliable_mq = GNUNET_MESH_mq_create (line->tunnel_reliable);
443   *tunnel_ctx = line;
444   cring.header.type = htons (GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_RING);
445   cring.header.size = htons (sizeof (cring));
446   cring.reserved = htonl (0);
447   cring.caller_id = msg->caller_id;
448   GNUNET_SERVER_notification_context_unicast (nc,
449                                               line->client,
450                                               &cring.header,
451                                               GNUNET_NO);
452   return GNUNET_OK;
453 }
454
455
456 /**
457  * Function to handle a hangup message incoming over mesh
458  *
459  * @param cls closure, NULL
460  * @param tunnel the tunnel over which the message arrived
461  * @param tunnel_ctx the tunnel context, can be NULL
462  * @param message the incoming message
463  * @return #GNUNET_OK
464  */
465 static int
466 handle_mesh_hangup_message (void *cls,
467                             struct GNUNET_MESH_Tunnel *tunnel,
468                             void **tunnel_ctx,
469                             const struct GNUNET_MessageHeader *message)
470 {
471   const struct MeshPhoneHangupMessage *msg;
472   
473   msg = (const struct MeshPhoneHangupMessage *) message;
474   GNUNET_break (0); // FIXME
475   return GNUNET_OK;
476 }
477
478
479 /**
480  * Function to handle a pickup message incoming over mesh
481  *
482  * @param cls closure, NULL
483  * @param tunnel the tunnel over which the message arrived
484  * @param tunnel_ctx the tunnel context, can be NULL
485  * @param message the incoming message
486  * @return #GNUNET_OK
487  */
488 static int
489 handle_mesh_pickup_message (void *cls,
490                             struct GNUNET_MESH_Tunnel *tunnel,
491                             void **tunnel_ctx,
492                             const struct GNUNET_MessageHeader *message)
493 {
494   const struct MeshPhonePickupMessage *msg;
495   struct Line *line = *tunnel_ctx;
496   
497   msg = (const struct MeshPhonePickupMessage *) message;
498   GNUNET_break (0); // FIXME
499
500
501   line->tunnel_unreliable = GNUNET_MESH_tunnel_create (mesh,
502                                                        line,
503                                                        &line->target,
504                                                        GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
505                                                        GNUNET_YES,
506                                                        GNUNET_NO);
507   
508
509   return GNUNET_OK;
510 }
511
512
513 /**
514  * Function to handle a busy message incoming over mesh
515  *
516  * @param cls closure, NULL
517  * @param tunnel the tunnel over which the message arrived
518  * @param tunnel_ctx the tunnel context, can be NULL
519  * @param message the incoming message
520  * @return #GNUNET_OK
521  */
522 static int
523 handle_mesh_busy_message (void *cls,
524                           struct GNUNET_MESH_Tunnel *tunnel,
525                           void **tunnel_ctx,
526                           const struct GNUNET_MessageHeader *message)
527 {
528   const struct MeshPhoneBusyMessage *msg;
529   
530   msg = (const struct MeshPhoneBusyMessage *) message;
531   GNUNET_break (0); // FIXME
532   return GNUNET_OK;
533 }
534
535
536 /**
537  * Function to handle an audio message incoming over mesh
538  *
539  * @param cls closure, NULL
540  * @param tunnel the tunnel over which the message arrived
541  * @param tunnel_ctx the tunnel context, can be NULL
542  * @param message the incoming message
543  * @return #GNUNET_OK
544  */
545 static int
546 handle_mesh_audio_message (void *cls,
547                            struct GNUNET_MESH_Tunnel *tunnel,
548                            void **tunnel_ctx,
549                            const struct GNUNET_MessageHeader *message)
550 {
551   const struct MeshAudioMessage *msg;
552   
553   msg = (const struct MeshAudioMessage *) message;
554   GNUNET_break (0); // FIXME
555   return GNUNET_OK;
556 }
557
558
559 /**
560  * Method called whenever another peer has added us to a tunnel
561  * the other peer initiated.
562  *
563  * @param cls closure
564  * @param tunnel new handle to the tunnel
565  * @param initiator peer that started the tunnel
566  * @param port port
567  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
568  */
569 static void *
570 inbound_tunnel (void *cls,
571                 struct GNUNET_MESH_Tunnel *tunnel,
572                 const struct GNUNET_PeerIdentity *initiator, 
573                 uint32_t port)
574 {
575   
576   GNUNET_break (0); // FIXME
577   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
578               _("Received incoming tunnel on port %d\n"), port);
579   return NULL;
580 }
581
582
583 /**
584  * Function called whenever an inbound tunnel is destroyed.  Should clean up
585  * any associated state.
586  *
587  * @param cls closure (set from #GNUNET_MESH_connect)
588  * @param tunnel connection to the other end (henceforth invalid)
589  * @param tunnel_ctx place where local state associated
590  *                   with the tunnel is stored
591  */
592 static void
593 inbound_end (void *cls,
594              const struct GNUNET_MESH_Tunnel *tunnel,
595              void *tunnel_ctx)
596 {
597   GNUNET_break (0); // FIXME
598 }
599
600
601 /**
602  * A client disconnected.  Remove all of its data structure entries.
603  *
604  * @param cls closure, NULL
605  * @param client identification of the client
606  */
607 static void
608 handle_client_disconnect (void *cls, 
609                           struct GNUNET_SERVER_Client *client)
610 {
611   struct Line *line;
612
613   line = GNUNET_SERVER_client_get_user_context (client, struct Line);
614   if (NULL == line)
615     return;
616   GNUNET_CONTAINER_DLL_remove (lines_head,
617                                lines_tail,
618                                line);
619   GNUNET_free (line);
620   GNUNET_SERVER_client_set_user_context (client, NULL);
621 }
622
623
624 /**
625  * Shutdown nicely
626  * 
627  * @param cls closure, NULL
628  * @param tc the task context
629  */
630 static void
631 do_shutdown (void *cls,
632              const struct GNUNET_SCHEDULER_TaskContext *tc)
633 {
634   GNUNET_break (0); // FIXME
635   if (NULL != mesh)
636   {
637     GNUNET_MESH_disconnect (mesh);
638     mesh = NULL;
639   }
640   if (NULL != nc)
641   {
642     GNUNET_SERVER_notification_context_destroy (nc);
643     nc = NULL;
644   }
645 }
646
647
648 /**
649  * Main function that will be run by the scheduler.
650  *
651  * @param cls closure
652  * @param server server handle
653  * @param c configuration
654  */
655 static void
656 run (void *cls, 
657      struct GNUNET_SERVER_Handle *server,
658      const struct GNUNET_CONFIGURATION_Handle *c)
659 {
660   static const struct GNUNET_SERVER_MessageHandler server_handlers[] = {
661     {&handle_client_register_message, NULL,
662      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_REGISTER,
663      sizeof (struct ClientPhoneRegisterMessage)},
664     {&handle_client_pickup_message, NULL,
665      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_PICK_UP,
666      0},
667     {&handle_client_hangup_message, NULL,
668      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_HANG_UP,
669      0},
670     {&handle_client_call_message, NULL,
671      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_PHONE_CALL,
672      0},
673     {&handle_client_audio_message, NULL,
674      GNUNET_MESSAGE_TYPE_CONVERSATION_CS_AUDIO,
675      0},
676     {NULL, NULL, 0, 0}
677   };
678   static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
679     {&handle_mesh_ring_message,
680      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_RING,
681      sizeof (struct MeshPhoneRingMessage)},
682     {&handle_mesh_hangup_message, 
683      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_HANG_UP,
684      0},
685     {&handle_mesh_pickup_message, 
686      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_PICK_UP,
687      0},
688     {&handle_mesh_busy_message, 
689      GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_PHONE_BUSY,
690      sizeof (struct MeshPhoneBusyMessage)},
691     {&handle_mesh_audio_message, GNUNET_MESSAGE_TYPE_CONVERSATION_MESH_AUDIO,
692      0},
693     {NULL, 0, 0}
694   };
695   static uint32_t ports[] = { 
696     GNUNET_APPLICATION_TYPE_CONVERSATION_CONTROL,
697     GNUNET_APPLICATION_TYPE_CONVERSATION_AUDIO,
698     0 
699   };
700
701   cfg = c;
702   GNUNET_assert (GNUNET_OK ==
703                  GNUNET_CRYPTO_get_host_identity (cfg,
704                                                   &my_identity));
705   mesh = GNUNET_MESH_connect (cfg,
706                               NULL,
707                               &inbound_tunnel,
708                               &inbound_end, 
709                               mesh_handlers, 
710                               ports);
711
712   if (NULL == mesh)
713   {
714     GNUNET_break (0);
715     GNUNET_SCHEDULER_shutdown ();
716     return;
717   }
718   nc = GNUNET_SERVER_notification_context_create (server, 16);
719   GNUNET_SERVER_add_handlers (server, server_handlers);
720   GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, NULL);
721   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 
722                                 &do_shutdown,
723                                 NULL);
724 }
725
726
727 /**
728  * The main function for the conversation service.
729  *
730  * @param argc number of arguments from the command line
731  * @param argv command line arguments
732  * @return 0 ok, 1 on error
733  */
734 int
735 main (int argc, 
736       char *const *argv)
737 {
738   return (GNUNET_OK ==
739           GNUNET_SERVICE_run (argc, argv,
740                               "conversation", 
741                               GNUNET_SERVICE_OPTION_NONE,
742                               &run, NULL)) ? 0 : 1;
743 }
744
745 /* end of gnunet-service-conversation.c */