diff options
author | Christian Grothoff <christian@grothoff.org> | 2019-02-11 20:39:36 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2019-02-11 20:39:36 +0100 |
commit | 1f59e703d82b47f3aeaf432045a2633c2841169b (patch) | |
tree | 6af5609b388cf1906a29b5d572bec2dd8fb2ae1c /src/psycstore | |
download | gnunet-secushare-1f59e703d82b47f3aeaf432045a2633c2841169b.tar.gz gnunet-secushare-1f59e703d82b47f3aeaf432045a2633c2841169b.zip |
initial import from gnunet.git
Diffstat (limited to 'src/psycstore')
-rw-r--r-- | src/psycstore/.gitignore | 5 | ||||
-rw-r--r-- | src/psycstore/Makefile.am | 155 | ||||
-rw-r--r-- | src/psycstore/gnunet-service-psycstore.c | 1049 | ||||
-rw-r--r-- | src/psycstore/plugin_psycstore_mysql.c | 1960 | ||||
-rw-r--r-- | src/psycstore/plugin_psycstore_postgres.c | 1530 | ||||
-rw-r--r-- | src/psycstore/plugin_psycstore_sqlite.c | 1948 | ||||
-rw-r--r-- | src/psycstore/psycstore.conf.in | 28 | ||||
-rw-r--r-- | src/psycstore/psycstore.h | 520 | ||||
-rw-r--r-- | src/psycstore/psycstore_api.c | 1285 | ||||
-rw-r--r-- | src/psycstore/test_plugin_psycstore.c | 532 | ||||
-rw-r--r-- | src/psycstore/test_plugin_psycstore_mysql.conf | 7 | ||||
-rw-r--r-- | src/psycstore/test_plugin_psycstore_postgres.conf | 2 | ||||
-rw-r--r-- | src/psycstore/test_plugin_psycstore_sqlite.conf | 2 | ||||
-rw-r--r-- | src/psycstore/test_psycstore.c | 586 | ||||
-rw-r--r-- | src/psycstore/test_psycstore.conf | 8 |
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 @@ | |||
1 | gnunet-service-psycstore | ||
2 | test_plugin_psycstore_mysql | ||
3 | test_plugin_psycstore_sqlite | ||
4 | test_plugin_psycstore_postgres | ||
5 | test_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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | plugindir = $(libdir)/gnunet | ||
5 | |||
6 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
7 | |||
8 | libexecdir= $(pkglibdir)/libexec/ | ||
9 | |||
10 | pkgcfg_DATA = \ | ||
11 | psycstore.conf | ||
12 | |||
13 | |||
14 | if MINGW | ||
15 | WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols | ||
16 | endif | ||
17 | |||
18 | if USE_COVERAGE | ||
19 | AM_CFLAGS = --coverage -O0 | ||
20 | XLIB = -lgcov | ||
21 | endif | ||
22 | |||
23 | if HAVE_MYSQL | ||
24 | MYSQL_PLUGIN = libgnunet_plugin_psycstore_mysql.la | ||
25 | if HAVE_TESTING | ||
26 | MYSQL_TESTS = test_plugin_psycstore_mysql | ||
27 | endif | ||
28 | endif | ||
29 | |||
30 | if HAVE_POSTGRESQL | ||
31 | POSTGRES_PLUGIN = libgnunet_plugin_psycstore_postgres.la | ||
32 | if HAVE_TESTING | ||
33 | POSTGRES_TESTS = test_plugin_psycstore_postgres | ||
34 | endif | ||
35 | endif | ||
36 | |||
37 | if HAVE_SQLITE | ||
38 | SQLITE_PLUGIN = libgnunet_plugin_psycstore_sqlite.la | ||
39 | if HAVE_TESTING | ||
40 | SQLITE_TESTS = test_plugin_psycstore_sqlite | ||
41 | endif | ||
42 | endif | ||
43 | |||
44 | lib_LTLIBRARIES = libgnunetpsycstore.la | ||
45 | |||
46 | libgnunetpsycstore_la_SOURCES = \ | ||
47 | psycstore_api.c \ | ||
48 | psycstore.h | ||
49 | libgnunetpsycstore_la_LIBADD = \ | ||
50 | $(top_builddir)/src/util/libgnunetutil.la \ | ||
51 | $(GN_LIBINTL) $(XLIB) | ||
52 | libgnunetpsycstore_la_LDFLAGS = \ | ||
53 | $(GN_LIB_LDFLAGS) $(WINFLAGS) \ | ||
54 | -version-info 0:0:0 | ||
55 | |||
56 | bin_PROGRAMS = | ||
57 | |||
58 | libexec_PROGRAMS = \ | ||
59 | gnunet-service-psycstore | ||
60 | |||
61 | gnunet_service_psycstore_SOURCES = \ | ||
62 | gnunet-service-psycstore.c | ||
63 | gnunet_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 | |||
69 | plugin_LTLIBRARIES = \ | ||
70 | $(SQLITE_PLUGIN) \ | ||
71 | $(MYSQL_PLUGIN) \ | ||
72 | $(POSTGRES_PLUGIN) | ||
73 | |||
74 | |||
75 | libgnunet_plugin_psycstore_mysql_la_SOURCES = \ | ||
76 | plugin_psycstore_mysql.c | ||
77 | libgnunet_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) | ||
84 | libgnunet_plugin_psycstore_mysql_la_LDFLAGS = \ | ||
85 | $(GN_PLUGIN_LDFLAGS) | ||
86 | |||
87 | libgnunet_plugin_psycstore_postgres_la_SOURCES = \ | ||
88 | plugin_psycstore_postgres.c | ||
89 | libgnunet_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) | ||
95 | libgnunet_plugin_psycstore_postgres_la_LDFLAGS = \ | ||
96 | $(GN_PLUGIN_LDFLAGS) $(POSTGRESQL_LDFLAGS) | ||
97 | libgnunet_plugin_psycstore_postgres_la_CPPFLAGS = \ | ||
98 | $(POSTGRESQL_CPPFLAGS) $(AM_CPPFLAGS) | ||
99 | |||
100 | |||
101 | libgnunet_plugin_psycstore_sqlite_la_SOURCES = \ | ||
102 | plugin_psycstore_sqlite.c | ||
103 | libgnunet_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) | ||
108 | libgnunet_plugin_psycstore_sqlite_la_LDFLAGS = \ | ||
109 | $(GN_PLUGIN_LDFLAGS) | ||
110 | |||
111 | |||
112 | if HAVE_SQLITE | ||
113 | if HAVE_TESTING | ||
114 | check_PROGRAMS = \ | ||
115 | $(SQLITE_TESTS) \ | ||
116 | $(MYSQL_TESTS) \ | ||
117 | $(POSTGRES_TESTS) \ | ||
118 | test_psycstore | ||
119 | endif | ||
120 | endif | ||
121 | |||
122 | if ENABLE_TEST_RUN | ||
123 | AM_TESTS_ENVIRONMENT=export GNUNET_PREFIX=$${GNUNET_PREFIX:-@libdir@};export PATH=$${GNUNET_PREFIX:-@prefix@}/bin:$$PATH;unset XDG_DATA_HOME;unset XDG_CONFIG_HOME; | ||
124 | TESTS = $(check_PROGRAMS) | ||
125 | endif | ||
126 | |||
127 | test_psycstore_SOURCES = \ | ||
128 | test_psycstore.c | ||
129 | test_psycstore_LDADD = \ | ||
130 | libgnunetpsycstore.la \ | ||
131 | $(top_builddir)/src/testing/libgnunettesting.la \ | ||
132 | $(top_builddir)/src/util/libgnunetutil.la | ||
133 | |||
134 | EXTRA_DIST = \ | ||
135 | test_psycstore.conf | ||
136 | |||
137 | |||
138 | test_plugin_psycstore_sqlite_SOURCES = \ | ||
139 | test_plugin_psycstore.c | ||
140 | test_plugin_psycstore_sqlite_LDADD = \ | ||
141 | $(top_builddir)/src/testing/libgnunettesting.la \ | ||
142 | $(top_builddir)/src/util/libgnunetutil.la | ||
143 | |||
144 | test_plugin_psycstore_mysql_SOURCES = \ | ||
145 | test_plugin_psycstore.c | ||
146 | test_plugin_psycstore_mysql_LDADD = \ | ||
147 | $(top_builddir)/src/testing/libgnunettesting.la \ | ||
148 | $(top_builddir)/src/util/libgnunetutil.la | ||
149 | |||
150 | test_plugin_psycstore_postgres_SOURCES = \ | ||
151 | test_plugin_psycstore.c | ||
152 | test_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 | */ | ||
44 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
45 | |||
46 | /** | ||
47 | * Service handle. | ||
48 | */ | ||
49 | static struct GNUNET_SERVICE_Handle *service; | ||
50 | |||
51 | /** | ||
52 | * Handle to the statistics service. | ||
53 | */ | ||
54 | static struct GNUNET_STATISTICS_Handle *stats; | ||
55 | |||
56 | /** | ||
57 | * Database handle | ||
58 | */ | ||
59 | static struct GNUNET_PSYCSTORE_PluginFunctions *db; | ||
60 | |||
61 | /** | ||
62 | * Name of the database plugin | ||
63 | */ | ||
64 | static char *db_lib_name; | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Task run during shutdown. | ||
69 | * | ||
70 | * @param cls unused | ||
71 | */ | ||
72 | static void | ||
73 | shutdown_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 | */ | ||
98 | static void | ||
99 | send_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 | |||
128 | enum | ||
129 | { | ||
130 | MEMBERSHIP_TEST_NOT_NEEDED = 0, | ||
131 | MEMBERSHIP_TEST_NEEDED = 1, | ||
132 | MEMBERSHIP_TEST_DONE = 2, | ||
133 | } MessageMembershipTest; | ||
134 | |||
135 | |||
136 | struct 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 | |||
168 | static int | ||
169 | send_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 | |||
210 | static int | ||
211 | send_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 | |||
235 | static void | ||
236 | handle_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 | |||
256 | static void | ||
257 | handle_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 | |||
279 | static int | ||
280 | check_client_fragment_store (void *cls, | ||
281 | const struct FragmentStoreRequest *req) | ||
282 | { | ||
283 | return GNUNET_OK; | ||
284 | } | ||
285 | |||
286 | |||
287 | static void | ||
288 | handle_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 | |||
318 | static void | ||
319 | handle_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 | |||
375 | static int | ||
376 | check_client_message_get (void *cls, | ||
377 | const struct MessageGetRequest *req) | ||
378 | { | ||
379 | return GNUNET_OK; | ||
380 | } | ||
381 | |||
382 | |||
383 | static void | ||
384 | handle_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 | |||
442 | static void | ||
443 | handle_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 | |||
472 | static void | ||
473 | handle_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 | |||
509 | struct 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 | |||
522 | static void | ||
523 | recv_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 | |||
633 | static int | ||
634 | recv_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 | |||
657 | static void | ||
658 | handle_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 | |||
708 | static int | ||
709 | check_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 */ | ||
717 | static void | ||
718 | handle_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 | |||
769 | static void | ||
770 | handle_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 | |||
786 | static void | ||
787 | handle_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 | |||
802 | static int | ||
803 | check_client_state_get (void *cls, | ||
804 | const struct OperationRequest *req) | ||
805 | { | ||
806 | return GNUNET_OK; | ||
807 | } | ||
808 | |||
809 | |||
810 | static void | ||
811 | handle_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 | |||
859 | static int | ||
860 | check_client_state_get_prefix (void *cls, | ||
861 | const struct OperationRequest *req) | ||
862 | { | ||
863 | return GNUNET_OK; | ||
864 | } | ||
865 | |||
866 | |||
867 | static void | ||
868 | handle_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 | */ | ||
912 | static void * | ||
913 | client_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 | */ | ||
931 | static void | ||
932 | client_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 | */ | ||
946 | static void | ||
947 | run (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 | */ | ||
989 | GNUNET_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 | |||
69 | enum 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 | */ | ||
78 | struct 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 | |||
222 | static void | ||
223 | mysql_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 | */ | ||
239 | static int | ||
240 | mysql_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 | */ | ||
271 | static int | ||
272 | database_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 | */ | ||
543 | static void | ||
544 | database_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 | */ | ||
559 | static int | ||
560 | exec_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 | */ | ||
588 | static int | ||
589 | transaction_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 | */ | ||
605 | static int | ||
606 | transaction_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 | */ | ||
622 | static int | ||
623 | transaction_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 | |||
636 | static int | ||
637 | channel_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 | |||
665 | static int | ||
666 | slave_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 | */ | ||
702 | static int | ||
703 | mysql_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 | */ | ||
765 | static int | ||
766 | membership_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 | */ | ||
829 | static int | ||
830 | fragment_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 | */ | ||
904 | static int | ||
905 | message_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 | |||
946 | static int | ||
947 | fragment_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 | |||
1037 | static int | ||
1038 | fragment_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 | */ | ||
1075 | static int | ||
1076 | fragment_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 | */ | ||
1115 | static int | ||
1116 | fragment_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 | */ | ||
1156 | static int | ||
1157 | message_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 | */ | ||
1202 | static int | ||
1203 | message_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 | */ | ||
1245 | static int | ||
1246 | message_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 | */ | ||
1298 | static int | ||
1299 | counters_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 | */ | ||
1356 | static int | ||
1357 | counters_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 | */ | ||
1409 | static int | ||
1410 | state_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 | |||
1442 | static int | ||
1443 | update_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 | */ | ||
1476 | static int | ||
1477 | state_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 | */ | ||
1525 | static int | ||
1526 | state_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 | */ | ||
1550 | static int | ||
1551 | state_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 | */ | ||
1571 | static int | ||
1572 | state_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 | */ | ||
1587 | static int | ||
1588 | state_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 | */ | ||
1601 | static int | ||
1602 | state_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 | */ | ||
1642 | static int | ||
1643 | state_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 | */ | ||
1657 | static int | ||
1658 | state_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 | */ | ||
1673 | static int | ||
1674 | state_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 | */ | ||
1739 | static int | ||
1740 | state_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 | */ | ||
1820 | static int | ||
1821 | state_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 | */ | ||
1895 | void * | ||
1896 | libgnunet_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 | */ | ||
1947 | void * | ||
1948 | libgnunet_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 | |||
56 | enum 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 | */ | ||
65 | struct 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 | */ | ||
89 | static int | ||
90 | database_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 | */ | ||
368 | static void | ||
369 | database_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 | */ | ||
385 | static int | ||
386 | exec_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 | */ | ||
405 | static int | ||
406 | transaction_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 | */ | ||
424 | static int | ||
425 | transaction_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 | */ | ||
443 | static int | ||
444 | transaction_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 | |||
459 | static int | ||
460 | channel_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 | |||
478 | static int | ||
479 | slave_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 | */ | ||
503 | static int | ||
504 | postgres_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 | */ | ||
558 | static int | ||
559 | membership_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 | */ | ||
595 | static int | ||
596 | fragment_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 | */ | ||
657 | static int | ||
658 | message_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 | */ | ||
683 | struct 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 | */ | ||
706 | void 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 | |||
773 | static int | ||
774 | fragment_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 | */ | ||
803 | static int | ||
804 | fragment_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 | */ | ||
836 | static int | ||
837 | fragment_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 | */ | ||
869 | static int | ||
870 | message_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 | */ | ||
906 | static int | ||
907 | message_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 | */ | ||
939 | static int | ||
940 | message_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 | */ | ||
979 | static int | ||
980 | counters_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 | */ | ||
1017 | static int | ||
1018 | counters_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 | */ | ||
1050 | static int | ||
1051 | state_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 | |||
1070 | static int | ||
1071 | update_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 | */ | ||
1093 | static int | ||
1094 | state_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 | */ | ||
1143 | static int | ||
1144 | state_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 | */ | ||
1168 | static int | ||
1169 | state_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 | */ | ||
1189 | static int | ||
1190 | state_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 | */ | ||
1205 | static int | ||
1206 | state_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 | */ | ||
1219 | static int | ||
1220 | state_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 | */ | ||
1260 | static int | ||
1261 | state_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 | */ | ||
1275 | static int | ||
1276 | state_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 | */ | ||
1291 | static int | ||
1292 | state_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 | */ | ||
1327 | struct 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 | */ | ||
1352 | static void | ||
1353 | get_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 | */ | ||
1389 | static int | ||
1390 | state_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 | */ | ||
1430 | static int | ||
1431 | state_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 | */ | ||
1465 | void * | ||
1466 | libgnunet_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 | */ | ||
1517 | void * | ||
1518 | libgnunet_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 | |||
65 | enum 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 | */ | ||
74 | struct 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 | |||
231 | static void | ||
232 | sql_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 | */ | ||
247 | static int | ||
248 | sql_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 | */ | ||
272 | static int | ||
273 | sql_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 | */ | ||
296 | static int | ||
297 | database_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 | */ | ||
621 | static void | ||
622 | database_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 | */ | ||
648 | static int | ||
649 | exec_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 | */ | ||
677 | static int | ||
678 | transaction_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 | */ | ||
702 | static int | ||
703 | transaction_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 | */ | ||
727 | static int | ||
728 | transaction_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 | |||
748 | static int | ||
749 | channel_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 | |||
777 | static int | ||
778 | slave_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 | */ | ||
815 | static int | ||
816 | sqlite_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 | */ | ||
877 | static int | ||
878 | membership_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 | */ | ||
924 | static int | ||
925 | fragment_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 | */ | ||
997 | static int | ||
998 | message_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 | |||
1038 | static int | ||
1039 | fragment_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 | |||
1066 | static int | ||
1067 | fragment_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 | */ | ||
1106 | static int | ||
1107 | fragment_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 | */ | ||
1151 | static int | ||
1152 | fragment_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 | */ | ||
1194 | static int | ||
1195 | message_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 | */ | ||
1244 | static int | ||
1245 | message_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 | */ | ||
1291 | static int | ||
1292 | message_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 | */ | ||
1344 | static int | ||
1345 | counters_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 | */ | ||
1397 | static int | ||
1398 | counters_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 | */ | ||
1445 | static int | ||
1446 | state_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 | |||
1485 | static int | ||
1486 | update_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 | */ | ||
1515 | static int | ||
1516 | state_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 | */ | ||
1564 | static int | ||
1565 | state_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 | */ | ||
1589 | static int | ||
1590 | state_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 | */ | ||
1610 | static int | ||
1611 | state_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 | */ | ||
1626 | static int | ||
1627 | state_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 | */ | ||
1640 | static int | ||
1641 | state_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 | */ | ||
1681 | static int | ||
1682 | state_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 | */ | ||
1696 | static int | ||
1697 | state_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 | */ | ||
1712 | static int | ||
1713 | state_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 | */ | ||
1763 | static int | ||
1764 | state_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 | */ | ||
1824 | static int | ||
1825 | state_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 | */ | ||
1883 | void * | ||
1884 | libgnunet_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 | */ | ||
1935 | void * | ||
1936 | libgnunet_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] | ||
2 | START_ON_DEMAND = @START_ON_DEMAND@ | ||
3 | BINARY = gnunet-service-psycstore | ||
4 | |||
5 | UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-psycstore.sock | ||
6 | UNIX_MATCH_UID = YES | ||
7 | UNIX_MATCH_GID = YES | ||
8 | |||
9 | @UNIXONLY@PORT = 2111 | ||
10 | HOSTNAME = localhost | ||
11 | ACCEPT_FROM = 127.0.0.1; | ||
12 | ACCEPT_FROM6 = ::1; | ||
13 | |||
14 | DATABASE = sqlite | ||
15 | |||
16 | [psycstore-sqlite] | ||
17 | FILENAME = $GNUNET_DATA_HOME/psycstore/sqlite.db | ||
18 | |||
19 | [psycstore-mysql] | ||
20 | DATABASE = gnunet | ||
21 | CONFIG = ~/.my.cnf | ||
22 | # USER = gnunet | ||
23 | # PASSWORD = | ||
24 | # HOST = localhost | ||
25 | # PORT = 3306 | ||
26 | |||
27 | [psycstore-postgres] | ||
28 | CONFIG = 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 | |||
33 | GNUNET_NETWORK_STRUCT_BEGIN | ||
34 | |||
35 | /** | ||
36 | * Answer from service to client about last operation. | ||
37 | */ | ||
38 | struct 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 | */ | ||
67 | struct 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 | */ | ||
100 | struct 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 | */ | ||
121 | struct 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 | */ | ||
144 | struct 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 | */ | ||
162 | struct 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 | */ | ||
196 | struct 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 | */ | ||
229 | struct 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 | */ | ||
258 | struct 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 | */ | ||
308 | struct 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 | */ | ||
365 | struct 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 | */ | ||
410 | struct 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 | |||
433 | enum 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 | */ | ||
443 | struct 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 | */ | ||
475 | struct 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 | |||
518 | GNUNET_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 | */ | ||
43 | struct 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 | */ | ||
80 | struct 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 | |||
120 | static int | ||
121 | check_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 | |||
136 | static void | ||
137 | handle_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 | |||
165 | static void | ||
166 | handle_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 | |||
197 | static int | ||
198 | check_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 | |||
216 | static void | ||
217 | handle_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 | |||
243 | static int | ||
244 | check_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 | |||
263 | static void | ||
264 | handle_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 | |||
292 | static void | ||
293 | reconnect (void *cls); | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Client disconnected from service. | ||
298 | * | ||
299 | * Reconnect after backoff period.= | ||
300 | */ | ||
301 | static void | ||
302 | disconnected (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 | |||
323 | static void | ||
324 | do_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 | */ | ||
362 | static void | ||
363 | reconnect (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 | */ | ||
378 | struct GNUNET_PSYCSTORE_Handle * | ||
379 | GNUNET_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 | */ | ||
395 | void | ||
396 | GNUNET_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 | */ | ||
419 | static void | ||
420 | message_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 | */ | ||
430 | static struct GNUNET_PSYCSTORE_OperationHandle * | ||
431 | op_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 | */ | ||
461 | static struct GNUNET_PSYCSTORE_OperationHandle * | ||
462 | op_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 | */ | ||
488 | int | ||
489 | GNUNET_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 | */ | ||
535 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
536 | GNUNET_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 | */ | ||
597 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
598 | GNUNET_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 | */ | ||
633 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
634 | GNUNET_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 | */ | ||
684 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
685 | GNUNET_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 | */ | ||
742 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
743 | GNUNET_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 | */ | ||
799 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
800 | GNUNET_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 | */ | ||
866 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
867 | GNUNET_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 | */ | ||
930 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
931 | GNUNET_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 | */ | ||
978 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
979 | GNUNET_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 | */ | ||
1018 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1019 | GNUNET_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 | |||
1038 | struct StateSyncClosure | ||
1039 | { | ||
1040 | GNUNET_PSYCSTORE_ResultCallback result_cb; | ||
1041 | void *cls; | ||
1042 | uint8_t last; | ||
1043 | }; | ||
1044 | |||
1045 | |||
1046 | static void | ||
1047 | state_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 | */ | ||
1079 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1080 | GNUNET_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 | */ | ||
1148 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1149 | GNUNET_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 | */ | ||
1182 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1183 | GNUNET_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 | */ | ||
1220 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1221 | GNUNET_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 | */ | ||
1262 | struct GNUNET_PSYCSTORE_OperationHandle * | ||
1263 | GNUNET_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 | |||
50 | static int ok; | ||
51 | |||
52 | /** | ||
53 | * Name of plugin under test. | ||
54 | */ | ||
55 | static const char *plugin_name; | ||
56 | |||
57 | static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key; | ||
58 | static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key; | ||
59 | |||
60 | static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key; | ||
61 | static 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 | */ | ||
69 | static void | ||
70 | unload_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 | */ | ||
86 | static struct GNUNET_PSYCSTORE_PluginFunctions * | ||
87 | load_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 | |||
107 | struct FragmentClosure | ||
108 | { | ||
109 | uint8_t n; | ||
110 | uint64_t flags[MAX_MSG]; | ||
111 | struct GNUNET_MULTICAST_MessageHeader *msg[MAX_MSG]; | ||
112 | }; | ||
113 | |||
114 | static int | ||
115 | fragment_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 | |||
155 | struct StateClosure { | ||
156 | size_t n; | ||
157 | char *name[16]; | ||
158 | void *value[16]; | ||
159 | size_t value_size[16]; | ||
160 | }; | ||
161 | |||
162 | static int | ||
163 | state_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 | |||
182 | static void | ||
183 | run (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 | |||
500 | int | ||
501 | main (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] | ||
2 | DATABASE = 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] | ||
2 | CONFIG = 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] | ||
2 | FILENAME = $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 | */ | ||
42 | static int res; | ||
43 | |||
44 | /** | ||
45 | * Handle to PSYCstore service. | ||
46 | */ | ||
47 | static struct GNUNET_PSYCSTORE_Handle *h; | ||
48 | |||
49 | /** | ||
50 | * Handle to PSYCstore operation. | ||
51 | */ | ||
52 | static struct GNUNET_PSYCSTORE_OperationHandle *op; | ||
53 | |||
54 | /** | ||
55 | * Handle for task for timeout termination. | ||
56 | */ | ||
57 | static struct GNUNET_SCHEDULER_Task *end_badly_task; | ||
58 | |||
59 | static struct GNUNET_CRYPTO_EddsaPrivateKey *channel_key; | ||
60 | static struct GNUNET_CRYPTO_EcdsaPrivateKey *slave_key; | ||
61 | |||
62 | static struct GNUNET_CRYPTO_EddsaPublicKey channel_pub_key; | ||
63 | static struct GNUNET_CRYPTO_EcdsaPublicKey slave_pub_key; | ||
64 | |||
65 | static 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 | |||
73 | struct StateClosure { | ||
74 | size_t n; | ||
75 | char *name[16]; | ||
76 | void *value[16]; | ||
77 | size_t value_size[16]; | ||
78 | } scls; | ||
79 | |||
80 | static struct GNUNET_PSYC_Modifier modifiers[16]; | ||
81 | |||
82 | /** | ||
83 | * Clean up all resources used. | ||
84 | */ | ||
85 | static void | ||
86 | cleanup () | ||
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 | */ | ||
117 | static void | ||
118 | end_badly (void *cls) | ||
119 | { | ||
120 | res = 1; | ||
121 | cleanup (); | ||
122 | } | ||
123 | |||
124 | |||
125 | /** | ||
126 | * Terminate the testcase (success). | ||
127 | * | ||
128 | * @param cls NULL | ||
129 | */ | ||
130 | static void | ||
131 | end_normally (void *cls) | ||
132 | { | ||
133 | res = 0; | ||
134 | cleanup (); | ||
135 | } | ||
136 | |||
137 | |||
138 | /** | ||
139 | * Finish the testcase (successfully). | ||
140 | */ | ||
141 | static void | ||
142 | end () | ||
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 | |||
154 | static void | ||
155 | state_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 | |||
174 | static int | ||
175 | state_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 | |||
205 | static void | ||
206 | state_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 | |||
219 | static void | ||
220 | state_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 | |||
243 | static void | ||
244 | counters_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 | |||
272 | static void | ||
273 | state_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 | |||
285 | static void | ||
286 | state_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 | |||
300 | static int | ||
301 | fragment_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 | |||
329 | static void | ||
330 | message_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 | |||
358 | static void | ||
359 | message_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 | |||
375 | static void | ||
376 | message_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 | |||
394 | static void | ||
395 | fragment_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 | |||
413 | static void | ||
414 | fragment_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 | |||
433 | static void | ||
434 | fragment_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 | |||
454 | static void | ||
455 | fragment_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 | |||
505 | static void | ||
506 | membership_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 | |||
517 | static void | ||
518 | membership_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 | */ | ||
538 | static void | ||
539 | #if DEBUG_TEST_PSYCSTORE | ||
540 | run (void *cls, char *const *args, const char *cfgfile, | ||
541 | const struct GNUNET_CONFIGURATION_Handle *cfg) | ||
542 | #else | ||
543 | run (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 | |||
566 | int | ||
567 | main (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] | ||
2 | GNUNET_TEST_HOME = $GNUNET_TMP/test-gnunet-psycstore/ | ||
3 | |||
4 | [psycstore] | ||
5 | DATABASE = sqlite | ||
6 | |||
7 | [psycstore-sqlite] | ||
8 | FILENAME = $GNUNET_TEST_HOME/psycstore/sqlite.db | ||