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