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