PHASE #2: (Goal: recover basic file-sharing functionality)
* TESTING (needed for DV, DHT, Topology)
- implement library for local testing
- + check API for hostname specification;
- maybe use space-separated list instead?
+ modify configuration to allow controlling
connections for non-local starts
- + starting of groups of peers (and auto-setting
- of PORT options)
- + testbed creation with topology
- + testbed with churn
+ + CORE service does not start with valid peer ID (all zeross)
+ -- testcase fails!
+ + consider changing API for peer-group termination
+ to call continuation when done
+ + testbed creation with topology (needs working F2F topology) [Nate]
+ + testbed with churn [Nate]
- implement testcases for library
- + test basic peer start
+ + get test for basic peer start to work!
+ test basic peer connect
+ test group start
- + test topology creation
- + test churn generation
+ + test basic peer re-configure [Nate]
+ + test topology creation [Nate]
+ + test churn generation [Nate]
* TOPOLOGY:
- implement testcases (needs TESTING)
* HOSTLIST:
statistics \
datacache \
datastore \
- testing \
template \
transport \
core \
+ testing \
$(HOSTLIST_DIR) \
topology
#include "gnunet_common.h"
-#define DEBUG_ARM GNUNET_NO
+#define DEBUG_ARM GNUNET_YES
#endif
{
uint16_t *res;
+ if (NULL == client)
+ return;
#if DEBUG_ARM
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Telling client that service `%s' is now %s\n",
sl->mtime = sbuf.st_mtime;
running = sl;
start_process (sl);
- signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
+ if (NULL != client)
+ signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP);
}
struct GNUNET_DATACACHE_PluginFunctions *api = cls;
struct Plugin *plugin = api->cls;
- UNLINK (plugin->fn);
+ if (0 != UNLINK (plugin->fn))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+ "unlink",
+ plugin->fn);
GNUNET_free (plugin->fn);
sqlite3_close (plugin->dbh);
GNUNET_free (plugin);
sm->header.size = htons(sizeof(struct StatusMessage) + slen);
sm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_STATUS);
sm->status = htonl(code);
- memcpy (&sm[1], msg, slen);
+ if (slen > 0)
+ memcpy (&sm[1], msg, slen);
transmit (client, &sm->header, NULL, NULL, GNUNET_YES);
}
--- /dev/null
+INCLUDES = -I$(top_srcdir)/src/include
+
+if MINGW
+ WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+
+lib_LTLIBRARIES = libgnunetfs.la
+
+libgnunetfs_la_SOURCES = \
+ fs_getopt.c \
+ fs_uri.c
+libgnunetfs_la_LIBADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL) $(XLIB)
+libgnunetfs_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) $(WINFLAGS) \
+ -version-info 0:0:0
+
+
+#bin_PROGRAMS = \
+# gnunet-directory \
+# gnunet-download \
+# gnunet-pseudonym \
+# gnunet-search \
+# gnunet-share \
+# gnunet-unindex
+
+#gnunet_directory_SOURCES = \
+# gnunet-directory.c
+#gnunet_directory_LDADD = \
+# $(top_builddir)/src/fs/libgnunetfs.la \
+# $(top_builddir)/src/util/libgnunetutil.la \
+# $(GN_LIBINTL)
+
+
+check_PROGRAMS = \
+ test_fs_getopt \
+ test_fs_uri
+
+TESTS = $(check_PROGRAMS)
+
+test_fs_uri_SOURCES = \
+ test_fs_uri.c
+test_fs_LDADD = \
+ $(top_builddir)/src/fs/libgnunetfs.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_fs_getopt_SOURCES = \
+ test_fs_getopt.c
+test_fs_LDADD = \
+ $(top_builddir)/src/fs/libgnunetfs.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+#EXTRA_DIST = \
+# test_fs_data.conf
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file fs/fs.h
+ * @brief definitions for the entire fs module
+ * @author Igor Wronsky, Christian Grothoff
+ */
+#ifndef FS_H
+#define FS_H
+
+/**
+ * @brief content hash key
+ */
+struct ContentHashKey
+{
+ GNUNET_HashCode key;
+ GNUNET_HashCode query;
+};
+
+
+/**
+ * @brief complete information needed
+ * to download a file.
+ */
+struct FileIdentifier
+{
+
+ /**
+ * Total size of the file in bytes. (network byte order (!))
+ */
+ unsigned long long file_length;
+
+ /**
+ * Query and key of the top GNUNET_EC_IBlock.
+ */
+ struct ContentHashKey chk;
+
+};
+
+
+/**
+ * Information about a file and its location
+ * (peer claiming to share the file).
+ */
+struct Location
+{
+ /**
+ * Information about the shared file.
+ */
+ struct FileIdentifier fi;
+
+ /**
+ * Identity of the peer sharing the file.
+ */
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded peer;
+
+ /**
+ * Time when the HELLO *and* this location URI
+ * expire (they expire together!).
+ */
+ struct GNUNET_TIME_Absolute expirationTime;
+
+ /**
+ * RSA signature over the GNUNET_EC_FileIdentifier,
+ * GNUNET_hash of the peer and expiration time.
+ */
+ struct GNUNET_CRYPTO_RsaSignature contentSignature;
+
+};
+
+enum uri_types
+{ chk, sks, ksk, loc };
+
+/**
+ * A Universal Resource Identifier (URI), opaque.
+ */
+struct GNUNET_FS_Uri
+{
+ enum uri_types type;
+ union
+ {
+ struct
+ {
+ /**
+ * Keywords start with a '+' if they are
+ * mandatory (in which case the '+' is NOT
+ * part of the keyword) and with a
+ * simple space if they are optional
+ * (in which case the space is ALSO not
+ * part of the actual keyword).
+ *
+ * Double-quotes to protect spaces and
+ * %-encoding are NOT used internally
+ * (only in URI-strings).
+ */
+ char **keywords;
+ unsigned int keywordCount;
+ } ksk;
+ struct
+ {
+ GNUNET_HashCode namespace;
+ char *identifier;
+ } sks;
+ struct FileIdentifier chk;
+ struct Location loc;
+ } data;
+
+};
+
+#endif
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file fs/fs_getopt.c
+ * @brief helper functions for command-line argument processing
+ * @author Igor Wronsky, Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_fs_lib.h"
+
+
+
+/* ******************** command-line option parsing API *********************** */
+
+/**
+ * Command-line option parser function that allows the user
+ * to specify one or more '-k' options with keywords. Each
+ * specified keyword will be added to the URI. A pointer to
+ * the URI must be passed as the "scls" argument.
+ *
+ * @param ctx command line processor context
+ * @param scls must be of type "struct GNUNET_FS_Uri **"
+ * @param option name of the option (typically 'k')
+ * @param value command line argument given
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_FS_getopt_configure_set_keywords (GNUNET_GETOPT_CommandLineProcessorContext* ctx,
+ void *scls,
+ const char *option,
+ const char *value)
+{
+ struct GNUNET_FS_Uri **uri = scls;
+ struct GNUNET_FS_Uri *u = *uri;
+ char *val;
+ size_t slen;
+
+ if (u == NULL)
+ {
+ u = GNUNET_malloc (sizeof (struct GNUNET_ECRS_URI));
+ *uri = u;
+ u->type = ksk;
+ u->data.ksk.keywordCount = 0;
+ u->data.ksk.keywords = NULL;
+ }
+ else
+ {
+ GNUNET_assert (u->type == ksk);
+ }
+ slen = strlen (value);
+ if (slen == 0)
+ return GNUNET_SYSERR; /* cannot be empty */
+ if (value[0] == '+')
+ {
+ /* simply preserve the "mandatory" flag */
+ if (slen < 2)
+ return GNUNET_SYSERR; /* empty keywords not allowed */
+ if ((value[1] == '"') && (slen > 3) && (value[slen - 1] == '"'))
+ {
+ /* remove the quotes, keep the '+' */
+ val = GNUNET_malloc (slen - 1);
+ val[0] = '+';
+ memcpy (&val[1], &value[2], slen - 3);
+ val[slen - 2] = '\0';
+ }
+ else
+ {
+ /* no quotes, just keep the '+' */
+ val = GNUNET_strdup (value);
+ }
+ }
+ else
+ {
+ if ((value[0] == '"') && (slen > 2) && (value[slen - 1] == '"'))
+ {
+ /* remove the quotes, add a space */
+ val = GNUNET_malloc (slen);
+ val[0] = ' ';
+ memcpy (&val[1], &value[1], slen - 2);
+ val[slen - 1] = '\0';
+ }
+ else
+ {
+ /* add a space to indicate "not mandatory" */
+ val = GNUNET_malloc (slen + 2);
+ strcpy (val, " ");
+ strcat (val, value);
+ }
+ }
+ GNUNET_array_grow (u->data.ksk.keywords,
+ u->data.ksk.keywordCount, u->data.ksk.keywordCount + 1);
+ u->data.ksk.keywords[u->data.ksk.keywordCount - 1] = val;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Command-line option parser function that allows the user to specify
+ * one or more '-m' options with metadata. Each specified entry of
+ * the form "type=value" will be added to the metadata. A pointer to
+ * the metadata must be passed as the "scls" argument.
+ *
+ * @param ctx command line processor context
+ * @param scls must be of type "struct GNUNET_MetaData **"
+ * @param option name of the option (typically 'k')
+ * @param value command line argument given
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_FS_getopt_configure_set_metadata (GNUNET_GETOPT_CommandLineProcessorContext* ctx,
+ void *scls,
+ const char *option,
+ const char *value)
+
+{
+ struct GNUNET_CONTAINER_MetaData **mm = scls;
+ EXTRACTOR_KeywordType type;
+ const char *typename;
+ const char *typename_i18n;
+ struct GNUNET_CONTAINER_MetaData *meta;
+ char *tmp;
+
+ meta = *mm;
+ if (meta == NULL)
+ {
+ meta = GNUNET_CONTAINER_meta_data_create ();
+ *mm = meta;
+ }
+
+ tmp = GNUNET_STRINGS_to_utf8 (NULL, value, strlen (value),
+#if ENABLE_NLS
+ nl_langinfo (CODESET)
+#else
+ "utf-8"
+#endif
+ );
+ type = EXTRACTOR_getHighestKeywordTypeNumber ();
+ while (type > 0)
+ {
+ type--;
+ typename = EXTRACTOR_getKeywordTypeAsString (type);
+ typename_i18n = dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, typename);
+ if ((strlen (tmp) >= strlen (typename) + 1) &&
+ (tmp[strlen (typename)] == ':') &&
+ (0 == strncmp (typename, tmp, strlen (typename))))
+ {
+ GNUNET_CONTAINER_meta_data_insert (meta, type, &tmp[strlen (typename) + 1]);
+ GNUNET_free (tmp);
+ tmp = NULL;
+ break;
+ }
+ if ((strlen (tmp) >= strlen (typename_i18n) + 1) &&
+ (tmp[strlen (typename_i18n)] == ':') &&
+ (0 == strncmp (typename_i18n, tmp, strlen (typename_i18n))))
+ {
+ GNUNET_CONTAINER_meta_data_insert (meta, type,
+ &tmp[strlen (typename_i18n) + 1]);
+ GNUNET_free (tmp);
+ tmp = NULL;
+ break;
+ }
+ }
+ if (tmp != NULL)
+ {
+ GNUNET_CONTAINER_meta_data_insert (meta, EXTRACTOR_UNKNOWN, tmp);
+ GNUNET_free (tmp);
+ printf (_
+ ("Unknown metadata type in metadata option `%s'. Using metadata type `unknown' instead.\n"),
+ value);
+ }
+ return GNUNET_OK;
+}
+
+/* end of fs_getopt.c */
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file fs/fs_uri.c
+ * @brief Parses and produces uri strings.
+ * @author Igor Wronsky, Christian Grothoff
+ *
+ * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER".
+ * The specific structure of "IDENTIFIER" depends on the module and
+ * maybe differenciated into additional subcategories if applicable.
+ * This module only deals with ecrs identifiers (MODULE = "ecrs").
+ * <p>
+ *
+ * This module only parses URIs for the AFS module. The ECRS URIs fall
+ * into four categories, "chk", "sks", "ksk" and "loc". The first three
+ * categories were named in analogy (!) to Freenet, but they do NOT
+ * work in exactly the same way. They are very similar from the user's
+ * point of view (unique file identifier, subspace, keyword), but the
+ * implementation is rather different in pretty much every detail.
+ * The concrete URI formats are:
+ *
+ * <ul><li>
+ *
+ * First, there are URIs that identify a file. They have the format
+ * "gnunet://ecrs/chk/HEX1.HEX2.SIZE". These URIs can be used to
+ * download the file. The description, filename, mime-type and other
+ * meta-data is NOT part of the file-URI since a URI uniquely
+ * identifies a resource (and the contents of the file would be the
+ * same even if it had a different description).
+ *
+ * </li><li>
+ *
+ * The second category identifies entries in a namespace. The format
+ * is "gnunet://ecrs/sks/NAMESPACE/IDENTIFIER" where the namespace
+ * should be given in HEX. Applications may allow using a nickname
+ * for the namespace if the nickname is not ambiguous. The identifier
+ * can be either an ASCII sequence or a HEX-encoding. If the
+ * identifier is in ASCII but the format is ambiguous and could denote
+ * a HEX-string a "/" is appended to indicate ASCII encoding.
+ *
+ * </li> <li>
+ *
+ * The third category identifies ordinary searches. The format is
+ * "gnunet://ecrs/ksk/KEYWORD[+KEYWORD]*". Using the "+" syntax
+ * it is possible to encode searches with the boolean "AND" operator.
+ * "+" is used since it indicates a commutative 'and' operation and
+ * is unlikely to be used in a keyword by itself.
+ *
+ * </li><li>
+ *
+ * The last category identifies a datum on a specific machine. The
+ * format is "gnunet://ecrs/loc/HEX1.HEX2.SIZE.PEER.SIG.EXPTIME". PEER is
+ * the BinName of the public key of the peer storing the datum. The
+ * signature (SIG) certifies that this peer has this content.
+ * HEX1, HEX2 and SIZE correspond to a 'chk' URI.
+ *
+ * </li></ul>
+ *
+ * The encoding for hexadecimal values is defined in the hashing.c
+ * module in the gnunetutil library and discussed there.
+ * <p>
+ */
+#include "platform.h"
+#include "gnunet_fs_lib.h"
+#include "fs.h"
+
+
+/**
+ * Get a unique key from a URI. This is for putting URIs
+ * into HashMaps. The key may change between FS implementations.
+ *
+ * @param uri uri to convert to a unique key
+ * @param key wherer to store the unique key
+ */
+void
+GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri,
+ GNUNET_HashCode * key)
+{
+ switch (uri->type)
+ {
+ case chk:
+ *key = uri->data.fi.chk.query;
+ return;
+ case sks:
+ GNUNET_hash (uri->data.sks.identifier,
+ strlen (uri->data.sks.identifier), key);
+ break;
+ case ksk:
+ if (uri->data.ksk.keywordCount > 0)
+ GNUNET_hash (uri->data.ksk.keywords[0],
+ strlen (uri->data.ksk.keywords[0]), key);
+ break;
+ case loc:
+ GNUNET_hash (&uri->data.loc.fi,
+ sizeof (GNUNET_EC_FileIdentifier) +
+ sizeof (GNUNET_RSA_PublicKey), key);
+ break;
+ default:
+ memset (key, 0, sizeof (GNUNET_HashCode));
+ break;
+ }
+}
+
+
+/**
+ * Convert a URI to a UTF-8 String.
+ *
+ * @param uri uri to convert to a string
+ * @return the UTF-8 string
+ */
+char *
+GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri);
+
+
+/**
+ * Convert keyword URI to a human readable format
+ * (i.e. the search query that was used in the first place)
+ *
+ * @param uri ksk uri to convert to a string
+ * @return string with the keywords
+ */
+char *
+GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri);
+
+/**
+ * Convert a UTF-8 String to a URI.
+ *
+ * @param uri string to parse
+ * @param emsg where to store the parser error message (if any)
+ * @return NULL on error
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_parse (const char *uri,
+ char **emsg);
+
+/**
+ * Free URI.
+ *
+ * @param uri uri to free
+ */
+void
+GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri)
+{
+ unsigned int i;
+
+ GNUNET_assert (uri != NULL);
+ switch (uri->type)
+ {
+ case ksk:
+ for (i = 0; i < uri->data.ksk.keywordCount; i++)
+ GNUNET_free (uri->data.ksk.keywords[i]);
+ GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount,
+ 0);
+ break;
+ case sks:
+ GNUNET_free (uri->data.sks.identifier);
+ break;
+ case loc:
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ GNUNET_free (uri);
+}
+
+/**
+ * How many keywords are ANDed in this keyword URI?
+ *
+ * @param uri ksk uri to get the number of keywords from
+ * @return 0 if this is not a keyword URI
+ */
+unsigned int
+GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri)
+{
+ if (uri->type != ksk)
+ return 0;
+ return uri->data.ksk.keywordCount;
+}
+
+
+/**
+ * Iterate over all keywords in this keyword URI.
+ *
+ * @param uri ksk uri to get the keywords from
+ * @param iterator function to call on each keyword
+ * @param iterator_cls closure for iterator
+ * @return -1 if this is not a keyword URI, otherwise number of
+ * keywords iterated over until iterator aborted
+ */
+int
+GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri,
+ GNUNET_FS_KeywordIterator iterator,
+ void *iterator_cls)
+{
+ unsigned int i;
+ char *keyword;
+
+ if (uri->type != ksk)
+ return -1;
+ if (iterator == NULL)
+ return uri->data.ksk.keywordCount;
+ for (i = 0; i < uri->data.ksk.keywordCount; i++)
+ {
+ keyword = uri->data.ksk.keywords[i];
+ /* first character of keyword indicates
+ if it is mandatory or not */
+ if (GNUNET_OK != iterator (&keyword[1], keyword[0] == '+', cls))
+ return i;
+ }
+ return i;
+}
+
+
+/**
+ * Obtain the identity of the peer offering the data
+ *
+ * @param uri the location URI to inspect
+ * @param peer where to store the identify of the peer (presumably) offering the content
+ * @return GNUNET_SYSERR if this is not a location URI, otherwise GNUNET_OK
+ */
+int
+GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri,
+ struct GNUNET_PeerIdentity * peer)
+{
+ if (uri->type != loc)
+ return GNUNET_SYSERR;
+ GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey),
+ &peer->hashPubKey);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Obtain the URI of the content itself.
+ *
+ * @param uri location URI to get the content URI from
+ * @return NULL if argument is not a location URI
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri)
+{
+ struct GNUNET_ECRS_Uri *ret;
+
+ if (uri->type != loc)
+ return NULL;
+ ret = GNUNET_malloc (sizeof (struct GNUNET_ECRS_Uri));
+ ret->type = chk;
+ ret->data.chk = uri->data.loc.fi;
+ return ret;
+}
+
+
+/**
+ * Construct a location URI (this peer will be used for the location).
+ *
+ * @param baseURI content offered by the sender
+ * @param cfg configuration information (used to find our hostkey)
+ * @param expiration_time how long will the content be offered?
+ * @return the location URI, NULL on error
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *baseUri,
+ struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_TIME_Absolute expiration_time);
+
+
+/**
+ * Canonicalize keyword URI. Performs operations such
+ * as decapitalization and removal of certain characters.
+ * (useful for search).
+ *
+ * @param uri the URI to canonicalize
+ * @return canonicalized version of the URI, NULL on error
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_ksk_canonicalize (const struct GNUNET_FS_Uri *uri);
+
+
+/**
+ * Merge the sets of keywords from two KSK URIs.
+ * (useful for merging the canonicalized keywords with
+ * the original keywords for sharing).
+ *
+ * @param u1 first uri
+ * @param u2 second uri
+ * @return merged URI, NULL on error
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1,
+ const struct GNUNET_FS_Uri *u2);
+
+
+/**
+ * Duplicate URI.
+ *
+ * @param uri the URI to duplicate
+ * @return copy of the URI
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri)
+{
+ struct GNUNET_ECRS_URI *ret;
+ unsigned int i;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
+ memcpy (ret, uri, sizeof (struct GNUNET_FS_Uri));
+ switch (ret->type)
+ {
+ case ksk:
+ if (ret->data.ksk.keywordCount > 0)
+ {
+ ret->data.ksk.keywords
+ = GNUNET_malloc (ret->data.ksk.keywordCount * sizeof (char *));
+ for (i = 0; i < ret->data.ksk.keywordCount; i++)
+ ret->data.ksk.keywords[i] =
+ GNUNET_strdup (uri->data.ksk.keywords[i]);
+ }
+ else
+ ret->data.ksk.keywords = NULL; /* just to be sure */
+ break;
+ case sks:
+ ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier);
+ break;
+ case loc:
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+/**
+ * Create an FS URI from a single user-supplied string of keywords.
+ * The string is broken up at spaces into individual keywords.
+ * Keywords that start with "+" are mandatory. Double-quotes can
+ * be used to prevent breaking up strings at spaces (and also
+ * to specify non-mandatory keywords starting with "+").
+ *
+ * Keywords must contain a balanced number of double quotes and
+ * double quotes can not be used in the actual keywords (for
+ * example, the string '""foo bar""' will be turned into two
+ * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'.
+ *
+ * @param keywords the keyword string
+ * @return an FS URI for the given keywords, NULL
+ * if keywords is not legal (i.e. empty).
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_ksk_create (const char *keywords);
+
+
+/**
+ * Create an FS URI from a user-supplied command line of keywords.
+ * Arguments should start with "+" to indicate mandatory
+ * keywords.
+ *
+ * @param argc number of keywords
+ * @param argv keywords (double quotes are not required for
+ * keywords containing spaces; however, double
+ * quotes are required for keywords starting with
+ * "+"); there is no mechanism for having double
+ * quotes in the actual keywords (if the user
+ * did specifically specify double quotes, the
+ * caller should convert each double quote
+ * into two single quotes).
+ * @return an FS URI for the given keywords, NULL
+ * if keywords is not legal (i.e. empty).
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_ksk_create_from_args (unsigned int argc,
+ const char **argv);
+
+
+/**
+ * Test if two URIs are equal.
+ *
+ * @param u1 one of the URIs
+ * @param u2 the other URI
+ * @return GNUNET_YES if the URIs are equal
+ */
+int
+GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1,
+ const struct GNUNET_FS_Uri *u2)
+{
+ int ret;
+ unsigned int i;
+ unsigned int j;
+
+ GNUNET_assert (uri1 != NULL);
+ GNUNET_assert (uri2 != NULL);
+ if (uri1->type != uri2->type)
+ return GNUNET_NO;
+ switch (uri1->type)
+ {
+ case chk:
+ if (0 == memcmp (&uri1->data.chk,
+ &uri2->data.chk,
+ sizeof (struct FileIdentifier)))
+ return GNUNET_YES;
+ return GNUNET_NO;
+ case sks:
+ if ((0 == memcmp (&uri1->data.sks.namespace,
+ &uri2->data.sks.namespace,
+ sizeof (GNUNET_HashCode))) &&
+ (0 == strcmp (uri1->data.sks.identifier,
+ uri2->data.sks.identifier)))
+
+ return GNUNET_YES;
+ return GNUNET_NO;
+ case ksk:
+ if (uri1->data.ksk.keywordCount != uri2->data.ksk.keywordCount)
+ return GNUNET_NO;
+ for (i = 0; i < uri1->data.ksk.keywordCount; i++)
+ {
+ ret = GNUNET_NO;
+ for (j = 0; j < uri2->data.ksk.keywordCount; j++)
+ {
+ if (0 == strcmp (uri1->data.ksk.keywords[i],
+ uri2->data.ksk.keywords[j]))
+ {
+ ret = GNUNET_YES;
+ break;
+ }
+ }
+ if (ret == GNUNET_NO)
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+ case loc:
+ if (memcmp (&uri1->data.loc,
+ &uri2->data.loc,
+ sizeof (struct FileIdentifier) +
+ sizeof (GNUNET_RSA_PublicKey) +
+ sizeof (struct GNUNET_TIME_Absolute) +
+ sizeof (unsigned short) + sizeof (unsigned short)) != 0)
+ return GNUNET_NO;
+ return GNUNET_YES;
+ default:
+ return GNUNET_NO;
+ }
+}
+
+
+/**
+ * Is this a namespace URI?
+ *
+ * @param uri the uri to check
+ * @return GNUNET_YES if this is an SKS uri
+ */
+int
+GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri)
+{
+ return uri->type == sks;
+}
+
+
+/**
+ * Get the ID of a namespace from the given
+ * namespace URI.
+ *
+ * @param uri the uri to get the namespace ID from
+ * @param nsid where to store the ID of the namespace
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri,
+ GNUNET_HashCode * nsid)
+{
+ if (! GNUNET_FS_uri_test_sks (uri))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ *id = uri->data.sks.namespace;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get the content identifier of an SKS URI.
+ *
+ * @param uri the sks uri
+ * @return NULL on error (not a valid SKS URI)
+ */
+char *
+GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri)
+{
+ if (!GNUNET_FS_uri_test_sks (uri))
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ return GNUNET_strdup (uri->data.sks.identifier);
+}
+
+
+/**
+ * Convert namespace URI to a human readable format
+ * (using the namespace description, if available).
+ *
+ * @param cfg configuration to use
+ * @param uri SKS uri to convert
+ * @return NULL on error (not an SKS URI)
+ */
+char *
+GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_FS_Uri *uri);
+
+
+/**
+ * Is this a keyword URI?
+ *
+ * @param uri the uri
+ * @return GNUNET_YES if this is a KSK uri
+ */
+int
+GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri)
+{
+#if EXTRA_CHECKS
+ unsigned int i;
+
+ if (uri->type == ksk)
+ {
+ for (i = uri->data.ksk.keywordCount - 1; i >= 0; i--)
+ GNUNET_assert (uri->data.ksk.keywords[i] != NULL);
+ }
+#endif
+ return uri->type == ksk;
+}
+
+
+/**
+ * Is this a file (or directory) URI?
+ *
+ * @param uri the uri to check
+ * @return GNUNET_YES if this is a CHK uri
+ */
+int
+GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri)
+{
+ return uri->type == chk;
+}
+
+
+/**
+ * What is the size of the file that this URI
+ * refers to?
+ *
+ * @param uri the CHK URI to inspect
+ * @return size of the file as specified in the CHK URI
+ */
+uint64_t
+GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri)
+{
+ switch (uri->type)
+ {
+ case chk:
+ return GNUNET_ntohll (uri->data.chk.file_length);
+ case loc:
+ return GNUNET_ntohll (uri->data.loc.fi.file_length);
+ default:
+ GNUNET_assert (0);
+ }
+ return 0; /* unreachable */
+}
+
+
+/**
+ * Is this a location URI?
+ *
+ * @param uri the uri to check
+ * @return GNUNET_YES if this is a LOC uri
+ */
+int
+GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri)
+{
+ return uri->type == loc;
+}
+
+
+/**
+ * Function called on each value in the meta data.
+ * Adds it to the URI.
+ *
+ * @param cls URI to update
+ * @param type type of the meta data
+ * @param data value of the meta data
+ * @return GNUNET_OK (always)
+ */
+static int
+gather_uri_data (void *cls,
+ EXTRACTOR_KeywordType type,
+ const char *data)
+{
+ struct GNUNET_FS_Uri *uri = cls;
+ char *nkword;
+ int j;
+
+ for (j = uri->data.ksk.keywordCount - 1; j >= 0; j--)
+ if (0 == strcmp (&uri->data.ksk.keywords[j][1], data))
+ return GNUNET_OK;
+ nkword = GNUNET_malloc (strlen (data) + 2);
+ strcpy (nkword, " "); /* not mandatory */
+ strcat (nkword, data);
+ uri->data.ksk.keywords[uri->data.ksk.keywordCount++] = nkword;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Construct a keyword-URI from meta-data (take all entries
+ * in the meta-data and construct one large keyword URI
+ * that lists all keywords that can be found in the meta-data).
+ * @deprecated
+ */
+struct GNUNET_FS_Uri *
+GNUNET_FS_uri_ksk_create_from_meta_data (const struct GNUNET_MetaData *md)
+{
+ struct GNUNET_FS_Uri *ret;
+
+ if (md == NULL)
+ return NULL;
+ ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri));
+ ret->type = ksk;
+ ret->data.ksk.keywordCount = 0;
+ ret->data.ksk.keywords = NULL;
+ ret->data.ksk.keywords
+ = GNUNET_malloc (sizeof (char *) *
+ GNUNET_meta_data_get_contents (md, NULL, NULL));
+ GNUNET_meta_data_get_contents (md, &gather_uri_data, ret);
+ return ret;
+
+}
+
+#if 0
+
+// old code...
+
+
+
+/**
+ * In URI-encoding, does the given character
+ * need to be encoded using %-encoding?
+ */
+static int
+needs_percent (char c)
+{
+ return (!((isalnum (c)) ||
+ (c == '-') || (c == '_') || (c == '.') || (c == '~')));
+}
+
+/**
+ * Generate a keyword URI.
+ * @return NULL on error (i.e. keywordCount == 0)
+ */
+static char *
+createKeywordURI (char **keywords, unsigned int keywordCount)
+{
+ size_t n;
+ char *ret;
+ unsigned int i;
+ unsigned int j;
+ unsigned int wpos;
+ size_t slen;
+ const char *keyword;
+
+ n =
+ keywordCount + strlen (GNUNET_ECRS_URI_PREFIX) +
+ strlen (GNUNET_ECRS_SEARCH_INFIX) + 1;
+ for (i = 0; i < keywordCount; i++)
+ {
+ keyword = keywords[i];
+ slen = strlen (keyword);
+ n += slen;
+ for (j = 0; j < slen; j++)
+ {
+ if ((j == 0) && (keyword[j] == ' '))
+ {
+ n--;
+ continue; /* skip leading space */
+ }
+ if (needs_percent (keyword[j]))
+ n += 2; /* will use %-encoding */
+ }
+ }
+ ret = GNUNET_malloc (n);
+ strcpy (ret, GNUNET_ECRS_URI_PREFIX);
+ strcat (ret, GNUNET_ECRS_SEARCH_INFIX);
+ wpos = strlen (ret);
+ for (i = 0; i < keywordCount; i++)
+ {
+ keyword = keywords[i];
+ slen = strlen (keyword);
+ for (j = 0; j < slen; j++)
+ {
+ if ((j == 0) && (keyword[j] == ' '))
+ continue; /* skip leading space */
+ if (needs_percent (keyword[j]))
+ {
+ sprintf (&ret[wpos], "%%%02X", keyword[j]);
+ wpos += 3;
+ }
+ else
+ {
+ ret[wpos++] = keyword[j];
+ }
+ }
+ if (i != keywordCount - 1)
+ ret[wpos++] = '+';
+ }
+ return ret;
+}
+
+/**
+ * Generate a subspace URI.
+ */
+static char *
+createSubspaceURI (const GNUNET_HashCode * namespace, const char *identifier)
+{
+ size_t n;
+ char *ret;
+ GNUNET_EncName ns;
+
+ n =
+ sizeof (GNUNET_EncName) + strlen (GNUNET_ECRS_URI_PREFIX) +
+ strlen (GNUNET_ECRS_SUBSPACE_INFIX) + 1 + strlen (identifier);
+ ret = GNUNET_malloc (n);
+ GNUNET_hash_to_enc (namespace, &ns);
+ GNUNET_snprintf (ret, n,
+ "%s%s%s/%s",
+ GNUNET_ECRS_URI_PREFIX, GNUNET_ECRS_SUBSPACE_INFIX,
+ (const char *) &ns, identifier);
+ return ret;
+}
+
+/**
+ * Generate a file URI.
+ */
+static char *
+createFileURI (const GNUNET_EC_FileIdentifier * fi)
+{
+ char *ret;
+ GNUNET_EncName keyhash;
+ GNUNET_EncName queryhash;
+ size_t n;
+
+ GNUNET_hash_to_enc (&fi->chk.key, &keyhash);
+ GNUNET_hash_to_enc (&fi->chk.query, &queryhash);
+
+ n =
+ strlen (GNUNET_ECRS_URI_PREFIX) + 2 * sizeof (GNUNET_EncName) + 8 + 16 +
+ 32 + strlen (GNUNET_ECRS_FILE_INFIX);
+ ret = GNUNET_malloc (n);
+ GNUNET_snprintf (ret,
+ n,
+ "%s%s%s.%s.%llu",
+ GNUNET_ECRS_URI_PREFIX,
+ GNUNET_ECRS_FILE_INFIX,
+ (char *) &keyhash, (char *) &queryhash,
+ GNUNET_ntohll (fi->file_length));
+ return ret;
+}
+
+#include "bincoder.c"
+
+/**
+ * Create a (string) location URI from a Location.
+ */
+static char *
+createLocURI (const Location * loc)
+{
+ size_t n;
+ char *ret;
+ GNUNET_EncName keyhash;
+ GNUNET_EncName queryhash;
+ char *peerId;
+ char *peerSig;
+
+ GNUNET_hash_to_enc (&loc->fi.chk.key, &keyhash);
+ GNUNET_hash_to_enc (&loc->fi.chk.query, &queryhash);
+ n = 2148;
+ peerId = bin2enc (&loc->peer, sizeof (GNUNET_RSA_PublicKey));
+ peerSig = bin2enc (&loc->contentSignature, sizeof (GNUNET_RSA_Signature));
+ ret = GNUNET_malloc (n);
+ GNUNET_snprintf (ret,
+ n,
+ "%s%s%s.%s.%llu.%s.%s.%u",
+ GNUNET_ECRS_URI_PREFIX,
+ GNUNET_ECRS_LOCATION_INFIX,
+ (char *) &keyhash,
+ (char *) &queryhash,
+ GNUNET_ntohll (loc->fi.file_length),
+ peerId, peerSig, loc->expirationTime);
+ GNUNET_free (peerSig);
+ GNUNET_free (peerId);
+ return ret;
+}
+
+/**
+ * Convert a URI to a UTF-8 String.
+ */
+char *
+GNUNET_ECRS_uri_to_string (const struct GNUNET_ECRS_URI *uri)
+{
+ if (uri == NULL)
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ return NULL;
+ }
+ switch (uri->type)
+ {
+ case ksk:
+ return createKeywordURI (uri->data.ksk.keywords,
+ uri->data.ksk.keywordCount);
+ case sks:
+ return createSubspaceURI (&uri->data.sks.namespace,
+ uri->data.sks.identifier);
+ case chk:
+ return createFileURI (&uri->data.fi);
+ case loc:
+ return createLocURI (&uri->data.loc);
+ default:
+ GNUNET_GE_BREAK (NULL, 0);
+ return NULL;
+ }
+}
+
+/**
+ * Convert keyword URI to a human readable format
+ * (i.e. the search query that was used in the first place)
+ */
+char *
+GNUNET_ECRS_ksk_uri_to_human_readable_string (const struct GNUNET_ECRS_URI
+ *uri)
+{
+ size_t n;
+ char *ret;
+ unsigned int i;
+ const char *keyword;
+ char **keywords;
+ unsigned int keywordCount;
+
+ if ((uri == NULL) || (uri->type != ksk))
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ return NULL;
+ }
+ keywords = uri->data.ksk.keywords;
+ keywordCount = uri->data.ksk.keywordCount;
+ n = keywordCount + 1;
+ for (i = 0; i < keywordCount; i++)
+ {
+ keyword = keywords[i];
+ n += strlen (keyword) - 1;
+ if (NULL != strstr (&keyword[1], " "))
+ n += 2;
+ if (keyword[0] == '+')
+ n++;
+ }
+ ret = GNUNET_malloc (n);
+ strcpy (ret, "");
+ for (i = 0; i < keywordCount; i++)
+ {
+ keyword = keywords[i];
+ if (NULL != strstr (&keyword[1], " "))
+ {
+ strcat (ret, "\"");
+ if (keyword[0] == '+')
+ strcat (ret, keyword);
+ else
+ strcat (ret, &keyword[1]);
+ strcat (ret, "\"");
+ }
+ else
+ {
+ if (keyword[0] == '+')
+ strcat (ret, keyword);
+ else
+ strcat (ret, &keyword[1]);
+ }
+ strcat (ret, " ");
+ }
+ return ret;
+}
+
+/**
+ * Given a keyword with %-encoding (and possibly quotes to protect
+ * spaces), return a copy of the keyword without %-encoding and
+ * without double-quotes (%22). Also, add a space at the beginning
+ * if there is not a '+'.
+ */
+static char *
+percent_decode_keyword (const char *in)
+{
+ char *out;
+ char *ret;
+ unsigned int rpos;
+ unsigned int wpos;
+ unsigned int hx;
+
+ out = GNUNET_strdup (in);
+ rpos = 0;
+ wpos = 0;
+ while (out[rpos] != '\0')
+ {
+ if (out[rpos] == '%')
+ {
+ if (1 != sscanf (&out[rpos + 1], "%2X", &hx))
+ {
+ GNUNET_free (out);
+ return NULL;
+ }
+ rpos += 3;
+ if (hx == '"')
+ continue; /* skip double quote */
+ out[wpos++] = (char) hx;
+ }
+ else
+ {
+ out[wpos++] = out[rpos++];
+ }
+ }
+ out[wpos] = '\0';
+ if (out[0] == '+')
+ {
+ ret = GNUNET_strdup (out);
+ }
+ else
+ {
+ /* need to prefix with space */
+ ret = GNUNET_malloc (strlen (out) + 2);
+ strcpy (ret, " ");
+ strcat (ret, out);
+ }
+ GNUNET_free (out);
+ return ret;
+}
+
+/**
+ * Parses an ECRS search URI.
+ *
+ * @param uri an uri string
+ * @param keyword will be set to an array with the keywords
+ * @return GNUNET_SYSERR if this is not a search URI, otherwise
+ * the number of keywords placed in the array
+ */
+static int
+parseKeywordURI (struct GNUNET_GE_Context *ectx, const char *uri,
+ char ***keywords)
+{
+ unsigned int pos;
+ int ret;
+ int iret;
+ int i;
+ size_t slen;
+ char *dup;
+ int saw_quote;
+
+ GNUNET_GE_ASSERT (ectx, uri != NULL);
+
+ slen = strlen (uri);
+ pos = strlen (GNUNET_ECRS_URI_PREFIX);
+
+ if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos))
+ return GNUNET_SYSERR;
+ if (0 !=
+ strncmp (&uri[pos], GNUNET_ECRS_SEARCH_INFIX,
+ strlen (GNUNET_ECRS_SEARCH_INFIX)))
+ return GNUNET_SYSERR;
+ pos += strlen (GNUNET_ECRS_SEARCH_INFIX);
+ if (slen == pos)
+ {
+ /* no keywords */
+ (*keywords) = NULL;
+ return 0;
+ }
+ if ((uri[slen - 1] == '+') || (uri[pos] == '+'))
+ return GNUNET_SYSERR; /* no keywords / malformed */
+
+ ret = 1;
+ saw_quote = 0;
+ for (i = pos; i < slen; i++)
+ {
+ if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22")))
+ {
+ saw_quote = (saw_quote + 1) % 2;
+ i += 3;
+ continue;
+ }
+ if ((uri[i] == '+') && (saw_quote == 0))
+ {
+ ret++;
+ if (uri[i - 1] == '+')
+ return GNUNET_SYSERR; /* "++" not allowed */
+ }
+ }
+ if (saw_quote == 1)
+ return GNUNET_SYSERR; /* quotes not balanced */
+ iret = ret;
+ dup = GNUNET_strdup (uri);
+ (*keywords) = GNUNET_malloc (ret * sizeof (char *));
+ for (i = 0; i < ret; i++)
+ (*keywords)[i] = NULL;
+ for (i = slen - 1; i >= pos; i--)
+ {
+ if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22")))
+ {
+ saw_quote = (saw_quote + 1) % 2;
+ i += 3;
+ continue;
+ }
+ if ((dup[i] == '+') && (saw_quote == 0))
+ {
+ (*keywords)[--ret] = percent_decode_keyword (&dup[i + 1]);
+ if (NULL == (*keywords)[ret])
+ goto CLEANUP;
+ dup[i] = '\0';
+ }
+ }
+ (*keywords)[--ret] = percent_decode_keyword (&dup[pos]);
+ if (NULL == (*keywords)[ret])
+ goto CLEANUP;
+ GNUNET_GE_ASSERT (ectx, ret == 0);
+ GNUNET_free (dup);
+ return iret;
+CLEANUP:
+ for (i = 0; i < ret; i++)
+ GNUNET_free_non_null ((*keywords)[i]);
+ GNUNET_free (*keywords);
+ *keywords = NULL;
+ GNUNET_free (dup);
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Parses an AFS namespace / subspace identifier URI.
+ *
+ * @param uri an uri string
+ * @param namespace set to the namespace ID
+ * @param identifier set to the ID in the namespace
+ * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a namespace URI
+ */
+static int
+parseSubspaceURI (struct GNUNET_GE_Context *ectx,
+ const char *uri,
+ GNUNET_HashCode * namespace, char **identifier)
+{
+ unsigned int pos;
+ size_t slen;
+ char *up;
+
+ GNUNET_GE_ASSERT (ectx, uri != NULL);
+
+ slen = strlen (uri);
+ pos = strlen (GNUNET_ECRS_URI_PREFIX);
+
+ if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos))
+ return GNUNET_SYSERR;
+ if (0 != strncmp (&uri[pos],
+ GNUNET_ECRS_SUBSPACE_INFIX,
+ strlen (GNUNET_ECRS_SUBSPACE_INFIX)))
+ return GNUNET_SYSERR;
+ pos += strlen (GNUNET_ECRS_SUBSPACE_INFIX);
+ if ((slen < pos + sizeof (GNUNET_EncName) + 1) ||
+ (!((uri[pos + sizeof (GNUNET_EncName) - 1] == '/') ||
+ (uri[pos + sizeof (GNUNET_EncName) - 1] == '\\'))))
+ return GNUNET_SYSERR;
+
+ up = GNUNET_strdup (uri);
+ up[pos + sizeof (GNUNET_EncName) - 1] = '\0';
+ if ((GNUNET_OK != GNUNET_enc_to_hash (&up[pos], namespace)))
+ {
+ GNUNET_free (up);
+ return GNUNET_SYSERR;
+ }
+ *identifier = GNUNET_strdup (&up[pos + sizeof (GNUNET_EncName)]);
+ GNUNET_free (up);
+ return GNUNET_OK;
+}
+
+/**
+ * Parses an URI that identifies a file
+ *
+ * @param uri an uri string
+ * @param fi the file identifier
+ * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a file URI
+ */
+static int
+parseFileURI (struct GNUNET_GE_Context *ectx, const char *uri,
+ GNUNET_EC_FileIdentifier * fi)
+{
+ unsigned int pos;
+ size_t slen;
+ char *dup;
+
+ GNUNET_GE_ASSERT (ectx, uri != NULL);
+
+ slen = strlen (uri);
+ pos = strlen (GNUNET_ECRS_URI_PREFIX);
+
+ if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos))
+ return GNUNET_SYSERR;
+ if (0 !=
+ strncmp (&uri[pos], GNUNET_ECRS_FILE_INFIX,
+ strlen (GNUNET_ECRS_FILE_INFIX)))
+ return GNUNET_SYSERR;
+ pos += strlen (GNUNET_ECRS_FILE_INFIX);
+ if ((slen < pos + 2 * sizeof (GNUNET_EncName) + 1) ||
+ (uri[pos + sizeof (GNUNET_EncName) - 1] != '.') ||
+ (uri[pos + sizeof (GNUNET_EncName) * 2 - 1] != '.'))
+ return GNUNET_SYSERR;
+
+ dup = GNUNET_strdup (uri);
+ dup[pos + sizeof (GNUNET_EncName) - 1] = '\0';
+ dup[pos + sizeof (GNUNET_EncName) * 2 - 1] = '\0';
+ if ((GNUNET_OK != GNUNET_enc_to_hash (&dup[pos],
+ &fi->chk.key)) ||
+ (GNUNET_OK != GNUNET_enc_to_hash (&dup[pos + sizeof (GNUNET_EncName)],
+ &fi->chk.query)) ||
+ (1 != SSCANF (&dup[pos + sizeof (GNUNET_EncName) * 2],
+ "%llu", &fi->file_length)))
+ {
+ GNUNET_free (dup);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (dup);
+ fi->file_length = GNUNET_htonll (fi->file_length);
+ return GNUNET_OK;
+}
+
+/**
+ * Parses an URI that identifies a location (and file).
+ * Also verifies validity of the location URI.
+ *
+ * @param uri an uri string
+ * @param loc where to store the location
+ * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a file URI
+ */
+static int
+parseLocationURI (struct GNUNET_GE_Context *ectx, const char *uri,
+ Location * loc)
+{
+ unsigned int pos;
+ unsigned int npos;
+ int ret;
+ size_t slen;
+ char *dup;
+ char *addr;
+
+
+ GNUNET_GE_ASSERT (ectx, uri != NULL);
+ addr = NULL;
+ slen = strlen (uri);
+ pos = strlen (GNUNET_ECRS_URI_PREFIX);
+
+ if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos))
+ return GNUNET_SYSERR;
+ if (0 != strncmp (&uri[pos],
+ GNUNET_ECRS_LOCATION_INFIX,
+ strlen (GNUNET_ECRS_LOCATION_INFIX)))
+ return GNUNET_SYSERR;
+ pos += strlen (GNUNET_ECRS_LOCATION_INFIX);
+ if ((slen < pos + 2 * sizeof (GNUNET_EncName) + 1) ||
+ (uri[pos + sizeof (GNUNET_EncName) - 1] != '.') ||
+ (uri[pos + sizeof (GNUNET_EncName) * 2 - 1] != '.'))
+ return GNUNET_SYSERR;
+
+ dup = GNUNET_strdup (uri);
+ dup[pos + sizeof (GNUNET_EncName) - 1] = '\0';
+ dup[pos + sizeof (GNUNET_EncName) * 2 - 1] = '\0';
+ npos = pos + sizeof (GNUNET_EncName) * 2;
+ while ((uri[npos] != '\0') && (uri[npos] != '.'))
+ npos++;
+ if (dup[npos] == '\0')
+ goto ERR;
+ dup[npos++] = '\0';
+ if ((GNUNET_OK != GNUNET_enc_to_hash (&dup[pos],
+ &loc->fi.chk.key)) ||
+ (GNUNET_OK != GNUNET_enc_to_hash (&dup[pos + sizeof (GNUNET_EncName)],
+ &loc->fi.chk.query)) ||
+ (1 != SSCANF (&dup[pos + sizeof (GNUNET_EncName) * 2],
+ "%llu", &loc->fi.file_length)))
+ goto ERR;
+ loc->fi.file_length = GNUNET_htonll (loc->fi.file_length);
+ ret = enc2bin (&dup[npos], &loc->peer, sizeof (GNUNET_RSA_PublicKey));
+ if (ret == -1)
+ goto ERR;
+ npos += ret;
+ if (dup[npos++] != '.')
+ goto ERR;
+ ret =
+ enc2bin (&dup[npos], &loc->contentSignature,
+ sizeof (GNUNET_RSA_Signature));
+ if (ret == -1)
+ goto ERR;
+ npos += ret;
+ if (dup[npos++] != '.')
+ goto ERR;
+ if (1 != SSCANF (&dup[npos], "%u", &loc->expirationTime))
+ goto ERR;
+ /* Finally: verify sigs! */
+ if (GNUNET_OK != GNUNET_RSA_verify (&loc->fi,
+ sizeof (GNUNET_EC_FileIdentifier) +
+ sizeof (GNUNET_PeerIdentity) +
+ sizeof (GNUNET_Int32Time),
+ &loc->contentSignature, &loc->peer))
+ goto ERR;
+ GNUNET_free (dup);
+ return GNUNET_OK;
+ERR:
+ GNUNET_free (dup);
+ GNUNET_free_non_null (addr);
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Convert a UTF-8 String to a URI.
+ */
+URI *
+GNUNET_ECRS_string_to_uri (struct GNUNET_GE_Context * ectx, const char *uri)
+{
+ URI *ret;
+ int len;
+
+ ret = GNUNET_malloc (sizeof (URI));
+ if (GNUNET_OK == parseFileURI (ectx, uri, &ret->data.fi))
+ {
+ ret->type = chk;
+ return ret;
+ }
+ if (GNUNET_OK == parseSubspaceURI (ectx,
+ uri,
+ &ret->data.sks.namespace,
+ &ret->data.sks.identifier))
+ {
+ ret->type = sks;
+ return ret;
+ }
+ if (GNUNET_OK == parseLocationURI (ectx, uri, &ret->data.loc))
+ {
+ ret->type = loc;
+ return ret;
+ }
+ len = parseKeywordURI (ectx, uri, &ret->data.ksk.keywords);
+ if (len < 0)
+ {
+ GNUNET_free (ret);
+ return NULL;
+ }
+ ret->type = ksk;
+ ret->data.ksk.keywordCount = len;
+ return ret;
+}
+
+
+
+/**
+ * Construct a location URI.
+ *
+ * @param baseURI content offered by the sender
+ * @param sender identity of the peer with the content
+ * @param expiration_time how long will the content be offered?
+ * @param proto transport protocol to reach the peer
+ * @param sas sender address size (for HELLO)
+ * @param address sas bytes of address information
+ * @param signer function to call for obtaining
+ * RSA signatures for "sender".
+ * @return the location URI
+ */
+struct GNUNET_ECRS_URI *
+GNUNET_ECRS_location_to_uri (const struct GNUNET_ECRS_URI *baseUri,
+ const GNUNET_RSA_PublicKey * sender,
+ GNUNET_Int32Time expirationTime,
+ GNUNET_ECRS_SignFunction signer,
+ void *signer_cls)
+{
+ struct GNUNET_ECRS_URI *uri;
+
+ if (baseUri->type != chk)
+ return NULL;
+
+ uri = GNUNET_malloc (sizeof (struct GNUNET_ECRS_URI));
+ uri->type = loc;
+ uri->data.loc.fi = baseUri->data.fi;
+ uri->data.loc.peer = *sender;
+ uri->data.loc.expirationTime = expirationTime;
+ signer (signer_cls,
+ sizeof (GNUNET_EC_FileIdentifier) +
+ sizeof (GNUNET_PeerIdentity) +
+ sizeof (GNUNET_Int32Time),
+ &uri->data.loc.fi, &uri->data.loc.contentSignature);
+ return uri;
+}
+
+#endif
+
+/* end of uri.c */
--- /dev/null
+/*
+ This file is part of GNUnet
+ (C) 2009 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file fs/test_fs_getopt.c
+ * @brief test for fs_getopt.c
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_fs_lib.h"
+
+int
+main (int argc, char *argv[])
+{
+ fprintf (stderr, "WARNING: testcase not yet written.\n");
+ return 0; /* testcase passed */
+}
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2006, 2007 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/fs/ecrs/uritest.c
+ * @brief Test for uri.c
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_util.h"
+#include "gnunet_ecrs_lib.h"
+#include "ecrs.h"
+
+#define ABORT() { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); return 1; }
+
+static int
+testKeyword ()
+{
+ char *uri;
+ struct GNUNET_ECRS_URI *ret;
+
+ if (NULL != GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/ksk/++"))
+ ABORT ();
+ ret = GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/ksk/foo+bar");
+ if (ret == NULL)
+ ABORT ();
+ if (!GNUNET_ECRS_uri_test_ksk (ret))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ if ((2 != ret->data.ksk.keywordCount) ||
+ (0 != strcmp (" foo", ret->data.ksk.keywords[0])) ||
+ (0 != strcmp (" bar", ret->data.ksk.keywords[1])))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+
+ uri = GNUNET_ECRS_uri_to_string (ret);
+ if (0 != strcmp (uri, "gnunet://ecrs/ksk/foo+bar"))
+ {
+ GNUNET_free (uri);
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ GNUNET_free (uri);
+ GNUNET_ECRS_uri_destroy (ret);
+ return 0;
+}
+
+static int
+testLocation ()
+{
+ struct GNUNET_ECRS_URI *uri;
+ char *uric;
+ struct GNUNET_ECRS_URI *uri2;
+ GNUNET_RSA_PublicKey pk;
+ struct GNUNET_RSA_PrivateKey *hk;
+ struct GNUNET_ECRS_URI *baseURI;
+
+ baseURI =
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42");
+ hk = GNUNET_RSA_create_key ();
+ GNUNET_RSA_get_public_key (hk, &pk);
+ uri = GNUNET_ECRS_location_to_uri (baseURI,
+ &pk, 43,
+ (GNUNET_ECRS_SignFunction) &
+ GNUNET_RSA_sign, hk);
+ GNUNET_RSA_free_key (hk);
+ if (uri == NULL)
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ GNUNET_ECRS_uri_destroy (baseURI);
+ return 1;
+ }
+ if (!GNUNET_ECRS_uri_test_loc (uri))
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ GNUNET_ECRS_uri_destroy (uri);
+ GNUNET_ECRS_uri_destroy (baseURI);
+ return 1;
+ }
+ uri2 = GNUNET_ECRS_uri_get_content_uri_from_loc (uri);
+ if (!GNUNET_ECRS_uri_test_equal (baseURI, uri2))
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ GNUNET_ECRS_uri_destroy (uri);
+ GNUNET_ECRS_uri_destroy (uri2);
+ GNUNET_ECRS_uri_destroy (baseURI);
+ return 1;
+ }
+ GNUNET_ECRS_uri_destroy (uri2);
+ GNUNET_ECRS_uri_destroy (baseURI);
+ uric = GNUNET_ECRS_uri_to_string (uri);
+#if 0
+ /* not for the faint of heart: */
+ printf ("URI: `%s'\n", uric);
+#endif
+ uri2 = GNUNET_ECRS_string_to_uri (NULL, uric);
+ GNUNET_free (uric);
+ if (uri2 == NULL)
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ GNUNET_ECRS_uri_destroy (uri);
+ return 1;
+ }
+ if (GNUNET_YES != GNUNET_ECRS_uri_test_equal (uri, uri2))
+ {
+ GNUNET_GE_BREAK (NULL, 0);
+ GNUNET_ECRS_uri_destroy (uri);
+ GNUNET_ECRS_uri_destroy (uri2);
+ return 1;
+ }
+ GNUNET_ECRS_uri_destroy (uri2);
+ GNUNET_ECRS_uri_destroy (uri);
+ return 0;
+}
+
+static int
+testNamespace (int i)
+{
+ char *uri;
+ struct GNUNET_ECRS_URI *ret;
+
+ if (NULL !=
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3VUK"))
+ ABORT ();
+ if (NULL !=
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3V/test"))
+ ABORT ();
+ if (NULL != GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/sks/test"))
+ ABORT ();
+ ret =
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/sks/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820/test");
+ if (ret == NULL)
+ ABORT ();
+ if (GNUNET_ECRS_uri_test_ksk (ret))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ if (!GNUNET_ECRS_uri_test_sks (ret))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+
+ uri = GNUNET_ECRS_uri_to_string (ret);
+ if (0 != strcmp (uri,
+ "gnunet://ecrs/sks/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820/test"))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ GNUNET_free (uri);
+ ABORT ();
+ }
+ GNUNET_free (uri);
+ GNUNET_ECRS_uri_destroy (ret);
+ return 0;
+}
+
+static int
+testFile (int i)
+{
+ char *uri;
+ struct GNUNET_ECRS_URI *ret;
+
+ if (NULL !=
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H00000440000.42"))
+ ABORT ();
+ if (NULL !=
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000"))
+ ABORT ();
+ if (NULL !=
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.FGH"))
+ ABORT ();
+ ret =
+ GNUNET_ECRS_string_to_uri (NULL,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42");
+ if (ret == NULL)
+ ABORT ();
+ if (GNUNET_ECRS_uri_test_ksk (ret))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ if (GNUNET_ECRS_uri_test_sks (ret))
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ if (GNUNET_ntohll (ret->data.fi.file_length) != 42)
+ {
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+
+ uri = GNUNET_ECRS_uri_to_string (ret);
+ if (0 != strcmp (uri,
+ "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42"))
+ {
+ GNUNET_free (uri);
+ GNUNET_ECRS_uri_destroy (ret);
+ ABORT ();
+ }
+ GNUNET_free (uri);
+ GNUNET_ECRS_uri_destroy (ret);
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int failureCount = 0;
+ int i;
+
+ GNUNET_disable_entropy_gathering ();
+ failureCount += testKeyword ();
+ failureCount += testLocation ();
+ for (i = 0; i < 255; i++)
+ {
+ failureCount += testNamespace (i);
+ failureCount += testFile (i);
+ }
+ if (failureCount != 0)
+ return 1;
+ return 0;
+}
+
+/* end of uritest.c */
cfg = c;
sched = s;
stats = st;
- proxy = NULL;
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "HOSTLIST",
- "HTTP-PROXY",
- &proxy);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ "HOSTLIST",
+ "HTTP-PROXY",
+ &proxy))
+ proxy = NULL;
*ch = &connect_handler;
*dh = &disconnect_handler;
GNUNET_STATISTICS_get (stats,
size_t s;
if (peer == NULL)
- finish_response (results);
+ {
+ finish_response (results);
+ return;
+ }
old = results->size;
s = GNUNET_HELLO_size(hello);
if (old + s >= GNUNET_MAX_MALLOC_CHECKED)
/**
* Create a new configuration object.
- *
- * @param component name of responsible component
+ * @return fresh configuration object
*/
struct GNUNET_CONFIGURATION_Handle *GNUNET_CONFIGURATION_create (void);
+
+/**
+ * Duplicate an existing configuration object.
+ *
+ * @param c configuration to duplicate
+ * @return duplicate configuration
+ */
+struct GNUNET_CONFIGURATION_Handle *
+GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *c);
+
+
/**
* Destroy configuration object.
*/
void GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg);
+
/**
* Load configuration. This function will first parse the
* defaults and then parse the specific configuration file
int GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
const char *filename);
+
/**
* Parse a configuration file, add all of the options in the
* file to the configuration environment.
int GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg,
const char *filename);
+
/**
* Write configuration file.
* @return GNUNET_OK on success, GNUNET_SYSERR on error
int GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg,
const char *filename);
+
/**
* Test if there are configuration options that were
* changed since the last save.
*/
int GNUNET_CONFIGURATION_is_dirty (const struct GNUNET_CONFIGURATION_Handle *cfg);
+
+/**
+ * Function to iterate over options.
+ *
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
+ */
+typedef void (*GNUNET_CONFIGURATION_Iterator)(void *cls,
+ const char *section,
+ const char *option,
+ const char *value);
+
+
+/**
+ * Iterate over all options in the configuration.
+ *
+ * @param cfg configuration to inspect
+ * @param iter function to call on each option
+ * @param iter_cls closure for iter
+ */
+void GNUNET_CONFIGURATION_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GNUNET_CONFIGURATION_Iterator iter,
+ void *iter_cls);
+
+
/**
* Get a configuration value that should be a number.
* @return GNUNET_OK on success, GNUNET_SYSERR on error
const char *option,
const char *value);
+
#if 0 /* keep Emacsens' auto-indent happy */
{
#endif
/**
* Iterator over meta data.
+ *
+ * @param cls closure
+ * @param type type of the meta data
+ * @param data value of the meta data
* @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
*/
-typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (EXTRACTOR_KeywordType type,
- const char *data,
- void *closure);
+typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (void *cls,
+ EXTRACTOR_KeywordType type,
+ const char *data);
/**
* Create a fresh MetaData token.
*/
struct GNUNET_TESTING_Daemon *
GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
- struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *hostname,
GNUNET_TESTING_NotifyDaemonRunning cb,
void *cb_cls);
* @param total number of daemons to start
* @param cb function to call on each daemon that was started
* @param cb_cls closure for cb
- * @param hostname where to run the peers; can be NULL (to run
- * everything on localhost).
- * @param va Additional hosts can be specified using a NULL-terminated list of
- * varargs, hosts will then be used round-robin from that
- * list; va only contains anything if hostname != NULL.
- * @return NULL on error, otherwise handle to control peer group
- */
-struct GNUNET_TESTING_PeerGroup *
-GNUNET_TESTING_daemons_start_va (struct GNUNET_SCHEDULER_Handle *sched,
- const struct GNUNET_CONFIGURATION_Handle *cfg,
- unsigned int total,
- GNUNET_TESTING_NotifyDaemonRunning cb,
- void *cb_cls,
- const char *hostname,
- va_list va);
-
-
-/**
- * Start count gnunetd processes with the same set of
- * transports and applications. The port numbers will
- * be computed by adding delta each time (zero
- * times for the first peer).
- *
- * @param sched scheduler to use
- * @param cfg configuration template to use
- * @param total number of daemons to start
- * @param timeout how long is this allowed to take?
- * @param cb function to call on each daemon that was started
- * @param cb_cls closure for cb
- * @param hostname where to run the peers; can be NULL (to run
- * everything on localhost). Additional
- * hosts can be specified using a NULL-terminated list of
- * varargs, hosts will then be used round-robin from that
- * list.
+ * @param hostnames space-separated list of hostnames to use,
+ * NULL to use localhost only
* @return NULL on error, otherwise handle to control peer group
*/
struct GNUNET_TESTING_PeerGroup *
GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
- struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
unsigned int total,
GNUNET_TESTING_NotifyDaemonRunning cb,
void *cb_cls,
- const char *hostname,
- ...);
+ const char *hostnames);
/**
testing.c \
testing_group.c \
testing_testbed.c
-libgnunettesting_la_LIBADD = \
- $(top_builddir)/src/util/libgnunetutil.la $(XLIB)
-
-#check_PROGRAMS = \
-# test_testing
-#
-#TESTS = $(check_PROGRAMS)
-#
-#test_testing_SOURCES = \
-# test_testing.c
-#test_testing_LDADD = \
-# $(top_builddir)/src/testing/libgnunettesting.la \
-# $(top_builddir)/src/util/libgnunetutil.la
+libgnunettesting_la_LIBADD = $(XLIB) \
+ $(top_builddir)/src/core/libgnunetcore.la \
+ $(top_builddir)/src/transport/libgnunettransport.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+check_PROGRAMS = \
+ test_testing
+
+TESTS = $(check_PROGRAMS)
+
+test_testing_SOURCES = \
+ test_testing.c
+test_testing_LDADD = \
+ $(top_builddir)/src/testing/libgnunettesting.la \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+EXTRA_DIST = test_testing_data.conf
--- /dev/null
+/*
+ This file is part of GNUnet.
+ (C) 2009 Christian Grothoff (and other contributing authors)
+
+ 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 2, 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, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file testing/test_testing.c
+ * @brief testcase for testing.c
+ */
+#include "platform.h"
+#include "gnunet_testing_lib.h"
+
+#define VERBOSE GNUNET_YES
+
+static int ok;
+
+static void end_cb(void *cls,
+ const char *emsg)
+{
+ GNUNET_assert (emsg == NULL);
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Daemon terminated, will now exit.\n");
+#endif
+ ok = 0;
+}
+
+static void my_cb(void *cls,
+ const struct GNUNET_PeerIdentity *id,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_TESTING_Daemon *d,
+ const char *emsg)
+{
+ GNUNET_assert (id != NULL);
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Daemon started, will now stop it.\n");
+#endif
+ GNUNET_TESTING_daemon_stop (d, &end_cb, NULL);
+}
+
+
+static void
+run (void *cls,
+ struct GNUNET_SCHEDULER_Handle *sched,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_TESTING_Daemon *d;
+
+ ok = 1;
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Starting daemon.\n");
+#endif
+ d = GNUNET_TESTING_daemon_start (sched,
+ cfg,
+ NULL,
+ &my_cb,
+ NULL);
+ GNUNET_assert (d != NULL);
+}
+
+static int
+check ()
+{
+ char *const argv[] = { "test-testing",
+ "-c",
+ "test_testing_data.conf",
+#if VERBOSE
+ "-L", "DEBUG",
+#endif
+ NULL
+ };
+ struct GNUNET_GETOPT_CommandLineOption options[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+ GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
+ argv, "test-tesing", "nohelp",
+ options, &run, &ok);
+ return ok;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int ret;
+
+ GNUNET_log_setup ("test-testing",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret = check ();
+
+ return ret;
+}
+
+/* end of test_testing.c */
--- /dev/null
+[PATHS]
+SERVICEHOME = /tmp/test-gnunet-testing/
+DEFAULTCONFIG = test_testing_data.conf
+
+[resolver]
+PORT = 2564
+
+[transport]
+PORT = 2565
+PLUGINS = tcp
+
+[arm]
+PORT = 2566
+DEFAULTSERVICES = transport core
+
+[statistics]
+PORT = 2567
+
+[tcp]
+PORT = 2568
+
+[peerinfo]
+PORT = 2569
+
+[core]
+PORT = 2570
+
+[testing]
+WEAKRANDOM = YES
/**
* Our configuration.
*/
- const struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Host to run GNUnet on.
d->cb = NULL;
if (server == NULL)
{
- cb (d->cb_cls, NULL, d->cfg, d,
- _("Failed to connect to core service\n"));
if (GNUNET_YES == d->dead)
GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
+ else if (NULL != cb)
+ cb (d->cb_cls, NULL, d->cfg, d,
+ _("Failed to connect to core service\n"));
return;
}
#if DEBUG_TESTING
d->id = *my_identity;
if (GNUNET_YES == d->dead)
GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
- else
+ else if (NULL != cb)
cb (d->cb_cls, my_identity, d->cfg, d, NULL);
d->server = server;
}
unsigned long code;
char *dst;
+#if DEBUG_TESTING
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer FSM is in phase %u.\n",
+ d->phase);
+#endif
d->task = GNUNET_SCHEDULER_NO_TASK;
switch (d->phase)
{
{
cb = d->cb;
d->cb = NULL;
- cb (d->cb_cls,
- NULL,
- d->cfg,
- d,
- _("`scp' does not seem to terminate.\n"));
+ if (NULL != cb)
+ cb (d->cb_cls,
+ NULL,
+ d->cfg,
+ d,
+ _("`scp' does not seem to terminate.\n"));
return;
}
/* wait some more */
{
cb = d->cb;
d->cb = NULL;
- cb (d->cb_cls,
- NULL,
- d->cfg,
- d,
- _("`scp' did not complete cleanly.\n"));
+ if (NULL != cb)
+ cb (d->cb_cls,
+ NULL,
+ d->cfg,
+ d,
+ _("`scp' did not complete cleanly.\n"));
return;
}
#if DEBUG_TESTING
"gnunet-service-arm",
"-c",
d->cfgfile,
+#if DEBUG_TESTING
+ "-L", "DEBUG",
+#else
"-d",
+#endif
NULL);
}
else
(NULL == d->hostname) ? "gnunet-service-arm" : "ssh");
cb = d->cb;
d->cb = NULL;
- cb (d->cb_cls,
- NULL,
- d->cfg,
- d,
- (NULL == d->hostname)
- ? _("Failed to start `gnunet-service-arm' process.\n")
- : _("Failed to start `ssh' process.\n"));
+ if (NULL != cb)
+ cb (d->cb_cls,
+ NULL,
+ d->cfg,
+ d,
+ (NULL == d->hostname)
+ ? _("Failed to start `gnunet-service-arm' process.\n")
+ : _("Failed to start `ssh' process.\n"));
}
+#if DEBUG_TESTING
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Started `%s', waiting for `%s' to be up.\n",
+ "gnunet-service-arm",
+ "gnunet-service-core");
+#endif
d->phase = SP_START_ARMING;
d->wait_runs = 0;
+ d->task
+ = GNUNET_SCHEDULER_add_delayed (d->sched,
+ GNUNET_NO,
+ GNUNET_SCHEDULER_PRIORITY_KEEP,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_CONSTANTS_EXEC_WAIT,
+ &start_fsm,
+ d);
break;
case SP_START_ARMING:
if (GNUNET_OK !=
{
cb = d->cb;
d->cb = NULL;
- cb (d->cb_cls,
- NULL,
- d->cfg,
- d,
- (NULL == d->hostname)
- ? _("`gnunet-service-arm' does not seem to terminate.\n")
- : _("`ssh' does not seem to terminate.\n"));
+ if (NULL != cb)
+ cb (d->cb_cls,
+ NULL,
+ d->cfg,
+ d,
+ (NULL == d->hostname)
+ ? _("`gnunet-service-arm' does not seem to terminate.\n")
+ : _("`ssh' does not seem to terminate.\n"));
return;
}
/* wait some more */
if ( (type != GNUNET_OS_PROCESS_EXITED) ||
(code != 0) )
{
- d->dead_cb (d->dead_cb_cls,
- _("`sshp' did not complete cleanly.\n"));
+ if (NULL != d->dead_cb)
+ d->dead_cb (d->dead_cb_cls,
+ _("`ssh' did not complete cleanly.\n"));
GNUNET_free (d->cfgfile);
GNUNET_free_non_null (d->hostname);
GNUNET_free_non_null (d->username);
GNUNET_free (d);
return;
}
+#if DEBUG_TESTING
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Peer shutdown complete.\n");
+#endif
GNUNET_free (d->cfgfile);
GNUNET_free_non_null (d->hostname);
GNUNET_free_non_null (d->username);
- d->dead_cb (d->dead_cb_cls, NULL);
+ if (NULL != d->dead_cb)
+ d->dead_cb (d->dead_cb_cls, NULL);
GNUNET_free (d);
break;
case SP_CONFIG_UPDATE:
{
cb = d->cb;
d->cb = NULL;
- cb (d->cb_cls,
- NULL,
- d->cfg,
- d,
- _("`scp' does not seem to terminate.\n"));
+ if (NULL != cb)
+ cb (d->cb_cls,
+ NULL,
+ d->cfg,
+ d,
+ _("`scp' does not seem to terminate.\n"));
return;
}
/* wait some more */
if ( (type != GNUNET_OS_PROCESS_EXITED) ||
(code != 0) )
{
- d->update_cb (d->update_cb_cls,
- _("`scp' did not complete cleanly.\n"));
+ if (NULL != d->update_cb)
+ d->update_cb (d->update_cb_cls,
+ _("`scp' did not complete cleanly.\n"));
return;
}
#if DEBUG_TESTING
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Successfully copied configuration file.\n");
#endif
- d->update_cb (d->update_cb_cls, NULL);
+ if (NULL != d->update_cb)
+ d->update_cb (d->update_cb_cls, NULL);
d->phase = SP_START_DONE;
break;
}
*/
struct GNUNET_TESTING_Daemon *
GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
- struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *hostname,
GNUNET_TESTING_NotifyDaemonRunning cb,
void *cb_cls)
ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon));
ret->sched = sched;
- ret->cfg = cfg;
ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname);
ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config");
+#if DEBUG_TESTING
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Setting up peer with configuration file `%s'.\n",
+ ret->cfgfile);
+#endif
if (NULL == ret->cfgfile)
{
GNUNET_free_non_null (ret->hostname);
}
ret->cb = cb;
ret->cb_cls = cb_cls;
+ ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
+ GNUNET_CONFIGURATION_set_value_string (ret->cfg,
+ "PATHS",
+ "DEFAULTCONFIG",
+ ret->cfgfile);
/* 1) write configuration to temporary file */
if (GNUNET_OK !=
- GNUNET_CONFIGURATION_write (cfg,
+ GNUNET_CONFIGURATION_write (ret->cfg,
ret->cfgfile))
{
if (0 != UNLINK (ret->cfgfile))
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
"unlink",
ret->cfgfile);
+ GNUNET_CONFIGURATION_destroy (ret->cfg);
GNUNET_free_non_null (ret->hostname);
GNUNET_free (ret->cfgfile);
GNUNET_free (ret);
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
"unlink",
ret->cfgfile);
+ GNUNET_CONFIGURATION_destroy (ret->cfg);
GNUNET_free_non_null (ret->hostname);
GNUNET_free_non_null (ret->username);
GNUNET_free (ret->cfgfile);
}
ret->task
= GNUNET_SCHEDULER_add_delayed (sched,
- GNUNET_NO,
+ GNUNET_YES,
GNUNET_SCHEDULER_PRIORITY_KEEP,
GNUNET_SCHEDULER_NO_TASK,
GNUNET_CONSTANTS_EXEC_WAIT,
ret);
return ret;
}
+#if DEBUG_TESTING
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "No need to copy configuration file since we are running locally.\n");
+#endif
ret->phase = SP_COPIED;
GNUNET_SCHEDULER_add_continuation (sched,
GNUNET_NO,
d->dead_cb_cls = cb_cls;
d->task
= GNUNET_SCHEDULER_add_delayed (d->sched,
- GNUNET_NO,
+ GNUNET_YES,
GNUNET_SCHEDULER_PRIORITY_KEEP,
GNUNET_SCHEDULER_NO_TASK,
GNUNET_CONSTANTS_EXEC_WAIT,
d);
return;
}
+ GNUNET_CONFIGURATION_destroy (d->cfg);
GNUNET_free (d->cfgfile);
GNUNET_free_non_null (d->hostname);
GNUNET_free_non_null (d->username);
GNUNET_free (d);
- cb (cb_cls, NULL);
+ if (NULL != cb)
+ cb (cb_cls, NULL);
}
if (d->phase != SP_START_DONE)
{
- cb (cb_cls,
- _("Peer not yet running, can not change configuration at this point."));
+ if (NULL != cb)
+ cb (cb_cls,
+ _("Peer not yet running, can not change configuration at this point."));
return;
}
GNUNET_CONFIGURATION_write (cfg,
d->cfgfile))
{
- cb (cb_cls,
+ if (NULL != cb)
+ cb (cb_cls,
_("Failed to write new configuration to disk."));
return;
}
if (NULL == d->hostname)
{
/* signal success */
- cb (cb_cls, NULL);
+ if (NULL != cb)
+ cb (cb_cls, NULL);
return;
}
d->phase = SP_CONFIG_UPDATE;
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
_("Could not start `%s' process to copy configuration file.\n"),
"scp");
- cb (cb_cls,
- _("Failed to copy new configuration to remote machine."));
+ if (NULL != cb)
+ cb (cb_cls,
+ _("Failed to copy new configuration to remote machine."));
d->phase = SP_START_DONE;
return;
}
transmit_ready (void *cls, size_t size, void *buf)
{
struct ConnectContext *ctx = cls;
- if (buf == NULL)
- ctx->cb (ctx->cb_cls, _("Peers failed to connect"));
- else
- ctx->cb (ctx->cb_cls, NULL);
+
+ if (NULL != ctx->cb)
+ {
+ if (buf == NULL)
+ ctx->cb (ctx->cb_cls, _("Peers failed to connect"));
+ else
+ ctx->cb (ctx->cb_cls, NULL);
+ }
GNUNET_free (ctx);
return 0;
}
if (peer == NULL)
{
/* signal error */
- ctx->cb (ctx->cb_cls,
- _("Failed to receive `HELLO' from peer\n"));
+ if (NULL != ctx->cb)
+ ctx->cb (ctx->cb_cls,
+ _("Failed to receive `HELLO' from peer\n"));
GNUNET_TRANSPORT_disconnect (ctx->d1th);
GNUNET_TRANSPORT_disconnect (ctx->d2th);
GNUNET_free (ctx);
if ( (d1->server == NULL) ||
(d2->server == NULL) )
{
- cb (cb_cls, _("Peers are not fully running yet, can not connect!\n"));
+ if (NULL != cb)
+ cb (cb_cls, _("Peers are not fully running yet, can not connect!\n"));
return;
}
ctx = GNUNET_malloc (sizeof(struct ConnectContext));
if (ctx->d1th == NULL)
{
GNUNET_free (ctx);
- cb (cb_cls, _("Failed to connect to transport service!\n"));
+ if (NULL != cb)
+ cb (cb_cls, _("Failed to connect to transport service!\n"));
return;
}
ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched, d2->cfg, d2, NULL, NULL, NULL);
{
GNUNET_TRANSPORT_disconnect (ctx->d1th);
GNUNET_free (ctx);
- cb (cb_cls, _("Failed to connect to transport service!\n"));
+ if (NULL != cb)
+ cb (cb_cls, _("Failed to connect to transport service!\n"));
return;
}
GNUNET_TRANSPORT_get_hello (ctx->d1th,
#include "gnunet_arm_service.h"
#include "gnunet_testing_lib.h"
+/**
+ * Lowest port used for GNUnet testing. Should be high enough to not
+ * conflict with other applications running on the hosts but be low
+ * enough to not conflict with client-ports (typically starting around
+ * 32k).
+ */
+#define LOW_PORT 10000
+
+/**
+ * Highest port used for GNUnet testing. Should be low enough to not
+ * conflict with the port range for "local" ports (client apps; see
+ * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
+ */
+#define HIGH_PORT 32000
+
+/**
+ * Data we keep per peer.
+ */
+struct PeerData
+{
+ /**
+ * (Initial) configuration of the host.
+ * (initial because clients could change
+ * it and we would not know about those
+ * updates).
+ */
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Handle for controlling the daemon.
+ */
+ struct GNUNET_TESTING_Daemon *daemon;
+};
+
+
+/**
+ * Data we keep per host.
+ */
+struct HostData
+{
+ /**
+ * Name of the host.
+ */
+ char *hostname;
+
+ /**
+ * Lowest port that we have not yet used
+ * for GNUnet.
+ */
+ uint16_t minport;
+};
+
/**
* Handle to a group of GNUnet peers.
/**
* Configuration template.
*/
- struct GNUNET_CONFIGURATION_Handle *cfg;
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
/**
* Function to call on each started daemon.
void *cb_cls;
/**
- * NULL-terminated array of hostnames.
+ * NULL-terminated array of information about
+ * hosts.
*/
- char **hostnames;
+ struct HostData *hosts;
/**
* Array of "total" peers.
*/
- struct GNUNET_TESTING_Daemon **peers;
+ struct PeerData *peers;
/**
* Number of peers in this group.
};
+struct UpdateContext
+{
+ struct GNUNET_CONFIGURATION_Handle *ret;
+ unsigned int nport;
+};
+
/**
- * Start count gnunetd processes with the same set of transports and
- * applications. The port numbers (any option called "PORT") will be
- * adjusted to ensure that no two peers running on the same system
- * have the same port(s) in their respective configurations.
+ * Function to iterate over options. Copies
+ * the options to the target configuration,
+ * updating PORT values as needed.
*
- * @param sched scheduler to use
- * @param cfg configuration template to use
- * @param total number of daemons to start
- * @param cb function to call on each daemon that was started
- * @param cb_cls closure for cb
- * @param hostname where to run the peers; can be NULL (to run
- * everything on localhost).
- * @param va Additional hosts can be specified using a NULL-terminated list of
- * varargs, hosts will then be used round-robin from that
- * list; va only contains anything if hostname != NULL.
- * @return NULL on error, otherwise handle to control peer group
+ * @param cls closure
+ * @param section name of the section
+ * @param option name of the option
+ * @param value value of the option
*/
-struct GNUNET_TESTING_PeerGroup *
-GNUNET_TESTING_daemons_start_va (struct GNUNET_SCHEDULER_Handle *sched,
- const struct GNUNET_CONFIGURATION_Handle *cfg,
- unsigned int total,
- GNUNET_TESTING_NotifyDaemonRunning cb,
- void *cb_cls,
- const char *hostname,
- va_list va)
+static void
+update_config(void *cls,
+ const char *section,
+ const char *option,
+ const char *value)
{
- struct GNUNET_TESTING_PeerGroup *pg;
-
- pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup));
- return pg;
+ struct UpdateContext *ctx = cls;
+ unsigned int ival;
+ char cval[12];
+
+ if ( (0 == strcmp (option, "PORT")) &&
+ (1 == sscanf (value, "%u", &ival)) )
+ {
+ GNUNET_snprintf (cval,
+ sizeof(cval),
+ "%u",
+ ctx->nport++);
+ value = cval;
+ }
+ GNUNET_CONFIGURATION_set_value_string (ctx->ret,
+ section,
+ option,
+ value);
}
/**
- * Start count gnunetd processes with the same set of
- * transports and applications. The port numbers will
- * be computed by adding delta each time (zero
- * times for the first peer).
+ * Create a new configuration using the given configuration
+ * as a template; however, each PORT in the existing cfg
+ * must be renumbered by incrementing "*port". If we run
+ * out of "*port" numbers, return NULL.
+ *
+ * @param cfg template configuration
+ * @param port port numbers to use, update to reflect
+ * port numbers that were used
+ * @return new configuration, NULL on error
+ */
+static struct GNUNET_CONFIGURATION_Handle*
+make_config (const struct GNUNET_CONFIGURATION_Handle*cfg,
+ uint16_t *port)
+{
+ struct UpdateContext uc;
+ uint16_t orig;
+
+ orig = *port;
+ uc.nport = *port;
+ uc.ret = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_iterate (cfg,
+ &update_config,
+ &uc);
+ if (uc.nport >= HIGH_PORT)
+ {
+ *port = orig;
+ GNUNET_CONFIGURATION_destroy (uc.ret);
+ return NULL;
+ }
+ *port = (uint16_t) uc.nport;
+ return uc.ret;
+}
+
+
+/**
+ * Start count gnunetd processes with the same set of transports and
+ * applications. The port numbers (any option called "PORT") will be
+ * adjusted to ensure that no two peers running on the same system
+ * have the same port(s) in their respective configurations.
*
* @param sched scheduler to use
* @param cfg configuration template to use
* @param total number of daemons to start
- * @param timeout how long is this allowed to take?
* @param cb function to call on each daemon that was started
* @param cb_cls closure for cb
- * @param hostname where to run the peers; can be NULL (to run
- * everything on localhost). Additional
- * hosts can be specified using a NULL-terminated list of
- * varargs, hosts will then be used round-robin from that
- * list.
+ * @param hostnames space-separated list of hostnames to use; can be NULL (to run
+ * everything on localhost).
* @return NULL on error, otherwise handle to control peer group
*/
struct GNUNET_TESTING_PeerGroup *
GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
- struct GNUNET_CONFIGURATION_Handle *cfg,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
unsigned int total,
GNUNET_TESTING_NotifyDaemonRunning cb,
void *cb_cls,
- const char *hostname,
- ...)
+ const char *hostnames)
{
- struct GNUNET_TESTING_PeerGroup * ret;
- va_list va;
-
- va_start (va, hostname);
- ret = GNUNET_TESTING_daemons_start_va (sched, cfg,
- total, cb, cb_cls, hostname,
- va);
- va_end (va);
- return ret;
-}
+ struct GNUNET_TESTING_PeerGroup *pg;
+ const char *rpos;
+ char *pos;
+ char *start;
+ const char *hostname;
+ struct GNUNET_CONFIGURATION_Handle *pcfg;
+ unsigned int off;
+ unsigned int hostcnt;
+ uint16_t minport;
+ if (0 == total)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup));
+ pg->sched = sched;
+ pg->cfg = cfg;
+ pg->cb = cb;
+ pg->cb_cls = cb_cls;
+ pg->total = total;
+ pg->peers = GNUNET_malloc (total * sizeof(struct PeerData));
+ if (NULL != hostnames)
+ {
+ off = 2;
+ /* skip leading spaces */
+ while ( (0 != *hostnames) &&
+ (isspace(*hostnames)))
+ hostnames++;
+ rpos = hostnames;
+ while ('\0' != *rpos)
+ {
+ if (isspace (*rpos))
+ off++;
+ rpos++;
+ }
+ pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
+ off = 0;
+ start = GNUNET_strdup (hostnames);
+ pos = start;
+ while ('\0' != *pos)
+ {
+ if (isspace (*pos))
+ {
+ *pos = '\0';
+ if (strlen(start) > 0)
+ {
+ pg->hosts[off].minport = LOW_PORT;
+ pg->hosts[off++].hostname = start;
+ }
+ start = pos+1;
+ }
+ pos++;
+ }
+ if (strlen(start) > 0)
+ {
+ pg->hosts[off].minport = LOW_PORT;
+ pg->hosts[off++].hostname = start;
+ }
+ if (off == 0)
+ {
+ GNUNET_free (start);
+ GNUNET_free (pg->hosts);
+ pg->hosts = NULL;
+ }
+ hostcnt = off;
+ minport = 0; /* make gcc happy */
+ }
+ else
+ {
+ hostcnt = 0;
+ minport = LOW_PORT;
+ }
+ for (off = 0; off < total; off++)
+ {
+ if (hostcnt > 0)
+ {
+ hostname = pg->hosts[off % hostcnt].hostname;
+ pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport);
+ }
+ else
+ {
+ hostname = NULL;
+ pcfg = make_config (cfg, &minport);
+ }
+ if (NULL == pcfg)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Could not create configuration for peer number %u on `%s'!\n"),
+ off,
+ hostname == NULL ? "localhost" : hostname);
+ continue;
+ }
+ pg->peers[off].cfg = pcfg;
+ pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
+ pcfg,
+ hostname,
+ cb,
+ cb_cls);
+ if (NULL == pg->peers[off].daemon)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Could not start peer number %u!\n"),
+ off);
+ }
+ return pg;
+}
/**
void
GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
{
-
+ unsigned int off;
+
+ for (off = 0; off < pg->total; off++)
+ {
+ /* FIXME: should we wait for our
+ continuations to be called here? This
+ would require us to take a continuation
+ as well... */
+ if (NULL != pg->peers[off].daemon)
+ GNUNET_TESTING_daemon_stop (pg->peers[off].daemon,
+ NULL, NULL);
+ if (NULL != pg->peers[off].cfg)
+ GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
+ }
+ GNUNET_free (pg->peers);
+ if (NULL != pg->hosts)
+ {
+ GNUNET_free (pg->hosts[0].hostname);
+ GNUNET_free (pg->hosts);
+ }
GNUNET_free (pg);
}
&have_address);
if (GNUNET_NO == have_address)
return; /* no point in advertising this one... */
- GNUNET_HELLO_get_id (hello, &pid);
+ GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
pos = hellos;
while (pos != NULL)
{
unsigned int entries_found;
struct PeerList *fl;
- fn = NULL;
- GNUNET_CONFIGURATION_get_value_filename (cfg,
- "TOPOLOGY",
- "FRIENDS",
- &fn);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg,
+ "TOPOLOGY",
+ "FRIENDS",
+ &fn))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Option `%s' in section `%s' not specified!\n"),
+ "FRIENDS",
+ "TOPOLOGY");
+ return;
+ }
if (GNUNET_OK != GNUNET_DISK_file_test (fn))
GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
| GNUNET_DISK_PERM_USER_WRITE);
if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
{
/* time to discard... */
- if (prev == NULL)
+ if (prev != NULL)
prev->next = next;
else
hellos = next;
friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
"TOPOLOGY",
"FRIENDS-ONLY");
- opt = 0;
- GNUNET_CONFIGURATION_get_value_number (cfg,
- "TOPOLOGY",
- "MINIMUM-FRIENDS",
- &opt);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "TOPOLOGY",
+ "MINIMUM-FRIENDS",
+ &opt))
+ opt = 0;
minimum_friend_count = (unsigned int) opt;
- opt = 16;
- GNUNET_CONFIGURATION_get_value_number (cfg,
- "TOPOLOGY",
- "TARGET-CONNECTION-COUNT",
- &opt);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg,
+ "TOPOLOGY",
+ "TARGET-CONNECTION-COUNT",
+ &opt))
+ opt = 16;
target_connection_count = (unsigned int) opt;
if ( (friends_only == GNUNET_YES) ||
_
("Dropping incoming message due to repeated bandwidth quota violations.\n"));
/* TODO: call stats */
- GNUNET_assert (NULL != service_context->neighbour);
+ GNUNET_assert ( (service_context == NULL) ||
+ (NULL != service_context->neighbour) );
return service_context;
}
switch (ntohs (message->type))
}
+void GNUNET_CONFIGURATION_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GNUNET_CONFIGURATION_Iterator iter,
+ void *iter_cls)
+{
+ struct ConfigSection *spos;
+ struct ConfigEntry *epos;
+
+ spos = cfg->sections;
+ while (spos != NULL)
+ {
+ epos = spos->entries;
+ while (epos != NULL)
+ {
+ iter (iter_cls, spos->name, epos->key, epos->val);
+ epos = epos->next;
+ }
+ spos = spos->next;
+ }
+}
+
+
+static void
+copy_entry (void *cls,
+ const char *section,
+ const char *option,
+ const char *value)
+{
+ struct GNUNET_CONFIGURATION_Handle *dst = cls;
+ GNUNET_CONFIGURATION_set_value_string (dst, section, option, value);
+}
+
+
+struct GNUNET_CONFIGURATION_Handle *
+GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_CONFIGURATION_Handle *ret;
+
+ ret = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_iterate (cfg, ©_entry, ret);
+ return ret;
+}
+
+
static struct ConfigSection *
findSection (const struct GNUNET_CONFIGURATION_Handle *data, const char *section)
{
if (!EXTRACTOR_isBinaryType (md->items[i].type))
{
if ((iterator != NULL) &&
- (GNUNET_OK != iterator (md->items[i].type,
- md->items[i].data, closure)))
+ (GNUNET_OK != iterator (closure,
+ md->items[i].type,
+ md->items[i].data)))
return GNUNET_SYSERR;
}
else
{
if (genproc != 0)
{
- PLIBC_KILL(genproc, SIGTERM);
+ if (0 != PLIBC_KILL(genproc, SIGTERM))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "kill");
GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
genproc = 0;
}
GNUNET_break (0);
return;
}
- PLIBC_KILL(genproc, SIGTERM);
+ if (0 != PLIBC_KILL(genproc, SIGTERM))
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "kill");
GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
genproc = 0;
}
GNUNET_free (fn);
return NULL;
}
- CLOSE (fd);
+ if (0 != CLOSE (fd))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
+ "close",
+ fn);
return fn;
}
else
{
GNUNET_break (0);
+ GNUNET_free (expfn);
return NULL;
}
if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)