2b09725e8ccbfe9c7e9350674d96523f691efaa3
[oweals/gnunet.git] / src / set / set_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2016 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file set/set_api.c
22  * @brief api for the set service
23  * @author Florian Dold
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.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 struct SetCopyRequest
36 {
37   struct SetCopyRequest *next;
38
39   struct SetCopyRequest *prev;
40
41   void *cls;
42
43   GNUNET_SET_CopyReadyCallback cb;
44 };
45
46 /**
47  * Opaque handle to a set.
48  */
49 struct GNUNET_SET_Handle
50 {
51   /**
52    * Message queue for @e client.
53    */
54   struct GNUNET_MQ_Handle *mq;
55
56   /**
57    * Linked list of operations on the set.
58    */
59   struct GNUNET_SET_OperationHandle *ops_head;
60
61   /**
62    * Linked list of operations on the set.
63    */
64   struct GNUNET_SET_OperationHandle *ops_tail;
65
66   /**
67    * Callback for the current iteration over the set,
68    * NULL if no iterator is active.
69    */
70   GNUNET_SET_ElementIterator iterator;
71
72   /**
73    * Closure for @e iterator
74    */
75   void *iterator_cls;
76
77   /**
78    * Should the set be destroyed once all operations are gone?
79    */
80   int destroy_requested;
81
82   /**
83    * Has the set become invalid (e.g. service died)?
84    */
85   int invalid;
86
87   /**
88    * Both client and service count the number of iterators
89    * created so far to match replies with iterators.
90    */
91   uint16_t iteration_id;
92
93   /**
94    * Configuration, needed when creating (lazy) copies.
95    */
96   const struct GNUNET_CONFIGURATION_Handle *cfg;
97
98   /**
99    * Doubly linked list of copy requests.
100    */
101   struct SetCopyRequest *copy_req_head;
102
103   /**
104    * Doubly linked list of copy requests.
105    */
106   struct SetCopyRequest *copy_req_tail;
107 };
108
109
110 /**
111  * Handle for a set operation request from another peer.
112  */
113 struct GNUNET_SET_Request
114 {
115   /**
116    * Id of the request, used to identify the request when
117    * accepting/rejecting it.
118    */
119   uint32_t accept_id;
120
121   /**
122    * Has the request been accepted already?
123    * #GNUNET_YES/#GNUNET_NO
124    */
125   int accepted;
126 };
127
128
129 /**
130  * Handle to an operation.  Only known to the service after committing
131  * the handle with a set.
132  */
133 struct GNUNET_SET_OperationHandle
134 {
135   /**
136    * Function to be called when we have a result,
137    * or an error.
138    */
139   GNUNET_SET_ResultIterator result_cb;
140
141   /**
142    * Closure for @e result_cb.
143    */
144   void *result_cls;
145
146   /**
147    * Local set used for the operation,
148    * NULL if no set has been provided by conclude yet.
149    */
150   struct GNUNET_SET_Handle *set;
151
152   /**
153    * Message sent to the server on calling conclude,
154    * NULL if conclude has been called.
155    */
156   struct GNUNET_MQ_Envelope *conclude_mqm;
157
158   /**
159    * Address of the request if in the conclude message,
160    * used to patch the request id into the message when the set is known.
161    */
162   uint32_t *request_id_addr;
163
164   /**
165    * Handles are kept in a linked list.
166    */
167   struct GNUNET_SET_OperationHandle *prev;
168
169   /**
170    * Handles are kept in a linked list.
171    */
172   struct GNUNET_SET_OperationHandle *next;
173
174   /**
175    * Request ID to identify the operation within the set.
176    */
177   uint32_t request_id;
178 };
179
180
181 /**
182  * Opaque handle to a listen operation.
183  */
184 struct GNUNET_SET_ListenHandle
185 {
186
187   /**
188    * Message queue for the client.
189    */
190   struct GNUNET_MQ_Handle* mq;
191
192   /**
193    * Configuration handle for the listener, stored
194    * here to be able to reconnect transparently on
195    * connection failure.
196    */
197   const struct GNUNET_CONFIGURATION_Handle *cfg;
198
199   /**
200    * Function to call on a new incoming request,
201    * or on error.
202    */
203   GNUNET_SET_ListenCallback listen_cb;
204
205   /**
206    * Closure for @e listen_cb.
207    */
208   void *listen_cls;
209
210   /**
211    * Application ID we listen for.
212    */
213   struct GNUNET_HashCode app_id;
214
215   /**
216    * Time to wait until we try to reconnect on failure.
217    */
218   struct GNUNET_TIME_Relative reconnect_backoff;
219
220   /**
221    * Task for reconnecting when the listener fails.
222    */
223   struct GNUNET_SCHEDULER_Task *reconnect_task;
224
225   /**
226    * Operation we listen for.
227    */
228   enum GNUNET_SET_OperationType operation;
229 };
230
231
232 /* mutual recursion with handle_copy_lazy */
233 static struct GNUNET_SET_Handle *
234 create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
235                  enum GNUNET_SET_OperationType op,
236                  const uint32_t *cookie);
237
238
239 /**
240  * Handle element for iteration over the set.  Notifies the
241  * iterator and sends an acknowledgement to the service.
242  *
243  * @param cls the `struct GNUNET_SET_Handle *`
244  * @param msg the message
245  */
246 static void
247 handle_copy_lazy (void *cls,
248                   const struct GNUNET_SET_CopyLazyResponseMessage *msg)
249 {
250   struct GNUNET_SET_Handle *set = cls;
251   struct SetCopyRequest *req;
252   struct GNUNET_SET_Handle *new_set;
253
254   req = set->copy_req_head;
255   if (NULL == req)
256   {
257     /* Service sent us unsolicited lazy copy response */
258     GNUNET_break (0);
259     return;
260   }
261
262   LOG (GNUNET_ERROR_TYPE_DEBUG,
263        "Handling response to lazy copy\n");
264   GNUNET_CONTAINER_DLL_remove (set->copy_req_head,
265                                set->copy_req_tail,
266                                req);
267   // We pass none as operation here, since it doesn't matter when
268   // cloning.
269   new_set = create_internal (set->cfg,
270                              GNUNET_SET_OPERATION_NONE,
271                              &msg->cookie);
272   req->cb (req->cls, new_set);
273   GNUNET_free (req);
274 }
275
276
277 /**
278  * Check that the given @a msg is well-formed.
279  *
280  * @param cls closure
281  * @param msg message to check
282  * @return #GNUNET_OK if message is well-formed
283  */
284 static int
285 check_iter_element (void *cls,
286                     const struct GNUNET_SET_IterResponseMessage *msg)
287 {
288   /* minimum size was already checked, everything else is OK! */
289   return GNUNET_OK;
290 }
291
292
293 /**
294  * Handle element for iteration over the set.  Notifies the
295  * iterator and sends an acknowledgement to the service.
296  *
297  * @param cls the `struct GNUNET_SET_Handle *`
298  * @param mh the message
299  */
300 static void
301 handle_iter_element (void *cls,
302                      const struct GNUNET_SET_IterResponseMessage *msg)
303 {
304   struct GNUNET_SET_Handle *set = cls;
305   GNUNET_SET_ElementIterator iter = set->iterator;
306   struct GNUNET_SET_Element element;
307   struct GNUNET_SET_IterAckMessage *ack_msg;
308   struct GNUNET_MQ_Envelope *ev;
309   uint16_t msize;
310
311   msize = ntohs (msg->header.size);
312   if (set->iteration_id != ntohs (msg->iteration_id))
313   {
314     /* element from a previous iteration, skip! */
315     iter = NULL;
316   }
317   if (NULL != iter)
318   {
319     element.size = msize - sizeof (struct GNUNET_SET_IterResponseMessage);
320     element.element_type = ntohs (msg->element_type);
321     element.data = &msg[1];
322     iter (set->iterator_cls,
323           &element);
324   }
325   ev = GNUNET_MQ_msg (ack_msg,
326                       GNUNET_MESSAGE_TYPE_SET_ITER_ACK);
327   ack_msg->send_more = htonl ((NULL != iter));
328   GNUNET_MQ_send (set->mq, ev);
329 }
330
331
332 /**
333  * Handle message signalling conclusion of iteration over the set.
334  * Notifies the iterator that we are done.
335  *
336  * @param cls the set
337  * @param mh the message
338  */
339 static void
340 handle_iter_done (void *cls,
341                   const struct GNUNET_MessageHeader *mh)
342 {
343   struct GNUNET_SET_Handle *set = cls;
344   GNUNET_SET_ElementIterator iter = set->iterator;
345
346   if (NULL == iter)
347     return;
348   set->iterator = NULL;
349   set->iteration_id++;
350   iter (set->iterator_cls,
351         NULL);
352 }
353
354
355 /**
356  * Check that the given @a msg is well-formed.
357  *
358  * @param cls closure
359  * @param msg message to check
360  * @return #GNUNET_OK if message is well-formed
361  */
362 static int
363 check_result (void *cls,
364               const struct GNUNET_SET_ResultMessage *msg)
365 {
366   /* minimum size was already checked, everything else is OK! */
367   return GNUNET_OK;
368 }
369
370
371 /**
372  * Handle result message for a set operation.
373  *
374  * @param cls the set
375  * @param mh the message
376  */
377 static void
378 handle_result (void *cls,
379                const struct GNUNET_SET_ResultMessage *msg)
380 {
381   struct GNUNET_SET_Handle *set = cls;
382   struct GNUNET_SET_OperationHandle *oh;
383   struct GNUNET_SET_Element e;
384   enum GNUNET_SET_Status result_status;
385   int destroy_set;
386
387   GNUNET_assert (NULL != set->mq);
388   result_status = ntohs (msg->result_status);
389   LOG (GNUNET_ERROR_TYPE_DEBUG,
390        "Got result message with status %d\n",
391        result_status);
392
393   oh = GNUNET_MQ_assoc_get (set->mq,
394                             ntohl (msg->request_id));
395   if (NULL == oh)
396   {
397     /* 'oh' can be NULL if we canceled the operation, but the service
398        did not get the cancel message yet. */
399     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400                 "Ignoring result from canceled operation\n");
401     return;
402   }
403
404   switch (result_status)
405   {
406     case GNUNET_SET_STATUS_OK:
407     case GNUNET_SET_STATUS_ADD_LOCAL:
408     case GNUNET_SET_STATUS_ADD_REMOTE:
409       goto do_element;
410     case GNUNET_SET_STATUS_FAILURE:
411     case GNUNET_SET_STATUS_DONE:
412       goto do_final;
413     case GNUNET_SET_STATUS_HALF_DONE:
414       /* not used anymore */
415       GNUNET_assert (0);
416   }
417
418 do_final:
419   LOG (GNUNET_ERROR_TYPE_DEBUG,
420        "Treating result as final status\n");
421   GNUNET_MQ_assoc_remove (set->mq,
422                           ntohl (msg->request_id));
423   GNUNET_CONTAINER_DLL_remove (set->ops_head,
424                                set->ops_tail,
425                                oh);
426   /* Need to do this calculation _before_ the result callback,
427      as IF the application still has a valid set handle, it
428      may trigger destruction of the set during the callback. */
429   destroy_set = (GNUNET_YES == set->destroy_requested) &&
430                 (NULL == set->ops_head);
431   if (NULL != oh->result_cb)
432   {
433     oh->result_cb (oh->result_cls,
434                    NULL,
435                    result_status);
436   }
437   else
438   {
439     LOG (GNUNET_ERROR_TYPE_DEBUG,
440          "No callback for final status\n");
441   }
442   if (destroy_set)
443     GNUNET_SET_destroy (set);
444   GNUNET_free (oh);
445   return;
446
447 do_element:
448   LOG (GNUNET_ERROR_TYPE_DEBUG,
449        "Treating result as element\n");
450   e.data = &msg[1];
451   e.size = ntohs (msg->header.size) - sizeof (struct GNUNET_SET_ResultMessage);
452   e.element_type = ntohs (msg->element_type);
453   if (NULL != oh->result_cb)
454     oh->result_cb (oh->result_cls,
455                    &e,
456                    result_status);
457 }
458
459
460 /**
461  * Destroy the given set operation.
462  *
463  * @param oh set operation to destroy
464  */
465 static void
466 set_operation_destroy (struct GNUNET_SET_OperationHandle *oh)
467 {
468   struct GNUNET_SET_Handle *set = oh->set;
469   struct GNUNET_SET_OperationHandle *h_assoc;
470
471   if (NULL != oh->conclude_mqm)
472     GNUNET_MQ_discard (oh->conclude_mqm);
473   /* is the operation already commited? */
474   if (NULL != set)
475   {
476     GNUNET_CONTAINER_DLL_remove (set->ops_head,
477                                  set->ops_tail,
478                                  oh);
479     h_assoc = GNUNET_MQ_assoc_remove (set->mq,
480                                       oh->request_id);
481     GNUNET_assert ((NULL == h_assoc) || (h_assoc == oh));
482   }
483   GNUNET_free (oh);
484 }
485
486
487 /**
488  * Cancel the given set operation.  We need to send an explicit cancel
489  * message, as all operations one one set communicate using one
490  * handle.
491  *
492  * @param oh set operation to cancel
493  */
494 void
495 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
496 {
497   struct GNUNET_SET_Handle *set = oh->set;
498   struct GNUNET_SET_CancelMessage *m;
499   struct GNUNET_MQ_Envelope *mqm;
500
501   if (NULL != set)
502   {
503     mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SET_CANCEL);
504     m->request_id = htonl (oh->request_id);
505     GNUNET_MQ_send (set->mq, mqm);
506   }
507   set_operation_destroy (oh);
508   if ( (NULL != set) &&
509        (GNUNET_YES == set->destroy_requested) &&
510        (NULL == set->ops_head) )
511   {
512     LOG (GNUNET_ERROR_TYPE_DEBUG,
513          "Destroying set after operation cancel\n");
514     GNUNET_SET_destroy (set);
515   }
516 }
517
518
519 /**
520  * We encountered an error communicating with the set service while
521  * performing a set operation. Report to the application.
522  *
523  * @param cls the `struct GNUNET_SET_Handle`
524  * @param error error code
525  */
526 static void
527 handle_client_set_error (void *cls,
528                          enum GNUNET_MQ_Error error)
529 {
530   struct GNUNET_SET_Handle *set = cls;
531   GNUNET_SET_ElementIterator iter = set->iterator;
532
533   LOG (GNUNET_ERROR_TYPE_ERROR,
534        "Handling client set error %d\n",
535        error);
536   while (NULL != set->ops_head)
537   {
538     if (NULL != set->ops_head->result_cb)
539       set->ops_head->result_cb (set->ops_head->result_cls,
540                                 NULL,
541                                 GNUNET_SET_STATUS_FAILURE);
542     set_operation_destroy (set->ops_head);
543   }
544   set->iterator = NULL;
545   set->iteration_id++;
546   set->invalid = GNUNET_YES;
547   if (NULL != iter)
548     iter (set->iterator_cls,
549           NULL);
550 }
551
552
553 static struct GNUNET_SET_Handle *
554 create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
555                  enum GNUNET_SET_OperationType op,
556                  const uint32_t *cookie)
557 {
558   struct GNUNET_SET_Handle *set = GNUNET_new (struct GNUNET_SET_Handle);
559   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
560     GNUNET_MQ_hd_var_size (result,
561                            GNUNET_MESSAGE_TYPE_SET_RESULT,
562                            struct GNUNET_SET_ResultMessage,
563                            set),
564     GNUNET_MQ_hd_var_size (iter_element,
565                            GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT,
566                            struct GNUNET_SET_IterResponseMessage,
567                            set),
568     GNUNET_MQ_hd_fixed_size (iter_done,
569                              GNUNET_MESSAGE_TYPE_SET_ITER_DONE,
570                              struct GNUNET_MessageHeader,
571                              set),
572     GNUNET_MQ_hd_fixed_size (copy_lazy,
573                              GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE,
574                              struct GNUNET_SET_CopyLazyResponseMessage,
575                              set),
576     GNUNET_MQ_handler_end ()
577   };
578   struct GNUNET_MQ_Envelope *mqm;
579   struct GNUNET_SET_CreateMessage *create_msg;
580   struct GNUNET_SET_CopyLazyConnectMessage *copy_msg;
581
582   set->cfg = cfg;
583   set->mq = GNUNET_CLIENT_connect (cfg,
584                                    "set",
585                                    mq_handlers,
586                                    &handle_client_set_error,
587                                    set);
588   if (NULL == set->mq)
589   {
590     GNUNET_free (set);
591     return NULL;
592   }
593   if (NULL == cookie)
594   {
595     LOG (GNUNET_ERROR_TYPE_DEBUG,
596          "Creating new set (operation %u)\n",
597          op);
598     mqm = GNUNET_MQ_msg (create_msg,
599                          GNUNET_MESSAGE_TYPE_SET_CREATE);
600     create_msg->operation = htonl (op);
601   }
602   else
603   {
604     LOG (GNUNET_ERROR_TYPE_DEBUG,
605          "Creating new set (lazy copy)\n",
606          op);
607     mqm = GNUNET_MQ_msg (copy_msg,
608                          GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT);
609     copy_msg->cookie = *cookie;
610   }
611   GNUNET_MQ_send (set->mq, mqm);
612   return set;
613 }
614
615
616 /**
617  * Create an empty set, supporting the specified operation.
618  *
619  * @param cfg configuration to use for connecting to the
620  *        set service
621  * @param op operation supported by the set
622  *        Note that the operation has to be specified
623  *        beforehand, as certain set operations need to maintain
624  *        data structures spefific to the operation
625  * @return a handle to the set
626  */
627 struct GNUNET_SET_Handle *
628 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
629                    enum GNUNET_SET_OperationType op)
630 {
631   return create_internal (cfg, op, NULL);
632 }
633
634
635 /**
636  * Add an element to the given set.  After the element has been added
637  * (in the sense of being transmitted to the set service), @a cont
638  * will be called.  Multiple calls to GNUNET_SET_add_element() can be
639  * queued.
640  *
641  * @param set set to add element to
642  * @param element element to add to the set
643  * @param cont continuation called after the element has been added
644  * @param cont_cls closure for @a cont
645  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
646  *         set is invalid (e.g. the set service crashed)
647  */
648 int
649 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
650                         const struct GNUNET_SET_Element *element,
651                         GNUNET_SET_Continuation cont,
652                         void *cont_cls)
653 {
654   struct GNUNET_MQ_Envelope *mqm;
655   struct GNUNET_SET_ElementMessage *msg;
656
657   if (GNUNET_YES == set->invalid)
658   {
659     if (NULL != cont)
660       cont (cont_cls);
661     return GNUNET_SYSERR;
662   }
663   mqm = GNUNET_MQ_msg_extra (msg,
664                              element->size,
665                              GNUNET_MESSAGE_TYPE_SET_ADD);
666   msg->element_type = htons (element->element_type);
667   GNUNET_memcpy (&msg[1],
668                  element->data,
669                  element->size);
670   GNUNET_MQ_notify_sent (mqm,
671                          cont, cont_cls);
672   GNUNET_MQ_send (set->mq, mqm);
673   return GNUNET_OK;
674 }
675
676
677 /**
678  * Remove an element to the given set.  After the element has been
679  * removed (in the sense of the request being transmitted to the set
680  * service), @a cont will be called.  Multiple calls to
681  * GNUNET_SET_remove_element() can be queued
682  *
683  * @param set set to remove element from
684  * @param element element to remove from the set
685  * @param cont continuation called after the element has been removed
686  * @param cont_cls closure for @a cont
687  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
688  *         set is invalid (e.g. the set service crashed)
689  */
690 int
691 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
692                            const struct GNUNET_SET_Element *element,
693                            GNUNET_SET_Continuation cont,
694                            void *cont_cls)
695 {
696   struct GNUNET_MQ_Envelope *mqm;
697   struct GNUNET_SET_ElementMessage *msg;
698
699   if (GNUNET_YES == set->invalid)
700   {
701     if (NULL != cont)
702       cont (cont_cls);
703     return GNUNET_SYSERR;
704   }
705   mqm = GNUNET_MQ_msg_extra (msg,
706                              element->size,
707                              GNUNET_MESSAGE_TYPE_SET_REMOVE);
708   msg->element_type = htons (element->element_type);
709   GNUNET_memcpy (&msg[1],
710                  element->data,
711                  element->size);
712   GNUNET_MQ_notify_sent (mqm,
713                          cont, cont_cls);
714   GNUNET_MQ_send (set->mq, mqm);
715   return GNUNET_OK;
716 }
717
718
719 /**
720  * Destroy the set handle if no operations are left, mark the set
721  * for destruction otherwise.
722  *
723  * @param set set handle to destroy
724  */
725 void
726 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
727 {
728   /* destroying set while iterator is active is currently
729      not supported; we should expand the API to allow
730      clients to explicitly cancel the iteration! */
731   GNUNET_assert (NULL == set->iterator);
732   if (NULL != set->ops_head)
733   {
734     LOG (GNUNET_ERROR_TYPE_DEBUG,
735          "Set operations are pending, delaying set destruction\n");
736     set->destroy_requested = GNUNET_YES;
737     return;
738   }
739   LOG (GNUNET_ERROR_TYPE_DEBUG,
740        "Really destroying set\n");
741   if (NULL != set->mq)
742   {
743     GNUNET_MQ_destroy (set->mq);
744     set->mq = NULL;
745   }
746   GNUNET_free (set);
747 }
748
749
750 /**
751  * Prepare a set operation to be evaluated with another peer.
752  * The evaluation will not start until the client provides
753  * a local set with #GNUNET_SET_commit().
754  *
755  * @param other_peer peer with the other set
756  * @param app_id hash for the application using the set
757  * @param context_msg additional information for the request
758  * @param result_mode specified how results will be returned,
759  *        see `enum GNUNET_SET_ResultMode`.
760  * @param result_cb called on error or success
761  * @param result_cls closure for @e result_cb
762  * @return a handle to cancel the operation
763  */
764 struct GNUNET_SET_OperationHandle *
765 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
766                     const struct GNUNET_HashCode *app_id,
767                     const struct GNUNET_MessageHeader *context_msg,
768                     enum GNUNET_SET_ResultMode result_mode,
769                     struct GNUNET_SET_Option options[],
770                     GNUNET_SET_ResultIterator result_cb,
771                     void *result_cls)
772 {
773   struct GNUNET_MQ_Envelope *mqm;
774   struct GNUNET_SET_OperationHandle *oh;
775   struct GNUNET_SET_EvaluateMessage *msg;
776   struct GNUNET_SET_Option *opt;
777
778   LOG (GNUNET_ERROR_TYPE_DEBUG,
779        "Client prepares set operation (%d)\n",
780        result_mode);
781   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
782   oh->result_cb = result_cb;
783   oh->result_cls = result_cls;
784   mqm = GNUNET_MQ_msg_nested_mh (msg,
785                                  GNUNET_MESSAGE_TYPE_SET_EVALUATE,
786                                  context_msg);
787   msg->app_id = *app_id;
788   msg->result_mode = htonl (result_mode);
789   msg->target_peer = *other_peer;
790   for (opt = options; opt->type != 0; opt++)
791   {
792     switch (opt->type)
793     {
794       case GNUNET_SET_OPTION_BYZANTINE:
795         msg->byzantine = GNUNET_YES;
796         msg->byzantine_lower_bound = opt->v.num;
797         break;
798       case GNUNET_SET_OPTION_FORCE_FULL:
799         msg->force_full = GNUNET_YES;
800         break;
801       case GNUNET_SET_OPTION_FORCE_DELTA:
802         msg->force_delta = GNUNET_YES;
803         break;
804       default:
805         LOG (GNUNET_ERROR_TYPE_ERROR, 
806              "Option with type %d not recognized\n", (int) opt->type);
807     }
808   }
809   oh->conclude_mqm = mqm;
810   oh->request_id_addr = &msg->request_id;
811
812   return oh;
813 }
814
815
816 /**
817  * Connect to the set service in order to listen for requests.
818  *
819  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
820  */
821 static void
822 listen_connect (void *cls);
823
824
825 /**
826  * Check validity of request message for a listen operation
827  *
828  * @param cls the listen handle
829  * @param msg the message
830  * @return #GNUNET_OK if the message is well-formed
831  */
832 static int
833 check_request (void *cls,
834                const struct GNUNET_SET_RequestMessage *msg)
835 {
836   const struct GNUNET_MessageHeader *context_msg;
837
838   if (ntohs (msg->header.size) == sizeof (*msg))
839     return GNUNET_OK; /* no context message is OK */
840   context_msg = GNUNET_MQ_extract_nested_mh (msg);
841   if (NULL == context_msg)
842   {
843     /* malformed context message is NOT ok */
844     GNUNET_break_op (0);
845     return GNUNET_SYSERR;
846   }
847   return GNUNET_OK;
848 }
849
850
851 /**
852  * Handle request message for a listen operation
853  *
854  * @param cls the listen handle
855  * @param msg the message
856  */
857 static void
858 handle_request (void *cls,
859                 const struct GNUNET_SET_RequestMessage *msg)
860 {
861   struct GNUNET_SET_ListenHandle *lh = cls;
862   struct GNUNET_SET_Request req;
863   const struct GNUNET_MessageHeader *context_msg;
864   struct GNUNET_MQ_Envelope *mqm;
865   struct GNUNET_SET_RejectMessage *rmsg;
866
867   LOG (GNUNET_ERROR_TYPE_DEBUG,
868        "Processing incoming operation request\n");
869   /* we got another valid request => reset the backoff */
870   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
871   req.accept_id = ntohl (msg->accept_id);
872   req.accepted = GNUNET_NO;
873   context_msg = GNUNET_MQ_extract_nested_mh (msg);
874   /* calling #GNUNET_SET_accept() in the listen cb will set req->accepted */
875   lh->listen_cb (lh->listen_cls,
876                  &msg->peer_id,
877                  context_msg,
878                  &req);
879   if (GNUNET_YES == req.accepted)
880     return; /* the accept-case is handled in #GNUNET_SET_accept() */
881   LOG (GNUNET_ERROR_TYPE_DEBUG,
882        "Rejecting request\n");
883   mqm = GNUNET_MQ_msg (rmsg,
884                        GNUNET_MESSAGE_TYPE_SET_REJECT);
885   rmsg->accept_reject_id = msg->accept_id;
886   GNUNET_MQ_send (lh->mq, mqm);
887 }
888
889
890 /**
891  * Our connection with the set service encountered an error,
892  * re-initialize with exponential back-off.
893  *
894  * @param cls the `struct GNUNET_SET_ListenHandle *`
895  * @param error reason for the disconnect
896  */
897 static void
898 handle_client_listener_error (void *cls,
899                               enum GNUNET_MQ_Error error)
900 {
901   struct GNUNET_SET_ListenHandle *lh = cls;
902
903   LOG (GNUNET_ERROR_TYPE_DEBUG,
904        "Listener broke down (%d), re-connecting\n",
905        (int) error);
906   GNUNET_MQ_destroy (lh->mq);
907   lh->mq = NULL;
908   lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
909                                                      &listen_connect,
910                                                      lh);
911   lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
912 }
913
914
915 /**
916  * Connect to the set service in order to listen for requests.
917  *
918  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
919  */
920 static void
921 listen_connect (void *cls)
922 {
923   struct GNUNET_SET_ListenHandle *lh = cls;
924   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
925     GNUNET_MQ_hd_var_size (request,
926                            GNUNET_MESSAGE_TYPE_SET_REQUEST,
927                            struct GNUNET_SET_RequestMessage,
928                            lh),
929     GNUNET_MQ_handler_end ()
930   };
931   struct GNUNET_MQ_Envelope *mqm;
932   struct GNUNET_SET_ListenMessage *msg;
933
934   lh->reconnect_task = NULL;
935   GNUNET_assert (NULL == lh->mq);
936   lh->mq = GNUNET_CLIENT_connect (lh->cfg,
937                                   "set",
938                                   mq_handlers,
939                                   &handle_client_listener_error,
940                                   lh);
941   if (NULL == lh->mq)
942     return;
943   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
944   msg->operation = htonl (lh->operation);
945   msg->app_id = lh->app_id;
946   GNUNET_MQ_send (lh->mq,
947                   mqm);
948 }
949
950
951 /**
952  * Wait for set operation requests for the given application id
953  *
954  * @param cfg configuration to use for connecting to
955  *            the set service, needs to be valid for the lifetime of the listen handle
956  * @param operation operation we want to listen for
957  * @param app_id id of the application that handles set operation requests
958  * @param listen_cb called for each incoming request matching the operation
959  *                  and application id
960  * @param listen_cls handle for @a listen_cb
961  * @return a handle that can be used to cancel the listen operation
962  */
963 struct GNUNET_SET_ListenHandle *
964 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
965                    enum GNUNET_SET_OperationType operation,
966                    const struct GNUNET_HashCode *app_id,
967                    GNUNET_SET_ListenCallback listen_cb,
968                    void *listen_cls)
969 {
970   struct GNUNET_SET_ListenHandle *lh;
971
972   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
973   lh->listen_cb = listen_cb;
974   lh->listen_cls = listen_cls;
975   lh->cfg = cfg;
976   lh->operation = operation;
977   lh->app_id = *app_id;
978   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
979   listen_connect (lh);
980   if (NULL == lh->mq)
981   {
982     GNUNET_free (lh);
983     return NULL;
984   }
985   return lh;
986 }
987
988
989 /**
990  * Cancel the given listen operation.
991  *
992  * @param lh handle for the listen operation
993  */
994 void
995 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
996 {
997   LOG (GNUNET_ERROR_TYPE_DEBUG,
998        "Canceling listener\n");
999   if (NULL != lh->mq)
1000   {
1001     GNUNET_MQ_destroy (lh->mq);
1002     lh->mq = NULL;
1003   }
1004   if (NULL != lh->reconnect_task)
1005   {
1006     GNUNET_SCHEDULER_cancel (lh->reconnect_task);
1007     lh->reconnect_task = NULL;
1008   }
1009   GNUNET_free (lh);
1010 }
1011
1012
1013 /**
1014  * Accept a request we got via #GNUNET_SET_listen.  Must be called during
1015  * #GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
1016  * afterwards.
1017  * Call #GNUNET_SET_commit to provide the local set to use for the operation,
1018  * and to begin the exchange with the remote peer.
1019  *
1020  * @param request request to accept
1021  * @param result_mode specified how results will be returned,
1022  *        see `enum GNUNET_SET_ResultMode`.
1023  * @param result_cb callback for the results
1024  * @param result_cls closure for @a result_cb
1025  * @return a handle to cancel the operation
1026  */
1027 struct GNUNET_SET_OperationHandle *
1028 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
1029                    enum GNUNET_SET_ResultMode result_mode,
1030                    struct GNUNET_SET_Option options[],
1031                    GNUNET_SET_ResultIterator result_cb,
1032                    void *result_cls)
1033 {
1034   struct GNUNET_MQ_Envelope *mqm;
1035   struct GNUNET_SET_OperationHandle *oh;
1036   struct GNUNET_SET_AcceptMessage *msg;
1037
1038   GNUNET_assert (GNUNET_NO == request->accepted);
1039   LOG (GNUNET_ERROR_TYPE_DEBUG,
1040        "Client accepts set operation (%d)\n",
1041        result_mode);
1042   request->accepted = GNUNET_YES;
1043   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
1044   msg->accept_reject_id = htonl (request->accept_id);
1045   msg->result_mode = htonl (result_mode);
1046   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
1047   oh->result_cb = result_cb;
1048   oh->result_cls = result_cls;
1049   oh->conclude_mqm = mqm;
1050   oh->request_id_addr = &msg->request_id;
1051   return oh;
1052 }
1053
1054
1055 /**
1056  * Commit a set to be used with a set operation.
1057  * This function is called once we have fully constructed
1058  * the set that we want to use for the operation.  At this
1059  * time, the P2P protocol can then begin to exchange the
1060  * set information and call the result callback with the
1061  * result information.
1062  *
1063  * @param oh handle to the set operation
1064  * @param set the set to use for the operation
1065  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
1066  *         set is invalid (e.g. the set service crashed)
1067  */
1068 int
1069 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
1070                    struct GNUNET_SET_Handle *set)
1071 {
1072   if (NULL != oh->set)
1073   {
1074     /* Some other set was already commited for this
1075      * operation, there is a logic bug in the client of this API */
1076     GNUNET_break (0);
1077     return GNUNET_OK;
1078   }
1079   if (GNUNET_YES == set->invalid)
1080     return GNUNET_SYSERR;
1081   LOG (GNUNET_ERROR_TYPE_DEBUG,
1082        "Client commits to SET\n");
1083   GNUNET_assert (NULL != oh->conclude_mqm);
1084   oh->set = set;
1085   GNUNET_CONTAINER_DLL_insert (set->ops_head,
1086                                set->ops_tail,
1087                                oh);
1088   oh->request_id = GNUNET_MQ_assoc_add (set->mq,
1089                                         oh);
1090   *oh->request_id_addr = htonl (oh->request_id);
1091   GNUNET_MQ_send (set->mq,
1092                   oh->conclude_mqm);
1093   oh->conclude_mqm = NULL;
1094   oh->request_id_addr = NULL;
1095   return GNUNET_OK;
1096 }
1097
1098
1099 /**
1100  * Iterate over all elements in the given set.  Note that this
1101  * operation involves transferring every element of the set from the
1102  * service to the client, and is thus costly.
1103  *
1104  * @param set the set to iterate over
1105  * @param iter the iterator to call for each element
1106  * @param iter_cls closure for @a iter
1107  * @return #GNUNET_YES if the iteration started successfuly,
1108  *         #GNUNET_NO if another iteration is active
1109  *         #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
1110  */
1111 int
1112 GNUNET_SET_iterate (struct GNUNET_SET_Handle *set,
1113                     GNUNET_SET_ElementIterator iter,
1114                     void *iter_cls)
1115 {
1116   struct GNUNET_MQ_Envelope *ev;
1117
1118   GNUNET_assert (NULL != iter);
1119   if (GNUNET_YES == set->invalid)
1120     return GNUNET_SYSERR;
1121   if (NULL != set->iterator)
1122     return GNUNET_NO;
1123   LOG (GNUNET_ERROR_TYPE_DEBUG,
1124        "Iterating over set\n");
1125   set->iterator = iter;
1126   set->iterator_cls = iter_cls;
1127   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
1128   GNUNET_MQ_send (set->mq, ev);
1129   return GNUNET_YES;
1130 }
1131
1132
1133 void
1134 GNUNET_SET_copy_lazy (struct GNUNET_SET_Handle *set,
1135                       GNUNET_SET_CopyReadyCallback cb,
1136                       void *cls)
1137 {
1138   struct GNUNET_MQ_Envelope *ev;
1139   struct SetCopyRequest *req;
1140
1141   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE);
1142   GNUNET_MQ_send (set->mq, ev);
1143
1144   req = GNUNET_new (struct SetCopyRequest);
1145   req->cb = cb;
1146   req->cls = cls;
1147   GNUNET_CONTAINER_DLL_insert (set->copy_req_head,
1148                                set->copy_req_tail,
1149                                req);
1150 }
1151
1152
1153 /**
1154  * Create a copy of an element.  The copy
1155  * must be GNUNET_free-d by the caller.
1156  *
1157  * @param element the element to copy
1158  * @return the copied element
1159  */
1160 struct GNUNET_SET_Element *
1161 GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element)
1162 {
1163   struct GNUNET_SET_Element *copy;
1164
1165   copy = GNUNET_malloc (element->size + sizeof (struct GNUNET_SET_Element));
1166   copy->size = element->size;
1167   copy->element_type = element->element_type;
1168   copy->data = &copy[1];
1169   GNUNET_memcpy (&copy[1],
1170                  element->data,
1171                  copy->size);
1172   return copy;
1173 }
1174
1175
1176 /**
1177  * Hash a set element.
1178  *
1179  * @param element the element that should be hashed
1180  * @param[out] ret_hash a pointer to where the hash of @a element
1181  *        should be stored
1182  */
1183 void
1184 GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element,
1185                          struct GNUNET_HashCode *ret_hash)
1186 {
1187   struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
1188
1189   /* It's not guaranteed that the element data is always after the element header,
1190      so we need to hash the chunks separately. */
1191   GNUNET_CRYPTO_hash_context_read (ctx, &element->size, sizeof (uint16_t));
1192   GNUNET_CRYPTO_hash_context_read (ctx, &element->element_type, sizeof (uint16_t));
1193   GNUNET_CRYPTO_hash_context_read (ctx, element->data, element->size);
1194   GNUNET_CRYPTO_hash_context_finish (ctx, ret_hash);
1195 }
1196
1197 /* end of set_api.c */