psycstore: add option to perform membership test when retrieving fragment or message
[oweals/gnunet.git] / src / psycstore / psycstore_api.c
1 /*
2  * This file is part of GNUnet
3  * (C) 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 psycstore/psycstore_api.c
23  * @brief API to interact with the PSYCstore service
24  * @author Gabor X Toth
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #include "gnunet_util_lib.h"
30 #include "gnunet_constants.h"
31 #include "gnunet_protocols.h"
32 #include "gnunet_psycstore_service.h"
33 #include "gnunet_multicast_service.h"
34 #include "psycstore.h"
35
36 #define LOG(kind,...) GNUNET_log_from (kind, "psycstore-api",__VA_ARGS__)
37
38 typedef void (*DataCallback) ();
39
40 /**
41  * Handle for an operation with the PSYCstore service.
42  */
43 struct GNUNET_PSYCSTORE_OperationHandle
44 {
45
46   /**
47    * Main PSYCstore handle.
48    */
49   struct GNUNET_PSYCSTORE_Handle *h;
50
51   /**
52    * We keep operations in a DLL.
53    */
54   struct GNUNET_PSYCSTORE_OperationHandle *next;
55
56   /**
57    * We keep operations in a DLL.
58    */
59   struct GNUNET_PSYCSTORE_OperationHandle *prev;
60
61   /**
62    * Continuation to invoke with the result of an operation.
63    */
64   GNUNET_PSYCSTORE_ResultCallback res_cb;
65
66   /**
67    * Continuation to invoke with the result of an operation returning data.
68    */
69   DataCallback data_cb;
70
71   /**
72    * Closure for the callbacks.
73    */
74   void *cls;
75
76   /**
77    * Operation ID.
78    */
79   uint32_t op_id;
80
81   /**
82    * Message to send to the PSYCstore service.
83    * Allocated at the end of this struct.
84    */
85   const struct GNUNET_MessageHeader *msg;
86 };
87
88
89 /**
90  * Handle for the service.
91  */
92 struct GNUNET_PSYCSTORE_Handle
93 {
94   /**
95    * Configuration to use.
96    */
97   const struct GNUNET_CONFIGURATION_Handle *cfg;
98
99   /**
100    * Socket (if available).
101    */
102   struct GNUNET_CLIENT_Connection *client;
103
104   /**
105    * Head of operations to transmit.
106    */
107   struct GNUNET_PSYCSTORE_OperationHandle *transmit_head;
108
109   /**
110    * Tail of operations to transmit.
111    */
112   struct GNUNET_PSYCSTORE_OperationHandle *transmit_tail;
113
114   /**
115    * Head of active operations waiting for response.
116    */
117   struct GNUNET_PSYCSTORE_OperationHandle *op_head;
118
119   /**
120    * Tail of active operations waiting for response.
121    */
122   struct GNUNET_PSYCSTORE_OperationHandle *op_tail;
123
124   /**
125    * Currently pending transmission request, or NULL for none.
126    */
127   struct GNUNET_CLIENT_TransmitHandle *th;
128
129   /**
130    * Task doing exponential back-off trying to reconnect.
131    */
132   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
133
134   /**
135    * Time for next connect retry.
136    */
137   struct GNUNET_TIME_Relative reconnect_delay;
138
139   /**
140    * Are we polling for incoming messages right now?
141    */
142   int in_receive;
143
144   /**
145    * The last operation id used for a PSYCstore operation.
146    */
147   uint32_t last_op_id_used;
148
149 };
150
151
152 /**
153  * Get a fresh operation ID to distinguish between PSYCstore requests.
154  *
155  * @param h Handle to the PSYCstore service.
156  * @return next operation id to use
157  */
158 static uint32_t
159 get_next_op_id (struct GNUNET_PSYCSTORE_Handle *h)
160 {
161   return h->last_op_id_used++;
162 }
163
164
165 /**
166  * Find operation by ID.
167  *
168  * @return OperationHandle if found, or NULL otherwise.
169  */
170 static struct GNUNET_PSYCSTORE_OperationHandle *
171 find_op_by_id (struct GNUNET_PSYCSTORE_Handle *h, uint32_t op_id)
172 {
173   struct GNUNET_PSYCSTORE_OperationHandle *op = h->op_head;
174   while (NULL != op)
175   {
176     if (op->op_id == op_id)
177       return op;
178     op = op->next;
179   }
180   return NULL;
181 }
182
183
184 /**
185  * Try again to connect to the PSYCstore service.
186  *
187  * @param cls handle to the PSYCstore service.
188  * @param tc scheduler context
189  */
190 static void
191 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
192
193
194 /**
195  * Reschedule a connect attempt to the service.
196  *
197  * @param h transport service to reconnect
198  */
199 static void
200 reschedule_connect (struct GNUNET_PSYCSTORE_Handle *h)
201 {
202   GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
203
204   if (NULL != h->th)
205   {
206     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
207     h->th = NULL;
208   }
209   if (NULL != h->client)
210   {
211     GNUNET_CLIENT_disconnect (h->client);
212     h->client = NULL;
213   }
214   h->in_receive = GNUNET_NO;
215   LOG (GNUNET_ERROR_TYPE_DEBUG,
216        "Scheduling task to reconnect to PSYCstore service in %s.\n",
217        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
218   h->reconnect_task =
219       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
220   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
221 }
222
223
224 /**
225  * Schedule transmission of the next message from our queue.
226  *
227  * @param h PSYCstore handle
228  */
229 static void
230 transmit_next (struct GNUNET_PSYCSTORE_Handle *h);
231
232
233 /**
234  * Type of a function to call when we receive a message
235  * from the service.
236  *
237  * @param cls closure
238  * @param msg message received, NULL on timeout or fatal error
239  */
240 static void
241 message_handler (void *cls, const struct GNUNET_MessageHeader *msg)
242 {
243   struct GNUNET_PSYCSTORE_Handle *h = cls;
244   struct GNUNET_PSYCSTORE_OperationHandle *op;
245   const struct OperationResult *opres;
246   const struct CountersResult *cres;
247   const struct FragmentResult *fres;
248   const struct StateResult *sres;
249   const char *str;
250
251   if (NULL == msg)
252   {
253     reschedule_connect (h);
254     return;
255   }
256   LOG (GNUNET_ERROR_TYPE_DEBUG,
257        "Received message of type %d from PSYCstore service.\n",
258        ntohs (msg->type));
259   uint16_t size = ntohs (msg->size);
260   uint16_t type = ntohs (msg->type);
261   switch (type)
262   {
263   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE:
264     if (size < sizeof (struct OperationResult))
265     {
266       LOG (GNUNET_ERROR_TYPE_ERROR,
267            "Received message of type %d with length %lu bytes. "
268            "Expected >= %lu\n",
269            type, size, sizeof (struct OperationResult));
270       GNUNET_break (0);
271       reschedule_connect (h);
272       return;
273     }
274
275     opres = (const struct OperationResult *) msg;
276     str = (const char *) &opres[1];
277     if ( (size > sizeof (struct OperationResult)) &&
278          ('\0' != str[size - sizeof (struct OperationResult) - 1]) )
279     {
280       GNUNET_break (0);
281       reschedule_connect (h);
282       return;
283     }
284     if (size == sizeof (struct OperationResult))
285       str = NULL;
286
287     op = find_op_by_id (h, ntohl (opres->op_id));
288     if (NULL == op)
289     {
290       LOG (GNUNET_ERROR_TYPE_DEBUG,
291            "No callback registered for operation with ID %ld.\n",
292            type, ntohl (opres->op_id));
293     }
294     else
295     {
296       LOG (GNUNET_ERROR_TYPE_DEBUG,
297            "Received result message (type %d) with operation ID: %ld\n",
298            type, op->op_id);
299
300       GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
301       if (NULL != op->res_cb)
302       {
303         const struct StateModifyRequest *smreq;
304         const struct StateSyncRequest *ssreq;
305         switch (ntohs (op->msg->type))
306         {
307         case GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY:
308           smreq = (const struct StateModifyRequest *) op->msg;
309           if (!(smreq->flags & STATE_OP_LAST
310                 || GNUNET_OK != ntohl (opres->result_code)))
311             op->res_cb = NULL;
312           break;
313         case GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC:
314           ssreq = (const struct StateSyncRequest *) op->msg;
315           if (!(ssreq->flags & STATE_OP_LAST
316                 || GNUNET_OK != ntohl (opres->result_code)))
317             op->res_cb = NULL;
318           break;
319         }
320       }
321       if (NULL != op->res_cb)
322         op->res_cb (op->cls, ntohl (opres->result_code), str);
323       GNUNET_free (op);
324     }
325     break;
326
327   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS:
328     if (size != sizeof (struct CountersResult))
329     {
330       LOG (GNUNET_ERROR_TYPE_ERROR,
331            "Received message of type %d with length %lu bytes. "
332            "Expected %lu\n",
333            type, size, sizeof (struct CountersResult));
334       GNUNET_break (0);
335       reschedule_connect (h);
336       return;
337     }
338
339     cres = (const struct CountersResult *) msg;
340
341     op = find_op_by_id (h, ntohl (cres->op_id));
342     if (NULL == op)
343     {
344       LOG (GNUNET_ERROR_TYPE_DEBUG,
345            "No callback registered for operation with ID %ld.\n",
346            type, ntohl (cres->op_id));
347     }
348     else
349     {
350       GNUNET_CONTAINER_DLL_remove (h->op_head, h->op_tail, op);
351       if (NULL != op->data_cb)
352         ((GNUNET_PSYCSTORE_CountersCallback)
353          op->data_cb) (op->cls, ntohl (cres->result_code),
354                        GNUNET_ntohll (cres->max_fragment_id),
355                        GNUNET_ntohll (cres->max_message_id),
356                        GNUNET_ntohll (cres->max_group_generation),
357                        GNUNET_ntohll (cres->max_state_message_id));
358       GNUNET_free (op);
359     }
360     break;
361
362   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT:
363     if (size < sizeof (struct FragmentResult))
364     {
365       LOG (GNUNET_ERROR_TYPE_ERROR,
366            "Received message of type %d with length %lu bytes. "
367            "Expected >= %lu\n",
368            type, size, sizeof (struct FragmentResult));
369       GNUNET_break (0);
370       reschedule_connect (h);
371       return;
372     }
373
374     fres = (const struct FragmentResult *) msg;
375     struct GNUNET_MULTICAST_MessageHeader *mmsg =
376       (struct GNUNET_MULTICAST_MessageHeader *) &fres[1];
377     if (size != sizeof (struct FragmentResult) + ntohs (mmsg->header.size))
378     {
379       LOG (GNUNET_ERROR_TYPE_ERROR,
380            "Received message of type %d with length %lu bytes. "
381            "Expected = %lu\n",
382            type, size,
383            sizeof (struct FragmentResult) + ntohs (mmsg->header.size));
384       GNUNET_break (0);
385       reschedule_connect (h);
386       return;
387     }
388
389     op = find_op_by_id (h, ntohl (fres->op_id));
390     if (NULL == op)
391     {
392       LOG (GNUNET_ERROR_TYPE_DEBUG,
393            "No callback registered for operation with ID %ld.\n",
394            type, ntohl (fres->op_id));
395     }
396     else
397     {
398       if (NULL != op->data_cb)
399         ((GNUNET_PSYCSTORE_FragmentCallback)
400          op->data_cb) (op->cls, mmsg, ntohl (fres->psycstore_flags));
401     }
402     break;
403
404   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE:
405     if (size < sizeof (struct StateResult))
406     {
407       LOG (GNUNET_ERROR_TYPE_ERROR,
408            "Received message of type %d with length %lu bytes. "
409            "Expected >= %lu\n",
410            type, size, sizeof (struct StateResult));
411       GNUNET_break (0);
412       reschedule_connect (h);
413       return;
414     }
415
416     sres = (const struct StateResult *) msg;
417     const char *name = (const char *) &sres[1];
418     uint16_t name_size = ntohs (sres->name_size);
419
420     if (name_size <= 2 || '\0' != name[name_size - 1])
421     {
422       LOG (GNUNET_ERROR_TYPE_ERROR,
423            "Received state result message (type %d) with invalid name.\n",
424            type);
425       GNUNET_break (0);
426       reschedule_connect (h);
427       return;
428     }
429
430     op = find_op_by_id (h, ntohl (sres->op_id));
431     if (NULL == op)
432     {
433       LOG (GNUNET_ERROR_TYPE_DEBUG,
434            "No callback registered for operation with ID %ld.\n",
435            type, ntohl (sres->op_id));
436     }
437     else
438     {
439       if (NULL != op->data_cb)
440         ((GNUNET_PSYCSTORE_StateCallback)
441          op->data_cb) (op->cls, name, (char *) &sres[1] + name_size,
442                        ntohs (sres->header.size) - sizeof (*sres) - name_size);
443     }
444     break;
445
446   default:
447     GNUNET_break (0);
448     reschedule_connect (h);
449     return;
450   }
451
452   GNUNET_CLIENT_receive (h->client, &message_handler, h,
453                          GNUNET_TIME_UNIT_FOREVER_REL);
454 }
455
456
457 /**
458  * Transmit next message to service.
459  *
460  * @param cls The 'struct GNUNET_PSYCSTORE_Handle'.
461  * @param size Number of bytes available in buf.
462  * @param buf Where to copy the message.
463  * @return Number of bytes copied to buf.
464  */
465 static size_t
466 send_next_message (void *cls, size_t size, void *buf)
467 {
468   struct GNUNET_PSYCSTORE_Handle *h = cls;
469   struct GNUNET_PSYCSTORE_OperationHandle *op = h->transmit_head;
470   size_t ret;
471
472   h->th = NULL;
473   if (NULL == op)
474     return 0;
475   ret = ntohs (op->msg->size);
476   if (ret > size)
477   {
478     reschedule_connect (h);
479     return 0;
480   }
481   LOG (GNUNET_ERROR_TYPE_DEBUG,
482        "Sending message of type %d to PSYCstore service. ID: %u\n",
483        ntohs (op->msg->type), op->op_id);
484   memcpy (buf, op->msg, ret);
485
486   GNUNET_CONTAINER_DLL_remove (h->transmit_head, h->transmit_tail, op);
487
488   if (NULL == op->res_cb && NULL == op->data_cb)
489   {
490     GNUNET_free (op);
491   }
492   else
493   {
494     GNUNET_CONTAINER_DLL_insert_tail (h->op_head, h->op_tail, op);
495   }
496
497   if (NULL != h->transmit_head)
498     transmit_next (h);
499
500   if (GNUNET_NO == h->in_receive)
501   {
502     h->in_receive = GNUNET_YES;
503     GNUNET_CLIENT_receive (h->client, &message_handler, h,
504                            GNUNET_TIME_UNIT_FOREVER_REL);
505   }
506   return ret;
507 }
508
509
510 /**
511  * Schedule transmission of the next message from our queue.
512  *
513  * @param h PSYCstore handle.
514  */
515 static void
516 transmit_next (struct GNUNET_PSYCSTORE_Handle *h)
517 {
518   if (NULL != h->th || NULL == h->client)
519     return;
520
521   struct GNUNET_PSYCSTORE_OperationHandle *op = h->transmit_head;
522   if (NULL == op)
523     return;
524
525   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
526                                                ntohs (op->msg->size),
527                                                GNUNET_TIME_UNIT_FOREVER_REL,
528                                                GNUNET_NO,
529                                                &send_next_message,
530                                                h);
531 }
532
533
534 /**
535  * Try again to connect to the PSYCstore service.
536  *
537  * @param cls Handle to the PSYCstore service.
538  * @param tc Scheduler context.
539  */
540 static void
541 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
542 {
543   struct GNUNET_PSYCSTORE_Handle *h = cls;
544
545   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
546   LOG (GNUNET_ERROR_TYPE_DEBUG,
547        "Connecting to PSYCstore service.\n");
548   GNUNET_assert (NULL == h->client);
549   h->client = GNUNET_CLIENT_connect ("psycstore", h->cfg);
550   GNUNET_assert (NULL != h->client);
551   transmit_next (h);
552 }
553
554
555 /**
556  * Connect to the PSYCstore service.
557  *
558  * @param cfg The configuration to use
559  * @return Handle to use
560  */
561 struct GNUNET_PSYCSTORE_Handle *
562 GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
563 {
564   struct GNUNET_PSYCSTORE_Handle *h
565     = GNUNET_new (struct GNUNET_PSYCSTORE_Handle);
566   h->cfg = cfg;
567   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
568   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
569   return h;
570 }
571
572
573 /**
574  * Disconnect from PSYCstore service
575  *
576  * @param h Handle to destroy
577  */
578 void
579 GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h)
580 {
581   GNUNET_assert (NULL != h);
582   if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
583   {
584     GNUNET_SCHEDULER_cancel (h->reconnect_task);
585     h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
586   }
587   if (NULL != h->th)
588   {
589     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
590     h->th = NULL;
591   }
592   if (NULL != h->client)
593   {
594     GNUNET_CLIENT_disconnect (h->client);
595     h->client = NULL;
596   }
597   GNUNET_free (h);
598 }
599
600
601 /**
602  * Cancel a PSYCstore operation. Note that the operation MAY still
603  * be executed; this merely cancels the continuation; if the request
604  * was already transmitted, the service may still choose to complete
605  * the operation.
606  *
607  * @param op Operation to cancel.
608  */
609 void
610 GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
611 {
612   struct GNUNET_PSYCSTORE_Handle *h = op->h;
613
614   if (h->transmit_head != NULL && (h->transmit_head != op || NULL == h->client))
615   {
616     /* request not active, can simply remove */
617     GNUNET_CONTAINER_DLL_remove (h->transmit_head, h->transmit_tail, op);
618     GNUNET_free (op);
619     return;
620   }
621   if (NULL != h->th)
622   {
623     /* request active but not yet with service, can still abort */
624     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
625     h->th = NULL;
626     GNUNET_CONTAINER_DLL_remove (h->transmit_head, h->transmit_tail, op);
627     GNUNET_free (op);
628     transmit_next (h);
629     return;
630   }
631   /* request active with service, simply ensure continuations are not called */
632   op->res_cb = NULL;
633   op->data_cb = NULL;
634 }
635
636
637 /**
638  * Store join/leave events for a PSYC channel in order to be able to answer
639  * membership test queries later.
640  *
641  * @param h Handle for the PSYCstore.
642  * @param channel_key The channel where the event happened.
643  * @param slave_key Public key of joining/leaving slave.
644  * @param did_join #GNUNET_YES on join, #GNUNET_NO on part.
645  * @param announced_at ID of the message that announced the membership change.
646  * @param effective_since Message ID this membership change is in effect since.
647  *        For joins it is <= announced_at, for parts it is always 0.
648  * @param group_generation In case of a part, the last group generation the
649  *        slave has access to.  It has relevance when a larger message have
650  *        fragments with different group generations.
651  * @param rcb Callback to call with the result of the storage operation.
652  * @param rcb_cls Closure for the callback.
653  *
654  * @return Operation handle that can be used to cancel the operation.
655  */
656 struct GNUNET_PSYCSTORE_OperationHandle *
657 GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
658                                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
659                                    const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
660                                    int did_join,
661                                    uint64_t announced_at,
662                                    uint64_t effective_since,
663                                    uint64_t group_generation,
664                                    GNUNET_PSYCSTORE_ResultCallback rcb,
665                                    void *rcb_cls)
666 {
667   GNUNET_assert (NULL != h);
668   GNUNET_assert (NULL != channel_key);
669   GNUNET_assert (NULL != slave_key);
670   GNUNET_assert (did_join
671                  ? effective_since <= announced_at
672                  : effective_since == 0);
673
674   struct MembershipStoreRequest *req;
675   struct GNUNET_PSYCSTORE_OperationHandle *op
676     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
677   op->h = h;
678   op->res_cb = rcb;
679   op->cls = rcb_cls;
680
681   req = (struct MembershipStoreRequest *) &op[1];
682   op->msg = (struct GNUNET_MessageHeader *) req;
683   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE);
684   req->header.size = htons (sizeof (*req));
685   req->channel_key = *channel_key;
686   req->slave_key = *slave_key;
687   req->did_join = htonl (did_join);
688   req->announced_at = GNUNET_htonll (announced_at);
689   req->effective_since = GNUNET_htonll (effective_since);
690   req->group_generation = GNUNET_htonll (group_generation);
691
692   op->op_id = get_next_op_id (h);
693   req->op_id = htonl (op->op_id);
694
695   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
696   transmit_next (h);
697
698   return op;
699 }
700
701
702 /**
703  * Test if a member was admitted to the channel at the given message ID.
704  *
705  * This is useful when relaying and replaying messages to check if a particular
706  * slave has access to the message fragment with a given group generation.  It
707  * is also used when handling join requests to determine whether the slave is
708  * currently admitted to the channel.
709  *
710  * @param h Handle for the PSYCstore.
711  * @param channel_key The channel we are interested in.
712  * @param slave_key Public key of slave whose membership to check.
713  * @param message_id Message ID for which to do the membership test.
714  * @param group_generation Group generation of the fragment of the message to
715  *        test.  It has relevance if the message consists of multiple fragments
716  *        with different group generations.
717  * @param rcb Callback to call with the test result.
718  * @param rcb_cls Closure for the callback.
719  *
720  * @return Operation handle that can be used to cancel the operation.
721  */
722 struct GNUNET_PSYCSTORE_OperationHandle *
723 GNUNET_PSYCSTORE_membership_test (struct GNUNET_PSYCSTORE_Handle *h,
724                                   const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
725                                   const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
726                                   uint64_t message_id,
727                                   uint64_t group_generation,
728                                   GNUNET_PSYCSTORE_ResultCallback rcb,
729                                   void *rcb_cls)
730 {
731   struct MembershipTestRequest *req;
732   struct GNUNET_PSYCSTORE_OperationHandle *op
733     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
734   op->h = h;
735   op->res_cb = rcb;
736   op->cls = rcb_cls;
737
738   req = (struct MembershipTestRequest *) &op[1];
739   op->msg = (struct GNUNET_MessageHeader *) req;
740   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST);
741   req->header.size = htons (sizeof (*req));
742   req->channel_key = *channel_key;
743   req->slave_key = *slave_key;
744   req->message_id = GNUNET_htonll (message_id);
745   req->group_generation = GNUNET_htonll (group_generation);
746
747   op->op_id = get_next_op_id (h);
748   req->op_id = htonl (op->op_id);
749
750   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
751   transmit_next (h);
752
753   return op;
754 }
755
756
757 /**
758  * Store a message fragment sent to a channel.
759  *
760  * @param h Handle for the PSYCstore.
761  * @param channel_key The channel the message belongs to.
762  * @param message Message to store.
763  * @param psycstore_flags Flags indicating whether the PSYC message contains
764  *        state modifiers.
765  * @param rcb Callback to call with the result of the operation.
766  * @param rcb_cls Closure for the callback.
767  *
768  * @return Handle that can be used to cancel the operation.
769  */
770 struct GNUNET_PSYCSTORE_OperationHandle *
771 GNUNET_PSYCSTORE_fragment_store (struct GNUNET_PSYCSTORE_Handle *h,
772                                  const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
773                                  const struct GNUNET_MULTICAST_MessageHeader *msg,
774                                  enum GNUNET_PSYCSTORE_MessageFlags psycstore_flags,
775                                  GNUNET_PSYCSTORE_ResultCallback rcb,
776                                  void *rcb_cls)
777 {
778   uint16_t size = ntohs (msg->header.size);
779   struct FragmentStoreRequest *req;
780   struct GNUNET_PSYCSTORE_OperationHandle *op
781     = GNUNET_malloc (sizeof (*op) + sizeof (*req) + size);
782   op->h = h;
783   op->res_cb = rcb;
784   op->cls = rcb_cls;
785
786   req = (struct FragmentStoreRequest *) &op[1];
787   op->msg = (struct GNUNET_MessageHeader *) req;
788   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE);
789   req->header.size = htons (sizeof (*req) + size);
790   req->channel_key = *channel_key;
791   req->psycstore_flags = htonl (psycstore_flags);
792   memcpy (&req[1], msg, size);
793
794   op->op_id = get_next_op_id (h);
795   req->op_id = htonl (op->op_id);
796
797   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
798   transmit_next (h);
799
800   return op;
801 }
802
803
804 /**
805  * Retrieve a message fragment by fragment ID.
806  *
807  * @param h
808  *        Handle for the PSYCstore.
809  * @param channel_key
810  *        The channel we are interested in.
811  * @param slave_key
812  *        The slave requesting the fragment.  If not NULL, a membership test is
813  *        performed first and the fragment is only returned if the slave has
814  *        access to it.
815  * @param fragment_id
816  *        Fragment ID to retrieve.  Use 0 to get the latest message fragment.
817  * @param fcb
818  *        Callback to call with the retrieved fragments.
819  * @param rcb
820  *        Callback to call with the result of the operation.
821  * @param cls
822  *        Closure for the callbacks.
823  *
824  * @return Handle that can be used to cancel the operation.
825  */
826 struct GNUNET_PSYCSTORE_OperationHandle *
827 GNUNET_PSYCSTORE_fragment_get (struct GNUNET_PSYCSTORE_Handle *h,
828                                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
829                                const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
830                                uint64_t fragment_id,
831                                GNUNET_PSYCSTORE_FragmentCallback fcb,
832                                GNUNET_PSYCSTORE_ResultCallback rcb,
833                                void *cls)
834 {
835   struct FragmentGetRequest *req;
836   struct GNUNET_PSYCSTORE_OperationHandle *op
837     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
838   op->h = h;
839   op->data_cb = (DataCallback) fcb;
840   op->res_cb = rcb;
841   op->cls = cls;
842
843   req = (struct FragmentGetRequest *) &op[1];
844   op->msg = (struct GNUNET_MessageHeader *) req;
845   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET);
846   req->header.size = htons (sizeof (*req));
847   req->channel_key = *channel_key;
848   req->fragment_id = GNUNET_htonll (fragment_id);
849   if (NULL != slave_key)
850   {
851     req->slave_key = *slave_key;
852     req->do_membership_test = GNUNET_YES;
853   }
854
855   op->op_id = get_next_op_id (h);
856   req->op_id = htonl (op->op_id);
857
858   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
859   transmit_next (h);
860
861   return op;
862 }
863
864
865 /**
866  * Retrieve all fragments of a message.
867  *
868  * @param h
869  *        Handle for the PSYCstore.
870  * @param channel_key
871  *        The channel we are interested in.
872  * @param slave_key
873  *        The slave requesting the message.  If not NULL, a membership test is
874  *        performed first and the message is only returned if the slave has
875  *        access to it.
876  * @param message_id
877  *        Message ID to retrieve.  Use 0 to get the latest message.
878  * @param fcb
879  *        Callback to call with the retrieved fragments.
880  * @param rcb
881  *        Callback to call with the result of the operation.
882  * @param cls
883  *        Closure for the callbacks.
884  *
885  * @return Handle that can be used to cancel the operation.
886  */
887 struct GNUNET_PSYCSTORE_OperationHandle *
888 GNUNET_PSYCSTORE_message_get (struct GNUNET_PSYCSTORE_Handle *h,
889                               const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
890                               const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
891                               uint64_t message_id,
892                               GNUNET_PSYCSTORE_FragmentCallback fcb,
893                               GNUNET_PSYCSTORE_ResultCallback rcb,
894                               void *cls)
895 {
896   struct MessageGetRequest *req;
897   struct GNUNET_PSYCSTORE_OperationHandle *op
898     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
899   op->h = h;
900   op->data_cb = (DataCallback) fcb;
901   op->res_cb = rcb;
902   op->cls = cls;
903
904   req = (struct MessageGetRequest *) &op[1];
905   op->msg = (struct GNUNET_MessageHeader *) req;
906   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET);
907   req->header.size = htons (sizeof (*req));
908   req->channel_key = *channel_key;
909   req->message_id = GNUNET_htonll (message_id);
910   if (NULL != slave_key)
911   {
912     req->slave_key = *slave_key;
913     req->do_membership_test = GNUNET_YES;
914   }
915
916   op->op_id = get_next_op_id (h);
917   req->op_id = htonl (op->op_id);
918
919   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
920   transmit_next (h);
921
922   return op;
923 }
924
925
926 /**
927  * Retrieve a fragment of message specified by its message ID and fragment
928  * offset.
929  *
930  * @param h
931  *        Handle for the PSYCstore.
932  * @param channel_key
933  *        The channel we are interested in.
934  * @param slave_key
935  *        The slave requesting the message fragment.  If not NULL, a membership
936  *        test is performed first and the message fragment is only returned
937  *        if the slave has access to it.
938  * @param message_id
939  *        Message ID to retrieve.  Use 0 to get the latest message.
940  * @param fragment_offset
941  *        Offset of the fragment to retrieve.
942  * @param fcb
943  *        Callback to call with the retrieved fragments.
944  * @param rcb
945  *        Callback to call with the result of the operation.
946  * @param cls
947  *        Closure for the callbacks.
948  *
949  * @return Handle that can be used to cancel the operation.
950  */
951 struct GNUNET_PSYCSTORE_OperationHandle *
952 GNUNET_PSYCSTORE_message_get_fragment (struct GNUNET_PSYCSTORE_Handle *h,
953                                        const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
954                                        const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
955                                        uint64_t message_id,
956                                        uint64_t fragment_offset,
957                                        GNUNET_PSYCSTORE_FragmentCallback fcb,
958                                        GNUNET_PSYCSTORE_ResultCallback rcb,
959                                        void *cls)
960 {
961   struct MessageGetFragmentRequest *req;
962   struct GNUNET_PSYCSTORE_OperationHandle *op
963     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
964   op->h = h;
965   op->data_cb = (DataCallback) fcb;
966   op->res_cb = rcb;
967   op->cls = cls;
968
969   req = (struct MessageGetFragmentRequest *) &op[1];
970   op->msg = (struct GNUNET_MessageHeader *) req;
971   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT);
972   req->header.size = htons (sizeof (*req));
973   req->channel_key = *channel_key;
974   req->message_id = GNUNET_htonll (message_id);
975   req->fragment_offset = GNUNET_htonll (fragment_offset);
976   if (NULL != slave_key)
977   {
978     req->slave_key = *slave_key;
979     req->do_membership_test = GNUNET_YES;
980   }
981
982   op->op_id = get_next_op_id (h);
983   req->op_id = htonl (op->op_id);
984
985   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
986   transmit_next (h);
987
988   return op;
989 }
990
991
992 /**
993  * Retrieve latest values of counters for a channel master.
994  *
995  * The current value of counters are needed when a channel master is restarted,
996  * so that it can continue incrementing the counters from their last value.
997  *
998  * @param h Handle for the PSYCstore.
999  * @param channel_key Public key that identifies the channel.
1000  * @param ccb Callback to call with the result.
1001  * @param ccb_cls Closure for the @a ccb callback.
1002  *
1003  * @return Handle that can be used to cancel the operation.
1004  */
1005 struct GNUNET_PSYCSTORE_OperationHandle *
1006 GNUNET_PSYCSTORE_counters_get (struct GNUNET_PSYCSTORE_Handle *h,
1007                                struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1008                                GNUNET_PSYCSTORE_CountersCallback ccb,
1009                                void *ccb_cls)
1010 {
1011   struct OperationRequest *req;
1012   struct GNUNET_PSYCSTORE_OperationHandle *op
1013     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
1014   op->h = h;
1015   op->data_cb = ccb;
1016   op->cls = ccb_cls;
1017
1018   req = (struct OperationRequest *) &op[1];
1019   op->msg = (struct GNUNET_MessageHeader *) req;
1020   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET);
1021   req->header.size = htons (sizeof (*req));
1022   req->channel_key = *channel_key;
1023
1024   op->op_id = get_next_op_id (h);
1025   req->op_id = htonl (op->op_id);
1026
1027   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1028   transmit_next (h);
1029
1030   return op;
1031 }
1032
1033
1034 /**
1035  * Apply modifiers of a message to the current channel state.
1036  *
1037  * An error is returned if there are missing messages containing state
1038  * operations before the current one.
1039  *
1040  * @param h Handle for the PSYCstore.
1041  * @param channel_key The channel we are interested in.
1042  * @param message_id ID of the message that contains the @a modifiers.
1043  * @param state_delta Value of the _state_delta PSYC header variable of the message.
1044  * @param modifier_count Number of elements in the @a modifiers array.
1045  * @param modifiers List of modifiers to apply.
1046  * @param rcb Callback to call with the result of the operation.
1047  * @param rcb_cls Closure for the @a rcb callback.
1048  *
1049  * @return Handle that can be used to cancel the operation.
1050  */
1051 struct GNUNET_PSYCSTORE_OperationHandle *
1052 GNUNET_PSYCSTORE_state_modify (struct GNUNET_PSYCSTORE_Handle *h,
1053                                const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1054                                uint64_t message_id,
1055                                uint64_t state_delta,
1056                                size_t modifier_count,
1057                                const struct GNUNET_ENV_Modifier *modifiers,
1058                                GNUNET_PSYCSTORE_ResultCallback rcb,
1059                                void *rcb_cls)
1060 {
1061   struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
1062   size_t i;
1063
1064   for (i = 0; i < modifier_count; i++) {
1065     struct StateModifyRequest *req;
1066     uint16_t name_size = strlen (modifiers[i].name) + 1;
1067
1068     op = GNUNET_malloc (sizeof (*op) + sizeof (*req) + name_size +
1069                         modifiers[i].value_size);
1070     op->h = h;
1071     op->res_cb = rcb;
1072     op->cls = rcb_cls;
1073
1074     req = (struct StateModifyRequest *) &op[1];
1075     op->msg = (struct GNUNET_MessageHeader *) req;
1076     req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY);
1077     req->header.size = htons (sizeof (*req) + name_size
1078                               + modifiers[i].value_size);
1079     req->channel_key = *channel_key;
1080     req->message_id = GNUNET_htonll (message_id);
1081     req->state_delta = GNUNET_htonll (state_delta);
1082     req->oper = modifiers[i].oper;
1083     req->name_size = htons (name_size);
1084     req->flags
1085       = 0 == i
1086       ? STATE_OP_FIRST
1087       : modifier_count - 1 == i
1088       ? STATE_OP_LAST
1089       : 0;
1090
1091     memcpy (&req[1], modifiers[i].name, name_size);
1092     memcpy ((char *) &req[1] + name_size, modifiers[i].value, modifiers[i].value_size);
1093
1094     op->op_id = get_next_op_id (h);
1095     req->op_id = htonl (op->op_id);
1096
1097     GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1098     transmit_next (h);
1099   }
1100   return op;
1101   /* FIXME: only the last operation is returned,
1102    *        operation_cancel() should be able to cancel all of them.
1103    */
1104 }
1105
1106
1107 /**
1108  * Store synchronized state.
1109  *
1110  * @param h Handle for the PSYCstore.
1111  * @param channel_key The channel we are interested in.
1112  * @param message_id ID of the message that contains the state_hash PSYC header variable.
1113  * @param modifier_count Number of elements in the @a modifiers array.
1114  * @param modifiers Full state to store.
1115  * @param rcb Callback to call with the result of the operation.
1116  * @param rcb_cls Closure for the callback.
1117  *
1118  * @return Handle that can be used to cancel the operation.
1119  */
1120 struct GNUNET_PSYCSTORE_OperationHandle *
1121 GNUNET_PSYCSTORE_state_sync (struct GNUNET_PSYCSTORE_Handle *h,
1122                              const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1123                              uint64_t message_id,
1124                              size_t modifier_count,
1125                              const struct GNUNET_ENV_Modifier *modifiers,
1126                              GNUNET_PSYCSTORE_ResultCallback rcb,
1127                              void *rcb_cls)
1128 {
1129   struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
1130   size_t i;
1131
1132   for (i = 0; i < modifier_count; i++) {
1133     struct StateSyncRequest *req;
1134     uint16_t name_size = strlen (modifiers[i].name) + 1;
1135
1136     op = GNUNET_malloc (sizeof (*op) + sizeof (*req) + name_size +
1137                         modifiers[i].value_size);
1138     op->h = h;
1139     op->res_cb = rcb;
1140     op->cls = rcb_cls;
1141
1142     req = (struct StateSyncRequest *) &op[1];
1143     op->msg = (struct GNUNET_MessageHeader *) req;
1144     req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC);
1145     req->header.size = htons (sizeof (*req) + name_size
1146                               + modifiers[i].value_size);
1147     req->channel_key = *channel_key;
1148     req->message_id = GNUNET_htonll (message_id);
1149     req->name_size = htons (name_size);
1150     req->flags
1151       = (0 == i)
1152       ? STATE_OP_FIRST
1153       : (modifier_count - 1 == i)
1154       ? STATE_OP_LAST
1155       : 0;
1156
1157     memcpy (&req[1], modifiers[i].name, name_size);
1158     memcpy ((char *) &req[1] + name_size, modifiers[i].value, modifiers[i].value_size);
1159
1160     op->op_id = get_next_op_id (h);
1161     req->op_id = htonl (op->op_id);
1162
1163     GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1164     transmit_next (h);
1165   }
1166   return op;
1167 }
1168
1169
1170 /**
1171  * Reset the state of a channel.
1172  *
1173  * Delete all state variables stored for the given channel.
1174  *
1175  * @param h Handle for the PSYCstore.
1176  * @param channel_key The channel we are interested in.
1177  * @param rcb Callback to call with the result of the operation.
1178  * @param rcb_cls Closure for the callback.
1179  *
1180  * @return Handle that can be used to cancel the operation.
1181  */
1182 struct GNUNET_PSYCSTORE_OperationHandle *
1183 GNUNET_PSYCSTORE_state_reset (struct GNUNET_PSYCSTORE_Handle *h,
1184                               const struct GNUNET_CRYPTO_EddsaPublicKey
1185                               *channel_key,
1186                               GNUNET_PSYCSTORE_ResultCallback rcb,
1187                               void *rcb_cls)
1188 {
1189   struct OperationRequest *req;
1190   struct GNUNET_PSYCSTORE_OperationHandle *op
1191     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
1192   op->h = h;
1193   op->res_cb = rcb;
1194   op->cls = rcb_cls;
1195
1196   req = (struct OperationRequest *) &op[1];
1197   op->msg = (struct GNUNET_MessageHeader *) req;
1198   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET);
1199   req->header.size = htons (sizeof (*req));
1200   req->channel_key = *channel_key;
1201
1202   op->op_id = get_next_op_id (h);
1203   req->op_id = htonl (op->op_id);
1204
1205   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1206   transmit_next (h);
1207
1208   return op;
1209 }
1210
1211
1212
1213 /**
1214  * Update signed values of state variables in the state store.
1215  *
1216  * @param h Handle for the PSYCstore.
1217  * @param channel_key The channel we are interested in.
1218  * @param message_id Message ID that contained the state @a hash.
1219  * @param hash Hash of the serialized full state.
1220  * @param rcb Callback to call with the result of the operation.
1221  * @param rcb_cls Closure for the callback.
1222  *
1223  */
1224 struct GNUNET_PSYCSTORE_OperationHandle *
1225 GNUNET_PSYCSTORE_state_hash_update (struct GNUNET_PSYCSTORE_Handle *h,
1226                                     const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1227                                     uint64_t message_id,
1228                                     const struct GNUNET_HashCode *hash,
1229                                     GNUNET_PSYCSTORE_ResultCallback rcb,
1230                                     void *rcb_cls)
1231 {
1232   struct StateHashUpdateRequest *req;
1233   struct GNUNET_PSYCSTORE_OperationHandle *op
1234     = GNUNET_malloc (sizeof (*op) + sizeof (*req));
1235   op->h = h;
1236   op->res_cb = rcb;
1237   op->cls = rcb_cls;
1238
1239   req = (struct StateHashUpdateRequest *) &op[1];
1240   op->msg = (struct GNUNET_MessageHeader *) req;
1241   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET);
1242   req->header.size = htons (sizeof (*req));
1243   req->channel_key = *channel_key;
1244   req->hash = *hash;
1245
1246   op->op_id = get_next_op_id (h);
1247   req->op_id = htonl (op->op_id);
1248
1249   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1250   transmit_next (h);
1251
1252   return op;
1253 }
1254
1255
1256 /**
1257  * Retrieve the best matching state variable.
1258  *
1259  * @param h Handle for the PSYCstore.
1260  * @param channel_key The channel we are interested in.
1261  * @param name Name of variable to match, the returned variable might be less specific.
1262  * @param scb Callback to return the matching state variable.
1263  * @param rcb Callback to call with the result of the operation.
1264  * @param cls Closure for the callbacks.
1265  *
1266  * @return Handle that can be used to cancel the operation.
1267  */
1268 struct GNUNET_PSYCSTORE_OperationHandle *
1269 GNUNET_PSYCSTORE_state_get (struct GNUNET_PSYCSTORE_Handle *h,
1270                             const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1271                             const char *name,
1272                             GNUNET_PSYCSTORE_StateCallback scb,
1273                             GNUNET_PSYCSTORE_ResultCallback rcb,
1274                             void *cls)
1275 {
1276   size_t name_size = strlen (name) + 1;
1277   struct OperationRequest *req;
1278   struct GNUNET_PSYCSTORE_OperationHandle *op
1279     = GNUNET_malloc (sizeof (*op) + sizeof (*req) + name_size);
1280   op->h = h;
1281   op->data_cb = (DataCallback) scb;
1282   op->res_cb = rcb;
1283   op->cls = cls;
1284
1285   req = (struct OperationRequest *) &op[1];
1286   op->msg = (struct GNUNET_MessageHeader *) req;
1287   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET);
1288   req->header.size = htons (sizeof (*req) + name_size);
1289   req->channel_key = *channel_key;
1290   memcpy (&req[1], name, name_size);
1291
1292   op->op_id = get_next_op_id (h);
1293   req->op_id = htonl (op->op_id);
1294
1295   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1296   transmit_next (h);
1297
1298   return op;
1299 }
1300
1301
1302
1303 /**
1304  * Retrieve all state variables for a channel with the given prefix.
1305  *
1306  * @param h Handle for the PSYCstore.
1307  * @param channel_key The channel we are interested in.
1308  * @param name_prefix Prefix of state variable names to match.
1309  * @param scb Callback to return matching state variables.
1310  * @param rcb Callback to call with the result of the operation.
1311  * @param cls Closure for the callbacks.
1312  *
1313  * @return Handle that can be used to cancel the operation.
1314  */
1315 struct GNUNET_PSYCSTORE_OperationHandle *
1316 GNUNET_PSYCSTORE_state_get_prefix (struct GNUNET_PSYCSTORE_Handle *h,
1317                                    const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1318                                    const char *name_prefix,
1319                                    GNUNET_PSYCSTORE_StateCallback scb,
1320                                    GNUNET_PSYCSTORE_ResultCallback rcb,
1321                                    void *cls)
1322 {
1323   size_t name_size = strlen (name_prefix) + 1;
1324   struct OperationRequest *req;
1325   struct GNUNET_PSYCSTORE_OperationHandle *op
1326     = GNUNET_malloc (sizeof (*op) + sizeof (*req) + name_size);
1327   op->h = h;
1328   op->data_cb = (DataCallback) scb;
1329   op->res_cb = rcb;
1330   op->cls = cls;
1331
1332   req = (struct OperationRequest *) &op[1];
1333   op->msg = (struct GNUNET_MessageHeader *) req;
1334   req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX);
1335   req->header.size = htons (sizeof (*req) + name_size);
1336   req->channel_key = *channel_key;
1337   memcpy (&req[1], name_prefix, name_size);
1338
1339   op->op_id = get_next_op_id (h);
1340   req->op_id = htonl (op->op_id);
1341
1342   GNUNET_CONTAINER_DLL_insert_tail (h->transmit_head, h->transmit_tail, op);
1343   transmit_next (h);
1344
1345   return op;
1346 }
1347
1348 /* end of psycstore_api.c */