b919f6e020df8af0814da20c2b9db29963f83141
[oweals/gnunet.git] / src / set / set_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012-2014 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 set/set_api.c
22  * @brief api for the set service
23  * @author Florian Dold
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_protocols.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_set_service.h"
30 #include "set.h"
31
32
33 #define LOG(kind,...) GNUNET_log_from (kind, "set-api",__VA_ARGS__)
34
35 /**
36  * Opaque handle to a set.
37  */
38 struct GNUNET_SET_Handle
39 {
40   /**
41    * Client connected to the set service.
42    */
43   struct GNUNET_CLIENT_Connection *client;
44
45   /**
46    * Message queue for @e client.
47    */
48   struct GNUNET_MQ_Handle *mq;
49
50   /**
51    * Linked list of operations on the set.
52    */
53   struct GNUNET_SET_OperationHandle *ops_head;
54
55   /**
56    * Linked list of operations on the set.
57    */
58   struct GNUNET_SET_OperationHandle *ops_tail;
59
60   /**
61    * Callback for the current iteration over the set,
62    * NULL if no iterator is active.
63    */
64   GNUNET_SET_ElementIterator iterator;
65
66   /**
67    * Closure for @e iterator
68    */
69   void *iterator_cls;
70
71   /**
72    * Should the set be destroyed once all operations are gone?
73    */
74   int destroy_requested;
75
76   /**
77    * Has the set become invalid (e.g. service died)?
78    */
79   int invalid;
80
81   /**
82    * Both client and service count the number of iterators
83    * created so far to match replies with iterators.
84    */
85   uint16_t iteration_id;
86 };
87
88
89 /**
90  * Handle for a set operation request from another peer.
91  */
92 struct GNUNET_SET_Request
93 {
94   /**
95    * Id of the request, used to identify the request when
96    * accepting/rejecting it.
97    */
98   uint32_t accept_id;
99
100   /**
101    * Has the request been accepted already?
102    * #GNUNET_YES/#GNUNET_NO
103    */
104   int accepted;
105 };
106
107
108 /**
109  * Handle to an operation.  Only known to the service after committing
110  * the handle with a set.
111  */
112 struct GNUNET_SET_OperationHandle
113 {
114   /**
115    * Function to be called when we have a result,
116    * or an error.
117    */
118   GNUNET_SET_ResultIterator result_cb;
119
120   /**
121    * Closure for @e result_cb.
122    */
123   void *result_cls;
124
125   /**
126    * Local set used for the operation,
127    * NULL if no set has been provided by conclude yet.
128    */
129   struct GNUNET_SET_Handle *set;
130
131   /**
132    * Message sent to the server on calling conclude,
133    * NULL if conclude has been called.
134    */
135   struct GNUNET_MQ_Envelope *conclude_mqm;
136
137   /**
138    * Address of the request if in the conclude message,
139    * used to patch the request id into the message when the set is known.
140    */
141   uint32_t *request_id_addr;
142
143   /**
144    * Handles are kept in a linked list.
145    */
146   struct GNUNET_SET_OperationHandle *prev;
147
148   /**
149    * Handles are kept in a linked list.
150    */
151   struct GNUNET_SET_OperationHandle *next;
152
153   /**
154    * Request ID to identify the operation within the set.
155    */
156   uint32_t request_id;
157 };
158
159
160 /**
161  * Opaque handle to a listen operation.
162  */
163 struct GNUNET_SET_ListenHandle
164 {
165   /**
166    * Connection to the service.
167    */
168   struct GNUNET_CLIENT_Connection *client;
169
170   /**
171    * Message queue for the client.
172    */
173   struct GNUNET_MQ_Handle* mq;
174
175   /**
176    * Configuration handle for the listener, stored
177    * here to be able to reconnect transparently on
178    * connection failure.
179    */
180   const struct GNUNET_CONFIGURATION_Handle *cfg;
181
182   /**
183    * Function to call on a new incoming request,
184    * or on error.
185    */
186   GNUNET_SET_ListenCallback listen_cb;
187
188   /**
189    * Closure for @e listen_cb.
190    */
191   void *listen_cls;
192
193   /**
194    * Application ID we listen for.
195    */
196   struct GNUNET_HashCode app_id;
197
198   /**
199    * Time to wait until we try to reconnect on failure.
200    */
201   struct GNUNET_TIME_Relative reconnect_backoff;
202
203   /**
204    * Task for reconnecting when the listener fails.
205    */
206   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
207
208   /**
209    * Operation we listen for.
210    */
211   enum GNUNET_SET_OperationType operation;
212 };
213
214
215 /**
216  * Handle element for iteration over the set.  Notifies the
217  * iterator and sends an acknowledgement to the service.
218  *
219  * @param cls the `struct GNUNET_SET_Handle *`
220  * @param mh the message
221  */
222 static void
223 handle_iter_element (void *cls,
224                      const struct GNUNET_MessageHeader *mh)
225 {
226   struct GNUNET_SET_Handle *set = cls;
227   GNUNET_SET_ElementIterator iter = set->iterator;
228   struct GNUNET_SET_Element element;
229   const struct GNUNET_SET_IterResponseMessage *msg;
230   struct GNUNET_SET_IterAckMessage *ack_msg;
231   struct GNUNET_MQ_Envelope *ev;
232   uint16_t msize;
233
234   msize = ntohs (mh->size);
235   if (msize < sizeof (sizeof (struct GNUNET_SET_IterResponseMessage)))
236   {
237     /* message malformed */
238     GNUNET_break (0);
239     set->iterator = NULL;
240     set->iteration_id++;
241     iter (set->iterator_cls,
242           NULL);
243     iter = NULL;
244   }
245   msg = (const struct GNUNET_SET_IterResponseMessage *) mh;
246   if (set->iteration_id != ntohs (msg->iteration_id))
247   {
248     /* element from a previous iteration, skip! */
249     iter = NULL;
250   }
251   if (NULL != iter)
252   {
253     element.size = msize - sizeof (struct GNUNET_SET_IterResponseMessage);
254     element.element_type = htons (msg->element_type);
255     element.data = &msg[1];
256     iter (set->iterator_cls,
257           &element);
258   }
259   ev = GNUNET_MQ_msg (ack_msg,
260                       GNUNET_MESSAGE_TYPE_SET_ITER_ACK);
261   ack_msg->send_more = htonl ((NULL != iter));
262   GNUNET_MQ_send (set->mq, ev);
263 }
264
265
266 /**
267  * Handle message signalling conclusion of iteration over the set.
268  * Notifies the iterator that we are done.
269  *
270  * @param cls the set
271  * @param mh the message
272  */
273 static void
274 handle_iter_done (void *cls,
275                   const struct GNUNET_MessageHeader *mh)
276 {
277   struct GNUNET_SET_Handle *set = cls;
278   GNUNET_SET_ElementIterator iter = set->iterator;
279
280   if (NULL == iter)
281     return;
282   set->iterator = NULL;
283   set->iteration_id++;
284   iter (set->iterator_cls,
285         NULL);
286 }
287
288
289 /**
290  * Handle result message for a set operation.
291  *
292  * @param cls the set
293  * @param mh the message
294  */
295 static void
296 handle_result (void *cls,
297                const struct GNUNET_MessageHeader *mh)
298 {
299   struct GNUNET_SET_Handle *set = cls;
300   const struct GNUNET_SET_ResultMessage *msg;
301   struct GNUNET_SET_OperationHandle *oh;
302   struct GNUNET_SET_Element e;
303   enum GNUNET_SET_Status result_status;
304
305   msg = (const struct GNUNET_SET_ResultMessage *) mh;
306   GNUNET_assert (NULL != set->mq);
307   result_status = ntohs (msg->result_status);
308   oh = GNUNET_MQ_assoc_get (set->mq,
309                             ntohl (msg->request_id));
310   if (NULL == oh)
311   {
312     /* 'oh' can be NULL if we canceled the operation, but the service
313        did not get the cancel message yet. */
314     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315                 "Ignoring result from canceled operation\n");
316     return;
317   }
318   if (GNUNET_SET_STATUS_OK != result_status)
319   {
320     /* status is not #GNUNET_SET_STATUS_OK => there's no attached element,
321      * and this is the last result message we get */
322     GNUNET_MQ_assoc_remove (set->mq, ntohl (msg->request_id));
323     GNUNET_CONTAINER_DLL_remove (set->ops_head,
324                                  set->ops_tail,
325                                  oh);
326     if ( (GNUNET_YES == set->destroy_requested) &&
327          (NULL == set->ops_head) )
328       GNUNET_SET_destroy (set);
329     if (NULL != oh->result_cb)
330       oh->result_cb (oh->result_cls,
331                      NULL,
332                      result_status);
333     GNUNET_free (oh);
334     return;
335   }
336   e.data = &msg[1];
337   e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage);
338   e.element_type = msg->element_type;
339   if (NULL != oh->result_cb)
340     oh->result_cb (oh->result_cls,
341                    &e,
342                    result_status);
343 }
344
345
346 /**
347  * Handle request message for a listen operation
348  *
349  * @param cls the listen handle
350  * @param mh the message
351  */
352 static void
353 handle_request (void *cls,
354                 const struct GNUNET_MessageHeader *mh)
355 {
356   const struct GNUNET_SET_RequestMessage *msg = (const struct GNUNET_SET_RequestMessage *) mh;
357   struct GNUNET_SET_ListenHandle *lh = cls;
358   struct GNUNET_SET_Request *req;
359   const struct GNUNET_MessageHeader *context_msg;
360
361   LOG (GNUNET_ERROR_TYPE_DEBUG,
362        "processing operation request\n");
363   req = GNUNET_new (struct GNUNET_SET_Request);
364   req->accept_id = ntohl (msg->accept_id);
365   context_msg = GNUNET_MQ_extract_nested_mh (msg);
366   /* calling #GNUNET_SET_accept() in the listen cb will set req->accepted */
367   lh->listen_cb (lh->listen_cls, &msg->peer_id, context_msg, req);
368
369   /* we got another request => reset the backoff */
370   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
371
372   if (GNUNET_NO == req->accepted)
373   {
374     struct GNUNET_MQ_Envelope *mqm;
375     struct GNUNET_SET_RejectMessage *rmsg;
376
377     mqm = GNUNET_MQ_msg (rmsg,
378                          GNUNET_MESSAGE_TYPE_SET_REJECT);
379     rmsg->accept_reject_id = msg->accept_id;
380     GNUNET_MQ_send (lh->mq, mqm);
381     LOG (GNUNET_ERROR_TYPE_DEBUG,
382          "rejecting request\n");
383   }
384   GNUNET_free (req);
385
386   LOG (GNUNET_ERROR_TYPE_DEBUG,
387        "processed op request from service\n");
388
389   /* the accept-case is handled in GNUNET_SET_accept,
390    * as we have the accept message available there */
391 }
392
393
394 /**
395  * Destroy the given set operation.
396  *
397  * @param oh set operation to destroy
398  */
399 static void
400 set_operation_destroy (struct GNUNET_SET_OperationHandle *oh)
401 {
402   struct GNUNET_SET_Handle *set = oh->set;
403   struct GNUNET_SET_OperationHandle *h_assoc;
404
405   if (NULL != oh->conclude_mqm)
406     GNUNET_MQ_discard (oh->conclude_mqm);
407   /* is the operation already commited? */
408   if (NULL != set)
409   {
410     GNUNET_CONTAINER_DLL_remove (set->ops_head,
411                                  set->ops_tail,
412                                  oh);
413     h_assoc = GNUNET_MQ_assoc_remove (set->mq,
414                                       oh->request_id);
415     GNUNET_assert ((NULL == h_assoc) || (h_assoc == oh));
416   }
417   GNUNET_free (oh);
418 }
419
420
421 /**
422  * Cancel the given set operation.  We need to send an explicit cancel
423  * message, as all operations one one set communicate using one
424  * handle.
425  *
426  * @param oh set operation to cancel
427  */
428 void
429 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
430 {
431   struct GNUNET_SET_Handle *set = oh->set;
432   struct GNUNET_SET_CancelMessage *m;
433   struct GNUNET_MQ_Envelope *mqm;
434
435   if (NULL != set)
436   {
437     mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SET_CANCEL);
438     m->request_id = htonl (oh->request_id);
439     GNUNET_MQ_send (set->mq, mqm);
440   }
441   set_operation_destroy (oh);
442   if ( (NULL != set) &&
443        (GNUNET_YES == set->destroy_requested) &&
444        (NULL == set->ops_head) )
445   {
446     LOG (GNUNET_ERROR_TYPE_DEBUG,
447          "Destroying set after operation cancel\n");
448     GNUNET_SET_destroy (set);
449   }
450 }
451
452
453 /**
454  * We encountered an error communicating with the set service while
455  * performing a set operation. Report to the application.
456  *
457  * @param cls the `struct GNUNET_SET_Handle`
458  * @param error error code
459  */
460 static void
461 handle_client_set_error (void *cls,
462                          enum GNUNET_MQ_Error error)
463 {
464   struct GNUNET_SET_Handle *set = cls;
465
466   LOG (GNUNET_ERROR_TYPE_DEBUG,
467        "Handling client set error\n");
468   while (NULL != set->ops_head)
469   {
470     if (NULL != set->ops_head->result_cb)
471       set->ops_head->result_cb (set->ops_head->result_cls,
472                                 NULL,
473                                 GNUNET_SET_STATUS_FAILURE);
474     set_operation_destroy (set->ops_head);
475   }
476   set->invalid = GNUNET_YES;
477   if (GNUNET_YES == set->destroy_requested)
478   {
479     LOG (GNUNET_ERROR_TYPE_DEBUG,
480          "Destroying set after operation failure\n");
481     GNUNET_SET_destroy (set);
482   }
483 }
484
485
486 /**
487  * Create an empty set, supporting the specified operation.
488  *
489  * @param cfg configuration to use for connecting to the
490  *        set service
491  * @param op operation supported by the set
492  *        Note that the operation has to be specified
493  *        beforehand, as certain set operations need to maintain
494  *        data structures spefific to the operation
495  * @return a handle to the set
496  */
497 struct GNUNET_SET_Handle *
498 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
499                    enum GNUNET_SET_OperationType op)
500 {
501   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
502     { &handle_result, GNUNET_MESSAGE_TYPE_SET_RESULT, 0},
503     { &handle_iter_element, GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT, 0},
504     { &handle_iter_done,
505       GNUNET_MESSAGE_TYPE_SET_ITER_DONE,
506       sizeof (struct GNUNET_MessageHeader) },
507     GNUNET_MQ_HANDLERS_END
508   };
509   struct GNUNET_SET_Handle *set;
510   struct GNUNET_MQ_Envelope *mqm;
511   struct GNUNET_SET_CreateMessage *msg;
512
513   set = GNUNET_new (struct GNUNET_SET_Handle);
514   set->client = GNUNET_CLIENT_connect ("set", cfg);
515   if (NULL == set->client)
516   {
517     GNUNET_free (set);
518     return NULL;
519   }
520   set->mq = GNUNET_MQ_queue_for_connection_client (set->client,
521                                                    mq_handlers,
522                                                    &handle_client_set_error, set);
523   GNUNET_assert (NULL != set->mq);
524   mqm = GNUNET_MQ_msg (msg,
525                        GNUNET_MESSAGE_TYPE_SET_CREATE);
526   msg->operation = htonl (op);
527   GNUNET_MQ_send (set->mq, mqm);
528   return set;
529 }
530
531
532 /**
533  * Add an element to the given set.  After the element has been added
534  * (in the sense of being transmitted to the set service), @a cont
535  * will be called.  Multiple calls to GNUNET_SET_add_element() can be
536  * queued.
537  *
538  * @param set set to add element to
539  * @param element element to add to the set
540  * @param cont continuation called after the element has been added
541  * @param cont_cls closure for @a cont
542  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
543  *         set is invalid (e.g. the set service crashed)
544  */
545 int
546 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
547                         const struct GNUNET_SET_Element *element,
548                         GNUNET_SET_Continuation cont,
549                         void *cont_cls)
550 {
551   struct GNUNET_MQ_Envelope *mqm;
552   struct GNUNET_SET_ElementMessage *msg;
553
554   if (GNUNET_YES == set->invalid)
555   {
556     if (NULL != cont)
557       cont (cont_cls);
558     return GNUNET_SYSERR;
559   }
560   mqm = GNUNET_MQ_msg_extra (msg, element->size,
561                              GNUNET_MESSAGE_TYPE_SET_ADD);
562   msg->element_type = element->element_type;
563   memcpy (&msg[1],
564           element->data,
565           element->size);
566   GNUNET_MQ_notify_sent (mqm,
567                          cont, cont_cls);
568   GNUNET_MQ_send (set->mq, mqm);
569   return GNUNET_OK;
570 }
571
572
573 /**
574  * Remove an element to the given set.  After the element has been
575  * removed (in the sense of the request being transmitted to the set
576  * service), @a cont will be called.  Multiple calls to
577  * GNUNET_SET_remove_element() can be queued
578  *
579  * @param set set to remove element from
580  * @param element element to remove from the set
581  * @param cont continuation called after the element has been removed
582  * @param cont_cls closure for @a cont
583  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
584  *         set is invalid (e.g. the set service crashed)
585  */
586 int
587 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
588                            const struct GNUNET_SET_Element *element,
589                            GNUNET_SET_Continuation cont,
590                            void *cont_cls)
591 {
592   struct GNUNET_MQ_Envelope *mqm;
593   struct GNUNET_SET_ElementMessage *msg;
594
595   if (GNUNET_YES == set->invalid)
596   {
597     if (NULL != cont)
598       cont (cont_cls);
599     return GNUNET_SYSERR;
600   }
601   mqm = GNUNET_MQ_msg_extra (msg,
602                              element->size,
603                              GNUNET_MESSAGE_TYPE_SET_REMOVE);
604   msg->element_type = element->element_type;
605   memcpy (&msg[1],
606           element->data,
607           element->size);
608   GNUNET_MQ_notify_sent (mqm,
609                          cont, cont_cls);
610   GNUNET_MQ_send (set->mq, mqm);
611   return GNUNET_OK;
612 }
613
614
615 /**
616  * Destroy the set handle if no operations are left, mark the set
617  * for destruction otherwise.
618  *
619  * @param set set handle to destroy
620  */
621 void
622 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
623 {
624   /* destroying set while iterator is active is currently
625      not supported; we should expand the API to allow
626      clients to explicitly cancel the iteration! */
627   GNUNET_assert (NULL == set->iterator);
628   if (NULL != set->ops_head)
629   {
630     LOG (GNUNET_ERROR_TYPE_DEBUG,
631          "Set operations are pending, delaying set destruction\n");
632     set->destroy_requested = GNUNET_YES;
633     return;
634   }
635   LOG (GNUNET_ERROR_TYPE_DEBUG,
636        "Really destroying set\n");
637   if (NULL != set->client)
638   {
639     GNUNET_CLIENT_disconnect (set->client);
640     set->client = NULL;
641   }
642   if (NULL != set->mq)
643   {
644     GNUNET_MQ_destroy (set->mq);
645     set->mq = NULL;
646   }
647   GNUNET_free (set);
648 }
649
650
651 /**
652  * Prepare a set operation to be evaluated with another peer.
653  * The evaluation will not start until the client provides
654  * a local set with #GNUNET_SET_commit().
655  *
656  * @param other_peer peer with the other set
657  * @param app_id hash for the application using the set
658  * @param context_msg additional information for the request
659  * @param result_mode specified how results will be returned,
660  *        see `enum GNUNET_SET_ResultMode`.
661  * @param result_cb called on error or success
662  * @param result_cls closure for @e result_cb
663  * @return a handle to cancel the operation
664  */
665 struct GNUNET_SET_OperationHandle *
666 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
667                     const struct GNUNET_HashCode *app_id,
668                     const struct GNUNET_MessageHeader *context_msg,
669                     enum GNUNET_SET_ResultMode result_mode,
670                     GNUNET_SET_ResultIterator result_cb,
671                     void *result_cls)
672 {
673   struct GNUNET_MQ_Envelope *mqm;
674   struct GNUNET_SET_OperationHandle *oh;
675   struct GNUNET_SET_EvaluateMessage *msg;
676
677   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
678   oh->result_cb = result_cb;
679   oh->result_cls = result_cls;
680   mqm = GNUNET_MQ_msg_nested_mh (msg,
681                                  GNUNET_MESSAGE_TYPE_SET_EVALUATE,
682                                  context_msg);
683   msg->app_id = *app_id;
684   msg->result_mode = htonl (result_mode);
685   msg->target_peer = *other_peer;
686   oh->conclude_mqm = mqm;
687   oh->request_id_addr = &msg->request_id;
688
689   return oh;
690 }
691
692
693 /**
694  * Connect to the set service in order to listen for requests.
695  *
696  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
697  * @param tc task context if invoked as a task, NULL otherwise
698  */
699 static void
700 listen_connect (void *cls,
701                 const struct GNUNET_SCHEDULER_TaskContext *tc);
702
703
704 /**
705  * Our connection with the set service encountered an error,
706  * re-initialize with exponential back-off.
707  *
708  * @param cls the `struct GNUNET_SET_ListenHandle *`
709  * @param error reason for the disconnect
710  */
711 static void
712 handle_client_listener_error (void *cls,
713                               enum GNUNET_MQ_Error error)
714 {
715   struct GNUNET_SET_ListenHandle *lh = cls;
716
717   LOG (GNUNET_ERROR_TYPE_DEBUG,
718        "Listener broke down (%d), re-connecting\n",
719        (int) error);
720   GNUNET_CLIENT_disconnect (lh->client);
721   lh->client = NULL;
722   GNUNET_MQ_destroy (lh->mq);
723   lh->mq = NULL;
724   lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
725                                                      &listen_connect, lh);
726   lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
727 }
728
729
730 /**
731  * Connect to the set service in order to listen for requests.
732  *
733  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
734  * @param tc task context if invoked as a task, NULL otherwise
735  */
736 static void
737 listen_connect (void *cls,
738                 const struct GNUNET_SCHEDULER_TaskContext *tc)
739 {
740   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
741     { &handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST },
742     GNUNET_MQ_HANDLERS_END
743   };
744   struct GNUNET_SET_ListenHandle *lh = cls;
745   struct GNUNET_MQ_Envelope *mqm;
746   struct GNUNET_SET_ListenMessage *msg;
747
748   if ( (NULL != tc) &&
749        (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) )
750   {
751     LOG (GNUNET_ERROR_TYPE_DEBUG,
752          "Listener not reconnecting due to shutdown\n");
753     return;
754   }
755   lh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
756
757   GNUNET_assert (NULL == lh->client);
758   lh->client = GNUNET_CLIENT_connect ("set", lh->cfg);
759   if (NULL == lh->client)
760     return;
761   GNUNET_assert (NULL == lh->mq);
762   lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, mq_handlers,
763                                                   &handle_client_listener_error, lh);
764   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
765   msg->operation = htonl (lh->operation);
766   msg->app_id = lh->app_id;
767   GNUNET_MQ_send (lh->mq, mqm);
768 }
769
770
771 /**
772  * Wait for set operation requests for the given application id
773  *
774  * @param cfg configuration to use for connecting to
775  *            the set service, needs to be valid for the lifetime of the listen handle
776  * @param operation operation we want to listen for
777  * @param app_id id of the application that handles set operation requests
778  * @param listen_cb called for each incoming request matching the operation
779  *                  and application id
780  * @param listen_cls handle for @a listen_cb
781  * @return a handle that can be used to cancel the listen operation
782  */
783 struct GNUNET_SET_ListenHandle *
784 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
785                    enum GNUNET_SET_OperationType operation,
786                    const struct GNUNET_HashCode *app_id,
787                    GNUNET_SET_ListenCallback listen_cb,
788                    void *listen_cls)
789 {
790   struct GNUNET_SET_ListenHandle *lh;
791
792   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
793   lh->listen_cb = listen_cb;
794   lh->listen_cls = listen_cls;
795   lh->cfg = cfg;
796   lh->operation = operation;
797   lh->app_id = *app_id;
798   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
799   listen_connect (lh, NULL);
800   if (NULL == lh->client)
801   {
802     GNUNET_free (lh);
803     return NULL;
804   }
805   return lh;
806 }
807
808
809 /**
810  * Cancel the given listen operation.
811  *
812  * @param lh handle for the listen operation
813  */
814 void
815 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
816 {
817   LOG (GNUNET_ERROR_TYPE_DEBUG,
818        "Canceling listener\n");
819   if (NULL != lh->mq)
820   {
821     GNUNET_MQ_destroy (lh->mq);
822     lh->mq = NULL;
823   }
824   if (NULL != lh->client)
825   {
826     GNUNET_CLIENT_disconnect (lh->client);
827     lh->client = NULL;
828   }
829   if (GNUNET_SCHEDULER_NO_TASK != lh->reconnect_task)
830   {
831     GNUNET_SCHEDULER_cancel (lh->reconnect_task);
832     lh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
833   }
834   GNUNET_free (lh);
835 }
836
837
838 /**
839  * Accept a request we got via #GNUNET_SET_listen.  Must be called during
840  * #GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
841  * afterwards.
842  * Call #GNUNET_SET_commit to provide the local set to use for the operation,
843  * and to begin the exchange with the remote peer.
844  *
845  * @param request request to accept
846  * @param result_mode specified how results will be returned,
847  *        see `enum GNUNET_SET_ResultMode`.
848  * @param result_cb callback for the results
849  * @param result_cls closure for @a result_cb
850  * @return a handle to cancel the operation
851  */
852 struct GNUNET_SET_OperationHandle *
853 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
854                    enum GNUNET_SET_ResultMode result_mode,
855                    GNUNET_SET_ResultIterator result_cb,
856                    void *result_cls)
857 {
858   struct GNUNET_MQ_Envelope *mqm;
859   struct GNUNET_SET_OperationHandle *oh;
860   struct GNUNET_SET_AcceptMessage *msg;
861
862   GNUNET_assert (GNUNET_NO == request->accepted);
863   request->accepted = GNUNET_YES;
864   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
865   msg->accept_reject_id = htonl (request->accept_id);
866   msg->result_mode = htonl (result_mode);
867   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
868   oh->result_cb = result_cb;
869   oh->result_cls = result_cls;
870   oh->conclude_mqm = mqm;
871   oh->request_id_addr = &msg->request_id;
872   return oh;
873 }
874
875
876 /**
877  * Commit a set to be used with a set operation.
878  * This function is called once we have fully constructed
879  * the set that we want to use for the operation.  At this
880  * time, the P2P protocol can then begin to exchange the
881  * set information and call the result callback with the
882  * result information.
883  *
884  * @param oh handle to the set operation
885  * @param set the set to use for the operation
886  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
887  *         set is invalid (e.g. the set service crashed)
888  */
889 int
890 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
891                    struct GNUNET_SET_Handle *set)
892 {
893   GNUNET_assert (NULL == oh->set);
894   if (GNUNET_YES == set->invalid)
895     return GNUNET_SYSERR;
896   GNUNET_assert (NULL != oh->conclude_mqm);
897   oh->set = set;
898   GNUNET_CONTAINER_DLL_insert (set->ops_head,
899                                set->ops_tail,
900                                oh);
901   oh->request_id = GNUNET_MQ_assoc_add (set->mq, oh);
902   *oh->request_id_addr = htonl (oh->request_id);
903   GNUNET_MQ_send (set->mq, oh->conclude_mqm);
904   oh->conclude_mqm = NULL;
905   oh->request_id_addr = NULL;
906   return GNUNET_OK;
907 }
908
909
910 /**
911  * Iterate over all elements in the given set.  Note that this
912  * operation involves transferring every element of the set from the
913  * service to the client, and is thus costly.
914  *
915  * @param set the set to iterate over
916  * @param iter the iterator to call for each element
917  * @param iter_cls closure for @a iter
918  * @return #GNUNET_YES if the iteration started successfuly,
919  *         #GNUNET_NO if another iteration is active
920  *         #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
921  */
922 int
923 GNUNET_SET_iterate (struct GNUNET_SET_Handle *set,
924                     GNUNET_SET_ElementIterator iter,
925                     void *iter_cls)
926 {
927   struct GNUNET_MQ_Envelope *ev;
928
929   GNUNET_assert (NULL != iter);
930   if (GNUNET_YES == set->invalid)
931     return GNUNET_SYSERR;
932   if (NULL != set->iterator)
933     return GNUNET_NO;
934   LOG (GNUNET_ERROR_TYPE_DEBUG,
935        "Iterating over set\n");
936   set->iterator = iter;
937   set->iterator_cls = iter_cls;
938   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
939   GNUNET_MQ_send (set->mq, ev);
940   return GNUNET_YES;
941 }
942
943
944 /**
945  * Stop iteration over all elements in the given set.  Can only
946  * be called before the iteration has "naturally" completed its
947  * turn.
948  *
949  * @param set the set to stop iterating over
950  */
951 void
952 GNUNET_SET_iterate_cancel (struct GNUNET_SET_Handle *set)
953 {
954   GNUNET_assert (NULL != set->iterator);
955   set->iterator = NULL;
956   set->iteration_id++;
957 }
958
959
960 /* end of set_api.c */