don't free element that is stack-allocated now
[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                    GNUNET_ntohll (msg->current_size),
436                    result_status);
437   }
438   else
439   {
440     LOG (GNUNET_ERROR_TYPE_DEBUG,
441          "No callback for final status\n");
442   }
443   if (destroy_set)
444     GNUNET_SET_destroy (set);
445   GNUNET_free (oh);
446   return;
447
448 do_element:
449   LOG (GNUNET_ERROR_TYPE_DEBUG,
450        "Treating result as element\n");
451   e.data = &msg[1];
452   e.size = ntohs (msg->header.size) - sizeof (struct GNUNET_SET_ResultMessage);
453   e.element_type = ntohs (msg->element_type);
454   if (NULL != oh->result_cb)
455     oh->result_cb (oh->result_cls,
456                    &e,
457                    GNUNET_ntohll (msg->current_size),
458                    result_status);
459 }
460
461
462 /**
463  * Destroy the given set operation.
464  *
465  * @param oh set operation to destroy
466  */
467 static void
468 set_operation_destroy (struct GNUNET_SET_OperationHandle *oh)
469 {
470   struct GNUNET_SET_Handle *set = oh->set;
471   struct GNUNET_SET_OperationHandle *h_assoc;
472
473   if (NULL != oh->conclude_mqm)
474     GNUNET_MQ_discard (oh->conclude_mqm);
475   /* is the operation already commited? */
476   if (NULL != set)
477   {
478     GNUNET_CONTAINER_DLL_remove (set->ops_head,
479                                  set->ops_tail,
480                                  oh);
481     h_assoc = GNUNET_MQ_assoc_remove (set->mq,
482                                       oh->request_id);
483     GNUNET_assert ((NULL == h_assoc) || (h_assoc == oh));
484   }
485   GNUNET_free (oh);
486 }
487
488
489 /**
490  * Cancel the given set operation.  We need to send an explicit cancel
491  * message, as all operations one one set communicate using one
492  * handle.
493  *
494  * @param oh set operation to cancel
495  */
496 void
497 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
498 {
499   struct GNUNET_SET_Handle *set = oh->set;
500   struct GNUNET_SET_CancelMessage *m;
501   struct GNUNET_MQ_Envelope *mqm;
502
503   if (NULL != set)
504   {
505     mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SET_CANCEL);
506     m->request_id = htonl (oh->request_id);
507     GNUNET_MQ_send (set->mq, mqm);
508   }
509   set_operation_destroy (oh);
510   if ( (NULL != set) &&
511        (GNUNET_YES == set->destroy_requested) &&
512        (NULL == set->ops_head) )
513   {
514     LOG (GNUNET_ERROR_TYPE_DEBUG,
515          "Destroying set after operation cancel\n");
516     GNUNET_SET_destroy (set);
517   }
518 }
519
520
521 /**
522  * We encountered an error communicating with the set service while
523  * performing a set operation. Report to the application.
524  *
525  * @param cls the `struct GNUNET_SET_Handle`
526  * @param error error code
527  */
528 static void
529 handle_client_set_error (void *cls,
530                          enum GNUNET_MQ_Error error)
531 {
532   struct GNUNET_SET_Handle *set = cls;
533   GNUNET_SET_ElementIterator iter = set->iterator;
534
535   LOG (GNUNET_ERROR_TYPE_ERROR,
536        "Handling client set error %d\n",
537        error);
538   while (NULL != set->ops_head)
539   {
540     if (NULL != set->ops_head->result_cb)
541       set->ops_head->result_cb (set->ops_head->result_cls,
542                                 NULL,
543                                 0,
544                                 GNUNET_SET_STATUS_FAILURE);
545     set_operation_destroy (set->ops_head);
546   }
547   set->iterator = NULL;
548   set->iteration_id++;
549   set->invalid = GNUNET_YES;
550   if (NULL != iter)
551     iter (set->iterator_cls,
552           NULL);
553 }
554
555
556 static struct GNUNET_SET_Handle *
557 create_internal (const struct GNUNET_CONFIGURATION_Handle *cfg,
558                  enum GNUNET_SET_OperationType op,
559                  const uint32_t *cookie)
560 {
561   struct GNUNET_SET_Handle *set = GNUNET_new (struct GNUNET_SET_Handle);
562   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
563     GNUNET_MQ_hd_var_size (result,
564                            GNUNET_MESSAGE_TYPE_SET_RESULT,
565                            struct GNUNET_SET_ResultMessage,
566                            set),
567     GNUNET_MQ_hd_var_size (iter_element,
568                            GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT,
569                            struct GNUNET_SET_IterResponseMessage,
570                            set),
571     GNUNET_MQ_hd_fixed_size (iter_done,
572                              GNUNET_MESSAGE_TYPE_SET_ITER_DONE,
573                              struct GNUNET_MessageHeader,
574                              set),
575     GNUNET_MQ_hd_fixed_size (copy_lazy,
576                              GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_RESPONSE,
577                              struct GNUNET_SET_CopyLazyResponseMessage,
578                              set),
579     GNUNET_MQ_handler_end ()
580   };
581   struct GNUNET_MQ_Envelope *mqm;
582   struct GNUNET_SET_CreateMessage *create_msg;
583   struct GNUNET_SET_CopyLazyConnectMessage *copy_msg;
584
585   set->cfg = cfg;
586   set->mq = GNUNET_CLIENT_connect (cfg,
587                                    "set",
588                                    mq_handlers,
589                                    &handle_client_set_error,
590                                    set);
591   if (NULL == set->mq)
592   {
593     GNUNET_free (set);
594     return NULL;
595   }
596   if (NULL == cookie)
597   {
598     LOG (GNUNET_ERROR_TYPE_DEBUG,
599          "Creating new set (operation %u)\n",
600          op);
601     mqm = GNUNET_MQ_msg (create_msg,
602                          GNUNET_MESSAGE_TYPE_SET_CREATE);
603     create_msg->operation = htonl (op);
604   }
605   else
606   {
607     LOG (GNUNET_ERROR_TYPE_DEBUG,
608          "Creating new set (lazy copy)\n",
609          op);
610     mqm = GNUNET_MQ_msg (copy_msg,
611                          GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_CONNECT);
612     copy_msg->cookie = *cookie;
613   }
614   GNUNET_MQ_send (set->mq, mqm);
615   return set;
616 }
617
618
619 /**
620  * Create an empty set, supporting the specified operation.
621  *
622  * @param cfg configuration to use for connecting to the
623  *        set service
624  * @param op operation supported by the set
625  *        Note that the operation has to be specified
626  *        beforehand, as certain set operations need to maintain
627  *        data structures spefific to the operation
628  * @return a handle to the set
629  */
630 struct GNUNET_SET_Handle *
631 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
632                    enum GNUNET_SET_OperationType op)
633 {
634   return create_internal (cfg, op, NULL);
635 }
636
637
638 /**
639  * Add an element to the given set.  After the element has been added
640  * (in the sense of being transmitted to the set service), @a cont
641  * will be called.  Multiple calls to GNUNET_SET_add_element() can be
642  * queued.
643  *
644  * @param set set to add element to
645  * @param element element to add to the set
646  * @param cont continuation called after the element has been added
647  * @param cont_cls closure for @a cont
648  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
649  *         set is invalid (e.g. the set service crashed)
650  */
651 int
652 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
653                         const struct GNUNET_SET_Element *element,
654                         GNUNET_SET_Continuation cont,
655                         void *cont_cls)
656 {
657   struct GNUNET_MQ_Envelope *mqm;
658   struct GNUNET_SET_ElementMessage *msg;
659
660   LOG (GNUNET_ERROR_TYPE_INFO, "adding element of type %u\n", (unsigned) element->element_type);
661
662   if (GNUNET_YES == set->invalid)
663   {
664     if (NULL != cont)
665       cont (cont_cls);
666     return GNUNET_SYSERR;
667   }
668   mqm = GNUNET_MQ_msg_extra (msg,
669                              element->size,
670                              GNUNET_MESSAGE_TYPE_SET_ADD);
671   msg->element_type = htons (element->element_type);
672   GNUNET_memcpy (&msg[1],
673                  element->data,
674                  element->size);
675   GNUNET_MQ_notify_sent (mqm,
676                          cont, cont_cls);
677   GNUNET_MQ_send (set->mq, mqm);
678   return GNUNET_OK;
679 }
680
681
682 /**
683  * Remove an element to the given set.  After the element has been
684  * removed (in the sense of the request being transmitted to the set
685  * service), @a cont will be called.  Multiple calls to
686  * GNUNET_SET_remove_element() can be queued
687  *
688  * @param set set to remove element from
689  * @param element element to remove from the set
690  * @param cont continuation called after the element has been removed
691  * @param cont_cls closure for @a cont
692  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
693  *         set is invalid (e.g. the set service crashed)
694  */
695 int
696 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
697                            const struct GNUNET_SET_Element *element,
698                            GNUNET_SET_Continuation cont,
699                            void *cont_cls)
700 {
701   struct GNUNET_MQ_Envelope *mqm;
702   struct GNUNET_SET_ElementMessage *msg;
703
704   if (GNUNET_YES == set->invalid)
705   {
706     if (NULL != cont)
707       cont (cont_cls);
708     return GNUNET_SYSERR;
709   }
710   mqm = GNUNET_MQ_msg_extra (msg,
711                              element->size,
712                              GNUNET_MESSAGE_TYPE_SET_REMOVE);
713   msg->element_type = htons (element->element_type);
714   GNUNET_memcpy (&msg[1],
715                  element->data,
716                  element->size);
717   GNUNET_MQ_notify_sent (mqm,
718                          cont, cont_cls);
719   GNUNET_MQ_send (set->mq, mqm);
720   return GNUNET_OK;
721 }
722
723
724 /**
725  * Destroy the set handle if no operations are left, mark the set
726  * for destruction otherwise.
727  *
728  * @param set set handle to destroy
729  */
730 void
731 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
732 {
733   /* destroying set while iterator is active is currently
734      not supported; we should expand the API to allow
735      clients to explicitly cancel the iteration! */
736   GNUNET_assert (NULL == set->iterator);
737   if (NULL != set->ops_head)
738   {
739     LOG (GNUNET_ERROR_TYPE_DEBUG,
740          "Set operations are pending, delaying set destruction\n");
741     set->destroy_requested = GNUNET_YES;
742     return;
743   }
744   LOG (GNUNET_ERROR_TYPE_DEBUG,
745        "Really destroying set\n");
746   if (NULL != set->mq)
747   {
748     GNUNET_MQ_destroy (set->mq);
749     set->mq = NULL;
750   }
751   GNUNET_free (set);
752 }
753
754
755 /**
756  * Prepare a set operation to be evaluated with another peer.
757  * The evaluation will not start until the client provides
758  * a local set with #GNUNET_SET_commit().
759  *
760  * @param other_peer peer with the other set
761  * @param app_id hash for the application using the set
762  * @param context_msg additional information for the request
763  * @param result_mode specified how results will be returned,
764  *        see `enum GNUNET_SET_ResultMode`.
765  * @param result_cb called on error or success
766  * @param result_cls closure for @e result_cb
767  * @return a handle to cancel the operation
768  */
769 struct GNUNET_SET_OperationHandle *
770 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
771                     const struct GNUNET_HashCode *app_id,
772                     const struct GNUNET_MessageHeader *context_msg,
773                     enum GNUNET_SET_ResultMode result_mode,
774                     struct GNUNET_SET_Option options[],
775                     GNUNET_SET_ResultIterator result_cb,
776                     void *result_cls)
777 {
778   struct GNUNET_MQ_Envelope *mqm;
779   struct GNUNET_SET_OperationHandle *oh;
780   struct GNUNET_SET_EvaluateMessage *msg;
781   struct GNUNET_SET_Option *opt;
782
783   LOG (GNUNET_ERROR_TYPE_DEBUG,
784        "Client prepares set operation (%d)\n",
785        result_mode);
786   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
787   oh->result_cb = result_cb;
788   oh->result_cls = result_cls;
789   mqm = GNUNET_MQ_msg_nested_mh (msg,
790                                  GNUNET_MESSAGE_TYPE_SET_EVALUATE,
791                                  context_msg);
792   msg->app_id = *app_id;
793   msg->result_mode = htonl (result_mode);
794   msg->target_peer = *other_peer;
795   for (opt = options; opt->type != 0; opt++)
796   {
797     switch (opt->type)
798     {
799       case GNUNET_SET_OPTION_BYZANTINE:
800         msg->byzantine = GNUNET_YES;
801         msg->byzantine_lower_bound = opt->v.num;
802         break;
803       case GNUNET_SET_OPTION_FORCE_FULL:
804         msg->force_full = GNUNET_YES;
805         break;
806       case GNUNET_SET_OPTION_FORCE_DELTA:
807         msg->force_delta = GNUNET_YES;
808         break;
809       default:
810         LOG (GNUNET_ERROR_TYPE_ERROR, 
811              "Option with type %d not recognized\n", (int) opt->type);
812     }
813   }
814   oh->conclude_mqm = mqm;
815   oh->request_id_addr = &msg->request_id;
816
817   return oh;
818 }
819
820
821 /**
822  * Connect to the set service in order to listen for requests.
823  *
824  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
825  */
826 static void
827 listen_connect (void *cls);
828
829
830 /**
831  * Check validity of request message for a listen operation
832  *
833  * @param cls the listen handle
834  * @param msg the message
835  * @return #GNUNET_OK if the message is well-formed
836  */
837 static int
838 check_request (void *cls,
839                const struct GNUNET_SET_RequestMessage *msg)
840 {
841   const struct GNUNET_MessageHeader *context_msg;
842
843   if (ntohs (msg->header.size) == sizeof (*msg))
844     return GNUNET_OK; /* no context message is OK */
845   context_msg = GNUNET_MQ_extract_nested_mh (msg);
846   if (NULL == context_msg)
847   {
848     /* malformed context message is NOT ok */
849     GNUNET_break_op (0);
850     return GNUNET_SYSERR;
851   }
852   return GNUNET_OK;
853 }
854
855
856 /**
857  * Handle request message for a listen operation
858  *
859  * @param cls the listen handle
860  * @param msg the message
861  */
862 static void
863 handle_request (void *cls,
864                 const struct GNUNET_SET_RequestMessage *msg)
865 {
866   struct GNUNET_SET_ListenHandle *lh = cls;
867   struct GNUNET_SET_Request req;
868   const struct GNUNET_MessageHeader *context_msg;
869   struct GNUNET_MQ_Envelope *mqm;
870   struct GNUNET_SET_RejectMessage *rmsg;
871
872   LOG (GNUNET_ERROR_TYPE_DEBUG,
873        "Processing incoming operation request\n");
874   /* we got another valid request => reset the backoff */
875   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
876   req.accept_id = ntohl (msg->accept_id);
877   req.accepted = GNUNET_NO;
878   context_msg = GNUNET_MQ_extract_nested_mh (msg);
879   /* calling #GNUNET_SET_accept() in the listen cb will set req->accepted */
880   lh->listen_cb (lh->listen_cls,
881                  &msg->peer_id,
882                  context_msg,
883                  &req);
884   if (GNUNET_YES == req.accepted)
885     return; /* the accept-case is handled in #GNUNET_SET_accept() */
886   LOG (GNUNET_ERROR_TYPE_DEBUG,
887        "Rejecting request\n");
888   mqm = GNUNET_MQ_msg (rmsg,
889                        GNUNET_MESSAGE_TYPE_SET_REJECT);
890   rmsg->accept_reject_id = msg->accept_id;
891   GNUNET_MQ_send (lh->mq, mqm);
892 }
893
894
895 /**
896  * Our connection with the set service encountered an error,
897  * re-initialize with exponential back-off.
898  *
899  * @param cls the `struct GNUNET_SET_ListenHandle *`
900  * @param error reason for the disconnect
901  */
902 static void
903 handle_client_listener_error (void *cls,
904                               enum GNUNET_MQ_Error error)
905 {
906   struct GNUNET_SET_ListenHandle *lh = cls;
907
908   LOG (GNUNET_ERROR_TYPE_DEBUG,
909        "Listener broke down (%d), re-connecting\n",
910        (int) error);
911   GNUNET_MQ_destroy (lh->mq);
912   lh->mq = NULL;
913   lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
914                                                      &listen_connect,
915                                                      lh);
916   lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
917 }
918
919
920 /**
921  * Connect to the set service in order to listen for requests.
922  *
923  * @param cls the `struct GNUNET_SET_ListenHandle *` to connect
924  */
925 static void
926 listen_connect (void *cls)
927 {
928   struct GNUNET_SET_ListenHandle *lh = cls;
929   struct GNUNET_MQ_MessageHandler mq_handlers[] = {
930     GNUNET_MQ_hd_var_size (request,
931                            GNUNET_MESSAGE_TYPE_SET_REQUEST,
932                            struct GNUNET_SET_RequestMessage,
933                            lh),
934     GNUNET_MQ_handler_end ()
935   };
936   struct GNUNET_MQ_Envelope *mqm;
937   struct GNUNET_SET_ListenMessage *msg;
938
939   lh->reconnect_task = NULL;
940   GNUNET_assert (NULL == lh->mq);
941   lh->mq = GNUNET_CLIENT_connect (lh->cfg,
942                                   "set",
943                                   mq_handlers,
944                                   &handle_client_listener_error,
945                                   lh);
946   if (NULL == lh->mq)
947     return;
948   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
949   msg->operation = htonl (lh->operation);
950   msg->app_id = lh->app_id;
951   GNUNET_MQ_send (lh->mq,
952                   mqm);
953 }
954
955
956 /**
957  * Wait for set operation requests for the given application id
958  *
959  * @param cfg configuration to use for connecting to
960  *            the set service, needs to be valid for the lifetime of the listen handle
961  * @param operation operation we want to listen for
962  * @param app_id id of the application that handles set operation requests
963  * @param listen_cb called for each incoming request matching the operation
964  *                  and application id
965  * @param listen_cls handle for @a listen_cb
966  * @return a handle that can be used to cancel the listen operation
967  */
968 struct GNUNET_SET_ListenHandle *
969 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
970                    enum GNUNET_SET_OperationType operation,
971                    const struct GNUNET_HashCode *app_id,
972                    GNUNET_SET_ListenCallback listen_cb,
973                    void *listen_cls)
974 {
975   struct GNUNET_SET_ListenHandle *lh;
976
977   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
978   lh->listen_cb = listen_cb;
979   lh->listen_cls = listen_cls;
980   lh->cfg = cfg;
981   lh->operation = operation;
982   lh->app_id = *app_id;
983   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
984   listen_connect (lh);
985   if (NULL == lh->mq)
986   {
987     GNUNET_free (lh);
988     return NULL;
989   }
990   return lh;
991 }
992
993
994 /**
995  * Cancel the given listen operation.
996  *
997  * @param lh handle for the listen operation
998  */
999 void
1000 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
1001 {
1002   LOG (GNUNET_ERROR_TYPE_DEBUG,
1003        "Canceling listener\n");
1004   if (NULL != lh->mq)
1005   {
1006     GNUNET_MQ_destroy (lh->mq);
1007     lh->mq = NULL;
1008   }
1009   if (NULL != lh->reconnect_task)
1010   {
1011     GNUNET_SCHEDULER_cancel (lh->reconnect_task);
1012     lh->reconnect_task = NULL;
1013   }
1014   GNUNET_free (lh);
1015 }
1016
1017
1018 /**
1019  * Accept a request we got via #GNUNET_SET_listen.  Must be called during
1020  * #GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
1021  * afterwards.
1022  * Call #GNUNET_SET_commit to provide the local set to use for the operation,
1023  * and to begin the exchange with the remote peer.
1024  *
1025  * @param request request to accept
1026  * @param result_mode specified how results will be returned,
1027  *        see `enum GNUNET_SET_ResultMode`.
1028  * @param result_cb callback for the results
1029  * @param result_cls closure for @a result_cb
1030  * @return a handle to cancel the operation
1031  */
1032 struct GNUNET_SET_OperationHandle *
1033 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
1034                    enum GNUNET_SET_ResultMode result_mode,
1035                    struct GNUNET_SET_Option options[],
1036                    GNUNET_SET_ResultIterator result_cb,
1037                    void *result_cls)
1038 {
1039   struct GNUNET_MQ_Envelope *mqm;
1040   struct GNUNET_SET_OperationHandle *oh;
1041   struct GNUNET_SET_AcceptMessage *msg;
1042
1043   GNUNET_assert (GNUNET_NO == request->accepted);
1044   LOG (GNUNET_ERROR_TYPE_DEBUG,
1045        "Client accepts set operation (%d)\n",
1046        result_mode);
1047   request->accepted = GNUNET_YES;
1048   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
1049   msg->accept_reject_id = htonl (request->accept_id);
1050   msg->result_mode = htonl (result_mode);
1051   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
1052   oh->result_cb = result_cb;
1053   oh->result_cls = result_cls;
1054   oh->conclude_mqm = mqm;
1055   oh->request_id_addr = &msg->request_id;
1056   return oh;
1057 }
1058
1059
1060 /**
1061  * Commit a set to be used with a set operation.
1062  * This function is called once we have fully constructed
1063  * the set that we want to use for the operation.  At this
1064  * time, the P2P protocol can then begin to exchange the
1065  * set information and call the result callback with the
1066  * result information.
1067  *
1068  * @param oh handle to the set operation
1069  * @param set the set to use for the operation
1070  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
1071  *         set is invalid (e.g. the set service crashed)
1072  */
1073 int
1074 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
1075                    struct GNUNET_SET_Handle *set)
1076 {
1077   if (NULL != oh->set)
1078   {
1079     /* Some other set was already commited for this
1080      * operation, there is a logic bug in the client of this API */
1081     GNUNET_break (0);
1082     return GNUNET_OK;
1083   }
1084   if (GNUNET_YES == set->invalid)
1085     return GNUNET_SYSERR;
1086   LOG (GNUNET_ERROR_TYPE_DEBUG,
1087        "Client commits to SET\n");
1088   GNUNET_assert (NULL != oh->conclude_mqm);
1089   oh->set = set;
1090   GNUNET_CONTAINER_DLL_insert (set->ops_head,
1091                                set->ops_tail,
1092                                oh);
1093   oh->request_id = GNUNET_MQ_assoc_add (set->mq,
1094                                         oh);
1095   *oh->request_id_addr = htonl (oh->request_id);
1096   GNUNET_MQ_send (set->mq,
1097                   oh->conclude_mqm);
1098   oh->conclude_mqm = NULL;
1099   oh->request_id_addr = NULL;
1100   return GNUNET_OK;
1101 }
1102
1103
1104 /**
1105  * Iterate over all elements in the given set.  Note that this
1106  * operation involves transferring every element of the set from the
1107  * service to the client, and is thus costly.
1108  *
1109  * @param set the set to iterate over
1110  * @param iter the iterator to call for each element
1111  * @param iter_cls closure for @a iter
1112  * @return #GNUNET_YES if the iteration started successfuly,
1113  *         #GNUNET_NO if another iteration is active
1114  *         #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
1115  */
1116 int
1117 GNUNET_SET_iterate (struct GNUNET_SET_Handle *set,
1118                     GNUNET_SET_ElementIterator iter,
1119                     void *iter_cls)
1120 {
1121   struct GNUNET_MQ_Envelope *ev;
1122
1123   GNUNET_assert (NULL != iter);
1124   if (GNUNET_YES == set->invalid)
1125     return GNUNET_SYSERR;
1126   if (NULL != set->iterator)
1127     return GNUNET_NO;
1128   LOG (GNUNET_ERROR_TYPE_DEBUG,
1129        "Iterating over set\n");
1130   set->iterator = iter;
1131   set->iterator_cls = iter_cls;
1132   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
1133   GNUNET_MQ_send (set->mq, ev);
1134   return GNUNET_YES;
1135 }
1136
1137
1138 void
1139 GNUNET_SET_copy_lazy (struct GNUNET_SET_Handle *set,
1140                       GNUNET_SET_CopyReadyCallback cb,
1141                       void *cls)
1142 {
1143   struct GNUNET_MQ_Envelope *ev;
1144   struct SetCopyRequest *req;
1145
1146   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_COPY_LAZY_PREPARE);
1147   GNUNET_MQ_send (set->mq, ev);
1148
1149   req = GNUNET_new (struct SetCopyRequest);
1150   req->cb = cb;
1151   req->cls = cls;
1152   GNUNET_CONTAINER_DLL_insert (set->copy_req_head,
1153                                set->copy_req_tail,
1154                                req);
1155 }
1156
1157
1158 /**
1159  * Create a copy of an element.  The copy
1160  * must be GNUNET_free-d by the caller.
1161  *
1162  * @param element the element to copy
1163  * @return the copied element
1164  */
1165 struct GNUNET_SET_Element *
1166 GNUNET_SET_element_dup (const struct GNUNET_SET_Element *element)
1167 {
1168   struct GNUNET_SET_Element *copy;
1169
1170   copy = GNUNET_malloc (element->size + sizeof (struct GNUNET_SET_Element));
1171   copy->size = element->size;
1172   copy->element_type = element->element_type;
1173   copy->data = &copy[1];
1174   GNUNET_memcpy (&copy[1],
1175                  element->data,
1176                  copy->size);
1177   return copy;
1178 }
1179
1180
1181 /**
1182  * Hash a set element.
1183  *
1184  * @param element the element that should be hashed
1185  * @param[out] ret_hash a pointer to where the hash of @a element
1186  *        should be stored
1187  */
1188 void
1189 GNUNET_SET_element_hash (const struct GNUNET_SET_Element *element,
1190                          struct GNUNET_HashCode *ret_hash)
1191 {
1192   struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
1193
1194   /* It's not guaranteed that the element data is always after the element header,
1195      so we need to hash the chunks separately. */
1196   GNUNET_CRYPTO_hash_context_read (ctx, &element->size, sizeof (uint16_t));
1197   GNUNET_CRYPTO_hash_context_read (ctx, &element->element_type, sizeof (uint16_t));
1198   GNUNET_CRYPTO_hash_context_read (ctx, element->data, element->size);
1199   GNUNET_CRYPTO_hash_context_finish (ctx, ret_hash);
1200 }
1201
1202 /* end of set_api.c */