It doesn't match the new design.
};
struct rpki_cache {
- char *tal;
struct cache_node *ht;
time_t startup_ts; /* When we started the last validation */
};
struct path_builder pb;
int error;
- error = pb_init_cache(&pb, NULL, name);
+ error = pb_init_cache(&pb, name);
if (error) {
if (fatal) {
pr_crit("Cannot create path to %s: %s", name,
struct path_builder pb;
int error;
- error = pb_init_cache(&pb, NULL, TMPDIR);
+ error = pb_init_cache(&pb, TMPDIR);
if (error)
return error;
}
static char *
-get_tal_json_filename(struct rpki_cache *cache)
+get_tal_json_filename(void)
{
struct path_builder pb;
- return pb_init_cache(&pb, cache->tal, TAL_METAFILE) ? NULL : pb.string;
+ return pb_init_cache(&pb, TAL_METAFILE) ? NULL : pb.string;
}
static struct cache_node *
-json2node(struct rpki_cache *cache, json_t *json)
+json2node(json_t *json)
{
struct cache_node *node;
char const *type_str;
}
}
- error = uri_create(&node->url, cache->tal, type, NULL, url);
+ error = uri_create(&node->url, type, NULL, url);
if (error) {
pr_op_err("Cannot parse '%s' into a URI.", url);
goto fail;
* without killing itself. It's just a cache of a cache.
*/
- filename = get_tal_json_filename(cache);
+ filename = get_tal_json_filename();
if (filename == NULL)
return;
}
for (n = 0; n < json_array_size(root); n++) {
- node = json2node(cache, json_array_get(root, n));
+ node = json2node(json_array_get(root, n));
if (node != NULL)
add_node(cache, node);
}
}
struct rpki_cache *
-cache_create(char const *tal)
+cache_create(void)
{
struct rpki_cache *cache;
cache = pzalloc(sizeof(struct rpki_cache));
- cache->tal = pstrdup(tal);
cache->startup_ts = time(NULL);
if (cache->startup_ts == (time_t) -1)
pr_crit("time(NULL) returned (time_t) -1.");
if (json == NULL)
return;
- filename = get_tal_json_filename(cache);
+ filename = get_tal_json_filename();
if (filename == NULL)
goto end;
}
static int
-get_url(struct rpki_uri *uri, const char *tal, struct rpki_uri **url)
+get_url(struct rpki_uri *uri, struct rpki_uri **url)
{
char const *guri, *c;
char *gcopy;
return 0;
gcopy2url:
- error = uri_create(url, tal, UT_RPP, NULL, gcopy);
+ error = uri_create(url, UT_RPP, NULL, gcopy);
free(gcopy);
return error;
}
if (changed != NULL)
*changed = false;
- error = get_url(uri, cache->tal, &url);
+ error = get_url(uri, &url);
if (error)
return error;
ARRAYLIST_FOREACH(uris, uri) {
cursor.uri = *uri;
- if (get_url(cursor.uri, cache->tal, &url) != 0)
+ if (get_url(cursor.uri, &url) != 0)
continue;
cursor.node = find_node(cache, url);
uri_refput(url);
struct rpki_uri *cage;
if (uri_get_type(node->url) == UT_NOTIF) {
- if (uri_create_cage(&cage, cache->tal, node->url) == 0) {
+ if (uri_create_cage(&cage, node->url) == 0) {
pr_op_debug("Deleting cage %s.", uri_get_local(cage));
file_rm_rf(uri_get_local(cage));
uri_refput(cage);
return 0;
}
+/*
+ * FIXME this needs to account I'm merging the TAL directories.
+ * It might already work.
+ */
static void
delete_unknown_files(struct rpki_cache *cache)
{
struct path_builder pb;
int error;
- error = pb_init_cache(&pb, cache->tal, TAL_METAFILE);
+ error = pb_init_cache(&pb, TAL_METAFILE);
if (error) {
- pr_op_err("Cannot delete unknown files from %s's cache: %s",
- cache->tal, strerror(error));
+ pr_op_err("Cannot delete unknown files from the cache: %s",
+ strerror(error));
return;
}
if (uri_get_type(node->url) != UT_NOTIF)
continue;
- if (uri_create_cage(&cage, cache->tal, node->url) != 0) {
+ if (uri_create_cage(&cage, node->url) != 0) {
pr_op_err("Cannot generate %s's cage. I'm probably going to end up deleting it from the cache.",
uri_op_get_printable(node->url));
continue;
HASH_ITER(hh, cache->ht, node, tmp)
delete_node(cache, node);
- free(cache->tal);
free(cache);
}
int cache_tmpfile(char **);
-struct rpki_cache *cache_create(char const *);
+struct rpki_cache *cache_create(void);
/* Will destroy the cache object, but not the cache directory itself, obv. */
void cache_destroy(struct rpki_cache *);
}
int
-pb_init_cache(struct path_builder *pb, char const *tal, char const *subdir)
+pb_init_cache(struct path_builder *pb, char const *subdir)
{
int error;
error = pb_append(pb, config_get_local_repository());
if (error)
goto cancel;
- if (tal != NULL) {
- error = pb_append(pb, tal);
- if (error)
- goto cancel;
- }
error = pb_append(pb, subdir);
if (error)
goto cancel;
void __pb_init(struct path_builder *, size_t);
#define pb_init(pb) __pb_init(pb, 0)
-int pb_init_cache(struct path_builder *, char const *, char const *);
+int pb_init_cache(struct path_builder *, char const *);
/*
* The appends are atomic.
str = pstrndup((char const *)ASN1_STRING_get0_data(asn1str),
ASN1_STRING_length(asn1str));
- error = uri_create(uri,
- tal_get_file_name(validation_tal(state_retrieve())),
- type, NULL, str);
+ error = uri_create(uri, type, NULL, str);
free(str);
return error;
return 0;
}
- return uri_create_caged(uri,
- tal_get_file_name(validation_tal(state_retrieve())), notif,
- uri_get_global(*uri));
+ return uri_create_caged(uri, notif, uri_get_global(*uri));
}
static int
build_rpp(struct Manifest *mft, struct rpki_uri *notif,
struct rpki_uri *mft_uri, struct rpp **pp)
{
- char const *tal;
int i;
struct FileAndHash *fah;
struct rpki_uri *uri;
*pp = rpp_create();
- tal = tal_get_file_name(validation_tal(state_retrieve()));
-
for (i = 0; i < mft->fileList.list.count; i++) {
fah = mft->fileList.list.array[i];
- error = uri_create_mft(&uri, tal, notif, mft_uri, &fah->file);
+ error = uri_create_mft(&uri, notif, mft_uri, &fah->file);
/*
* Not handling ENOTRSYNC is fine because the manifest URL
* should have been RSYNC. Something went wrong if an RSYNC URL
int error;
if (str_starts_with(uri, "rsync://"))
- error = uri_create(&new, tal->file_name, UT_TA_RSYNC, NULL, uri);
+ error = uri_create(&new, UT_TA_RSYNC, NULL, uri);
else if (str_starts_with(uri, "https://"))
- error = uri_create(&new, tal->file_name, UT_TA_HTTP, NULL, uri);
+ error = uri_create(&new, UT_TA_HTTP, NULL, uri);
else
return pr_op_err("TAL has non-rsync/HTTPS URI: %s", uri);
if (error)
goto end;
}
- tal->cache = cache_create(file_name);
+ tal->cache = cache_create();
end:
file_free(&file);
static BIO *
rsync2bio_cache(char const *src)
{
- char const *tal;
- struct rpki_uri *uri;
+ struct rpki_uri *uri = NULL;
BIO *bio;
int error;
- tal = strrchr(config_get_tal(), '/');
- tal = (tal != NULL) ? (tal + 1) : config_get_tal();
- uri = NULL;
-
/*
* TODO (#82) maybe rename UT_TA_RSYNC into single rsync.
* If applies and it's going to survive.
*/
- error = uri_create(&uri, tal, UT_TA_RSYNC, NULL, src);
+ error = uri_create(&uri, UT_TA_RSYNC, NULL, src);
if (error) {
pr_op_err("Unparseable rsync URI: %s", strerror(abs(error)));
return NULL;
if (xmlattr == NULL)
return -EINVAL;
- error = uri_create(result,
- tal_get_file_name(validation_tal(state_retrieve())),
- (notif != NULL) ? UT_CAGED : UT_TMP,
- notif, (char const *)xmlattr);
+ error = uri_create(result, (notif != NULL) ? UT_CAGED : UT_TMP, notif,
+ (char const *)xmlattr);
xmlFree(xmlattr);
return error;
}
static void
-delete_rpp(char const *tal, struct rpki_uri *notif)
+delete_rpp(struct rpki_uri *notif)
{
- char *path = uri_get_rrdp_workspace(tal, notif);
+ char *path = uri_get_rrdp_workspace(notif);
pr_val_debug("Snapshot: Deleting cached RPP '%s'.", path);
file_rm_rf(path);
free(path);
static int
handle_snapshot(struct update_notification *notif)
{
- struct validation *state;
struct rpki_uri *uri;
int error;
- state = state_retrieve();
-
- delete_rpp(tal_get_file_name(validation_tal(state)), notif->uri);
+ delete_rpp(notif->uri);
uri = notif->snapshot.uri;
* Maybe stream it instead.
* Same for deltas.
*/
- error = cache_download(validation_cache(state), uri, NULL, NULL);
+ error = cache_download(validation_cache(state_retrieve()), uri, NULL,
+ NULL);
if (error)
goto end;
error = validate_hash(¬if->snapshot);
}
static int
-get_rrdp_workspace(struct path_builder *pb, char const *tal,
- struct rpki_uri *notif)
+get_rrdp_workspace(struct path_builder *pb, struct rpki_uri *notif)
{
int error;
- error = pb_init_cache(pb, tal, "rrdp");
+ error = pb_init_cache(pb, "rrdp");
if (error)
return error;
* Maps "rsync://a.b.c/d/e.cer" into "<local-repository>/rsync/a.b.c/d/e.cer".
*/
static int
-map_simple(struct rpki_uri *uri, char const *tal, char const *subdir)
+map_simple(struct rpki_uri *uri, char const *subdir)
{
struct path_builder pb;
int error;
- error = pb_init_cache(&pb, tal, subdir);
+ error = pb_init_cache(&pb, subdir);
if (error)
return error;
* "<local-repository>/rrdp/<notification-path>/a.b.c/d/e.cer".
*/
static int
-map_caged(struct rpki_uri *uri, char const *tal, struct rpki_uri *notif)
+map_caged(struct rpki_uri *uri, struct rpki_uri *notif)
{
struct path_builder pb;
int error;
- error = get_rrdp_workspace(&pb, tal, notif);
+ error = get_rrdp_workspace(&pb, notif);
if (error)
return error;
}
static int
-autocomplete_local(struct rpki_uri *uri, char const *tal,
- struct rpki_uri *notif)
+autocomplete_local(struct rpki_uri *uri, struct rpki_uri *notif)
{
switch (uri->type) {
case UT_TA_RSYNC:
case UT_RPP:
case UT_MFT:
- return map_simple(uri, tal, "rsync");
+ return map_simple(uri, "rsync");
case UT_TA_HTTP:
- return map_simple(uri, tal, "https");
+ return map_simple(uri, "https");
case UT_NOTIF:
case UT_TMP:
return cache_tmpfile(&uri->local);
case UT_CAGED:
- return map_caged(uri, tal, notif);
+ return map_caged(uri, notif);
case UT_AIA:
case UT_SO:
}
int
-uri_create(struct rpki_uri **result, char const *tal, enum uri_type type,
- struct rpki_uri *notif, char const *guri)
+uri_create(struct rpki_uri **result, enum uri_type type, struct rpki_uri *notif,
+ char const *guri)
{
struct rpki_uri *uri;
int error;
return error;
}
- error = autocomplete_local(uri, tal, notif);
+ error = autocomplete_local(uri, notif);
if (error) {
free(uri->global);
free(uri);
* names. This function will infer the rest of the URL.
*/
int
-uri_create_mft(struct rpki_uri **result, char const *tal,
- struct rpki_uri *notif, struct rpki_uri *mft, IA5String_t *ia5)
+uri_create_mft(struct rpki_uri **result, struct rpki_uri *notif,
+ struct rpki_uri *mft, IA5String_t *ia5)
{
struct rpki_uri *uri;
int error;
return error;
}
- error = autocomplete_local(uri, tal, notif);
+ error = autocomplete_local(uri, notif);
if (error) {
free(uri->global);
free(uri);
}
char *
-uri_get_rrdp_workspace(char const *tal, struct rpki_uri *notif)
+uri_get_rrdp_workspace(struct rpki_uri *notif)
{
struct path_builder pb;
- return (get_rrdp_workspace(&pb, tal, notif) == 0) ? pb.string : NULL;
+ return (get_rrdp_workspace(&pb, notif) == 0) ? pb.string : NULL;
}
DEFINE_ARRAY_LIST_FUNCTIONS(uri_list, struct rpki_uri *, static)
struct rpki_uri;
-int uri_create(struct rpki_uri **, char const *, enum uri_type,
- struct rpki_uri *, char const *);
-int uri_create_mft(struct rpki_uri **, char const *, struct rpki_uri *,
- struct rpki_uri *, IA5String_t *);
+int uri_create(struct rpki_uri **, enum uri_type, struct rpki_uri *,
+ char const *);
+int uri_create_mft(struct rpki_uri **, struct rpki_uri *, struct rpki_uri *,
+ IA5String_t *);
struct rpki_uri *uri_create_cache(char const *);
-#define uri_create_caged(uri, tal, notif, guri) \
- uri_create(uri, tal, UT_CAGED, notif, guri)
-#define uri_create_cage(uri, tal, notif) \
- uri_create_caged(uri, tal, notif, NULL)
+#define uri_create_caged(uri, notif, guri) \
+ uri_create(uri, UT_CAGED, notif, guri)
+#define uri_create_cage(uri, notif) \
+ uri_create_caged(uri, notif, NULL)
struct rpki_uri *uri_refget(struct rpki_uri *);
void uri_refput(struct rpki_uri *);
char const *uri_val_get_printable(struct rpki_uri *);
char const *uri_op_get_printable(struct rpki_uri *);
-char *uri_get_rrdp_workspace(char const *, struct rpki_uri *);
+char *uri_get_rrdp_workspace(struct rpki_uri *);
/* Plural */
/* Mocks */
-#define TAL_FILE "test.tal"
-
static struct rpki_cache *cache;
static bool dl_error; /* Download should return error? */
ck_assert_int_eq(0, system("rm -rf tmp/"));
dl_error = false;
- cache = cache_create(TAL_FILE);
+ cache = cache_create();
ck_assert_ptr_ne(NULL, cache);
SLIST_INIT(&downloaded);
}
rsync_counter = 0;
https_counter = 0;
- ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, NULL, url));
+ ck_assert_int_eq(0, uri_create(&uri, type, NULL, url));
ck_assert_int_eq(expected_error, cache_download(cache, uri, NULL, NULL));
ck_assert_uint_eq(rsync_calls, rsync_counter);
ck_assert_uint_eq(https_calls, https_counter);
ck_abort_msg("Bad protocol: %s", url);
result = pzalloc(sizeof(struct cache_node));
- ck_assert_int_eq(0, uri_create(&result->url, TAL_FILE, type, NULL, url));
+ ck_assert_int_eq(0, uri_create(&result->url, type, NULL, url));
result->attempt.ts = attempt;
result->attempt.result = err;
result->success.happened = succeeded;
NODE("rsync://a.b.c/e/", 0, 1, true),
NODE("rsync://a.b.c/f/", 0, 1, true),
NULL);
- ck_assert_int_eq(0, file_rm_rf("tmp/" TAL_FILE "/rsync/a.b.c/f"));
+ ck_assert_int_eq(0, file_rm_rf("tmp/rsync/a.b.c/f"));
cache_cleanup(cache);
validate_cache(0, NODE("rsync://a.b.c/e/", 0, 1, true), NULL);
NODE("https://a.b.c/e", 0, 1, 1),
NODE("https://a.b.c/f/g/h", 0, 1, 1),
NULL);
- ck_assert_int_eq(0, file_rm_rf("tmp/" TAL_FILE "/https/a.b.c/f/g/h"));
+ ck_assert_int_eq(0, file_rm_rf("tmp/https/a.b.c/f/g/h"));
cache_cleanup(cache);
validate_cache(0, NODE("https://a.b.c/e", 0, 1, 1), NULL);
setup_test();
ck_assert_int_eq(0, system("rm -rf tmp/"));
- ck_assert_int_eq(0, system("mkdir -p tmp/" TAL_FILE));
+ ck_assert_int_eq(0, system("mkdir -p tmp"));
add_node(cache, NODE("rsync://a.b.c/d", 0, 1, 0));
add_node(cache, NODE("rsync://a.b.c/e", 1, 0, 0));
add_node(cache, node("https://a/c", 0, 0, 1, 0, 1));
json = build_tal_json(cache);
- ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_FILE "/" TAL_METAFILE, JSON_COMPACT));
+ ck_assert_int_eq(0, json_dump_file(json, "tmp/" TAL_METAFILE, JSON_COMPACT));
str = json_dumps(json, /* JSON_INDENT(4) */ JSON_COMPACT);
json_decref(json);
type = UT_RPP;
else
ck_abort_msg("Bad protocol: %s", str);
- ck_assert_int_eq(0, uri_create(&uri, TAL_FILE, type, NULL, str));
+ ck_assert_int_eq(0, uri_create(&uri, type, NULL, str));
uris_add(uris, uri);
}
va_end(args);
/* Mocks */
MOCK_ABORT_VOID(cache_setup, void)
-MOCK(cache_create, struct rpki_cache *, NULL, char const *tal)
+MOCK(cache_create, struct rpki_cache *, NULL, void)
MOCK_VOID(cache_destroy, struct rpki_cache *cache)
MOCK_ABORT_INT(cache_download, struct rpki_cache *cache, struct rpki_uri *uri,
bool *changed, struct cachefile_notification ***notif)
MOCK(state_retrieve, struct validation *, NULL, void)
MOCK(validation_tal, struct tal *, NULL, struct validation *state)
-MOCK(tal_get_file_name, char const *, "test.tal", struct tal *tal)
+MOCK(tal_get_file_name, char const *, NULL, struct tal *tal)
MOCK_ABORT_INT(rrdp_update, struct rpki_uri *uri)
/* Tests */
-#define URI_CREATE_HTTP(uri, str) uri_create(&uri, "test.tal", UT_TA_HTTP, NULL, str)
-#define URI_CREATE(uri, type, str) uri_create(&uri, "test.tal", type, NULL, str)
+#define URI_CREATE_HTTP(uri, str) uri_create(&uri, UT_TA_HTTP, NULL, str)
+#define URI_CREATE(uri, type, str) uri_create(&uri, type, NULL, str)
START_TEST(test_constructor)
{
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c"));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/"));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d"));
ck_assert_str_eq("https://a.b.c/d", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c/d", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c/d", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d/e"));
ck_assert_str_eq("https://a.b.c/d/e", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c/d/e", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c/d/e", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/d/.."));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/."));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/././d/././e/./."));
ck_assert_str_eq("https://a.b.c/d/e", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c/d/e", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c/d/e", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE_HTTP(uri, "https://a.b.c/a/b/.././.."));
ck_assert_str_eq("https://a.b.c", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/https/a.b.c", uri_get_local(uri));
+ ck_assert_str_eq("tmp/https/a.b.c", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(-EINVAL, URI_CREATE_HTTP(uri, "https://a.b.c/.."));
ck_assert_int_eq(0, URI_CREATE(uri, UT_RPP, "rsync://a.b.c/d"));
ck_assert_str_eq("rsync://a.b.c/d", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/rsync/a.b.c/d", uri_get_local(uri));
+ ck_assert_str_eq("tmp/rsync/a.b.c/d", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE(uri, UT_TA_RSYNC, "rsync://a.b.c/d.cer"));
ck_assert_str_eq("rsync://a.b.c/d.cer", uri_get_global(uri));
- ck_assert_str_eq("tmp/test.tal/rsync/a.b.c/d.cer", uri_get_local(uri));
+ ck_assert_str_eq("tmp/rsync/a.b.c/d.cer", uri_get_local(uri));
uri_refput(uri);
ck_assert_int_eq(0, URI_CREATE(uri, UT_NOTIF, "https://a.b.c/notification.xml"));
{
struct rpki_uri *uri;
- ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_NOTIF, NULL, "https://a.b.c/d/e.xml"));
- ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, notif, "rsync://x.y.z/v/w.cer"));
- ck_assert_str_eq("tmp/test.tal/rrdp/a.b.c/d/e.xml/x.y.z/v/w.cer", uri_get_local(uri));
+ ck_assert_int_eq(0, uri_create(¬if, UT_NOTIF, NULL, "https://a.b.c/d/e.xml"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, notif, "rsync://x.y.z/v/w.cer"));
+ ck_assert_str_eq("tmp/rrdp/a.b.c/d/e.xml/x.y.z/v/w.cer", uri_get_local(uri));
uri_refput(uri);
uri_refput(notif);
- ck_assert_int_eq(0, uri_create(¬if, "test.tal", UT_NOTIF, NULL, "https://a.b.c"));
- ck_assert_int_eq(0, uri_create(&uri, "test.tal", UT_CAGED, notif, "rsync://w"));
- ck_assert_str_eq("tmp/test.tal/rrdp/a.b.c/w", uri_get_local(uri));
+ ck_assert_int_eq(0, uri_create(¬if, UT_NOTIF, NULL, "https://a.b.c"));
+ ck_assert_int_eq(0, uri_create(&uri, UT_CAGED, notif, "rsync://w"));
+ ck_assert_str_eq("tmp/rrdp/a.b.c/w", uri_get_local(uri));
uri_refput(uri);
uri_refput(notif);
}