From: Christian Grothoff Date: Thu, 17 Mar 2016 19:24:35 +0000 (+0000) Subject: adding library for basic JSON conversions X-Git-Tag: initial-import-from-subversion-38251~1010 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=a5361231e36224607d8c2c1376757c0b99f34f59;p=oweals%2Fgnunet.git adding library for basic JSON conversions --- diff --git a/src/Makefile.am b/src/Makefile.am index edfbb6922..47d6896cb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,14 +21,18 @@ if HAVE_EXPERIMENTAL psyc \ rps \ social \ - $(CONSENSUS) \ + $(CONSENSUS) \ $(SECRETSHARING) \ $(SENSOR) \ $(SENSORDASHBOARD) endif if HAVE_REST - EXP_DIR += identity-provider + PROVIDER_DIR = identity-provider +endif + +if HAVE_JSON + JSON_DIR = json endif if BUILD_PULSE_HELPERS @@ -60,7 +64,8 @@ else SUBDIRS = \ include $(INTLEMU_SUBDIRS) \ util \ - $(REST_DIR) \ + $(JSON_DIR) \ + $(REST_DIR) \ hello \ tun \ block \ @@ -104,6 +109,7 @@ SUBDIRS = \ exit \ pt \ integration-tests \ - $(EXP_DIR) + $(EXP_DIR) \ + $(PROVIDER_DIR) endif diff --git a/src/include/Makefile.am b/src/include/Makefile.am index 626089dbc..82fa9006b 100644 --- a/src/include/Makefile.am +++ b/src/include/Makefile.am @@ -65,6 +65,7 @@ gnunetinclude_HEADERS = \ gnunet_hello_lib.h \ gnunet_helper_lib.h \ gnunet_identity_service.h \ + gnunet_json_lib.h \ gnunet_load_lib.h \ gnunet_cadet_service.h \ gnunet_microphone_lib.h \ diff --git a/src/include/gnunet_json_lib.h b/src/include/gnunet_json_lib.h new file mode 100644 index 000000000..e39938858 --- /dev/null +++ b/src/include/gnunet_json_lib.h @@ -0,0 +1,354 @@ +/* + This file is part of GNUnet + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNUnet; see the file COPYING. If not, If not, see +*/ +/** + * @file gnunet_json_lib.h + * @brief functions to parse JSON objects into GNUnet objects + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include + + +/* ****************** Generic parser interface ******************* */ + +/** + * @brief Entry in parser specification for #GNUNET_JSON_parse(). + */ +struct GNUNET_JSON_Specification; + + +/** + * Function called to parse JSON argument. + * + * @param cls closure + * @param root JSON to parse + * @param spec our specification entry with further details + * @return #GNUNET_SYSERR on error, + * #GNUNET_OK on success + */ +typedef int +(*GNUNET_JSON_Parser)(void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec); + + +/** + * Function called to clean up data from earlier parsing. + * + * @param cls closure + * @param spec our specification entry with data to clean. + */ +typedef void +(*GNUNET_JSON_Cleaner)(void *cls, + struct GNUNET_JSON_Specification *spec); + + +/** + * @brief Entry in parser specification for #GNUNET_JSON_parse(). + */ +struct GNUNET_JSON_Specification +{ + /** + * Function for how to parse this type of entry. + */ + GNUNET_JSON_Parser parser; + + /** + * Function for how to clean up this type of entry. + */ + GNUNET_JSON_Cleaner cleaner; + + /** + * Closure for @e parser and @e cleaner. + */ + void *cls; + + /** + * Name of the field to parse, use NULL to get the JSON + * of the main object instead of the JSON of an individual field. + */ + const char *field; + + /** + * Pointer, details specific to the @e parser. + */ + void *ptr; + + /** + * Number of bytes available in @e ptr. + */ + size_t ptr_size; + + /** + * Where should we store the final size of @e ptr. + */ + size_t *size_ptr; + +}; + + +/** + * Navigate and parse data in a JSON tree. Tries to parse the @a root + * to find all of the values given in the @a spec. If one of the + * entries in @a spec cannot be found or parsed, the name of the JSON + * field is returned in @a error_json_name, and the offset of the + * entry in @a spec is returned in @a error_line. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @param[out] error_json_name which JSON field was problematic + * @param[out] which index into @a spec did we encounter an error + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +GNUNET_JSON_parse (const json_t *root, + struct GNUNET_JSON_Specification *spec, + const char **error_json_name, + unsigned int *error_line); + + +/** + * Frees all elements allocated during a #GNUNET_JSON_parse() + * operation. + * + * @param spec specification of the parse operation + */ +void +GNUNET_JSON_parse_free (struct GNUNET_JSON_Specification *spec); + + + +/* ****************** Canonical parser specifications ******************* */ + + +/** + * End of a parser specification. + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_end (void); + + +/** + * Variable size object (in network byte order, encoded using Crockford + * Base32hex encoding). + * + * @param name name of the JSON field + * @param[out] obj pointer where to write the data, must have @a size bytes + * @param size number of bytes expected in @a obj + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_fixed (const char *name, + void *obj, + size_t size); + + +/** + * Fixed size object (in network byte order, encoded using Crockford + * Base32hex encoding). + * + * @param name name of the JSON field + * @param obj pointer where to write the data (type of `*obj` will determine size) + */ +#define GNUNET_JSON_spec_fixed_auto(name,obj) GNUNET_JSON_spec_fixed (name, obj, sizeof (*obj)) + + +/** + * Variable size object (in network byte order, encoded using + * Crockford Base32hex encoding). + * + * @param name name of the JSON field + * @param[out] obj pointer where to write the data, will be allocated + * @param[out] size where to store the number of bytes allocated for @a obj + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_varsize (const char *name, + void **obj, + size_t *size); + + +/** + * The expected field stores a string. + * + * @param name name of the JSON field + * @param strptr where to store a pointer to the field + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_string (const char *name, + const char **strptr); + +/** + * JSON object. + * + * @param name name of the JSON field + * @param[out] jsonp where to store the JSON found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_json (const char *name, + json_t **jsonp); + + +/** + * 8-bit integer. + * + * @param name name of the JSON field + * @param[out] u8 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint8 (const char *name, + uint8_t *u8); + + +/** + * 16-bit integer. + * + * @param name name of the JSON field + * @param[out] u16 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint16 (const char *name, + uint16_t *u16); + + +/** + * 32-bit integer. + * + * @param name name of the JSON field + * @param[out] u32 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint32 (const char *name, + uint32_t *u32); + + +/** + * 64-bit integer. + * + * @param name name of the JSON field + * @param[out] u64 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint64 (const char *name, + uint64_t *u64); + + +/* ************ GNUnet-specific parser specifications ******************* */ + +/** + * Absolute time. + * + * @param name name of the JSON field + * @param[out] at where to store the absolute time found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_absolute_time (const char *name, + struct GNUNET_TIME_Absolute *at); + + +/** + * Relative time. + * + * @param name name of the JSON field + * @param[out] rt where to store the relative time found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_relative_time (const char *name, + struct GNUNET_TIME_Relative *rt); + + +/** + * Specification for parsing an RSA public key. + * + * @param name name of the JSON field + * @param pk where to store the RSA key found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_rsa_public_key (const char *name, + struct GNUNET_CRYPTO_rsa_PublicKey **pk); + + +/** + * Specification for parsing an RSA signature. + * + * @param name name of the JSON field + * @param sig where to store the RSA signature found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_rsa_signature (const char *name, + struct GNUNET_CRYPTO_rsa_Signature **sig); + + +/* ****************** Generic generator interface ******************* */ + + +/** + * Convert binary data to a JSON string with the base32crockford + * encoding. + * + * @param data binary data + * @param size size of @a data in bytes + * @return json string that encodes @a data + */ +json_t * +GNUNET_JSON_from_data (const void *data, + size_t size); + + +/** + * Convert absolute timestamp to a json string. + * + * @param stamp the time stamp + * @return a json string with the timestamp in @a stamp + */ +json_t * +GNUNET_JSON_from_time_abs (struct GNUNET_TIME_Absolute stamp); + + +/** + * Convert relative timestamp to a json string. + * + * @param stamp the time stamp + * @return a json string with the timestamp in @a stamp + */ +json_t * +GNUNET_JSON_from_time_rel (struct GNUNET_TIME_Relative stamp); + + +/** + * Convert RSA public key to JSON. + * + * @param pk public key to convert + * @return corresponding JSON encoding + */ +json_t * +GNUNET_JSON_from_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *pk); + + +/** + * Convert RSA signature to JSON. + * + * @param sig signature to convert + * @return corresponding JSON encoding + */ +json_t * +GNUNET_JSON_from_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *sig); + + + + +/* end of gnunet_json_lib.h */ diff --git a/src/include/gnunet_pq_lib.h b/src/include/gnunet_pq_lib.h index dd41406d3..a7525df7e 100644 --- a/src/include/gnunet_pq_lib.h +++ b/src/include/gnunet_pq_lib.h @@ -58,8 +58,7 @@ struct GNUNET_PQ_QueryParam { /** - * Format of the rest of the entry, determines the data - * type that is being added to the query. + * Function for how to handle this type of entry. */ GNUNET_PQ_QueryConverter conv; diff --git a/src/include/gnunet_time_lib.h b/src/include/gnunet_time_lib.h index b805d1e0a..3dad179b5 100644 --- a/src/include/gnunet_time_lib.h +++ b/src/include/gnunet_time_lib.h @@ -258,6 +258,30 @@ struct GNUNET_TIME_Absolute GNUNET_TIME_relative_to_absolute (struct GNUNET_TIME_Relative rel); +/** + * Round a time value so that it is suitable for transmission + * via JSON encodings. + * + * @param at time to round + * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if + * it was just now rounded + */ +int +GNUNET_TIME_round_abs (struct GNUNET_TIME_Absolute *at); + + +/** + * Round a time value so that it is suitable for transmission + * via JSON encodings. + * + * @param rt time to round + * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if + * it was just now rounded + */ +int +GNUNET_TIME_round_rel (struct GNUNET_TIME_Relative *rt); + + /** * Return the minimum of two relative time values. * diff --git a/src/json/Makefile.am b/src/json/Makefile.am new file mode 100644 index 000000000..83cd6dd2d --- /dev/null +++ b/src/json/Makefile.am @@ -0,0 +1,35 @@ +# This Makefile.am is in the public domain +AM_CPPFLAGS = -I$(top_srcdir)/src/include + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + +lib_LTLIBRARIES = \ + libgnunetjson.la + +libgnunetjson_la_LDFLAGS = \ + -version-info 0:0:0 \ + -no-undefined +libgnunetjson_la_SOURCES = \ + json.c \ + json_generator.c \ + json_helper.c +libgnunetjson_la_LIBADD = \ + -lgnunetutil \ + -ljansson \ + $(XLIB) + +check_PROGRAMS = \ + test_json + +TESTS = \ + $(check_PROGRAMS) + +test_json_SOURCES = \ + test_json.c +test_json_LDADD = \ + libgnunetjson.la \ + -lgnunetutil \ + -ljansson diff --git a/src/json/json.c b/src/json/json.c new file mode 100644 index 000000000..aa74bfd48 --- /dev/null +++ b/src/json/json.c @@ -0,0 +1,92 @@ +/* + This file is part of GNUnet + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNUnet; see the file COPYING. If not, If not, see +*/ +/** + * @file json/json.c + * @brief functions to parse JSON snippets + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_json_lib.h" + + +/** + * Navigate and parse data in a JSON tree. Tries to parse the @a root + * to find all of the values given in the @a spec. If one of the + * entries in @a spec cannot be found or parsed, the name of the JSON + * field is returned in @a error_json_name, and the offset of the + * entry in @a spec is returned in @a error_line. + * + * @param root the JSON node to start the navigation at. + * @param spec parse specification array + * @param[out] error_json_name which JSON field was problematic + * @param[out] which index into @a spec did we encounter an error + * @return #GNUNET_OK on success, #GNUNET_SYSERR on error + */ +int +GNUNET_JSON_parse (const json_t *root, + struct GNUNET_JSON_Specification *spec, + const char **error_json_name, + unsigned int *error_line) +{ + unsigned int i; + json_t *pos; + + for (i=0;NULL != spec[i].parser;i++) + { + if (NULL == spec[i].field) + pos = (json_t *) root; + else + pos = json_object_get (root, + spec[i].field); + if ( (NULL == pos) || + (GNUNET_OK != + spec[i].parser (spec[i].cls, + pos, + &spec[i])) ) + { + if (NULL != error_json_name) + *error_json_name = spec[i].field; + if (NULL != error_line) + *error_line = i; + GNUNET_JSON_parse_free (spec); + return GNUNET_SYSERR; + } + } + return GNUNET_OK; /* all OK! */ +} + + +/** + * Frees all elements allocated during a #GNUNET_JSON_parse() + * operation. + * + * @param spec specification of the parse operation + */ +void +GNUNET_JSON_parse_free (struct GNUNET_JSON_Specification *spec) +{ + unsigned int i; + + for (i=0;NULL != spec[i].parser;i++) + if (NULL != spec[i].cleaner) + spec[i].cleaner (spec[i].cls, + &spec[i]); +} + + +/* end of json.c */ diff --git a/src/json/json_generator.c b/src/json/json_generator.c new file mode 100644 index 000000000..4b1ac31b1 --- /dev/null +++ b/src/json/json_generator.c @@ -0,0 +1,146 @@ +/* + This file is part of GNUnet + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNUnet; see the file COPYING. If not, If not, see +*/ +/** + * @file json/json_generator.c + * @brief helper functions for generating JSON from GNUnet data structures + * @author Sree Harsha Totakura + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_json_lib.h" + + +/** + * Convert binary data to a JSON string + * with the base32crockford encoding. + * + * @param data binary data + * @param size size of @a data in bytes + * @return json string that encodes @a data + */ +json_t * +GNUNET_JSON_from_data (const void *data, + size_t size) +{ + char *buf; + json_t *json; + + buf = GNUNET_STRINGS_data_to_string_alloc (data, size); + json = json_string (buf); + GNUNET_free (buf); + return json; +} + + +/** + * Convert absolute timestamp to a json string. + * + * @param stamp the time stamp + * @return a json string with the timestamp in @a stamp + */ +json_t * +GNUNET_JSON_from_time_abs (struct GNUNET_TIME_Absolute stamp) +{ + json_t *j; + char *mystr; + int ret; + + GNUNET_assert (GNUNET_OK == + GNUNET_TIME_round_abs (&stamp)); + if (stamp.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) + return json_string ("/never/"); + ret = GNUNET_asprintf (&mystr, + "/Date(%llu)/", + (unsigned long long) (stamp.abs_value_us / (1000LL * 1000LL))); + GNUNET_assert (ret > 0); + j = json_string (mystr); + GNUNET_free (mystr); + return j; +} + + +/** + * Convert relative timestamp to a json string. + * + * @param stamp the time stamp + * @return a json string with the timestamp in @a stamp + */ +json_t * +GNUNET_JSON_from_time_rel (struct GNUNET_TIME_Relative stamp) +{ + json_t *j; + char *mystr; + int ret; + + GNUNET_assert (GNUNET_OK == + GNUNET_TIME_round_rel (&stamp)); + if (stamp.rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + return json_string ("/forever/"); + ret = GNUNET_asprintf (&mystr, + "/Delay(%llu)/", + (unsigned long long) (stamp.rel_value_us / (1000LL * 1000LL))); + GNUNET_assert (ret > 0); + j = json_string (mystr); + GNUNET_free (mystr); + return j; +} + + +/** + * Convert RSA public key to JSON. + * + * @param pk public key to convert + * @return corresponding JSON encoding + */ +json_t * +GNUNET_JSON_from_rsa_public_key (const struct GNUNET_CRYPTO_rsa_PublicKey *pk) +{ + char *buf; + size_t buf_len; + json_t *ret; + + buf_len = GNUNET_CRYPTO_rsa_public_key_encode (pk, + &buf); + ret = GNUNET_JSON_from_data (buf, + buf_len); + GNUNET_free (buf); + return ret; +} + + +/** + * Convert RSA signature to JSON. + * + * @param sig signature to convert + * @return corresponding JSON encoding + */ +json_t * +GNUNET_JSON_from_rsa_signature (const struct GNUNET_CRYPTO_rsa_Signature *sig) +{ + char *buf; + size_t buf_len; + json_t *ret; + + buf_len = GNUNET_CRYPTO_rsa_signature_encode (sig, + &buf); + ret = GNUNET_JSON_from_data (buf, + buf_len); + GNUNET_free (buf); + return ret; +} + + +/* End of json/json_generator.c */ diff --git a/src/json/json_helper.c b/src/json/json_helper.c new file mode 100644 index 000000000..cf84ae997 --- /dev/null +++ b/src/json/json_helper.c @@ -0,0 +1,864 @@ +/* + This file is part of GNUnet + Copyright (C) 2014, 2015, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNUnet; see the file COPYING. If not, If not, see +*/ +/** + * @file json/json_helper.c + * @brief functions to generate specifciations for JSON parsing + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_json_lib.h" + + +/** + * End of a parser specification. + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_end () +{ + struct GNUNET_JSON_Specification ret = { + .parser = NULL, + .cleaner = NULL, + .cls = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to fixed size data + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_fixed_data (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *enc; + unsigned int len; + + if (NULL == (enc = json_string_value (root))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + len = strlen (enc); + if (((len * 5) / 8) != spec->ptr_size) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (enc, + len, + spec->ptr, + spec->ptr_size)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Variable size object (in network byte order, encoded using Crockford + * Base32hex encoding). + * + * @param name name of the JSON field + * @param[out] obj pointer where to write the data, must have @a size bytes + * @param size number of bytes expected in @a obj + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_fixed (const char *name, + void *obj, + size_t size) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_fixed_data, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = obj, + .ptr_size = size, + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to variable size data + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_variable_data (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *str; + size_t size; + void *data; + int res; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size = (strlen (str) * 5) / 8; + if (size >= 1024) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + data = GNUNET_malloc (size); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + data, + size); + if (GNUNET_OK != res) + { + GNUNET_break_op (0); + GNUNET_free (data); + return GNUNET_SYSERR; + } + *(void**) spec->ptr = data; + *spec->size_ptr = size; + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing variable size data + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_variable_data (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + if (0 != *spec->size_ptr) + { + GNUNET_free (*(void **) spec->ptr); + *(void**) spec->ptr = NULL; + *spec->size_ptr = 0; + } +} + + +/** + * Variable size object (in network byte order, encoded using + * Crockford Base32hex encoding). + * + * @param name name of the JSON field + * @param[out] obj pointer where to write the data, will be allocated + * @param[out] size where to store the number of bytes allocated for @a obj + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_varsize (const char *name, + void **obj, + size_t *size) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_variable_data, + .cleaner = &clean_variable_data, + .cls = NULL, + .field = name, + .ptr = obj, + .ptr_size = 0, + .size_ptr = size + }; + *obj = NULL; + *size = 0; + return ret; +} + + +/** + * Parse given JSON object to string. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_string (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + const char *str; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *(const char **) spec->ptr = str; + return GNUNET_OK; +} + + +/** + * The expected field stores a string. + * + * @param name name of the JSON field + * @param strptr where to store a pointer to the field + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_string (const char *name, + const char **strptr) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_string, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = strptr, + .ptr_size = 0, + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to a JSON object. (Yes, trivial.) + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_object (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + if (! (json_is_object (root) || json_is_array (root)) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + json_incref (root); + *(json_t **) spec->ptr = root; + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing JSON object. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_object (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + json_t **ptr = (json_t **) spec->ptr; + if (NULL != *ptr) + { + json_decref (*ptr); + *ptr = NULL; + } +} + + +/** + * JSON object. + * + * @param name name of the JSON field + * @param[out] jsonp where to store the JSON found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_json (const char *name, + json_t **jsonp) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_object, + .cleaner = &clean_object, + .cls = NULL, + .field = name, + .ptr = jsonp, + .ptr_size = 0, + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to a uint8_t. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_u8 (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + json_int_t val; + uint8_t *up = spec->ptr; + + if (! json_is_integer (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + val = json_integer_value (root); + if ( (0 > val) || (val > UINT8_MAX) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *up = (uint8_t) val; + return GNUNET_OK; +} + + +/** + * 8-bit integer. + * + * @param name name of the JSON field + * @param[out] u8 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint8 (const char *name, + uint8_t *u8) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_u8, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = u8, + .ptr_size = sizeof (uint8_t), + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to a uint16_t. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_u16 (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + json_int_t val; + uint16_t *up = spec->ptr; + + if (! json_is_integer (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + val = json_integer_value (root); + if ( (0 > val) || (val > UINT16_MAX) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *up = (uint16_t) val; + return GNUNET_OK; +} + + +/** + * 16-bit integer. + * + * @param name name of the JSON field + * @param[out] u16 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint16 (const char *name, + uint16_t *u16) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_u16, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = u16, + .ptr_size = sizeof (uint16_t), + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to a uint32_t. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_u32 (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + json_int_t val; + uint32_t *up = spec->ptr; + + if (! json_is_integer (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + val = json_integer_value (root); + if ( (0 > val) || (val > UINT32_MAX) ) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + *up = (uint32_t) val; + return GNUNET_OK; +} + + +/** + * 32-bit integer. + * + * @param name name of the JSON field + * @param[out] u32 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint32 (const char *name, + uint32_t *u32) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_u32, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = u32, + .ptr_size = sizeof (uint32_t), + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to a uint8_t. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_u64 (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + json_int_t val; + uint64_t *up = spec->ptr; + + if (! json_is_integer (root)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + val = json_integer_value (root); + *up = (uint64_t) val; + return GNUNET_OK; +} + + +/** + * 64-bit integer. + * + * @param name name of the JSON field + * @param[out] u64 where to store the integer found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_uint64 (const char *name, + uint64_t *u64) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_u64, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = u64, + .ptr_size = sizeof (uint64_t), + .size_ptr = NULL + }; + return ret; +} + + +/* ************ GNUnet-specific parser specifications ******************* */ + +/** + * Parse given JSON object to absolute time. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_abs_time (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_TIME_Absolute *abs = spec->ptr; + const char *val; + unsigned long long int tval; + + val = json_string_value (root); + if (NULL == val) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (0 == strcasecmp (val, + "/forever/")) || + (0 == strcasecmp (val, + "/end of time/")) || + (0 == strcasecmp (val, + "/never/")) ) + { + *abs = GNUNET_TIME_UNIT_FOREVER_ABS; + return GNUNET_OK; + } + if (1 != sscanf (val, + "/Date(%llu)/", + &tval)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Time is in seconds in JSON, but in microseconds in GNUNET_TIME_Absolute */ + abs->abs_value_us = tval * 1000LL * 1000LL; + if ( (abs->abs_value_us) / 1000LL / 1000LL != tval) + { + /* Integer overflow */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Absolute time. + * + * @param name name of the JSON field + * @param[out] at where to store the absolute time found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_absolute_time (const char *name, + struct GNUNET_TIME_Absolute *at) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_abs_time, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = at, + .ptr_size = sizeof (uint64_t), + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to relative time. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_rel_time (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_TIME_Relative *rel = spec->ptr; + const char *val; + unsigned long long int tval; + + val = json_string_value (root); + if (NULL == val) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if ( (0 == strcasecmp (val, + "/forever/")) ) + { + *rel = GNUNET_TIME_UNIT_FOREVER_REL; + return GNUNET_OK; + } + if (1 != sscanf (val, + "/Delay(%llu)/", + &tval)) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + /* Time is in seconds in JSON, but in microseconds in GNUNET_TIME_Relative */ + rel->rel_value_us = tval * 1000LL * 1000LL; + if ( (rel->rel_value_us) / 1000LL / 1000LL != tval) + { + /* Integer overflow */ + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Relative time. + * + * @param name name of the JSON field + * @param[out] rt where to store the relative time found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_relative_time (const char *name, + struct GNUNET_TIME_Relative *rt) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_rel_time, + .cleaner = NULL, + .cls = NULL, + .field = name, + .ptr = rt, + .ptr_size = sizeof (uint64_t), + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to RSA public key. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_rsa_public_key (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_CRYPTO_rsa_PublicKey **pk = spec->ptr; + const char *enc; + char *buf; + size_t len; + size_t buf_len; + + if (NULL == (enc = json_string_value (root))) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + len = strlen (enc); + buf_len = (len * 5) / 8; + buf = GNUNET_malloc (buf_len); + if (GNUNET_OK != + GNUNET_STRINGS_string_to_data (enc, + len, + buf, + buf_len)) + { + GNUNET_break_op (0); + GNUNET_free (buf); + return GNUNET_SYSERR; + } + if (NULL == (*pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, + buf_len))) + { + GNUNET_break_op (0); + GNUNET_free (buf); + return GNUNET_SYSERR; + } + GNUNET_free (buf); + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing RSA public key. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_rsa_public_key (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_CRYPTO_rsa_PublicKey **pk = spec->ptr; + + if (NULL != *pk) + { + GNUNET_CRYPTO_rsa_public_key_free (*pk); + *pk = NULL; + } +} + + +/** + * Specification for parsing an RSA public key. + * + * @param name name of the JSON field + * @param pk where to store the RSA key found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_rsa_public_key (const char *name, + struct GNUNET_CRYPTO_rsa_PublicKey **pk) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_rsa_public_key, + .cleaner = &clean_rsa_public_key, + .cls = NULL, + .field = name, + .ptr = pk, + .ptr_size = 0, + .size_ptr = NULL + }; + return ret; +} + + +/** + * Parse given JSON object to RSA signature. + * + * @param cls closure, NULL + * @param root the json object representing data + * @param[out] spec where to write the data + * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error + */ +static int +parse_rsa_signature (void *cls, + json_t *root, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_CRYPTO_rsa_Signature **sig = spec->ptr; + size_t size; + const char *str; + int res; + void *buf; + + str = json_string_value (root); + if (NULL == str) + { + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + size = (strlen (str) * 5) / 8; + buf = GNUNET_malloc (size); + res = GNUNET_STRINGS_string_to_data (str, + strlen (str), + buf, + size); + if (GNUNET_OK != res) + { + GNUNET_free (buf); + GNUNET_break_op (0); + return GNUNET_SYSERR; + } + if (NULL == (*sig = GNUNET_CRYPTO_rsa_signature_decode (buf, + size))) + { + GNUNET_break_op (0); + GNUNET_free (buf); + return GNUNET_SYSERR; + } + GNUNET_free (buf); + return GNUNET_OK; +} + + +/** + * Cleanup data left from parsing RSA signature. + * + * @param cls closure, NULL + * @param[out] spec where to free the data + */ +static void +clean_rsa_signature (void *cls, + struct GNUNET_JSON_Specification *spec) +{ + struct GNUNET_CRYPTO_rsa_Signature **sig = spec->ptr; + + if (NULL != *sig) + { + GNUNET_CRYPTO_rsa_signature_free (*sig); + *sig = NULL; + } +} + + +/** + * Specification for parsing an RSA signature. + * + * @param name name of the JSON field + * @param sig where to store the RSA signature found under @a name + */ +struct GNUNET_JSON_Specification +GNUNET_JSON_spec_rsa_signature (const char *name, + struct GNUNET_CRYPTO_rsa_Signature **sig) +{ + struct GNUNET_JSON_Specification ret = { + .parser = &parse_rsa_signature, + .cleaner = &clean_rsa_signature, + .cls = NULL, + .field = name, + .ptr = sig, + .ptr_size = 0, + .size_ptr = NULL + }; + return ret; +} + + +/* end of json_helper.c */ diff --git a/src/json/test_json.c b/src/json/test_json.c new file mode 100644 index 000000000..a334bf599 --- /dev/null +++ b/src/json/test_json.c @@ -0,0 +1,217 @@ +/* + This file is part of GNUnet + (C) 2015, 2016 GNUnet e.V. + + GNUnet is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNUnet; see the file COPYING. If not, If not, see +*/ + +/** + * @file json/test_json.c + * @brief Tests for JSON conversion functions + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_json_lib.h" + + +/** + * Test absolute time conversion from/to JSON. + * + * @return 0 on success + */ +static int +test_abs_time () +{ + json_t *j; + struct GNUNET_TIME_Absolute a1; + struct GNUNET_TIME_Absolute a2; + struct GNUNET_JSON_Specification s1[] = { + GNUNET_JSON_spec_absolute_time (NULL, &a2), + GNUNET_JSON_spec_end() + }; + struct GNUNET_JSON_Specification s2[] = { + GNUNET_JSON_spec_absolute_time (NULL, &a2), + GNUNET_JSON_spec_end() + }; + + a1 = GNUNET_TIME_absolute_get (); + GNUNET_TIME_round_abs (&a1); + j = GNUNET_JSON_from_time_abs (a1); + GNUNET_assert (NULL != j); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (j, s1, NULL, NULL)); + GNUNET_assert (a1.abs_value_us == + a2.abs_value_us); + json_decref (j); + + a1 = GNUNET_TIME_UNIT_FOREVER_ABS; + j = GNUNET_JSON_from_time_abs (a1); + GNUNET_assert (NULL != j); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (j, s2, NULL, NULL)); + GNUNET_assert (a1.abs_value_us == + a2.abs_value_us); + json_decref (j); + return 0; +} + + +/** + * Test relative time conversion from/to JSON. + * + * @return 0 on success + */ +static int +test_rel_time () +{ + json_t *j; + struct GNUNET_TIME_Relative r1; + struct GNUNET_TIME_Relative r2; + struct GNUNET_JSON_Specification s1[] = { + GNUNET_JSON_spec_relative_time (NULL, &r2), + GNUNET_JSON_spec_end() + }; + struct GNUNET_JSON_Specification s2[] = { + GNUNET_JSON_spec_relative_time (NULL, &r2), + GNUNET_JSON_spec_end() + }; + + r1 = GNUNET_TIME_UNIT_SECONDS; + j = GNUNET_JSON_from_time_rel (r1); + GNUNET_assert (NULL != j); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (j, s1, NULL, NULL)); + GNUNET_assert (r1.rel_value_us == + r2.rel_value_us); + json_decref (j); + + r1 = GNUNET_TIME_UNIT_FOREVER_REL; + j = GNUNET_JSON_from_time_rel (r1); + GNUNET_assert (NULL != j); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (j, s2, NULL, NULL)); + GNUNET_assert (r1.rel_value_us == + r2.rel_value_us); + json_decref (j); + return 0; +} + + +/** + * Test raw (binary) conversion from/to JSON. + * + * @return 0 on success + */ +static int +test_raw () +{ + char blob[256]; + unsigned int i; + json_t *j; + + for (i=0;i<=256;i++) + { + char blob2[256]; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed (NULL, blob2, i), + GNUNET_JSON_spec_end() + }; + + memset (blob, i, i); + j = GNUNET_JSON_from_data (blob, i); + GNUNET_assert (NULL != j); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (j, spec, + NULL, NULL)); + GNUNET_assert (0 == + memcmp (blob, + blob2, + i)); + } + return 0; +} + + +/** + * Test rsa conversions from/to JSON. + * + * @return 0 on success + */ +static int +test_rsa () +{ + struct GNUNET_CRYPTO_rsa_PublicKey *pub; + struct GNUNET_CRYPTO_rsa_PublicKey *pub2; + struct GNUNET_JSON_Specification pspec[] = { + GNUNET_JSON_spec_rsa_public_key (NULL, &pub2), + GNUNET_JSON_spec_end() + }; + struct GNUNET_CRYPTO_rsa_Signature *sig; + struct GNUNET_CRYPTO_rsa_Signature *sig2; + struct GNUNET_JSON_Specification sspec[] = { + GNUNET_JSON_spec_rsa_signature (NULL, &sig2), + GNUNET_JSON_spec_end() + }; + struct GNUNET_CRYPTO_rsa_PrivateKey *priv; + char msg[] = "Hello"; + json_t *jp; + json_t *js; + + priv = GNUNET_CRYPTO_rsa_private_key_create (1024); + pub = GNUNET_CRYPTO_rsa_private_key_get_public (priv); + sig = GNUNET_CRYPTO_rsa_sign (priv, + msg, + sizeof (msg)); + GNUNET_assert (NULL != (jp = GNUNET_JSON_from_rsa_public_key (pub))); + GNUNET_assert (NULL != (js = GNUNET_JSON_from_rsa_signature (sig))); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (jp, pspec, + NULL, NULL)); + GNUNET_assert (GNUNET_OK == + GNUNET_JSON_parse (js, sspec, + NULL, NULL)); + GNUNET_break (0 == + GNUNET_CRYPTO_rsa_signature_cmp (sig, + sig2)); + GNUNET_break (0 == + GNUNET_CRYPTO_rsa_public_key_cmp (pub, + pub2)); + GNUNET_CRYPTO_rsa_signature_free (sig); + GNUNET_CRYPTO_rsa_signature_free (sig2); + GNUNET_CRYPTO_rsa_private_key_free (priv); + GNUNET_CRYPTO_rsa_public_key_free (pub); + GNUNET_CRYPTO_rsa_public_key_free (pub2); + return 0; +} + + +int +main(int argc, + const char *const argv[]) +{ + GNUNET_log_setup ("test-json", + "WARNING", + NULL); + if (0 != test_abs_time ()) + return 1; + if (0 != test_rel_time ()) + return 1; + if (0 != test_raw ()) + return 1; + if (0 != test_rsa ()) + return 1; + /* FIXME: test EdDSA signature conversion... */ + return 0; +} + +/* end of test_json.c */ diff --git a/src/util/time.c b/src/util/time.c index 4f3eaa7fb..654b567f0 100644 --- a/src/util/time.c +++ b/src/util/time.c @@ -58,6 +58,46 @@ GNUNET_TIME_get_offset () } +/** + * Round a time value so that it is suitable for transmission + * via JSON encodings. + * + * @param at time to round + * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if + * it was just now rounded + */ +int +GNUNET_TIME_round_abs (struct GNUNET_TIME_Absolute *at) +{ + if (at->abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us) + return GNUNET_OK; + if (0 == at->abs_value_us % 1000000) + return GNUNET_OK; + at->abs_value_us -= at->abs_value_us % 1000000; + return GNUNET_NO; +} + + +/** + * Round a time value so that it is suitable for transmission + * via JSON encodings. + * + * @param rt time to round + * @return #GNUNET_OK if time was already rounded, #GNUNET_NO if + * it was just now rounded + */ +int +GNUNET_TIME_round_rel (struct GNUNET_TIME_Relative *rt) +{ + if (rt->rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us) + return GNUNET_OK; + if (0 == rt->rel_value_us % 1000000) + return GNUNET_OK; + rt->rel_value_us -= rt->rel_value_us % 1000000; + return GNUNET_NO; +} + + /** * Get the current time (works just as "time", just that we use the * unit of time that the cron-jobs use (and is 64 bit)).