diff options
author | Omar Tarabai <tarabai@devegypt.com> | 2014-06-04 16:03:17 +0000 |
---|---|---|
committer | Omar Tarabai <tarabai@devegypt.com> | 2014-06-04 16:03:17 +0000 |
commit | f286cd0b87731774b9448a0cdc82f7c8682c4efe (patch) | |
tree | d84ace0bf67dd4f8a99888548f6ab635206eab4f /src/peerstore | |
parent | 7d18e9d89d2c920f9139bec7f55f01b00b5fd81a (diff) | |
download | gnunet-f286cd0b87731774b9448a0cdc82f7c8682c4efe.tar.gz gnunet-f286cd0b87731774b9448a0cdc82f7c8682c4efe.zip |
peerstore: added 'replace' option and other fixes
Diffstat (limited to 'src/peerstore')
-rw-r--r-- | src/peerstore/gnunet-service-peerstore.c | 39 | ||||
-rw-r--r-- | src/peerstore/peerstore.h | 6 | ||||
-rw-r--r-- | src/peerstore/peerstore_api.c | 3 | ||||
-rw-r--r-- | src/peerstore/peerstore_common.c | 2 | ||||
-rw-r--r-- | src/peerstore/peerstore_common.h | 1 | ||||
-rw-r--r-- | src/peerstore/plugin_peerstore_sqlite.c | 85 | ||||
-rw-r--r-- | src/peerstore/test_peerstore_api.c | 1 |
7 files changed, 102 insertions, 35 deletions
diff --git a/src/peerstore/gnunet-service-peerstore.c b/src/peerstore/gnunet-service-peerstore.c index 5d3ea6bb1..418204850 100644 --- a/src/peerstore/gnunet-service-peerstore.c +++ b/src/peerstore/gnunet-service-peerstore.c | |||
@@ -30,24 +30,6 @@ | |||
30 | #include "peerstore_common.h" | 30 | #include "peerstore_common.h" |
31 | 31 | ||
32 | /** | 32 | /** |
33 | * Context of a PEERSTORE watch | ||
34 | */ | ||
35 | struct WatchContext | ||
36 | { | ||
37 | |||
38 | /** | ||
39 | * Hash of key of watched record | ||
40 | */ | ||
41 | struct GNUNET_HashCode keyhash; | ||
42 | |||
43 | /** | ||
44 | * Client requested the watch | ||
45 | */ | ||
46 | struct GNUNET_SERVER_Client *client; | ||
47 | |||
48 | }; | ||
49 | |||
50 | /** | ||
51 | * Interval for expired records cleanup (in seconds) | 33 | * Interval for expired records cleanup (in seconds) |
52 | */ | 34 | */ |
53 | #define CLEANUP_INTERVAL 300 /* 5mins */ | 35 | #define CLEANUP_INTERVAL 300 /* 5mins */ |
@@ -95,6 +77,7 @@ shutdown_task (void *cls, | |||
95 | } | 77 | } |
96 | GNUNET_SERVER_notification_context_destroy(nc); | 78 | GNUNET_SERVER_notification_context_destroy(nc); |
97 | GNUNET_CONTAINER_multihashmap_destroy(watchers); | 79 | GNUNET_CONTAINER_multihashmap_destroy(watchers); |
80 | watchers = NULL; | ||
98 | GNUNET_SCHEDULER_shutdown(); | 81 | GNUNET_SCHEDULER_shutdown(); |
99 | } | 82 | } |
100 | 83 | ||
@@ -123,7 +106,7 @@ cleanup_expired_records(void *cls, | |||
123 | * @param cls closuer, a 'struct GNUNET_PEERSTORE_Record *' | 106 | * @param cls closuer, a 'struct GNUNET_PEERSTORE_Record *' |
124 | * @param key hash of record key | 107 | * @param key hash of record key |
125 | * @param value the watcher client, a 'struct GNUNET_SERVER_Client *' | 108 | * @param value the watcher client, a 'struct GNUNET_SERVER_Client *' |
126 | * @return #GNUNET_YES to continue iterating | 109 | * @return #GNUNET_OK to continue iterating |
127 | */ | 110 | */ |
128 | int client_disconnect_it(void *cls, | 111 | int client_disconnect_it(void *cls, |
129 | const struct GNUNET_HashCode *key, | 112 | const struct GNUNET_HashCode *key, |
@@ -131,7 +114,7 @@ int client_disconnect_it(void *cls, | |||
131 | { | 114 | { |
132 | if(cls == value) | 115 | if(cls == value) |
133 | GNUNET_CONTAINER_multihashmap_remove(watchers, key, value); | 116 | GNUNET_CONTAINER_multihashmap_remove(watchers, key, value); |
134 | return GNUNET_YES; | 117 | return GNUNET_OK; |
135 | } | 118 | } |
136 | 119 | ||
137 | /** | 120 | /** |
@@ -146,8 +129,9 @@ handle_client_disconnect (void *cls, | |||
146 | * client) | 129 | * client) |
147 | { | 130 | { |
148 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "A client was disconnected, cleaning up.\n"); | 131 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "A client was disconnected, cleaning up.\n"); |
149 | GNUNET_CONTAINER_multihashmap_iterate(watchers, | 132 | if(NULL != watchers) |
150 | &client_disconnect_it, client); | 133 | GNUNET_CONTAINER_multihashmap_iterate(watchers, |
134 | &client_disconnect_it, client); | ||
151 | } | 135 | } |
152 | 136 | ||
153 | /** | 137 | /** |
@@ -196,12 +180,6 @@ int watch_notifier_it(void *cls, | |||
196 | struct StoreRecordMessage *srm; | 180 | struct StoreRecordMessage *srm; |
197 | 181 | ||
198 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n"); | 182 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Found a watcher to update.\n"); |
199 | if(NULL == client) | ||
200 | { | ||
201 | GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Removing a dead client.\n"); | ||
202 | GNUNET_CONTAINER_multihashmap_remove(watchers, key, client); | ||
203 | return GNUNET_YES; | ||
204 | } | ||
205 | srm = PEERSTORE_create_record_message(record->sub_system, | 183 | srm = PEERSTORE_create_record_message(record->sub_system, |
206 | record->peer, | 184 | record->peer, |
207 | record->key, | 185 | record->key, |
@@ -335,6 +313,7 @@ void handle_store (void *cls, | |||
335 | const struct GNUNET_MessageHeader *message) | 313 | const struct GNUNET_MessageHeader *message) |
336 | { | 314 | { |
337 | struct GNUNET_PEERSTORE_Record *record; | 315 | struct GNUNET_PEERSTORE_Record *record; |
316 | struct StoreRecordMessage *srm; | ||
338 | 317 | ||
339 | record = PEERSTORE_parse_record_message(message); | 318 | record = PEERSTORE_parse_record_message(message); |
340 | if(NULL == record) | 319 | if(NULL == record) |
@@ -343,6 +322,7 @@ void handle_store (void *cls, | |||
343 | GNUNET_SERVER_receive_done(client, GNUNET_SYSERR); | 322 | GNUNET_SERVER_receive_done(client, GNUNET_SYSERR); |
344 | return; | 323 | return; |
345 | } | 324 | } |
325 | srm = (struct StoreRecordMessage *)message; | ||
346 | if(NULL == record->sub_system | 326 | if(NULL == record->sub_system |
347 | || NULL == record->peer | 327 | || NULL == record->peer |
348 | || NULL == record->key) | 328 | || NULL == record->key) |
@@ -363,7 +343,8 @@ void handle_store (void *cls, | |||
363 | record->key, | 343 | record->key, |
364 | record->value, | 344 | record->value, |
365 | record->value_size, | 345 | record->value_size, |
366 | *record->expiry)) | 346 | *record->expiry, |
347 | srm->options)) | ||
367 | { | 348 | { |
368 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to store requested value, sqlite database error."); | 349 | GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Failed to store requested value, sqlite database error."); |
369 | PEERSTORE_destroy_record(record); | 350 | PEERSTORE_destroy_record(record); |
diff --git a/src/peerstore/peerstore.h b/src/peerstore/peerstore.h index 5adf9f363..5757f784e 100644 --- a/src/peerstore/peerstore.h +++ b/src/peerstore/peerstore.h | |||
@@ -75,6 +75,12 @@ struct StoreRecordMessage | |||
75 | */ | 75 | */ |
76 | struct GNUNET_TIME_Absolute expiry; | 76 | struct GNUNET_TIME_Absolute expiry; |
77 | 77 | ||
78 | /** | ||
79 | * Options, needed only in case of a | ||
80 | * store operation | ||
81 | */ | ||
82 | enum GNUNET_PEERSTORE_StoreOption options; | ||
83 | |||
78 | }; | 84 | }; |
79 | 85 | ||
80 | /** | 86 | /** |
diff --git a/src/peerstore/peerstore_api.c b/src/peerstore/peerstore_api.c index 238c7be19..8797d7818 100644 --- a/src/peerstore/peerstore_api.c +++ b/src/peerstore/peerstore_api.c | |||
@@ -419,6 +419,7 @@ GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h, | |||
419 | const void *value, | 419 | const void *value, |
420 | size_t size, | 420 | size_t size, |
421 | struct GNUNET_TIME_Absolute expiry, | 421 | struct GNUNET_TIME_Absolute expiry, |
422 | enum GNUNET_PEERSTORE_StoreOption options, | ||
422 | GNUNET_PEERSTORE_Continuation cont, | 423 | GNUNET_PEERSTORE_Continuation cont, |
423 | void *cont_cls) | 424 | void *cont_cls) |
424 | { | 425 | { |
@@ -434,6 +435,7 @@ GNUNET_PEERSTORE_store (struct GNUNET_PEERSTORE_Handle *h, | |||
434 | value, | 435 | value, |
435 | size, | 436 | size, |
436 | &expiry, | 437 | &expiry, |
438 | options, | ||
437 | GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); | 439 | GNUNET_MESSAGE_TYPE_PEERSTORE_STORE); |
438 | sc = GNUNET_new(struct GNUNET_PEERSTORE_StoreContext); | 440 | sc = GNUNET_new(struct GNUNET_PEERSTORE_StoreContext); |
439 | sc->ev = ev; | 441 | sc->ev = ev; |
@@ -595,6 +597,7 @@ GNUNET_PEERSTORE_iterate (struct GNUNET_PEERSTORE_Handle *h, | |||
595 | NULL, | 597 | NULL, |
596 | 0, | 598 | 0, |
597 | NULL, | 599 | NULL, |
600 | 0, | ||
598 | GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); | 601 | GNUNET_MESSAGE_TYPE_PEERSTORE_ITERATE); |
599 | ic = GNUNET_new(struct GNUNET_PEERSTORE_IterateContext); | 602 | ic = GNUNET_new(struct GNUNET_PEERSTORE_IterateContext); |
600 | ic->callback = callback; | 603 | ic->callback = callback; |
diff --git a/src/peerstore/peerstore_common.c b/src/peerstore/peerstore_common.c index eeaa3144c..48c04010a 100644 --- a/src/peerstore/peerstore_common.c +++ b/src/peerstore/peerstore_common.c | |||
@@ -137,6 +137,7 @@ PEERSTORE_create_record_mq_envelope(const char *sub_system, | |||
137 | const void *value, | 137 | const void *value, |
138 | size_t value_size, | 138 | size_t value_size, |
139 | struct GNUNET_TIME_Absolute *expiry, | 139 | struct GNUNET_TIME_Absolute *expiry, |
140 | enum GNUNET_PEERSTORE_StoreOption options, | ||
140 | uint16_t msg_type) | 141 | uint16_t msg_type) |
141 | { | 142 | { |
142 | struct StoreRecordMessage *srm; | 143 | struct StoreRecordMessage *srm; |
@@ -168,6 +169,7 @@ PEERSTORE_create_record_mq_envelope(const char *sub_system, | |||
168 | } | 169 | } |
169 | srm->sub_system_size = htons(ss_size); | 170 | srm->sub_system_size = htons(ss_size); |
170 | srm->value_size = htons(value_size); | 171 | srm->value_size = htons(value_size); |
172 | srm->options = options; | ||
171 | dummy = &srm[1]; | 173 | dummy = &srm[1]; |
172 | memcpy(dummy, sub_system, ss_size); | 174 | memcpy(dummy, sub_system, ss_size); |
173 | dummy += ss_size; | 175 | dummy += ss_size; |
diff --git a/src/peerstore/peerstore_common.h b/src/peerstore/peerstore_common.h index dae2b437b..297eb9fc0 100644 --- a/src/peerstore/peerstore_common.h +++ b/src/peerstore/peerstore_common.h | |||
@@ -76,6 +76,7 @@ PEERSTORE_create_record_mq_envelope(const char *sub_system, | |||
76 | const void *value, | 76 | const void *value, |
77 | size_t value_size, | 77 | size_t value_size, |
78 | struct GNUNET_TIME_Absolute *expiry, | 78 | struct GNUNET_TIME_Absolute *expiry, |
79 | enum GNUNET_PEERSTORE_StoreOption options, | ||
79 | uint16_t msg_type); | 80 | uint16_t msg_type); |
80 | 81 | ||
81 | /** | 82 | /** |
diff --git a/src/peerstore/plugin_peerstore_sqlite.c b/src/peerstore/plugin_peerstore_sqlite.c index 8705ce188..38e72df51 100644 --- a/src/peerstore/plugin_peerstore_sqlite.c +++ b/src/peerstore/plugin_peerstore_sqlite.c | |||
@@ -100,13 +100,58 @@ struct Plugin | |||
100 | sqlite3_stmt *select_peerstoredata_by_all; | 100 | sqlite3_stmt *select_peerstoredata_by_all; |
101 | 101 | ||
102 | /** | 102 | /** |
103 | * Precompiled SQL for deleting expired records from peerstoredata | 103 | * Precompiled SQL for deleting expired |
104 | * records from peerstoredata | ||
104 | */ | 105 | */ |
105 | sqlite3_stmt *expire_peerstoredata; | 106 | sqlite3_stmt *expire_peerstoredata; |
106 | 107 | ||
108 | /** | ||
109 | * Precompiled SQL for deleting records | ||
110 | * with given key | ||
111 | */ | ||
112 | sqlite3_stmt *delete_peerstoredata; | ||
113 | |||
107 | }; | 114 | }; |
108 | 115 | ||
109 | /** | 116 | /** |
117 | * Delete records with the given key | ||
118 | * | ||
119 | * @param cls closure (internal context for the plugin) | ||
120 | * @param sub_system name of sub system | ||
121 | * @param peer Peer identity (can be NULL) | ||
122 | * @param key entry key string (can be NULL) | ||
123 | * @return number of deleted records | ||
124 | */ | ||
125 | static int | ||
126 | peerstore_sqlite_delete_records(void *cls, | ||
127 | const char *sub_system, | ||
128 | const struct GNUNET_PeerIdentity *peer, | ||
129 | const char *key) | ||
130 | { | ||
131 | struct Plugin *plugin = cls; | ||
132 | sqlite3_stmt *stmt = plugin->delete_peerstoredata; | ||
133 | |||
134 | if((SQLITE_OK != sqlite3_bind_text(stmt, 1, sub_system, strlen(sub_system) + 1, SQLITE_STATIC)) | ||
135 | || (SQLITE_OK != sqlite3_bind_blob(stmt, 2, peer, sizeof(struct GNUNET_PeerIdentity), SQLITE_STATIC)) | ||
136 | || (SQLITE_OK != sqlite3_bind_text(stmt, 3, key, strlen(key) + 1, SQLITE_STATIC))) | ||
137 | { | ||
138 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind"); | ||
139 | } | ||
140 | else if (SQLITE_DONE != sqlite3_step (stmt)) | ||
141 | { | ||
142 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
143 | "sqlite3_step"); | ||
144 | } | ||
145 | if (SQLITE_OK != sqlite3_reset (stmt)) | ||
146 | { | ||
147 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | ||
148 | "sqlite3_reset"); | ||
149 | return 0; | ||
150 | } | ||
151 | return sqlite3_changes(plugin->dbh); | ||
152 | } | ||
153 | |||
154 | /** | ||
110 | * Delete expired records (expiry < now) | 155 | * Delete expired records (expiry < now) |
111 | * | 156 | * |
112 | * @param cls closure (internal context for the plugin) | 157 | * @param cls closure (internal context for the plugin) |
@@ -120,7 +165,7 @@ peerstore_sqlite_expire_records(void *cls, | |||
120 | struct Plugin *plugin = cls; | 165 | struct Plugin *plugin = cls; |
121 | sqlite3_stmt *stmt = plugin->expire_peerstoredata; | 166 | sqlite3_stmt *stmt = plugin->expire_peerstoredata; |
122 | 167 | ||
123 | if(SQLITE_OK != sqlite3_bind_int64(stmt, 1, (sqlite3_int64)now.abs_value_us)) | 168 | if(SQLITE_OK != sqlite3_bind_int64(stmt, 1, (sqlite3_uint64)now.abs_value_us)) |
124 | { | 169 | { |
125 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind"); | 170 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "sqlite3_bind"); |
126 | } | 171 | } |
@@ -254,16 +299,21 @@ peerstore_sqlite_store_record(void *cls, | |||
254 | const char *key, | 299 | const char *key, |
255 | const void *value, | 300 | const void *value, |
256 | size_t size, | 301 | size_t size, |
257 | struct GNUNET_TIME_Absolute expiry) | 302 | struct GNUNET_TIME_Absolute expiry, |
303 | enum GNUNET_PEERSTORE_StoreOption options) | ||
258 | { | 304 | { |
259 | struct Plugin *plugin = cls; | 305 | struct Plugin *plugin = cls; |
260 | sqlite3_stmt *stmt = plugin->insert_peerstoredata; | 306 | sqlite3_stmt *stmt = plugin->insert_peerstoredata; |
261 | 307 | ||
308 | if(GNUNET_PEERSTORE_STOREOPTION_REPLACE == options) | ||
309 | { | ||
310 | peerstore_sqlite_delete_records(cls, sub_system, peer, key); | ||
311 | } | ||
262 | if(SQLITE_OK != sqlite3_bind_text(stmt, 1, sub_system, strlen(sub_system) + 1, SQLITE_STATIC) | 312 | if(SQLITE_OK != sqlite3_bind_text(stmt, 1, sub_system, strlen(sub_system) + 1, SQLITE_STATIC) |
263 | || SQLITE_OK != sqlite3_bind_blob(stmt, 2, peer, sizeof(struct GNUNET_PeerIdentity), SQLITE_STATIC) | 313 | || SQLITE_OK != sqlite3_bind_blob(stmt, 2, peer, sizeof(struct GNUNET_PeerIdentity), SQLITE_STATIC) |
264 | || SQLITE_OK != sqlite3_bind_text(stmt, 3, key, strlen(key) + 1, SQLITE_STATIC) | 314 | || SQLITE_OK != sqlite3_bind_text(stmt, 3, key, strlen(key) + 1, SQLITE_STATIC) |
265 | || SQLITE_OK != sqlite3_bind_blob(stmt, 4, value, size, SQLITE_STATIC) | 315 | || SQLITE_OK != sqlite3_bind_blob(stmt, 4, value, size, SQLITE_STATIC) |
266 | || SQLITE_OK != sqlite3_bind_int64(stmt, 5, (sqlite3_int64)expiry.abs_value_us)) | 316 | || SQLITE_OK != sqlite3_bind_int64(stmt, 5, (sqlite3_uint64)expiry.abs_value_us)) |
267 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | 317 | LOG_SQLITE (plugin, GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, |
268 | "sqlite3_bind"); | 318 | "sqlite3_bind"); |
269 | else if (SQLITE_DONE != sqlite3_step (stmt)) | 319 | else if (SQLITE_DONE != sqlite3_step (stmt)) |
@@ -330,6 +380,21 @@ sql_prepare (sqlite3 *dbh, const char *sql, sqlite3_stmt **stmt) | |||
330 | } | 380 | } |
331 | 381 | ||
332 | /** | 382 | /** |
383 | * sqlite3 custom function for comparison of uint64_t values | ||
384 | * since it is not supported by default | ||
385 | */ | ||
386 | void sqlite3_lessthan(sqlite3_context* ctx, int dummy, | ||
387 | sqlite3_value** values) | ||
388 | { | ||
389 | uint64_t v1; | ||
390 | uint64_t v2; | ||
391 | |||
392 | v1 = (uint64_t)sqlite3_value_int64(values[0]); | ||
393 | v2 = (uint64_t)sqlite3_value_int64(values[1]); | ||
394 | sqlite3_result_int(ctx, v1 < v2); | ||
395 | } | ||
396 | |||
397 | /** | ||
333 | * Initialize the database connections and associated | 398 | * Initialize the database connections and associated |
334 | * data structures (create tables and indices | 399 | * data structures (create tables and indices |
335 | * as needed as well). | 400 | * as needed as well). |
@@ -389,9 +454,11 @@ database_setup (struct Plugin *plugin) | |||
389 | " peer_id BLOB NOT NULL,\n" | 454 | " peer_id BLOB NOT NULL,\n" |
390 | " key TEXT NOT NULL,\n" | 455 | " key TEXT NOT NULL,\n" |
391 | " value BLOB NULL,\n" | 456 | " value BLOB NULL,\n" |
392 | " expiry INTEGER NOT NULL" | 457 | " expiry sqlite3_uint64 NOT NULL" |
393 | ");"); | 458 | ");"); |
394 | 459 | ||
460 | sqlite3_create_function(plugin->dbh, "UINT64_LT", 2, SQLITE_UTF8, NULL, &sqlite3_lessthan, NULL, NULL); | ||
461 | |||
395 | /* Prepare statements */ | 462 | /* Prepare statements */ |
396 | 463 | ||
397 | sql_prepare (plugin->dbh, | 464 | sql_prepare (plugin->dbh, |
@@ -419,8 +486,14 @@ database_setup (struct Plugin *plugin) | |||
419 | &plugin->select_peerstoredata_by_all); | 486 | &plugin->select_peerstoredata_by_all); |
420 | sql_prepare(plugin->dbh, | 487 | sql_prepare(plugin->dbh, |
421 | "DELETE FROM peerstoredata" | 488 | "DELETE FROM peerstoredata" |
422 | " WHERE expiry < ?", | 489 | " WHERE UINT64_LT(expiry, ?)", |
423 | &plugin->expire_peerstoredata); | 490 | &plugin->expire_peerstoredata); |
491 | sql_prepare(plugin->dbh, | ||
492 | "DELETE FROM peerstoredata" | ||
493 | " WHERE sub_system = ?" | ||
494 | " AND peer_id = ?" | ||
495 | " AND key = ?", | ||
496 | &plugin->delete_peerstoredata); | ||
424 | 497 | ||
425 | return GNUNET_OK; | 498 | return GNUNET_OK; |
426 | } | 499 | } |
diff --git a/src/peerstore/test_peerstore_api.c b/src/peerstore/test_peerstore_api.c index 968467231..e4f6225c6 100644 --- a/src/peerstore/test_peerstore_api.c +++ b/src/peerstore/test_peerstore_api.c | |||
@@ -125,6 +125,7 @@ run (void *cls, | |||
125 | val, | 125 | val, |
126 | val_size, | 126 | val_size, |
127 | expiry, | 127 | expiry, |
128 | GNUNET_PEERSTORE_STOREOPTION_MULTIPLE, | ||
128 | &store_cont, | 129 | &store_cont, |
129 | NULL); | 130 | NULL); |
130 | } | 131 | } |