5847fc8520bb2e9286c3cb058657c5081d08f6f3
[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 Liceidentity 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 Liceidentity for more details.
14
15      You should have received a copy of the GNU General Public Liceidentity
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 "psycstore.h"
34
35 #define LOG(kind,...) GNUNET_log_from (kind, "psycstore-api",__VA_ARGS__)
36
37
38 /** 
39  * Handle for an operation with the PSYCstore service.
40  */
41 struct GNUNET_PSYCSTORE_OperationHandle
42 {
43
44   /**
45    * Main PSYCstore handle.
46    */
47   struct GNUNET_PSYCSTORE_Handle *h;
48   
49   /**
50    * We keep operations in a DLL.
51    */
52   struct GNUNET_PSYCSTORE_OperationHandle *next;
53
54   /**
55    * We keep operations in a DLL.
56    */
57   struct GNUNET_PSYCSTORE_OperationHandle *prev;
58
59   /**
60    * Message to send to the PSYCstore service.
61    * Allocated at the end of this struct.
62    */
63   const struct GNUNET_MessageHeader *msg;
64
65   /**
66    * Continuation to invoke with the result of an operation.
67    */
68   GNUNET_PSYCSTORE_ResultCallback res_cb;
69
70   /**
71    * Continuation to invoke with the result of an operation returning a fragment.
72    */
73   GNUNET_PSYCSTORE_FragmentCallback frag_cb;
74
75   /**
76    * Continuation to invoke with the result of an operation returning a state variable.
77    */
78   GNUNET_PSYCSTORE_StateCallback state_cb;
79
80   /**
81    * Closure for the callbacks.
82    */
83   void *cls;
84
85 };
86
87
88 /**
89  * Handle for the service.
90  */
91 struct GNUNET_PSYCSTORE_Handle
92 {
93   /**
94    * Configuration to use.
95    */
96   const struct GNUNET_CONFIGURATION_Handle *cfg;
97
98   /**
99    * Socket (if available).
100    */
101   struct GNUNET_CLIENT_Connection *client;
102
103   /**
104    * Head of active operations.
105    */ 
106   struct GNUNET_PSYCSTORE_OperationHandle *op_head;
107
108   /**
109    * Tail of active operations.
110    */ 
111   struct GNUNET_PSYCSTORE_OperationHandle *op_tail;
112
113   /**
114    * Currently pending transmission request, or NULL for none.
115    */
116   struct GNUNET_CLIENT_TransmitHandle *th;
117
118   /**
119    * Task doing exponential back-off trying to reconnect.
120    */
121   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
122
123   /**
124    * Time for next connect retry.
125    */
126   struct GNUNET_TIME_Relative reconnect_delay;
127
128   /**
129    * Are we polling for incoming messages right now?
130    */
131   int in_receive;
132
133 };
134
135
136 /**
137  * Try again to connect to the PSYCstore service.
138  *
139  * @param cls handle to the PSYCstore service.
140  * @param tc scheduler context
141  */
142 static void
143 reconnect (void *cls,
144            const struct GNUNET_SCHEDULER_TaskContext *tc);
145
146
147 /**
148  * Reschedule a connect attempt to the service.
149  *
150  * @param h transport service to reconnect
151  */
152 static void
153 reschedule_connect (struct GNUNET_PSYCSTORE_Handle *h)
154 {
155   GNUNET_assert (h->reconnect_task == GNUNET_SCHEDULER_NO_TASK);
156
157   if (NULL != h->th)
158   {
159     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
160     h->th = NULL;
161   }
162   if (NULL != h->client)
163   {
164     GNUNET_CLIENT_disconnect (h->client);
165     h->client = NULL;
166   }
167   h->in_receive = GNUNET_NO;
168   LOG (GNUNET_ERROR_TYPE_DEBUG,
169        "Scheduling task to reconnect to PSYCstore service in %s.\n",
170        GNUNET_STRINGS_relative_time_to_string (h->reconnect_delay, GNUNET_YES));
171   h->reconnect_task =
172       GNUNET_SCHEDULER_add_delayed (h->reconnect_delay, &reconnect, h);
173   h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
174 }
175
176
177 /**
178  * Type of a function to call when we receive a message
179  * from the service.
180  *
181  * @param cls closure
182  * @param msg message received, NULL on timeout or fatal error
183  */
184 static void
185 message_handler (void *cls, 
186                  const struct GNUNET_MessageHeader *msg)
187 {
188   struct GNUNET_PSYCSTORE_Handle *h = cls;
189   struct GNUNET_PSYCSTORE_OperationHandle *op;
190   const struct ResultCodeMessage *rcm;
191   const char *str;
192   uint16_t size;
193
194   if (NULL == msg)
195   {
196     reschedule_connect (h);
197     return;
198   }
199   LOG (GNUNET_ERROR_TYPE_DEBUG,
200        "Received message of type %d from PSYCstore service\n",
201        ntohs (msg->type));
202   size = ntohs (msg->size);
203   switch (ntohs (msg->type))
204   {
205   case GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE:
206     if (size < sizeof (struct ResultCodeMessage))
207     {
208       GNUNET_break (0);
209       reschedule_connect (h);
210       return;
211     }
212     rcm = (const struct ResultCodeMessage *) msg;
213     str = (const char *) &rcm[1];
214     if ( (size > sizeof (struct ResultCodeMessage)) &&
215          ('\0' != str[size - sizeof (struct ResultCodeMessage) - 1]) )
216     {
217       GNUNET_break (0);
218       reschedule_connect (h);
219       return;
220     }
221     if (size == sizeof (struct ResultCodeMessage))
222       str = NULL;
223
224     op = h->op_head;
225     GNUNET_CONTAINER_DLL_remove (h->op_head,
226                                  h->op_tail,
227                                  op);
228     GNUNET_CLIENT_receive (h->client, &message_handler, h,
229                            GNUNET_TIME_UNIT_FOREVER_REL);
230     if (NULL != op->res_cb)
231       op->res_cb (op->cls, rcm->result_code , str);
232     GNUNET_free (op);
233     break;
234   default:
235     GNUNET_break (0);
236     reschedule_connect (h);
237     return;
238   }
239 }
240
241
242 /**
243  * Schedule transmission of the next message from our queue.
244  *
245  * @param h PSYCstore handle
246  */
247 static void
248 transmit_next (struct GNUNET_PSYCSTORE_Handle *h);
249
250
251 /**
252  * Transmit next message to service.
253  *
254  * @param cls the 'struct GNUNET_PSYCSTORE_Handle'.
255  * @param size number of bytes available in buf
256  * @param buf where to copy the message
257  * @return number of bytes copied to buf
258  */
259 static size_t
260 send_next_message (void *cls, 
261                    size_t size, 
262                    void *buf)
263 {
264   struct GNUNET_PSYCSTORE_Handle *h = cls;
265   struct GNUNET_PSYCSTORE_OperationHandle *op = h->op_head;
266   size_t ret;
267   
268   h->th = NULL;
269   if (NULL == op)
270     return 0;
271   ret = ntohs (op->msg->size);
272   if (ret > size)
273   {
274     reschedule_connect (h);
275     return 0;
276   }  
277   LOG (GNUNET_ERROR_TYPE_DEBUG,
278        "Sending message of type %d to PSYCstore service\n",
279        ntohs (op->msg->type));
280   memcpy (buf, op->msg, ret);
281   if ( (NULL == op->res_cb) &&
282        (NULL == op->frag_cb) &&
283        (NULL == op->state_cb))
284   {
285     GNUNET_CONTAINER_DLL_remove (h->op_head,
286                                  h->op_tail,
287                                  op);
288     GNUNET_free (op);
289     transmit_next (h);
290   }
291   if (GNUNET_NO == h->in_receive)
292   {
293     h->in_receive = GNUNET_YES;
294     GNUNET_CLIENT_receive (h->client,
295                            &message_handler, h,
296                            GNUNET_TIME_UNIT_FOREVER_REL);
297   }
298   return ret;
299 }
300
301
302 /**
303  * Schedule transmission of the next message from our queue.
304  *
305  * @param h PSYCstore handle
306  */
307 static void
308 transmit_next (struct GNUNET_PSYCSTORE_Handle *h)
309 {
310   struct GNUNET_PSYCSTORE_OperationHandle *op = h->op_head;
311
312   GNUNET_assert (NULL == h->th);
313   if (NULL == op)
314     return;
315   if (NULL == h->client)
316     return;
317   h->th = GNUNET_CLIENT_notify_transmit_ready (h->client,
318                                                ntohs (op->msg->size),
319                                                GNUNET_TIME_UNIT_FOREVER_REL,
320                                                GNUNET_NO,
321                                                &send_next_message,
322                                                h);
323 }
324
325
326 /**
327  * Try again to connect to the PSYCstore service.
328  *
329  * @param cls the handle to the PSYCstore service
330  * @param tc scheduler context
331  */
332 static void
333 reconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
334 {
335   struct GNUNET_PSYCSTORE_Handle *h = cls;
336
337   h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
338   LOG (GNUNET_ERROR_TYPE_DEBUG,
339        "Connecting to PSYCstore service.\n");
340   GNUNET_assert (NULL == h->client);
341   h->client = GNUNET_CLIENT_connect ("psycstore", h->cfg);
342   GNUNET_assert (NULL != h->client);
343   transmit_next (h);
344 }
345
346
347 /**
348  * Connect to the PSYCstore service.
349  *
350  * @param cfg the configuration to use
351  * @return handle to use
352  */
353 struct GNUNET_PSYCSTORE_Handle *
354 GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
355 {
356   struct GNUNET_PSYCSTORE_Handle *h;
357
358   h = GNUNET_new (struct GNUNET_PSYCSTORE_Handle);
359   h->cfg = cfg;
360   h->reconnect_delay = GNUNET_TIME_UNIT_ZERO;
361   h->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, h);
362   return h;
363 }
364
365
366 /**
367  * Disconnect from PSYCstore service
368  *
369  * @param h handle to destroy
370  */
371 void
372 GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h)
373 {
374   GNUNET_assert (NULL != h);
375   GNUNET_assert (h->op_head == h->op_tail);
376   if (h->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
377   {
378     GNUNET_SCHEDULER_cancel (h->reconnect_task);
379     h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
380   }
381   if (NULL != h->th)
382   {
383     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
384     h->th = NULL;
385   }
386   if (NULL != h->client)
387   {
388     GNUNET_CLIENT_disconnect (h->client);
389     h->client = NULL;
390   }
391   GNUNET_free (h);
392 }
393
394
395 /**
396  * Cancel a PSYCstore operation. Note that the operation MAY still
397  * be executed; this merely cancels the continuation; if the request
398  * was already transmitted, the service may still choose to complete
399  * the operation.
400  *
401  * @param op Operation to cancel.
402  */
403 void
404 GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
405 {
406   struct GNUNET_PSYCSTORE_Handle *h = op->h;
407
408   if ( (h->op_head != op) ||
409        (NULL == h->client) )
410   {
411     /* request not active, can simply remove */
412     GNUNET_CONTAINER_DLL_remove (h->op_head,
413                                  h->op_tail,
414                                  op);
415     GNUNET_free (op);
416     return;
417   }
418   if (NULL != h->th)
419   {
420     /* request active but not yet with service, can still abort */
421     GNUNET_CLIENT_notify_transmit_ready_cancel (h->th);
422     h->th = NULL;
423     GNUNET_CONTAINER_DLL_remove (h->op_head,
424                                  h->op_tail,
425                                  op);
426     GNUNET_free (op);
427     transmit_next (h);
428     return;
429   }
430   /* request active with service, simply ensure continuations are not called */
431   op->res_cb = NULL;
432   op->frag_cb = NULL;
433   op->state_cb = NULL;
434 }
435
436
437 struct GNUNET_PSYCSTORE_OperationHandle *
438 GNUNET_PSYCSTORE_membership_store (
439   struct GNUNET_PSYCSTORE_Handle *h,
440   const struct GNUNET_CRYPTO_EccPublicSignKey *channel_key,
441   const struct GNUNET_CRYPTO_EccPublicSignKey *slave_key,
442   int did_join,
443   uint64_t announced_at,
444   uint64_t effective_since,
445   uint64_t group_generation,
446   GNUNET_PSYCSTORE_ResultCallback rcb,
447   void *rcb_cls)
448 {
449   
450 }
451
452
453 struct GNUNET_PSYCSTORE_OperationHandle *
454 GNUNET_PSYCSTORE_membership_test (
455   struct GNUNET_PSYCSTORE_Handle *h,
456   const struct GNUNET_CRYPTO_EccPublicSignKey *channel_key,
457   const struct GNUNET_CRYPTO_EccPublicSignKey *slave_key,
458   uint64_t message_id,
459   uint64_t group_generation,
460   GNUNET_PSYCSTORE_ResultCallback rcb,
461   void *rcb_cls)
462 {
463
464 }
465
466 /* end of psycstore_api.c */