aboutsummaryrefslogtreecommitdiff
path: root/src/setu/setu_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/setu/setu_api.c')
-rw-r--r--src/setu/setu_api.c897
1 files changed, 897 insertions, 0 deletions
diff --git a/src/setu/setu_api.c b/src/setu/setu_api.c
new file mode 100644
index 000000000..dd3a4a769
--- /dev/null
+++ b/src/setu/setu_api.c
@@ -0,0 +1,897 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2016, 2020 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file set/setu_api.c
22 * @brief api for the set union 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_setu_service.h"
30#include "setu.h"
31
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "set-api", __VA_ARGS__)
34
35/**
36 * Opaque handle to a set.
37 */
38struct GNUNET_SETU_Handle
39{
40 /**
41 * Message queue for @e client.
42 */
43 struct GNUNET_MQ_Handle *mq;
44
45 /**
46 * Linked list of operations on the set.
47 */
48 struct GNUNET_SETU_OperationHandle *ops_head;
49
50 /**
51 * Linked list of operations on the set.
52 */
53 struct GNUNET_SETU_OperationHandle *ops_tail;
54
55 /**
56 * Should the set be destroyed once all operations are gone?
57 * #GNUNET_SYSERR if #GNUNET_SETU_destroy() must raise this flag,
58 * #GNUNET_YES if #GNUNET_SETU_destroy() did raise this flag.
59 */
60 int destroy_requested;
61
62 /**
63 * Has the set become invalid (e.g. service died)?
64 */
65 int invalid;
66
67};
68
69
70/**
71 * Handle for a set operation request from another peer.
72 */
73struct GNUNET_SETU_Request
74{
75 /**
76 * Id of the request, used to identify the request when
77 * accepting/rejecting it.
78 */
79 uint32_t accept_id;
80
81 /**
82 * Has the request been accepted already?
83 * #GNUNET_YES/#GNUNET_NO
84 */
85 int accepted;
86};
87
88
89/**
90 * Handle to an operation. Only known to the service after committing
91 * the handle with a set.
92 */
93struct GNUNET_SETU_OperationHandle
94{
95 /**
96 * Function to be called when we have a result,
97 * or an error.
98 */
99 GNUNET_SETU_ResultIterator result_cb;
100
101 /**
102 * Closure for @e result_cb.
103 */
104 void *result_cls;
105
106 /**
107 * Local set used for the operation,
108 * NULL if no set has been provided by conclude yet.
109 */
110 struct GNUNET_SETU_Handle *set;
111
112 /**
113 * Message sent to the server on calling conclude,
114 * NULL if conclude has been called.
115 */
116 struct GNUNET_MQ_Envelope *conclude_mqm;
117
118 /**
119 * Address of the request if in the conclude message,
120 * used to patch the request id into the message when the set is known.
121 */
122 uint32_t *request_id_addr;
123
124 /**
125 * Handles are kept in a linked list.
126 */
127 struct GNUNET_SETU_OperationHandle *prev;
128
129 /**
130 * Handles are kept in a linked list.
131 */
132 struct GNUNET_SETU_OperationHandle *next;
133
134 /**
135 * Request ID to identify the operation within the set.
136 */
137 uint32_t request_id;
138};
139
140
141/**
142 * Opaque handle to a listen operation.
143 */
144struct GNUNET_SETU_ListenHandle
145{
146 /**
147 * Message queue for the client.
148 */
149 struct GNUNET_MQ_Handle*mq;
150
151 /**
152 * Configuration handle for the listener, stored
153 * here to be able to reconnect transparently on
154 * connection failure.
155 */
156 const struct GNUNET_CONFIGURATION_Handle *cfg;
157
158 /**
159 * Function to call on a new incoming request,
160 * or on error.
161 */
162 GNUNET_SETU_ListenCallback listen_cb;
163
164 /**
165 * Closure for @e listen_cb.
166 */
167 void *listen_cls;
168
169 /**
170 * Application ID we listen for.
171 */
172 struct GNUNET_HashCode app_id;
173
174 /**
175 * Time to wait until we try to reconnect on failure.
176 */
177 struct GNUNET_TIME_Relative reconnect_backoff;
178
179 /**
180 * Task for reconnecting when the listener fails.
181 */
182 struct GNUNET_SCHEDULER_Task *reconnect_task;
183
184};
185
186
187/**
188 * Check that the given @a msg is well-formed.
189 *
190 * @param cls closure
191 * @param msg message to check
192 * @return #GNUNET_OK if message is well-formed
193 */
194static int
195check_result (void *cls,
196 const struct GNUNET_SETU_ResultMessage *msg)
197{
198 /* minimum size was already checked, everything else is OK! */
199 return GNUNET_OK;
200}
201
202
203/**
204 * Handle result message for a set operation.
205 *
206 * @param cls the set
207 * @param mh the message
208 */
209static void
210handle_result (void *cls,
211 const struct GNUNET_SETU_ResultMessage *msg)
212{
213 struct GNUNET_SETU_Handle *set = cls;
214 struct GNUNET_SETU_OperationHandle *oh;
215 struct GNUNET_SETU_Element e;
216 enum GNUNET_SETU_Status result_status;
217 int destroy_set;
218
219 GNUNET_assert (NULL != set->mq);
220 result_status = (enum GNUNET_SETU_Status) ntohs (msg->result_status);
221 LOG (GNUNET_ERROR_TYPE_DEBUG,
222 "Got result message with status %d\n",
223 result_status);
224 oh = GNUNET_MQ_assoc_get (set->mq,
225 ntohl (msg->request_id));
226 if (NULL == oh)
227 {
228 /* 'oh' can be NULL if we canceled the operation, but the service
229 did not get the cancel message yet. */
230 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
231 "Ignoring result from canceled operation\n");
232 return;
233 }
234
235 switch (result_status)
236 {
237 case GNUNET_SETU_STATUS_ADD_LOCAL:
238 case GNUNET_SETU_STATUS_ADD_REMOTE:
239 e.data = &msg[1];
240 e.size = ntohs (msg->header.size)
241 - sizeof(struct GNUNET_SETU_ResultMessage);
242 e.element_type = ntohs (msg->element_type);
243 if (NULL != oh->result_cb)
244 oh->result_cb (oh->result_cls,
245 &e,
246 GNUNET_ntohll (msg->current_size),
247 result_status);
248 return;
249 case GNUNET_SETU_STATUS_FAILURE:
250 case GNUNET_SETU_STATUS_DONE:
251 LOG (GNUNET_ERROR_TYPE_DEBUG,
252 "Treating result as final status\n");
253 GNUNET_MQ_assoc_remove (set->mq,
254 ntohl (msg->request_id));
255 GNUNET_CONTAINER_DLL_remove (set->ops_head,
256 set->ops_tail,
257 oh);
258 /* Need to do this calculation _before_ the result callback,
259 as IF the application still has a valid set handle, it
260 may trigger destruction of the set during the callback. */
261 destroy_set = (GNUNET_YES == set->destroy_requested) &&
262 (NULL == set->ops_head);
263 if (NULL != oh->result_cb)
264 {
265 oh->result_cb (oh->result_cls,
266 NULL,
267 GNUNET_ntohll (msg->current_size),
268 result_status);
269 }
270 else
271 {
272 LOG (GNUNET_ERROR_TYPE_DEBUG,
273 "No callback for final status\n");
274 }
275 if (destroy_set)
276 GNUNET_SETU_destroy (set);
277 GNUNET_free (oh);
278 return;
279 }
280}
281
282
283/**
284 * Destroy the given set operation.
285 *
286 * @param oh set operation to destroy
287 */
288static void
289set_operation_destroy (struct GNUNET_SETU_OperationHandle *oh)
290{
291 struct GNUNET_SETU_Handle *set = oh->set;
292 struct GNUNET_SETU_OperationHandle *h_assoc;
293
294 if (NULL != oh->conclude_mqm)
295 GNUNET_MQ_discard (oh->conclude_mqm);
296 /* is the operation already commited? */
297 if (NULL != set)
298 {
299 GNUNET_CONTAINER_DLL_remove (set->ops_head,
300 set->ops_tail,
301 oh);
302 h_assoc = GNUNET_MQ_assoc_remove (set->mq,
303 oh->request_id);
304 GNUNET_assert ((NULL == h_assoc) ||
305 (h_assoc == oh));
306 }
307 GNUNET_free (oh);
308}
309
310
311/**
312 * Cancel the given set operation. We need to send an explicit cancel
313 * message, as all operations one one set communicate using one
314 * handle.
315 *
316 * @param oh set operation to cancel
317 */
318void
319GNUNET_SETU_operation_cancel (struct GNUNET_SETU_OperationHandle *oh)
320{
321 struct GNUNET_SETU_Handle *set = oh->set;
322 struct GNUNET_SETU_CancelMessage *m;
323 struct GNUNET_MQ_Envelope *mqm;
324
325 LOG (GNUNET_ERROR_TYPE_DEBUG,
326 "Cancelling SET operation\n");
327 if (NULL != set)
328 {
329 mqm = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_SETU_CANCEL);
330 m->request_id = htonl (oh->request_id);
331 GNUNET_MQ_send (set->mq, mqm);
332 }
333 set_operation_destroy (oh);
334 if ((NULL != set) &&
335 (GNUNET_YES == set->destroy_requested) &&
336 (NULL == set->ops_head))
337 {
338 LOG (GNUNET_ERROR_TYPE_DEBUG,
339 "Destroying set after operation cancel\n");
340 GNUNET_SETU_destroy (set);
341 }
342}
343
344
345/**
346 * We encountered an error communicating with the set service while
347 * performing a set operation. Report to the application.
348 *
349 * @param cls the `struct GNUNET_SETU_Handle`
350 * @param error error code
351 */
352static void
353handle_client_set_error (void *cls,
354 enum GNUNET_MQ_Error error)
355{
356 struct GNUNET_SETU_Handle *set = cls;
357
358 LOG (GNUNET_ERROR_TYPE_ERROR,
359 "Handling client set error %d\n",
360 error);
361 while (NULL != set->ops_head)
362 {
363 if ((NULL != set->ops_head->result_cb) &&
364 (GNUNET_NO == set->destroy_requested))
365 set->ops_head->result_cb (set->ops_head->result_cls,
366 NULL,
367 0,
368 GNUNET_SETU_STATUS_FAILURE);
369 set_operation_destroy (set->ops_head);
370 }
371 set->invalid = GNUNET_YES;
372}
373
374
375/**
376 * Create an empty set, supporting the specified operation.
377 *
378 * @param cfg configuration to use for connecting to the
379 * set service
380 * @return a handle to the set
381 */
382struct GNUNET_SETU_Handle *
383GNUNET_SETU_create (const struct GNUNET_CONFIGURATION_Handle *cfg)
384{
385 struct GNUNET_SETU_Handle *set = GNUNET_new (struct GNUNET_SETU_Handle);
386 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
387 GNUNET_MQ_hd_var_size (result,
388 GNUNET_MESSAGE_TYPE_SETU_RESULT,
389 struct GNUNET_SETU_ResultMessage,
390 set),
391 GNUNET_MQ_handler_end ()
392 };
393 struct GNUNET_MQ_Envelope *mqm;
394 struct GNUNET_SETU_CreateMessage *create_msg;
395
396 set->mq = GNUNET_CLIENT_connect (cfg,
397 "setu",
398 mq_handlers,
399 &handle_client_set_error,
400 set);
401 if (NULL == set->mq)
402 {
403 GNUNET_free (set);
404 return NULL;
405 }
406 mqm = GNUNET_MQ_msg (create_msg,
407 GNUNET_MESSAGE_TYPE_SETU_CREATE);
408 GNUNET_MQ_send (set->mq,
409 mqm);
410 return set;
411}
412
413
414/**
415 * Add an element to the given set. After the element has been added
416 * (in the sense of being transmitted to the set service), @a cont
417 * will be called. Multiple calls to GNUNET_SETU_add_element() can be
418 * queued.
419 *
420 * @param set set to add element to
421 * @param element element to add to the set
422 * @param cb continuation called after the element has been added
423 * @param cb_cls closure for @a cb
424 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
425 * set is invalid (e.g. the set service crashed)
426 */
427int
428GNUNET_SETU_add_element (struct GNUNET_SETU_Handle *set,
429 const struct GNUNET_SETU_Element *element,
430 GNUNET_SCHEDULER_TaskCallback cb,
431 void *cb_cls)
432{
433 struct GNUNET_MQ_Envelope *mqm;
434 struct GNUNET_SETU_ElementMessage *msg;
435
436 LOG (GNUNET_ERROR_TYPE_DEBUG,
437 "adding element of type %u to set %p\n",
438 (unsigned int) element->element_type,
439 set);
440 GNUNET_assert (NULL != set);
441 if (GNUNET_YES == set->invalid)
442 {
443 if (NULL != cb)
444 cb (cb_cls);
445 return GNUNET_SYSERR;
446 }
447 mqm = GNUNET_MQ_msg_extra (msg,
448 element->size,
449 GNUNET_MESSAGE_TYPE_SETU_ADD);
450 msg->element_type = htons (element->element_type);
451 GNUNET_memcpy (&msg[1],
452 element->data,
453 element->size);
454 GNUNET_MQ_notify_sent (mqm,
455 cb,
456 cb_cls);
457 GNUNET_MQ_send (set->mq,
458 mqm);
459 return GNUNET_OK;
460}
461
462
463/**
464 * Destroy the set handle if no operations are left, mark the set
465 * for destruction otherwise.
466 *
467 * @param set set handle to destroy
468 */
469void
470GNUNET_SETU_destroy (struct GNUNET_SETU_Handle *set)
471{
472 /* destroying set while iterator is active is currently
473 not supported; we should expand the API to allow
474 clients to explicitly cancel the iteration! */
475 GNUNET_assert (NULL != set);
476 if ((NULL != set->ops_head) ||
477 (GNUNET_SYSERR == set->destroy_requested))
478 {
479 LOG (GNUNET_ERROR_TYPE_DEBUG,
480 "Set operations are pending, delaying set destruction\n");
481 set->destroy_requested = GNUNET_YES;
482 return;
483 }
484 LOG (GNUNET_ERROR_TYPE_DEBUG,
485 "Really destroying set\n");
486 if (NULL != set->mq)
487 {
488 GNUNET_MQ_destroy (set->mq);
489 set->mq = NULL;
490 }
491 GNUNET_free (set);
492}
493
494
495/**
496 * Prepare a set operation to be evaluated with another peer.
497 * The evaluation will not start until the client provides
498 * a local set with #GNUNET_SETU_commit().
499 *
500 * @param other_peer peer with the other set
501 * @param app_id hash for the application using the set
502 * @param context_msg additional information for the request
503 * @param result_cb called on error or success
504 * @param result_cls closure for @e result_cb
505 * @return a handle to cancel the operation
506 */
507struct GNUNET_SETU_OperationHandle *
508GNUNET_SETU_prepare (const struct GNUNET_PeerIdentity *other_peer,
509 const struct GNUNET_HashCode *app_id,
510 const struct GNUNET_MessageHeader *context_msg,
511 const struct GNUNET_SETU_Option options[],
512 GNUNET_SETU_ResultIterator result_cb,
513 void *result_cls)
514{
515 struct GNUNET_MQ_Envelope *mqm;
516 struct GNUNET_SETU_OperationHandle *oh;
517 struct GNUNET_SETU_EvaluateMessage *msg;
518
519 LOG (GNUNET_ERROR_TYPE_DEBUG,
520 "Client prepares set union operation\n");
521 oh = GNUNET_new (struct GNUNET_SETU_OperationHandle);
522 oh->result_cb = result_cb;
523 oh->result_cls = result_cls;
524 mqm = GNUNET_MQ_msg_nested_mh (msg,
525 GNUNET_MESSAGE_TYPE_SETU_EVALUATE,
526 context_msg);
527 msg->app_id = *app_id;
528 msg->target_peer = *other_peer;
529 for (const struct GNUNET_SETU_Option *opt = options; opt->type != 0; opt++)
530 {
531 switch (opt->type)
532 {
533 case GNUNET_SETU_OPTION_BYZANTINE:
534 msg->byzantine = GNUNET_YES;
535 msg->byzantine_lower_bound = htonl (opt->v.num);
536 break;
537 case GNUNET_SETU_OPTION_FORCE_FULL:
538 msg->force_full = GNUNET_YES;
539 break;
540 case GNUNET_SETU_OPTION_FORCE_DELTA:
541 msg->force_delta = GNUNET_YES;
542 break;
543 case GNUNET_SETU_OPTION_SYMMETRIC:
544 msg->symmetric = GNUNET_YES;
545 break;
546 default:
547 LOG (GNUNET_ERROR_TYPE_ERROR,
548 "Option with type %d not recognized\n",
549 (int) opt->type);
550 }
551 }
552 oh->conclude_mqm = mqm;
553 oh->request_id_addr = &msg->request_id;
554 return oh;
555}
556
557
558/**
559 * Connect to the set service in order to listen for requests.
560 *
561 * @param cls the `struct GNUNET_SETU_ListenHandle *` to connect
562 */
563static void
564listen_connect (void *cls);
565
566
567/**
568 * Check validity of request message for a listen operation
569 *
570 * @param cls the listen handle
571 * @param msg the message
572 * @return #GNUNET_OK if the message is well-formed
573 */
574static int
575check_request (void *cls,
576 const struct GNUNET_SETU_RequestMessage *msg)
577{
578 const struct GNUNET_MessageHeader *context_msg;
579
580 if (ntohs (msg->header.size) == sizeof(*msg))
581 return GNUNET_OK; /* no context message is OK */
582 context_msg = GNUNET_MQ_extract_nested_mh (msg);
583 if (NULL == context_msg)
584 {
585 /* malformed context message is NOT ok */
586 GNUNET_break_op (0);
587 return GNUNET_SYSERR;
588 }
589 return GNUNET_OK;
590}
591
592
593/**
594 * Handle request message for a listen operation
595 *
596 * @param cls the listen handle
597 * @param msg the message
598 */
599static void
600handle_request (void *cls,
601 const struct GNUNET_SETU_RequestMessage *msg)
602{
603 struct GNUNET_SETU_ListenHandle *lh = cls;
604 struct GNUNET_SETU_Request req;
605 const struct GNUNET_MessageHeader *context_msg;
606 struct GNUNET_MQ_Envelope *mqm;
607 struct GNUNET_SETU_RejectMessage *rmsg;
608
609 LOG (GNUNET_ERROR_TYPE_DEBUG,
610 "Processing incoming operation request with id %u\n",
611 ntohl (msg->accept_id));
612 /* we got another valid request => reset the backoff */
613 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
614 req.accept_id = ntohl (msg->accept_id);
615 req.accepted = GNUNET_NO;
616 context_msg = GNUNET_MQ_extract_nested_mh (msg);
617 /* calling #GNUNET_SETU_accept() in the listen cb will set req->accepted */
618 lh->listen_cb (lh->listen_cls,
619 &msg->peer_id,
620 context_msg,
621 &req);
622 if (GNUNET_YES == req.accepted)
623 return; /* the accept-case is handled in #GNUNET_SETU_accept() */
624 LOG (GNUNET_ERROR_TYPE_DEBUG,
625 "Rejected request %u\n",
626 ntohl (msg->accept_id));
627 mqm = GNUNET_MQ_msg (rmsg,
628 GNUNET_MESSAGE_TYPE_SETU_REJECT);
629 rmsg->accept_reject_id = msg->accept_id;
630 GNUNET_MQ_send (lh->mq,
631 mqm);
632}
633
634
635/**
636 * Our connection with the set service encountered an error,
637 * re-initialize with exponential back-off.
638 *
639 * @param cls the `struct GNUNET_SETU_ListenHandle *`
640 * @param error reason for the disconnect
641 */
642static void
643handle_client_listener_error (void *cls,
644 enum GNUNET_MQ_Error error)
645{
646 struct GNUNET_SETU_ListenHandle *lh = cls;
647
648 LOG (GNUNET_ERROR_TYPE_DEBUG,
649 "Listener broke down (%d), re-connecting\n",
650 (int) error);
651 GNUNET_MQ_destroy (lh->mq);
652 lh->mq = NULL;
653 lh->reconnect_task = GNUNET_SCHEDULER_add_delayed (lh->reconnect_backoff,
654 &listen_connect,
655 lh);
656 lh->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (lh->reconnect_backoff);
657}
658
659
660/**
661 * Connect to the set service in order to listen for requests.
662 *
663 * @param cls the `struct GNUNET_SETU_ListenHandle *` to connect
664 */
665static void
666listen_connect (void *cls)
667{
668 struct GNUNET_SETU_ListenHandle *lh = cls;
669 struct GNUNET_MQ_MessageHandler mq_handlers[] = {
670 GNUNET_MQ_hd_var_size (request,
671 GNUNET_MESSAGE_TYPE_SETU_REQUEST,
672 struct GNUNET_SETU_RequestMessage,
673 lh),
674 GNUNET_MQ_handler_end ()
675 };
676 struct GNUNET_MQ_Envelope *mqm;
677 struct GNUNET_SETU_ListenMessage *msg;
678
679 lh->reconnect_task = NULL;
680 GNUNET_assert (NULL == lh->mq);
681 lh->mq = GNUNET_CLIENT_connect (lh->cfg,
682 "setu",
683 mq_handlers,
684 &handle_client_listener_error,
685 lh);
686 if (NULL == lh->mq)
687 return;
688 mqm = GNUNET_MQ_msg (msg,
689 GNUNET_MESSAGE_TYPE_SETU_LISTEN);
690 msg->app_id = lh->app_id;
691 GNUNET_MQ_send (lh->mq,
692 mqm);
693}
694
695
696/**
697 * Wait for set operation requests for the given application id
698 *
699 * @param cfg configuration to use for connecting to
700 * the set service, needs to be valid for the lifetime of the listen handle
701 * @param app_id id of the application that handles set operation requests
702 * @param listen_cb called for each incoming request matching the operation
703 * and application id
704 * @param listen_cls handle for @a listen_cb
705 * @return a handle that can be used to cancel the listen operation
706 */
707struct GNUNET_SETU_ListenHandle *
708GNUNET_SETU_listen (const struct GNUNET_CONFIGURATION_Handle *cfg,
709 const struct GNUNET_HashCode *app_id,
710 GNUNET_SETU_ListenCallback listen_cb,
711 void *listen_cls)
712{
713 struct GNUNET_SETU_ListenHandle *lh;
714
715 LOG (GNUNET_ERROR_TYPE_DEBUG,
716 "Starting listener for app %s\n",
717 GNUNET_h2s (app_id));
718 lh = GNUNET_new (struct GNUNET_SETU_ListenHandle);
719 lh->listen_cb = listen_cb;
720 lh->listen_cls = listen_cls;
721 lh->cfg = cfg;
722 lh->app_id = *app_id;
723 lh->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
724 listen_connect (lh);
725 if (NULL == lh->mq)
726 {
727 GNUNET_free (lh);
728 return NULL;
729 }
730 return lh;
731}
732
733
734/**
735 * Cancel the given listen operation.
736 *
737 * @param lh handle for the listen operation
738 */
739void
740GNUNET_SETU_listen_cancel (struct GNUNET_SETU_ListenHandle *lh)
741{
742 LOG (GNUNET_ERROR_TYPE_DEBUG,
743 "Canceling listener %s\n",
744 GNUNET_h2s (&lh->app_id));
745 if (NULL != lh->mq)
746 {
747 GNUNET_MQ_destroy (lh->mq);
748 lh->mq = NULL;
749 }
750 if (NULL != lh->reconnect_task)
751 {
752 GNUNET_SCHEDULER_cancel (lh->reconnect_task);
753 lh->reconnect_task = NULL;
754 }
755 GNUNET_free (lh);
756}
757
758
759/**
760 * Accept a request we got via #GNUNET_SETU_listen. Must be called during
761 * #GNUNET_SETU_listen, as the 'struct GNUNET_SETU_Request' becomes invalid
762 * afterwards.
763 * Call #GNUNET_SETU_commit to provide the local set to use for the operation,
764 * and to begin the exchange with the remote peer.
765 *
766 * @param request request to accept
767 * @param result_mode specified how results will be returned,
768 * see `enum GNUNET_SETU_ResultMode`.
769 * @param result_cb callback for the results
770 * @param result_cls closure for @a result_cb
771 * @return a handle to cancel the operation
772 */
773struct GNUNET_SETU_OperationHandle *
774GNUNET_SETU_accept (struct GNUNET_SETU_Request *request,
775 const struct GNUNET_SETU_Option options[],
776 GNUNET_SETU_ResultIterator result_cb,
777 void *result_cls)
778{
779 struct GNUNET_MQ_Envelope *mqm;
780 struct GNUNET_SETU_OperationHandle *oh;
781 struct GNUNET_SETU_AcceptMessage *msg;
782
783 GNUNET_assert (GNUNET_NO == request->accepted);
784 LOG (GNUNET_ERROR_TYPE_DEBUG,
785 "Client accepts set union operation with id %u\n",
786 request->accept_id);
787 request->accepted = GNUNET_YES;
788 mqm = GNUNET_MQ_msg (msg,
789 GNUNET_MESSAGE_TYPE_SETU_ACCEPT);
790 msg->accept_reject_id = htonl (request->accept_id);
791 for (const struct GNUNET_SETU_Option *opt = options; opt->type != 0; opt++)
792 {
793 switch (opt->type)
794 {
795 case GNUNET_SETU_OPTION_BYZANTINE:
796 msg->byzantine = GNUNET_YES;
797 msg->byzantine_lower_bound = htonl (opt->v.num);
798 break;
799 case GNUNET_SETU_OPTION_FORCE_FULL:
800 msg->force_full = GNUNET_YES;
801 break;
802 case GNUNET_SETU_OPTION_FORCE_DELTA:
803 msg->force_delta = GNUNET_YES;
804 break;
805 case GNUNET_SETU_OPTION_SYMMETRIC:
806 msg->symmetric = GNUNET_YES;
807 break;
808 default:
809 LOG (GNUNET_ERROR_TYPE_ERROR,
810 "Option with type %d not recognized\n",
811 (int) opt->type);
812 }
813 }
814 oh = GNUNET_new (struct GNUNET_SETU_OperationHandle);
815 oh->result_cb = result_cb;
816 oh->result_cls = result_cls;
817 oh->conclude_mqm = mqm;
818 oh->request_id_addr = &msg->request_id;
819 return oh;
820}
821
822
823/**
824 * Commit a set to be used with a set operation.
825 * This function is called once we have fully constructed
826 * the set that we want to use for the operation. At this
827 * time, the P2P protocol can then begin to exchange the
828 * set information and call the result callback with the
829 * result information.
830 *
831 * @param oh handle to the set operation
832 * @param set the set to use for the operation
833 * @return #GNUNET_OK on success, #GNUNET_SYSERR if the
834 * set is invalid (e.g. the set service crashed)
835 */
836int
837GNUNET_SETU_commit (struct GNUNET_SETU_OperationHandle *oh,
838 struct GNUNET_SETU_Handle *set)
839{
840 if (NULL != oh->set)
841 {
842 /* Some other set was already committed for this
843 * operation, there is a logic bug in the client of this API */
844 GNUNET_break (0);
845 return GNUNET_OK;
846 }
847 GNUNET_assert (NULL != set);
848 if (GNUNET_YES == set->invalid)
849 return GNUNET_SYSERR;
850 LOG (GNUNET_ERROR_TYPE_DEBUG,
851 "Client commits to SET\n");
852 GNUNET_assert (NULL != oh->conclude_mqm);
853 oh->set = set;
854 GNUNET_CONTAINER_DLL_insert (set->ops_head,
855 set->ops_tail,
856 oh);
857 oh->request_id = GNUNET_MQ_assoc_add (set->mq,
858 oh);
859 *oh->request_id_addr = htonl (oh->request_id);
860 GNUNET_MQ_send (set->mq,
861 oh->conclude_mqm);
862 oh->conclude_mqm = NULL;
863 oh->request_id_addr = NULL;
864 return GNUNET_OK;
865}
866
867
868/**
869 * Hash a set element.
870 *
871 * @param element the element that should be hashed
872 * @param[out] ret_hash a pointer to where the hash of @a element
873 * should be stored
874 */
875void
876GNUNET_SETU_element_hash (const struct GNUNET_SETU_Element *element,
877 struct GNUNET_HashCode *ret_hash)
878{
879 struct GNUNET_HashContext *ctx = GNUNET_CRYPTO_hash_context_start ();
880
881 /* It's not guaranteed that the element data is always after the element header,
882 so we need to hash the chunks separately. */
883 GNUNET_CRYPTO_hash_context_read (ctx,
884 &element->size,
885 sizeof(uint16_t));
886 GNUNET_CRYPTO_hash_context_read (ctx,
887 &element->element_type,
888 sizeof(uint16_t));
889 GNUNET_CRYPTO_hash_context_read (ctx,
890 element->data,
891 element->size);
892 GNUNET_CRYPTO_hash_context_finish (ctx,
893 ret_hash);
894}
895
896
897/* end of setu_api.c */