fixed a bug where a set handle was used after free
[oweals/gnunet.git] / src / set / set_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file set/set_api.c
23  * @brief api for the set service
24  * @author Florian Dold
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_client_lib.h"
30 #include "gnunet_set_service.h"
31 #include "set.h"
32
33
34 #define LOG(kind,...) GNUNET_log_from (kind, "set-api",__VA_ARGS__)
35
36 /**
37  * Opaque handle to a set.
38  */
39 struct GNUNET_SET_Handle
40 {
41   /**
42    * Client connected to the set service.
43    */
44   struct GNUNET_CLIENT_Connection *client;
45
46   /**
47    * Message queue for 'client'.
48    */
49   struct GNUNET_MQ_Handle *mq;
50
51   /**
52    * Linked list of operations on the set.
53    */
54   struct GNUNET_SET_OperationHandle *ops_head;
55
56   /**
57    * Linked list of operations on the set.
58    */
59   struct GNUNET_SET_OperationHandle *ops_tail;
60
61   /**
62    * Should the set be destroyed once all operations are gone?
63    */
64   int destroy_requested;
65
66   /**
67    * Has the set become invalid (e.g. service died)?
68    */
69   int invalid;
70
71   /**
72    * Callback for the current iteration over the set,
73    * NULL if no iterator is active.
74    */
75   GNUNET_SET_ElementIterator iterator;
76
77   /**
78    * Closure for 'iterator'
79    */
80   void *iterator_cls;
81 };
82
83
84 /**
85  * Opaque handle to a set operation request from another peer.
86  */
87 struct GNUNET_SET_Request
88 {
89   /**
90    * Id of the request, used to identify the request when
91    * accepting/rejecting it.
92    */
93   uint32_t accept_id;
94
95   /**
96    * Has the request been accepted already?
97    * GNUNET_YES/GNUNET_NO
98    */
99   int accepted;
100 };
101
102
103 /**
104  * Handle to an operation.
105  * Only known to the service after commiting
106  * the handle with a set.
107  */
108 struct GNUNET_SET_OperationHandle
109 {
110   /**
111    * Function to be called when we have a result,
112    * or an error.
113    */
114   GNUNET_SET_ResultIterator result_cb;
115
116   /**
117    * Closure for result_cb.
118    */
119   void *result_cls;
120
121   /**
122    * Local set used for the operation,
123    * NULL if no set has been provided by conclude yet.
124    */
125   struct GNUNET_SET_Handle *set;
126
127   /**
128    * Request ID to identify the operation within the set.
129    */
130   uint32_t request_id;
131
132   /**
133    * Message sent to the server on calling conclude,
134    * NULL if conclude has been called.
135    */
136   struct GNUNET_MQ_Envelope *conclude_mqm;
137
138   /**
139    * Address of the request if in the conclude message,
140    * used to patch the request id into the message when the set is known.
141    */
142   uint32_t *request_id_addr;
143
144   /**
145    * Handles are kept in a linked list.
146    */
147   struct GNUNET_SET_OperationHandle *prev;
148
149   /**
150    * Handles are kept in a linked list.
151    */
152   struct GNUNET_SET_OperationHandle *next;
153 };
154
155
156 /**
157  * Opaque handle to a listen operation.
158  */
159 struct GNUNET_SET_ListenHandle
160 {
161   /**
162    * Connection to the service.
163    */
164   struct GNUNET_CLIENT_Connection *client;
165
166   /**
167    * Message queue for the client.
168    */
169   struct GNUNET_MQ_Handle* mq;
170
171   /**
172    * Configuration handle for the listener, stored
173    * here to be able to reconnect transparently on
174    * connection failure.
175    */
176   const struct GNUNET_CONFIGURATION_Handle *cfg;
177
178   /**
179    * Function to call on a new incoming request,
180    * or on error.
181    */
182   GNUNET_SET_ListenCallback listen_cb;
183
184   /**
185    * Closure for listen_cb.
186    */
187   void *listen_cls;
188
189   /**
190    * Operation we listen for.
191    */
192   enum GNUNET_SET_OperationType operation;
193
194   /**
195    * Application ID we listen for.
196    */
197   struct GNUNET_HashCode app_id;
198
199   /**
200    * Time to wait until we try to reconnect on failure.
201    */
202   struct GNUNET_TIME_Relative reconnect_backoff;
203 };
204
205
206 /* forward declaration */
207 static void
208 listen_connect (void *cls,
209                 const struct GNUNET_SCHEDULER_TaskContext *tc);
210
211
212 /**
213  * Handle element for iteration over the set.
214  *
215  * @param cls the set
216  * @param mh the message
217  */
218 static void
219 handle_iter_element (void *cls, const struct GNUNET_MessageHeader *mh)
220 {
221   struct GNUNET_SET_Handle *set = cls;
222   struct GNUNET_SET_Element element;
223   const struct GNUNET_SET_IterResponseMessage *msg =
224     (const struct GNUNET_SET_IterResponseMessage *) mh;
225   struct GNUNET_SET_IterAckMessage *ack_msg;
226   struct GNUNET_MQ_Envelope *ev;
227
228   if (NULL == set->iterator)
229     return;
230
231   element.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_IterResponseMessage);
232   element.type = htons (msg->element_type);
233   element.data = &msg[1];
234   set->iterator (set->iterator_cls, &element);
235   ev = GNUNET_MQ_msg (ack_msg, GNUNET_MESSAGE_TYPE_SET_ITER_ACK);
236   ack_msg->send_more = htonl (1);
237   GNUNET_MQ_send (set->mq, ev);
238 }
239
240
241 /**
242  * Handle element for iteration over the set.
243  *
244  * @param cls the set
245  * @param mh the message
246  */
247 static void
248 handle_iter_done (void *cls, const struct GNUNET_MessageHeader *mh)
249 {
250   struct GNUNET_SET_Handle *set = cls;
251
252   if (NULL == set->iterator)
253     return;
254
255   set->iterator (set->iterator_cls, NULL);
256 }
257
258
259 /**
260  * Handle result message for a set operation.
261  *
262  * @param cls the set
263  * @param mh the message
264  */
265 static void
266 handle_result (void *cls, const struct GNUNET_MessageHeader *mh)
267 {
268   const struct GNUNET_SET_ResultMessage *msg;
269   struct GNUNET_SET_Handle *set = cls;
270   struct GNUNET_SET_OperationHandle *oh;
271   struct GNUNET_SET_Element e;
272   enum GNUNET_SET_Status result_status;
273
274   msg = (const struct GNUNET_SET_ResultMessage *) mh;
275   GNUNET_assert (NULL != set);
276   GNUNET_assert (NULL != set->mq);
277
278   result_status = ntohs (msg->result_status);
279
280   oh = GNUNET_MQ_assoc_get (set->mq, ntohl (msg->request_id));
281   GNUNET_assert (NULL != oh);
282   /* status is not STATUS_OK => there's no attached element,
283    * and this is the last result message we get */
284   if (GNUNET_SET_STATUS_OK != result_status)
285   {
286     GNUNET_MQ_assoc_remove (set->mq, ntohl (msg->request_id));
287     GNUNET_CONTAINER_DLL_remove (oh->set->ops_head, oh->set->ops_tail, oh);
288     if (GNUNET_YES == oh->set->destroy_requested)
289       GNUNET_SET_destroy (oh->set);
290     if (NULL != oh->result_cb)
291       oh->result_cb (oh->result_cls, NULL, result_status);
292     GNUNET_free (oh);
293     return;
294   }
295
296   e.data = &msg[1];
297   e.size = ntohs (mh->size) - sizeof (struct GNUNET_SET_ResultMessage);
298   e.type = msg->element_type;
299   if (NULL != oh->result_cb)
300     oh->result_cb (oh->result_cls, &e, result_status);
301 }
302
303
304 /**
305  * Handle request message for a listen operation
306  *
307  * @param cls the listen handle
308  * @param mh the message
309  */
310 static void
311 handle_request (void *cls, const struct GNUNET_MessageHeader *mh)
312 {
313   const struct GNUNET_SET_RequestMessage *msg = (const struct GNUNET_SET_RequestMessage *) mh;
314   struct GNUNET_SET_ListenHandle *lh = cls;
315   struct GNUNET_SET_Request *req;
316   struct GNUNET_MessageHeader *context_msg;
317
318   LOG (GNUNET_ERROR_TYPE_DEBUG, "processing operation request\n");
319   req = GNUNET_new (struct GNUNET_SET_Request);
320   req->accept_id = ntohl (msg->accept_id);
321   context_msg = GNUNET_MQ_extract_nested_mh (msg);
322   /* calling GNUNET_SET_accept in the listen cb will set req->accepted */
323   lh->listen_cb (lh->listen_cls, &msg->peer_id, context_msg, req);
324
325   /* we got another request => reset the backoff */
326   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
327
328   if (GNUNET_NO == req->accepted)
329   {
330     struct GNUNET_MQ_Envelope *mqm;
331     struct GNUNET_SET_AcceptRejectMessage *amsg;
332
333     mqm = GNUNET_MQ_msg (amsg, GNUNET_MESSAGE_TYPE_SET_REJECT);
334     /* no request id, as we refused */
335     amsg->request_id = htonl (0);
336     amsg->accept_reject_id = msg->accept_id;
337     GNUNET_MQ_send (lh->mq, mqm);
338     LOG (GNUNET_ERROR_TYPE_DEBUG, "rejecting request\n");
339   }
340   GNUNET_free (req);
341
342   LOG (GNUNET_ERROR_TYPE_DEBUG, "processed op request from service\n");
343
344   /* the accept-case is handled in GNUNET_SET_accept,
345    * as we have the accept message available there */
346 }
347
348
349 static void
350 handle_client_listener_error (void *cls, enum GNUNET_MQ_Error error)
351 {
352   struct GNUNET_SET_ListenHandle *lh = cls;
353
354   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "listener broke down, re-connecting\n");
355   GNUNET_CLIENT_disconnect (lh->client);
356   lh->client = NULL;
357   GNUNET_MQ_destroy (lh->mq);
358   lh->mq = NULL;
359
360   GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff, listen_connect, lh);
361   lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
362 }
363
364
365 /**
366  * Destroy the set handle if no operations are left, mark the set
367  * for destruction otherwise.
368  *
369  * @param set set handle to destroy
370  */
371 static int
372 set_destroy (struct GNUNET_SET_Handle *set)
373 {
374   if (NULL != set->ops_head)
375   {
376     set->destroy_requested = GNUNET_YES;
377     return GNUNET_NO;
378   }
379   GNUNET_CLIENT_disconnect (set->client);
380   set->client = NULL;
381   GNUNET_MQ_destroy (set->mq);
382   set->mq = NULL;
383   GNUNET_free (set);
384   return GNUNET_YES;
385 }
386
387
388
389
390 /**
391  * Cancel the given set operation.  We need to send an explicit cancel message,
392  * as all operations one one set communicate using one handle.
393  *
394  * In contrast to GNUNET_SET_operation_cancel, this function indicates whether
395  * the set of the operation has been destroyed because all operations are done and
396  * the set's destruction was requested before.
397  *
398  * @param oh set operation to cancel
399  * @return GNUNET_YES if the set of the operation was destroyed
400  */
401 static int
402 set_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
403 {
404   int ret = GNUNET_NO;
405
406   if (NULL != oh->conclude_mqm)
407     GNUNET_MQ_discard (oh->conclude_mqm);
408
409   /* is the operation already commited? */
410   if (NULL != oh->set)
411   {
412     struct GNUNET_SET_OperationHandle *h_assoc;
413     struct GNUNET_MQ_Envelope *mqm;
414
415     GNUNET_CONTAINER_DLL_remove (oh->set->ops_head, oh->set->ops_tail, oh);
416     h_assoc = GNUNET_MQ_assoc_remove (oh->set->mq, oh->request_id);
417     GNUNET_assert ((h_assoc == NULL) || (h_assoc == oh));
418     mqm = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_CANCEL);
419     GNUNET_MQ_send (oh->set->mq, mqm);
420
421     if (GNUNET_YES == oh->set->destroy_requested)
422       ret = set_destroy (oh->set);
423   }
424
425   GNUNET_free (oh);
426   
427   return ret;
428 }
429
430
431 /**
432  * Cancel the given set operation.  We need to send an explicit cancel message,
433  * as all operations one one set communicate using one handle.
434  *
435  * @param oh set operation to cancel
436  */
437 void
438 GNUNET_SET_operation_cancel (struct GNUNET_SET_OperationHandle *oh)
439 {
440   (void) set_operation_cancel (oh);
441 }
442
443
444 static void
445 handle_client_set_error (void *cls, enum GNUNET_MQ_Error error)
446 {
447   struct GNUNET_SET_Handle *set = cls;
448
449   while (NULL != set->ops_head)
450   {
451     if (NULL != set->ops_head->result_cb)
452       set->ops_head->result_cb (set->ops_head->result_cls, NULL,
453                                 GNUNET_SET_STATUS_FAILURE);
454     if (GNUNET_YES == set_operation_cancel (set->ops_head))
455       return; /* stop if the set is destroyed */
456   }
457   set->invalid = GNUNET_YES;
458 }
459
460
461 /**
462  * Create an empty set, supporting the specified operation.
463  *
464  * @param cfg configuration to use for connecting to the
465  *        set service
466  * @param op operation supported by the set
467  *        Note that the operation has to be specified
468  *        beforehand, as certain set operations need to maintain
469  *        data structures spefific to the operation
470  * @return a handle to the set
471  */
472 struct GNUNET_SET_Handle *
473 GNUNET_SET_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
474                    enum GNUNET_SET_OperationType op)
475 {
476   struct GNUNET_SET_Handle *set;
477   struct GNUNET_MQ_Envelope *mqm;
478   struct GNUNET_SET_CreateMessage *msg;
479   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
480     {handle_result, GNUNET_MESSAGE_TYPE_SET_RESULT, 0},
481     {handle_iter_element, GNUNET_MESSAGE_TYPE_SET_ITER_ELEMENT, 0},
482     {handle_iter_done, GNUNET_MESSAGE_TYPE_SET_ITER_DONE, 0},
483     GNUNET_MQ_HANDLERS_END
484   };
485
486   set = GNUNET_new (struct GNUNET_SET_Handle);
487   set->client = GNUNET_CLIENT_connect ("set", cfg);
488   LOG (GNUNET_ERROR_TYPE_DEBUG, "set client created\n");
489   GNUNET_assert (NULL != set->client);
490   set->mq = GNUNET_MQ_queue_for_connection_client (set->client, mq_handlers,
491                                                    handle_client_set_error, set);
492   GNUNET_assert (NULL != set->mq);
493   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_CREATE);
494   msg->operation = htons (op);
495   GNUNET_MQ_send (set->mq, mqm);
496   return set;
497 }
498
499
500 /**
501  * Add an element to the given set.
502  * After the element has been added (in the sense of being
503  * transmitted to the set service), cont will be called.
504  * Calls to add_element can be queued
505  *
506  * @param set set to add element to
507  * @param element element to add to the set
508  * @param cont continuation called after the element has been added
509  * @param cont_cls closure for @a cont
510  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
511  *         set is invalid (e.g. the set service crashed)
512  */
513 int
514 GNUNET_SET_add_element (struct GNUNET_SET_Handle *set,
515                         const struct GNUNET_SET_Element *element,
516                         GNUNET_SET_Continuation cont,
517                         void *cont_cls)
518 {
519   struct GNUNET_MQ_Envelope *mqm;
520   struct GNUNET_SET_ElementMessage *msg;
521
522   if (GNUNET_YES == set->invalid)
523   {
524     if (NULL != cont)
525       cont (cont_cls);
526     return GNUNET_SYSERR;
527   }
528
529   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_ADD);
530   msg->element_type = element->type;
531   memcpy (&msg[1], element->data, element->size);
532   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
533   GNUNET_MQ_send (set->mq, mqm);
534   return GNUNET_OK;
535 }
536
537
538 /**
539  * Remove an element to the given set.
540  * After the element has been removed (in the sense of the
541  * request being transmitted to the set service), cont will be called.
542  * Calls to remove_element can be queued
543  *
544  * @param set set to remove element from
545  * @param element element to remove from the set
546  * @param cont continuation called after the element has been removed
547  * @param cont_cls closure for cont
548  * @return GNUNET_OK on success, GNUNET_SYSERR if the
549  *         set is invalid (e.g. the set service crashed)
550  */
551 int
552 GNUNET_SET_remove_element (struct GNUNET_SET_Handle *set,
553                            const struct GNUNET_SET_Element *element,
554                            GNUNET_SET_Continuation cont,
555                            void *cont_cls)
556 {
557   struct GNUNET_MQ_Envelope *mqm;
558   struct GNUNET_SET_ElementMessage *msg;
559
560   if (GNUNET_YES == set->invalid)
561   {
562     if (NULL != cont)
563       cont (cont_cls);
564     return GNUNET_SYSERR;
565   }
566
567   mqm = GNUNET_MQ_msg_extra (msg, element->size, GNUNET_MESSAGE_TYPE_SET_REMOVE);
568   msg->element_type = element->type;
569   memcpy (&msg[1], element->data, element->size);
570   GNUNET_MQ_notify_sent (mqm, cont, cont_cls);
571   GNUNET_MQ_send (set->mq, mqm);
572   return GNUNET_OK;
573 }
574
575
576 /**
577  * Destroy the set handle, and free all associated resources.
578  *
579  * @param set set handle to destroy
580  */
581 void
582 GNUNET_SET_destroy (struct GNUNET_SET_Handle *set)
583 {
584   (void) set_destroy (set);
585 }
586
587
588 /**
589  * Prepare a set operation to be evaluated with another peer.
590  * The evaluation will not start until the client provides
591  * a local set with GNUNET_SET_commit.
592  *
593  * @param other_peer peer with the other set
594  * @param app_id hash for the application using the set
595  * @param context_msg additional information for the request
596  * @param salt salt used for the set operation; sometimes set operations
597  *        fail due to hash collisions, using a different salt for each operation
598  *        makes it harder for an attacker to exploit this
599  * @param result_mode specified how results will be returned,
600  *        see 'GNUNET_SET_ResultMode'.
601  * @param result_cb called on error or success
602  * @param result_cls closure for result_cb
603  * @return a handle to cancel the operation
604  */
605 struct GNUNET_SET_OperationHandle *
606 GNUNET_SET_prepare (const struct GNUNET_PeerIdentity *other_peer,
607                     const struct GNUNET_HashCode *app_id,
608                     const struct GNUNET_MessageHeader *context_msg,
609                     uint16_t salt,
610                     enum GNUNET_SET_ResultMode result_mode,
611                     GNUNET_SET_ResultIterator result_cb,
612                     void *result_cls)
613 {
614   struct GNUNET_MQ_Envelope *mqm;
615   struct GNUNET_SET_OperationHandle *oh;
616   struct GNUNET_SET_EvaluateMessage *msg;
617
618   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
619   oh->result_cb = result_cb;
620   oh->result_cls = result_cls;
621
622   mqm = GNUNET_MQ_msg_nested_mh (msg, GNUNET_MESSAGE_TYPE_SET_EVALUATE, context_msg);
623
624   msg->app_id = *app_id;
625   msg->result_mode = htons (result_mode);
626   msg->target_peer = *other_peer;
627   msg->salt = salt;
628   oh->conclude_mqm = mqm;
629   oh->request_id_addr = &msg->request_id;
630
631   return oh;
632 }
633
634
635 /**
636  * Connect to the set service in order to listen
637  * for request.
638  *
639  * @param cls the listen handle to connect
640  * @param tc task context if invoked as a task, NULL otherwise
641  */
642 static void
643 listen_connect (void *cls,
644                 const struct GNUNET_SCHEDULER_TaskContext *tc)
645 {
646   struct GNUNET_MQ_Envelope *mqm;
647   struct GNUNET_SET_ListenMessage *msg;
648   struct GNUNET_SET_ListenHandle *lh = cls;
649   static const struct GNUNET_MQ_MessageHandler mq_handlers[] = {
650     {handle_request, GNUNET_MESSAGE_TYPE_SET_REQUEST},
651     GNUNET_MQ_HANDLERS_END
652   };
653
654   GNUNET_assert (NULL == lh->client);
655   lh->client = GNUNET_CLIENT_connect ("set", lh->cfg);
656   if (NULL == lh->client)
657   {
658     LOG (GNUNET_ERROR_TYPE_ERROR,
659          "could not connect to set (wrong configuration?), giving up listening\n");
660     return;
661   }
662   GNUNET_assert (NULL == lh->mq);
663   lh->mq = GNUNET_MQ_queue_for_connection_client (lh->client, mq_handlers,
664                                                   handle_client_listener_error, lh);
665   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_LISTEN);
666   msg->operation = htonl (lh->operation);
667   msg->app_id = lh->app_id;
668   GNUNET_MQ_send (lh->mq, mqm);
669 }
670
671
672 /**
673  * Wait for set operation requests for the given application id
674  *
675  * @param cfg configuration to use for connecting to
676  *            the set service, needs to be valid for the lifetime of the listen handle
677  * @param operation operation we want to listen for
678  * @param app_id id of the application that handles set operation requests
679  * @param listen_cb called for each incoming request matching the operation
680  *                  and application id
681  * @param listen_cls handle for listen_cb
682  * @return a handle that can be used to cancel the listen operation
683  */
684 struct GNUNET_SET_ListenHandle *
685 GNUNET_SET_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
686                    enum GNUNET_SET_OperationType operation,
687                    const struct GNUNET_HashCode *app_id,
688                    GNUNET_SET_ListenCallback listen_cb,
689                    void *listen_cls)
690 {
691   struct GNUNET_SET_ListenHandle *lh;
692
693   lh = GNUNET_new (struct GNUNET_SET_ListenHandle);
694   lh->listen_cb = listen_cb;
695   lh->listen_cls = listen_cls;
696   lh->cfg = cfg;
697   lh->operation = operation;
698   lh->app_id = *app_id;
699   lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
700   listen_connect (lh, NULL);
701   return lh;
702 }
703
704
705 /**
706  * Cancel the given listen operation.
707  *
708  * @param lh handle for the listen operation
709  */
710 void
711 GNUNET_SET_listen_cancel (struct GNUNET_SET_ListenHandle *lh)
712 {
713   LOG (GNUNET_ERROR_TYPE_DEBUG, "canceling listener\n");
714   GNUNET_MQ_destroy (lh->mq);
715   GNUNET_CLIENT_disconnect (lh->client);
716   GNUNET_free (lh);
717 }
718
719
720 /**
721  * Accept a request we got via #GNUNET_SET_listen.  Must be called during
722  * #GNUNET_SET_listen, as the 'struct GNUNET_SET_Request' becomes invalid
723  * afterwards.
724  * Call #GNUNET_SET_commit to provide the local set to use for the operation,
725  * and to begin the exchange with the remote peer.
726  *
727  * @param request request to accept
728  * @param result_mode specified how results will be returned,
729  *        see 'GNUNET_SET_ResultMode'.
730  * @param result_cb callback for the results
731  * @param result_cls closure for result_cb
732  * @return a handle to cancel the operation
733  */
734 struct GNUNET_SET_OperationHandle *
735 GNUNET_SET_accept (struct GNUNET_SET_Request *request,
736                    enum GNUNET_SET_ResultMode result_mode,
737                    GNUNET_SET_ResultIterator result_cb,
738                    void *result_cls)
739 {
740   struct GNUNET_MQ_Envelope *mqm;
741   struct GNUNET_SET_OperationHandle *oh;
742   struct GNUNET_SET_AcceptRejectMessage *msg;
743
744   GNUNET_assert (NULL != request);
745   GNUNET_assert (GNUNET_NO == request->accepted);
746   request->accepted = GNUNET_YES;
747
748   oh = GNUNET_new (struct GNUNET_SET_OperationHandle);
749   oh->result_cb = result_cb;
750   oh->result_cls = result_cls;
751
752   mqm = GNUNET_MQ_msg (msg, GNUNET_MESSAGE_TYPE_SET_ACCEPT);
753   msg->accept_reject_id = htonl (request->accept_id);
754   msg->result_mode = htons (result_mode);
755
756   oh->conclude_mqm = mqm;
757   oh->request_id_addr = &msg->request_id;
758
759   return oh;
760 }
761
762
763 /**
764  * Commit a set to be used with a set operation.
765  * This function is called once we have fully constructed
766  * the set that we want to use for the operation.  At this
767  * time, the P2P protocol can then begin to exchange the
768  * set information and call the result callback with the
769  * result information.
770  *
771  * @param oh handle to the set operation
772  * @param set the set to use for the operation
773  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
774  *         set is invalid (e.g. the set service crashed)
775  */
776 int
777 GNUNET_SET_commit (struct GNUNET_SET_OperationHandle *oh,
778                    struct GNUNET_SET_Handle *set)
779 {
780   GNUNET_assert (NULL == oh->set);
781   if (GNUNET_YES == set->invalid)
782     return GNUNET_SYSERR;
783   GNUNET_assert (NULL != oh->conclude_mqm);
784   oh->set = set;
785   GNUNET_CONTAINER_DLL_insert (set->ops_head, set->ops_tail, oh);
786   oh->request_id = GNUNET_MQ_assoc_add (set->mq, oh);
787   *oh->request_id_addr = htonl (oh->request_id);
788   GNUNET_MQ_send (set->mq, oh->conclude_mqm);
789   oh->conclude_mqm = NULL;
790   oh->request_id_addr = NULL;
791   return GNUNET_OK;
792 }
793
794
795 /**
796  * Iterate over all elements in the given set.
797  * Note that this operation involves transferring every element of the set
798  * from the service to the client, and is thus costly.
799  *
800  * @param set the set to iterate over
801  * @param iter the iterator to call for each element
802  * @param cls closure for @a iter
803  * @return #GNUNET_YES if the iteration started successfuly,
804  *         #GNUNET_NO if another iteration is active
805  *         #GNUNET_SYSERR if the set is invalid (e.g. the server crashed, disconnected)
806  */
807 int
808 GNUNET_SET_iterate (struct GNUNET_SET_Handle *set, GNUNET_SET_ElementIterator iter, void *cls)
809 {
810   struct GNUNET_MQ_Envelope *ev;
811
812   GNUNET_assert (NULL != iter);
813
814   if (GNUNET_YES == set->invalid)
815     return GNUNET_SYSERR;
816   if (NULL != set->iterator)
817     return GNUNET_NO;
818
819   set->iterator = iter;
820   set->iterator_cls = cls;
821   ev = GNUNET_MQ_msg_header (GNUNET_MESSAGE_TYPE_SET_ITER_REQUEST);
822   GNUNET_MQ_send (set->mq, ev);
823   return GNUNET_YES;
824 }
825
826 /* end of set_api.c */