aboutsummaryrefslogtreecommitdiff
path: root/src/psycstore
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2019-02-11 20:39:36 +0100
committerChristian Grothoff <christian@grothoff.org>2019-02-11 20:39:36 +0100
commit1f59e703d82b47f3aeaf432045a2633c2841169b (patch)
tree6af5609b388cf1906a29b5d572bec2dd8fb2ae1c /src/psycstore
downloadgnunet-secushare-1f59e703d82b47f3aeaf432045a2633c2841169b.tar.gz
gnunet-secushare-1f59e703d82b47f3aeaf432045a2633c2841169b.zip
initial import from gnunet.git
Diffstat (limited to 'src/psycstore')
-rw-r--r--src/psycstore/.gitignore5
-rw-r--r--src/psycstore/Makefile.am155
-rw-r--r--src/psycstore/gnunet-service-psycstore.c1049
-rw-r--r--src/psycstore/plugin_psycstore_mysql.c1960
-rw-r--r--src/psycstore/plugin_psycstore_postgres.c1530
-rw-r--r--src/psycstore/plugin_psycstore_sqlite.c1948
-rw-r--r--src/psycstore/psycstore.conf.in28
-rw-r--r--src/psycstore/psycstore.h520
-rw-r--r--src/psycstore/psycstore_api.c1285
-rw-r--r--src/psycstore/test_plugin_psycstore.c532
-rw-r--r--src/psycstore/test_plugin_psycstore_mysql.conf7
-rw-r--r--src/psycstore/test_plugin_psycstore_postgres.conf2
-rw-r--r--src/psycstore/test_plugin_psycstore_sqlite.conf2
-rw-r--r--src/psycstore/test_psycstore.c586
-rw-r--r--src/psycstore/test_psycstore.conf8
15 files changed, 9617 insertions, 0 deletions
diff --git a/src/psycstore/.gitignore b/src/psycstore/.gitignore
new file mode 100644
index 0000000..5ec7832
--- /dev/null
+++ b/src/psycstore/.gitignore
@@ -0,0 +1,5 @@
1gnunet-service-psycstore
2test_plugin_psycstore_mysql
3test_plugin_psycstore_sqlite
4test_plugin_psycstore_postgres
5test_psycstore
diff --git a/src/psycstore/Makefile.am b/src/psycstore/Makefile.am
new file mode 100644
index 0000000..557bb42
--- /dev/null
+++ b/src/psycstore/Makefile.am
@@ -0,0 +1,155 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4plugindir = $(libdir)/gnunet
5
6pkgcfgdir= $(pkgdatadir)/config.d/
7
8libexecdir= $(pkglibdir)/libexec/
9
10pkgcfg_DATA = \
11 psycstore.conf
12
13
14if MINGW
15 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
16endif
17
18if USE_COVERAGE
19 AM_CFLAGS = --coverage -O0
20 XLIB = -lgcov
21endif
22
23if HAVE_MYSQL
24MYSQL_PLUGIN = libgnunet_plugin_psycstore_mysql.la
25if HAVE_TESTING
26MYSQL_TESTS = test_plugin_psycstore_mysql
27endif
28endif
29
30if HAVE_POSTGRESQL
31POSTGRES_PLUGIN = libgnunet_plugin_psycstore_postgres.la
32if HAVE_TESTING
33POSTGRES_TESTS = test_plugin_psycstore_postgres
34endif
35endif
36
37if HAVE_SQLITE
38SQLITE_PLUGIN = libgnunet_plugin_psycstore_sqlite.la
39if HAVE_TESTING
40SQLITE_TESTS = test_plugin_psycstore_sqlite
41endif
42endif
43
44lib_LTLIBRARIES = libgnunetpsycstore.la
45
46libgnunetpsycstore_la_SOURCES = \
47 psycstore_api.c \
48 psycstore.h
49libgnunetpsycstore_la_LIBADD = \
50 $(top_builddir)/src/util/libgnunetutil.la \
51 $(GN_LIBINTL) $(XLIB)
52libgnunetpsycstore_la_LDFLAGS = \
53 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
54 -version-info 0:0:0
55
56bin_PROGRAMS =
57
58libexec_PROGRAMS = \
59 gnunet-service-psycstore
60
61gnunet_service_psycstore_SOURCES = \
62 gnunet-service-psycstore.c
63gnunet_service_psycstore_LDADD = \
64 $(top_builddir)/src/statistics/libgnunetstatistics.la \
65 $(top_builddir)/src/util/libgnunetutil.la \
66 $(top_builddir)/src/psycutil/libgnunetpsycutil.la \
67 $(GN_LIBINTL)
68
69plugin_LTLIBRARIES = \
70 $(SQLITE_PLUGIN) \
71 $(MYSQL_PLUGIN) \
72 $(POSTGRES_PLUGIN)
73
74
75libgnunet_plugin_psycstore_mysql_la_SOURCES = \
76 plugin_psycstore_mysql.c
77libgnunet_plugin_psycstore_mysql_la_LIBADD = \
78 libgnunetpsycstore.la \
79 $(top_builddir)/src/my/libgnunetmy.la \
80 $(top_builddir)/src/mysql/libgnunetmysql.la \
81 $(top_builddir)/src/statistics/libgnunetstatistics.la \
82 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
83 $(LTLIBINTL)
84libgnunet_plugin_psycstore_mysql_la_LDFLAGS = \
85 $(GN_PLUGIN_LDFLAGS)
86
87libgnunet_plugin_psycstore_postgres_la_SOURCES = \
88 plugin_psycstore_postgres.c
89libgnunet_plugin_psycstore_postgres_la_LIBADD = \
90 libgnunetpsycstore.la \
91 $(top_builddir)/src/pq/libgnunetpq.la \
92 $(top_builddir)/src/statistics/libgnunetstatistics.la \
93 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lpq \
94 $(LTLIBINTL)
95libgnunet_plugin_psycstore_postgres_la_LDFLAGS = \
96 $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS)
97libgnunet_plugin_psycstore_postgres_la_CPPFLAGS = \
98 $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS)
99
100
101libgnunet_plugin_psycstore_sqlite_la_SOURCES = \
102 plugin_psycstore_sqlite.c
103libgnunet_plugin_psycstore_sqlite_la_LIBADD = \
104 libgnunetpsycstore.la \
105 $(top_builddir)/src/statistics/libgnunetstatistics.la \
106 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) -lsqlite3 \
107 $(LTLIBINTL)
108libgnunet_plugin_psycstore_sqlite_la_LDFLAGS = \
109 $(GN_PLUGIN_LDFLAGS)
110
111
112if HAVE_SQLITE
113if HAVE_TESTING
114check_PROGRAMS = \
115 $(SQLITE_TESTS) \
116 $(MYSQL_TESTS) \
117 $(POSTGRES_TESTS) \
118 test_psycstore
119endif
120endif
121
122if ENABLE_TEST_RUN
123AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME;
124TESTS = $(check_PROGRAMS)
125endif
126
127test_psycstore_SOURCES = \
128 test_psycstore.c
129test_psycstore_LDADD = \
130 libgnunetpsycstore.la \
131 $(top_builddir)/src/testing/libgnunettesting.la \
132 $(top_builddir)/src/util/libgnunetutil.la
133
134EXTRA_DIST = \
135 test_psycstore.conf
136
137
138test_plugin_psycstore_sqlite_SOURCES = \
139 test_plugin_psycstore.c
140test_plugin_psycstore_sqlite_LDADD = \
141 $(top_builddir)/src/testing/libgnunettesting.la \
142 $(top_builddir)/src/util/libgnunetutil.la
143
144test_plugin_psycstore_mysql_SOURCES = \
145 test_plugin_psycstore.c
146test_plugin_psycstore_mysql_LDADD = \
147 $(top_builddir)/src/testing/libgnunettesting.la \
148 $(top_builddir)/src/util/libgnunetutil.la
149
150test_plugin_psycstore_postgres_SOURCES = \
151 test_plugin_psycstore.c
152test_plugin_psycstore_postgres_LDADD = \
153 $(top_builddir)/src/testing/libgnunettesting.la \
154 $(top_builddir)/src/util/libgnunetutil.la
155
diff --git a/src/psycstore/gnunet-service-psycstore.c b/src/psycstore/gnunet-service-psycstore.c
new file mode 100644
index 0000000..9aebd3e
--- /dev/null
+++ b/src/psycstore/gnunet-service-psycstore.c
@@ -0,0 +1,1049 @@
1/**
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file psycstore/gnunet-service-psycstore.c
23 * @brief PSYCstore service
24 * @author Gabor X Toth
25 * @author Christian Grothoff
26 */
27
28#include <inttypes.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_constants.h"
33#include "gnunet_protocols.h"
34#include "gnunet_statistics_service.h"
35#include "gnunet_psyc_util_lib.h"
36#include "gnunet_psycstore_service.h"
37#include "gnunet_psycstore_plugin.h"
38#include "psycstore.h"
39
40
41/**
42 * Handle to our current configuration.
43 */
44static const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46/**
47 * Service handle.
48 */
49static struct GNUNET_SERVICE_Handle *service;
50
51/**
52 * Handle to the statistics service.
53 */
54static struct GNUNET_STATISTICS_Handle *stats;
55
56/**
57 * Database handle
58 */
59static struct GNUNET_PSYCSTORE_PluginFunctions *db;
60
61/**
62 * Name of the database plugin
63 */
64static char *db_lib_name;
65
66
67/**
68 * Task run during shutdown.
69 *
70 * @param cls unused
71 */
72static void
73shutdown_task (void *cls)
74{
75 if (NULL != stats)
76 {
77 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
78 stats = NULL;
79 }
80 GNUNET_break (NULL == GNUNET_PLUGIN_unload (db_lib_name, db));
81 GNUNET_free (db_lib_name);
82 db_lib_name = NULL;
83}
84
85
86/**
87 * Send a result code back to the client.
88 *
89 * @param client
90 * Client that should receive the result code.
91 * @param result_code
92 * Code to transmit.
93 * @param op_id
94 * Operation ID in network byte order.
95 * @param err_msg
96 * Error message to include (or NULL for none).
97 */
98static void
99send_result_code (struct GNUNET_SERVICE_Client *client,
100 uint64_t op_id,
101 int64_t result_code,
102 const char *err_msg)
103{
104 struct OperationResult *res;
105 size_t err_size = 0;
106
107 if (NULL != err_msg)
108 err_size = strnlen (err_msg,
109 GNUNET_MAX_MESSAGE_SIZE - sizeof (*res) - 1) + 1;
110 struct GNUNET_MQ_Envelope *
111 env = GNUNET_MQ_msg_extra (res, err_size,
112 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE);
113 res->result_code = GNUNET_htonll (result_code - INT64_MIN);
114 res->op_id = op_id;
115 if (0 < err_size)
116 {
117 GNUNET_memcpy (&res[1], err_msg, err_size);
118 ((char *) &res[1])[err_size - 1] = '\0';
119 }
120
121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
122 "Sending result to client: %" PRId64 " (%s)\n",
123 result_code, err_msg);
124 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
125}
126
127
128enum
129{
130 MEMBERSHIP_TEST_NOT_NEEDED = 0,
131 MEMBERSHIP_TEST_NEEDED = 1,
132 MEMBERSHIP_TEST_DONE = 2,
133} MessageMembershipTest;
134
135
136struct SendClosure
137{
138 struct GNUNET_SERVICE_Client *client;
139
140 /**
141 * Channel's public key.
142 */
143 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
144
145 /**
146 * Slave's public key.
147 */
148 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
149
150 /**
151 * Operation ID.
152 */
153 uint64_t op_id;
154
155 /**
156 * Membership test result.
157 */
158 int membership_test_result;
159
160 /**
161 * Do membership test with @a slave_key before returning fragment?
162 * @see enum MessageMembershipTest
163 */
164 uint8_t membership_test;
165};
166
167
168static int
169send_fragment (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg,
170 enum GNUNET_PSYCSTORE_MessageFlags flags)
171{
172 struct SendClosure *sc = cls;
173 struct FragmentResult *res;
174
175 if (MEMBERSHIP_TEST_NEEDED == sc->membership_test)
176 {
177 sc->membership_test = MEMBERSHIP_TEST_DONE;
178 sc->membership_test_result
179 = db->membership_test (db->cls, &sc->channel_key, &sc->slave_key,
180 GNUNET_ntohll (msg->message_id));
181 switch (sc->membership_test_result)
182 {
183 case GNUNET_YES:
184 break;
185
186 case GNUNET_NO:
187 case GNUNET_SYSERR:
188 return GNUNET_NO;
189 }
190 }
191
192 size_t msg_size = ntohs (msg->header.size);
193
194 struct GNUNET_MQ_Envelope *
195 env = GNUNET_MQ_msg_extra (res, msg_size,
196 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT);
197 res->op_id = sc->op_id;
198 res->psycstore_flags = htonl (flags);
199 GNUNET_memcpy (&res[1], msg, msg_size);
200 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201 "Sending fragment %llu to client\n",
202 (unsigned long long) GNUNET_ntohll (msg->fragment_id));
203 GNUNET_free (msg);
204
205 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (sc->client), env);
206 return GNUNET_YES;
207}
208
209
210static int
211send_state_var (void *cls, const char *name,
212 const void *value, uint32_t value_size)
213{
214 struct SendClosure *sc = cls;
215 struct StateResult *res;
216 size_t name_size = strlen (name) + 1;
217
218 /** @todo FIXME: split up value into 64k chunks */
219
220 struct GNUNET_MQ_Envelope *
221 env = GNUNET_MQ_msg_extra (res, name_size + value_size,
222 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE);
223 res->op_id = sc->op_id;
224 res->name_size = htons (name_size);
225 GNUNET_memcpy (&res[1], name, name_size);
226 GNUNET_memcpy ((char *) &res[1] + name_size, value, value_size);
227 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
228 "Sending state variable %s to client\n", name);
229
230 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (sc->client), env);
231 return GNUNET_OK;
232}
233
234
235static void
236handle_client_membership_store (void *cls,
237 const struct MembershipStoreRequest *req)
238{
239 struct GNUNET_SERVICE_Client *client = cls;
240
241 int ret = db->membership_store (db->cls, &req->channel_key, &req->slave_key,
242 req->did_join,
243 GNUNET_ntohll (req->announced_at),
244 GNUNET_ntohll (req->effective_since),
245 GNUNET_ntohll (req->group_generation));
246
247 if (ret != GNUNET_OK)
248 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
249 _("Failed to store membership information!\n"));
250
251 send_result_code (client, req->op_id, ret, NULL);
252 GNUNET_SERVICE_client_continue (client);
253}
254
255
256static void
257handle_client_membership_test (void *cls,
258 const struct MembershipTestRequest *req)
259{
260 struct GNUNET_SERVICE_Client *client = cls;
261
262 int ret = db->membership_test (db->cls, &req->channel_key, &req->slave_key,
263 GNUNET_ntohll (req->message_id));
264 switch (ret)
265 {
266 case GNUNET_YES:
267 case GNUNET_NO:
268 break;
269 default:
270 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
271 _("Failed to test membership!\n"));
272 }
273
274 send_result_code (client, req->op_id, ret, NULL);
275 GNUNET_SERVICE_client_continue (client);
276}
277
278
279static int
280check_client_fragment_store (void *cls,
281 const struct FragmentStoreRequest *req)
282{
283 return GNUNET_OK;
284}
285
286
287static void
288handle_client_fragment_store (void *cls,
289 const struct FragmentStoreRequest *req)
290{
291 struct GNUNET_SERVICE_Client *client = cls;
292
293 const struct GNUNET_MessageHeader *
294 msg = GNUNET_MQ_extract_nested_mh (req);
295 if (NULL == msg
296 || ntohs (msg->size) < sizeof (struct GNUNET_MULTICAST_MessageHeader))
297 {
298 GNUNET_break (0);
299 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
300 _("Dropping invalid fragment\n"));
301 GNUNET_SERVICE_client_drop (client);
302 return;
303 }
304
305 int ret = db->fragment_store (db->cls, &req->channel_key,
306 (const struct GNUNET_MULTICAST_MessageHeader *)
307 msg, ntohl (req->psycstore_flags));
308
309 if (ret != GNUNET_OK)
310 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
311 _("Failed to store fragment\n"));
312
313 send_result_code (client, req->op_id, ret, NULL);
314 GNUNET_SERVICE_client_continue (client);
315}
316
317
318static void
319handle_client_fragment_get (void *cls,
320 const struct FragmentGetRequest *req)
321{
322 struct GNUNET_SERVICE_Client *client = cls;
323
324 struct SendClosure
325 sc = { .op_id = req->op_id,
326 .client = client,
327 .channel_key = req->channel_key,
328 .slave_key = req->slave_key,
329 .membership_test = req->do_membership_test };
330
331 int64_t ret;
332 uint64_t ret_frags = 0;
333 uint64_t first_fragment_id = GNUNET_ntohll (req->first_fragment_id);
334 uint64_t last_fragment_id = GNUNET_ntohll (req->last_fragment_id);
335 uint64_t limit = GNUNET_ntohll (req->fragment_limit);
336
337 if (0 == limit)
338 ret = db->fragment_get (db->cls, &req->channel_key,
339 first_fragment_id, last_fragment_id,
340 &ret_frags, send_fragment, &sc);
341 else
342 ret = db->fragment_get_latest (db->cls, &req->channel_key, limit,
343 &ret_frags, send_fragment, &sc);
344
345 switch (ret)
346 {
347 case GNUNET_YES:
348 case GNUNET_NO:
349 if (MEMBERSHIP_TEST_DONE == sc.membership_test)
350 {
351 switch (sc.membership_test_result)
352 {
353 case GNUNET_YES:
354 break;
355
356 case GNUNET_NO:
357 ret = GNUNET_PSYCSTORE_MEMBERSHIP_TEST_FAILED;
358 break;
359
360 case GNUNET_SYSERR:
361 ret = GNUNET_SYSERR;
362 break;
363 }
364 }
365 break;
366 default:
367 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
368 _("Failed to get fragment!\n"));
369 }
370 send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
371 GNUNET_SERVICE_client_continue (client);
372}
373
374
375static int
376check_client_message_get (void *cls,
377 const struct MessageGetRequest *req)
378{
379 return GNUNET_OK;
380}
381
382
383static void
384handle_client_message_get (void *cls,
385 const struct MessageGetRequest *req)
386{
387 struct GNUNET_SERVICE_Client *client = cls;
388
389 uint16_t size = ntohs (req->header.size);
390 const char *method_prefix = (const char *) &req[1];
391
392 if (size < sizeof (*req) + 1
393 || '\0' != method_prefix[size - sizeof (*req) - 1])
394 {
395 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
396 "Message get: invalid method prefix. size: %u < %u?\n",
397 size,
398 (unsigned int) (sizeof (*req) + 1));
399 GNUNET_break (0);
400 GNUNET_SERVICE_client_drop (client);
401 return;
402 }
403
404 struct SendClosure
405 sc = { .op_id = req->op_id,
406 .client = client,
407 .channel_key = req->channel_key,
408 .slave_key = req->slave_key,
409 .membership_test = req->do_membership_test };
410
411 int64_t ret;
412 uint64_t ret_frags = 0;
413 uint64_t first_message_id = GNUNET_ntohll (req->first_message_id);
414 uint64_t last_message_id = GNUNET_ntohll (req->last_message_id);
415 uint64_t msg_limit = GNUNET_ntohll (req->message_limit);
416 uint64_t frag_limit = GNUNET_ntohll (req->fragment_limit);
417
418 /** @todo method_prefix */
419 if (0 == msg_limit)
420 ret = db->message_get (db->cls, &req->channel_key,
421 first_message_id, last_message_id, frag_limit,
422 &ret_frags, send_fragment, &sc);
423 else
424 ret = db->message_get_latest (db->cls, &req->channel_key, msg_limit,
425 &ret_frags, send_fragment, &sc);
426
427 switch (ret)
428 {
429 case GNUNET_YES:
430 case GNUNET_NO:
431 break;
432 default:
433 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
434 _("Failed to get message!\n"));
435 }
436
437 send_result_code (client, req->op_id, (ret < 0) ? ret : ret_frags, NULL);
438 GNUNET_SERVICE_client_continue (client);
439}
440
441
442static void
443handle_client_message_get_fragment (void *cls,
444 const struct MessageGetFragmentRequest *req)
445{
446 struct GNUNET_SERVICE_Client *client = cls;
447
448 struct SendClosure
449 sc = { .op_id = req->op_id, .client = client,
450 .channel_key = req->channel_key, .slave_key = req->slave_key,
451 .membership_test = req->do_membership_test };
452
453 int ret = db->message_get_fragment (db->cls, &req->channel_key,
454 GNUNET_ntohll (req->message_id),
455 GNUNET_ntohll (req->fragment_offset),
456 &send_fragment, &sc);
457 switch (ret)
458 {
459 case GNUNET_YES:
460 case GNUNET_NO:
461 break;
462 default:
463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
464 _("Failed to get message fragment!\n"));
465 }
466
467 send_result_code (client, req->op_id, ret, NULL);
468 GNUNET_SERVICE_client_continue (client);
469}
470
471
472static void
473handle_client_counters_get (void *cls,
474 const struct OperationRequest *req)
475{
476 struct GNUNET_SERVICE_Client *client = cls;
477
478 struct CountersResult *res;
479 struct GNUNET_MQ_Envelope *
480 env = GNUNET_MQ_msg (res, GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS);
481
482 int ret = db->counters_message_get (db->cls, &req->channel_key,
483 &res->max_fragment_id, &res->max_message_id,
484 &res->max_group_generation);
485 switch (ret)
486 {
487 case GNUNET_OK:
488 ret = db->counters_state_get (db->cls, &req->channel_key,
489 &res->max_state_message_id);
490 case GNUNET_NO:
491 break;
492 default:
493 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
494 _("Failed to get master counters!\n"));
495 }
496
497 res->result_code = htonl (ret);
498 res->op_id = req->op_id;
499 res->max_fragment_id = GNUNET_htonll (res->max_fragment_id);
500 res->max_message_id = GNUNET_htonll (res->max_message_id);
501 res->max_group_generation = GNUNET_htonll (res->max_group_generation);
502 res->max_state_message_id = GNUNET_htonll (res->max_state_message_id);
503
504 GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (client), env);
505 GNUNET_SERVICE_client_continue (client);
506}
507
508
509struct StateModifyClosure
510{
511 const struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
512 struct GNUNET_PSYC_ReceiveHandle *recv;
513 enum GNUNET_PSYC_MessageState msg_state;
514 char mod_oper;
515 char *mod_name;
516 char *mod_value;
517 uint32_t mod_value_size;
518 uint32_t mod_value_remaining;
519};
520
521
522static void
523recv_state_message_part (void *cls,
524 const struct GNUNET_PSYC_MessageHeader *msg,
525 const struct GNUNET_MessageHeader *pmsg)
526{
527 struct StateModifyClosure *scls = cls;
528 uint16_t psize;
529
530 if (NULL == msg)
531 { // FIXME: error on unknown message
532 return;
533 }
534
535 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
536 "recv_state_message_part() message_id: %" PRIu64
537 ", fragment_offset: %" PRIu64 ", flags: %u\n",
538 GNUNET_ntohll (msg->message_id),
539 GNUNET_ntohll (msg->fragment_offset),
540 ntohl (msg->flags));
541
542 if (NULL == pmsg)
543 {
544 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
545 return;
546 }
547
548 switch (ntohs (pmsg->type))
549 {
550 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_METHOD:
551 {
552 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_METHOD;
553 break;
554 }
555
556 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MODIFIER:
557 {
558 struct GNUNET_PSYC_MessageModifier *
559 pmod = (struct GNUNET_PSYC_MessageModifier *) pmsg;
560 psize = ntohs (pmod->header.size);
561 uint16_t name_size = ntohs (pmod->name_size);
562 uint32_t value_size = ntohl (pmod->value_size);
563
564 const char *name = (const char *) &pmod[1];
565 const void *value = name + name_size;
566
567 if (GNUNET_PSYC_OP_SET != pmod->oper)
568 { // Apply non-transient operation.
569 if (psize == sizeof (*pmod) + name_size + value_size)
570 {
571 db->state_modify_op (db->cls, &scls->channel_key,
572 pmod->oper, name, value, value_size);
573 }
574 else
575 {
576 scls->mod_oper = pmod->oper;
577 scls->mod_name = GNUNET_malloc (name_size);
578 GNUNET_memcpy (scls->mod_name, name, name_size);
579
580 scls->mod_value_size = value_size;
581 scls->mod_value = GNUNET_malloc (scls->mod_value_size);
582 scls->mod_value_remaining
583 = scls->mod_value_size - (psize - sizeof (*pmod) - name_size);
584 GNUNET_memcpy (scls->mod_value, value, value_size - scls->mod_value_remaining);
585 }
586 }
587 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_MODIFIER;
588 break;
589 }
590
591 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_MOD_CONT:
592 if (GNUNET_PSYC_OP_SET != scls->mod_oper)
593 {
594 if (scls->mod_value_remaining == 0)
595 {
596 GNUNET_break_op (0);
597 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
598 }
599 psize = ntohs (pmsg->size);
600 GNUNET_memcpy (scls->mod_value + (scls->mod_value_size - scls->mod_value_remaining),
601 &pmsg[1], psize - sizeof (*pmsg));
602 scls->mod_value_remaining -= psize - sizeof (*pmsg);
603 if (0 == scls->mod_value_remaining)
604 {
605 db->state_modify_op (db->cls, &scls->channel_key,
606 scls->mod_oper, scls->mod_name,
607 scls->mod_value, scls->mod_value_size);
608 GNUNET_free (scls->mod_name);
609 GNUNET_free (scls->mod_value);
610 scls->mod_oper = 0;
611 scls->mod_name = NULL;
612 scls->mod_value = NULL;
613 scls->mod_value_size = 0;
614 }
615 }
616 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_MOD_CONT;
617 break;
618
619 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_DATA:
620 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_DATA;
621 break;
622
623 case GNUNET_MESSAGE_TYPE_PSYC_MESSAGE_END:
624 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_END;
625 break;
626
627 default:
628 scls->msg_state = GNUNET_PSYC_MESSAGE_STATE_ERROR;
629 }
630}
631
632
633static int
634recv_state_fragment (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg,
635 enum GNUNET_PSYCSTORE_MessageFlags flags)
636{
637 struct StateModifyClosure *scls = cls;
638
639 if (NULL == scls->recv)
640 {
641 scls->recv = GNUNET_PSYC_receive_create (NULL, recv_state_message_part,
642 scls);
643 }
644
645 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
646 "recv_state_fragment: %" PRIu64 "\n", GNUNET_ntohll (msg->fragment_id));
647
648 struct GNUNET_PSYC_MessageHeader *
649 pmsg = GNUNET_PSYC_message_header_create (msg, flags);
650 GNUNET_PSYC_receive_message (scls->recv, pmsg);
651 GNUNET_free (pmsg);
652
653 return GNUNET_YES;
654}
655
656
657static void
658handle_client_state_modify (void *cls,
659 const struct StateModifyRequest *req)
660{
661 struct GNUNET_SERVICE_Client *client = cls;
662
663 uint64_t message_id = GNUNET_ntohll (req->message_id);
664 uint64_t state_delta = GNUNET_ntohll (req->state_delta);
665 uint64_t ret_frags = 0;
666 struct StateModifyClosure
667 scls = { .channel_key = req->channel_key };
668
669 int ret = db->state_modify_begin (db->cls, &req->channel_key,
670 message_id, state_delta);
671
672 if (GNUNET_OK != ret)
673 {
674 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
675 _("Failed to begin modifying state: %d\n"), ret);
676 }
677 else
678 {
679 ret = db->message_get (db->cls, &req->channel_key,
680 message_id, message_id, 0,
681 &ret_frags, recv_state_fragment, &scls);
682 if (GNUNET_OK != ret)
683 {
684 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685 _("Failed to modify state: %d\n"), ret);
686 GNUNET_break (0);
687 }
688 else
689 {
690 if (GNUNET_OK != db->state_modify_end (db->cls, &req->channel_key, message_id))
691 {
692 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693 _("Failed to end modifying state!\n"));
694 GNUNET_break (0);
695 }
696 }
697 if (NULL != scls.recv)
698 {
699 GNUNET_PSYC_receive_destroy (scls.recv);
700 }
701 }
702
703 send_result_code (client, req->op_id, ret, NULL);
704 GNUNET_SERVICE_client_continue (client);
705}
706
707
708static int
709check_client_state_sync (void *cls,
710 const struct StateSyncRequest *req)
711{
712 return GNUNET_OK;
713}
714
715
716/** @todo FIXME: stop processing further state sync messages after an error */
717static void
718handle_client_state_sync (void *cls,
719 const struct StateSyncRequest *req)
720{
721 struct GNUNET_SERVICE_Client *client = cls;
722
723 int ret = GNUNET_SYSERR;
724 const char *name = (const char *) &req[1];
725 uint16_t name_size = ntohs (req->name_size);
726
727 if (name_size <= 2 || '\0' != name[name_size - 1])
728 {
729 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
730 _("Tried to set invalid state variable name!\n"));
731 GNUNET_break_op (0);
732 }
733 else
734 {
735 ret = GNUNET_OK;
736
737 if (req->flags & STATE_OP_FIRST)
738 {
739 ret = db->state_sync_begin (db->cls, &req->channel_key);
740 }
741 if (ret != GNUNET_OK)
742 {
743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
744 _("Failed to begin synchronizing state!\n"));
745 }
746 else
747 {
748 ret = db->state_sync_assign (db->cls, &req->channel_key, name,
749 name + ntohs (req->name_size),
750 ntohs (req->header.size) - sizeof (*req)
751 - ntohs (req->name_size));
752 }
753
754 if (GNUNET_OK == ret && req->flags & STATE_OP_LAST)
755 {
756 ret = db->state_sync_end (db->cls, &req->channel_key,
757 GNUNET_ntohll (req->max_state_message_id),
758 GNUNET_ntohll (req->state_hash_message_id));
759 if (ret != GNUNET_OK)
760 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
761 _("Failed to end synchronizing state!\n"));
762 }
763 }
764 send_result_code (client, req->op_id, ret, NULL);
765 GNUNET_SERVICE_client_continue (client);
766}
767
768
769static void
770handle_client_state_reset (void *cls,
771 const struct OperationRequest *req)
772{
773 struct GNUNET_SERVICE_Client *client = cls;
774
775 int ret = db->state_reset (db->cls, &req->channel_key);
776
777 if (ret != GNUNET_OK)
778 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
779 _("Failed to reset state!\n"));
780
781 send_result_code (client, req->op_id, ret, NULL);
782 GNUNET_SERVICE_client_continue (client);
783}
784
785
786static void
787handle_client_state_hash_update (void *cls,
788 const struct StateHashUpdateRequest *req)
789{
790 struct GNUNET_SERVICE_Client *client = cls;
791
792 int ret = db->state_reset (db->cls, &req->channel_key);
793 if (ret != GNUNET_OK)
794 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
795 _("Failed to reset state!\n"));
796
797 send_result_code (client, req->op_id, ret, NULL);
798 GNUNET_SERVICE_client_continue (client);
799}
800
801
802static int
803check_client_state_get (void *cls,
804 const struct OperationRequest *req)
805{
806 return GNUNET_OK;
807}
808
809
810static void
811handle_client_state_get (void *cls,
812 const struct OperationRequest *req)
813{
814 struct GNUNET_SERVICE_Client *client = cls;
815
816 struct SendClosure sc = { .op_id = req->op_id, .client = client };
817 int64_t ret = GNUNET_SYSERR;
818 const char *name = (const char *) &req[1];
819 uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
820
821 if (name_size <= 2 || '\0' != name[name_size - 1])
822 {
823 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
824 _("Tried to get invalid state variable name!\n"));
825 GNUNET_break (0);
826 }
827 else
828 {
829 ret = db->state_get (db->cls, &req->channel_key, name,
830 &send_state_var, &sc);
831 if (GNUNET_NO == ret && name_size >= 5) /* min: _a_b\0 */
832 {
833 char *p, *n = GNUNET_malloc (name_size);
834 GNUNET_memcpy (n, name, name_size);
835 while (&n[1] < (p = strrchr (n, '_')) && GNUNET_NO == ret)
836 {
837 *p = '\0';
838 ret = db->state_get (db->cls, &req->channel_key, n,
839 &send_state_var, &sc);
840 }
841 GNUNET_free (n);
842 }
843 }
844 switch (ret)
845 {
846 case GNUNET_OK:
847 case GNUNET_NO:
848 break;
849 default:
850 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
851 _("Failed to get state variable!\n"));
852 }
853
854 send_result_code (client, req->op_id, ret, NULL);
855 GNUNET_SERVICE_client_continue (client);
856}
857
858
859static int
860check_client_state_get_prefix (void *cls,
861 const struct OperationRequest *req)
862{
863 return GNUNET_OK;
864}
865
866
867static void
868handle_client_state_get_prefix (void *cls,
869 const struct OperationRequest *req)
870{
871 struct GNUNET_SERVICE_Client *client = cls;
872
873 struct SendClosure sc = { .op_id = req->op_id, .client = client };
874 int64_t ret = GNUNET_SYSERR;
875 const char *name = (const char *) &req[1];
876 uint16_t name_size = ntohs (req->header.size) - sizeof (*req);
877
878 if (name_size <= 1 || '\0' != name[name_size - 1])
879 {
880 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
881 _("Tried to get invalid state variable name!\n"));
882 GNUNET_break (0);
883 }
884 else
885 {
886 ret = db->state_get_prefix (db->cls, &req->channel_key, name,
887 &send_state_var, &sc);
888 }
889 switch (ret)
890 {
891 case GNUNET_OK:
892 case GNUNET_NO:
893 break;
894 default:
895 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
896 _("Failed to get state variable!\n"));
897 }
898
899 send_result_code (client, req->op_id, ret, NULL);
900 GNUNET_SERVICE_client_continue (client);
901}
902
903
904/**
905 * A new client connected.
906 *
907 * @param cls NULL
908 * @param client client to add
909 * @param mq message queue for @a client
910 * @return @a client
911 */
912static void *
913client_notify_connect (void *cls,
914 struct GNUNET_SERVICE_Client *client,
915 struct GNUNET_MQ_Handle *mq)
916{
917 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Client connected: %p\n", client);
918
919 return client;
920}
921
922
923/**
924 * Called whenever a client is disconnected.
925 * Frees our resources associated with that client.
926 *
927 * @param cls closure
928 * @param client identification of the client
929 * @param app_ctx must match @a client
930 */
931static void
932client_notify_disconnect (void *cls,
933 struct GNUNET_SERVICE_Client *client,
934 void *app_ctx)
935{
936}
937
938
939/**
940 * Initialize the PSYCstore service.
941 *
942 * @param cls Closure.
943 * @param server The initialized server.
944 * @param c Configuration to use.
945 */
946static void
947run (void *cls,
948 const struct GNUNET_CONFIGURATION_Handle *c,
949 struct GNUNET_SERVICE_Handle *svc)
950{
951 cfg = c;
952 service = svc;
953
954 /* Loading database plugin */
955 char *database;
956 if (GNUNET_OK !=
957 GNUNET_CONFIGURATION_get_value_string (cfg, "psycstore", "database",
958 &database))
959 {
960 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
961 "psycstore",
962 "database");
963 }
964 else
965 {
966 GNUNET_asprintf (&db_lib_name,
967 "libgnunet_plugin_psycstore_%s",
968 database);
969 db = GNUNET_PLUGIN_load (db_lib_name, (void *) cfg);
970 GNUNET_free (database);
971 }
972 if (NULL == db)
973 {
974 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
975 "Could not load database backend `%s'\n",
976 db_lib_name);
977 GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
978 return;
979 }
980
981 stats = GNUNET_STATISTICS_create ("psycstore", cfg);
982 GNUNET_SCHEDULER_add_shutdown (shutdown_task,
983 NULL);
984}
985
986/**
987 * Define "main" method using service macro.
988 */
989GNUNET_SERVICE_MAIN
990("psycstore",
991 GNUNET_SERVICE_OPTION_NONE,
992 run,
993 client_notify_connect,
994 client_notify_disconnect,
995 NULL,
996 GNUNET_MQ_hd_fixed_size (client_membership_store,
997 GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE,
998 struct MembershipStoreRequest,
999 NULL),
1000 GNUNET_MQ_hd_fixed_size (client_membership_test,
1001 GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST,
1002 struct MembershipTestRequest,
1003 NULL),
1004 GNUNET_MQ_hd_var_size (client_fragment_store,
1005 GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE,
1006 struct FragmentStoreRequest,
1007 NULL),
1008 GNUNET_MQ_hd_fixed_size (client_fragment_get,
1009 GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET,
1010 struct FragmentGetRequest,
1011 NULL),
1012 GNUNET_MQ_hd_var_size (client_message_get,
1013 GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET,
1014 struct MessageGetRequest,
1015 NULL),
1016 GNUNET_MQ_hd_fixed_size (client_message_get_fragment,
1017 GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT,
1018 struct MessageGetFragmentRequest,
1019 NULL),
1020 GNUNET_MQ_hd_fixed_size (client_counters_get,
1021 GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET,
1022 struct OperationRequest,
1023 NULL),
1024 GNUNET_MQ_hd_fixed_size (client_state_modify,
1025 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY,
1026 struct StateModifyRequest,
1027 NULL),
1028 GNUNET_MQ_hd_var_size (client_state_sync,
1029 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC,
1030 struct StateSyncRequest,
1031 NULL),
1032 GNUNET_MQ_hd_fixed_size (client_state_reset,
1033 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET,
1034 struct OperationRequest,
1035 NULL),
1036 GNUNET_MQ_hd_fixed_size (client_state_hash_update,
1037 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE,
1038 struct StateHashUpdateRequest,
1039 NULL),
1040 GNUNET_MQ_hd_var_size (client_state_get,
1041 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET,
1042 struct OperationRequest,
1043 NULL),
1044 GNUNET_MQ_hd_var_size (client_state_get_prefix,
1045 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX,
1046 struct OperationRequest,
1047 NULL));
1048
1049/* end of gnunet-service-psycstore.c */
diff --git a/src/psycstore/plugin_psycstore_mysql.c b/src/psycstore/plugin_psycstore_mysql.c
new file mode 100644
index 0000000..c36b6f7
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_mysql.c
@@ -0,0 +1,1960 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file psycstore/plugin_psycstore_mysql.c
23 * @brief mysql-based psycstore backend
24 * @author Gabor X Toth
25 * @author Christian Grothoff
26 * @author Christophe Genevey
27 */
28
29#include "platform.h"
30#include "gnunet_psycstore_plugin.h"
31#include "gnunet_psycstore_service.h"
32#include "gnunet_multicast_service.h"
33#include "gnunet_crypto_lib.h"
34#include "gnunet_psyc_util_lib.h"
35#include "psycstore.h"
36#include "gnunet_my_lib.h"
37#include "gnunet_mysql_lib.h"
38#include <mysql/mysql.h>
39
40/**
41 * After how many ms "busy" should a DB operation fail for good? A
42 * low value makes sure that we are more responsive to requests
43 * (especially PUTs). A high value guarantees a higher success rate
44 * (SELECTs in iterate can take several seconds despite LIMIT=1).
45 *
46 * The default value of 1s should ensure that users do not experience
47 * huge latencies while at the same time allowing operations to
48 * succeed with reasonable probability.
49 */
50#define BUSY_TIMEOUT_MS 1000
51
52#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
53
54/**
55 * Log an error message at log-level 'level' that indicates
56 * a failure of the command 'cmd' on file 'filename'
57 * with the message given by strerror(errno).
58 */
59#define LOG_MYSQL(db, level, cmd, stmt) \
60 do { \
61 GNUNET_log_from (level, "psycstore-mysql", \
62 _("`%s' failed at %s:%d with error: %s\n"), \
63 cmd, __FILE__, __LINE__, \
64 mysql_stmt_error (GNUNET_MYSQL_statement_get_stmt(stmt))); \
65 } while (0)
66
67#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-mysql", __VA_ARGS__)
68
69enum Transactions {
70 TRANSACTION_NONE = 0,
71 TRANSACTION_STATE_MODIFY,
72 TRANSACTION_STATE_SYNC,
73};
74
75/**
76 * Context for all functions in this plugin.
77 */
78struct Plugin
79{
80
81 const struct GNUNET_CONFIGURATION_Handle *cfg;
82
83 /**
84 * MySQL context.
85 */
86 struct GNUNET_MYSQL_Context *mc;
87
88 /**
89 * Current transaction.
90 */
91 enum Transactions transaction;
92
93 /**
94 * Precompiled SQL for channel_key_store()
95 */
96 struct GNUNET_MYSQL_StatementHandle *insert_channel_key;
97
98 /**
99 * Precompiled SQL for slave_key_store()
100 */
101 struct GNUNET_MYSQL_StatementHandle *insert_slave_key;
102
103 /**
104 * Precompiled SQL for membership_store()
105 */
106 struct GNUNET_MYSQL_StatementHandle *insert_membership;
107
108 /**
109 * Precompiled SQL for membership_test()
110 */
111 struct GNUNET_MYSQL_StatementHandle *select_membership;
112
113 /**
114 * Precompiled SQL for fragment_store()
115 */
116 struct GNUNET_MYSQL_StatementHandle *insert_fragment;
117
118 /**
119 * Precompiled SQL for message_add_flags()
120 */
121 struct GNUNET_MYSQL_StatementHandle *update_message_flags;
122
123 /**
124 * Precompiled SQL for fragment_get()
125 */
126 struct GNUNET_MYSQL_StatementHandle *select_fragments;
127
128 /**
129 * Precompiled SQL for fragment_get()
130 */
131 struct GNUNET_MYSQL_StatementHandle *select_latest_fragments;
132
133 /**
134 * Precompiled SQL for message_get()
135 */
136 struct GNUNET_MYSQL_StatementHandle *select_messages;
137
138 /**
139 * Precompiled SQL for message_get()
140 */
141 struct GNUNET_MYSQL_StatementHandle *select_latest_messages;
142
143 /**
144 * Precompiled SQL for message_get_fragment()
145 */
146 struct GNUNET_MYSQL_StatementHandle *select_message_fragment;
147
148 /**
149 * Precompiled SQL for counters_get_message()
150 */
151 struct GNUNET_MYSQL_StatementHandle *select_counters_message;
152
153 /**
154 * Precompiled SQL for counters_get_state()
155 */
156 struct GNUNET_MYSQL_StatementHandle *select_counters_state;
157
158 /**
159 * Precompiled SQL for state_modify_end()
160 */
161 struct GNUNET_MYSQL_StatementHandle *update_state_hash_message_id;
162
163 /**
164 * Precompiled SQL for state_sync_end()
165 */
166 struct GNUNET_MYSQL_StatementHandle *update_max_state_message_id;
167
168 /**
169 * Precompiled SQL for state_modify_op()
170 */
171 struct GNUNET_MYSQL_StatementHandle *insert_state_current;
172
173 /**
174 * Precompiled SQL for state_modify_end()
175 */
176 struct GNUNET_MYSQL_StatementHandle *delete_state_empty;
177
178 /**
179 * Precompiled SQL for state_set_signed()
180 */
181 struct GNUNET_MYSQL_StatementHandle *update_state_signed;
182
183 /**
184 * Precompiled SQL for state_sync()
185 */
186 struct GNUNET_MYSQL_StatementHandle *insert_state_sync;
187
188 /**
189 * Precompiled SQL for state_sync()
190 */
191 struct GNUNET_MYSQL_StatementHandle *delete_state;
192
193 /**
194 * Precompiled SQL for state_sync()
195 */
196 struct GNUNET_MYSQL_StatementHandle *insert_state_from_sync;
197
198 /**
199 * Precompiled SQL for state_sync()
200 */
201 struct GNUNET_MYSQL_StatementHandle *delete_state_sync;
202
203 /**
204 * Precompiled SQL for state_get_signed()
205 */
206 struct GNUNET_MYSQL_StatementHandle *select_state_signed;
207
208 /**
209 * Precompiled SQL for state_get()
210 */
211 struct GNUNET_MYSQL_StatementHandle *select_state_one;
212
213 /**
214 * Precompiled SQL for state_get_prefix()
215 */
216 struct GNUNET_MYSQL_StatementHandle *select_state_prefix;
217
218};
219
220#if DEBUG_PSYCSTORE
221
222static void
223mysql_trace (void *cls, const char *sql)
224{
225 LOG(GNUNET_ERROR_TYPE_DEBUG, "MYSQL query:\n%s\n", sql);
226}
227
228#endif
229
230
231/**
232 * @brief Prepare a SQL statement
233 *
234 * @param dbh handle to the database
235 * @param sql SQL statement, UTF-8 encoded
236 * @param stmt set to the prepared statement
237 * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
238 */
239static int
240mysql_prepare (struct GNUNET_MYSQL_Context *mc,
241 const char *sql,
242 struct GNUNET_MYSQL_StatementHandle **stmt)
243{
244 *stmt = GNUNET_MYSQL_statement_prepare (mc,
245 sql);
246
247 if (NULL == *stmt)
248 {
249 LOG (GNUNET_ERROR_TYPE_ERROR,
250 _("Error preparing SQL query: %s\n %s\n"),
251 mysql_stmt_error (GNUNET_MYSQL_statement_get_stmt (*stmt)),
252 sql);
253 return GNUNET_SYSERR;
254 }
255 LOG (GNUNET_ERROR_TYPE_DEBUG,
256 "Prepared `%s' / %p\n",
257 sql,
258 stmt);
259 return GNUNET_OK;
260}
261
262
263/**
264 * Initialize the database connections and associated
265 * data structures (create tables and indices
266 * as needed as well).
267 *
268 * @param plugin the plugin context (state for this module)
269 * @return #GNUNET_OK on success
270 */
271static int
272database_setup (struct Plugin *plugin)
273{
274 /* Open database and precompile statements */
275 plugin->mc = GNUNET_MYSQL_context_create (plugin->cfg,
276 "psycstore-mysql");
277
278 if (NULL == plugin->mc)
279 {
280 LOG (GNUNET_ERROR_TYPE_ERROR,
281 _("Unable to initialize Mysql.\n"));
282 return GNUNET_SYSERR;
283 }
284
285#define STMT_RUN(sql) \
286 if (GNUNET_OK != \
287 GNUNET_MYSQL_statement_run (plugin->mc, \
288 sql)) \
289 { \
290 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, \
291 _("Failed to run SQL statement `%s'\n"), \
292 sql); \
293 return GNUNET_SYSERR; \
294 }
295
296 /* Create tables */
297 STMT_RUN ("CREATE TABLE IF NOT EXISTS channels (\n"
298 " id BIGINT UNSIGNED AUTO_INCREMENT,\n"
299 " pub_key BLOB(32),\n"
300 " max_state_message_id BIGINT UNSIGNED,\n"
301 " state_hash_message_id BIGINT UNSIGNED,\n"
302 " PRIMARY KEY(id),\n"
303 " UNIQUE KEY(pub_key(32))\n"
304 ");");
305
306 STMT_RUN ("CREATE TABLE IF NOT EXISTS slaves (\n"
307 " id BIGINT UNSIGNED AUTO_INCREMENT,\n"
308 " pub_key BLOB(32),\n"
309 " PRIMARY KEY(id),\n"
310 " UNIQUE KEY(pub_key(32))\n"
311 ");");
312
313 STMT_RUN ("CREATE TABLE IF NOT EXISTS membership (\n"
314 " channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
315 " slave_id BIGINT UNSIGNED NOT NULL REFERENCES slaves(id),\n"
316 " did_join TINYINT NOT NULL,\n"
317 " announced_at BIGINT UNSIGNED NOT NULL,\n"
318 " effective_since BIGINT UNSIGNED NOT NULL,\n"
319 " group_generation BIGINT UNSIGNED NOT NULL\n"
320 ");");
321
322/*** FIX because IF NOT EXISTS doesn't work ***/
323 GNUNET_MYSQL_statement_run (plugin->mc,
324 "CREATE INDEX idx_membership_channel_id_slave_id "
325 "ON membership (channel_id, slave_id);");
326
327 /** @todo messages table: add method_name column */
328 STMT_RUN ("CREATE TABLE IF NOT EXISTS messages (\n"
329 " channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
330 " hop_counter BIGINT UNSIGNED NOT NULL,\n"
331 " signature BLOB,\n"
332 " purpose BLOB,\n"
333 " fragment_id BIGINT UNSIGNED NOT NULL,\n"
334 " fragment_offset BIGINT UNSIGNED NOT NULL,\n"
335 " message_id BIGINT UNSIGNED NOT NULL,\n"
336 " group_generation BIGINT UNSIGNED NOT NULL,\n"
337 " multicast_flags BIGINT UNSIGNED NOT NULL,\n"
338 " psycstore_flags BIGINT UNSIGNED NOT NULL,\n"
339 " data BLOB,\n"
340 " PRIMARY KEY (channel_id, fragment_id),\n"
341 " UNIQUE KEY(channel_id, message_id, fragment_offset)\n"
342 ");");
343
344 STMT_RUN ("CREATE TABLE IF NOT EXISTS state (\n"
345 " channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
346 " name TEXT NOT NULL,\n"
347 " value_current BLOB,\n"
348 " value_signed BLOB\n"
349 //" PRIMARY KEY (channel_id, name(255))\n"
350 ");");
351
352 STMT_RUN ("CREATE TABLE IF NOT EXISTS state_sync (\n"
353 " channel_id BIGINT UNSIGNED NOT NULL REFERENCES channels(id),\n"
354 " name TEXT NOT NULL,\n"
355 " value BLOB\n"
356 //" PRIMARY KEY (channel_id, name(255))\n"
357 ");");
358#undef STMT_RUN
359
360 /* Prepare statements */
361#define PREP(stmt,handle) \
362 if (GNUNET_OK != mysql_prepare (plugin->mc, stmt, handle)) \
363 { \
364 GNUNET_break (0); \
365 return GNUNET_SYSERR; \
366 }
367 PREP ("INSERT IGNORE INTO channels (pub_key) VALUES (?);",
368 &plugin->insert_channel_key);
369 PREP ("INSERT IGNORE INTO slaves (pub_key) VALUES (?);",
370 &plugin->insert_slave_key);
371 PREP ("INSERT INTO membership\n"
372 " (channel_id, slave_id, did_join, announced_at,\n"
373 " effective_since, group_generation)\n"
374 "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
375 " (SELECT id FROM slaves WHERE pub_key = ?),\n"
376 " ?, ?, ?, ?);",
377 &plugin->insert_membership);
378 PREP ("SELECT did_join FROM membership\n"
379 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
380 " AND slave_id = (SELECT id FROM slaves WHERE pub_key = ?)\n"
381 " AND effective_since <= ? AND did_join = 1\n"
382 "ORDER BY announced_at DESC LIMIT 1;",
383 &plugin->select_membership);
384
385 PREP ("INSERT IGNORE INTO messages\n"
386 " (channel_id, hop_counter, signature, purpose,\n"
387 " fragment_id, fragment_offset, message_id,\n"
388 " group_generation, multicast_flags, psycstore_flags, data)\n"
389 "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
390 " ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
391 &plugin->insert_fragment);
392
393 PREP ("UPDATE messages\n"
394 "SET psycstore_flags = psycstore_flags | ?\n"
395 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
396 " AND message_id = ? AND fragment_offset = 0;",
397 &plugin->update_message_flags);
398
399 PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
400 " fragment_offset, message_id, group_generation,\n"
401 " multicast_flags, psycstore_flags, data\n"
402 "FROM messages\n"
403 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
404 " AND ? <= fragment_id AND fragment_id <= ? LIMIT 1;",
405 &plugin->select_fragments);
406
407 /** @todo select_messages: add method_prefix filter */
408 PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
409 " fragment_offset, message_id, group_generation,\n"
410 " multicast_flags, psycstore_flags, data\n"
411 "FROM messages\n"
412 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
413 " AND ? <= message_id AND message_id <= ?\n"
414 "LIMIT ?;",
415 &plugin->select_messages);
416
417 PREP ("SELECT * FROM\n"
418 "(SELECT hop_counter, signature, purpose, fragment_id,\n"
419 " fragment_offset, message_id, group_generation,\n"
420 " multicast_flags, psycstore_flags, data\n"
421 " FROM messages\n"
422 " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
423 " ORDER BY fragment_id DESC\n"
424 " LIMIT ?)\n"
425 "ORDER BY fragment_id;",
426 &plugin->select_latest_fragments);
427
428 /** @todo select_latest_messages: add method_prefix filter */
429 PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
430 " fragment_offset, message_id, group_generation,\n"
431 " multicast_flags, psycstore_flags, data\n"
432 "FROM messages\n"
433 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
434 " AND message_id IN\n"
435 " (SELECT message_id\n"
436 " FROM messages\n"
437 " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
438 " GROUP BY message_id\n"
439 " ORDER BY message_id\n"
440 " DESC LIMIT ?)\n"
441 "ORDER BY fragment_id;",
442 &plugin->select_latest_messages);
443
444 PREP ("SELECT hop_counter, signature, purpose, fragment_id,\n"
445 " fragment_offset, message_id, group_generation,\n"
446 " multicast_flags, psycstore_flags, data\n"
447 "FROM messages\n"
448 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
449 " AND message_id = ? AND fragment_offset = ?;",
450 &plugin->select_message_fragment);
451
452 PREP ("SELECT fragment_id, message_id, group_generation\n"
453 "FROM messages\n"
454 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
455 "ORDER BY fragment_id DESC LIMIT 1;",
456 &plugin->select_counters_message);
457
458 PREP ("SELECT max_state_message_id\n"
459 "FROM channels\n"
460 "WHERE pub_key = ? AND max_state_message_id IS NOT NULL;",
461 &plugin->select_counters_state);
462
463 PREP ("UPDATE channels\n"
464 "SET max_state_message_id = ?\n"
465 "WHERE pub_key = ?;",
466 &plugin->update_max_state_message_id);
467
468 PREP ("UPDATE channels\n"
469 "SET state_hash_message_id = ?\n"
470 "WHERE pub_key = ?;",
471 &plugin->update_state_hash_message_id);
472
473 PREP ("REPLACE INTO state\n"
474 " (channel_id, name, value_current, value_signed)\n"
475 "SELECT new.channel_id, new.name, new.value_current, old.value_signed\n"
476 "FROM (SELECT (SELECT id FROM channels WHERE pub_key = ?) AS channel_id,\n"
477 " (SELECT ?) AS name,\n"
478 " (SELECT ?) AS value_current\n"
479 " ) AS new\n"
480 "LEFT JOIN (SELECT channel_id, name, value_signed\n"
481 " FROM state) AS old\n"
482 "ON new.channel_id = old.channel_id AND new.name = old.name;",
483 &plugin->insert_state_current);
484
485 PREP ("DELETE FROM state\n"
486 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
487 " AND (value_current IS NULL OR length(value_current) = 0)\n"
488 " AND (value_signed IS NULL OR length(value_signed) = 0);",
489 &plugin->delete_state_empty);
490
491 PREP ("UPDATE state\n"
492 "SET value_signed = value_current\n"
493 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
494 &plugin->update_state_signed);
495
496 PREP ("DELETE FROM state\n"
497 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
498 &plugin->delete_state);
499
500 PREP ("INSERT INTO state_sync (channel_id, name, value)\n"
501 "VALUES ((SELECT id FROM channels WHERE pub_key = ?), ?, ?);",
502 &plugin->insert_state_sync);
503
504 PREP ("INSERT INTO state\n"
505 " (channel_id, name, value_current, value_signed)\n"
506 "SELECT channel_id, name, value, value\n"
507 "FROM state_sync\n"
508 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
509 &plugin->insert_state_from_sync);
510
511 PREP ("DELETE FROM state_sync\n"
512 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
513 &plugin->delete_state_sync);
514
515 PREP ("SELECT value_current\n"
516 "FROM state\n"
517 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
518 " AND name = ?;",
519 &plugin->select_state_one);
520
521 PREP ("SELECT name, value_current\n"
522 "FROM state\n"
523 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
524 " AND (name = ? OR substr(name, 1, ?) = ?);",
525 &plugin->select_state_prefix);
526
527 PREP ("SELECT name, value_signed\n"
528 "FROM state\n"
529 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)"
530 " AND value_signed IS NOT NULL;",
531 &plugin->select_state_signed);
532#undef PREP
533
534 return GNUNET_OK;
535}
536
537
538/**
539 * Shutdown database connection and associate data
540 * structures.
541 * @param plugin the plugin context (state for this module)
542 */
543static void
544database_shutdown (struct Plugin *plugin)
545{
546 GNUNET_MYSQL_context_destroy (plugin->mc);
547}
548
549
550/**
551 * Execute a prepared statement with a @a channel_key argument.
552 *
553 * @param plugin Plugin handle.
554 * @param stmt Statement to execute.
555 * @param channel_key Public key of the channel.
556 *
557 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
558 */
559static int
560exec_channel (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
561 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
562{
563 struct GNUNET_MY_QueryParam params[] = {
564 GNUNET_MY_query_param_auto_from_type (channel_key),
565 GNUNET_MY_query_param_end
566 };
567
568 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
569 {
570 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
571 "mysql exec_channel", stmt);
572 }
573
574 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
575 {
576 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
577 "mysql_stmt_reset", stmt);
578 return GNUNET_SYSERR;
579 }
580
581 return GNUNET_OK;
582}
583
584
585/**
586 * Begin a transaction.
587 */
588static int
589transaction_begin (struct Plugin *plugin, enum Transactions transaction)
590{
591 if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "BEGIN"))
592 {
593 LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_begin failed");
594 return GNUNET_SYSERR;
595 }
596
597 plugin->transaction = transaction;
598 return GNUNET_OK;
599}
600
601
602/**
603 * Commit current transaction.
604 */
605static int
606transaction_commit (struct Plugin *plugin)
607{
608 if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "COMMIT"))
609 {
610 LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_commit failed");
611 return GNUNET_SYSERR;
612 }
613
614 plugin->transaction = TRANSACTION_NONE;
615 return GNUNET_OK;
616}
617
618
619/**
620 * Roll back current transaction.
621 */
622static int
623transaction_rollback (struct Plugin *plugin)
624{
625 if (GNUNET_OK != GNUNET_MYSQL_statement_run (plugin->mc, "ROLLBACK"))
626 {
627 LOG(GNUNET_ERROR_TYPE_ERROR, "transaction_rollback failed");
628 return GNUNET_SYSERR;
629 }
630
631 plugin->transaction = TRANSACTION_NONE;
632 return GNUNET_OK;
633}
634
635
636static int
637channel_key_store (struct Plugin *plugin,
638 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
639{
640 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_channel_key;
641
642 struct GNUNET_MY_QueryParam params[] = {
643 GNUNET_MY_query_param_auto_from_type (channel_key),
644 GNUNET_MY_query_param_end
645 };
646
647 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
648 {
649 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
650 "mysql exec_prepared", stmt);
651 return GNUNET_SYSERR;
652 }
653
654 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
655 {
656 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
657 "mysql_stmt_reset", stmt);
658 return GNUNET_SYSERR;
659 }
660
661 return GNUNET_OK;
662}
663
664
665static int
666slave_key_store (struct Plugin *plugin,
667 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
668{
669 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_slave_key;
670
671 struct GNUNET_MY_QueryParam params[] = {
672 GNUNET_MY_query_param_auto_from_type (slave_key),
673 GNUNET_MY_query_param_end
674 };
675
676 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
677 {
678 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
679 "mysql exec_prepared", stmt);
680 return GNUNET_SYSERR;
681 }
682
683 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
684 {
685 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
686 "mysql_stmt_reset", stmt);
687 return GNUNET_SYSERR;
688 }
689
690 return GNUNET_OK;
691}
692
693
694/**
695 * Store join/leave events for a PSYC channel in order to be able to answer
696 * membership test queries later.
697 *
698 * @see GNUNET_PSYCSTORE_membership_store()
699 *
700 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
701 */
702static int
703mysql_membership_store (void *cls,
704 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
705 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
706 int did_join,
707 uint64_t announced_at,
708 uint64_t effective_since,
709 uint64_t group_generation)
710{
711 struct Plugin *plugin = cls;
712
713 uint32_t idid_join = (uint32_t)did_join;
714
715 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_membership;
716
717 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
718
719 if (announced_at > INT64_MAX ||
720 effective_since > INT64_MAX ||
721 group_generation > INT64_MAX)
722 {
723 GNUNET_break (0);
724 return GNUNET_SYSERR;
725 }
726
727 if (GNUNET_OK != channel_key_store (plugin, channel_key)
728 || GNUNET_OK != slave_key_store (plugin, slave_key))
729 return GNUNET_SYSERR;
730
731 struct GNUNET_MY_QueryParam params[] = {
732 GNUNET_MY_query_param_auto_from_type (channel_key),
733 GNUNET_MY_query_param_auto_from_type (slave_key),
734 GNUNET_MY_query_param_uint32 (&idid_join),
735 GNUNET_MY_query_param_uint64 (&announced_at),
736 GNUNET_MY_query_param_uint64 (&effective_since),
737 GNUNET_MY_query_param_uint64 (&group_generation),
738 GNUNET_MY_query_param_end
739 };
740
741 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params))
742 {
743 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
744 "mysql exec_prepared", stmt);
745 return GNUNET_SYSERR;
746 }
747
748 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
749 {
750 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
751 "mysql_stmt_reset", stmt);
752 return GNUNET_SYSERR;
753 }
754 return GNUNET_OK;
755}
756
757/**
758 * Test if a member was admitted to the channel at the given message ID.
759 *
760 * @see GNUNET_PSYCSTORE_membership_test()
761 *
762 * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
763 * #GNUNET_SYSERR if there was en error.
764 */
765static int
766membership_test (void *cls,
767 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
768 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
769 uint64_t message_id)
770{
771 struct Plugin *plugin = cls;
772
773 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_membership;
774
775 uint32_t did_join = 0;
776
777 int ret = GNUNET_SYSERR;
778
779 struct GNUNET_MY_QueryParam params_select[] = {
780 GNUNET_MY_query_param_auto_from_type (channel_key),
781 GNUNET_MY_query_param_auto_from_type (slave_key),
782 GNUNET_MY_query_param_uint64 (&message_id),
783 GNUNET_MY_query_param_end
784 };
785
786 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
787 {
788 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
789 "mysql execute prepared", stmt);
790 return GNUNET_SYSERR;
791 }
792
793 struct GNUNET_MY_ResultSpec results_select[] = {
794 GNUNET_MY_result_spec_uint32 (&did_join),
795 GNUNET_MY_result_spec_end
796 };
797
798 switch (GNUNET_MY_extract_result (stmt, results_select))
799 {
800 case GNUNET_NO:
801 ret = GNUNET_NO;
802 break;
803 case GNUNET_OK:
804 ret = GNUNET_YES;
805 break;
806 default:
807 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
808 "mysql extract_result", stmt);
809 return GNUNET_SYSERR;
810 }
811
812 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
813 {
814 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
815 "mysql_stmt_reset", stmt);
816 return GNUNET_SYSERR;
817 }
818
819 return ret;
820}
821
822/**
823 * Store a message fragment sent to a channel.
824 *
825 * @see GNUNET_PSYCSTORE_fragment_store()
826 *
827 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
828 */
829static int
830fragment_store (void *cls,
831 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
832 const struct GNUNET_MULTICAST_MessageHeader *msg,
833 uint32_t psycstore_flags)
834{
835 struct Plugin *plugin = cls;
836
837 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->insert_fragment;
838
839 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
840
841 uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
842
843 uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
844 uint64_t message_id = GNUNET_ntohll (msg->message_id);
845 uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
846
847 uint64_t hop_counter = ntohl(msg->hop_counter);
848 uint64_t flags = ntohl(msg->flags);
849
850 if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
851 message_id > INT64_MAX || group_generation > INT64_MAX)
852 {
853 LOG(GNUNET_ERROR_TYPE_ERROR,
854 "Tried to store fragment with a field > INT64_MAX: "
855 "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
856 message_id, group_generation);
857 GNUNET_break (0);
858 return GNUNET_SYSERR;
859 }
860
861 if (GNUNET_OK != channel_key_store (plugin, channel_key))
862 return GNUNET_SYSERR;
863
864 struct GNUNET_MY_QueryParam params_insert[] = {
865 GNUNET_MY_query_param_auto_from_type (channel_key),
866 GNUNET_MY_query_param_uint64 (&hop_counter),
867 GNUNET_MY_query_param_auto_from_type (&msg->signature),
868 GNUNET_MY_query_param_auto_from_type (&msg->purpose),
869 GNUNET_MY_query_param_uint64 (&fragment_id),
870 GNUNET_MY_query_param_uint64 (&fragment_offset),
871 GNUNET_MY_query_param_uint64 (&message_id),
872 GNUNET_MY_query_param_uint64 (&group_generation),
873 GNUNET_MY_query_param_uint64 (&flags),
874 GNUNET_MY_query_param_uint32 (&psycstore_flags),
875 GNUNET_MY_query_param_fixed_size (&msg[1], ntohs (msg->header.size)
876 - sizeof (*msg)),
877 GNUNET_MY_query_param_end
878 };
879
880 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_insert))
881 {
882 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
883 "mysql execute prepared", stmt);
884 return GNUNET_SYSERR;
885 }
886
887 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
888 {
889 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
890 "mysql_stmt_reset", stmt);
891 return GNUNET_SYSERR;
892 }
893
894 return GNUNET_OK;
895}
896
897/**
898 * Set additional flags for a given message.
899 *
900 * They are OR'd with any existing flags set.
901 *
902 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
903 */
904static int
905message_add_flags (void *cls,
906 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
907 uint64_t message_id,
908 uint32_t psycstore_flags)
909{
910 struct Plugin *plugin = cls;
911 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->update_message_flags;
912
913 int sql_ret;
914 int ret = GNUNET_SYSERR;
915
916 struct GNUNET_MY_QueryParam params_update[] = {
917 GNUNET_MY_query_param_uint32 (&psycstore_flags),
918 GNUNET_MY_query_param_auto_from_type (channel_key),
919 GNUNET_MY_query_param_uint64 (&message_id),
920 GNUNET_MY_query_param_end
921 };
922
923 sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params_update);
924 switch (sql_ret)
925 {
926 case GNUNET_OK:
927 ret = GNUNET_OK;
928 break;
929
930 default:
931 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
932 "mysql execute prepared", stmt);
933 }
934
935 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
936 {
937 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
938 "mysql_stmt_reset", stmt);
939 return GNUNET_SYSERR;
940 }
941
942 return ret;
943}
944
945
946static int
947fragment_row (struct GNUNET_MYSQL_StatementHandle *stmt,
948 GNUNET_PSYCSTORE_FragmentCallback cb,
949 void *cb_cls,
950 uint64_t *returned_fragments)
951{
952
953 uint32_t hop_counter;
954 void *signature = NULL;
955 void *purpose = NULL;
956 size_t signature_size;
957 size_t purpose_size;
958 uint64_t fragment_id;
959 uint64_t fragment_offset;
960 uint64_t message_id;
961 uint64_t group_generation;
962 uint64_t flags;
963 void *buf;
964 size_t buf_size;
965 int ret = GNUNET_SYSERR;
966 int sql_ret;
967 struct GNUNET_MULTICAST_MessageHeader *mp;
968 uint64_t msg_flags;
969 struct GNUNET_MY_ResultSpec results[] = {
970 GNUNET_MY_result_spec_uint32 (&hop_counter),
971 GNUNET_MY_result_spec_variable_size (&signature, &signature_size),
972 GNUNET_MY_result_spec_variable_size (&purpose, &purpose_size),
973 GNUNET_MY_result_spec_uint64 (&fragment_id),
974 GNUNET_MY_result_spec_uint64 (&fragment_offset),
975 GNUNET_MY_result_spec_uint64 (&message_id),
976 GNUNET_MY_result_spec_uint64 (&group_generation),
977 GNUNET_MY_result_spec_uint64 (&msg_flags),
978 GNUNET_MY_result_spec_uint64 (&flags),
979 GNUNET_MY_result_spec_variable_size (&buf,
980 &buf_size),
981 GNUNET_MY_result_spec_end
982 };
983
984 do
985 {
986 sql_ret = GNUNET_MY_extract_result (stmt, results);
987 switch (sql_ret)
988 {
989 case GNUNET_NO:
990 if (ret != GNUNET_YES)
991 ret = GNUNET_NO;
992 break;
993
994 case GNUNET_YES:
995 mp = GNUNET_malloc (sizeof (*mp) + buf_size);
996
997 mp->header.size = htons (sizeof (*mp) + buf_size);
998 mp->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
999 mp->hop_counter = htonl (hop_counter);
1000 GNUNET_memcpy (&mp->signature,
1001 signature,
1002 signature_size);
1003 GNUNET_memcpy (&mp->purpose,
1004 purpose,
1005 purpose_size);
1006 mp->fragment_id = GNUNET_htonll (fragment_id);
1007 mp->fragment_offset = GNUNET_htonll (fragment_offset);
1008 mp->message_id = GNUNET_htonll (message_id);
1009 mp->group_generation = GNUNET_htonll (group_generation);
1010 mp->flags = htonl(msg_flags);
1011
1012 GNUNET_memcpy (&mp[1],
1013 buf,
1014 buf_size);
1015 ret = cb (cb_cls, mp, (enum GNUNET_PSYCSTORE_MessageFlags) flags);
1016 if (NULL != returned_fragments)
1017 (*returned_fragments)++;
1018 GNUNET_MY_cleanup_result (results);
1019 break;
1020
1021 default:
1022 LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1023 "mysql extract_result", stmt);
1024 }
1025 }
1026 while (GNUNET_YES == sql_ret);
1027
1028 // for debugging
1029 if (GNUNET_NO == ret)
1030 GNUNET_log (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
1031 "Empty result set\n");
1032
1033 return ret;
1034}
1035
1036
1037static int
1038fragment_select (struct Plugin *plugin,
1039 struct GNUNET_MYSQL_StatementHandle *stmt,
1040 struct GNUNET_MY_QueryParam *params,
1041 uint64_t *returned_fragments,
1042 GNUNET_PSYCSTORE_FragmentCallback cb,
1043 void *cb_cls)
1044{
1045 int ret = GNUNET_SYSERR;
1046 int sql_ret;
1047
1048 sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params);
1049 switch (sql_ret)
1050 {
1051 case GNUNET_NO:
1052 if (ret != GNUNET_YES)
1053 ret = GNUNET_NO;
1054 break;
1055
1056 case GNUNET_YES:
1057 ret = fragment_row (stmt, cb, cb_cls, returned_fragments);
1058 break;
1059
1060 default:
1061 LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1062 "mysql exec_prepared", stmt);
1063 }
1064 return ret;
1065}
1066
1067
1068/**
1069 * Retrieve a message fragment range by fragment ID.
1070 *
1071 * @see GNUNET_PSYCSTORE_fragment_get()
1072 *
1073 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1074 */
1075static int
1076fragment_get (void *cls,
1077 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1078 uint64_t first_fragment_id,
1079 uint64_t last_fragment_id,
1080 uint64_t *returned_fragments,
1081 GNUNET_PSYCSTORE_FragmentCallback cb,
1082 void *cb_cls)
1083{
1084 struct Plugin *plugin = cls;
1085 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_fragments;
1086 int ret = GNUNET_SYSERR;
1087 struct GNUNET_MY_QueryParam params_select[] = {
1088 GNUNET_MY_query_param_auto_from_type (channel_key),
1089 GNUNET_MY_query_param_uint64 (&first_fragment_id),
1090 GNUNET_MY_query_param_uint64 (&last_fragment_id),
1091 GNUNET_MY_query_param_end
1092 };
1093
1094 *returned_fragments = 0;
1095 ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, cb_cls);
1096
1097 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1098 {
1099 LOG_MYSQL (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1100 "mysql_stmt_reset", stmt);
1101 return GNUNET_SYSERR;
1102 }
1103
1104 return ret;
1105}
1106
1107
1108/**
1109 * Retrieve a message fragment range by fragment ID.
1110 *
1111 * @see GNUNET_PSYCSTORE_fragment_get_latest()
1112 *
1113 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1114 */
1115static int
1116fragment_get_latest (void *cls,
1117 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1118 uint64_t fragment_limit,
1119 uint64_t *returned_fragments,
1120 GNUNET_PSYCSTORE_FragmentCallback cb,
1121 void *cb_cls)
1122{
1123 struct Plugin *plugin = cls;
1124
1125 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_latest_fragments;
1126
1127 int ret = GNUNET_SYSERR;
1128 *returned_fragments = 0;
1129
1130 struct GNUNET_MY_QueryParam params_select[] = {
1131 GNUNET_MY_query_param_auto_from_type (channel_key),
1132 GNUNET_MY_query_param_uint64 (&fragment_limit),
1133 GNUNET_MY_query_param_end
1134 };
1135
1136 ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, cb_cls);
1137
1138 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1139 {
1140 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1141 "mysql_stmt_reset", stmt);
1142 return GNUNET_SYSERR;
1143 }
1144
1145 return ret;
1146}
1147
1148
1149/**
1150 * Retrieve all fragments of a message ID range.
1151 *
1152 * @see GNUNET_PSYCSTORE_message_get()
1153 *
1154 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1155 */
1156static int
1157message_get (void *cls,
1158 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1159 uint64_t first_message_id,
1160 uint64_t last_message_id,
1161 uint64_t fragment_limit,
1162 uint64_t *returned_fragments,
1163 GNUNET_PSYCSTORE_FragmentCallback cb,
1164 void *cb_cls)
1165{
1166 struct Plugin *plugin = cls;
1167 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_messages;
1168 int ret;
1169
1170 if (0 == fragment_limit)
1171 fragment_limit = UINT64_MAX;
1172
1173 struct GNUNET_MY_QueryParam params_select[] = {
1174 GNUNET_MY_query_param_auto_from_type (channel_key),
1175 GNUNET_MY_query_param_uint64 (&first_message_id),
1176 GNUNET_MY_query_param_uint64 (&last_message_id),
1177 GNUNET_MY_query_param_uint64 (&fragment_limit),
1178 GNUNET_MY_query_param_end
1179 };
1180
1181 *returned_fragments = 0;
1182 ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, cb_cls);
1183
1184 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1185 {
1186 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1187 "mysql_stmt_reset", stmt);
1188 return GNUNET_SYSERR;
1189 }
1190
1191 return ret;
1192}
1193
1194
1195/**
1196 * Retrieve all fragments of the latest messages.
1197 *
1198 * @see GNUNET_PSYCSTORE_message_get_latest()
1199 *
1200 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1201 */
1202static int
1203message_get_latest (void *cls,
1204 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1205 uint64_t message_limit,
1206 uint64_t *returned_fragments,
1207 GNUNET_PSYCSTORE_FragmentCallback cb,
1208 void *cb_cls)
1209{
1210 struct Plugin *plugin = cls;
1211
1212 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_latest_messages;
1213
1214 int ret = GNUNET_SYSERR;
1215 *returned_fragments = 0;
1216
1217 struct GNUNET_MY_QueryParam params_select[] = {
1218 GNUNET_MY_query_param_auto_from_type (channel_key),
1219 GNUNET_MY_query_param_auto_from_type (channel_key),
1220 GNUNET_MY_query_param_uint64 (&message_limit),
1221 GNUNET_MY_query_param_end
1222 };
1223
1224 ret = fragment_select (plugin, stmt, params_select, returned_fragments, cb, cb_cls);
1225
1226 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1227 {
1228 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1229 "mysql_stmt_reset", stmt);
1230 return GNUNET_SYSERR;
1231 }
1232
1233 return ret;
1234}
1235
1236
1237/**
1238 * Retrieve a fragment of message specified by its message ID and fragment
1239 * offset.
1240 *
1241 * @see GNUNET_PSYCSTORE_message_get_fragment()
1242 *
1243 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1244 */
1245static int
1246message_get_fragment (void *cls,
1247 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1248 uint64_t message_id,
1249 uint64_t fragment_offset,
1250 GNUNET_PSYCSTORE_FragmentCallback cb,
1251 void *cb_cls)
1252{
1253 struct Plugin *plugin = cls;
1254 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_message_fragment;
1255 int sql_ret;
1256 int ret = GNUNET_SYSERR;
1257
1258 struct GNUNET_MY_QueryParam params_select[] = {
1259 GNUNET_MY_query_param_auto_from_type (channel_key),
1260 GNUNET_MY_query_param_uint64 (&message_id),
1261 GNUNET_MY_query_param_uint64 (&fragment_offset),
1262 GNUNET_MY_query_param_end
1263 };
1264
1265 sql_ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select);
1266 switch (sql_ret)
1267 {
1268 case GNUNET_NO:
1269 ret = GNUNET_NO;
1270 break;
1271
1272 case GNUNET_OK:
1273 ret = fragment_row (stmt, cb, cb_cls, NULL);
1274 break;
1275
1276 default:
1277 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1278 "mysql execute prepared", stmt);
1279 }
1280
1281 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1282 {
1283 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1284 "mysql_stmt_reset", stmt);
1285 return GNUNET_SYSERR;
1286 }
1287
1288 return ret;
1289}
1290
1291/**
1292 * Retrieve the max. values of message counters for a channel.
1293 *
1294 * @see GNUNET_PSYCSTORE_counters_get()
1295 *
1296 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1297 */
1298static int
1299counters_message_get (void *cls,
1300 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1301 uint64_t *max_fragment_id,
1302 uint64_t *max_message_id,
1303 uint64_t *max_group_generation)
1304{
1305 struct Plugin *plugin = cls;
1306
1307 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_counters_message;
1308
1309 int ret = GNUNET_SYSERR;
1310
1311 struct GNUNET_MY_QueryParam params_select[] = {
1312 GNUNET_MY_query_param_auto_from_type (channel_key),
1313 GNUNET_MY_query_param_end
1314 };
1315
1316 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
1317 {
1318 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1319 "mysql execute prepared", stmt);
1320 return GNUNET_SYSERR;
1321 }
1322
1323 struct GNUNET_MY_ResultSpec results_select[] = {
1324 GNUNET_MY_result_spec_uint64 (max_fragment_id),
1325 GNUNET_MY_result_spec_uint64 (max_message_id),
1326 GNUNET_MY_result_spec_uint64 (max_group_generation),
1327 GNUNET_MY_result_spec_end
1328 };
1329
1330 ret = GNUNET_MY_extract_result (stmt, results_select);
1331
1332 if (GNUNET_OK != ret)
1333 {
1334 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1335 "mysql extract_result", stmt);
1336 return GNUNET_SYSERR;
1337 }
1338
1339 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1340 {
1341 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1342 "mysql_stmt_reset", stmt);
1343 return GNUNET_SYSERR;
1344 }
1345
1346 return ret;
1347}
1348
1349/**
1350 * Retrieve the max. values of state counters for a channel.
1351 *
1352 * @see GNUNET_PSYCSTORE_counters_get()
1353 *
1354 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1355 */
1356static int
1357counters_state_get (void *cls,
1358 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1359 uint64_t *max_state_message_id)
1360{
1361 struct Plugin *plugin = cls;
1362
1363 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_counters_state;
1364
1365 int ret = GNUNET_SYSERR;
1366
1367 struct GNUNET_MY_QueryParam params_select[] = {
1368 GNUNET_MY_query_param_auto_from_type (channel_key),
1369 GNUNET_MY_query_param_end
1370 };
1371
1372 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
1373 {
1374 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1375 "mysql execute prepared", stmt);
1376 return GNUNET_SYSERR;
1377 }
1378
1379 struct GNUNET_MY_ResultSpec results_select[] = {
1380 GNUNET_MY_result_spec_uint64 (max_state_message_id),
1381 GNUNET_MY_result_spec_end
1382 };
1383
1384 ret = GNUNET_MY_extract_result (stmt, results_select);
1385
1386 if (GNUNET_OK != ret)
1387 {
1388 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1389 "mysql extract_result", stmt);
1390 return GNUNET_SYSERR;
1391 }
1392
1393 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1394 {
1395 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1396 "mysql_stmt_reset", stmt);
1397 return GNUNET_SYSERR;
1398 }
1399
1400 return ret;
1401}
1402
1403
1404/**
1405 * Assign a value to a state variable.
1406 *
1407 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1408 */
1409static int
1410state_assign (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
1411 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1412 const char *name, const void *value, size_t value_size)
1413{
1414 int ret = GNUNET_SYSERR;
1415
1416 struct GNUNET_MY_QueryParam params[] = {
1417 GNUNET_MY_query_param_auto_from_type (channel_key),
1418 GNUNET_MY_query_param_string (name),
1419 GNUNET_MY_query_param_fixed_size(value, value_size),
1420 GNUNET_MY_query_param_end
1421 };
1422
1423 ret = GNUNET_MY_exec_prepared (plugin->mc, stmt, params);
1424 if (GNUNET_OK != ret)
1425 {
1426 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1427 "mysql exec_prepared", stmt);
1428 return GNUNET_SYSERR;
1429 }
1430
1431 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1432 {
1433 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1434 "mysql_stmt_reset", stmt);
1435 return GNUNET_SYSERR;
1436 }
1437
1438 return ret;
1439}
1440
1441
1442static int
1443update_message_id (struct Plugin *plugin, struct GNUNET_MYSQL_StatementHandle *stmt,
1444 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1445 uint64_t message_id)
1446{
1447 struct GNUNET_MY_QueryParam params[] = {
1448 GNUNET_MY_query_param_uint64 (&message_id),
1449 GNUNET_MY_query_param_auto_from_type (channel_key),
1450 GNUNET_MY_query_param_end
1451 };
1452
1453 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc,
1454 stmt,
1455 params))
1456 {
1457 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1458 "mysql execute prepared", stmt);
1459 return GNUNET_SYSERR;
1460 }
1461
1462 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1463 {
1464 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1465 "mysql_stmt_reset", stmt);
1466 return GNUNET_SYSERR;
1467 }
1468
1469 return GNUNET_OK;
1470}
1471
1472
1473/**
1474 * Begin modifying current state.
1475 */
1476static int
1477state_modify_begin (void *cls,
1478 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1479 uint64_t message_id, uint64_t state_delta)
1480{
1481 struct Plugin *plugin = cls;
1482
1483 if (state_delta > 0)
1484 {
1485 /**
1486 * We can only apply state modifiers in the current message if modifiers in
1487 * the previous stateful message (message_id - state_delta) were already
1488 * applied.
1489 */
1490
1491 uint64_t max_state_message_id = 0;
1492 int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
1493 switch (ret)
1494 {
1495 case GNUNET_OK:
1496 case GNUNET_NO: // no state yet
1497 ret = GNUNET_OK;
1498 break;
1499 default:
1500 return ret;
1501 }
1502
1503 if (max_state_message_id < message_id - state_delta)
1504 return GNUNET_NO; /* some stateful messages not yet applied */
1505 else if (message_id - state_delta < max_state_message_id)
1506 return GNUNET_NO; /* changes already applied */
1507 }
1508
1509 if (TRANSACTION_NONE != plugin->transaction)
1510 {
1511 /** @todo FIXME: wait for other transaction to finish */
1512 return GNUNET_SYSERR;
1513 }
1514 return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
1515}
1516
1517
1518/**
1519 * Set the current value of state variable.
1520 *
1521 * @see GNUNET_PSYCSTORE_state_modify()
1522 *
1523 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1524 */
1525static int
1526state_modify_op (void *cls,
1527 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1528 enum GNUNET_PSYC_Operator op,
1529 const char *name, const void *value, size_t value_size)
1530{
1531 struct Plugin *plugin = cls;
1532 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1533
1534 switch (op)
1535 {
1536 case GNUNET_PSYC_OP_ASSIGN:
1537 return state_assign (plugin, plugin->insert_state_current,
1538 channel_key, name, value, value_size);
1539
1540 default: /** @todo implement more state operations */
1541 GNUNET_break (0);
1542 return GNUNET_SYSERR;
1543 }
1544}
1545
1546
1547/**
1548 * End modifying current state.
1549 */
1550static int
1551state_modify_end (void *cls,
1552 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1553 uint64_t message_id)
1554{
1555 struct Plugin *plugin = cls;
1556 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1557
1558 return
1559 GNUNET_OK == exec_channel (plugin, plugin->delete_state_empty, channel_key)
1560 && GNUNET_OK == update_message_id (plugin,
1561 plugin->update_max_state_message_id,
1562 channel_key, message_id)
1563 && GNUNET_OK == transaction_commit (plugin)
1564 ? GNUNET_OK : GNUNET_SYSERR;
1565}
1566
1567
1568/**
1569 * Begin state synchronization.
1570 */
1571static int
1572state_sync_begin (void *cls,
1573 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1574{
1575 struct Plugin *plugin = cls;
1576 return exec_channel (plugin, plugin->delete_state_sync, channel_key);
1577}
1578
1579
1580/**
1581 * Assign current value of a state variable.
1582 *
1583 * @see GNUNET_PSYCSTORE_state_modify()
1584 *
1585 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1586 */
1587static int
1588state_sync_assign (void *cls,
1589 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1590 const char *name, const void *value, size_t value_size)
1591{
1592 struct Plugin *plugin = cls;
1593 return state_assign (cls, plugin->insert_state_sync,
1594 channel_key, name, value, value_size);
1595}
1596
1597
1598/**
1599 * End modifying current state.
1600 */
1601static int
1602state_sync_end (void *cls,
1603 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1604 uint64_t max_state_message_id,
1605 uint64_t state_hash_message_id)
1606{
1607 struct Plugin *plugin = cls;
1608 int ret = GNUNET_SYSERR;
1609
1610 if (TRANSACTION_NONE != plugin->transaction)
1611 {
1612 /** @todo FIXME: wait for other transaction to finish */
1613 return GNUNET_SYSERR;
1614 }
1615
1616 GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
1617 && GNUNET_OK == exec_channel (plugin, plugin->delete_state, channel_key)
1618 && GNUNET_OK == exec_channel (plugin, plugin->insert_state_from_sync,
1619 channel_key)
1620 && GNUNET_OK == exec_channel (plugin, plugin->delete_state_sync,
1621 channel_key)
1622 && GNUNET_OK == update_message_id (plugin,
1623 plugin->update_state_hash_message_id,
1624 channel_key, state_hash_message_id)
1625 && GNUNET_OK == update_message_id (plugin,
1626 plugin->update_max_state_message_id,
1627 channel_key, max_state_message_id)
1628 && GNUNET_OK == transaction_commit (plugin)
1629 ? ret = GNUNET_OK
1630 : transaction_rollback (plugin);
1631 return ret;
1632}
1633
1634
1635/**
1636 * Delete the whole state.
1637 *
1638 * @see GNUNET_PSYCSTORE_state_reset()
1639 *
1640 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1641 */
1642static int
1643state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1644{
1645 struct Plugin *plugin = cls;
1646 return exec_channel (plugin, plugin->delete_state, channel_key);
1647}
1648
1649
1650/**
1651 * Update signed values of state variables in the state store.
1652 *
1653 * @see GNUNET_PSYCSTORE_state_hash_update()
1654 *
1655 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1656 */
1657static int
1658state_update_signed (void *cls,
1659 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1660{
1661 struct Plugin *plugin = cls;
1662 return exec_channel (plugin, plugin->update_state_signed, channel_key);
1663}
1664
1665
1666/**
1667 * Retrieve a state variable by name.
1668 *
1669 * @see GNUNET_PSYCSTORE_state_get()
1670 *
1671 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1672 */
1673static int
1674state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1675 const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1676{
1677 struct Plugin *plugin = cls;
1678 int ret = GNUNET_SYSERR;
1679 int sql_ret ;
1680
1681 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_one;
1682
1683 struct GNUNET_MY_QueryParam params_select[] = {
1684 GNUNET_MY_query_param_auto_from_type (channel_key),
1685 GNUNET_MY_query_param_string (name),
1686 GNUNET_MY_query_param_end
1687 };
1688
1689 void *value_current = NULL;
1690 size_t value_size = 0;
1691
1692 struct GNUNET_MY_ResultSpec results[] = {
1693 GNUNET_MY_result_spec_variable_size (&value_current, &value_size),
1694 GNUNET_MY_result_spec_end
1695 };
1696
1697 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
1698 {
1699 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1700 "mysql exec_prepared", stmt);
1701 }
1702 else
1703 {
1704 sql_ret = GNUNET_MY_extract_result (stmt, results);
1705 switch (sql_ret)
1706 {
1707 case GNUNET_NO:
1708 ret = GNUNET_NO;
1709 break;
1710
1711 case GNUNET_YES:
1712 ret = cb (cb_cls, name, value_current, value_size);
1713 break;
1714
1715 default:
1716 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1717 "mysql extract_result", stmt);
1718 }
1719 }
1720
1721 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1722 {
1723 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1724 "mysql_stmt_reset", stmt);
1725 return GNUNET_SYSERR;
1726 }
1727
1728 return ret;
1729}
1730
1731
1732/**
1733 * Retrieve all state variables for a channel with the given prefix.
1734 *
1735 * @see GNUNET_PSYCSTORE_state_get_prefix()
1736 *
1737 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1738 */
1739static int
1740state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1741 const char *name, GNUNET_PSYCSTORE_StateCallback cb,
1742 void *cb_cls)
1743{
1744 struct Plugin *plugin = cls;
1745 int ret = GNUNET_SYSERR;
1746
1747 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_prefix;
1748
1749 uint32_t name_len = (uint32_t) strlen (name);
1750
1751 struct GNUNET_MY_QueryParam params_select[] = {
1752 GNUNET_MY_query_param_auto_from_type (channel_key),
1753 GNUNET_MY_query_param_string (name),
1754 GNUNET_MY_query_param_uint32 (&name_len),
1755 GNUNET_MY_query_param_string (name),
1756 GNUNET_MY_query_param_end
1757 };
1758
1759 char *name2 = "";
1760 void *value_current = NULL;
1761 size_t value_size = 0;
1762
1763 struct GNUNET_MY_ResultSpec results[] = {
1764 GNUNET_MY_result_spec_string (&name2),
1765 GNUNET_MY_result_spec_variable_size (&value_current, &value_size),
1766 GNUNET_MY_result_spec_end
1767 };;
1768
1769 int sql_ret;
1770
1771 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
1772 {
1773 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1774 "mysql exec_prepared", stmt);
1775 return GNUNET_SYSERR;
1776 }
1777
1778 do
1779 {
1780 sql_ret = GNUNET_MY_extract_result (stmt, results);
1781 switch (sql_ret)
1782 {
1783 case GNUNET_NO:
1784 if (ret != GNUNET_YES)
1785 ret = GNUNET_NO;
1786 break;
1787
1788 case GNUNET_YES:
1789 ret = cb (cb_cls, (const char *) name2, value_current, value_size);
1790
1791 if (ret != GNUNET_YES)
1792 sql_ret = GNUNET_NO;
1793 break;
1794
1795 default:
1796 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1797 "mysql extract_result", stmt);
1798 }
1799 }
1800 while (sql_ret == GNUNET_YES);
1801
1802 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1803 {
1804 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1805 "mysql_stmt_reset", stmt);
1806 return GNUNET_SYSERR;
1807 }
1808
1809 return ret;
1810}
1811
1812
1813/**
1814 * Retrieve all signed state variables for a channel.
1815 *
1816 * @see GNUNET_PSYCSTORE_state_get_signed()
1817 *
1818 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1819 */
1820static int
1821state_get_signed (void *cls,
1822 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1823 GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1824{
1825 struct Plugin *plugin = cls;
1826 int ret = GNUNET_SYSERR;
1827
1828 struct GNUNET_MYSQL_StatementHandle *stmt = plugin->select_state_signed;
1829
1830 struct GNUNET_MY_QueryParam params_select[] = {
1831 GNUNET_MY_query_param_auto_from_type (channel_key),
1832 GNUNET_MY_query_param_end
1833 };
1834
1835 int sql_ret;
1836
1837 char *name = "";
1838 void *value_signed = NULL;
1839 size_t value_size = 0;
1840
1841 struct GNUNET_MY_ResultSpec results[] = {
1842 GNUNET_MY_result_spec_string (&name),
1843 GNUNET_MY_result_spec_variable_size (&value_signed, &value_size),
1844 GNUNET_MY_result_spec_end
1845 };
1846
1847 if (GNUNET_OK != GNUNET_MY_exec_prepared (plugin->mc, stmt, params_select))
1848 {
1849 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1850 "mysql exec_prepared", stmt);
1851 return GNUNET_SYSERR;
1852 }
1853
1854 do
1855 {
1856 sql_ret = GNUNET_MY_extract_result (stmt, results);
1857 switch (sql_ret)
1858 {
1859 case GNUNET_NO:
1860 if (ret != GNUNET_YES)
1861 ret = GNUNET_NO;
1862 break;
1863
1864 case GNUNET_YES:
1865 ret = cb (cb_cls, (const char *) name, value_signed, value_size);
1866
1867 if (ret != GNUNET_YES)
1868 sql_ret = GNUNET_NO;
1869 break;
1870
1871 default:
1872 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1873 "mysql extract_result", stmt);
1874 }
1875 }
1876 while (sql_ret == GNUNET_YES);
1877
1878 if (0 != mysql_stmt_reset (GNUNET_MYSQL_statement_get_stmt (stmt)))
1879 {
1880 LOG_MYSQL(plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1881 "mysql_stmt_reset", stmt);
1882 return GNUNET_SYSERR;
1883 }
1884
1885 return ret;
1886}
1887
1888
1889/**
1890 * Entry point for the plugin.
1891 *
1892 * @param cls The struct GNUNET_CONFIGURATION_Handle.
1893 * @return NULL on error, otherwise the plugin context
1894 */
1895void *
1896libgnunet_plugin_psycstore_mysql_init (void *cls)
1897{
1898 static struct Plugin plugin;
1899 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1900 struct GNUNET_PSYCSTORE_PluginFunctions *api;
1901
1902 if (NULL != plugin.cfg)
1903 return NULL; /* can only initialize once! */
1904 memset (&plugin, 0, sizeof (struct Plugin));
1905 plugin.cfg = cfg;
1906 if (GNUNET_OK != database_setup (&plugin))
1907 {
1908 database_shutdown (&plugin);
1909 return NULL;
1910 }
1911 api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
1912 api->cls = &plugin;
1913 api->membership_store = &mysql_membership_store;
1914 api->membership_test = &membership_test;
1915 api->fragment_store = &fragment_store;
1916 api->message_add_flags = &message_add_flags;
1917 api->fragment_get = &fragment_get;
1918 api->fragment_get_latest = &fragment_get_latest;
1919 api->message_get = &message_get;
1920 api->message_get_latest = &message_get_latest;
1921 api->message_get_fragment = &message_get_fragment;
1922 api->counters_message_get = &counters_message_get;
1923 api->counters_state_get = &counters_state_get;
1924 api->state_modify_begin = &state_modify_begin;
1925 api->state_modify_op = &state_modify_op;
1926 api->state_modify_end = &state_modify_end;
1927 api->state_sync_begin = &state_sync_begin;
1928 api->state_sync_assign = &state_sync_assign;
1929 api->state_sync_end = &state_sync_end;
1930 api->state_reset = &state_reset;
1931 api->state_update_signed = &state_update_signed;
1932 api->state_get = &state_get;
1933 api->state_get_prefix = &state_get_prefix;
1934 api->state_get_signed = &state_get_signed;
1935
1936 LOG (GNUNET_ERROR_TYPE_INFO, _("Mysql database running\n"));
1937 return api;
1938}
1939
1940
1941/**
1942 * Exit point from the plugin.
1943 *
1944 * @param cls The plugin context (as returned by "init")
1945 * @return Always NULL
1946 */
1947void *
1948libgnunet_plugin_psycstore_mysql_done (void *cls)
1949{
1950 struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
1951 struct Plugin *plugin = api->cls;
1952
1953 database_shutdown (plugin);
1954 plugin->cfg = NULL;
1955 GNUNET_free (api);
1956 LOG (GNUNET_ERROR_TYPE_DEBUG, "Mysql plugin is finished\n");
1957 return NULL;
1958}
1959
1960/* end of plugin_psycstore_mysql.c */
diff --git a/src/psycstore/plugin_psycstore_postgres.c b/src/psycstore/plugin_psycstore_postgres.c
new file mode 100644
index 0000000..33c9960
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_postgres.c
@@ -0,0 +1,1530 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2016 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/**
22 * @file psycstore/plugin_psycstore_postgres.c
23 * @brief PostgresQL-based psycstore backend
24 * @author Daniel Golle
25 * @author Gabor X Toth
26 * @author Christian Grothoff
27 * @author Christophe Genevey
28 * @author Jeffrey Burdges
29 */
30
31#include "platform.h"
32#include "gnunet_psycstore_plugin.h"
33#include "gnunet_psycstore_service.h"
34#include "gnunet_multicast_service.h"
35#include "gnunet_crypto_lib.h"
36#include "gnunet_psyc_util_lib.h"
37#include "psycstore.h"
38#include "gnunet_pq_lib.h"
39
40/**
41 * After how many ms "busy" should a DB operation fail for good? A
42 * low value makes sure that we are more responsive to requests
43 * (especially PUTs). A high value guarantees a higher success rate
44 * (SELECTs in iterate can take several seconds despite LIMIT=1).
45 *
46 * The default value of 1s should ensure that users do not experience
47 * huge latencies while at the same time allowing operations to
48 * succeed with reasonable probability.
49 */
50#define BUSY_TIMEOUT_MS 1000
51
52#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
53
54#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-postgres", __VA_ARGS__)
55
56enum Transactions {
57 TRANSACTION_NONE = 0,
58 TRANSACTION_STATE_MODIFY,
59 TRANSACTION_STATE_SYNC,
60};
61
62/**
63 * Context for all functions in this plugin.
64 */
65struct Plugin
66{
67
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69
70 /**
71 * Native Postgres database handle.
72 */
73 PGconn *dbh;
74
75 enum Transactions transaction;
76
77 void *cls;
78};
79
80
81/**
82 * Initialize the database connections and associated
83 * data structures (create tables and indices
84 * as needed as well).
85 *
86 * @param plugin the plugin context (state for this module)
87 * @return #GNUNET_OK on success
88 */
89static int
90database_setup (struct Plugin *plugin)
91{
92 struct GNUNET_PQ_ExecuteStatement es[] = {
93 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS channels (\n"
94 " id SERIAL,\n"
95 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
96 " max_state_message_id BIGINT,\n"
97 " state_hash_message_id BIGINT,\n"
98 " PRIMARY KEY(id)\n"
99 ")"
100 "WITH OIDS"),
101 GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS channel_pub_key_idx \n"
102 " ON channels (pub_key)"),
103 GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_chan_id(BYTEA) RETURNS INTEGER AS \n"
104 " 'SELECT id FROM channels WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
105 "RETURNS NULL ON NULL INPUT"),
106 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS slaves (\n"
107 " id SERIAL,\n"
108 " pub_key BYTEA NOT NULL CHECK (LENGTH(pub_key)=32),\n"
109 " PRIMARY KEY(id)\n"
110 ")"
111 "WITH OIDS"),
112 GNUNET_PQ_make_execute ("CREATE UNIQUE INDEX IF NOT EXISTS slaves_pub_key_idx \n"
113 " ON slaves (pub_key)"),
114 GNUNET_PQ_make_execute ("CREATE OR REPLACE FUNCTION get_slave_id(BYTEA) RETURNS INTEGER AS \n"
115 " 'SELECT id FROM slaves WHERE pub_key=$1;' LANGUAGE SQL STABLE \n"
116 "RETURNS NULL ON NULL INPUT"),
117 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS membership (\n"
118 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
119 " slave_id BIGINT NOT NULL REFERENCES slaves(id),\n"
120 " did_join INT NOT NULL,\n"
121 " announced_at BIGINT NOT NULL,\n"
122 " effective_since BIGINT NOT NULL,\n"
123 " group_generation BIGINT NOT NULL\n"
124 ")"
125 "WITH OIDS"),
126 GNUNET_PQ_make_execute ("CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
127 "ON membership (channel_id, slave_id)"),
128 /** @todo messages table: add method_name column */
129 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS messages (\n"
130 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
131 " hop_counter INT NOT NULL,\n"
132 " signature BYTEA CHECK (LENGTH(signature)=64),\n"
133 " purpose BYTEA CHECK (LENGTH(purpose)=8),\n"
134 " fragment_id BIGINT NOT NULL,\n"
135 " fragment_offset BIGINT NOT NULL,\n"
136 " message_id BIGINT NOT NULL,\n"
137 " group_generation BIGINT NOT NULL,\n"
138 " multicast_flags INT NOT NULL,\n"
139 " psycstore_flags INT NOT NULL,\n"
140 " data BYTEA,\n"
141 " PRIMARY KEY (channel_id, fragment_id),\n"
142 " UNIQUE (channel_id, message_id, fragment_offset)\n"
143 ")"
144 "WITH OIDS"),
145 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state (\n"
146 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
147 " name TEXT NOT NULL,\n"
148 " value_current BYTEA,\n"
149 " value_signed BYTEA,\n"
150 " PRIMARY KEY (channel_id, name)\n"
151 ")"
152 "WITH OIDS"),
153 GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS state_sync (\n"
154 " channel_id BIGINT NOT NULL REFERENCES channels(id),\n"
155 " name TEXT NOT NULL,\n"
156 " value BYTEA,\n"
157 " PRIMARY KEY (channel_id, name)\n"
158 ")"
159 "WITH OIDS"),
160 GNUNET_PQ_EXECUTE_STATEMENT_END
161 };
162
163 /* Open database and precompile statements */
164 plugin->dbh = GNUNET_PQ_connect_with_cfg (plugin->cfg,
165 "psycstore-postgres");
166 if (NULL == plugin->dbh)
167 return GNUNET_SYSERR;
168 if (GNUNET_OK !=
169 GNUNET_PQ_exec_statements (plugin->dbh,
170 es))
171 {
172 PQfinish (plugin->dbh);
173 plugin->dbh = NULL;
174 return GNUNET_SYSERR;
175 }
176
177 /* Prepare statements */
178 {
179 struct GNUNET_PQ_PreparedStatement ps[] = {
180 GNUNET_PQ_make_prepare ("transaction_begin",
181 "BEGIN", 0),
182 GNUNET_PQ_make_prepare ("transaction_commit",
183 "COMMIT", 0),
184 GNUNET_PQ_make_prepare ("transaction_rollback",
185 "ROLLBACK", 0),
186 GNUNET_PQ_make_prepare ("insert_channel_key",
187 "INSERT INTO channels (pub_key) VALUES ($1)"
188 " ON CONFLICT DO NOTHING", 1),
189 GNUNET_PQ_make_prepare ("insert_slave_key",
190 "INSERT INTO slaves (pub_key) VALUES ($1)"
191 " ON CONFLICT DO NOTHING", 1),
192 GNUNET_PQ_make_prepare ("insert_membership",
193 "INSERT INTO membership\n"
194 " (channel_id, slave_id, did_join, announced_at,\n"
195 " effective_since, group_generation)\n"
196 "VALUES (get_chan_id($1),\n"
197 " get_slave_id($2),\n"
198 " $3, $4, $5, $6)", 6),
199 GNUNET_PQ_make_prepare ("select_membership",
200 "SELECT did_join FROM membership\n"
201 "WHERE channel_id = get_chan_id($1)\n"
202 " AND slave_id = get_slave_id($2)\n"
203 " AND effective_since <= $3 AND did_join = 1\n"
204 "ORDER BY announced_at DESC LIMIT 1", 3),
205 GNUNET_PQ_make_prepare ("insert_fragment",
206 "INSERT INTO messages\n"
207 " (channel_id, hop_counter, signature, purpose,\n"
208 " fragment_id, fragment_offset, message_id,\n"
209 " group_generation, multicast_flags, psycstore_flags, data)\n"
210 "VALUES (get_chan_id($1),\n"
211 " $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)"
212 "ON CONFLICT DO NOTHING", 11),
213 GNUNET_PQ_make_prepare ("update_message_flags",
214 "UPDATE messages\n"
215 "SET psycstore_flags = psycstore_flags | $1\n"
216 "WHERE channel_id = get_chan_id($2) \n"
217 " AND message_id = $3 AND fragment_offset = 0", 3),
218 GNUNET_PQ_make_prepare ("select_fragments",
219 "SELECT hop_counter, signature, purpose, fragment_id,\n"
220 " fragment_offset, message_id, group_generation,\n"
221 " multicast_flags, psycstore_flags, data\n"
222 "FROM messages\n"
223 "WHERE channel_id = get_chan_id($1) \n"
224 " AND $2 <= fragment_id AND fragment_id <= $3", 3),
225 /** @todo select_messages: add method_prefix filter */
226 GNUNET_PQ_make_prepare ("select_messages",
227 "SELECT hop_counter, signature, purpose, fragment_id,\n"
228 " fragment_offset, message_id, group_generation,\n"
229 " multicast_flags, psycstore_flags, data\n"
230 "FROM messages\n"
231 "WHERE channel_id = get_chan_id($1) \n"
232 " AND $2 <= message_id AND message_id <= $3\n"
233 "LIMIT $4;", 4),
234 /** @todo select_latest_messages: add method_prefix filter */
235 GNUNET_PQ_make_prepare ("select_latest_fragments",
236 "SELECT rev.hop_counter AS hop_counter,\n"
237 " rev.signature AS signature,\n"
238 " rev.purpose AS purpose,\n"
239 " rev.fragment_id AS fragment_id,\n"
240 " rev.fragment_offset AS fragment_offset,\n"
241 " rev.message_id AS message_id,\n"
242 " rev.group_generation AS group_generation,\n"
243 " rev.multicast_flags AS multicast_flags,\n"
244 " rev.psycstore_flags AS psycstore_flags,\n"
245 " rev.data AS data\n"
246 " FROM\n"
247 " (SELECT hop_counter, signature, purpose, fragment_id,\n"
248 " fragment_offset, message_id, group_generation,\n"
249 " multicast_flags, psycstore_flags, data \n"
250 " FROM messages\n"
251 " WHERE channel_id = get_chan_id($1) \n"
252 " ORDER BY fragment_id DESC\n"
253 " LIMIT $2) AS rev\n"
254 " ORDER BY rev.fragment_id;", 2),
255 GNUNET_PQ_make_prepare ("select_latest_messages",
256 "SELECT hop_counter, signature, purpose, fragment_id,\n"
257 " fragment_offset, message_id, group_generation,\n"
258 " multicast_flags, psycstore_flags, data\n"
259 "FROM messages\n"
260 "WHERE channel_id = get_chan_id($1)\n"
261 " AND message_id IN\n"
262 " (SELECT message_id\n"
263 " FROM messages\n"
264 " WHERE channel_id = get_chan_id($2) \n"
265 " GROUP BY message_id\n"
266 " ORDER BY message_id\n"
267 " DESC LIMIT $3)\n"
268 "ORDER BY fragment_id", 3),
269 GNUNET_PQ_make_prepare ("select_message_fragment",
270 "SELECT hop_counter, signature, purpose, fragment_id,\n"
271 " fragment_offset, message_id, group_generation,\n"
272 " multicast_flags, psycstore_flags, data\n"
273 "FROM messages\n"
274 "WHERE channel_id = get_chan_id($1) \n"
275 " AND message_id = $2 AND fragment_offset = $3", 3),
276 GNUNET_PQ_make_prepare ("select_counters_message",
277 "SELECT fragment_id, message_id, group_generation\n"
278 "FROM messages\n"
279 "WHERE channel_id = get_chan_id($1)\n"
280 "ORDER BY fragment_id DESC LIMIT 1", 1),
281 GNUNET_PQ_make_prepare ("select_counters_state",
282 "SELECT max_state_message_id\n"
283 "FROM channels\n"
284 "WHERE pub_key = $1 AND max_state_message_id IS NOT NULL", 1),
285 GNUNET_PQ_make_prepare ("update_max_state_message_id",
286 "UPDATE channels\n"
287 "SET max_state_message_id = $1\n"
288 "WHERE pub_key = $2", 2),
289
290 GNUNET_PQ_make_prepare ("update_state_hash_message_id",
291 "UPDATE channels\n"
292 "SET state_hash_message_id = $1\n"
293 "WHERE pub_key = $2", 2),
294 GNUNET_PQ_make_prepare ("insert_state_current",
295 "INSERT INTO state\n"
296 " (channel_id, name, value_current, value_signed)\n"
297 "SELECT new.channel_id, new.name,\n"
298 " new.value_current, old.value_signed\n"
299 "FROM (SELECT get_chan_id($1) AS channel_id,\n"
300 " $2::TEXT AS name, $3::BYTEA AS value_current) AS new\n"
301 "LEFT JOIN (SELECT channel_id, name, value_signed\n"
302 " FROM state) AS old\n"
303 "ON new.channel_id = old.channel_id AND new.name = old.name\n"
304 "ON CONFLICT (channel_id, name)\n"
305 " DO UPDATE SET value_current = EXCLUDED.value_current,\n"
306 " value_signed = EXCLUDED.value_signed", 3),
307 GNUNET_PQ_make_prepare ("delete_state_empty",
308 "DELETE FROM state\n"
309 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = $1)\n"
310 " AND (value_current IS NULL OR length(value_current) = 0)\n"
311 " AND (value_signed IS NULL OR length(value_signed) = 0)", 1),
312 GNUNET_PQ_make_prepare ("update_state_signed",
313 "UPDATE state\n"
314 "SET value_signed = value_current\n"
315 "WHERE channel_id = get_chan_id($1) ", 1),
316 GNUNET_PQ_make_prepare ("delete_state",
317 "DELETE FROM state\n"
318 "WHERE channel_id = get_chan_id($1) ", 1),
319 GNUNET_PQ_make_prepare ("insert_state_sync",
320 "INSERT INTO state_sync (channel_id, name, value)\n"
321 "VALUES (get_chan_id($1), $2, $3)", 3),
322 GNUNET_PQ_make_prepare ("insert_state_from_sync",
323 "INSERT INTO state\n"
324 " (channel_id, name, value_current, value_signed)\n"
325 "SELECT channel_id, name, value, value\n"
326 "FROM state_sync\n"
327 "WHERE channel_id = get_chan_id($1)", 1),
328 GNUNET_PQ_make_prepare ("delete_state_sync",
329 "DELETE FROM state_sync\n"
330 "WHERE channel_id = get_chan_id($1)", 1),
331 GNUNET_PQ_make_prepare ("select_state_one",
332 "SELECT value_current\n"
333 "FROM state\n"
334 "WHERE channel_id = get_chan_id($1)\n"
335 " AND name = $2", 2),
336 GNUNET_PQ_make_prepare ("select_state_prefix",
337 "SELECT name, value_current\n"
338 "FROM state\n"
339 "WHERE channel_id = get_chan_id($1)\n"
340 " AND (name = $2 OR substr(name, 1, $3) = $4)", 4),
341 GNUNET_PQ_make_prepare ("select_state_signed",
342 "SELECT name, value_signed\n"
343 "FROM state\n"
344 "WHERE channel_id = get_chan_id($1)\n"
345 " AND value_signed IS NOT NULL", 1),
346 GNUNET_PQ_PREPARED_STATEMENT_END
347 };
348
349 if (GNUNET_OK !=
350 GNUNET_PQ_prepare_statements (plugin->dbh,
351 ps))
352 {
353 PQfinish (plugin->dbh);
354 plugin->dbh = NULL;
355 return GNUNET_SYSERR;
356 }
357 }
358
359 return GNUNET_OK;
360}
361
362
363/**
364 * Shutdown database connection and associate data
365 * structures.
366 * @param plugin the plugin context (state for this module)
367 */
368static void
369database_shutdown (struct Plugin *plugin)
370{
371 PQfinish (plugin->dbh);
372 plugin->dbh = NULL;
373}
374
375
376/**
377 * Execute a prepared statement with a @a channel_key argument.
378 *
379 * @param plugin Plugin handle.
380 * @param stmt Statement to execute.
381 * @param channel_key Public key of the channel.
382 *
383 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
384 */
385static int
386exec_channel (struct Plugin *plugin, const char *stmt,
387 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
388{
389 struct GNUNET_PQ_QueryParam params[] = {
390 GNUNET_PQ_query_param_auto_from_type (channel_key),
391 GNUNET_PQ_query_param_end
392 };
393
394 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
395 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
396 return GNUNET_SYSERR;
397
398 return GNUNET_OK;
399}
400
401
402/**
403 * Begin a transaction.
404 */
405static int
406transaction_begin (struct Plugin *plugin, enum Transactions transaction)
407{
408 struct GNUNET_PQ_QueryParam params[] = {
409 GNUNET_PQ_query_param_end
410 };
411
412 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
413 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_begin", params))
414 return GNUNET_SYSERR;
415
416 plugin->transaction = transaction;
417 return GNUNET_OK;
418}
419
420
421/**
422 * Commit current transaction.
423 */
424static int
425transaction_commit (struct Plugin *plugin)
426{
427 struct GNUNET_PQ_QueryParam params[] = {
428 GNUNET_PQ_query_param_end
429 };
430
431 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
432 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_commit", params))
433 return GNUNET_SYSERR;
434
435 plugin->transaction = TRANSACTION_NONE;
436 return GNUNET_OK;
437}
438
439
440/**
441 * Roll back current transaction.
442 */
443static int
444transaction_rollback (struct Plugin *plugin)
445{
446 struct GNUNET_PQ_QueryParam params[] = {
447 GNUNET_PQ_query_param_end
448 };
449
450 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
451 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "transaction_rollback", params))
452 return GNUNET_SYSERR;
453
454 plugin->transaction = TRANSACTION_NONE;
455 return GNUNET_OK;
456}
457
458
459static int
460channel_key_store (struct Plugin *plugin,
461 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
462{
463 struct GNUNET_PQ_QueryParam params[] = {
464 GNUNET_PQ_query_param_auto_from_type (channel_key),
465 GNUNET_PQ_query_param_end
466 };
467
468 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
469 GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
470 "insert_channel_key",
471 params))
472 return GNUNET_SYSERR;
473
474 return GNUNET_OK;
475}
476
477
478static int
479slave_key_store (struct Plugin *plugin,
480 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
481{
482 struct GNUNET_PQ_QueryParam params[] = {
483 GNUNET_PQ_query_param_auto_from_type (slave_key),
484 GNUNET_PQ_query_param_end
485 };
486
487 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
488 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "insert_slave_key", params))
489 return GNUNET_SYSERR;
490
491 return GNUNET_OK;
492}
493
494
495/**
496 * Store join/leave events for a PSYC channel in order to be able to answer
497 * membership test queries later.
498 *
499 * @see GNUNET_PSYCSTORE_membership_store()
500 *
501 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
502 */
503static int
504postgres_membership_store (void *cls,
505 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
506 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
507 int did_join,
508 uint64_t announced_at,
509 uint64_t effective_since,
510 uint64_t group_generation)
511{
512 struct Plugin *plugin = cls;
513 uint32_t idid_join = (uint32_t) did_join;
514
515 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
516
517 if ( (announced_at > INT64_MAX) ||
518 (effective_since > INT64_MAX) ||
519 (group_generation > INT64_MAX) )
520 {
521 GNUNET_break (0);
522 return GNUNET_SYSERR;
523 }
524
525 if ( (GNUNET_OK !=
526 channel_key_store (plugin, channel_key)) ||
527 (GNUNET_OK !=
528 slave_key_store (plugin, slave_key)) )
529 return GNUNET_SYSERR;
530
531 struct GNUNET_PQ_QueryParam params[] = {
532 GNUNET_PQ_query_param_auto_from_type (channel_key),
533 GNUNET_PQ_query_param_auto_from_type (slave_key),
534 GNUNET_PQ_query_param_uint32 (&idid_join),
535 GNUNET_PQ_query_param_uint64 (&announced_at),
536 GNUNET_PQ_query_param_uint64 (&effective_since),
537 GNUNET_PQ_query_param_uint64 (&group_generation),
538 GNUNET_PQ_query_param_end
539 };
540
541 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
542 GNUNET_PQ_eval_prepared_non_select (plugin->dbh,
543 "insert_membership",
544 params))
545 return GNUNET_SYSERR;
546
547 return GNUNET_OK;
548}
549
550/**
551 * Test if a member was admitted to the channel at the given message ID.
552 *
553 * @see GNUNET_PSYCSTORE_membership_test()
554 *
555 * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
556 * #GNUNET_SYSERR if there was en error.
557 */
558static int
559membership_test (void *cls,
560 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
561 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
562 uint64_t message_id)
563{
564 struct Plugin *plugin = cls;
565
566 uint32_t did_join = 0;
567
568 struct GNUNET_PQ_QueryParam params_select[] = {
569 GNUNET_PQ_query_param_auto_from_type (channel_key),
570 GNUNET_PQ_query_param_auto_from_type (slave_key),
571 GNUNET_PQ_query_param_uint64 (&message_id),
572 GNUNET_PQ_query_param_end
573 };
574
575 struct GNUNET_PQ_ResultSpec results_select[] = {
576 GNUNET_PQ_result_spec_uint32 ("did_join", &did_join),
577 GNUNET_PQ_result_spec_end
578 };
579
580 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
581 GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, "select_membership",
582 params_select, results_select))
583 return GNUNET_SYSERR;
584
585 return GNUNET_OK;
586}
587
588/**
589 * Store a message fragment sent to a channel.
590 *
591 * @see GNUNET_PSYCSTORE_fragment_store()
592 *
593 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
594 */
595static int
596fragment_store (void *cls,
597 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
598 const struct GNUNET_MULTICAST_MessageHeader *msg,
599 uint32_t psycstore_flags)
600{
601 struct Plugin *plugin = cls;
602
603 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
604
605 uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
606
607 uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
608 uint64_t message_id = GNUNET_ntohll (msg->message_id);
609 uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
610
611 uint32_t hop_counter = ntohl(msg->hop_counter);
612 uint32_t flags = ntohl(msg->flags);
613
614 if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
615 message_id > INT64_MAX || group_generation > INT64_MAX)
616 {
617 LOG(GNUNET_ERROR_TYPE_ERROR,
618 "Tried to store fragment with a field > INT64_MAX: "
619 "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
620 message_id, group_generation);
621 GNUNET_break (0);
622 return GNUNET_SYSERR;
623 }
624
625 if (GNUNET_OK != channel_key_store (plugin, channel_key))
626 return GNUNET_SYSERR;
627
628 struct GNUNET_PQ_QueryParam params_insert[] = {
629 GNUNET_PQ_query_param_auto_from_type (channel_key),
630 GNUNET_PQ_query_param_uint32 (&hop_counter),
631 GNUNET_PQ_query_param_auto_from_type (&msg->signature),
632 GNUNET_PQ_query_param_auto_from_type (&msg->purpose),
633 GNUNET_PQ_query_param_uint64 (&fragment_id),
634 GNUNET_PQ_query_param_uint64 (&fragment_offset),
635 GNUNET_PQ_query_param_uint64 (&message_id),
636 GNUNET_PQ_query_param_uint64 (&group_generation),
637 GNUNET_PQ_query_param_uint32 (&flags),
638 GNUNET_PQ_query_param_uint32 (&psycstore_flags),
639 GNUNET_PQ_query_param_fixed_size (&msg[1], ntohs (msg->header.size) - sizeof (*msg)),
640 GNUNET_PQ_query_param_end
641 };
642
643 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
644 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "insert_fragment", params_insert))
645 return GNUNET_SYSERR;
646
647 return GNUNET_OK;
648}
649
650/**
651 * Set additional flags for a given message.
652 *
653 * They are OR'd with any existing flags set.
654 *
655 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
656 */
657static int
658message_add_flags (void *cls,
659 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
660 uint64_t message_id,
661 uint32_t psycstore_flags)
662{
663 struct Plugin *plugin = cls;
664
665 struct GNUNET_PQ_QueryParam params_update[] = {
666 GNUNET_PQ_query_param_uint32 (&psycstore_flags),
667 GNUNET_PQ_query_param_auto_from_type (channel_key),
668 GNUNET_PQ_query_param_uint64 (&message_id),
669 GNUNET_PQ_query_param_end
670 };
671
672 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
673 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, "update_message_flags", params_update))
674 return GNUNET_SYSERR;
675
676 return GNUNET_OK;
677}
678
679
680/**
681 * Closure for #fragment_rows.
682 */
683struct FragmentRowsContext {
684 GNUNET_PSYCSTORE_FragmentCallback cb;
685 void *cb_cls;
686
687 uint64_t *returned_fragments;
688
689 /* I preserved this but I do not see the point since
690 * it cannot stop the loop early and gets overwritten ?? */
691 int ret;
692};
693
694
695/**
696 * Callback that retrieves the results of a SELECT statement
697 * reading form the messages table.
698 *
699 * Only passed to GNUNET_PQ_eval_prepared_multi_select and
700 * has type GNUNET_PQ_PostgresResultHandler.
701 *
702 * @param cls closure
703 * @param result the postgres result
704 * @param num_result the number of results in @a result
705 */
706void fragment_rows (void *cls,
707 PGresult *res,
708 unsigned int num_results)
709{
710 struct FragmentRowsContext *c = cls;
711
712 for (unsigned int i=0;i<num_results;i++)
713 {
714 uint32_t hop_counter;
715 void *signature = NULL;
716 void *purpose = NULL;
717 size_t signature_size;
718 size_t purpose_size;
719 uint64_t fragment_id;
720 uint64_t fragment_offset;
721 uint64_t message_id;
722 uint64_t group_generation;
723 uint32_t flags;
724 void *buf;
725 size_t buf_size;
726 uint32_t msg_flags;
727 struct GNUNET_PQ_ResultSpec results[] = {
728 GNUNET_PQ_result_spec_uint32 ("hop_counter", &hop_counter),
729 GNUNET_PQ_result_spec_variable_size ("signature", &signature, &signature_size),
730 GNUNET_PQ_result_spec_variable_size ("purpose", &purpose, &purpose_size),
731 GNUNET_PQ_result_spec_uint64 ("fragment_id", &fragment_id),
732 GNUNET_PQ_result_spec_uint64 ("fragment_offset", &fragment_offset),
733 GNUNET_PQ_result_spec_uint64 ("message_id", &message_id),
734 GNUNET_PQ_result_spec_uint64 ("group_generation", &group_generation),
735 GNUNET_PQ_result_spec_uint32 ("multicast_flags", &msg_flags),
736 GNUNET_PQ_result_spec_uint32 ("psycstore_flags", &flags),
737 GNUNET_PQ_result_spec_variable_size ("data", &buf, &buf_size),
738 GNUNET_PQ_result_spec_end
739 };
740 struct GNUNET_MULTICAST_MessageHeader *mp;
741
742 if (GNUNET_YES != GNUNET_PQ_extract_result (res, results, i))
743 {
744 GNUNET_PQ_cleanup_result(results); /* missing previously, a memory leak?? */
745 break; /* nothing more?? */
746 }
747
748 mp = GNUNET_malloc (sizeof (*mp) + buf_size);
749
750 mp->header.size = htons (sizeof (*mp) + buf_size);
751 mp->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
752 mp->hop_counter = htonl (hop_counter);
753 GNUNET_memcpy (&mp->signature,
754 signature, signature_size);
755 GNUNET_memcpy (&mp->purpose,
756 purpose, purpose_size);
757 mp->fragment_id = GNUNET_htonll (fragment_id);
758 mp->fragment_offset = GNUNET_htonll (fragment_offset);
759 mp->message_id = GNUNET_htonll (message_id);
760 mp->group_generation = GNUNET_htonll (group_generation);
761 mp->flags = htonl(msg_flags);
762
763 GNUNET_memcpy (&mp[1],
764 buf, buf_size);
765 GNUNET_PQ_cleanup_result(results);
766 c->ret = c->cb (c->cb_cls, mp, (enum GNUNET_PSYCSTORE_MessageFlags) flags);
767 if (NULL != c->returned_fragments)
768 (*c->returned_fragments)++;
769 }
770}
771
772
773static int
774fragment_select (struct Plugin *plugin,
775 const char *stmt,
776 struct GNUNET_PQ_QueryParam *params,
777 uint64_t *returned_fragments,
778 GNUNET_PSYCSTORE_FragmentCallback cb,
779 void *cb_cls)
780{
781 /* Stack based closure */
782 struct FragmentRowsContext frc = {
783 .cb = cb,
784 .cb_cls = cb_cls,
785 .returned_fragments = returned_fragments,
786 .ret = GNUNET_SYSERR
787 };
788
789 if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
790 stmt, params,
791 &fragment_rows, &frc))
792 return GNUNET_SYSERR;
793 return frc.ret; /* GNUNET_OK ?? */
794}
795
796/**
797 * Retrieve a message fragment range by fragment ID.
798 *
799 * @see GNUNET_PSYCSTORE_fragment_get()
800 *
801 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
802 */
803static int
804fragment_get (void *cls,
805 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
806 uint64_t first_fragment_id,
807 uint64_t last_fragment_id,
808 uint64_t *returned_fragments,
809 GNUNET_PSYCSTORE_FragmentCallback cb,
810 void *cb_cls)
811{
812 struct Plugin *plugin = cls;
813 struct GNUNET_PQ_QueryParam params_select[] = {
814 GNUNET_PQ_query_param_auto_from_type (channel_key),
815 GNUNET_PQ_query_param_uint64 (&first_fragment_id),
816 GNUNET_PQ_query_param_uint64 (&last_fragment_id),
817 GNUNET_PQ_query_param_end
818 };
819
820 *returned_fragments = 0;
821 return fragment_select (plugin,
822 "select_fragments",
823 params_select,
824 returned_fragments,
825 cb, cb_cls);
826}
827
828
829/**
830 * Retrieve a message fragment range by fragment ID.
831 *
832 * @see GNUNET_PSYCSTORE_fragment_get_latest()
833 *
834 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
835 */
836static int
837fragment_get_latest (void *cls,
838 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
839 uint64_t fragment_limit,
840 uint64_t *returned_fragments,
841 GNUNET_PSYCSTORE_FragmentCallback cb,
842 void *cb_cls)
843{
844 struct Plugin *plugin = cls;
845
846 *returned_fragments = 0;
847
848 struct GNUNET_PQ_QueryParam params_select[] = {
849 GNUNET_PQ_query_param_auto_from_type (channel_key),
850 GNUNET_PQ_query_param_uint64 (&fragment_limit),
851 GNUNET_PQ_query_param_end
852 };
853
854 return fragment_select (plugin,
855 "select_latest_fragments",
856 params_select,
857 returned_fragments,
858 cb, cb_cls);
859}
860
861
862/**
863 * Retrieve all fragments of a message ID range.
864 *
865 * @see GNUNET_PSYCSTORE_message_get()
866 *
867 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
868 */
869static int
870message_get (void *cls,
871 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
872 uint64_t first_message_id,
873 uint64_t last_message_id,
874 uint64_t fragment_limit,
875 uint64_t *returned_fragments,
876 GNUNET_PSYCSTORE_FragmentCallback cb,
877 void *cb_cls)
878{
879 struct Plugin *plugin = cls;
880 struct GNUNET_PQ_QueryParam params_select[] = {
881 GNUNET_PQ_query_param_auto_from_type (channel_key),
882 GNUNET_PQ_query_param_uint64 (&first_message_id),
883 GNUNET_PQ_query_param_uint64 (&last_message_id),
884 GNUNET_PQ_query_param_uint64 (&fragment_limit),
885 GNUNET_PQ_query_param_end
886 };
887
888 if (0 == fragment_limit)
889 fragment_limit = INT64_MAX;
890 *returned_fragments = 0;
891 return fragment_select (plugin,
892 "select_messages",
893 params_select,
894 returned_fragments,
895 cb, cb_cls);
896}
897
898
899/**
900 * Retrieve all fragments of the latest messages.
901 *
902 * @see GNUNET_PSYCSTORE_message_get_latest()
903 *
904 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
905 */
906static int
907message_get_latest (void *cls,
908 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
909 uint64_t message_limit,
910 uint64_t *returned_fragments,
911 GNUNET_PSYCSTORE_FragmentCallback cb,
912 void *cb_cls)
913{
914 struct Plugin *plugin = cls;
915 struct GNUNET_PQ_QueryParam params_select[] = {
916 GNUNET_PQ_query_param_auto_from_type (channel_key),
917 GNUNET_PQ_query_param_auto_from_type (channel_key),
918 GNUNET_PQ_query_param_uint64 (&message_limit),
919 GNUNET_PQ_query_param_end
920 };
921
922 *returned_fragments = 0;
923 return fragment_select (plugin,
924 "select_latest_messages",
925 params_select,
926 returned_fragments,
927 cb, cb_cls);
928}
929
930
931/**
932 * Retrieve a fragment of message specified by its message ID and fragment
933 * offset.
934 *
935 * @see GNUNET_PSYCSTORE_message_get_fragment()
936 *
937 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
938 */
939static int
940message_get_fragment (void *cls,
941 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
942 uint64_t message_id,
943 uint64_t fragment_offset,
944 GNUNET_PSYCSTORE_FragmentCallback cb,
945 void *cb_cls)
946{
947 struct Plugin *plugin = cls;
948 const char *stmt = "select_message_fragment";
949
950 struct GNUNET_PQ_QueryParam params_select[] = {
951 GNUNET_PQ_query_param_auto_from_type (channel_key),
952 GNUNET_PQ_query_param_uint64 (&message_id),
953 GNUNET_PQ_query_param_uint64 (&fragment_offset),
954 GNUNET_PQ_query_param_end
955 };
956
957 /* Stack based closure */
958 struct FragmentRowsContext frc = {
959 .cb = cb,
960 .cb_cls = cb_cls,
961 .returned_fragments = NULL,
962 .ret = GNUNET_SYSERR
963 };
964
965 if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
966 stmt, params_select,
967 &fragment_rows, &frc))
968 return GNUNET_SYSERR;
969 return frc.ret; /* GNUNET_OK ?? */
970}
971
972/**
973 * Retrieve the max. values of message counters for a channel.
974 *
975 * @see GNUNET_PSYCSTORE_counters_get()
976 *
977 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
978 */
979static int
980counters_message_get (void *cls,
981 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
982 uint64_t *max_fragment_id,
983 uint64_t *max_message_id,
984 uint64_t *max_group_generation)
985{
986 struct Plugin *plugin = cls;
987
988 const char *stmt = "select_counters_message";
989
990 struct GNUNET_PQ_QueryParam params_select[] = {
991 GNUNET_PQ_query_param_auto_from_type (channel_key),
992 GNUNET_PQ_query_param_end
993 };
994
995 struct GNUNET_PQ_ResultSpec results_select[] = {
996 GNUNET_PQ_result_spec_uint64 ("fragment_id", max_fragment_id),
997 GNUNET_PQ_result_spec_uint64 ("message_id", max_message_id),
998 GNUNET_PQ_result_spec_uint64 ("group_generation", max_group_generation),
999 GNUNET_PQ_result_spec_end
1000 };
1001
1002 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1003 GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt,
1004 params_select, results_select))
1005 return GNUNET_SYSERR;
1006
1007 return GNUNET_OK;
1008}
1009
1010/**
1011 * Retrieve the max. values of state counters for a channel.
1012 *
1013 * @see GNUNET_PSYCSTORE_counters_get()
1014 *
1015 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1016 */
1017static int
1018counters_state_get (void *cls,
1019 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1020 uint64_t *max_state_message_id)
1021{
1022 struct Plugin *plugin = cls;
1023
1024 const char *stmt = "select_counters_state";
1025
1026 struct GNUNET_PQ_QueryParam params_select[] = {
1027 GNUNET_PQ_query_param_auto_from_type (channel_key),
1028 GNUNET_PQ_query_param_end
1029 };
1030
1031 struct GNUNET_PQ_ResultSpec results_select[] = {
1032 GNUNET_PQ_result_spec_uint64 ("max_state_message_id", max_state_message_id),
1033 GNUNET_PQ_result_spec_end
1034 };
1035
1036 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1037 GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt,
1038 params_select, results_select))
1039 return GNUNET_SYSERR;
1040
1041 return GNUNET_OK;
1042}
1043
1044
1045/**
1046 * Assign a value to a state variable.
1047 *
1048 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1049 */
1050static int
1051state_assign (struct Plugin *plugin, const char *stmt,
1052 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1053 const char *name, const void *value, size_t value_size)
1054{
1055 struct GNUNET_PQ_QueryParam params[] = {
1056 GNUNET_PQ_query_param_auto_from_type (channel_key),
1057 GNUNET_PQ_query_param_string (name),
1058 GNUNET_PQ_query_param_fixed_size (value, value_size),
1059 GNUNET_PQ_query_param_end
1060 };
1061
1062 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1063 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
1064 return GNUNET_SYSERR;
1065
1066 return GNUNET_OK;
1067}
1068
1069
1070static int
1071update_message_id (struct Plugin *plugin,
1072 const char *stmt,
1073 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1074 uint64_t message_id)
1075{
1076 struct GNUNET_PQ_QueryParam params[] = {
1077 GNUNET_PQ_query_param_uint64 (&message_id),
1078 GNUNET_PQ_query_param_auto_from_type (channel_key),
1079 GNUNET_PQ_query_param_end
1080 };
1081
1082 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1083 GNUNET_PQ_eval_prepared_non_select (plugin->dbh, stmt, params))
1084 return GNUNET_SYSERR;
1085
1086 return GNUNET_OK;
1087}
1088
1089
1090/**
1091 * Begin modifying current state.
1092 */
1093static int
1094state_modify_begin (void *cls,
1095 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1096 uint64_t message_id, uint64_t state_delta)
1097{
1098 struct Plugin *plugin = cls;
1099
1100 if (state_delta > 0)
1101 {
1102 /**
1103 * We can only apply state modifiers in the current message if modifiers in
1104 * the previous stateful message (message_id - state_delta) were already
1105 * applied.
1106 */
1107
1108 uint64_t max_state_message_id = 0;
1109 int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
1110 switch (ret)
1111 {
1112 case GNUNET_OK:
1113 case GNUNET_NO: // no state yet
1114 ret = GNUNET_OK;
1115 break;
1116
1117 default:
1118 return ret;
1119 }
1120
1121 if (max_state_message_id < message_id - state_delta)
1122 return GNUNET_NO; /* some stateful messages not yet applied */
1123 else if (message_id - state_delta < max_state_message_id)
1124 return GNUNET_NO; /* changes already applied */
1125 }
1126
1127 if (TRANSACTION_NONE != plugin->transaction)
1128 {
1129 /** @todo FIXME: wait for other transaction to finish */
1130 return GNUNET_SYSERR;
1131 }
1132 return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
1133}
1134
1135
1136/**
1137 * Set the current value of state variable.
1138 *
1139 * @see GNUNET_PSYCSTORE_state_modify()
1140 *
1141 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1142 */
1143static int
1144state_modify_op (void *cls,
1145 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1146 enum GNUNET_PSYC_Operator op,
1147 const char *name, const void *value, size_t value_size)
1148{
1149 struct Plugin *plugin = cls;
1150 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1151
1152 switch (op)
1153 {
1154 case GNUNET_PSYC_OP_ASSIGN:
1155 return state_assign (plugin, "insert_state_current",
1156 channel_key, name, value, value_size);
1157
1158 default: /** @todo implement more state operations */
1159 GNUNET_break (0);
1160 return GNUNET_SYSERR;
1161 }
1162}
1163
1164
1165/**
1166 * End modifying current state.
1167 */
1168static int
1169state_modify_end (void *cls,
1170 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1171 uint64_t message_id)
1172{
1173 struct Plugin *plugin = cls;
1174 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1175
1176 return
1177 GNUNET_OK == exec_channel (plugin, "delete_state_empty", channel_key)
1178 && GNUNET_OK == update_message_id (plugin,
1179 "update_max_state_message_id",
1180 channel_key, message_id)
1181 && GNUNET_OK == transaction_commit (plugin)
1182 ? GNUNET_OK : GNUNET_SYSERR;
1183}
1184
1185
1186/**
1187 * Begin state synchronization.
1188 */
1189static int
1190state_sync_begin (void *cls,
1191 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1192{
1193 struct Plugin *plugin = cls;
1194 return exec_channel (plugin, "delete_state_sync", channel_key);
1195}
1196
1197
1198/**
1199 * Assign current value of a state variable.
1200 *
1201 * @see GNUNET_PSYCSTORE_state_modify()
1202 *
1203 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1204 */
1205static int
1206state_sync_assign (void *cls,
1207 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1208 const char *name, const void *value, size_t value_size)
1209{
1210 struct Plugin *plugin = cls;
1211 return state_assign (plugin, "insert_state_sync",
1212 channel_key, name, value, value_size);
1213}
1214
1215
1216/**
1217 * End modifying current state.
1218 */
1219static int
1220state_sync_end (void *cls,
1221 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1222 uint64_t max_state_message_id,
1223 uint64_t state_hash_message_id)
1224{
1225 struct Plugin *plugin = cls;
1226 int ret = GNUNET_SYSERR;
1227
1228 if (TRANSACTION_NONE != plugin->transaction)
1229 {
1230 /** @todo FIXME: wait for other transaction to finish */
1231 return GNUNET_SYSERR;
1232 }
1233
1234 GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
1235 && GNUNET_OK == exec_channel (plugin, "delete_state", channel_key)
1236 && GNUNET_OK == exec_channel (plugin, "insert_state_from_sync",
1237 channel_key)
1238 && GNUNET_OK == exec_channel (plugin, "delete_state_sync",
1239 channel_key)
1240 && GNUNET_OK == update_message_id (plugin,
1241 "update_state_hash_message_id",
1242 channel_key, state_hash_message_id)
1243 && GNUNET_OK == update_message_id (plugin,
1244 "update_max_state_message_id",
1245 channel_key, max_state_message_id)
1246 && GNUNET_OK == transaction_commit (plugin)
1247 ? ret = GNUNET_OK
1248 : transaction_rollback (plugin);
1249 return ret;
1250}
1251
1252
1253/**
1254 * Delete the whole state.
1255 *
1256 * @see GNUNET_PSYCSTORE_state_reset()
1257 *
1258 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1259 */
1260static int
1261state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1262{
1263 struct Plugin *plugin = cls;
1264 return exec_channel (plugin, "delete_state", channel_key);
1265}
1266
1267
1268/**
1269 * Update signed values of state variables in the state store.
1270 *
1271 * @see GNUNET_PSYCSTORE_state_hash_update()
1272 *
1273 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1274 */
1275static int
1276state_update_signed (void *cls,
1277 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1278{
1279 struct Plugin *plugin = cls;
1280 return exec_channel (plugin, "update_state_signed", channel_key);
1281}
1282
1283
1284/**
1285 * Retrieve a state variable by name.
1286 *
1287 * @see GNUNET_PSYCSTORE_state_get()
1288 *
1289 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1290 */
1291static int
1292state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1293 const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1294{
1295 struct Plugin *plugin = cls;
1296
1297 const char *stmt = "select_state_one";
1298
1299 struct GNUNET_PQ_QueryParam params_select[] = {
1300 GNUNET_PQ_query_param_auto_from_type (channel_key),
1301 GNUNET_PQ_query_param_string (name),
1302 GNUNET_PQ_query_param_end
1303 };
1304
1305 void *value_current = NULL;
1306 size_t value_size = 0;
1307
1308 struct GNUNET_PQ_ResultSpec results_select[] = {
1309 GNUNET_PQ_result_spec_variable_size ("value_current", &value_current, &value_size),
1310 GNUNET_PQ_result_spec_end
1311 };
1312
1313 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
1314 GNUNET_PQ_eval_prepared_singleton_select (plugin->dbh, stmt,
1315 params_select, results_select))
1316 return GNUNET_SYSERR;
1317
1318 return cb (cb_cls, name, value_current,
1319 value_size);
1320}
1321
1322
1323
1324/**
1325 * Closure for #get_state_cb.
1326 */
1327struct GetStateContext {
1328 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key;
1329 // const char *name,
1330 GNUNET_PSYCSTORE_StateCallback cb;
1331 void *cb_cls;
1332
1333 const char *value_id;
1334
1335 /* I preserved this but I do not see the point since
1336 * it cannot stop the loop early and gets overwritten ?? */
1337 int ret;
1338};
1339
1340
1341/**
1342 * Callback that retrieves the results of a SELECT statement
1343 * reading form the state table.
1344 *
1345 * Only passed to GNUNET_PQ_eval_prepared_multi_select and
1346 * has type GNUNET_PQ_PostgresResultHandler.
1347 *
1348 * @param cls closure
1349 * @param result the postgres result
1350 * @param num_result the number of results in @a result
1351 */
1352static void
1353get_state_cb (void *cls,
1354 PGresult *res,
1355 unsigned int num_results)
1356{
1357 struct GetStateContext *c = cls;
1358
1359 for (unsigned int i=0;i<num_results;i++)
1360 {
1361 char *name = "";
1362 void *value = NULL;
1363 size_t value_size = 0;
1364
1365 struct GNUNET_PQ_ResultSpec results[] = {
1366 GNUNET_PQ_result_spec_string ("name", &name),
1367 GNUNET_PQ_result_spec_variable_size (c->value_id, &value, &value_size),
1368 GNUNET_PQ_result_spec_end
1369 };
1370
1371 if (GNUNET_YES != GNUNET_PQ_extract_result (res, results, i))
1372 {
1373 GNUNET_PQ_cleanup_result(results); /* previously invoked via PQclear?? */
1374 break; /* nothing more?? */
1375 }
1376
1377 c->ret = c->cb (c->cb_cls, (const char *) name, value, value_size);
1378 GNUNET_PQ_cleanup_result(results);
1379 }
1380}
1381
1382/**
1383 * Retrieve all state variables for a channel with the given prefix.
1384 *
1385 * @see GNUNET_PSYCSTORE_state_get_prefix()
1386 *
1387 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1388 */
1389static int
1390state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1391 const char *name, GNUNET_PSYCSTORE_StateCallback cb,
1392 void *cb_cls)
1393{
1394 struct Plugin *plugin = cls;
1395
1396 const char *stmt = "select_state_prefix";
1397
1398 uint32_t name_len = (uint32_t) strlen (name);
1399
1400 struct GNUNET_PQ_QueryParam params_select[] = {
1401 GNUNET_PQ_query_param_auto_from_type (channel_key),
1402 GNUNET_PQ_query_param_string (name),
1403 GNUNET_PQ_query_param_uint32 (&name_len),
1404 GNUNET_PQ_query_param_string (name),
1405 GNUNET_PQ_query_param_end
1406 };
1407
1408 struct GetStateContext gsc = {
1409 .cb = cb,
1410 .cb_cls = cb_cls,
1411 .value_id = "value_current",
1412 .ret = GNUNET_NO
1413 };
1414
1415 if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
1416 stmt, params_select,
1417 &get_state_cb, &gsc))
1418 return GNUNET_SYSERR;
1419 return gsc.ret; /* GNUNET_OK ?? */
1420}
1421
1422
1423/**
1424 * Retrieve all signed state variables for a channel.
1425 *
1426 * @see GNUNET_PSYCSTORE_state_get_signed()
1427 *
1428 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1429 */
1430static int
1431state_get_signed (void *cls,
1432 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1433 GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1434{
1435 struct Plugin *plugin = cls;
1436
1437 const char *stmt = "select_state_signed";
1438
1439 struct GNUNET_PQ_QueryParam params_select[] = {
1440 GNUNET_PQ_query_param_auto_from_type (channel_key),
1441 GNUNET_PQ_query_param_end
1442 };
1443
1444 struct GetStateContext gsc = {
1445 .cb = cb,
1446 .cb_cls = cb_cls,
1447 .value_id = "value_signed",
1448 .ret = GNUNET_NO
1449 };
1450
1451 if (0 > GNUNET_PQ_eval_prepared_multi_select (plugin->dbh,
1452 stmt, params_select,
1453 &get_state_cb, &gsc))
1454 return GNUNET_SYSERR;
1455 return gsc.ret; /* GNUNET_OK ?? */
1456}
1457
1458
1459/**
1460 * Entry point for the plugin.
1461 *
1462 * @param cls The struct GNUNET_CONFIGURATION_Handle.
1463 * @return NULL on error, otherwise the plugin context
1464 */
1465void *
1466libgnunet_plugin_psycstore_postgres_init (void *cls)
1467{
1468 static struct Plugin plugin;
1469 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1470 struct GNUNET_PSYCSTORE_PluginFunctions *api;
1471
1472 if (NULL != plugin.cfg)
1473 return NULL; /* can only initialize once! */
1474 memset (&plugin, 0, sizeof (struct Plugin));
1475 plugin.cfg = cfg;
1476 if (GNUNET_OK != database_setup (&plugin))
1477 {
1478 database_shutdown (&plugin);
1479 return NULL;
1480 }
1481 api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
1482 api->cls = &plugin;
1483 api->membership_store = &postgres_membership_store;
1484 api->membership_test = &membership_test;
1485 api->fragment_store = &fragment_store;
1486 api->message_add_flags = &message_add_flags;
1487 api->fragment_get = &fragment_get;
1488 api->fragment_get_latest = &fragment_get_latest;
1489 api->message_get = &message_get;
1490 api->message_get_latest = &message_get_latest;
1491 api->message_get_fragment = &message_get_fragment;
1492 api->counters_message_get = &counters_message_get;
1493 api->counters_state_get = &counters_state_get;
1494 api->state_modify_begin = &state_modify_begin;
1495 api->state_modify_op = &state_modify_op;
1496 api->state_modify_end = &state_modify_end;
1497 api->state_sync_begin = &state_sync_begin;
1498 api->state_sync_assign = &state_sync_assign;
1499 api->state_sync_end = &state_sync_end;
1500 api->state_reset = &state_reset;
1501 api->state_update_signed = &state_update_signed;
1502 api->state_get = &state_get;
1503 api->state_get_prefix = &state_get_prefix;
1504 api->state_get_signed = &state_get_signed;
1505
1506 LOG (GNUNET_ERROR_TYPE_INFO, _("Postgres database running\n"));
1507 return api;
1508}
1509
1510
1511/**
1512 * Exit point from the plugin.
1513 *
1514 * @param cls The plugin context (as returned by "init")
1515 * @return Always NULL
1516 */
1517void *
1518libgnunet_plugin_psycstore_postgres_done (void *cls)
1519{
1520 struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
1521 struct Plugin *plugin = api->cls;
1522
1523 database_shutdown (plugin);
1524 plugin->cfg = NULL;
1525 GNUNET_free (api);
1526 LOG (GNUNET_ERROR_TYPE_DEBUG, "Postgres plugin has finished\n");
1527 return NULL;
1528}
1529
1530/* end of plugin_psycstore_postgres.c */
diff --git a/src/psycstore/plugin_psycstore_sqlite.c b/src/psycstore/plugin_psycstore_sqlite.c
new file mode 100644
index 0000000..24de383
--- /dev/null
+++ b/src/psycstore/plugin_psycstore_sqlite.c
@@ -0,0 +1,1948 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file psycstore/plugin_psycstore_sqlite.c
23 * @brief sqlite-based psycstore backend
24 * @author Gabor X Toth
25 * @author Christian Grothoff
26 */
27
28/*
29 * FIXME: SQLite3 only supports signed 64-bit integers natively,
30 * thus it can only store 63 bits of the uint64_t's.
31 */
32
33#include "platform.h"
34#include "gnunet_psycstore_plugin.h"
35#include "gnunet_psycstore_service.h"
36#include "gnunet_multicast_service.h"
37#include "gnunet_crypto_lib.h"
38#include "gnunet_psyc_util_lib.h"
39#include "psycstore.h"
40#include <sqlite3.h>
41
42/**
43 * After how many ms "busy" should a DB operation fail for good? A
44 * low value makes sure that we are more responsive to requests
45 * (especially PUTs). A high value guarantees a higher success rate
46 * (SELECTs in iterate can take several seconds despite LIMIT=1).
47 *
48 * The default value of 1s should ensure that users do not experience
49 * huge latencies while at the same time allowing operations to
50 * succeed with reasonable probability.
51 */
52#define BUSY_TIMEOUT_MS 1000
53
54#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
55
56/**
57 * Log an error message at log-level 'level' that indicates
58 * a failure of the command 'cmd' on file 'filename'
59 * with the message given by strerror(errno).
60 */
61#define LOG_SQLITE(db, level, cmd) do { GNUNET_log_from (level, "psycstore-sqlite", _("`%s' failed at %s:%d with error: %s (%d)\n"), cmd, __FILE__, __LINE__, sqlite3_errmsg(db->dbh), sqlite3_errcode(db->dbh)); } while(0)
62
63#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-sqlite", __VA_ARGS__)
64
65enum Transactions {
66 TRANSACTION_NONE = 0,
67 TRANSACTION_STATE_MODIFY,
68 TRANSACTION_STATE_SYNC,
69};
70
71/**
72 * Context for all functions in this plugin.
73 */
74struct Plugin
75{
76
77 const struct GNUNET_CONFIGURATION_Handle *cfg;
78
79 /**
80 * Database filename.
81 */
82 char *fn;
83
84 /**
85 * Native SQLite database handle.
86 */
87 sqlite3 *dbh;
88
89 /**
90 * Current transaction.
91 */
92 enum Transactions transaction;
93
94 sqlite3_stmt *transaction_begin;
95
96 sqlite3_stmt *transaction_commit;
97
98 sqlite3_stmt *transaction_rollback;
99
100 /**
101 * Precompiled SQL for channel_key_store()
102 */
103 sqlite3_stmt *insert_channel_key;
104
105 /**
106 * Precompiled SQL for slave_key_store()
107 */
108 sqlite3_stmt *insert_slave_key;
109
110
111 /**
112 * Precompiled SQL for membership_store()
113 */
114 sqlite3_stmt *insert_membership;
115
116 /**
117 * Precompiled SQL for membership_test()
118 */
119 sqlite3_stmt *select_membership;
120
121
122 /**
123 * Precompiled SQL for fragment_store()
124 */
125 sqlite3_stmt *insert_fragment;
126
127 /**
128 * Precompiled SQL for message_add_flags()
129 */
130 sqlite3_stmt *update_message_flags;
131
132 /**
133 * Precompiled SQL for fragment_get()
134 */
135 sqlite3_stmt *select_fragments;
136
137 /**
138 * Precompiled SQL for fragment_get()
139 */
140 sqlite3_stmt *select_latest_fragments;
141
142 /**
143 * Precompiled SQL for message_get()
144 */
145 sqlite3_stmt *select_messages;
146
147 /**
148 * Precompiled SQL for message_get()
149 */
150 sqlite3_stmt *select_latest_messages;
151
152 /**
153 * Precompiled SQL for message_get_fragment()
154 */
155 sqlite3_stmt *select_message_fragment;
156
157 /**
158 * Precompiled SQL for counters_get_message()
159 */
160 sqlite3_stmt *select_counters_message;
161
162 /**
163 * Precompiled SQL for counters_get_state()
164 */
165 sqlite3_stmt *select_counters_state;
166
167 /**
168 * Precompiled SQL for state_modify_end()
169 */
170 sqlite3_stmt *update_state_hash_message_id;
171
172 /**
173 * Precompiled SQL for state_sync_end()
174 */
175 sqlite3_stmt *update_max_state_message_id;
176
177 /**
178 * Precompiled SQL for state_modify_op()
179 */
180 sqlite3_stmt *insert_state_current;
181
182 /**
183 * Precompiled SQL for state_modify_end()
184 */
185 sqlite3_stmt *delete_state_empty;
186
187 /**
188 * Precompiled SQL for state_set_signed()
189 */
190 sqlite3_stmt *update_state_signed;
191
192 /**
193 * Precompiled SQL for state_sync()
194 */
195 sqlite3_stmt *insert_state_sync;
196
197 /**
198 * Precompiled SQL for state_sync()
199 */
200 sqlite3_stmt *delete_state;
201
202 /**
203 * Precompiled SQL for state_sync()
204 */
205 sqlite3_stmt *insert_state_from_sync;
206
207 /**
208 * Precompiled SQL for state_sync()
209 */
210 sqlite3_stmt *delete_state_sync;
211
212 /**
213 * Precompiled SQL for state_get_signed()
214 */
215 sqlite3_stmt *select_state_signed;
216
217 /**
218 * Precompiled SQL for state_get()
219 */
220 sqlite3_stmt *select_state_one;
221
222 /**
223 * Precompiled SQL for state_get_prefix()
224 */
225 sqlite3_stmt *select_state_prefix;
226
227};
228
229#if DEBUG_PSYCSTORE
230
231static void
232sql_trace (void *cls, const char *sql)
233{
234 LOG (GNUNET_ERROR_TYPE_DEBUG, "SQL query:\n%s\n", sql);
235}
236
237#endif
238
239/**
240 * @brief Prepare a SQL statement
241 *
242 * @param dbh handle to the database
243 * @param sql SQL statement, UTF-8 encoded
244 * @param stmt set to the prepared statement
245 * @return 0 on success
246 */
247static int
248sql_prepare (sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt)
249{
250 char *tail;
251 int result;
252
253 result = sqlite3_prepare_v2 (dbh, sql, strlen (sql), stmt,
254 (const char **) &tail);
255 LOG (GNUNET_ERROR_TYPE_DEBUG,
256 "Prepared `%s' / %p: %d\n", sql, *stmt, result);
257 if (result != SQLITE_OK)
258 LOG (GNUNET_ERROR_TYPE_ERROR,
259 _("Error preparing SQL query: %s\n %s\n"),
260 sqlite3_errmsg (dbh), sql);
261 return result;
262}
263
264
265/**
266 * @brief Prepare a SQL statement
267 *
268 * @param dbh handle to the database
269 * @param sql SQL statement, UTF-8 encoded
270 * @return 0 on success
271 */
272static int
273sql_exec (sqlite3 *dbh, const char *sql)
274{
275 int result;
276
277 result = sqlite3_exec (dbh, sql, NULL, NULL, NULL);
278 LOG (GNUNET_ERROR_TYPE_DEBUG,
279 "Executed `%s' / %d\n", sql, result);
280 if (result != SQLITE_OK)
281 LOG (GNUNET_ERROR_TYPE_ERROR,
282 _("Error executing SQL query: %s\n %s\n"),
283 sqlite3_errmsg (dbh), sql);
284 return result;
285}
286
287
288/**
289 * Initialize the database connections and associated
290 * data structures (create tables and indices
291 * as needed as well).
292 *
293 * @param plugin the plugin context (state for this module)
294 * @return GNUNET_OK on success
295 */
296static int
297database_setup (struct Plugin *plugin)
298{
299 char *filename;
300
301 if (GNUNET_OK !=
302 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "psycstore-sqlite",
303 "FILENAME", &filename))
304 {
305 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
306 "psycstore-sqlite", "FILENAME");
307 return GNUNET_SYSERR;
308 }
309 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
310 {
311 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (filename))
312 {
313 GNUNET_break (0);
314 GNUNET_free (filename);
315 return GNUNET_SYSERR;
316 }
317 }
318 /* filename should be UTF-8-encoded. If it isn't, it's a bug */
319 plugin->fn = filename;
320
321 /* Open database and precompile statements */
322 if (SQLITE_OK != sqlite3_open (plugin->fn, &plugin->dbh))
323 {
324 LOG (GNUNET_ERROR_TYPE_ERROR,
325 _("Unable to initialize SQLite: %s.\n"),
326 sqlite3_errmsg (plugin->dbh));
327 return GNUNET_SYSERR;
328 }
329
330#if DEBUG_PSYCSTORE
331 sqlite3_trace (plugin->dbh, &sql_trace, NULL);
332#endif
333
334 sql_exec (plugin->dbh, "PRAGMA temp_store=MEMORY");
335 sql_exec (plugin->dbh, "PRAGMA synchronous=NORMAL");
336 sql_exec (plugin->dbh, "PRAGMA legacy_file_format=OFF");
337 sql_exec (plugin->dbh, "PRAGMA auto_vacuum=INCREMENTAL");
338 sql_exec (plugin->dbh, "PRAGMA encoding=\"UTF-8\"");
339#if ! DEBUG_PSYCSTORE
340 sql_exec (plugin->dbh, "PRAGMA locking_mode=EXCLUSIVE");
341#endif
342 sql_exec (plugin->dbh, "PRAGMA page_size=4096");
343
344 sqlite3_busy_timeout (plugin->dbh, BUSY_TIMEOUT_MS);
345
346 /* Create tables */
347
348 sql_exec (plugin->dbh,
349 "CREATE TABLE IF NOT EXISTS channels (\n"
350 " id INTEGER PRIMARY KEY,\n"
351 " pub_key BLOB(32) UNIQUE,\n"
352 " max_state_message_id INTEGER,\n" // last applied state message ID
353 " state_hash_message_id INTEGER\n" // last message ID with a state hash
354 ");");
355
356 sql_exec (plugin->dbh,
357 "CREATE TABLE IF NOT EXISTS slaves (\n"
358 " id INTEGER PRIMARY KEY,\n"
359 " pub_key BLOB(32) UNIQUE\n"
360 ");");
361
362 sql_exec (plugin->dbh,
363 "CREATE TABLE IF NOT EXISTS membership (\n"
364 " channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
365 " slave_id INTEGER NOT NULL REFERENCES slaves(id),\n"
366 " did_join INTEGER NOT NULL,\n"
367 " announced_at INTEGER NOT NULL,\n"
368 " effective_since INTEGER NOT NULL,\n"
369 " group_generation INTEGER NOT NULL\n"
370 ");");
371 sql_exec (plugin->dbh,
372 "CREATE INDEX IF NOT EXISTS idx_membership_channel_id_slave_id "
373 "ON membership (channel_id, slave_id);");
374
375 /** @todo messages table: add method_name column */
376 sql_exec (plugin->dbh,
377 "CREATE TABLE IF NOT EXISTS messages (\n"
378 " channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
379 " hop_counter INTEGER NOT NULL,\n"
380 " signature BLOB,\n"
381 " purpose BLOB,\n"
382 " fragment_id INTEGER NOT NULL,\n"
383 " fragment_offset INTEGER NOT NULL,\n"
384 " message_id INTEGER NOT NULL,\n"
385 " group_generation INTEGER NOT NULL,\n"
386 " multicast_flags INTEGER NOT NULL,\n"
387 " psycstore_flags INTEGER NOT NULL,\n"
388 " data BLOB,\n"
389 " PRIMARY KEY (channel_id, fragment_id),\n"
390 " UNIQUE (channel_id, message_id, fragment_offset)\n"
391 ");");
392
393 sql_exec (plugin->dbh,
394 "CREATE TABLE IF NOT EXISTS state (\n"
395 " channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
396 " name TEXT NOT NULL,\n"
397 " value_current BLOB,\n"
398 " value_signed BLOB,\n"
399 " PRIMARY KEY (channel_id, name)\n"
400 ");");
401
402 sql_exec (plugin->dbh,
403 "CREATE TABLE IF NOT EXISTS state_sync (\n"
404 " channel_id INTEGER NOT NULL REFERENCES channels(id),\n"
405 " name TEXT NOT NULL,\n"
406 " value BLOB,\n"
407 " PRIMARY KEY (channel_id, name)\n"
408 ");");
409
410 /* Prepare statements */
411
412 sql_prepare (plugin->dbh, "BEGIN;", &plugin->transaction_begin);
413
414 sql_prepare (plugin->dbh, "COMMIT;", &plugin->transaction_commit);
415
416 sql_prepare (plugin->dbh, "ROLLBACK;", &plugin->transaction_rollback);
417
418 sql_prepare (plugin->dbh,
419 "INSERT OR IGNORE INTO channels (pub_key) VALUES (?);",
420 &plugin->insert_channel_key);
421
422 sql_prepare (plugin->dbh,
423 "INSERT OR IGNORE INTO slaves (pub_key) VALUES (?);",
424 &plugin->insert_slave_key);
425
426 sql_prepare (plugin->dbh,
427 "INSERT INTO membership\n"
428 " (channel_id, slave_id, did_join, announced_at,\n"
429 " effective_since, group_generation)\n"
430 "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
431 " (SELECT id FROM slaves WHERE pub_key = ?),\n"
432 " ?, ?, ?, ?);",
433 &plugin->insert_membership);
434
435 sql_prepare (plugin->dbh,
436 "SELECT did_join FROM membership\n"
437 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
438 " AND slave_id = (SELECT id FROM slaves WHERE pub_key = ?)\n"
439 " AND effective_since <= ? AND did_join = 1\n"
440 "ORDER BY announced_at DESC LIMIT 1;",
441 &plugin->select_membership);
442
443 sql_prepare (plugin->dbh,
444 "INSERT OR IGNORE INTO messages\n"
445 " (channel_id, hop_counter, signature, purpose,\n"
446 " fragment_id, fragment_offset, message_id,\n"
447 " group_generation, multicast_flags, psycstore_flags, data)\n"
448 "VALUES ((SELECT id FROM channels WHERE pub_key = ?),\n"
449 " ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
450 &plugin->insert_fragment);
451
452 sql_prepare (plugin->dbh,
453 "UPDATE messages\n"
454 "SET psycstore_flags = psycstore_flags | ?\n"
455 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
456 " AND message_id = ? AND fragment_offset = 0;",
457 &plugin->update_message_flags);
458
459 sql_prepare (plugin->dbh,
460 "SELECT hop_counter, signature, purpose, fragment_id,\n"
461 " fragment_offset, message_id, group_generation,\n"
462 " multicast_flags, psycstore_flags, data\n"
463 "FROM messages\n"
464 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
465 " AND ? <= fragment_id AND fragment_id <= ?;",
466 &plugin->select_fragments);
467
468 /** @todo select_messages: add method_prefix filter */
469 sql_prepare (plugin->dbh,
470 "SELECT hop_counter, signature, purpose, fragment_id,\n"
471 " fragment_offset, message_id, group_generation,\n"
472 " multicast_flags, psycstore_flags, data\n"
473 "FROM messages\n"
474 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
475 " AND ? <= message_id AND message_id <= ?"
476 "LIMIT ?;",
477 &plugin->select_messages);
478
479 sql_prepare (plugin->dbh,
480 "SELECT * FROM\n"
481 "(SELECT hop_counter, signature, purpose, fragment_id,\n"
482 " fragment_offset, message_id, group_generation,\n"
483 " multicast_flags, psycstore_flags, data\n"
484 " FROM messages\n"
485 " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
486 " ORDER BY fragment_id DESC\n"
487 " LIMIT ?)\n"
488 "ORDER BY fragment_id;",
489 &plugin->select_latest_fragments);
490
491 /** @todo select_latest_messages: add method_prefix filter */
492 sql_prepare (plugin->dbh,
493 "SELECT hop_counter, signature, purpose, fragment_id,\n"
494 " fragment_offset, message_id, group_generation,\n"
495 " multicast_flags, psycstore_flags, data\n"
496 "FROM messages\n"
497 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
498 " AND message_id IN\n"
499 " (SELECT message_id\n"
500 " FROM messages\n"
501 " WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
502 " GROUP BY message_id\n"
503 " ORDER BY message_id\n"
504 " DESC LIMIT ?)\n"
505 "ORDER BY fragment_id;",
506 &plugin->select_latest_messages);
507
508 sql_prepare (plugin->dbh,
509 "SELECT hop_counter, signature, purpose, fragment_id,\n"
510 " fragment_offset, message_id, group_generation,\n"
511 " multicast_flags, psycstore_flags, data\n"
512 "FROM messages\n"
513 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
514 " AND message_id = ? AND fragment_offset = ?;",
515 &plugin->select_message_fragment);
516
517 sql_prepare (plugin->dbh,
518 "SELECT fragment_id, message_id, group_generation\n"
519 "FROM messages\n"
520 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
521 "ORDER BY fragment_id DESC LIMIT 1;",
522 &plugin->select_counters_message);
523
524 sql_prepare (plugin->dbh,
525 "SELECT max_state_message_id\n"
526 "FROM channels\n"
527 "WHERE pub_key = ? AND max_state_message_id IS NOT NULL;",
528 &plugin->select_counters_state);
529
530 sql_prepare (plugin->dbh,
531 "UPDATE channels\n"
532 "SET max_state_message_id = ?\n"
533 "WHERE pub_key = ?;",
534 &plugin->update_max_state_message_id);
535
536 sql_prepare (plugin->dbh,
537 "UPDATE channels\n"
538 "SET state_hash_message_id = ?\n"
539 "WHERE pub_key = ?;",
540 &plugin->update_state_hash_message_id);
541
542 sql_prepare (plugin->dbh,
543 "INSERT OR REPLACE INTO state\n"
544 " (channel_id, name, value_current, value_signed)\n"
545 "SELECT new.channel_id, new.name,\n"
546 " new.value_current, old.value_signed\n"
547 "FROM (SELECT (SELECT id FROM channels WHERE pub_key = ?)\n"
548 " AS channel_id,\n"
549 " ? AS name, ? AS value_current) AS new\n"
550 "LEFT JOIN (SELECT channel_id, name, value_signed\n"
551 " FROM state) AS old\n"
552 "ON new.channel_id = old.channel_id AND new.name = old.name;",
553 &plugin->insert_state_current);
554
555 sql_prepare (plugin->dbh,
556 "DELETE FROM state\n"
557 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
558 " AND (value_current IS NULL OR length(value_current) = 0)\n"
559 " AND (value_signed IS NULL OR length(value_signed) = 0);",
560 &plugin->delete_state_empty);
561
562 sql_prepare (plugin->dbh,
563 "UPDATE state\n"
564 "SET value_signed = value_current\n"
565 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
566 &plugin->update_state_signed);
567
568 sql_prepare (plugin->dbh,
569 "DELETE FROM state\n"
570 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
571 &plugin->delete_state);
572
573 sql_prepare (plugin->dbh,
574 "INSERT INTO state_sync (channel_id, name, value)\n"
575 "VALUES ((SELECT id FROM channels WHERE pub_key = ?), ?, ?);",
576 &plugin->insert_state_sync);
577
578 sql_prepare (plugin->dbh,
579 "INSERT INTO state\n"
580 " (channel_id, name, value_current, value_signed)\n"
581 "SELECT channel_id, name, value, value\n"
582 "FROM state_sync\n"
583 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
584 &plugin->insert_state_from_sync);
585
586 sql_prepare (plugin->dbh,
587 "DELETE FROM state_sync\n"
588 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?);",
589 &plugin->delete_state_sync);
590
591 sql_prepare (plugin->dbh,
592 "SELECT value_current\n"
593 "FROM state\n"
594 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
595 " AND name = ?;",
596 &plugin->select_state_one);
597
598 sql_prepare (plugin->dbh,
599 "SELECT name, value_current\n"
600 "FROM state\n"
601 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)\n"
602 " AND (name = ? OR substr(name, 1, ?) = ?);",
603 &plugin->select_state_prefix);
604
605 sql_prepare (plugin->dbh,
606 "SELECT name, value_signed\n"
607 "FROM state\n"
608 "WHERE channel_id = (SELECT id FROM channels WHERE pub_key = ?)"
609 " AND value_signed IS NOT NULL;",
610 &plugin->select_state_signed);
611
612 return GNUNET_OK;
613}
614
615
616/**
617 * Shutdown database connection and associate data
618 * structures.
619 * @param plugin the plugin context (state for this module)
620 */
621static void
622database_shutdown (struct Plugin *plugin)
623{
624 int result;
625 sqlite3_stmt *stmt;
626 while (NULL != (stmt = sqlite3_next_stmt (plugin->dbh, NULL)))
627 {
628 result = sqlite3_finalize (stmt);
629 if (SQLITE_OK != result)
630 LOG (GNUNET_ERROR_TYPE_WARNING,
631 "Failed to close statement %p: %d\n", stmt, result);
632 }
633 if (SQLITE_OK != sqlite3_close (plugin->dbh))
634 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR, "sqlite3_close");
635
636 GNUNET_free_non_null (plugin->fn);
637}
638
639/**
640 * Execute a prepared statement with a @a channel_key argument.
641 *
642 * @param plugin Plugin handle.
643 * @param stmt Statement to execute.
644 * @param channel_key Public key of the channel.
645 *
646 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
647 */
648static int
649exec_channel (struct Plugin *plugin, sqlite3_stmt *stmt,
650 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
651{
652 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
653 sizeof (*channel_key), SQLITE_STATIC))
654 {
655 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
656 "sqlite3_bind");
657 }
658 else if (SQLITE_DONE != sqlite3_step (stmt))
659 {
660 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
661 "sqlite3_step");
662 }
663
664 if (SQLITE_OK != sqlite3_reset (stmt))
665 {
666 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
667 "sqlite3_reset");
668 return GNUNET_SYSERR;
669 }
670
671 return GNUNET_OK;
672}
673
674/**
675 * Begin a transaction.
676 */
677static int
678transaction_begin (struct Plugin *plugin, enum Transactions transaction)
679{
680 sqlite3_stmt *stmt = plugin->transaction_begin;
681
682 if (SQLITE_DONE != sqlite3_step (stmt))
683 {
684 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
685 "sqlite3_step");
686 }
687 if (SQLITE_OK != sqlite3_reset (stmt))
688 {
689 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
690 "sqlite3_reset");
691 return GNUNET_SYSERR;
692 }
693
694 plugin->transaction = transaction;
695 return GNUNET_OK;
696}
697
698
699/**
700 * Commit current transaction.
701 */
702static int
703transaction_commit (struct Plugin *plugin)
704{
705 sqlite3_stmt *stmt = plugin->transaction_commit;
706
707 if (SQLITE_DONE != sqlite3_step (stmt))
708 {
709 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
710 "sqlite3_step");
711 }
712 if (SQLITE_OK != sqlite3_reset (stmt))
713 {
714 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
715 "sqlite3_reset");
716 return GNUNET_SYSERR;
717 }
718
719 plugin->transaction = TRANSACTION_NONE;
720 return GNUNET_OK;
721}
722
723
724/**
725 * Roll back current transaction.
726 */
727static int
728transaction_rollback (struct Plugin *plugin)
729{
730 sqlite3_stmt *stmt = plugin->transaction_rollback;
731
732 if (SQLITE_DONE != sqlite3_step (stmt))
733 {
734 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
735 "sqlite3_step");
736 }
737 if (SQLITE_OK != sqlite3_reset (stmt))
738 {
739 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
740 "sqlite3_reset");
741 return GNUNET_SYSERR;
742 }
743 plugin->transaction = TRANSACTION_NONE;
744 return GNUNET_OK;
745}
746
747
748static int
749channel_key_store (struct Plugin *plugin,
750 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
751{
752 sqlite3_stmt *stmt = plugin->insert_channel_key;
753
754 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
755 sizeof (*channel_key), SQLITE_STATIC))
756 {
757 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
758 "sqlite3_bind");
759 }
760 else if (SQLITE_DONE != sqlite3_step (stmt))
761 {
762 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
763 "sqlite3_step");
764 }
765
766 if (SQLITE_OK != sqlite3_reset (stmt))
767 {
768 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
769 "sqlite3_reset");
770 return GNUNET_SYSERR;
771 }
772
773 return GNUNET_OK;
774}
775
776
777static int
778slave_key_store (struct Plugin *plugin,
779 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key)
780{
781 sqlite3_stmt *stmt = plugin->insert_slave_key;
782
783 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, slave_key,
784 sizeof (*slave_key), SQLITE_STATIC))
785 {
786 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
787 "sqlite3_bind");
788 }
789 else if (SQLITE_DONE != sqlite3_step (stmt))
790 {
791 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
792 "sqlite3_step");
793 }
794
795
796 if (SQLITE_OK != sqlite3_reset (stmt))
797 {
798 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
799 "sqlite3_reset");
800 return GNUNET_SYSERR;
801 }
802
803 return GNUNET_OK;
804}
805
806
807/**
808 * Store join/leave events for a PSYC channel in order to be able to answer
809 * membership test queries later.
810 *
811 * @see GNUNET_PSYCSTORE_membership_store()
812 *
813 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
814 */
815static int
816sqlite_membership_store (void *cls,
817 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
818 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
819 int did_join,
820 uint64_t announced_at,
821 uint64_t effective_since,
822 uint64_t group_generation)
823{
824 struct Plugin *plugin = cls;
825 sqlite3_stmt *stmt = plugin->insert_membership;
826
827 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
828
829 if (announced_at > INT64_MAX ||
830 effective_since > INT64_MAX ||
831 group_generation > INT64_MAX)
832 {
833 GNUNET_break (0);
834 return GNUNET_SYSERR;
835 }
836
837 if (GNUNET_OK != channel_key_store (plugin, channel_key)
838 || GNUNET_OK != slave_key_store (plugin, slave_key))
839 return GNUNET_SYSERR;
840
841 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
842 sizeof (*channel_key), SQLITE_STATIC)
843 || SQLITE_OK != sqlite3_bind_blob (stmt, 2, slave_key,
844 sizeof (*slave_key), SQLITE_STATIC)
845 || SQLITE_OK != sqlite3_bind_int (stmt, 3, did_join)
846 || SQLITE_OK != sqlite3_bind_int64 (stmt, 4, announced_at)
847 || SQLITE_OK != sqlite3_bind_int64 (stmt, 5, effective_since)
848 || SQLITE_OK != sqlite3_bind_int64 (stmt, 6, group_generation))
849 {
850 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
851 "sqlite3_bind");
852 }
853 else if (SQLITE_DONE != sqlite3_step (stmt))
854 {
855 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
856 "sqlite3_step");
857 }
858
859 if (SQLITE_OK != sqlite3_reset (stmt))
860 {
861 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
862 "sqlite3_reset");
863 return GNUNET_SYSERR;
864 }
865
866 return GNUNET_OK;
867}
868
869/**
870 * Test if a member was admitted to the channel at the given message ID.
871 *
872 * @see GNUNET_PSYCSTORE_membership_test()
873 *
874 * @return #GNUNET_YES if the member was admitted, #GNUNET_NO if not,
875 * #GNUNET_SYSERR if there was en error.
876 */
877static int
878membership_test (void *cls,
879 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
880 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
881 uint64_t message_id)
882{
883 struct Plugin *plugin = cls;
884 sqlite3_stmt *stmt = plugin->select_membership;
885 int ret = GNUNET_SYSERR;
886
887 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
888 sizeof (*channel_key), SQLITE_STATIC)
889 || SQLITE_OK != sqlite3_bind_blob (stmt, 2, slave_key,
890 sizeof (*slave_key), SQLITE_STATIC)
891 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_id))
892 {
893 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
894 "sqlite3_bind");
895 }
896 else
897 {
898 switch (sqlite3_step (stmt))
899 {
900 case SQLITE_DONE:
901 ret = GNUNET_NO;
902 break;
903 case SQLITE_ROW:
904 ret = GNUNET_YES;
905 }
906 }
907
908 if (SQLITE_OK != sqlite3_reset (stmt))
909 {
910 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
911 "sqlite3_reset");
912 }
913
914 return ret;
915}
916
917/**
918 * Store a message fragment sent to a channel.
919 *
920 * @see GNUNET_PSYCSTORE_fragment_store()
921 *
922 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
923 */
924static int
925fragment_store (void *cls,
926 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
927 const struct GNUNET_MULTICAST_MessageHeader *msg,
928 uint32_t psycstore_flags)
929{
930 struct Plugin *plugin = cls;
931 sqlite3_stmt *stmt = plugin->insert_fragment;
932
933 GNUNET_assert (TRANSACTION_NONE == plugin->transaction);
934
935 uint64_t fragment_id = GNUNET_ntohll (msg->fragment_id);
936 uint64_t fragment_offset = GNUNET_ntohll (msg->fragment_offset);
937 uint64_t message_id = GNUNET_ntohll (msg->message_id);
938 uint64_t group_generation = GNUNET_ntohll (msg->group_generation);
939
940 if (fragment_id > INT64_MAX || fragment_offset > INT64_MAX ||
941 message_id > INT64_MAX || group_generation > INT64_MAX)
942 {
943 LOG (GNUNET_ERROR_TYPE_ERROR,
944 "Tried to store fragment with a field > INT64_MAX: "
945 "%lu, %lu, %lu, %lu\n", fragment_id, fragment_offset,
946 message_id, group_generation);
947 GNUNET_break (0);
948 return GNUNET_SYSERR;
949 }
950
951 if (GNUNET_OK != channel_key_store (plugin, channel_key))
952 return GNUNET_SYSERR;
953
954 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
955 sizeof (*channel_key), SQLITE_STATIC)
956 || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, ntohl (msg->hop_counter) )
957 || SQLITE_OK != sqlite3_bind_blob (stmt, 3, (const void *) &msg->signature,
958 sizeof (msg->signature), SQLITE_STATIC)
959 || SQLITE_OK != sqlite3_bind_blob (stmt, 4, (const void *) &msg->purpose,
960 sizeof (msg->purpose), SQLITE_STATIC)
961 || SQLITE_OK != sqlite3_bind_int64 (stmt, 5, fragment_id)
962 || SQLITE_OK != sqlite3_bind_int64 (stmt, 6, fragment_offset)
963 || SQLITE_OK != sqlite3_bind_int64 (stmt, 7, message_id)
964 || SQLITE_OK != sqlite3_bind_int64 (stmt, 8, group_generation)
965 || SQLITE_OK != sqlite3_bind_int64 (stmt, 9, ntohl (msg->flags))
966 || SQLITE_OK != sqlite3_bind_int64 (stmt, 10, psycstore_flags)
967 || SQLITE_OK != sqlite3_bind_blob (stmt, 11, (const void *) &msg[1],
968 ntohs (msg->header.size)
969 - sizeof (*msg), SQLITE_STATIC))
970 {
971 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
972 "sqlite3_bind");
973 }
974 else if (SQLITE_DONE != sqlite3_step (stmt))
975 {
976 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
977 "sqlite3_step");
978 }
979
980 if (SQLITE_OK != sqlite3_reset (stmt))
981 {
982 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
983 "sqlite3_reset");
984 return GNUNET_SYSERR;
985 }
986
987 return GNUNET_OK;
988}
989
990/**
991 * Set additional flags for a given message.
992 *
993 * They are OR'd with any existing flags set.
994 *
995 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
996 */
997static int
998message_add_flags (void *cls,
999 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1000 uint64_t message_id,
1001 uint32_t psycstore_flags)
1002{
1003 struct Plugin *plugin = cls;
1004 sqlite3_stmt *stmt = plugin->update_message_flags;
1005 int ret = GNUNET_SYSERR;
1006
1007 if (SQLITE_OK != sqlite3_bind_int64 (stmt, 1, psycstore_flags)
1008 || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
1009 sizeof (*channel_key), SQLITE_STATIC)
1010 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_id))
1011 {
1012 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1013 "sqlite3_bind");
1014 }
1015 else
1016 {
1017 switch (sqlite3_step (stmt))
1018 {
1019 case SQLITE_DONE:
1020 ret = sqlite3_total_changes (plugin->dbh) > 0 ? GNUNET_OK : GNUNET_NO;
1021 break;
1022 default:
1023 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1024 "sqlite3_step");
1025 }
1026 }
1027
1028 if (SQLITE_OK != sqlite3_reset (stmt))
1029 {
1030 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1031 "sqlite3_reset");
1032 return GNUNET_SYSERR;
1033 }
1034
1035 return ret;
1036}
1037
1038static int
1039fragment_row (sqlite3_stmt *stmt, GNUNET_PSYCSTORE_FragmentCallback cb,
1040 void *cb_cls)
1041{
1042 int data_size = sqlite3_column_bytes (stmt, 9);
1043 struct GNUNET_MULTICAST_MessageHeader *msg
1044 = GNUNET_malloc (sizeof (*msg) + data_size);
1045
1046 msg->header.size = htons (sizeof (*msg) + data_size);
1047 msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
1048 msg->hop_counter = htonl ((uint32_t) sqlite3_column_int64 (stmt, 0));
1049 GNUNET_memcpy (&msg->signature,
1050 sqlite3_column_blob (stmt, 1),
1051 sqlite3_column_bytes (stmt, 1));
1052 GNUNET_memcpy (&msg->purpose,
1053 sqlite3_column_blob (stmt, 2),
1054 sqlite3_column_bytes (stmt, 2));
1055 msg->fragment_id = GNUNET_htonll (sqlite3_column_int64 (stmt, 3));
1056 msg->fragment_offset = GNUNET_htonll (sqlite3_column_int64 (stmt, 4));
1057 msg->message_id = GNUNET_htonll (sqlite3_column_int64 (stmt, 5));
1058 msg->group_generation = GNUNET_htonll (sqlite3_column_int64 (stmt, 6));
1059 msg->flags = htonl (sqlite3_column_int64 (stmt, 7));
1060 GNUNET_memcpy (&msg[1], sqlite3_column_blob (stmt, 9), data_size);
1061
1062 return cb (cb_cls, (void *) msg, sqlite3_column_int64 (stmt, 8));
1063}
1064
1065
1066static int
1067fragment_select (struct Plugin *plugin, sqlite3_stmt *stmt,
1068 uint64_t *returned_fragments,
1069 GNUNET_PSYCSTORE_FragmentCallback cb, void *cb_cls)
1070{
1071 int ret = GNUNET_SYSERR;
1072 int sql_ret;
1073
1074 do
1075 {
1076 sql_ret = sqlite3_step (stmt);
1077 switch (sql_ret)
1078 {
1079 case SQLITE_DONE:
1080 if (ret != GNUNET_OK)
1081 ret = GNUNET_NO;
1082 break;
1083 case SQLITE_ROW:
1084 ret = fragment_row (stmt, cb, cb_cls);
1085 (*returned_fragments)++;
1086 if (ret != GNUNET_YES)
1087 sql_ret = SQLITE_DONE;
1088 break;
1089 default:
1090 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1091 "sqlite3_step");
1092 }
1093 }
1094 while (sql_ret == SQLITE_ROW);
1095
1096 return ret;
1097}
1098
1099/**
1100 * Retrieve a message fragment range by fragment ID.
1101 *
1102 * @see GNUNET_PSYCSTORE_fragment_get()
1103 *
1104 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1105 */
1106static int
1107fragment_get (void *cls,
1108 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1109 uint64_t first_fragment_id,
1110 uint64_t last_fragment_id,
1111 uint64_t *returned_fragments,
1112 GNUNET_PSYCSTORE_FragmentCallback cb,
1113 void *cb_cls)
1114{
1115 struct Plugin *plugin = cls;
1116 sqlite3_stmt *stmt = plugin->select_fragments;
1117 int ret = GNUNET_SYSERR;
1118 *returned_fragments = 0;
1119
1120 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1121 sizeof (*channel_key),
1122 SQLITE_STATIC)
1123 || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, first_fragment_id)
1124 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, last_fragment_id))
1125 {
1126 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1127 "sqlite3_bind");
1128 }
1129 else
1130 {
1131 ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
1132 }
1133
1134 if (SQLITE_OK != sqlite3_reset (stmt))
1135 {
1136 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1137 "sqlite3_reset");
1138 }
1139
1140 return ret;
1141}
1142
1143
1144/**
1145 * Retrieve a message fragment range by fragment ID.
1146 *
1147 * @see GNUNET_PSYCSTORE_fragment_get_latest()
1148 *
1149 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1150 */
1151static int
1152fragment_get_latest (void *cls,
1153 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1154 uint64_t fragment_limit,
1155 uint64_t *returned_fragments,
1156 GNUNET_PSYCSTORE_FragmentCallback cb,
1157 void *cb_cls)
1158{
1159 struct Plugin *plugin = cls;
1160 sqlite3_stmt *stmt = plugin->select_latest_fragments;
1161 int ret = GNUNET_SYSERR;
1162 *returned_fragments = 0;
1163
1164 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1165 sizeof (*channel_key),
1166 SQLITE_STATIC)
1167 || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, fragment_limit))
1168 {
1169 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1170 "sqlite3_bind");
1171 }
1172 else
1173 {
1174 ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
1175 }
1176
1177 if (SQLITE_OK != sqlite3_reset (stmt))
1178 {
1179 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1180 "sqlite3_reset");
1181 }
1182
1183 return ret;
1184}
1185
1186
1187/**
1188 * Retrieve all fragments of a message ID range.
1189 *
1190 * @see GNUNET_PSYCSTORE_message_get()
1191 *
1192 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1193 */
1194static int
1195message_get (void *cls,
1196 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1197 uint64_t first_message_id,
1198 uint64_t last_message_id,
1199 uint64_t fragment_limit,
1200 uint64_t *returned_fragments,
1201 GNUNET_PSYCSTORE_FragmentCallback cb,
1202 void *cb_cls)
1203{
1204 struct Plugin *plugin = cls;
1205 sqlite3_stmt *stmt = plugin->select_messages;
1206 int ret = GNUNET_SYSERR;
1207 *returned_fragments = 0;
1208
1209 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1210 sizeof (*channel_key),
1211 SQLITE_STATIC)
1212 || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, first_message_id)
1213 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, last_message_id)
1214 || SQLITE_OK != sqlite3_bind_int64 (stmt, 4,
1215 (0 != fragment_limit)
1216 ? fragment_limit
1217 : INT64_MAX))
1218 {
1219 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1220 "sqlite3_bind");
1221 }
1222 else
1223 {
1224 ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
1225 }
1226
1227 if (SQLITE_OK != sqlite3_reset (stmt))
1228 {
1229 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1230 "sqlite3_reset");
1231 }
1232
1233 return ret;
1234}
1235
1236
1237/**
1238 * Retrieve all fragments of the latest messages.
1239 *
1240 * @see GNUNET_PSYCSTORE_message_get_latest()
1241 *
1242 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1243 */
1244static int
1245message_get_latest (void *cls,
1246 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1247 uint64_t message_limit,
1248 uint64_t *returned_fragments,
1249 GNUNET_PSYCSTORE_FragmentCallback cb,
1250 void *cb_cls)
1251{
1252 struct Plugin *plugin = cls;
1253 sqlite3_stmt *stmt = plugin->select_latest_messages;
1254 int ret = GNUNET_SYSERR;
1255 *returned_fragments = 0;
1256
1257 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1258 sizeof (*channel_key),
1259 SQLITE_STATIC)
1260 || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
1261 sizeof (*channel_key),
1262 SQLITE_STATIC)
1263 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, message_limit))
1264 {
1265 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1266 "sqlite3_bind");
1267 }
1268 else
1269 {
1270 ret = fragment_select (plugin, stmt, returned_fragments, cb, cb_cls);
1271 }
1272
1273 if (SQLITE_OK != sqlite3_reset (stmt))
1274 {
1275 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1276 "sqlite3_reset");
1277 }
1278
1279 return ret;
1280}
1281
1282
1283/**
1284 * Retrieve a fragment of message specified by its message ID and fragment
1285 * offset.
1286 *
1287 * @see GNUNET_PSYCSTORE_message_get_fragment()
1288 *
1289 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1290 */
1291static int
1292message_get_fragment (void *cls,
1293 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1294 uint64_t message_id,
1295 uint64_t fragment_offset,
1296 GNUNET_PSYCSTORE_FragmentCallback cb,
1297 void *cb_cls)
1298{
1299 struct Plugin *plugin = cls;
1300 sqlite3_stmt *stmt = plugin->select_message_fragment;
1301 int ret = GNUNET_SYSERR;
1302
1303 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1304 sizeof (*channel_key),
1305 SQLITE_STATIC)
1306 || SQLITE_OK != sqlite3_bind_int64 (stmt, 2, message_id)
1307 || SQLITE_OK != sqlite3_bind_int64 (stmt, 3, fragment_offset))
1308 {
1309 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1310 "sqlite3_bind");
1311 }
1312 else
1313 {
1314 switch (sqlite3_step (stmt))
1315 {
1316 case SQLITE_DONE:
1317 ret = GNUNET_NO;
1318 break;
1319 case SQLITE_ROW:
1320 ret = fragment_row (stmt, cb, cb_cls);
1321 break;
1322 default:
1323 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1324 "sqlite3_step");
1325 }
1326 }
1327
1328 if (SQLITE_OK != sqlite3_reset (stmt))
1329 {
1330 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1331 "sqlite3_reset");
1332 }
1333
1334 return ret;
1335}
1336
1337/**
1338 * Retrieve the max. values of message counters for a channel.
1339 *
1340 * @see GNUNET_PSYCSTORE_counters_get()
1341 *
1342 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1343 */
1344static int
1345counters_message_get (void *cls,
1346 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1347 uint64_t *max_fragment_id,
1348 uint64_t *max_message_id,
1349 uint64_t *max_group_generation)
1350{
1351 struct Plugin *plugin = cls;
1352 sqlite3_stmt *stmt = plugin->select_counters_message;
1353 int ret = GNUNET_SYSERR;
1354
1355 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1356 sizeof (*channel_key),
1357 SQLITE_STATIC))
1358 {
1359 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1360 "sqlite3_bind");
1361 }
1362 else
1363 {
1364 switch (sqlite3_step (stmt))
1365 {
1366 case SQLITE_DONE:
1367 ret = GNUNET_NO;
1368 break;
1369 case SQLITE_ROW:
1370 *max_fragment_id = sqlite3_column_int64 (stmt, 0);
1371 *max_message_id = sqlite3_column_int64 (stmt, 1);
1372 *max_group_generation = sqlite3_column_int64 (stmt, 2);
1373 ret = GNUNET_OK;
1374 break;
1375 default:
1376 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1377 "sqlite3_step");
1378 }
1379 }
1380
1381 if (SQLITE_OK != sqlite3_reset (stmt))
1382 {
1383 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1384 "sqlite3_reset");
1385 }
1386
1387 return ret;
1388}
1389
1390/**
1391 * Retrieve the max. values of state counters for a channel.
1392 *
1393 * @see GNUNET_PSYCSTORE_counters_get()
1394 *
1395 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1396 */
1397static int
1398counters_state_get (void *cls,
1399 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1400 uint64_t *max_state_message_id)
1401{
1402 struct Plugin *plugin = cls;
1403 sqlite3_stmt *stmt = plugin->select_counters_state;
1404 int ret = GNUNET_SYSERR;
1405
1406 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1407 sizeof (*channel_key),
1408 SQLITE_STATIC))
1409 {
1410 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1411 "sqlite3_bind");
1412 }
1413 else
1414 {
1415 switch (sqlite3_step (stmt))
1416 {
1417 case SQLITE_DONE:
1418 ret = GNUNET_NO;
1419 break;
1420 case SQLITE_ROW:
1421 *max_state_message_id = sqlite3_column_int64 (stmt, 0);
1422 ret = GNUNET_OK;
1423 break;
1424 default:
1425 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1426 "sqlite3_step");
1427 }
1428 }
1429
1430 if (SQLITE_OK != sqlite3_reset (stmt))
1431 {
1432 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1433 "sqlite3_reset");
1434 }
1435
1436 return ret;
1437}
1438
1439
1440/**
1441 * Assign a value to a state variable.
1442 *
1443 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1444 */
1445static int
1446state_assign (struct Plugin *plugin, sqlite3_stmt *stmt,
1447 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1448 const char *name, const void *value, size_t value_size)
1449{
1450 int ret = GNUNET_SYSERR;
1451
1452 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1453 sizeof (*channel_key), SQLITE_STATIC)
1454 || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, -1, SQLITE_STATIC)
1455 || SQLITE_OK != sqlite3_bind_blob (stmt, 3, value, value_size,
1456 SQLITE_STATIC))
1457 {
1458 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1459 "sqlite3_bind");
1460 }
1461 else
1462 {
1463 switch (sqlite3_step (stmt))
1464 {
1465 case SQLITE_DONE:
1466 ret = 0 < sqlite3_total_changes (plugin->dbh) ? GNUNET_OK : GNUNET_NO;
1467 break;
1468 default:
1469 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1470 "sqlite3_step");
1471 }
1472 }
1473
1474 if (SQLITE_OK != sqlite3_reset (stmt))
1475 {
1476 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1477 "sqlite3_reset");
1478 return GNUNET_SYSERR;
1479 }
1480
1481 return ret;
1482}
1483
1484
1485static int
1486update_message_id (struct Plugin *plugin, sqlite3_stmt *stmt,
1487 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1488 uint64_t message_id)
1489{
1490 if (SQLITE_OK != sqlite3_bind_int64 (stmt, 1, message_id)
1491 || SQLITE_OK != sqlite3_bind_blob (stmt, 2, channel_key,
1492 sizeof (*channel_key), SQLITE_STATIC))
1493 {
1494 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1495 "sqlite3_bind");
1496 }
1497 else if (SQLITE_DONE != sqlite3_step (stmt))
1498 {
1499 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1500 "sqlite3_step");
1501 }
1502 if (SQLITE_OK != sqlite3_reset (stmt))
1503 {
1504 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1505 "sqlite3_reset");
1506 return GNUNET_SYSERR;
1507 }
1508 return GNUNET_OK;
1509}
1510
1511
1512/**
1513 * Begin modifying current state.
1514 */
1515static int
1516state_modify_begin (void *cls,
1517 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1518 uint64_t message_id, uint64_t state_delta)
1519{
1520 struct Plugin *plugin = cls;
1521
1522 if (state_delta > 0)
1523 {
1524 /**
1525 * We can only apply state modifiers in the current message if modifiers in
1526 * the previous stateful message (message_id - state_delta) were already
1527 * applied.
1528 */
1529
1530 uint64_t max_state_message_id = 0;
1531 int ret = counters_state_get (plugin, channel_key, &max_state_message_id);
1532 switch (ret)
1533 {
1534 case GNUNET_OK:
1535 case GNUNET_NO: // no state yet
1536 ret = GNUNET_OK;
1537 break;
1538 default:
1539 return ret;
1540 }
1541
1542 if (max_state_message_id < message_id - state_delta)
1543 return GNUNET_NO; /* some stateful messages not yet applied */
1544 else if (message_id - state_delta < max_state_message_id)
1545 return GNUNET_NO; /* changes already applied */
1546 }
1547
1548 if (TRANSACTION_NONE != plugin->transaction)
1549 {
1550 /** @todo FIXME: wait for other transaction to finish */
1551 return GNUNET_SYSERR;
1552 }
1553 return transaction_begin (plugin, TRANSACTION_STATE_MODIFY);
1554}
1555
1556
1557/**
1558 * Set the current value of state variable.
1559 *
1560 * @see GNUNET_PSYCSTORE_state_modify()
1561 *
1562 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1563 */
1564static int
1565state_modify_op (void *cls,
1566 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1567 enum GNUNET_PSYC_Operator op,
1568 const char *name, const void *value, size_t value_size)
1569{
1570 struct Plugin *plugin = cls;
1571 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1572
1573 switch (op)
1574 {
1575 case GNUNET_PSYC_OP_ASSIGN:
1576 return state_assign (plugin, plugin->insert_state_current, channel_key,
1577 name, value, value_size);
1578
1579 default: /** @todo implement more state operations */
1580 GNUNET_break (0);
1581 return GNUNET_SYSERR;
1582 }
1583}
1584
1585
1586/**
1587 * End modifying current state.
1588 */
1589static int
1590state_modify_end (void *cls,
1591 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1592 uint64_t message_id)
1593{
1594 struct Plugin *plugin = cls;
1595 GNUNET_assert (TRANSACTION_STATE_MODIFY == plugin->transaction);
1596
1597 return
1598 GNUNET_OK == exec_channel (plugin, plugin->delete_state_empty, channel_key)
1599 && GNUNET_OK == update_message_id (plugin,
1600 plugin->update_max_state_message_id,
1601 channel_key, message_id)
1602 && GNUNET_OK == transaction_commit (plugin)
1603 ? GNUNET_OK : GNUNET_SYSERR;
1604}
1605
1606
1607/**
1608 * Begin state synchronization.
1609 */
1610static int
1611state_sync_begin (void *cls,
1612 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1613{
1614 struct Plugin *plugin = cls;
1615 return exec_channel (plugin, plugin->delete_state_sync, channel_key);
1616}
1617
1618
1619/**
1620 * Assign current value of a state variable.
1621 *
1622 * @see GNUNET_PSYCSTORE_state_modify()
1623 *
1624 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1625 */
1626static int
1627state_sync_assign (void *cls,
1628 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1629 const char *name, const void *value, size_t value_size)
1630{
1631 struct Plugin *plugin = cls;
1632 return state_assign (cls, plugin->insert_state_sync, channel_key,
1633 name, value, value_size);
1634}
1635
1636
1637/**
1638 * End modifying current state.
1639 */
1640static int
1641state_sync_end (void *cls,
1642 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1643 uint64_t max_state_message_id,
1644 uint64_t state_hash_message_id)
1645{
1646 struct Plugin *plugin = cls;
1647 int ret = GNUNET_SYSERR;
1648
1649 if (TRANSACTION_NONE != plugin->transaction)
1650 {
1651 /** @todo FIXME: wait for other transaction to finish */
1652 return GNUNET_SYSERR;
1653 }
1654
1655 GNUNET_OK == transaction_begin (plugin, TRANSACTION_STATE_SYNC)
1656 && GNUNET_OK == exec_channel (plugin, plugin->delete_state, channel_key)
1657 && GNUNET_OK == exec_channel (plugin, plugin->insert_state_from_sync,
1658 channel_key)
1659 && GNUNET_OK == exec_channel (plugin, plugin->delete_state_sync,
1660 channel_key)
1661 && GNUNET_OK == update_message_id (plugin,
1662 plugin->update_state_hash_message_id,
1663 channel_key, state_hash_message_id)
1664 && GNUNET_OK == update_message_id (plugin,
1665 plugin->update_max_state_message_id,
1666 channel_key, max_state_message_id)
1667 && GNUNET_OK == transaction_commit (plugin)
1668 ? ret = GNUNET_OK
1669 : transaction_rollback (plugin);
1670 return ret;
1671}
1672
1673
1674/**
1675 * Delete the whole state.
1676 *
1677 * @see GNUNET_PSYCSTORE_state_reset()
1678 *
1679 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1680 */
1681static int
1682state_reset (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1683{
1684 struct Plugin *plugin = cls;
1685 return exec_channel (plugin, plugin->delete_state, channel_key);
1686}
1687
1688
1689/**
1690 * Update signed values of state variables in the state store.
1691 *
1692 * @see GNUNET_PSYCSTORE_state_hash_update()
1693 *
1694 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1695 */
1696static int
1697state_update_signed (void *cls,
1698 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key)
1699{
1700 struct Plugin *plugin = cls;
1701 return exec_channel (plugin, plugin->update_state_signed, channel_key);
1702}
1703
1704
1705/**
1706 * Retrieve a state variable by name.
1707 *
1708 * @see GNUNET_PSYCSTORE_state_get()
1709 *
1710 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1711 */
1712static int
1713state_get (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1714 const char *name, GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1715{
1716 struct Plugin *plugin = cls;
1717 int ret = GNUNET_SYSERR;
1718
1719 sqlite3_stmt *stmt = plugin->select_state_one;
1720
1721 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1722 sizeof (*channel_key),
1723 SQLITE_STATIC)
1724 || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, -1, SQLITE_STATIC))
1725 {
1726 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1727 "sqlite3_bind");
1728 }
1729 else
1730 {
1731 switch (sqlite3_step (stmt))
1732 {
1733 case SQLITE_DONE:
1734 ret = GNUNET_NO;
1735 break;
1736 case SQLITE_ROW:
1737 ret = cb (cb_cls, name, sqlite3_column_blob (stmt, 0),
1738 sqlite3_column_bytes (stmt, 0));
1739 break;
1740 default:
1741 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1742 "sqlite3_step");
1743 }
1744 }
1745
1746 if (SQLITE_OK != sqlite3_reset (stmt))
1747 {
1748 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1749 "sqlite3_reset");
1750 }
1751
1752 return ret;
1753}
1754
1755
1756/**
1757 * Retrieve all state variables for a channel with the given prefix.
1758 *
1759 * @see GNUNET_PSYCSTORE_state_get_prefix()
1760 *
1761 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1762 */
1763static int
1764state_get_prefix (void *cls, const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1765 const char *name, GNUNET_PSYCSTORE_StateCallback cb,
1766 void *cb_cls)
1767{
1768 struct Plugin *plugin = cls;
1769 int ret = GNUNET_SYSERR;
1770 sqlite3_stmt *stmt = plugin->select_state_prefix;
1771 size_t name_len = strlen (name);
1772
1773 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1774 sizeof (*channel_key), SQLITE_STATIC)
1775 || SQLITE_OK != sqlite3_bind_text (stmt, 2, name, name_len, SQLITE_STATIC)
1776 || SQLITE_OK != sqlite3_bind_int (stmt, 3, name_len)
1777 || SQLITE_OK != sqlite3_bind_text (stmt, 4, name, name_len, SQLITE_STATIC))
1778 {
1779 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1780 "sqlite3_bind");
1781 }
1782 else
1783 {
1784 int sql_ret;
1785 do
1786 {
1787 sql_ret = sqlite3_step (stmt);
1788 switch (sql_ret)
1789 {
1790 case SQLITE_DONE:
1791 if (ret != GNUNET_OK)
1792 ret = GNUNET_NO;
1793 break;
1794 case SQLITE_ROW:
1795 ret = cb (cb_cls, (const char *) sqlite3_column_text (stmt, 0),
1796 sqlite3_column_blob (stmt, 1),
1797 sqlite3_column_bytes (stmt, 1));
1798 if (ret != GNUNET_YES)
1799 sql_ret = SQLITE_DONE;
1800 break;
1801 default:
1802 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1803 "sqlite3_step");
1804 }
1805 }
1806 while (sql_ret == SQLITE_ROW);
1807 }
1808 if (SQLITE_OK != sqlite3_reset (stmt))
1809 {
1810 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1811 "sqlite3_reset");
1812 }
1813 return ret;
1814}
1815
1816
1817/**
1818 * Retrieve all signed state variables for a channel.
1819 *
1820 * @see GNUNET_PSYCSTORE_state_get_signed()
1821 *
1822 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
1823 */
1824static int
1825state_get_signed (void *cls,
1826 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1827 GNUNET_PSYCSTORE_StateCallback cb, void *cb_cls)
1828{
1829 struct Plugin *plugin = cls;
1830 int ret = GNUNET_SYSERR;
1831
1832 sqlite3_stmt *stmt = plugin->select_state_signed;
1833
1834 if (SQLITE_OK != sqlite3_bind_blob (stmt, 1, channel_key,
1835 sizeof (*channel_key), SQLITE_STATIC))
1836 {
1837 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1838 "sqlite3_bind");
1839 }
1840 else
1841 {
1842 int sql_ret;
1843 do
1844 {
1845 sql_ret = sqlite3_step (stmt);
1846 switch (sql_ret)
1847 {
1848 case SQLITE_DONE:
1849 if (ret != GNUNET_OK)
1850 ret = GNUNET_NO;
1851 break;
1852 case SQLITE_ROW:
1853 ret = cb (cb_cls, (const char *) sqlite3_column_text (stmt, 0),
1854 sqlite3_column_blob (stmt, 1),
1855 sqlite3_column_bytes (stmt, 1));
1856 if (ret != GNUNET_YES)
1857 sql_ret = SQLITE_DONE;
1858 break;
1859 default:
1860 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1861 "sqlite3_step");
1862 }
1863 }
1864 while (sql_ret == SQLITE_ROW);
1865 }
1866
1867 if (SQLITE_OK != sqlite3_reset (stmt))
1868 {
1869 LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1870 "sqlite3_reset");
1871 }
1872
1873 return ret;
1874}
1875
1876
1877/**
1878 * Entry point for the plugin.
1879 *
1880 * @param cls The struct GNUNET_CONFIGURATION_Handle.
1881 * @return NULL on error, otherwise the plugin context
1882 */
1883void *
1884libgnunet_plugin_psycstore_sqlite_init (void *cls)
1885{
1886 static struct Plugin plugin;
1887 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1888 struct GNUNET_PSYCSTORE_PluginFunctions *api;
1889
1890 if (NULL != plugin.cfg)
1891 return NULL; /* can only initialize once! */
1892 memset (&plugin, 0, sizeof (struct Plugin));
1893 plugin.cfg = cfg;
1894 if (GNUNET_OK != database_setup (&plugin))
1895 {
1896 database_shutdown (&plugin);
1897 return NULL;
1898 }
1899 api = GNUNET_new (struct GNUNET_PSYCSTORE_PluginFunctions);
1900 api->cls = &plugin;
1901 api->membership_store = &sqlite_membership_store;
1902 api->membership_test = &membership_test;
1903 api->fragment_store = &fragment_store;
1904 api->message_add_flags = &message_add_flags;
1905 api->fragment_get = &fragment_get;
1906 api->fragment_get_latest = &fragment_get_latest;
1907 api->message_get = &message_get;
1908 api->message_get_latest = &message_get_latest;
1909 api->message_get_fragment = &message_get_fragment;
1910 api->counters_message_get = &counters_message_get;
1911 api->counters_state_get = &counters_state_get;
1912 api->state_modify_begin = &state_modify_begin;
1913 api->state_modify_op = &state_modify_op;
1914 api->state_modify_end = &state_modify_end;
1915 api->state_sync_begin = &state_sync_begin;
1916 api->state_sync_assign = &state_sync_assign;
1917 api->state_sync_end = &state_sync_end;
1918 api->state_reset = &state_reset;
1919 api->state_update_signed = &state_update_signed;
1920 api->state_get = &state_get;
1921 api->state_get_prefix = &state_get_prefix;
1922 api->state_get_signed = &state_get_signed;
1923
1924 LOG (GNUNET_ERROR_TYPE_INFO, _("SQLite database running\n"));
1925 return api;
1926}
1927
1928
1929/**
1930 * Exit point from the plugin.
1931 *
1932 * @param cls The plugin context (as returned by "init")
1933 * @return Always NULL
1934 */
1935void *
1936libgnunet_plugin_psycstore_sqlite_done (void *cls)
1937{
1938 struct GNUNET_PSYCSTORE_PluginFunctions *api = cls;
1939 struct Plugin *plugin = api->cls;
1940
1941 database_shutdown (plugin);
1942 plugin->cfg = NULL;
1943 GNUNET_free (api);
1944 LOG (GNUNET_ERROR_TYPE_DEBUG, "SQLite plugin is finished\n");
1945 return NULL;
1946}
1947
1948/* end of plugin_psycstore_sqlite.c */
diff --git a/src/psycstore/psycstore.conf.in b/src/psycstore/psycstore.conf.in
new file mode 100644
index 0000000..3905db1
--- /dev/null
+++ b/src/psycstore/psycstore.conf.in
@@ -0,0 +1,28 @@
1[psycstore]
2START_ON_DEMAND = @START_ON_DEMAND@
3BINARY = gnunet-service-psycstore
4
5UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-psycstore.sock
6UNIX_MATCH_UID = YES
7UNIX_MATCH_GID = YES
8
9@UNIXONLY@PORT = 2111
10HOSTNAME = localhost
11ACCEPT_FROM = 127.0.0.1;
12ACCEPT_FROM6 = ::1;
13
14DATABASE = sqlite
15
16[psycstore-sqlite]
17FILENAME = $GNUNET_DATA_HOME/psycstore/sqlite.db
18
19[psycstore-mysql]
20DATABASE = gnunet
21CONFIG = ~/.my.cnf
22# USER = gnunet
23# PASSWORD =
24# HOST = localhost
25# PORT = 3306
26
27[psycstore-postgres]
28CONFIG = connect_timeout=10; dbname=gnunet
diff --git a/src/psycstore/psycstore.h b/src/psycstore/psycstore.h
new file mode 100644
index 0000000..9a1c06a
--- /dev/null
+++ b/src/psycstore/psycstore.h
@@ -0,0 +1,520 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file psycstore/psycstore.h
23 * @brief Common type definitions for the PSYCstore service and API.
24 * @author Gabor X Toth
25 */
26
27#ifndef GNUNET_PSYCSTORE_H
28#define GNUNET_PSYCSTORE_H
29
30#include "gnunet_common.h"
31
32
33GNUNET_NETWORK_STRUCT_BEGIN
34
35/**
36 * Answer from service to client about last operation.
37 */
38struct OperationResult
39{
40 /**
41 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
42 */
43 struct GNUNET_MessageHeader header;
44
45 uint32_t reserved GNUNET_PACKED;
46
47 /**
48 * Operation ID.
49 */
50 uint64_t op_id GNUNET_PACKED;
51
52 /**lowed by
53 * Status code for the operation.
54 */
55 uint64_t result_code GNUNET_PACKED;
56
57 /* followed by 0-terminated error message (on error) */
58
59};
60
61
62/**
63 * Answer from service to client about master counters.
64 *
65 * @see GNUNET_PSYCSTORE_counters_get()
66 */
67struct CountersResult
68{
69 /**
70 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS
71 */
72 struct GNUNET_MessageHeader header;
73
74 /**
75 * Status code for the operation:
76 * #GNUNET_OK: success, counter values are returned.
77 * #GNUNET_NO: no message has been sent to the channel yet.
78 * #GNUNET_SYSERR: an error occurred.
79 */
80 uint32_t result_code GNUNET_PACKED;
81
82 /**
83 * Operation ID.
84 */
85 uint64_t op_id GNUNET_PACKED;
86
87 uint64_t max_fragment_id GNUNET_PACKED;
88
89 uint64_t max_message_id GNUNET_PACKED;
90
91 uint64_t max_group_generation GNUNET_PACKED;
92
93 uint64_t max_state_message_id GNUNET_PACKED;
94};
95
96
97/**
98 * Answer from service to client containing a message fragment.
99 */
100struct FragmentResult
101{
102 /**
103 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
104 */
105 struct GNUNET_MessageHeader header;
106
107 uint32_t psycstore_flags GNUNET_PACKED;
108
109 /**
110 * Operation ID.
111 */
112 uint64_t op_id GNUNET_PACKED;
113
114 /* Followed by GNUNET_MULTICAST_MessageHeader */
115};
116
117
118/**
119 * Answer from service to client containing a state variable.
120 */
121struct StateResult
122{
123 /**
124 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE
125 */
126 struct GNUNET_MessageHeader header;
127
128 uint16_t name_size GNUNET_PACKED;
129
130 uint16_t reserved GNUNET_PACKED;
131
132 /**
133 * Operation ID.
134 */
135 uint64_t op_id GNUNET_PACKED;
136
137 /* Followed by name and value */
138};
139
140
141/**
142 * Generic operation request.
143 */
144struct OperationRequest
145{
146 struct GNUNET_MessageHeader header;
147
148 uint32_t reserved GNUNET_PACKED;
149
150 /**
151 * Operation ID.
152 */
153 uint64_t op_id GNUNET_PACKED;
154
155 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
156};
157
158
159/**
160 * @see GNUNET_PSYCSTORE_membership_store()
161 */
162struct MembershipStoreRequest
163{
164 /**
165 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE
166 */
167 struct GNUNET_MessageHeader header;
168
169 uint32_t reserved GNUNET_PACKED;
170
171 /**
172 * Operation ID.
173 */
174 uint64_t op_id GNUNET_PACKED;
175
176 /**
177 * Channel's public key.
178 */
179 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
180
181 /**
182 * Slave's public key.
183 */
184 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
185
186 uint64_t announced_at GNUNET_PACKED;
187 uint64_t effective_since GNUNET_PACKED;
188 uint64_t group_generation GNUNET_PACKED;
189 uint8_t did_join;
190};
191
192
193/**
194 * @see GNUNET_PSYCSTORE_membership_test()
195 */
196struct MembershipTestRequest
197{
198 /**
199 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST
200 */
201 struct GNUNET_MessageHeader header;
202
203 uint32_t reserved GNUNET_PACKED;
204
205 /**
206 * Operation ID.
207 */
208 uint64_t op_id GNUNET_PACKED;
209
210 /**
211 * Channel's public key.
212 */
213 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
214
215 /**
216 * Slave's public key.
217 */
218 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
219
220 uint64_t message_id GNUNET_PACKED;
221
222 uint64_t group_generation GNUNET_PACKED;
223};
224
225
226/**
227 * @see GNUNET_PSYCSTORE_fragment_store()
228 */
229struct FragmentStoreRequest
230{
231 /**
232 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE
233 */
234 struct GNUNET_MessageHeader header;
235
236 /**
237 * enum GNUNET_PSYCSTORE_MessageFlags
238 */
239 uint32_t psycstore_flags GNUNET_PACKED;
240
241 /**
242 * Channel's public key.
243 */
244 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
245
246 /**
247 * Operation ID.
248 */
249 uint64_t op_id;
250
251 /* Followed by fragment */
252};
253
254
255/**
256 * @see GNUNET_PSYCSTORE_fragment_get()
257 */
258struct FragmentGetRequest
259{
260 /**
261 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET
262 */
263 struct GNUNET_MessageHeader header;
264
265 uint32_t reserved GNUNET_PACKED;
266
267 /**
268 * Operation ID.
269 */
270 uint64_t op_id GNUNET_PACKED;
271
272 /**
273 * Channel's public key.
274 */
275 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
276
277 /**
278 * Slave's public key.
279 */
280 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
281
282 /**
283 * First fragment ID to request.
284 */
285 uint64_t first_fragment_id GNUNET_PACKED;
286
287 /**
288 * Last fragment ID to request.
289 */
290 uint64_t last_fragment_id GNUNET_PACKED;
291
292 /**
293 * Maximum number of fragments to retrieve.
294 */
295 uint64_t fragment_limit GNUNET_PACKED;
296
297 /**
298 * Do membership test with @a slave_key before returning fragment?
299 * #GNUNET_YES or #GNUNET_NO
300 */
301 uint8_t do_membership_test;
302};
303
304
305/**
306 * @see GNUNET_PSYCSTORE_message_get()
307 */
308struct MessageGetRequest
309{
310 /**
311 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET
312 */
313 struct GNUNET_MessageHeader header;
314
315 uint32_t reserved GNUNET_PACKED;
316
317 /**
318 * Operation ID.
319 */
320 uint64_t op_id GNUNET_PACKED;
321
322 /**
323 * Channel's public key.
324 */
325 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
326
327 /**
328 * Slave's public key.
329 */
330 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
331
332 /**
333 * First message ID to request.
334 */
335 uint64_t first_message_id GNUNET_PACKED;
336
337 /**
338 * Last message ID to request.
339 */
340 uint64_t last_message_id GNUNET_PACKED;
341
342 /**
343 * Maximum number of messages to retrieve.
344 */
345 uint64_t message_limit GNUNET_PACKED;
346
347 /**
348 * Maximum number of fragments to retrieve.
349 */
350 uint64_t fragment_limit GNUNET_PACKED;
351
352 /**
353 * Do membership test with @a slave_key before returning fragment?
354 * #GNUNET_YES or #GNUNET_NO
355 */
356 uint8_t do_membership_test;
357
358 /* Followed by method_prefix */
359};
360
361
362/**
363 * @see GNUNET_PSYCSTORE_message_get_fragment()
364 */
365struct MessageGetFragmentRequest
366{
367 /**
368 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_FRAGMENT_GET
369 */
370 struct GNUNET_MessageHeader header;
371
372 uint32_t reserved GNUNET_PACKED;
373
374 /**
375 * Operation ID.
376 */
377 uint64_t op_id GNUNET_PACKED;
378
379 /**
380 * Channel's public key.
381 */
382 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
383
384 /**
385 * Slave's public key.
386 */
387 struct GNUNET_CRYPTO_EcdsaPublicKey slave_key;
388
389 /**
390 * Requested message ID.
391 */
392 uint64_t message_id GNUNET_PACKED;
393
394 /**
395 * Requested fragment offset.
396 */
397 uint64_t fragment_offset GNUNET_PACKED;
398
399 /**
400 * Do membership test with @a slave_key before returning fragment?
401 * #GNUNET_YES or #GNUNET_NO
402 */
403 uint8_t do_membership_test;
404};
405
406
407/**
408 * @see GNUNET_PSYCSTORE_state_hash_update()
409 */
410struct StateHashUpdateRequest
411{
412 /**
413 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE
414 */
415 struct GNUNET_MessageHeader header;
416
417 uint32_t reserved GNUNET_PACKED;
418
419 /**
420 * Operation ID.
421 */
422 uint64_t op_id GNUNET_PACKED;
423
424 /**
425 * Channel's public key.
426 */
427 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
428
429 struct GNUNET_HashCode hash;
430};
431
432
433enum StateOpFlags
434{
435 STATE_OP_FIRST = 1 << 0,
436 STATE_OP_LAST = 1 << 1
437};
438
439
440/**
441 * @see GNUNET_PSYCSTORE_state_modify()
442 */
443struct StateModifyRequest
444{
445 /**
446 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY
447 */
448 struct GNUNET_MessageHeader header;
449
450 /**
451 * Operation ID.
452 */
453 uint64_t op_id GNUNET_PACKED;
454
455 /**
456 * ID of the message to apply the state changes in.
457 */
458 uint64_t message_id GNUNET_PACKED;
459
460 /**
461 * State delta of the message with ID @a message_id.
462 */
463 uint64_t state_delta GNUNET_PACKED;
464
465 /**
466 * Channel's public key.
467 */
468 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
469};
470
471
472/**
473 * @see GNUNET_PSYCSTORE_state_sync()
474 */
475struct StateSyncRequest
476{
477 /**
478 * Type: GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC
479 */
480 struct GNUNET_MessageHeader header;
481
482 /**
483 * Size of name, including NUL terminator.
484 */
485 uint16_t name_size GNUNET_PACKED;
486
487 /**
488 * OR'd StateOpFlags
489 */
490 uint8_t flags;
491
492 uint8_t reserved;
493
494 /**
495 * Operation ID.
496 */
497 uint64_t op_id GNUNET_PACKED;
498
499 /**
500 * ID of the message that contains the state_hash PSYC header variable.
501 */
502 uint64_t state_hash_message_id GNUNET_PACKED;
503
504 /**
505 * ID of the last stateful message before @a state_hash_message_id.
506 */
507 uint64_t max_state_message_id GNUNET_PACKED;
508
509 /**
510 * Channel's public key.
511 */
512 struct GNUNET_CRYPTO_EddsaPublicKey channel_key;
513
514 /* Followed by NUL-terminated name, then the value. */
515};
516
517
518GNUNET_NETWORK_STRUCT_END
519
520#endif
diff --git a/src/psycstore/psycstore_api.c b/src/psycstore/psycstore_api.c
new file mode 100644
index 0000000..ab4cd0f
--- /dev/null
+++ b/src/psycstore/psycstore_api.c
@@ -0,0 +1,1285 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
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 <inttypes.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_constants.h"
33#include "gnunet_protocols.h"
34#include "gnunet_psycstore_service.h"
35#include "gnunet_multicast_service.h"
36#include "psycstore.h"
37
38#define LOG(kind,...) GNUNET_log_from (kind, "psycstore-api",__VA_ARGS__)
39
40/**
41 * Handle for an operation with the PSYCstore service.
42 */
43struct GNUNET_PSYCSTORE_OperationHandle
44{
45
46 /**
47 * Main PSYCstore handle.
48 */
49 struct GNUNET_PSYCSTORE_Handle *h;
50
51 /**
52 * Data callbacks.
53 */
54 union {
55 GNUNET_PSYCSTORE_FragmentCallback fragment_cb;
56 GNUNET_PSYCSTORE_CountersCallback counters_cb;
57 GNUNET_PSYCSTORE_StateCallback state_cb;
58 };
59
60 /**
61 * Closure for callbacks.
62 */
63 void *cls;
64
65 /**
66 * Message envelope.
67 */
68 struct GNUNET_MQ_Envelope *env;
69
70 /**
71 * Operation ID.
72 */
73 uint64_t op_id;
74};
75
76
77/**
78 * Handle for the service.
79 */
80struct GNUNET_PSYCSTORE_Handle
81{
82 /**
83 * Configuration to use.
84 */
85 const struct GNUNET_CONFIGURATION_Handle *cfg;
86
87 /**
88 * Client connection.
89 */
90 struct GNUNET_MQ_Handle *mq;
91
92 /**
93 * Async operations.
94 */
95 struct GNUNET_OP_Handle *op;
96
97 /**
98 * Task doing exponential back-off trying to reconnect.
99 */
100 struct GNUNET_SCHEDULER_Task *reconnect_task;
101
102 /**
103 * Delay for next connect retry.
104 */
105 struct GNUNET_TIME_Relative reconnect_delay;
106
107
108 GNUNET_PSYCSTORE_FragmentCallback *fragment_cb;
109
110 GNUNET_PSYCSTORE_CountersCallback *counters_cb;
111
112 GNUNET_PSYCSTORE_StateCallback *state_cb;
113 /**
114 * Closure for callbacks.
115 */
116 void *cb_cls;
117};
118
119
120static int
121check_result_code (void *cls, const struct OperationResult *opres)
122{
123 uint16_t size = ntohs (opres->header.size);
124 const char *str = (const char *) &opres[1];
125 if ( (sizeof (*opres) < size) &&
126 ('\0' != str[size - sizeof (*opres) - 1]) )
127 {
128 GNUNET_break (0);
129 return GNUNET_SYSERR;
130 }
131
132 return GNUNET_OK;
133}
134
135
136static void
137handle_result_code (void *cls, const struct OperationResult *opres)
138{
139 struct GNUNET_PSYCSTORE_Handle *h = cls;
140 struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
141 uint16_t size = ntohs (opres->header.size);
142
143 const char *
144 str = (sizeof (*opres) < size) ? (const char *) &opres[1] : "";
145
146 if (GNUNET_YES == GNUNET_OP_result (h->op, GNUNET_ntohll (opres->op_id),
147 GNUNET_ntohll (opres->result_code) + INT64_MIN,
148 str, size - sizeof (*opres), (void **) &op))
149 {
150 LOG (GNUNET_ERROR_TYPE_DEBUG,
151 "handle_result_code: Received result message with OP ID: %" PRIu64 "\n",
152 GNUNET_ntohll (opres->op_id));
153 GNUNET_free (op);
154 }
155 else
156 {
157 LOG (GNUNET_ERROR_TYPE_DEBUG,
158 "handle_result_code: No callback registered for OP ID %" PRIu64 ".\n",
159 GNUNET_ntohll (opres->op_id));
160 }
161 h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
162}
163
164
165static void
166handle_result_counters (void *cls, const struct CountersResult *cres)
167{
168 struct GNUNET_PSYCSTORE_Handle *h = cls;
169 struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
170
171 if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (cres->op_id),
172 NULL, NULL, (void **) &op))
173 {
174 GNUNET_assert (NULL != op);
175 if (NULL != op->counters_cb)
176 {
177 op->counters_cb (op->cls,
178 ntohl (cres->result_code),
179 GNUNET_ntohll (cres->max_fragment_id),
180 GNUNET_ntohll (cres->max_message_id),
181 GNUNET_ntohll (cres->max_group_generation),
182 GNUNET_ntohll (cres->max_state_message_id));
183 }
184 GNUNET_OP_remove (h->op, GNUNET_ntohll (cres->op_id));
185 GNUNET_free (op);
186 }
187 else
188 {
189 LOG (GNUNET_ERROR_TYPE_DEBUG,
190 "handle_result_counters: No callback registered for OP ID %" PRIu64 ".\n",
191 GNUNET_ntohll (cres->op_id));
192 }
193 h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
194}
195
196
197static int
198check_result_fragment (void *cls, const struct FragmentResult *fres)
199{
200 uint16_t size = ntohs (fres->header.size);
201 struct GNUNET_MULTICAST_MessageHeader *mmsg =
202 (struct GNUNET_MULTICAST_MessageHeader *) &fres[1];
203 if (sizeof (*fres) + sizeof (*mmsg) < size
204 && sizeof (*fres) + ntohs (mmsg->header.size) != size)
205 {
206 LOG (GNUNET_ERROR_TYPE_ERROR,
207 "check_result_fragment: Received message with invalid length %lu bytes.\n",
208 size, sizeof (*fres));
209 GNUNET_break (0);
210 return GNUNET_SYSERR;
211 }
212 return GNUNET_OK;
213}
214
215
216static void
217handle_result_fragment (void *cls, const struct FragmentResult *fres)
218{
219 struct GNUNET_PSYCSTORE_Handle *h = cls;
220 struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
221
222 if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (fres->op_id),
223 NULL, NULL, (void **) &op))
224 {
225 GNUNET_assert (NULL != op);
226 if (NULL != op->fragment_cb)
227 op->fragment_cb (op->cls,
228 (struct GNUNET_MULTICAST_MessageHeader *) &fres[1],
229 ntohl (fres->psycstore_flags));
230 //GNUNET_OP_remove (h->op, GNUNET_ntohll (fres->op_id));
231 //GNUNET_free (op);
232 }
233 else
234 {
235 LOG (GNUNET_ERROR_TYPE_DEBUG,
236 "handle_result_fragment: No callback registered for OP ID %" PRIu64 ".\n",
237 GNUNET_ntohll (fres->op_id));
238 }
239 h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
240}
241
242
243static int
244check_result_state (void *cls, const struct StateResult *sres)
245{
246 const char *name = (const char *) &sres[1];
247 uint16_t size = ntohs (sres->header.size);
248 uint16_t name_size = ntohs (sres->name_size);
249
250 if (name_size <= 2
251 || size - sizeof (*sres) < name_size
252 || '\0' != name[name_size - 1])
253 {
254 LOG (GNUNET_ERROR_TYPE_ERROR,
255 "check_result_state: Received state result message with invalid name.\n");
256 GNUNET_break (0);
257 return GNUNET_SYSERR;
258 }
259 return GNUNET_OK;
260}
261
262
263static void
264handle_result_state (void *cls, const struct StateResult *sres)
265{
266 struct GNUNET_PSYCSTORE_Handle *h = cls;
267 struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
268
269 const char *name = (const char *) &sres[1];
270 uint16_t name_size = ntohs (sres->name_size);
271
272 if (GNUNET_YES == GNUNET_OP_get (h->op, GNUNET_ntohll (sres->op_id),
273 NULL, NULL, (void **) &op))
274 {
275 GNUNET_assert (NULL != op);
276 if (NULL != op->state_cb)
277 op->state_cb (op->cls, name, (char *) &sres[1] + name_size,
278 ntohs (sres->header.size) - sizeof (*sres) - name_size);
279 //GNUNET_OP_remove (h->op, GNUNET_ntohll (sres->op_id));
280 //GNUNET_free (op);
281 }
282 else
283 {
284 LOG (GNUNET_ERROR_TYPE_DEBUG,
285 "handle_result_state: No callback registered for OP ID %" PRIu64 ".\n",
286 GNUNET_ntohll (sres->op_id));
287 }
288 h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
289}
290
291
292static void
293reconnect (void *cls);
294
295
296/**
297 * Client disconnected from service.
298 *
299 * Reconnect after backoff period.=
300 */
301static void
302disconnected (void *cls, enum GNUNET_MQ_Error error)
303{
304 struct GNUNET_PSYCSTORE_Handle *h = cls;
305
306 LOG (GNUNET_ERROR_TYPE_DEBUG,
307 "Origin client disconnected (%d), re-connecting\n",
308 (int) error);
309 if (NULL != h->mq)
310 {
311 GNUNET_MQ_destroy (h->mq);
312 GNUNET_OP_destroy (h->op);
313 h->mq = NULL;
314 h->op = NULL;
315 }
316
317 h->reconnect_task = GNUNET_SCHEDULER_add_delayed (h->reconnect_delay,
318 &reconnect, h);
319 h->reconnect_delay = GNUNET_TIME_STD_BACKOFF (h->reconnect_delay);
320}
321
322
323static void
324do_connect (struct GNUNET_PSYCSTORE_Handle *h)
325{
326 LOG (GNUNET_ERROR_TYPE_DEBUG,
327 "Connecting to PSYCstore service.\n");
328
329 struct GNUNET_MQ_MessageHandler handlers[] = {
330 GNUNET_MQ_hd_var_size (result_code,
331 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_CODE,
332 struct OperationResult,
333 h),
334 GNUNET_MQ_hd_fixed_size (result_counters,
335 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_COUNTERS,
336 struct CountersResult,
337 h),
338 GNUNET_MQ_hd_var_size (result_fragment,
339 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_FRAGMENT,
340 struct FragmentResult,
341 h),
342 GNUNET_MQ_hd_var_size (result_state,
343 GNUNET_MESSAGE_TYPE_PSYCSTORE_RESULT_STATE,
344 struct StateResult,
345 h),
346 GNUNET_MQ_handler_end ()
347 };
348
349 h->op = GNUNET_OP_create ();
350 GNUNET_assert (NULL == h->mq);
351 h->mq = GNUNET_CLIENT_connect (h->cfg, "psycstore",
352 handlers, disconnected, h);
353 GNUNET_assert (NULL != h->mq);
354}
355
356
357/**
358 * Try again to connect to the PSYCstore service.
359 *
360 * @param cls Handle to the PSYCstore service.
361 */
362static void
363reconnect (void *cls)
364{
365 struct GNUNET_PSYCSTORE_Handle *h = cls;
366
367 h->reconnect_task = NULL;
368 do_connect (cls);
369}
370
371
372/**
373 * Connect to the PSYCstore service.
374 *
375 * @param cfg The configuration to use
376 * @return Handle to use
377 */
378struct GNUNET_PSYCSTORE_Handle *
379GNUNET_PSYCSTORE_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
380{
381 struct GNUNET_PSYCSTORE_Handle *h
382 = GNUNET_new (struct GNUNET_PSYCSTORE_Handle);
383 h->cfg = cfg;
384 h->reconnect_delay = GNUNET_TIME_UNIT_MILLISECONDS;
385 do_connect (h);
386 return h;
387}
388
389
390/**
391 * Disconnect from PSYCstore service
392 *
393 * @param h Handle to destroy
394 */
395void
396GNUNET_PSYCSTORE_disconnect (struct GNUNET_PSYCSTORE_Handle *h)
397{
398 GNUNET_assert (NULL != h);
399 if (h->reconnect_task != NULL)
400 {
401 GNUNET_SCHEDULER_cancel (h->reconnect_task);
402 h->reconnect_task = NULL;
403 }
404 if (NULL != h->mq)
405 {
406 // FIXME: free data structures for pending operations
407 GNUNET_MQ_destroy (h->mq);
408 h->mq = NULL;
409 }
410 GNUNET_free (h);
411}
412
413
414/**
415 * Message sent notification.
416 *
417 * Remove invalidated envelope pointer.
418 */
419static void
420message_sent (void *cls)
421{
422 struct GNUNET_PSYCSTORE_OperationHandle *op = cls;
423 op->env = NULL;
424}
425
426
427/**
428 * Create a new operation.
429 */
430static struct GNUNET_PSYCSTORE_OperationHandle *
431op_create (struct GNUNET_PSYCSTORE_Handle *h,
432 struct GNUNET_OP_Handle *hop,
433 GNUNET_PSYCSTORE_ResultCallback result_cb,
434 void *cls)
435{
436 struct GNUNET_PSYCSTORE_OperationHandle *
437 op = GNUNET_malloc (sizeof (*op));
438 op->h = h;
439 op->op_id = GNUNET_OP_add (hop,
440 (GNUNET_ResultCallback) result_cb,
441 cls, op);
442 return op;
443}
444
445
446/**
447 * Send a message associated with an operation.
448 *
449 * @param h
450 * PSYCstore handle.
451 * @param op
452 * Operation handle.
453 * @param env
454 * Message envelope to send.
455 * @param[out] op_id
456 * Operation ID to write in network byte order. NULL if not needed.
457 *
458 * @return Operation handle.
459 *
460 */
461static struct GNUNET_PSYCSTORE_OperationHandle *
462op_send (struct GNUNET_PSYCSTORE_Handle *h,
463 struct GNUNET_PSYCSTORE_OperationHandle *op,
464 struct GNUNET_MQ_Envelope *env,
465 uint64_t *op_id)
466{
467 op->env = env;
468 if (NULL != op_id)
469 *op_id = GNUNET_htonll (op->op_id);
470
471 GNUNET_MQ_notify_sent (env, message_sent, op);
472 GNUNET_MQ_send (h->mq, env);
473 return op;
474}
475
476
477/**
478 * Cancel a PSYCstore operation. Note that the operation MAY still
479 * be executed; this merely cancels the continuation; if the request
480 * was already transmitted, the service may still choose to complete
481 * the operation.
482 *
483 * @param op Operation to cancel.
484 *
485 * @return #GNUNET_YES if message was not sent yet and got discarded,
486 * #GNUNET_NO if it was already sent, and only the callbacks got cancelled.
487 */
488int
489GNUNET_PSYCSTORE_operation_cancel (struct GNUNET_PSYCSTORE_OperationHandle *op)
490{
491 struct GNUNET_PSYCSTORE_Handle *h = op->h;
492 int ret = GNUNET_NO;
493
494 if (NULL != op->env)
495 {
496 GNUNET_MQ_send_cancel (op->env);
497 ret = GNUNET_YES;
498 }
499
500 GNUNET_OP_remove (h->op, op->op_id);
501 GNUNET_free (op);
502
503 return ret;
504}
505
506
507/**
508 * Store join/leave events for a PSYC channel in order to be able to answer
509 * membership test queries later.
510 *
511 * @param h
512 * Handle for the PSYCstore.
513 * @param channel_key
514 * The channel where the event happened.
515 * @param slave_key
516 * Public key of joining/leaving slave.
517 * @param did_join
518 * #GNUNET_YES on join, #GNUNET_NO on part.
519 * @param announced_at
520 * ID of the message that announced the membership change.
521 * @param effective_since
522 * Message ID this membership change is in effect since.
523 * For joins it is <= announced_at, for parts it is always 0.
524 * @param group_generation
525 * In case of a part, the last group generation the slave has access to.
526 * It has relevance when a larger message have fragments with different
527 * group generations.
528 * @param result_cb
529 * Callback to call with the result of the storage operation.
530 * @param cls
531 * Closure for the callback.
532 *
533 * @return Operation handle that can be used to cancel the operation.
534 */
535struct GNUNET_PSYCSTORE_OperationHandle *
536GNUNET_PSYCSTORE_membership_store (struct GNUNET_PSYCSTORE_Handle *h,
537 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
538 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
539 int did_join,
540 uint64_t announced_at,
541 uint64_t effective_since,
542 uint64_t group_generation,
543 GNUNET_PSYCSTORE_ResultCallback result_cb,
544 void *cls)
545{
546 GNUNET_assert (NULL != h);
547 GNUNET_assert (NULL != channel_key);
548 GNUNET_assert (NULL != slave_key);
549 GNUNET_assert (GNUNET_YES == did_join || GNUNET_NO == did_join);
550 GNUNET_assert (did_join
551 ? effective_since <= announced_at
552 : effective_since == 0);
553
554 struct MembershipStoreRequest *req;
555 struct GNUNET_MQ_Envelope *
556 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_STORE);
557 req->channel_key = *channel_key;
558 req->slave_key = *slave_key;
559 req->did_join = did_join;
560 req->announced_at = GNUNET_htonll (announced_at);
561 req->effective_since = GNUNET_htonll (effective_since);
562 req->group_generation = GNUNET_htonll (group_generation);
563
564 return
565 op_send (h, op_create (h, h->op, result_cb, cls),
566 env, &req->op_id);
567}
568
569
570/**
571 * Test if a member was admitted to the channel at the given message ID.
572 *
573 * This is useful when relaying and replaying messages to check if a particular
574 * slave has access to the message fragment with a given group generation. It
575 * is also used when handling join requests to determine whether the slave is
576 * currently admitted to the channel.
577 *
578 * @param h
579 * Handle for the PSYCstore.
580 * @param channel_key
581 * The channel we are interested in.
582 * @param slave_key
583 * Public key of slave whose membership to check.
584 * @param message_id
585 * Message ID for which to do the membership test.
586 * @param group_generation
587 * Group generation of the fragment of the message to test.
588 * It has relevance if the message consists of multiple fragments with
589 * different group generations.
590 * @param result_cb
591 * Callback to call with the test result.
592 * @param cls
593 * Closure for the callback.
594 *
595 * @return Operation handle that can be used to cancel the operation.
596 */
597struct GNUNET_PSYCSTORE_OperationHandle *
598GNUNET_PSYCSTORE_membership_test (struct GNUNET_PSYCSTORE_Handle *h,
599 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
600 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
601 uint64_t message_id,
602 uint64_t group_generation,
603 GNUNET_PSYCSTORE_ResultCallback result_cb,
604 void *cls)
605{
606 struct MembershipTestRequest *req;
607 struct GNUNET_MQ_Envelope *
608 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_MEMBERSHIP_TEST);
609 req->channel_key = *channel_key;
610 req->slave_key = *slave_key;
611 req->message_id = GNUNET_htonll (message_id);
612 req->group_generation = GNUNET_htonll (group_generation);
613
614 return
615 op_send (h, op_create (h, h->op, result_cb, cls),
616 env, &req->op_id);
617}
618
619
620/**
621 * Store a message fragment sent to a channel.
622 *
623 * @param h Handle for the PSYCstore.
624 * @param channel_key The channel the message belongs to.
625 * @param message Message to store.
626 * @param psycstore_flags Flags indicating whether the PSYC message contains
627 * state modifiers.
628 * @param result_cb Callback to call with the result of the operation.
629 * @param cls Closure for the callback.
630 *
631 * @return Handle that can be used to cancel the operation.
632 */
633struct GNUNET_PSYCSTORE_OperationHandle *
634GNUNET_PSYCSTORE_fragment_store (struct GNUNET_PSYCSTORE_Handle *h,
635 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
636 const struct GNUNET_MULTICAST_MessageHeader *msg,
637 enum GNUNET_PSYCSTORE_MessageFlags psycstore_flags,
638 GNUNET_PSYCSTORE_ResultCallback result_cb,
639 void *cls)
640{
641 uint16_t size = ntohs (msg->header.size);
642 struct FragmentStoreRequest *req;
643 struct GNUNET_MQ_Envelope *
644 env = GNUNET_MQ_msg_extra (req, size,
645 GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_STORE);
646 req->channel_key = *channel_key;
647 req->psycstore_flags = htonl (psycstore_flags);
648 GNUNET_memcpy (&req[1], msg, size);
649
650 return
651 op_send (h, op_create (h, h->op, result_cb, cls),
652 env, &req->op_id);
653}
654
655
656/**
657 * Retrieve message fragments by fragment ID range.
658 *
659 * @param h
660 * Handle for the PSYCstore.
661 * @param channel_key
662 * The channel we are interested in.
663 * @param slave_key
664 * The slave requesting the fragment. If not NULL, a membership test is
665 * performed first and the fragment is only returned if the slave has
666 * access to it.
667 * @param first_fragment_id
668 * First fragment ID to retrieve.
669 * Use 0 to get the latest message fragment.
670 * @param last_fragment_id
671 * Last consecutive fragment ID to retrieve.
672 * Use 0 to get the latest message fragment.
673 * @param fragment_limit
674 * Maximum number of fragments to retrieve.
675 * @param fragment_cb
676 * Callback to call with the retrieved fragments.
677 * @param result_cb
678 * Callback to call with the result of the operation.
679 * @param cls
680 * Closure for the callbacks.
681 *
682 * @return Handle that can be used to cancel the operation.
683 */
684struct GNUNET_PSYCSTORE_OperationHandle *
685GNUNET_PSYCSTORE_fragment_get (struct GNUNET_PSYCSTORE_Handle *h,
686 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
687 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
688 uint64_t first_fragment_id,
689 uint64_t last_fragment_id,
690 GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
691 GNUNET_PSYCSTORE_ResultCallback result_cb,
692 void *cls)
693{
694 struct FragmentGetRequest *req;
695 struct GNUNET_MQ_Envelope *
696 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET);
697 req->channel_key = *channel_key;
698 req->first_fragment_id = GNUNET_htonll (first_fragment_id);
699 req->last_fragment_id = GNUNET_htonll (last_fragment_id);
700 if (NULL != slave_key)
701 {
702 req->slave_key = *slave_key;
703 req->do_membership_test = GNUNET_YES;
704 }
705
706 struct GNUNET_PSYCSTORE_OperationHandle *
707 op = op_create (h, h->op, result_cb, cls);
708 op->fragment_cb = fragment_cb;
709 op->cls = cls;
710 return op_send (h, op, env, &req->op_id);
711}
712
713
714/**
715 * Retrieve latest message fragments.
716 *
717 * @param h
718 * Handle for the PSYCstore.
719 * @param channel_key
720 * The channel we are interested in.
721 * @param slave_key
722 * The slave requesting the fragment. If not NULL, a membership test is
723 * performed first and the fragment is only returned if the slave has
724 * access to it.
725 * @param first_fragment_id
726 * First fragment ID to retrieve.
727 * Use 0 to get the latest message fragment.
728 * @param last_fragment_id
729 * Last consecutive fragment ID to retrieve.
730 * Use 0 to get the latest message fragment.
731 * @param fragment_limit
732 * Maximum number of fragments to retrieve.
733 * @param fragment_cb
734 * Callback to call with the retrieved fragments.
735 * @param result_cb
736 * Callback to call with the result of the operation.
737 * @param cls
738 * Closure for the callbacks.
739 *
740 * @return Handle that can be used to cancel the operation.
741 */
742struct GNUNET_PSYCSTORE_OperationHandle *
743GNUNET_PSYCSTORE_fragment_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
744 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
745 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
746 uint64_t fragment_limit,
747 GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
748 GNUNET_PSYCSTORE_ResultCallback result_cb,
749 void *cls)
750{
751 struct FragmentGetRequest *req;
752 struct GNUNET_MQ_Envelope *
753 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_FRAGMENT_GET);
754 req->channel_key = *channel_key;
755 req->fragment_limit = GNUNET_ntohll (fragment_limit);
756 if (NULL != slave_key)
757 {
758 req->slave_key = *slave_key;
759 req->do_membership_test = GNUNET_YES;
760 }
761
762 struct GNUNET_PSYCSTORE_OperationHandle *
763 op = op_create (h, h->op, result_cb, cls);
764 op->fragment_cb = fragment_cb;
765 op->cls = cls;
766 return op_send (h, op, env, &req->op_id);
767}
768
769
770/**
771 * Retrieve all fragments of messages in a message ID range.
772 *
773 * @param h
774 * Handle for the PSYCstore.
775 * @param channel_key
776 * The channel we are interested in.
777 * @param slave_key
778 * The slave requesting the message.
779 * If not NULL, a membership test is performed first
780 * and the message is only returned if the slave has access to it.
781 * @param first_message_id
782 * First message ID to retrieve.
783 * @param last_message_id
784 * Last consecutive message ID to retrieve.
785 * @param fragment_limit
786 * Maximum number of fragments to retrieve.
787 * @param method_prefix
788 * Retrieve only messages with a matching method prefix.
789 * @todo Implement method_prefix query.
790 * @param fragment_cb
791 * Callback to call with the retrieved fragments.
792 * @param result_cb
793 * Callback to call with the result of the operation.
794 * @param cls
795 * Closure for the callbacks.
796 *
797 * @return Handle that can be used to cancel the operation.
798 */
799struct GNUNET_PSYCSTORE_OperationHandle *
800GNUNET_PSYCSTORE_message_get (struct GNUNET_PSYCSTORE_Handle *h,
801 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
802 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
803 uint64_t first_message_id,
804 uint64_t last_message_id,
805 uint64_t fragment_limit,
806 const char *method_prefix,
807 GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
808 GNUNET_PSYCSTORE_ResultCallback result_cb,
809 void *cls)
810{
811 struct MessageGetRequest *req;
812 if (NULL == method_prefix)
813 method_prefix = "";
814 uint16_t method_size = strnlen (method_prefix,
815 GNUNET_MAX_MESSAGE_SIZE
816 - sizeof (*req)) + 1;
817
818 struct GNUNET_MQ_Envelope *
819 env = GNUNET_MQ_msg_extra (req, method_size,
820 GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET);
821 req->channel_key = *channel_key;
822 req->first_message_id = GNUNET_htonll (first_message_id);
823 req->last_message_id = GNUNET_htonll (last_message_id);
824 req->fragment_limit = GNUNET_htonll (fragment_limit);
825 if (NULL != slave_key)
826 {
827 req->slave_key = *slave_key;
828 req->do_membership_test = GNUNET_YES;
829 }
830 GNUNET_memcpy (&req[1], method_prefix, method_size);
831 ((char *) &req[1])[method_size - 1] = '\0';
832
833 struct GNUNET_PSYCSTORE_OperationHandle *
834 op = op_create (h, h->op, result_cb, cls);
835 op->fragment_cb = fragment_cb;
836 op->cls = cls;
837 return op_send (h, op, env, &req->op_id);
838}
839
840
841/**
842 * Retrieve all fragments of the latest messages.
843 *
844 * @param h
845 * Handle for the PSYCstore.
846 * @param channel_key
847 * The channel we are interested in.
848 * @param slave_key
849 * The slave requesting the message.
850 * If not NULL, a membership test is performed first
851 * and the message is only returned if the slave has access to it.
852 * @param message_limit
853 * Maximum number of messages to retrieve.
854 * @param method_prefix
855 * Retrieve only messages with a matching method prefix.
856 * @todo Implement method_prefix query.
857 * @param fragment_cb
858 * Callback to call with the retrieved fragments.
859 * @param result_cb
860 * Callback to call with the result of the operation.
861 * @param cls
862 * Closure for the callbacks.
863 *
864 * @return Handle that can be used to cancel the operation.
865 */
866struct GNUNET_PSYCSTORE_OperationHandle *
867GNUNET_PSYCSTORE_message_get_latest (struct GNUNET_PSYCSTORE_Handle *h,
868 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
869 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
870 uint64_t message_limit,
871 const char *method_prefix,
872 GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
873 GNUNET_PSYCSTORE_ResultCallback result_cb,
874 void *cls)
875{
876 struct MessageGetRequest *req;
877
878 if (NULL == method_prefix)
879 method_prefix = "";
880 uint16_t method_size = strnlen (method_prefix,
881 GNUNET_MAX_MESSAGE_SIZE
882 - sizeof (*req)) + 1;
883 GNUNET_assert ('\0' == method_prefix[method_size - 1]);
884
885 struct GNUNET_MQ_Envelope *
886 env = GNUNET_MQ_msg_extra (req, method_size,
887 GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET);
888 req->channel_key = *channel_key;
889 req->message_limit = GNUNET_ntohll (message_limit);
890 if (NULL != slave_key)
891 {
892 req->slave_key = *slave_key;
893 req->do_membership_test = GNUNET_YES;
894 }
895 GNUNET_memcpy (&req[1], method_prefix, method_size);
896
897 struct GNUNET_PSYCSTORE_OperationHandle *
898 op = op_create (h, h->op, result_cb, cls);
899 op->fragment_cb = fragment_cb;
900 op->cls = cls;
901 return op_send (h, op, env, &req->op_id);
902}
903
904
905/**
906 * Retrieve a fragment of message specified by its message ID and fragment
907 * offset.
908 *
909 * @param h
910 * Handle for the PSYCstore.
911 * @param channel_key
912 * The channel we are interested in.
913 * @param slave_key
914 * The slave requesting the message fragment. If not NULL, a membership
915 * test is performed first and the message fragment is only returned
916 * if the slave has access to it.
917 * @param message_id
918 * Message ID to retrieve. Use 0 to get the latest message.
919 * @param fragment_offset
920 * Offset of the fragment to retrieve.
921 * @param fragment_cb
922 * Callback to call with the retrieved fragments.
923 * @param result_cb
924 * Callback to call with the result of the operation.
925 * @param cls
926 * Closure for the callbacks.
927 *
928 * @return Handle that can be used to cancel the operation.
929 */
930struct GNUNET_PSYCSTORE_OperationHandle *
931GNUNET_PSYCSTORE_message_get_fragment (struct GNUNET_PSYCSTORE_Handle *h,
932 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
933 const struct GNUNET_CRYPTO_EcdsaPublicKey *slave_key,
934 uint64_t message_id,
935 uint64_t fragment_offset,
936 GNUNET_PSYCSTORE_FragmentCallback fragment_cb,
937 GNUNET_PSYCSTORE_ResultCallback result_cb,
938 void *cls)
939{
940 struct MessageGetFragmentRequest *req;
941 struct GNUNET_MQ_Envelope *
942 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_MESSAGE_GET_FRAGMENT);
943
944 req->channel_key = *channel_key;
945 req->message_id = GNUNET_htonll (message_id);
946 req->fragment_offset = GNUNET_htonll (fragment_offset);
947 if (NULL != slave_key)
948 {
949 req->slave_key = *slave_key;
950 req->do_membership_test = GNUNET_YES;
951 }
952
953 struct GNUNET_PSYCSTORE_OperationHandle *
954 op = op_create (h, h->op, result_cb, cls);
955 op->fragment_cb = fragment_cb;
956 op->cls = cls;
957 return op_send (h, op, env, &req->op_id);
958}
959
960
961/**
962 * Retrieve latest values of counters for a channel master.
963 *
964 * The current value of counters are needed when a channel master is restarted,
965 * so that it can continue incrementing the counters from their last value.
966 *
967 * @param h
968 * Handle for the PSYCstore.
969 * @param channel_key
970 * Public key that identifies the channel.
971 * @param ccb
972 * Callback to call with the result.
973 * @param ccb_cls
974 * Closure for the @a ccb callback.
975 *
976 * @return Handle that can be used to cancel the operation.
977 */
978struct GNUNET_PSYCSTORE_OperationHandle *
979GNUNET_PSYCSTORE_counters_get (struct GNUNET_PSYCSTORE_Handle *h,
980 struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
981 GNUNET_PSYCSTORE_CountersCallback counters_cb,
982 void *cls)
983{
984 struct OperationRequest *req;
985 struct GNUNET_MQ_Envelope *
986 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_COUNTERS_GET);
987 req->channel_key = *channel_key;
988
989 struct GNUNET_PSYCSTORE_OperationHandle *
990 op = op_create (h, h->op, NULL, NULL);
991 op->counters_cb = counters_cb;
992 op->cls = cls;
993 return op_send (h, op, env, &req->op_id);
994}
995
996
997/**
998 * Apply modifiers of a message to the current channel state.
999 *
1000 * An error is returned if there are missing messages containing state
1001 * operations before the current one.
1002 *
1003 * @param h
1004 * Handle for the PSYCstore.
1005 * @param channel_key
1006 * The channel we are interested in.
1007 * @param message_id
1008 * ID of the message that contains the @a modifiers.
1009 * @param state_delta
1010 * Value of the _state_delta PSYC header variable of the message.
1011 * @param result_cb
1012 * Callback to call with the result of the operation.
1013 * @param cls
1014 * Closure for @a result_cb.
1015 *
1016 * @return Handle that can be used to cancel the operation.
1017 */
1018struct GNUNET_PSYCSTORE_OperationHandle *
1019GNUNET_PSYCSTORE_state_modify (struct GNUNET_PSYCSTORE_Handle *h,
1020 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1021 uint64_t message_id,
1022 uint64_t state_delta,
1023 GNUNET_PSYCSTORE_ResultCallback result_cb,
1024 void *cls)
1025{
1026 struct StateModifyRequest *req;
1027 struct GNUNET_MQ_Envelope *
1028 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_MODIFY);
1029 req->channel_key = *channel_key;
1030 req->message_id = GNUNET_htonll (message_id);
1031 req->state_delta = GNUNET_htonll (state_delta);
1032
1033 return op_send (h, op_create (h, h->op, result_cb, cls),
1034 env, &req->op_id);
1035}
1036
1037
1038struct StateSyncClosure
1039{
1040 GNUNET_PSYCSTORE_ResultCallback result_cb;
1041 void *cls;
1042 uint8_t last;
1043};
1044
1045
1046static void
1047state_sync_result (void *cls, int64_t result,
1048 const char *err_msg, uint16_t err_msg_size)
1049{
1050 struct StateSyncClosure *ssc = cls;
1051 if (GNUNET_OK != result || ssc->last)
1052 ssc->result_cb (ssc->cls, result, err_msg, err_msg_size);
1053 GNUNET_free (ssc);
1054}
1055
1056
1057/**
1058 * Store synchronized state.
1059 *
1060 * @param h
1061 * Handle for the PSYCstore.
1062 * @param channel_key
1063 * The channel we are interested in.
1064 * @param max_state_message_id
1065 * ID of the last stateful message before @a state_hash_message_id.
1066 * @param state_hash_message_id
1067 * ID of the message that contains the state_hash PSYC header variable.
1068 * @param modifier_count
1069 * Number of elements in the @a modifiers array.
1070 * @param modifiers
1071 * Full state to store.
1072 * @param result_cb
1073 * Callback to call with the result of the operation.
1074 * @param cls
1075 * Closure for the callback.
1076 *
1077 * @return Handle that can be used to cancel the operation.
1078 */
1079struct GNUNET_PSYCSTORE_OperationHandle *
1080GNUNET_PSYCSTORE_state_sync (struct GNUNET_PSYCSTORE_Handle *h,
1081 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1082 uint64_t max_state_message_id,
1083 uint64_t state_hash_message_id,
1084 size_t modifier_count,
1085 const struct GNUNET_PSYC_Modifier *modifiers,
1086 GNUNET_PSYCSTORE_ResultCallback result_cb,
1087 void *cls)
1088{
1089 struct GNUNET_PSYCSTORE_OperationHandle *op = NULL;
1090 size_t i;
1091
1092 for (i = 0; i < modifier_count; i++) {
1093 struct StateSyncRequest *req;
1094 uint16_t name_size = strlen (modifiers[i].name) + 1;
1095
1096 struct GNUNET_MQ_Envelope *
1097 env = GNUNET_MQ_msg_extra (req,
1098 sizeof (*req) + name_size + modifiers[i].value_size,
1099 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC);
1100
1101 req->header.type = htons (GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_SYNC);
1102 req->header.size = htons (sizeof (*req) + name_size
1103 + modifiers[i].value_size);
1104 req->channel_key = *channel_key;
1105 req->max_state_message_id = GNUNET_htonll (max_state_message_id);
1106 req->state_hash_message_id = GNUNET_htonll (state_hash_message_id);
1107 req->name_size = htons (name_size);
1108 req->flags
1109 = (0 == i)
1110 ? STATE_OP_FIRST
1111 : (modifier_count - 1 == i)
1112 ? STATE_OP_LAST
1113 : 0;
1114
1115 GNUNET_memcpy (&req[1], modifiers[i].name, name_size);
1116 GNUNET_memcpy ((char *) &req[1] + name_size, modifiers[i].value, modifiers[i].value_size);
1117
1118 struct StateSyncClosure *ssc = GNUNET_malloc (sizeof (*ssc));
1119 ssc->last = (req->flags & STATE_OP_LAST);
1120 ssc->result_cb = result_cb;
1121 ssc->cls = cls;
1122
1123 op_send (h, op_create (h, h->op, state_sync_result, ssc),
1124 env, &req->op_id);
1125 }
1126 // FIXME: only one operation is returned,
1127 // add pointers to other operations and make all cancellable.
1128 return op;
1129}
1130
1131
1132/**
1133 * Reset the state of a channel.
1134 *
1135 * Delete all state variables stored for the given channel.
1136 *
1137 * @param h
1138 * Handle for the PSYCstore.
1139 * @param channel_key
1140 * The channel we are interested in.
1141 * @param result_cb
1142 * Callback to call with the result of the operation.
1143 * @param cls
1144 * Closure for the callback.
1145 *
1146 * @return Handle that can be used to cancel the operation.
1147 */
1148struct GNUNET_PSYCSTORE_OperationHandle *
1149GNUNET_PSYCSTORE_state_reset (struct GNUNET_PSYCSTORE_Handle *h,
1150 const struct GNUNET_CRYPTO_EddsaPublicKey
1151 *channel_key,
1152 GNUNET_PSYCSTORE_ResultCallback result_cb,
1153 void *cls)
1154{
1155 struct OperationRequest *req;
1156 struct GNUNET_MQ_Envelope *
1157 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_RESET);
1158 req->channel_key = *channel_key;
1159
1160 return
1161 op_send (h, op_create (h, h->op, result_cb, cls),
1162 env, &req->op_id);
1163}
1164
1165
1166/**
1167 * Update signed values of state variables in the state store.
1168 *
1169 * @param h
1170 * Handle for the PSYCstore.
1171 * @param channel_key
1172 * The channel we are interested in.
1173 * @param message_id
1174 * Message ID that contained the state @a hash.
1175 * @param hash
1176 * Hash of the serialized full state.
1177 * @param result_cb
1178 * Callback to call with the result of the operation.
1179 * @param cls
1180 * Closure for the callback.
1181 */
1182struct GNUNET_PSYCSTORE_OperationHandle *
1183GNUNET_PSYCSTORE_state_hash_update (struct GNUNET_PSYCSTORE_Handle *h,
1184 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1185 uint64_t message_id,
1186 const struct GNUNET_HashCode *hash,
1187 GNUNET_PSYCSTORE_ResultCallback result_cb,
1188 void *cls)
1189{
1190 struct StateHashUpdateRequest *req;
1191 struct GNUNET_MQ_Envelope *
1192 env = GNUNET_MQ_msg (req, GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_HASH_UPDATE);
1193 req->channel_key = *channel_key;
1194 req->hash = *hash;
1195
1196 return
1197 op_send (h, op_create (h, h->op, result_cb, cls),
1198 env, &req->op_id);
1199}
1200
1201
1202/**
1203 * Retrieve the best matching state variable.
1204 *
1205 * @param h
1206 * Handle for the PSYCstore.
1207 * @param channel_key
1208 * The channel we are interested in.
1209 * @param name
1210 * Name of variable to match, the returned variable might be less specific.
1211 * @param state_cb
1212 * Callback to return the matching state variable.
1213 * @param result_cb
1214 * Callback to call with the result of the operation.
1215 * @param cls
1216 * Closure for the callbacks.
1217 *
1218 * @return Handle that can be used to cancel the operation.
1219 */
1220struct GNUNET_PSYCSTORE_OperationHandle *
1221GNUNET_PSYCSTORE_state_get (struct GNUNET_PSYCSTORE_Handle *h,
1222 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1223 const char *name,
1224 GNUNET_PSYCSTORE_StateCallback state_cb,
1225 GNUNET_PSYCSTORE_ResultCallback result_cb,
1226 void *cls)
1227{
1228 size_t name_size = strlen (name) + 1;
1229 struct OperationRequest *req;
1230 struct GNUNET_MQ_Envelope *
1231 env = GNUNET_MQ_msg_extra (req, name_size,
1232 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET);
1233 req->channel_key = *channel_key;
1234 GNUNET_memcpy (&req[1], name, name_size);
1235
1236 struct GNUNET_PSYCSTORE_OperationHandle *
1237 op = op_create (h, h->op, result_cb, cls);
1238 op->state_cb = state_cb;
1239 op->cls = cls;
1240 return op_send (h, op, env, &req->op_id);
1241}
1242
1243
1244/**
1245 * Retrieve all state variables for a channel with the given prefix.
1246 *
1247 * @param h
1248 * Handle for the PSYCstore.
1249 * @param channel_key
1250 * The channel we are interested in.
1251 * @param name_prefix
1252 * Prefix of state variable names to match.
1253 * @param state_cb
1254 * Callback to return matching state variables.
1255 * @param result_cb
1256 * Callback to call with the result of the operation.
1257 * @param cls
1258 * Closure for the callbacks.
1259 *
1260 * @return Handle that can be used to cancel the operation.
1261 */
1262struct GNUNET_PSYCSTORE_OperationHandle *
1263GNUNET_PSYCSTORE_state_get_prefix (struct GNUNET_PSYCSTORE_Handle *h,
1264 const struct GNUNET_CRYPTO_EddsaPublicKey *channel_key,
1265 const char *name_prefix,
1266 GNUNET_PSYCSTORE_StateCallback state_cb,
1267 GNUNET_PSYCSTORE_ResultCallback result_cb,
1268 void *cls)
1269{
1270 size_t name_size = strlen (name_prefix) + 1;
1271 struct OperationRequest *req;
1272 struct GNUNET_MQ_Envelope *
1273 env = GNUNET_MQ_msg_extra (req, name_size,
1274 GNUNET_MESSAGE_TYPE_PSYCSTORE_STATE_GET_PREFIX);
1275 req->channel_key = *channel_key;
1276 GNUNET_memcpy (&req[1], name_prefix, name_size);
1277
1278 struct GNUNET_PSYCSTORE_OperationHandle *
1279 op = op_create (h, h->op, result_cb, cls);
1280 op->state_cb = state_cb;
1281 op->cls = cls;
1282 return op_send (h, op, env, &req->op_id);
1283}
1284
1285/* end of psycstore_api.c */
diff --git a/src/psycstore/test_plugin_psycstore.c b/src/psycstore/test_plugin_psycstore.c
new file mode 100644
index 0000000..ff4eac8
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore.c
@@ -0,0 +1,532 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @author Gabor X Toth
23 * @author Christian Grothoff
24 *
25 * @file
26 * Test for the PSYCstore plugins.
27 */
28
29#include <inttypes.h>
30
31#include "platform.h"
32#include "gnunet_util_lib.h"
33#include "gnunet_testing_lib.h"
34#include "gnunet_psycstore_plugin.h"
35#include "gnunet_psycstore_service.h"
36#include "gnunet_multicast_service.h"
37
38#define DEBUG_PSYCSTORE GNUNET_EXTRA_LOGGING
39#if DEBUG_PSYCSTORE
40# define LOG_LEVEL "DEBUG"
41#else
42# define LOG_LEVEL "WARNING"
43#endif
44
45#define C2ARG(str) str, (sizeof (str) - 1)
46
47#define LOG(kind,...) \
48 GNUNET_log_from (kind, "test-plugin-psycstore", __VA_ARGS__)
49
50static int ok;
51
52/**
53 * Name of plugin under test.
54 */
55static const char *plugin_name;
56
57static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
58static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
59
60static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
61static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
62
63/**
64 * Function called when the service shuts down. Unloads our psycstore
65 * plugin.
66 *
67 * @param api api to unload
68 */
69static void
70unload_plugin (struct GNUNET_PSYCSTORE_PluginFunctions *api)
71{
72 char *libname;
73
74 GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
75 GNUNET_break (NULL == GNUNET_PLUGIN_unload (libname, api));
76 GNUNET_free (libname);
77}
78
79
80/**
81 * Load the psycstore plugin.
82 *
83 * @param cfg configuration to pass
84 * @return NULL on error
85 */
86static struct GNUNET_PSYCSTORE_PluginFunctions *
87load_plugin (const struct GNUNET_CONFIGURATION_Handle *cfg)
88{
89 struct GNUNET_PSYCSTORE_PluginFunctions *ret;
90 char *libname;
91
92 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Loading `%s' psycstore plugin\n"),
93 plugin_name);
94 GNUNET_asprintf (&libname, "libgnunet_plugin_psycstore_%s", plugin_name);
95 if (NULL == (ret = GNUNET_PLUGIN_load (libname, (void*) cfg)))
96 {
97 FPRINTF (stderr, "Failed to load plugin `%s'!\n", plugin_name);
98 return NULL;
99 }
100 GNUNET_free (libname);
101 return ret;
102}
103
104
105#define MAX_MSG 16
106
107struct FragmentClosure
108{
109 uint8_t n;
110 uint64_t flags[MAX_MSG];
111 struct GNUNET_MULTICAST_MessageHeader *msg[MAX_MSG];
112};
113
114static int
115fragment_cb (void *cls, struct GNUNET_MULTICAST_MessageHeader *msg2,
116 enum GNUNET_PSYCSTORE_MessageFlags flags)
117{
118 struct FragmentClosure *fcls = cls;
119 struct GNUNET_MULTICAST_MessageHeader *msg1;
120 uint64_t flags1;
121 int ret;
122
123 if (fcls->n >= MAX_MSG)
124 {
125 GNUNET_break (0);
126 return GNUNET_SYSERR;
127 }
128 msg1 = fcls->msg[fcls->n];
129 flags1 = fcls->flags[fcls->n++];
130 if (NULL == msg1)
131 {
132 GNUNET_break (0);
133 return GNUNET_SYSERR;
134 }
135
136 if (flags1 == flags && msg1->header.size == msg2->header.size
137 && 0 == memcmp (msg1, msg2, ntohs (msg1->header.size)))
138 {
139 LOG (GNUNET_ERROR_TYPE_DEBUG, "Fragment %llu matches\n",
140 GNUNET_ntohll (msg1->fragment_id));
141 ret = GNUNET_YES;
142 }
143 else
144 {
145 LOG (GNUNET_ERROR_TYPE_ERROR, "Fragment %llu differs\n",
146 GNUNET_ntohll (msg1->fragment_id));
147 ret = GNUNET_SYSERR;
148 }
149
150 GNUNET_free (msg2);
151 return ret;
152}
153
154
155struct StateClosure {
156 size_t n;
157 char *name[16];
158 void *value[16];
159 size_t value_size[16];
160};
161
162static int
163state_cb (void *cls, const char *name, const void *value, uint32_t value_size)
164{
165 struct StateClosure *scls = cls;
166 const void *val = scls->value[scls->n]; // FIXME: check for n out-of-bounds FIRST!
167 size_t val_size = scls->value_size[scls->n++];
168
169 /* FIXME: check name */
170
171 LOG (GNUNET_ERROR_TYPE_DEBUG,
172 " name = %s, value_size = %u\n",
173 name, value_size);
174
175 return GNUNET_YES;
176 return value_size == val_size && 0 == memcmp (value, val, val_size)
177 ? GNUNET_YES
178 : GNUNET_SYSERR;
179}
180
181
182static void
183run (void *cls, char *const *args, const char *cfgfile,
184 const struct GNUNET_CONFIGURATION_Handle *cfg)
185{
186 struct GNUNET_PSYCSTORE_PluginFunctions *db;
187
188 ok = 1;
189 db = load_plugin (cfg);
190 if (NULL == db)
191 {
192 FPRINTF (stderr,
193 "%s",
194 "Failed to initialize PSYCstore. "
195 "Database likely not setup, skipping test.\n");
196 ok = 77;
197 return;
198 }
199
200 /* Store & test membership */
201
202 LOG (GNUNET_ERROR_TYPE_INFO, "MEMBERSHIP\n");
203
204 channel_key = GNUNET_CRYPTO_eddsa_key_create ();
205 slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
206
207 GNUNET_CRYPTO_eddsa_key_get_public (channel_key,
208 &channel_pub_key);
209 GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
210
211 LOG (GNUNET_ERROR_TYPE_INFO, "membership_store()\n");
212
213 GNUNET_assert (GNUNET_OK == db->membership_store (db->cls, &channel_pub_key,
214 &slave_pub_key, GNUNET_YES,
215 4, 2, 1));
216
217 LOG (GNUNET_ERROR_TYPE_INFO, "membership_test()\n");
218
219 GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
220 &slave_pub_key, 4));
221
222 GNUNET_assert (GNUNET_YES == db->membership_test (db->cls, &channel_pub_key,
223 &slave_pub_key, 2));
224
225 GNUNET_assert (GNUNET_NO == db->membership_test (db->cls, &channel_pub_key,
226 &slave_pub_key, 1));
227
228 /* Store & get messages */
229
230 LOG (GNUNET_ERROR_TYPE_INFO, "MESSAGES\n");
231
232 struct GNUNET_MULTICAST_MessageHeader *msg
233 = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
234 GNUNET_assert (msg != NULL);
235
236 msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
237 msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key));
238
239 uint64_t fragment_id = INT64_MAX - 1;
240 msg->fragment_id = GNUNET_htonll (fragment_id);
241
242 uint64_t message_id = INT64_MAX - 10;
243 msg->message_id = GNUNET_htonll (message_id);
244
245 uint64_t group_generation = INT64_MAX - 3;
246 msg->group_generation = GNUNET_htonll (group_generation);
247
248 msg->hop_counter = htonl (9);
249 msg->fragment_offset = GNUNET_htonll (0);
250 msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT);
251
252 GNUNET_memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key));
253
254 msg->purpose.size = htonl (ntohs (msg->header.size)
255 - sizeof (msg->header)
256 - sizeof (msg->hop_counter)
257 - sizeof (msg->signature));
258 msg->purpose.purpose = htonl (234);
259 GNUNET_assert (GNUNET_OK ==
260 GNUNET_CRYPTO_eddsa_sign (channel_key, &msg->purpose, &msg->signature));
261
262 LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n");
263
264 struct FragmentClosure fcls = { 0 };
265 fcls.n = 0;
266 fcls.msg[0] = msg;
267 fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE;
268
269 GNUNET_assert (
270 GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg,
271 fcls.flags[0]));
272
273 LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id);
274
275 uint64_t ret_frags = 0;
276 GNUNET_assert (
277 GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
278 fragment_id, fragment_id,
279 &ret_frags, fragment_cb, &fcls));
280 GNUNET_assert (fcls.n == 1);
281
282 LOG (GNUNET_ERROR_TYPE_INFO, "message_get_fragment()\n");
283
284 fcls.n = 0;
285 GNUNET_assert (
286 GNUNET_OK == db->message_get_fragment (db->cls, &channel_pub_key,
287 GNUNET_ntohll (msg->message_id),
288 GNUNET_ntohll (msg->fragment_offset),
289 fragment_cb, &fcls));
290 GNUNET_assert (fcls.n == 1);
291
292 LOG (GNUNET_ERROR_TYPE_INFO, "message_add_flags()\n");
293 GNUNET_assert (
294 GNUNET_OK == db->message_add_flags (db->cls, &channel_pub_key,
295 GNUNET_ntohll (msg->message_id),
296 GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED));
297 LOG (GNUNET_ERROR_TYPE_INFO, "fragment_get(%" PRIu64 ")\n", fragment_id);
298
299 fcls.n = 0;
300 fcls.flags[0] |= GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED;
301
302 GNUNET_assert (
303 GNUNET_OK == db->fragment_get (db->cls, &channel_pub_key,
304 fragment_id, fragment_id,
305 &ret_frags, fragment_cb, &fcls));
306
307 GNUNET_assert (fcls.n == 1);
308
309 LOG (GNUNET_ERROR_TYPE_INFO, "fragment_store()\n");
310
311 struct GNUNET_MULTICAST_MessageHeader *msg1
312 = GNUNET_malloc (sizeof (*msg1) + sizeof (channel_pub_key));
313
314 GNUNET_memcpy (msg1, msg, sizeof (*msg1) + sizeof (channel_pub_key));
315
316 msg1->fragment_id = GNUNET_htonll (INT64_MAX);
317 msg1->fragment_offset = GNUNET_htonll (32768);
318
319 fcls.n = 0;
320 fcls.msg[1] = msg1;
321 fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH;
322
323 GNUNET_assert (GNUNET_OK == db->fragment_store (db->cls, &channel_pub_key, msg1,
324 fcls.flags[1]));
325
326 LOG (GNUNET_ERROR_TYPE_INFO, "message_get()\n");
327
328 GNUNET_assert (
329 GNUNET_OK == db->message_get (db->cls, &channel_pub_key,
330 message_id, message_id, 0,
331 &ret_frags, fragment_cb, &fcls));
332 GNUNET_assert (fcls.n == 2 && ret_frags == 2);
333
334 /* Message counters */
335
336 LOG (GNUNET_ERROR_TYPE_INFO, "counters_message_get()\n");
337
338 fragment_id = 0;
339 message_id = 0;
340 group_generation = 0;
341 GNUNET_assert (
342 GNUNET_OK == db->counters_message_get (db->cls, &channel_pub_key,
343 &fragment_id, &message_id,
344 &group_generation)
345 && fragment_id == GNUNET_ntohll (msg1->fragment_id)
346 && message_id == GNUNET_ntohll (msg1->message_id)
347 && group_generation == GNUNET_ntohll (msg1->group_generation));
348
349 /* Modify state */
350
351 LOG (GNUNET_ERROR_TYPE_INFO, "STATE\n");
352
353 LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n");
354
355 message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 1;
356 GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key,
357 message_id, 0));
358
359 GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
360 GNUNET_PSYC_OP_ASSIGN,
361 "_foo",
362 C2ARG("one two three")));
363
364 GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
365 GNUNET_PSYC_OP_ASSIGN,
366 "_foo_bar", slave_key,
367 sizeof (*slave_key)));
368
369 GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
370 message_id));
371
372 LOG (GNUNET_ERROR_TYPE_INFO, "state_get()\n");
373
374 struct StateClosure scls = { 0 };
375 scls.n = 0;
376 scls.value[0] = "one two three";
377 scls.value_size[0] = strlen ("one two three");
378
379 GNUNET_assert (GNUNET_OK == db->state_get (db->cls, &channel_pub_key, "_foo",
380 state_cb, &scls));
381 GNUNET_assert (scls.n == 1);
382
383 LOG (GNUNET_ERROR_TYPE_INFO, "state_get_prefix()\n");
384
385 scls.n = 0;
386 scls.value[1] = slave_key;
387 scls.value_size[1] = sizeof (*slave_key);
388
389 GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key,
390 "_foo", state_cb, &scls));
391 GNUNET_assert (scls.n == 2);
392
393 LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n");
394
395 scls.n = 0;
396 GNUNET_assert (GNUNET_NO == db->state_get_signed (db->cls, &channel_pub_key,
397 state_cb, &scls));
398 GNUNET_assert (scls.n == 0);
399
400 LOG (GNUNET_ERROR_TYPE_INFO, "state_update_signed()\n");
401
402 GNUNET_assert (GNUNET_OK == db->state_update_signed (db->cls,
403 &channel_pub_key));
404
405 LOG (GNUNET_ERROR_TYPE_INFO, "state_get_signed()\n");
406
407 scls.n = 0;
408 GNUNET_assert (GNUNET_YES == db->state_get_signed (db->cls, &channel_pub_key,
409 state_cb, &scls));
410 GNUNET_assert (scls.n == 2);
411
412 /* State counters */
413
414 LOG (GNUNET_ERROR_TYPE_INFO, "counters_state_get()\n");
415
416 uint64_t max_state_msg_id = 0;
417 GNUNET_assert (GNUNET_OK == db->counters_state_get (db->cls, &channel_pub_key,
418 &max_state_msg_id)
419 && max_state_msg_id == message_id);
420
421 /* State sync */
422
423 LOG (GNUNET_ERROR_TYPE_INFO, "state_sync_*()\n");
424
425 scls.n = 0;
426 scls.value[0] = channel_key;
427 scls.value_size[0] = sizeof (*channel_key);
428 scls.value[1] = "three two one";
429 scls.value_size[1] = strlen ("three two one");
430
431 GNUNET_assert (GNUNET_OK == db->state_sync_begin (db->cls, &channel_pub_key));
432
433 GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key,
434 "_sync_bar", scls.value[0],
435 scls.value_size[0]));
436
437 GNUNET_assert (GNUNET_OK == db->state_sync_assign (db->cls, &channel_pub_key,
438 "_sync_foo", scls.value[1],
439 scls.value_size[1]));
440
441 GNUNET_assert (GNUNET_OK == db->state_sync_end (db->cls, &channel_pub_key,
442 max_state_msg_id,
443 INT64_MAX - 5));
444
445 GNUNET_assert (GNUNET_NO == db->state_get_prefix (db->cls, &channel_pub_key,
446 "_foo", state_cb, &scls));
447 GNUNET_assert (scls.n == 0);
448
449 GNUNET_assert (GNUNET_OK == db->state_get_prefix (db->cls, &channel_pub_key,
450 "_sync", state_cb, &scls));
451 GNUNET_assert (scls.n == 2);
452
453 scls.n = 0;
454 GNUNET_assert (GNUNET_OK == db->state_get_signed (db->cls, &channel_pub_key,
455 state_cb, &scls));
456 GNUNET_assert (scls.n == 2);
457
458 /* Modify state after sync */
459
460 LOG (GNUNET_ERROR_TYPE_INFO, "state_modify_*()\n");
461
462 message_id = GNUNET_ntohll (fcls.msg[0]->message_id) + 6;
463 GNUNET_assert (GNUNET_OK == db->state_modify_begin (db->cls, &channel_pub_key,
464 message_id,
465 message_id - max_state_msg_id));
466
467 GNUNET_assert (GNUNET_OK == db->state_modify_op (db->cls, &channel_pub_key,
468 GNUNET_PSYC_OP_ASSIGN,
469 "_sync_foo",
470 C2ARG("five six seven")));
471
472 GNUNET_assert (GNUNET_OK == db->state_modify_end (db->cls, &channel_pub_key,
473 message_id));
474
475 /* Reset state */
476
477 LOG (GNUNET_ERROR_TYPE_INFO, "state_reset()\n");
478
479 scls.n = 0;
480 GNUNET_assert (GNUNET_OK == db->state_reset (db->cls, &channel_pub_key));
481 GNUNET_assert (scls.n == 0);
482
483 ok = 0;
484
485 if (NULL != channel_key)
486 {
487 GNUNET_free (channel_key);
488 channel_key = NULL;
489 }
490 if (NULL != slave_key)
491 {
492 GNUNET_free (slave_key);
493 slave_key = NULL;
494 }
495
496 unload_plugin (db);
497}
498
499
500int
501main (int argc, char *argv[])
502{
503 char cfg_name[128];
504 char *const xargv[] = {
505 "test-plugin-psycstore",
506 "-c", cfg_name,
507 "-L", LOG_LEVEL,
508 NULL
509 };
510 struct GNUNET_GETOPT_CommandLineOption options[] = {
511 GNUNET_GETOPT_OPTION_END
512 };
513 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
514 GNUNET_log_setup ("test-plugin-psycstore", LOG_LEVEL, NULL);
515 plugin_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
516 GNUNET_snprintf (cfg_name, sizeof (cfg_name), "test_plugin_psycstore_%s.conf",
517 plugin_name);
518 GNUNET_PROGRAM_run ((sizeof (xargv) / sizeof (char *)) - 1, xargv,
519 "test-plugin-psycstore", "nohelp", options, &run, NULL);
520
521 if ( (0 != ok) &&
522 (77 != ok) )
523 FPRINTF (stderr, "Missed some testcases: %d\n", ok);
524
525#if ! DEBUG_PSYCSTORE
526 GNUNET_DISK_directory_remove ("/tmp/gnunet-test-plugin-psycstore-sqlite");
527#endif
528
529 return ok;
530}
531
532/* end of test_plugin_psycstore.c */
diff --git a/src/psycstore/test_plugin_psycstore_mysql.conf b/src/psycstore/test_plugin_psycstore_mysql.conf
new file mode 100644
index 0000000..e15b3fd
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_mysql.conf
@@ -0,0 +1,7 @@
1[psycstore-mysql]
2DATABASE = test
3# CONFIG = ~/.my.cnf
4# USER = gnunet
5# PASSWORD =
6# HOST = localhost
7# PORT = 3306
diff --git a/src/psycstore/test_plugin_psycstore_postgres.conf b/src/psycstore/test_plugin_psycstore_postgres.conf
new file mode 100644
index 0000000..4b870dd
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_postgres.conf
@@ -0,0 +1,2 @@
1[psycstore-postgres]
2CONFIG = connect_timeout=10; dbname=template1
diff --git a/src/psycstore/test_plugin_psycstore_sqlite.conf b/src/psycstore/test_plugin_psycstore_sqlite.conf
new file mode 100644
index 0000000..498b1d0
--- /dev/null
+++ b/src/psycstore/test_plugin_psycstore_sqlite.conf
@@ -0,0 +1,2 @@
1[psycstore-sqlite]
2FILENAME = $GNUNET_TMP/gnunet-test-plugin-psycstore-sqlite/sqlite.db
diff --git a/src/psycstore/test_psycstore.c b/src/psycstore/test_psycstore.c
new file mode 100644
index 0000000..ca50904
--- /dev/null
+++ b/src/psycstore/test_psycstore.c
@@ -0,0 +1,586 @@
1/*
2 * This file is part of GNUnet
3 * Copyright (C) 2013 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/**
22 * @file psycstore/test_psycstore.c
23 * @brief Test for the PSYCstore service.
24 * @author Gabor X Toth
25 * @author Christian Grothoff
26 */
27
28#include <inttypes.h>
29
30#include "platform.h"
31#include "gnunet_util_lib.h"
32#include "gnunet_common.h"
33#include "gnunet_testing_lib.h"
34#include "gnunet_psycstore_service.h"
35
36#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
37
38
39/**
40 * Return value from 'main'.
41 */
42static int res;
43
44/**
45 * Handle to PSYCstore service.
46 */
47static struct GNUNET_PSYCSTORE_Handle *h;
48
49/**
50 * Handle to PSYCstore operation.
51 */
52static struct GNUNET_PSYCSTORE_OperationHandle *op;
53
54/**
55 * Handle for task for timeout termination.
56 */
57static struct GNUNET_SCHEDULER_Task *end_badly_task;
58
59static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key;
60static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key;
61
62static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key;
63static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key;
64
65static struct FragmentClosure
66{
67 uint8_t n;
68 uint8_t n_expected;
69 uint64_t flags[16];
70 struct GNUNET_MULTICAST_MessageHeader *msg[16];
71} fcls;
72
73struct StateClosure {
74 size_t n;
75 char *name[16];
76 void *value[16];
77 size_t value_size[16];
78} scls;
79
80static struct GNUNET_PSYC_Modifier modifiers[16];
81
82/**
83 * Clean up all resources used.
84 */
85static void
86cleanup ()
87{
88 if (NULL != op)
89 {
90 GNUNET_PSYCSTORE_operation_cancel (op);
91 op = NULL;
92 }
93 if (NULL != h)
94 {
95 GNUNET_PSYCSTORE_disconnect (h);
96 h = NULL;
97 }
98 if (NULL != channel_key)
99 {
100 GNUNET_free (channel_key);
101 channel_key = NULL;
102 }
103 if (NULL != slave_key)
104 {
105 GNUNET_free (slave_key);
106 slave_key = NULL;
107 }
108 GNUNET_SCHEDULER_shutdown ();
109}
110
111
112/**
113 * Terminate the testcase (failure).
114 *
115 * @param cls NULL
116 */
117static void
118end_badly (void *cls)
119{
120 res = 1;
121 cleanup ();
122}
123
124
125/**
126 * Terminate the testcase (success).
127 *
128 * @param cls NULL
129 */
130static void
131end_normally (void *cls)
132{
133 res = 0;
134 cleanup ();
135}
136
137
138/**
139 * Finish the testcase (successfully).
140 */
141static void
142end ()
143{
144 if (NULL != end_badly_task)
145 {
146 GNUNET_SCHEDULER_cancel (end_badly_task);
147 end_badly_task = NULL;
148 }
149 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
150 &end_normally, NULL);
151}
152
153
154static void
155state_reset_result (void *cls,
156 int64_t result,
157 const char *err_msg,
158 uint16_t err_msg_size)
159{
160 op = NULL;
161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
162 "state_reset_result:\t%d\n",
163 (int) result);
164 GNUNET_assert (GNUNET_OK == result);
165
166 op = GNUNET_PSYCSTORE_state_reset (h, &channel_pub_key,
167 &state_reset_result, cls);
168 GNUNET_PSYCSTORE_operation_cancel (op);
169 op = NULL;
170 end ();
171}
172
173
174static int
175state_result (void *cls,
176 const char *name,
177 const void *value,
178 uint32_t value_size)
179{
180 struct StateClosure *scls = cls;
181 const char *nam = scls->name[scls->n];
182 const void *val = scls->value[scls->n];
183 size_t val_size = scls->value_size[scls->n++];
184
185 if (value_size == val_size
186 && 0 == memcmp (value, val, val_size)
187 && 0 == strcmp (name, nam))
188 {
189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
190 " variable %s matches\n",
191 name);
192 return GNUNET_YES;
193 }
194 else
195 {
196 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197 " variable %s differs\nReceived: %.*s\nExpected: %.*s\n",
198 name, (int) value_size, (char*) value, (int) val_size, (char*) val);
199 GNUNET_assert (0);
200 return GNUNET_SYSERR;
201 }
202}
203
204
205static void
206state_get_prefix_result (void *cls, int64_t result,
207 const char *err_msg, uint16_t err_msg_size)
208{
209 struct StateClosure *scls = cls;
210 op = NULL;
211 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_get_prefix_result:\t%ld\n", (long int) result);
212 GNUNET_assert (GNUNET_OK == result && 2 == scls->n);
213
214 op = GNUNET_PSYCSTORE_state_reset (h, &channel_pub_key,
215 &state_reset_result, cls);
216}
217
218
219static void
220state_get_result (void *cls, int64_t result,
221 const char *err_msg, uint16_t err_msg_size)
222{
223 op = NULL;
224 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_get_result:\t%ld\n", (long int) result);
225 GNUNET_assert (GNUNET_OK == result);
226
227 scls.n = 0;
228
229 scls.name[0] = "_sync_bar";
230 scls.value[0] = "ten eleven twelve";
231 scls.value_size[0] = sizeof ("ten eleven twelve") - 1;
232
233 scls.name[1] = "_sync_foo";
234 scls.value[1] = "three two one";
235 scls.value_size[1] = sizeof ("three two one") - 1;
236
237 op = GNUNET_PSYCSTORE_state_get_prefix (h, &channel_pub_key, "_sync",
238 &state_result,
239 &state_get_prefix_result, &scls);
240}
241
242
243static void
244counters_result (void *cls, int status, uint64_t max_fragment_id,
245 uint64_t max_message_id, uint64_t max_group_generation,
246 uint64_t max_state_message_id)
247{
248 struct FragmentClosure *fcls = cls;
249 int result = 0;
250 op = NULL;
251
252 if (GNUNET_OK == status
253 && max_fragment_id == GNUNET_ntohll (fcls->msg[2]->fragment_id)
254 && max_message_id == GNUNET_ntohll (fcls->msg[2]->message_id)
255 && max_group_generation == GNUNET_ntohll (fcls->msg[2]->group_generation)
256 && max_state_message_id == GNUNET_ntohll (fcls->msg[0]->message_id))
257 result = 1;
258
259 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "counters_get:\t%d\n", result);
260 GNUNET_assert (result == 1);
261
262 scls.n = 0;
263 scls.name[0] = "_sync_bar";
264 scls.value[0] = "ten eleven twelve";
265 scls.value_size[0] = sizeof ("ten eleven twelve") - 1;
266
267 op = GNUNET_PSYCSTORE_state_get (h, &channel_pub_key, "_sync_bar_x_yy_zzz",
268 &state_result, &state_get_result, &scls);
269}
270
271
272static void
273state_modify_result (void *cls, int64_t result,
274 const char *err_msg, uint16_t err_msg_size)
275{
276 op = NULL;
277 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_modify_result:\t%ld\n", (long int) result);
278 GNUNET_assert (GNUNET_OK == result);
279
280 op = GNUNET_PSYCSTORE_counters_get (h, &channel_pub_key,
281 &counters_result, cls);
282}
283
284
285static void
286state_sync_result (void *cls, int64_t result,
287 const char *err_msg, uint16_t err_msg_size)
288{
289 struct FragmentClosure *fcls = cls;
290 op = NULL;
291 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "state_sync_result:\t%ld\n", (long int) result);
292 GNUNET_assert (GNUNET_OK == result);
293
294 op = GNUNET_PSYCSTORE_state_modify (h, &channel_pub_key,
295 GNUNET_ntohll (fcls->msg[0]->message_id),
296 0, state_modify_result, fcls);
297}
298
299
300static int
301fragment_result (void *cls,
302 struct GNUNET_MULTICAST_MessageHeader *msg,
303 enum GNUNET_PSYCSTORE_MessageFlags flags)
304{
305 struct FragmentClosure *fcls = cls;
306 GNUNET_assert (fcls->n < fcls->n_expected);
307 struct GNUNET_MULTICAST_MessageHeader *msg0 = fcls->msg[fcls->n];
308 uint64_t flags0 = fcls->flags[fcls->n++];
309
310 if (flags == flags0 && msg->header.size == msg0->header.size
311 && 0 == memcmp (msg, msg0, ntohs (msg->header.size)))
312 {
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " fragment %" PRIu64 " matches\n",
314 GNUNET_ntohll (msg->fragment_id));
315 return GNUNET_YES;
316 }
317 else
318 {
319 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
320 " fragment differs: expected %" PRIu64 ", got %" PRIu64 "\n",
321 GNUNET_ntohll (msg0->fragment_id),
322 GNUNET_ntohll (msg->fragment_id));
323 GNUNET_assert (0);
324 return GNUNET_SYSERR;
325 }
326}
327
328
329static void
330message_get_latest_result (void *cls, int64_t result,
331 const char *err_msg, uint16_t err_msg_size)
332{
333 struct FragmentClosure *fcls = cls;
334 op = NULL;
335 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get_latest:\t%ld\n", (long int) result);
336 GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
337
338 modifiers[0] = (struct GNUNET_PSYC_Modifier) {
339 .oper = '=',
340 .name = "_sync_foo",
341 .value = "three two one",
342 .value_size = sizeof ("three two one") - 1
343 };
344 modifiers[1] = (struct GNUNET_PSYC_Modifier) {
345 .oper = '=',
346 .name = "_sync_bar",
347 .value = "ten eleven twelve",
348 .value_size = sizeof ("ten eleven twelve") - 1
349 };
350
351 op = GNUNET_PSYCSTORE_state_sync (h, &channel_pub_key,
352 GNUNET_ntohll (fcls->msg[0]->message_id) + 1,
353 GNUNET_ntohll (fcls->msg[0]->message_id) + 2,
354 2, modifiers, state_sync_result, fcls);
355}
356
357
358static void
359message_get_result (void *cls, int64_t result,
360 const char *err_msg, uint16_t err_msg_size)
361{
362 struct FragmentClosure *fcls = cls;
363 op = NULL;
364 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get:\t%ld\n", (long int) result);
365 GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
366
367 fcls->n = 0;
368 fcls->n_expected = 3;
369 op = GNUNET_PSYCSTORE_message_get_latest (h, &channel_pub_key, &slave_pub_key,
370 1, "", &fragment_result,
371 &message_get_latest_result, fcls);
372}
373
374
375static void
376message_get_fragment_result (void *cls, int64_t result,
377 const char *err_msg, uint16_t err_msg_size)
378{
379 struct FragmentClosure *fcls = cls;
380 op = NULL;
381 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "message_get_fragment:\t%ld\n", (long int) result);
382 GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
383
384 fcls->n = 0;
385 fcls->n_expected = 3;
386 uint64_t message_id = GNUNET_ntohll (fcls->msg[0]->message_id);
387 op = GNUNET_PSYCSTORE_message_get (h, &channel_pub_key, &slave_pub_key,
388 message_id, message_id, 0, "",
389 &fragment_result,
390 &message_get_result, fcls);
391}
392
393
394static void
395fragment_get_latest_result (void *cls, int64_t result,
396 const char *err_msg, uint16_t err_msg_size)
397{
398 struct FragmentClosure *fcls = cls;
399 op = NULL;
400 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "fragment_get_latest:\t%ld\n", (long int) result);
401 GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
402
403 fcls->n = 1;
404 fcls->n_expected = 2;
405 op = GNUNET_PSYCSTORE_message_get_fragment (h, &channel_pub_key, &slave_pub_key,
406 GNUNET_ntohll (fcls->msg[1]->message_id),
407 GNUNET_ntohll (fcls->msg[1]->fragment_offset),
408 &fragment_result,
409 &message_get_fragment_result, fcls);
410}
411
412
413static void
414fragment_get_result (void *cls, int64_t result,
415 const char *err_msg, uint16_t err_msg_size)
416{
417 struct FragmentClosure *fcls = cls;
418 op = NULL;
419 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
420 "fragment_get:\t%d\n",
421 (int) result);
422 GNUNET_assert (0 < result && fcls->n == fcls->n_expected);
423
424 fcls->n = 0;
425 fcls->n_expected = 3;
426 op = GNUNET_PSYCSTORE_fragment_get_latest (h, &channel_pub_key,
427 &slave_pub_key, fcls->n_expected,
428 &fragment_result,
429 &fragment_get_latest_result, fcls);
430}
431
432
433static void
434fragment_store_result (void *cls, int64_t result,
435 const char *err_msg, uint16_t err_msg_size)
436{
437 op = NULL;
438 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "fragment_store:\t%ld\n", (long int) result);
439 GNUNET_assert (GNUNET_OK == result);
440
441 if ((intptr_t) cls == GNUNET_YES)
442 { /* last fragment */
443 fcls.n = 0;
444 fcls.n_expected = 1;
445 uint64_t fragment_id = GNUNET_ntohll (fcls.msg[0]->fragment_id);
446 op = GNUNET_PSYCSTORE_fragment_get (h, &channel_pub_key, &slave_pub_key,
447 fragment_id, fragment_id,
448 &fragment_result,
449 &fragment_get_result, &fcls);
450 }
451}
452
453
454static void
455fragment_store ()
456{
457 struct GNUNET_MULTICAST_MessageHeader *msg;
458 fcls.flags[0] = GNUNET_PSYCSTORE_MESSAGE_STATE;
459 fcls.msg[0] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
460 GNUNET_assert (msg != NULL);
461
462 msg->header.type = htons (GNUNET_MESSAGE_TYPE_MULTICAST_MESSAGE);
463 msg->header.size = htons (sizeof (*msg) + sizeof (channel_pub_key));
464
465 msg->hop_counter = htonl (9);
466 msg->fragment_id = GNUNET_htonll (INT64_MAX - 8);
467 msg->fragment_offset = GNUNET_htonll (0);
468 msg->message_id = GNUNET_htonll (INT64_MAX - 10);
469 msg->group_generation = GNUNET_htonll (INT64_MAX - 3);
470 msg->flags = htonl (GNUNET_MULTICAST_MESSAGE_LAST_FRAGMENT);
471
472 GNUNET_memcpy (&msg[1], &channel_pub_key, sizeof (channel_pub_key));
473
474 msg->purpose.size = htonl (ntohs (msg->header.size)
475 - sizeof (msg->header)
476 - sizeof (msg->hop_counter)
477 - sizeof (msg->signature));
478 msg->purpose.purpose = htonl (234);
479 GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_sign (channel_key, &msg->purpose,
480 &msg->signature));
481
482 op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, fcls.flags[0],
483 &fragment_store_result, GNUNET_NO);
484
485 fcls.flags[1] = GNUNET_PSYCSTORE_MESSAGE_STATE_APPLIED;
486 fcls.msg[1] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
487 GNUNET_memcpy (msg, fcls.msg[0], sizeof (*msg) + sizeof (channel_pub_key));
488 msg->fragment_id = GNUNET_htonll (INT64_MAX - 4);
489 msg->fragment_offset = GNUNET_htonll (1024);
490
491 op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, fcls.flags[1],
492 &fragment_store_result, GNUNET_NO);
493
494 fcls.flags[2] = GNUNET_PSYCSTORE_MESSAGE_STATE_HASH;
495 fcls.msg[2] = msg = GNUNET_malloc (sizeof (*msg) + sizeof (channel_pub_key));
496 GNUNET_memcpy (msg, fcls.msg[1], sizeof (*msg) + sizeof (channel_pub_key));
497 msg->fragment_id = GNUNET_htonll (INT64_MAX);
498 msg->fragment_offset = GNUNET_htonll (16384);
499
500 op = GNUNET_PSYCSTORE_fragment_store (h, &channel_pub_key, msg, fcls.flags[2],
501 &fragment_store_result, (void *) GNUNET_YES);
502}
503
504
505static void
506membership_test_result (void *cls, int64_t result,
507 const char *err_msg, uint16_t err_msg_size)
508{
509 op = NULL;
510 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "membership_test:\t%ld\n", (long int) result);
511 GNUNET_assert (GNUNET_OK == result);
512
513 fragment_store ();
514}
515
516
517static void
518membership_store_result (void *cls, int64_t result,
519 const char *err_msg, uint16_t err_msg_size)
520{
521 op = NULL;
522 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "membership_store:\t%ld\n", (long int) result);
523 GNUNET_assert (GNUNET_OK == result);
524
525 op = GNUNET_PSYCSTORE_membership_test (h, &channel_pub_key, &slave_pub_key,
526 INT64_MAX - 10, 2,
527 &membership_test_result, NULL);
528}
529
530
531/**
532 * Main function of the test, run from scheduler.
533 *
534 * @param cls NULL
535 * @param cfg configuration we use (also to connect to PSYCstore service)
536 * @param peer handle to access more of the peer (not used)
537 */
538static void
539#if DEBUG_TEST_PSYCSTORE
540run (void *cls, char *const *args, const char *cfgfile,
541 const struct GNUNET_CONFIGURATION_Handle *cfg)
542#else
543run (void *cls,
544 const struct GNUNET_CONFIGURATION_Handle *cfg,
545 struct GNUNET_TESTING_Peer *peer)
546#endif
547{
548 end_badly_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
549
550 h = GNUNET_PSYCSTORE_connect (cfg);
551 GNUNET_assert (NULL != h);
552
553 channel_key = GNUNET_CRYPTO_eddsa_key_create ();
554 slave_key = GNUNET_CRYPTO_ecdsa_key_create ();
555
556 GNUNET_CRYPTO_eddsa_key_get_public (channel_key, &channel_pub_key);
557 GNUNET_CRYPTO_ecdsa_key_get_public (slave_key, &slave_pub_key);
558
559 op = GNUNET_PSYCSTORE_membership_store (h, &channel_pub_key, &slave_pub_key,
560 GNUNET_YES, INT64_MAX - 5,
561 INT64_MAX - 10, 2,
562 &membership_store_result, NULL);
563}
564
565
566int
567main (int argc, char *argv[])
568{
569 res = 1;
570#if DEBUG_TEST_PSYCSTORE
571 const struct GNUNET_GETOPT_CommandLineOption opts[] = {
572 GNUNET_GETOPT_OPTION_END
573 };
574 if (GNUNET_OK != GNUNET_PROGRAM_run (argc, argv, "test-psycstore",
575 "test-psycstore [options]",
576 opts, &run, NULL))
577 return 1;
578#else
579 if (0 != GNUNET_TESTING_service_run ("test-psycstore", "psycstore",
580 "test_psycstore.conf", &run, NULL))
581 return 1;
582#endif
583 return res;
584}
585
586/* end of test_psycstore.c */
diff --git a/src/psycstore/test_psycstore.conf b/src/psycstore/test_psycstore.conf
new file mode 100644
index 0000000..fa7c2d0
--- /dev/null
+++ b/src/psycstore/test_psycstore.conf
@@ -0,0 +1,8 @@
1[PATHS]
2GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-psycstore/
3
4[psycstore]
5DATABASE = sqlite
6
7[psycstore-sqlite]
8FILENAME = $GNUNET_TEST_HOME/psycstore/sqlite.db