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