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