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