#
# add_file "globish.cc"
#
# add_file "globish.hh"
#
# patch "ChangeLog"
# from [f15c2359c3a9d16158dcac9119c14fd4b68d8194]
# to [81eece5640970d2c6a838995ff56ed60e0868cd9]
#
# patch "Makefile.am"
# from [f2cebcb5b9a6cf9ca254765464af8cccc11da225]
# to [cbd9fbc3d73a88f56a538bc5050a2ce3a2ffee31]
#
# patch "commands.cc"
# from [f9e412756872786e2b1e29b7628331844712d796]
# to [c9dd7afdabd028f1a9ec18da9aa1a9de1670765d]
#
# patch "globish.cc"
# from []
# to [6ae57bf040ce47674abb56f2e0c764f994d5ee62]
#
# patch "globish.hh"
# from []
# to [bcbaf2efddd720efb786f5f2843890071e42e3d8]
#
# patch "lua.cc"
# from [e4dd123e0c07b59361555b521f3b2cc17aa445b5]
# to [a8af56ad7fe0d9baa85c265b05dd94877a806c19]
#
# patch "lua.hh"
# from [0c4357742dc32474757a7905380b407e532563f4]
# to [0c4f6966a60230b591f07d0a0519485bf451a349]
#
# patch "monotone.1"
# from [8b3699fb3547458585d9e9acb48f4baf5fd0b6fa]
# to [da6b32b820606a2fa282d47374330c0ca455ffb8]
#
# patch "monotone.texi"
# from [131933a2c2cd95c13bf8e3b8c988b8e4bc97664d]
# to [18fdc4eac00664cacf2ca7e151a07c8ece75a479]
#
# patch "netcmd.cc"
# from [f1f30a4a9655a95d2007a309a0d4a618229dcdca]
# to [b8f2bfffbb6ebefdf274ea1d5ac59b58cd837671]
#
# patch "netcmd.hh"
# from [f5565d11c6b2db20139d0aab07a5b8bfe37b3cd8]
# to [f6262bdd5719defdd5ef6237e090687701ef4006]
#
# patch "netsync.cc"
# from [8cd3b5995624e0fe9d57a9954b88aef6944474f0]
# to [f1056e86785d18c2a7da04c76086fd3c5106ab9c]
#
# patch "netsync.hh"
# from [758b6ef261dc3d69e99e4fbcd79571e89749c9f4]
# to [276fdfd34e38b36adc1a45d012792fd43db07a2c]
#
# patch "tests/t_netsync_defaults.at"
# from [c041c4c1f93c04041b0fb53257e56c4b3b24a2cf]
# to [ad699c5f7470abec2b97ac1b4d0f71250d134d25]
#
# patch "tests/t_netsync_permissions.at"
# from [73b0366351e254dd0c272fc9d01c04d2b5fba795]
# to [fb766246587fa8cf19f1b2952ec63be65ad82b63]
#
# patch "tests/t_netsync_single.at"
# from [10e65a3f83698c36866333909f01cbdb6915efe3]
# to [b3d48b1881c63ccb549d37726ae2d7144a856820]
#
# patch "testsuite.at"
# from [2e17e62213b9878d08d7c792f244b6e193498618]
# to [e6acd9db25c55028d15b0e56f32214a94e55b4fc]
#
# patch "transforms.cc"
# from [041db043a6e340a55705de868e77a0fad8b5a413]
# to [589f2e927da5c33a2adb9bfea8f9b70d041ce212]
#
# patch "transforms.hh"
# from [10da66c1dec345841caf96a4c7bc2291b7d3ecb3]
# to [9f9adf9a36619e493cceea57d78e6c4cc456598d]
#
# patch "unit_tests.cc"
# from [230b4227d08bed323a2625f593073427bca9fe9b]
# to [99e4b00f7e1071461ac6d79372edda008bb5b182]
#
# patch "unit_tests.hh"
# from [b4b20ac190077a08db547eb1dcd79f40c58b2ebc]
# to [340f8c9e449facd6744cf6b926367fbd98bbb062]
#
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,98 @@
+2005-07-04 Nathaniel Smith
+
+ * tests/t_netsync_defaults.at: Update for new var names. All
+ tests now pass.
+
+2005-07-04 Nathaniel Smith
+
+ * lua.cc (hook_get_netsync_write_permitted): Fix typo.
+
+2005-07-04 Nathaniel Smith
+
+ * globish.cc (globish_matcher_test): Add check for {foo} (no
+ commas).
+
+2005-07-04 Nathaniel Smith
+
+ * globish.cc (checked_globish_to_regex): Make the special case for
+ the empty pattern, actually work. Unit tests now pass.
+
+2005-07-04 Nathaniel Smith
+
+ * netcmd.cc (test_netcmd_functions): Update for new anonymous/auth
+ packet formats.
+
+2005-07-04 Nathaniel Smith
+
+ * monotone.texi, monotone.1: Update for new glob stuff.
+ * commands.cc (process_netsync_args, push, pull, sync, serve):
+ 'serve' always requires arguments, rather than falling back on db
+ defaults.
+
+2005-07-04 Nathaniel Smith
+
+ * commands.cc (process_netsync_args, push, pull, sync, serve):
+ Adapt for patterns instead of regexen; slight refactoring too.
+
+2005-07-03 Nathaniel Smith
+
+ * netsync.cc: Finally self-consistent.
+
+2005-07-03 Nathaniel Smith
+
+ * netsync.hh (run_netsync_protocol): Fix prototype.
+
+2005-07-03 Nathaniel Smith
+
+ * globish.hh: Document the empty pattern as never matching.
+ * globish.cc (checked_globish_to_regex): Implement it.
+ (globish_matcher_test): Check it.
+
+2005-07-03 Nathaniel Smith
+
+ * monotone.texi (Network Service, Hooks):
+ * testsuite.at:
+ * tests/t_netsync_permissions.at:
+ * tests/t_netsync_single.at: Update to match new
+ get_netsync_write_permitted definition.
+
+2005-07-03 Nathaniel Smith
+
+ * lua.{cc,hh} (hook_get_netsync_write_permitted): Don't take a
+ branch argument; write permission is now all or none. (It really
+ was before anyway...)
+ * netsync.cc: Update accordingly.
+
+2005-07-03 Nathaniel Smith
+
+ * netsync.cc: More updating for pattern stuff; getting there...
+
+2005-06-28 Nathaniel Smith
+
+ * netsync.cc: Update low-level functions to use include_pattern
+ and exclude_pattern.
+
+2005-06-28 Nathaniel Smith
+
+ * netcmd.{cc,hh} (read_anonymous_cmd, write_anonymous_cmd)
+ (read_auth_cmd, write_auth_cmd): Take include_pattern and
+ exclude_pattern arguments.
+
+2005-06-28 Nathaniel Smith
+
+ * globish.{cc,hh}: New files.
+ * Makefile.am (MOST_SOURCES): Add them.
+ * transforms.{cc,hh}: Remove glob-related stuff.
+ * unit_tests.{cc,hh}: Call globish unit tests.
+
+2005-06-27 Nathaniel Smith
+
+ * transforms.cc (glob_to_regex, globs_to_regex, regexes_to_regex):
+ Choose "regex" as standard spelling. Clean up code, add code for
+ handling sets, start improving tests (don't currently pass).
+ * transforms.hh (glob_to_regex, globs_to_regex, regexes_to_regex):
+ Prototype.
+
2005-06-28 Matt Johnston
* constants.cc: increase db_version_cache_sz to 7 MB
--- Makefile.am
+++ Makefile.am
@@ -41,6 +41,7 @@
annotate.cc annotate.hh \
restrictions.cc restrictions.hh \
hmac.cc hmac.hh \
+ globish.cc globish.hh \
\
cleanup.hh unit_tests.hh interner.hh \
cycle_detector.hh randomfile.hh adler32.hh quick_alloc.hh \
--- commands.cc
+++ commands.cc
@@ -49,6 +49,7 @@
#include "selectors.hh"
#include "annotate.hh"
#include "options.hh"
+#include "globish.hh"
//
// this file defines the task-oriented "top level" commands which can be
@@ -1976,22 +1977,23 @@
static const var_key default_server_key(var_domain("database"),
var_name("default-server"));
-static const var_key default_pattern_key(var_domain("database"),
- var_name("default-pattern"));
+static const var_key default_include_pattern_key(var_domain("database"),
+ var_name("default-include-pattern"));
+static const var_key default_exclude_pattern_key(var_domain("database"),
+ var_name("default-exclude-pattern"));
static void
-process_netsync_client_args(std::string const & name,
- std::vector const & args,
- utf8 & addr, std::vector & patterns,
- app_state & app)
+process_netsync_args(std::string const & name,
+ std::vector const & args,
+ utf8 & addr,
+ utf8 & include_pattern, utf8 & exclude_pattern,
+ bool use_defaults,
+ app_state & app)
{
- if (args.size() > 2)
- throw usage(name);
-
if (args.size() >= 1)
{
addr = idx(args, 0);
- if (!app.db.var_exists(default_server_key))
+ if (use_defaults && !app.db.var_exists(default_server_key))
{
P(F("setting default server to %s\n") % addr);
app.db.set_var(default_server_key, var_value(addr()));
@@ -1999,6 +2001,7 @@
}
else
{
+ N(use_defaults, F("no hostname given"));
N(app.db.var_exists(default_server_key),
F("no server given and no default server set"));
var_value addr_value;
@@ -2006,74 +2009,73 @@
addr = utf8(addr_value());
L(F("using default server address: %s\n") % addr);
}
- // NB: even though the netsync code wants a vector of patterns, in fact
- // this only works for the server; when we're a client, our vector must have
- // length exactly 1.
- utf8 pattern;
if (args.size() >= 2)
{
- pattern = idx(args, 1);
- if (!app.db.var_exists(default_pattern_key))
+ std::set patterns(args.begin() + 1, args.end());
+ combine_and_check_globish(patterns, include_pattern);
+ if (use_defaults && !app.db.var_exists(default_include_pattern_key))
{
- P(F("setting default regex pattern to %s\n") % pattern);
- app.db.set_var(default_pattern_key, var_value(pattern()));
+ P(F("setting default branch pattern to %s\n") % include_pattern);
+ app.db.set_var(default_include_pattern_key, var_value(include_pattern()));
}
}
else
{
- N(app.db.var_exists(default_pattern_key),
- F("no regex pattern given and no default pattern set"));
+ N(use_defaults, F("no branch pattern given"));
+ N(app.db.var_exists(default_include_pattern_key),
+ F("no branch pattern given and no default pattern set"));
var_value pattern_value;
- app.db.get_var(default_pattern_key, pattern_value);
- pattern = utf8(pattern_value());
- L(F("using default regex pattern: %s\n") % pattern);
+ app.db.get_var(default_include_pattern_key, pattern_value);
+ include_pattern = utf8(pattern_value());
+ L(F("using default branch pattern: %s\n") % include_pattern);
}
- patterns.push_back(pattern);
+
+ // For now, don't handle excludes.
+ exclude_pattern = utf8("");
}
-CMD(push, "network", "[ADDRESS[:PORTNUMBER] [REGEX]]",
+CMD(push, "network", "[ADDRESS[:PORTNUMBER] [PATTERN]]",
"push branches matching REGEX to netsync server at ADDRESS", OPT_NONE)
{
- utf8 addr;
- vector patterns;
- process_netsync_client_args(name, args, addr, patterns, app);
+ utf8 addr, include_pattern, exclude_pattern;
+ process_netsync_args(name, args, addr, include_pattern, exclude_pattern, true, app);
rsa_keypair_id key;
N(guess_default_key(key, app), F("could not guess default signing key"));
app.signing_key = key;
- run_netsync_protocol(client_voice, source_role, addr, patterns, app);
+ run_netsync_protocol(client_voice, source_role, addr,
+ include_pattern, exclude_pattern, app);
}
-CMD(pull, "network", "[ADDRESS[:PORTNUMBER] [REGEX]]",
+CMD(pull, "network", "[ADDRESS[:PORTNUMBER] [PATTERN]]",
"pull branches matching REGEX from netsync server at ADDRESS", OPT_NONE)
{
- utf8 addr;
- vector patterns;
- process_netsync_client_args(name, args, addr, patterns, app);
+ utf8 addr, include_pattern, exclude_pattern;
+ process_netsync_args(name, args, addr, include_pattern, exclude_pattern, true, app);
if (app.signing_key() == "")
W(F("doing anonymous pull\n"));
- run_netsync_protocol(client_voice, sink_role, addr, patterns, app);
+ run_netsync_protocol(client_voice, sink_role, addr,
+ include_pattern, exclude_pattern, app);
}
-CMD(sync, "network", "[ADDRESS[:PORTNUMBER] [REGEX]]",
+CMD(sync, "network", "[ADDRESS[:PORTNUMBER] [PATTERN]]",
"sync branches matching REGEX with netsync server at ADDRESS", OPT_NONE)
{
- utf8 addr;
- vector patterns;
- process_netsync_client_args(name, args, addr, patterns, app);
+ utf8 addr, include_pattern, exclude_pattern;
+ process_netsync_args(name, args, addr, include_pattern, exclude_pattern, true, app);
rsa_keypair_id key;
N(guess_default_key(key, app), F("could not guess default signing key"));
app.signing_key = key;
- run_netsync_protocol(client_voice, source_and_sink_role, addr, patterns,
- app);
+ run_netsync_protocol(client_voice, source_and_sink_role, addr,
+ include_pattern, exclude_pattern, app);
}
-CMD(serve, "network", "ADDRESS[:PORTNUMBER] REGEX ...",
+CMD(serve, "network", "ADDRESS[:PORTNUMBER] PATTERN ...",
"listen on ADDRESS and serve the specified branches to connecting clients", OPT_PIDFILE)
{
if (args.size() < 2)
@@ -2089,9 +2091,10 @@
F("need permission to store persistent passphrase (see hook persist_phrase_ok())"));
require_password(key, app);
- utf8 addr(idx(args,0));
- vector patterns(args.begin() + 1, args.end());
- run_netsync_protocol(server_voice, source_and_sink_role, addr, patterns, app);
+ utf8 addr, include_pattern, exclude_pattern;
+ process_netsync_args(name, args, addr, include_pattern, exclude_pattern, false, app);
+ run_netsync_protocol(server_voice, source_and_sink_role, addr,
+ include_pattern, exclude_pattern, app);
}
--- globish.cc
+++ globish.cc
@@ -0,0 +1,227 @@
+// copyright (C) 2005 Richard Levitte
+// copyright (C) 2005 nathaniel smith
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include "sanity.hh"
+#include "globish.hh"
+
+// this converts a globish pattern to a regex. The regex should be usable by
+// the Boost regex library operating in default mode, i.e., it should be a
+// valid ECMAscript regex.
+//
+// Pattern tranformation:
+//
+// - As a special case, the empty pattern is translated to "$.^", which cannot
+// match any string.
+//
+// - Any character except those described below are copied as they are.
+// - The backslash (\) escapes the following character. The escaping
+// backslash is copied to the regex along with the following character.
+// - * is transformed to .* in the regex.
+// - ? is transformed to . in the regex.
+// - { is transformed to ( in the regex
+// - } is transformed to ) in the regex
+// - , is transformed to | in the regex, if within { and }
+// - ^ is escaped unless it comes directly after an unescaped [.
+// - ! is transformed to ^ in the regex if it comes directly after an
+// unescaped [.
+// - ] directly following an unescaped [ is escaped.
+static void
+maybe_quote(char c, std::string & re)
+{
+ if (!(isalnum(c) || c == '_'))
+ {
+ re += '\\';
+ }
+ re += c;
+}
+
+static void
+checked_globish_to_regex(std::string const & glob, std::string & regex)
+{
+ int in_braces = 0; // counter for levels if {}
+
+ regex.clear();
+ regex.reserve(glob.size() * 2);
+
+ L(F("checked_globish_to_regex: input = '%s'\n") % glob);
+
+ if (glob == "")
+ {
+ regex = "$.^";
+ // and the below loop will do nothing
+ }
+ for (std::string::const_iterator i = glob.begin(); i != glob.end(); ++i)
+ {
+ char c = *i;
+
+ N(in_braces < 5, F("braces nested too deep in pattern '%s'") % glob);
+
+ switch(c)
+ {
+ case '*':
+ regex += ".*";
+ break;
+ case '?':
+ regex += '.';
+ break;
+ case '{':
+ in_braces++;
+ regex += '(';
+ break;
+ case '}':
+ N(in_braces != 0,
+ F("trying to end a brace expression in a glob when none is started"));
+ regex += ')';
+ in_braces--;
+ break;
+ case ',':
+ if (in_braces > 0)
+ regex += '|';
+ else
+ maybe_quote(c, regex);
+ break;
+ case '\\':
+ N(++i != glob.end(), F("pattern '%s' ends with backslash") % glob);
+ maybe_quote(*i, regex);
+ break;
+ default:
+ maybe_quote(c, regex);
+ break;
+ }
+ }
+
+ N(in_braces == 0,
+ F("run-away brace expression in pattern '%s'") % glob);
+
+ L(F("checked_globish_to_regex: output = '%s'\n") % regex);
+}
+
+void
+combine_and_check_globish(std::set const &patterns, utf8 & pattern)
+{
+ std::string p;
+ p += '{';
+ bool first = true;
+ for (std::set::const_iterator i = patterns.begin(); i != patterns.end(); ++i)
+ {
+ std::string tmp;
+ // run for the checking it does
+ checked_globish_to_regex((*i)(), tmp);
+ if (!first)
+ p += ',';
+ first = false;
+ p += (*i)();
+ }
+ p += '}';
+ pattern = utf8(p);
+}
+
+globish_matcher::globish_matcher(utf8 const & include_pat, utf8 const & exclude_pat)
+{
+ std::string re;
+ checked_globish_to_regex(include_pat(), re);
+ r_inc = re;
+ checked_globish_to_regex(exclude_pat(), re);
+ r_exc = re;
+}
+
+bool
+globish_matcher::operator()(std::string const & s)
+{
+ // regex_match may throw a std::runtime_error, if the regex turns out to be
+ // really pathological
+ return boost::regex_match(s, r_inc) && !boost::regex_match(s, r_exc);
+}
+
+#ifdef BUILD_UNIT_TESTS
+#include "unit_tests.hh"
+
+static void
+checked_globish_to_regex_test()
+{
+ std::string pat;
+
+ checked_globish_to_regex("*", pat);
+ BOOST_CHECK(pat == ".*");
+ checked_globish_to_regex("?", pat);
+ BOOST_CHECK(pat == ".");
+ checked_globish_to_regex("{a,b,c}d", pat);
+ BOOST_CHECK(pat == "(a|b|c)d");
+ checked_globish_to_regex("foo{a,{b,c},?*}d", pat);
+ BOOST_CHECK(pat == "foo(a|(b|c)|..*)d");
+ checked_globish_to_regex("\\a\\b\\|\\{\\*", pat);
+ BOOST_CHECK(pat == "ab\\|\\{\\*");
+ checked_globish_to_regex(".+$^{}", pat);
+ BOOST_CHECK(pat == "\\.\\+\\$\\^()");
+ checked_globish_to_regex(",", pat);
+ // we're very conservative about metacharacters, and quote all
+ // non-alphanumerics, hence the backslash
+ BOOST_CHECK(pat == "\\,");
+
+ BOOST_CHECK_THROW(checked_globish_to_regex("foo\\", pat), informative_failure);
+ BOOST_CHECK_THROW(checked_globish_to_regex("{foo", pat), informative_failure);
+ BOOST_CHECK_THROW(checked_globish_to_regex("{foo,bar{baz,quux}", pat), informative_failure);
+ BOOST_CHECK_THROW(checked_globish_to_regex("foo}", pat), informative_failure);
+ BOOST_CHECK_THROW(checked_globish_to_regex("foo,bar{baz,quux}}", pat), informative_failure);
+ BOOST_CHECK_THROW(checked_globish_to_regex("{{{{{{{{{{a,b},c},d},e},f},g},h},i},j},k}", pat), informative_failure);
+}
+
+static void
+combine_and_check_globish_test()
+{
+ std::set s;
+ s.insert(utf8("a"));
+ s.insert(utf8("b"));
+ s.insert(utf8("c"));
+ utf8 combined;
+ combine_and_check_globish(s, combined);
+ BOOST_CHECK(combined() == "{a,b,c}");
+}
+
+static void
+globish_matcher_test()
+{
+ {
+ globish_matcher m(utf8("{a,b}?*\\*|"), utf8("*c*"));
+ BOOST_CHECK(m("aq*|"));
+ BOOST_CHECK(m("bq*|"));
+ BOOST_CHECK(!m("bc*|"));
+ BOOST_CHECK(!m("bq|"));
+ BOOST_CHECK(!m("b*|"));
+ BOOST_CHECK(!m(""));
+ }
+ {
+ globish_matcher m(utf8("{a,\\\\,b*}"), utf8("*c*"));
+ BOOST_CHECK(m("a"));
+ BOOST_CHECK(!m("ab"));
+ BOOST_CHECK(m("\\"));
+ BOOST_CHECK(!m("\\\\"));
+ BOOST_CHECK(m("b"));
+ BOOST_CHECK(m("bfoobar"));
+ BOOST_CHECK(!m("bfoobarcfoobar"));
+ }
+ {
+ globish_matcher m(utf8("*"), utf8(""));
+ BOOST_CHECK(m("foo"));
+ BOOST_CHECK(m(""));
+ }
+ {
+ globish_matcher m(utf8("{foo}"), utf8(""));
+ BOOST_CHECK(m("foo"));
+ BOOST_CHECK(!m("bar"));
+ }
+}
+
+
+void add_globish_tests(test_suite * suite)
+{
+ I(suite);
+ suite->add(BOOST_TEST_CASE(&checked_globish_to_regex_test));
+ suite->add(BOOST_TEST_CASE(&combine_and_check_globish_test));
+ suite->add(BOOST_TEST_CASE(&globish_matcher_test));
+}
+
+#endif // BUILD_UNIT_TESTS
--- globish.hh
+++ globish.hh
@@ -0,0 +1,47 @@
+#ifndef __GLOBISH_HH__
+#define __GLOBISH_HH__
+
+// copyright (C) 2005 nathaniel smith
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+// a sort of glob-like pattern matcher, for use in specifying branch
+// collections for netsync. it is important that it not be too expensive to
+// match (as opposed to common regex engines, which can be exponential on
+// pathological patterns), because we must match branches against untrusted
+// patterns when doing netsync.
+
+// the syntax is:
+// most things - match themselves
+// * - match 0 or more characters
+// ? - match 0 or 1 characters
+// \ - match
+// {,,...} - match any of the given items
+// so like standard globs, except without [] character sets, and with {}
+// alternation.
+// the one strange thing is there is a special-case -- the empty pattern
+// matches nothing, not even the empty string. this hardly ever matters, but
+// it's nice to have some way to say "don't exclude anything", for instance.
+
+#include
+#include
+#include
+
+#include "vocab.hh"
+
+void combine_and_check_globish(std::set const &patterns, utf8 & pattern);
+
+class globish_matcher
+{
+public:
+ // this may throw an informative_failure if a pattern is invalid
+ globish_matcher(utf8 const & include_pat, utf8 const & exclude_pat);
+ // this method may throw a std::runtime_error if the pattern is really
+ // pathological
+ bool operator()(std::string const & s);
+private:
+ boost::regex r_inc, r_exc;
+};
+
+#endif
--- lua.cc
+++ lua.cc
@@ -738,14 +738,14 @@
lua_hooks::hook_expand_date(std::string const & sel,
std::string & exp)
{
- exp.clear();
+ exp.clear();
bool res= Lua(st)
.func("expand_date")
.push_str(sel)
.call(1,1)
.extract_str(exp)
.ok();
- return res && exp.size();
+ return res && exp.size();
}
bool
@@ -1038,14 +1038,14 @@
}
bool
-lua_hooks::hook_get_netsync_read_permitted(std::string const & pattern,
+lua_hooks::hook_get_netsync_read_permitted(std::string const & branch,
rsa_keypair_id const & identity)
{
bool permitted = false, exec_ok = false;
exec_ok = Lua(st)
.func("get_netsync_read_permitted")
- .push_str(pattern)
+ .push_str(branch)
.push_str(identity())
.call(2,1)
.extract_bool(permitted)
@@ -1055,13 +1055,13 @@
}
bool
-lua_hooks::hook_get_netsync_anonymous_read_permitted(std::string const & pattern)
+lua_hooks::hook_get_netsync_anonymous_read_permitted(std::string const & branch)
{
bool permitted = false, exec_ok = false;
exec_ok = Lua(st)
.func("get_netsync_anonymous_read_permitted")
- .push_str(pattern)
+ .push_str(branch)
.call(1,1)
.extract_bool(permitted)
.ok();
@@ -1070,16 +1070,14 @@
}
bool
-lua_hooks::hook_get_netsync_write_permitted(std::string const & pattern,
- rsa_keypair_id const & identity)
+lua_hooks::hook_get_netsync_write_permitted(rsa_keypair_id const & identity)
{
bool permitted = false, exec_ok = false;
exec_ok = Lua(st)
.func("get_netsync_write_permitted")
- .push_str(pattern)
.push_str(identity())
- .call(2,1)
+ .call(1,1)
.extract_bool(permitted)
.ok();
--- lua.hh
+++ lua.hh
@@ -62,11 +62,10 @@
std::map const & new_results);
// network hooks
- bool hook_get_netsync_read_permitted(std::string const & pattern,
+ bool hook_get_netsync_read_permitted(std::string const & branch,
rsa_keypair_id const & identity);
- bool hook_get_netsync_anonymous_read_permitted(std::string const & pattern);
- bool hook_get_netsync_write_permitted(std::string const & pattern,
- rsa_keypair_id const & identity);
+ bool hook_get_netsync_anonymous_read_permitted(std::string const & branch);
+ bool hook_get_netsync_write_permitted(rsa_keypair_id const & identity);
// local repo hooks
bool hook_ignore_file(file_path const & p);
--- monotone.1
+++ monotone.1
@@ -162,17 +162,17 @@
\fBrefresh_inodeprints\fP
Turn on inodeprints mode, and force a cache refresh.
.TP
-\fBpush\fP \fI[ []]\fP
-Push contents of \fI\fP to database on \fI\fP.
+\fBpush\fP \fI[ []]\fP
+Push contents of \fI\fP to database on \fI\fP.
.TP
-\fBpull\fP \fI[ []]\fP
-Push contents of \fI\fP from database on \fI\fP.
+\fBpull\fP \fI[ []]\fP
+Push contents of \fI\fP from database on \fI\fP.
.TP
-\fBsync\fP \fI \fP
-Sync contents of \fI\fP with database on \fI\fP.
+\fBsync\fP \fI \fP
+Sync contents of \fI\fP with database on \fI\fP.
.TP
-\fBserve\fP \fI[--pid-file=] \fP
-Serve contents of \fI\fP at network address \fI\fP. If a
+\fBserve\fP \fI[--pid-file=] \fP
+Serve contents of \fI\fP at network address \fI\fP. If a
--pid-file option is provided on the command line, monotone will store the
process id of the server in the specified file.
.TP
--- monotone.texi
+++ monotone.texi
@@ -1641,7 +1641,7 @@
return false
end
-function get_netsync_write_permitted (branch, identity)
+function get_netsync_write_permitted (identity)
if (identity == "abe@@juicebot.co.jp") then return true end
if (identity == "beth@@juicebot.co.jp") then return true end
return false
@@ -1660,22 +1660,24 @@
@smallexample
@group
-$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7.*"
+$ monotone --db=jim.db serve jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
@end group
@end smallexample
This command sets up a single listener loop on the host
@code{jim-laptop.juicebot.co.jp}, serving all branches matching
address@hidden This will naturally
-include the @code{jp.co.juicebot.jb7} branch, and any sub-branches.
address@hidden This will naturally include the
address@hidden branch, and any sub-branches. The quotes
+around @code{"jp.co.juicebot.jb7*"} are there to protect the @code{*}
+from expansion by the shell; they have no meaning to monotone.
Now Abe decides he wishes to fetch Jim's code. To do this he issues
the monotone @code{sync} command:
@smallexample
@group
-monotone --db=abe.db sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7.*"
-monotone: rebuilding merkle trees for pattern jp.co.juicebot.jb7.*
+monotone --db=abe.db sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
+monotone: rebuilding merkle trees ...
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
@@ -1820,8 +1822,8 @@
@smallexample
@group
-$ monotone sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7.*"
-monotone: rebuilding merkle trees for pattern jp.co.juicebot.jb7.*
+$ monotone sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
+monotone: rebuilding merkle trees ...
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 2] [rcerts: 8]
monotone: connecting to jim-laptop.juicebot.co.jp
@@ -1835,8 +1837,8 @@
@smallexample
@group
-$ monotone --db=beth.db sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7.*"
-monotone: rebuilding merkle trees for pattern jp.co.juicebot.jb7.*
+$ monotone --db=beth.db sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
+monotone: rebuilding merkle trees ...
monotone: connecting to jim-laptop.juicebot.co.jp
monotone: [bytes in: 3200] [bytes out: 673]
monotone: successful exchange with jim-laptop.juicebot.co.jp
@@ -1887,8 +1889,8 @@
@smallexample
@group
-$ monotone sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7.*"
-monotone: rebuilding merkle trees for pattern jp.co.juicebot.jb7.*
+$ monotone sync jim-laptop.juicebot.co.jp "jp.co.juicebot.jb7*"
+monotone: rebuilding merkle trees ...
monotone: including branch jp.co.juicebot.jb7
monotone: [keys: 3] [rcerts: 12]
monotone: connecting to jim-laptop.juicebot.co.jp
@@ -2641,9 +2643,9 @@
@item default-server
The default server for netsync operations to use. Automatically set
by first use of netsync.
address@hidden default-pattern
-The default regex pattern for netsync operations to use. Automatically
-set by first use of netsync.
address@hidden default-include-pattern
+The default branch glob pattern for netsync operations to use.
+Automatically set by first use of netsync.
@end table
@item known-servers
@@ -3047,7 +3049,7 @@
@tab
@smallexample
@group
-$ monotone pull www.foo.com com.foo.wobbler
+$ monotone pull www.foo.com com.foo.wobbler*
$ monotone checkout --revision=fe37 wobbler
@end group
@end smallexample
@@ -3056,7 +3058,7 @@
The CVS command contacts a network server, retrieves a revision, and
stores it in your working copy. There are two cosmetic differences
with the monotone command: remote databases are specified by hostnames
-and regexes, and revisions are denoted by @sc{sha1} values (or
+and globs, and revisions are denoted by @sc{sha1} values (or
selectors).
There is also one deep difference: pulling revisions into your
@@ -3080,7 +3082,7 @@
@smallexample
@group
$ monotone commit --message="log message"
-$ monotone push www.foo.com com.foo.wobbler
+$ monotone push www.foo.com com.foo.wobbler*
@end group
@end smallexample
@end multitable
@@ -3102,7 +3104,7 @@
@tab
@smallexample
@group
-$ monotone pull www.foo.com com.foo.wobbler
+$ monotone pull www.foo.com com.foo.wobbler*
$ monotone merge
$ monotone update
@end group
@@ -3653,10 +3655,10 @@
@section Network
@ftable @command
address@hidden monotone serve @var{address}[:@var{port}] @var{regex} [...]
address@hidden monotone pull address@hidden:@var{port}] address@hidden
address@hidden monotone push address@hidden:@var{port}] address@hidden
address@hidden monotone sync address@hidden:@var{port}] address@hidden
address@hidden monotone serve @var{address}[:@var{port}] @var{glob} [...]
address@hidden monotone pull address@hidden:@var{port}] address@hidden [...]]]
address@hidden monotone push address@hidden:@var{port}] address@hidden [...]]]
address@hidden monotone sync address@hidden:@var{port}] address@hidden [...]]]
These commands operate the ``netsync'' protocol built into
monotone. This is a custom protocol for rapidly synchronizing two
@@ -3666,25 +3668,25 @@
The network @var{address} specified in each case should be the same: a
host name to listen on, or connect to, optionally followed by a colon
-and a port number. The @var{regex} parameter indicates a set of
-branches to exchange; every branch which matches @var{regex} exactly
+and a port number. The @var{glob} parameters indicate a set of
+branches to exchange; every branch which matches a @var{glob} exactly
will be indexed and made available for synchronization.
-The @command{serve} command can take multiple regexes, and it will
-make available all branches matching any of the listed regexes. Different
-permissions can be applied to each branch; see the hooks
+The @command{serve} command can take multiple globs, and it will make
+available all branches matching any of them. Different permissions can
+be applied to each branch; see the hooks
@code{get_netsync_read_permitted}, @code{get_netsync_write_permitted},
-and @code{get_netsync_anonymous_read_permitted}, all of which take a
address@hidden argument (see @ref{Hook Reference}).
+and @code{get_netsync_anonymous_read_permitted} (see @ref{Hook
+Reference}).
-For example, supposing Bob and Alice wish to synchronize their
+For example, perhaps Bob and Alice wish to synchronize their
@code{net.venge.monotone.win32} and @code{net.venge.monotone.i18n}
branches. Supposing Alice's computer has hostname
@code{alice.someisp.com}, then Alice might run:
@smallexample
@group
-$ monotone serve alice.someisp.com "net.venge.monotone.*"
+$ monotone serve alice.someisp.com "net.venge.monotone*"
@end group
@end smallexample
@@ -3692,19 +3694,19 @@
@smallexample
@group
-$ monotone sync alice.someisp.com "net.venge.monotone.*"
+$ monotone sync alice.someisp.com "net.venge.monotone*"
@end group
@end smallexample
When the operation completes, all branches matching
address@hidden will be synchronized between Alice and Bob's
address@hidden will be synchronized between Alice and Bob's
databases.
The @command{pull}, @command{push}, and @command{sync} commands only
-require you pass @var{address} and @var{regex} the first time you
-use one of them; monotone will memorize this use and in the future
-default to the same server and regex. For instance, if Bob wants
-to @command{sync} with Alice again, he can simply run:
+require you pass @var{address} and @var{glob} the first time you use one
+of them; monotone will memorize this use and in the future default to
+the same server and glob. For instance, if Bob wants to @command{sync}
+with Alice again, he can simply run:
@smallexample
@group
@@ -3713,18 +3715,26 @@
@end smallexample
Of course, he can still @command{sync} with other people and other
-branches by passing an address or address plus regex on the command
+branches by passing an address or address plus globs on the command
line; this will not affect his default affinity for Alice. If you ever
do want to change your defaults, use @code{monotone unset database
-default-server} or @code{monotone unset database default-pattern};
-these will clear your defaults, and cause them to be reset to the next
-person you netsync with.
+default-server} or @code{monotone unset database
+default-include-pattern}; these will clear your defaults, and cause them
+to be reset to the next person you netsync with.
If a @option{--pid-file} option is specified, the command
@command{serve} will create the specified file and record the process
identifier of the server in the file. This file can then be read to
identify specific monotone server processes.
+The syntax for patterns is very simple. @code{*} matches 0 or more
+arbitrary characters. @code{?} matches exactly 1 arbitrary character.
address@hidden@{foo,bar,address@hidden matches ``foo'', or ``bar'', or ``baz''. These
+can be combined arbitrarily. A backslash, @code{\}, can be prefixed to
+any character, to match exactly that character --- this might be useful
+in case someone, for some odd reason, decides to put a ``*'' into their
+branch name.
+
@end ftable
@@ -5477,17 +5487,13 @@
access hook. This hook has no default definition, therefore the
default behavior is to deny all anonymous reads.
address@hidden get_netsync_write_permitted (@var{branch}, @var{identity})
address@hidden get_netsync_write_permitted (@var{identity})
-Returns @code{true} if a peer authenticated as key @var{identity}
-should be allowed to write into your database certs, revisions,
-manifests, and files associated with @var{branch}; otherwise @code{false}.
-This hook has no default definition, therefore the default behavior is to deny all writes.
+Returns @code{true} if a peer authenticated as key @var{identity} should
+be allowed to write into your database certs, revisions, manifests, and
+files; otherwise @code{false}. This hook has no default definition,
+therefore the default behavior is to deny all writes.
-Note that if write access is granted for one branch it is effectively
-granted for the entire database, as there is currently no way to
-restrict that access to only that branch.
-
Note that the @var{identity} value is a key ID (such as
address@hidden@@pobox.com}'') but will correspond to a @emph{unique}
key fingerprint (hash) in your database. Monotone will not permit two
@@ -6729,20 +6735,20 @@
@item @b{refresh_inodeprints}
Turn on inodeprints mode, and force a cache refresh.
address@hidden @b{push} @i{ }
-Push contents of branches matching @i{} to database on @i{}
address@hidden @b{push} @i{ }
+Push contents of branches matching @i{} to database on @i{}
@comment TROFF INPUT: .SH DESCRIPTION
address@hidden @b{pull} @i{ }
-Pull contents of branches matching @i{} from database on @i{}
address@hidden @b{pull} @i{ }
+Pull contents of branches matching @i{} from database on @i{}
@comment TROFF INPUT: .SH DESCRIPTION
address@hidden @b{sync} @i{ }
-Sync contents of branches matching @i{} with database on @i{}
address@hidden @b{sync} @i{ }
+Sync contents of branches matching @i{} with database on @i{}
@comment TROFF INPUT: .SH DESCRIPTION
address@hidden @b{serve} @i{ }
-Serve contents of branches matching @i{} at network address @i{}
address@hidden @b{serve} @i{ }
+Serve contents of branches matching @i{} at network address @i{}
@comment TROFF INPUT: .SH DESCRIPTION
@item @b{automate} @i{(interface_version|heads|ancestors|attributes|parents|descendents|children|graph|erase_ancestors|toposort|ancestry_difference|leaves|inventory|stdio|certs|select)}
--- netcmd.cc
+++ netcmd.cc
@@ -214,19 +214,25 @@
void
netcmd::read_anonymous_cmd(protocol_role & role,
- std::string & pattern,
+ utf8 & include_pattern,
+ utf8 & exclude_pattern,
rsa_oaep_sha_data & hmac_key_encrypted) const
{
size_t pos = 0;
- // syntax is:
+ // syntax is:
u8 role_byte = extract_datum_lsb(payload, pos, "anonymous(hmac) netcmd, role");
if (role_byte != static_cast(source_role)
&& role_byte != static_cast(sink_role)
&& role_byte != static_cast(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen(role_byte));
role = static_cast(role_byte);
- extract_variable_length_string(payload, pattern, pos,
- "anonymous(hmac) netcmd, pattern");
+ std::string pattern_string;
+ extract_variable_length_string(payload, pattern_string, pos,
+ "anonymous(hmac) netcmd, include_pattern");
+ include_pattern = utf8(pattern_string);
+ extract_variable_length_string(payload, pattern_string, pos,
+ "anonymous(hmac) netcmd, exclude_pattern");
+ exclude_pattern = utf8(pattern_string);
string hmac_key_string;
extract_variable_length_string(payload, hmac_key_string, pos,
"anonymous(hmac) netcmd, hmac_key_encrypted");
@@ -236,25 +242,28 @@
void
netcmd::write_anonymous_cmd(protocol_role role,
- std::string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
rsa_oaep_sha_data const & hmac_key_encrypted)
{
cmd_code = anonymous_cmd;
payload = static_cast(role);
- insert_variable_length_string(pattern, payload);
+ insert_variable_length_string(include_pattern(), payload);
+ insert_variable_length_string(exclude_pattern(), payload);
insert_variable_length_string(hmac_key_encrypted(), payload);
}
void
netcmd::read_auth_cmd(protocol_role & role,
- string & pattern,
+ utf8 & include_pattern,
+ utf8 & exclude_pattern,
id & client,
id & nonce1,
rsa_oaep_sha_data & hmac_key_encrypted,
string & signature) const
{
size_t pos = 0;
- // syntax is:
+ // syntax is:
//
//
u8 role_byte = extract_datum_lsb(payload, pos, "auth netcmd, role");
@@ -263,7 +272,13 @@
&& role_byte != static_cast(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen(role_byte));
role = static_cast(role_byte);
- extract_variable_length_string(payload, pattern, pos, "auth(hmac) netcmd, pattern");
+ std::string pattern_string;
+ extract_variable_length_string(payload, pattern_string, pos,
+ "auth(hmac) netcmd, include_pattern");
+ include_pattern = utf8(pattern_string);
+ extract_variable_length_string(payload, pattern_string, pos,
+ "auth(hmac) netcmd, exclude_pattern");
+ exclude_pattern = utf8(pattern_string);
client = id(extract_substring(payload, pos,
constants::merkle_hash_length_in_bytes,
"auth(hmac) netcmd, client identifier"));
@@ -281,7 +296,8 @@
void
netcmd::write_auth_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & client,
id const & nonce1,
rsa_oaep_sha_data const & hmac_key_encrypted,
@@ -291,7 +307,8 @@
I(client().size() == constants::merkle_hash_length_in_bytes);
I(nonce1().size() == constants::merkle_hash_length_in_bytes);
payload = static_cast(role);
- insert_variable_length_string(pattern, payload);
+ insert_variable_length_string(include_pattern(), payload);
+ insert_variable_length_string(exclude_pattern(), payload);
payload += client();
payload += nonce1();
insert_variable_length_string(hmac_key_encrypted(), payload);
@@ -658,12 +675,15 @@
// total cheat, since we don't actually verify that rsa_oaep_sha_data
// is sensible anywhere here...
rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
- string out_pattern("radishes galore!"), in_pattern;
+ utf8 out_include_pattern("radishes galore!"), in_include_pattern;
+ utf8 out_exclude_pattern("turnips galore!"), in_exclude_pattern;
- out_cmd.write_anonymous_cmd(out_role, out_pattern, out_key);
+ out_cmd.write_anonymous_cmd(out_role, out_include_pattern, out_exclude_pattern, out_key);
do_netcmd_roundtrip(out_cmd, in_cmd, buf);
- in_cmd.read_anonymous_cmd(in_role, in_pattern, in_key);
+ in_cmd.read_anonymous_cmd(in_role, in_include_pattern, in_exclude_pattern, in_key);
BOOST_CHECK(in_key == out_key);
+ BOOST_CHECK(in_include_pattern == out_include_pattern);
+ BOOST_CHECK(in_exclude_pattern == out_exclude_pattern);
BOOST_CHECK(in_role == out_role);
L(F("anonymous_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -679,19 +699,22 @@
// total cheat, since we don't actually verify that rsa_oaep_sha_data
// is sensible anywhere here...
rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
- string out_signature(raw_sha1("burble") + raw_sha1("gorby")), out_pattern("radishes galore!"),
- in_signature, in_pattern;
+ string out_signature(raw_sha1("burble") + raw_sha1("gorby")), in_signature;
+ utf8 out_include_pattern("radishes galore!"), in_include_pattern;
+ utf8 out_exclude_pattern("turnips galore!"), in_exclude_pattern;
- out_cmd.write_auth_cmd(out_role, out_pattern, out_client, out_nonce1,
- out_key, out_signature);
+ out_cmd.write_auth_cmd(out_role, out_include_pattern, out_exclude_pattern
+ , out_client, out_nonce1, out_key, out_signature);
do_netcmd_roundtrip(out_cmd, in_cmd, buf);
- in_cmd.read_auth_cmd(in_role, in_pattern, in_client,
- in_nonce1, in_key, in_signature);
+ in_cmd.read_auth_cmd(in_role, in_include_pattern, in_exclude_pattern,
+ in_client, in_nonce1, in_key, in_signature);
BOOST_CHECK(in_client == out_client);
BOOST_CHECK(in_nonce1 == out_nonce1);
BOOST_CHECK(in_key == out_key);
BOOST_CHECK(in_signature == out_signature);
BOOST_CHECK(in_role == out_role);
+ BOOST_CHECK(in_include_pattern == out_include_pattern);
+ BOOST_CHECK(in_exclude_pattern == out_exclude_pattern);
L(F("auth_cmd test done, buffer was %d bytes\n") % buf.size());
}
--- netcmd.hh
+++ netcmd.hh
@@ -83,20 +83,24 @@
id const & nonce);
void read_anonymous_cmd(protocol_role & role,
- std::string & pattern,
+ utf8 & include_pattern,
+ utf8 & exclude_pattern,
rsa_oaep_sha_data & hmac_key_encrypted) const;
void write_anonymous_cmd(protocol_role role,
- std::string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
rsa_oaep_sha_data const & hmac_key_encrypted);
void read_auth_cmd(protocol_role & role,
- std::string & pattern,
+ utf8 & include_pattern,
+ utf8 & exclude_pattern,
id & client,
id & nonce1,
rsa_oaep_sha_data & hmac_key_encrypted,
std::string & signature) const;
void write_auth_cmd(protocol_role role,
- std::string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & client,
id const & nonce1,
rsa_oaep_sha_data const & hmac_key_encrypted,
--- netsync.cc
+++ netsync.cc
@@ -34,6 +34,7 @@
#include "epoch.hh"
#include "platform.hh"
#include "hmac.hh"
+#include "globish.hh"
#include "cryptopp/osrng.h"
@@ -154,17 +155,18 @@
//
// The client then responds with either:
//
-// An "auth (source|sink|both)
-// " command, which identifies its RSA key, notes the role it
-// wishes to play in the synchronization, identifies the pattern it
-// wishes to sync with, signs the previous nonce with its own key, and
-// informs the server of the HMAC key it wishes to use for this
-// session (encrypted with the server's public key); or
+// An "auth (source|sink|both)
+// " command, which identifies its RSA key, notes the
+// role it wishes to play in the synchronization, identifies the pattern it
+// wishes to sync with, signs the previous nonce with its own key, and informs
+// the server of the HMAC key it wishes to use for this session (encrypted
+// with the server's public key); or
//
-// An "anonymous (source|sink|both) " command,
-// which identifies the role it wishes to play in the synchronization,
-// the pattern it ishes to sync with, and the HMAC key it wishes to
-// use for this session (also encrypted with the server's public key).
+// An "anonymous (source|sink|both)
+// " command, which identifies the role it wishes to play in the
+// synchronization, the pattern it ishes to sync with, and the HMAC key it
+// wishes to use for this session (also encrypted with the server's public
+// key).
//
// The server then replies with a "confirm" command, which contains no
// other data but will only have the correct HMAC integrity code if
@@ -172,24 +174,6 @@
// the client. This transitions the peers into an authenticated state
// and begins refinement.
//
-// ---- Pre-v5 authentication process notes ----
-//
-// the exchange begins in a non-authenticated state. the server sends a
-// "hello " command, which identifies the server's RSA key and
-// issues a nonce which must be used for a subsequent authentication.
-// the client can then respond with an "auth (source|sink|both)
-// " command which identifies its
-// RSA key, notes the role it wishes to play in the synchronization,
-// identifies the pattern it wishes to sync with, signs the previous
-// nonce with its own key, and issues a nonce of its own for mutual
-// authentication.
-//
-// the server can then respond with a "confirm " command, which is
-// the signature of the second nonce sent by the client. this
-// transitions the peers into an authenticated state and begins refinement.
-//
-// ---- End pre-v5 authentication process ----
-//
// refinement begins with the client sending its root public key and
// manifest certificate merkle nodes to the server. the server then
// compares the root to each slot in *its* root node, and for each slot
@@ -254,7 +238,9 @@
{
protocol_role role;
protocol_voice const voice;
- vector patterns;
+ utf8 const & our_include_pattern;
+ utf8 const & our_exclude_pattern;
+ globish_matcher our_matcher;
app_state & app;
string peer_id;
@@ -273,8 +259,6 @@
bool armed;
bool arm();
- utf8 pattern;
- boost::regex pattern_re;
id remote_peer_key_hash;
rsa_keypair_id remote_peer_key_name;
netsync_session_key session_key;
@@ -313,7 +297,8 @@
session(protocol_role role,
protocol_voice voice,
- vector const & patterns,
+ utf8 const & our_include_pattern,
+ utf8 const & our_exclude_pattern,
app_state & app,
string const & peer,
Netxx::socket_type sock,
@@ -373,11 +358,13 @@
void queue_hello_cmd(id const & server,
id const & nonce);
void queue_anonymous_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & nonce2,
base64 server_key_encoded);
void queue_auth_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & client,
id const & nonce1,
id const & nonce2,
@@ -407,9 +394,11 @@
rsa_pub_key const & server_key,
id const & nonce);
bool process_anonymous_cmd(protocol_role role,
- string const & pattern);
+ utf8 const & their_include_pattern,
+ utf8 const & their_exclude_pattern);
bool process_auth_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & their_include_pattern,
+ utf8 const & their_exclude_pattern,
id const & client,
id const & nonce1,
string const & signature);
@@ -474,14 +463,17 @@
session::session(protocol_role role,
protocol_voice voice,
- vector const & patterns,
+ utf8 const & our_include_pattern,
+ utf8 const & our_exclude_pattern,
app_state & app,
string const & peer,
Netxx::socket_type sock,
Netxx::Timeout const & to) :
role(role),
voice(voice),
- patterns(patterns),
+ our_include_pattern(our_include_pattern),
+ our_exclude_pattern(our_exclude_pattern),
+ our_matcher(our_include_pattern, our_exclude_pattern),
app(app),
peer_id(peer),
fd(sock),
@@ -489,8 +481,6 @@
inbuf(""),
outbuf_size(0),
armed(false),
- pattern(""),
- pattern_re(".*"),
remote_peer_key_hash(""),
remote_peer_key_name(""),
session_key(constants::netsync_key_initializer),
@@ -512,14 +502,6 @@
dbw(app, true),
encountered_error(false)
{
- if (voice == client_voice)
- {
- N(patterns.size() == 1,
- F("client can only sync one pattern at a time"));
- this->pattern = idx(patterns, 0);
- this->pattern_re = boost::regex(this->pattern());
- }
-
dbw.set_on_revision_written(boost::bind(&session::rev_written_callback,
this, _1));
dbw.set_on_cert_written(boost::bind(&session::cert_written_callback,
@@ -1177,8 +1159,6 @@
// Write permissions checking:
// remove heads w/o proper certs, add their children to heads
// 1) remove unwanted branch certs from consideration
- // - server: check write permission hook
- // - client: check against sync pattern
// 2) remove heads w/o a branch tag, process new exposed heads
// 3) repeat 2 until no change
@@ -1202,13 +1182,7 @@
;
else
{
- bool ok;
- if (voice == server_voice)
- ok = app.lua.hook_get_netsync_write_permitted(name(),
- remote_peer_key_name);
- else
- ok = boost::regex_match(name(), pattern_re);
- if (ok)
+ if (our_matcher(name()))
{
ok_branches.insert(name());
keeping.push_back(*j);
@@ -1443,7 +1417,8 @@
void
session::queue_anonymous_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & nonce2,
base64 server_key_encoded)
{
@@ -1451,14 +1426,16 @@
rsa_oaep_sha_data hmac_key_encrypted;
encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
nonce2(), hmac_key_encrypted);
- cmd.write_anonymous_cmd(role, pattern, hmac_key_encrypted);
+ cmd.write_anonymous_cmd(role, include_pattern, exclude_pattern,
+ hmac_key_encrypted);
write_netcmd_and_try_flush(cmd);
set_session_key(nonce2());
}
void
session::queue_auth_cmd(protocol_role role,
- string const & pattern,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
id const & client,
id const & nonce1,
id const & nonce2,
@@ -1469,7 +1446,8 @@
rsa_oaep_sha_data hmac_key_encrypted;
encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
nonce2(), hmac_key_encrypted);
- cmd.write_auth_cmd(role, pattern, client, nonce1, hmac_key_encrypted, signature);
+ cmd.write_auth_cmd(role, include_pattern, exclude_pattern, client,
+ nonce1, hmac_key_encrypted, signature);
write_netcmd_and_try_flush(cmd);
set_session_key(nonce2());
}
@@ -1784,14 +1762,13 @@
this->remote_peer_key_hash = their_key_hash_decoded;
}
- utf8 pat(pattern);
vector branchnames;
set ok_branches;
get_branches(app, branchnames);
for (vector::const_iterator i = branchnames.begin();
i != branchnames.end(); i++)
{
- if (boost::regex_match(*i, pattern_re))
+ if (our_matcher(*i))
ok_branches.insert(utf8(*i));
}
rebuild_merkle_trees(app, ok_branches);
@@ -1817,31 +1794,22 @@
decode_base64(sig, sig_raw);
// make a new nonce of our own and send off the 'auth'
- queue_auth_cmd(this->role, this->pattern(), our_key_hash_raw,
- nonce, mk_nonce(), sig_raw(), their_key_encoded);
+ queue_auth_cmd(this->role, our_include_pattern, our_exclude_pattern,
+ our_key_hash_raw, nonce, mk_nonce(), sig_raw(),
+ their_key_encoded);
}
else
{
- queue_anonymous_cmd(this->role, this->pattern(),
- mk_nonce(), their_key_encoded);
+ queue_anonymous_cmd(this->role, our_include_pattern,
+ our_exclude_pattern, mk_nonce(), their_key_encoded);
}
return true;
}
-bool
-matches_one(string s, vector r)
-{
- for (vector::const_iterator i = r.begin(); i != r.end(); i++)
- {
- if (boost::regex_match(s, *i))
- return true;
- }
- return false;
-}
-
bool
session::process_anonymous_cmd(protocol_role role,
- string const & pattern)
+ utf8 const & their_include_pattern,
+ utf8 const & their_exclude_pattern)
{
//
// internally netsync thinks in terms of sources and sinks. users like
@@ -1875,35 +1843,27 @@
vector branchnames;
set ok_branches;
get_branches(app, branchnames);
- vector allowed;
- boost::regex reg(pattern);
- for (vector::const_iterator i = patterns.begin();
- i != patterns.end(); ++i)
- {
- allowed.push_back(boost::regex((*i)()));
- }
+ globish_matcher their_matcher(their_include_pattern, their_exclude_pattern);
for (vector::const_iterator i = branchnames.begin();
i != branchnames.end(); i++)
{
- if (boost::regex_match(*i, reg)
- && (allowed.size() == 0 || matches_one(*i, allowed)))
- {
- if (app.lua.hook_get_netsync_anonymous_read_permitted(*i))
- ok_branches.insert(utf8(*i));
- }
+ if (our_matcher(*i) && their_matcher(*i)
+ && app.lua.hook_get_netsync_anonymous_read_permitted(*i))
+ ok_branches.insert(utf8(*i));
}
- if (!ok_branches.size())
+ if (ok_branches.empty())
{
- W(F("denied anonymous read permission for '%s'\n") % pattern);
+ W(F("denied anonymous read permission for '%s' excluding '%s'\n")
+ % their_include_pattern % their_exclude_pattern);
this->saved_nonce = id("");
return false;
}
- P(F("allowed anonymous read permission for '%s'\n") % pattern);
+ P(F("allowed anonymous read permission for '%s' excluding '%s'\n")
+ % their_include_pattern % their_exclude_pattern);
rebuild_merkle_trees(app, ok_branches);
- this->pattern = pattern;
this->remote_peer_key_name = rsa_keypair_id("");
this->authenticated = true;
this->role = source_role;
@@ -1911,8 +1871,9 @@
}
bool
-session::process_auth_cmd(protocol_role role,
- string const & pattern,
+session::process_auth_cmd(protocol_role their_role,
+ utf8 const & their_include_pattern,
+ utf8 const & their_exclude_pattern,
id const & client,
id const & nonce1,
string const & signature)
@@ -1925,13 +1886,7 @@
set ok_branches;
vector branchnames;
get_branches(app, branchnames);
- vector allowed;
- for (vector::const_iterator i = patterns.begin();
- i != patterns.end(); ++i)
- {
- allowed.push_back(boost::regex((*i)()));
- }
- boost::regex reg(pattern);
+ globish_matcher their_matcher(their_include_pattern, their_exclude_pattern);
// check that they replied with the nonce we asked for
if (!(nonce1 == this->saved_nonce))
@@ -1950,7 +1905,7 @@
// if the user asks to run a "read only" service, this means they are
// willing to be a source but not a sink.
//
- // nb: the "role" here is the role the *client* wants to play
+ // nb: the "their_role" here is the role the *client* wants to play
// so we need to check that the opposite role is allowed for us,
// in our this->role field.
//
@@ -1969,12 +1924,12 @@
// client as sink, server as source (reading)
- if (role == sink_role || role == source_and_sink_role)
+ if (their_role == sink_role || their_role == source_and_sink_role)
{
if (this->role != source_role && this->role != source_and_sink_role)
{
- W(F("denied '%s' read permission for '%s' while running as sink\n")
- % their_id % pattern);
+ W(F("denied '%s' read permission for '%s' excluding '%s' while running as pure sink\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
this->saved_nonce = id("");
return false;
}
@@ -1982,48 +1937,46 @@
for (vector::const_iterator i = branchnames.begin();
i != branchnames.end(); i++)
{
- if (boost::regex_match(*i, reg)
- && (allowed.size() == 0 || matches_one(*i, allowed)))
- {
- if (app.lua.hook_get_netsync_read_permitted(*i, their_id))
- ok_branches.insert(utf8(*i));
- }
+ if (our_matcher(*i) && their_matcher(*i)
+ && app.lua.hook_get_netsync_read_permitted(*i, their_id))
+ ok_branches.insert(utf8(*i));
}
//if we're source_and_sink_role, continue even with no branches readable
//ex: serve --db=empty.db
- if (!ok_branches.size() && role == sink_role)
+ if (ok_branches.empty() && their_role == sink_role)
{
- W(F("denied '%s' read permission for '%s'\n") % their_id % pattern);
+ W(F("denied '%s' read permission for '%s' excluding '%s'\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
this->saved_nonce = id("");
return false;
}
- P(F("allowed '%s' read permission for '%s'\n") % their_id % pattern);
+ P(F("allowed '%s' read permission for '%s' excluding '%s'\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
}
// client as source, server as sink (writing)
- if (role == source_role || role == source_and_sink_role)
+ if (their_role == source_role || their_role == source_and_sink_role)
{
if (this->role != sink_role && this->role != source_and_sink_role)
{
- W(F("denied '%s' write permission for '%s' while running as source\n")
- % their_id % pattern);
+ W(F("denied '%s' write permission for '%s' excluding '%s' while running as pure source\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
this->saved_nonce = id("");
return false;
}
- // Write permissions are now checked from analyze_ancestry_graph.
- if (role == source_role)
+ if (!app.lua.hook_get_netsync_write_permitted(their_id))
{
- for (vector::const_iterator i = branchnames.begin();
- i != branchnames.end(); i++)
- {
- ok_branches.insert(utf8(*i));
- }
+ W(F("denied '%s' write permission for '%s' excluding '%s' while running as pure source\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
+ this->saved_nonce = id("");
+ return false;
}
- P(F("allowed '%s' write permission for '%s'\n") % their_id % pattern);
+ P(F("allowed '%s' write permission for '%s' excluding '%s'\n")
+ % their_id % their_include_pattern % their_exclude_pattern);
}
rebuild_merkle_trees(app, ok_branches);
@@ -2038,12 +1991,10 @@
{
// get our private key and sign back
L(F("client signature OK, accepting authentication\n"));
- this->pattern = pattern;
- this->pattern_re = boost::regex(this->pattern());
this->authenticated = true;
this->remote_peer_key_name = their_id;
// assume the (possibly degraded) opposite role
- switch (role)
+ switch (their_role)
{
case source_role:
I(this->role != source_role);
@@ -2088,9 +2039,10 @@
encode_hexenc(id(remote_peer_key_hash), their_key_hash);
// nb. this->role is our role, the server is in the opposite role
- L(F("received 'confirm' netcmd from server '%s' for pattern '%s' in %s mode\n")
- % their_key_hash % this->pattern % (this->role == source_and_sink_role ? "source and sink" :
- (this->role == source_role ? "sink" : "source")));
+ L(F("received 'confirm' netcmd from server '%s' for pattern '%s' exclude '%s' in %s mode\n")
+ % their_key_hash % our_include_pattern % our_exclude_pattern
+ % (this->role == source_and_sink_role ? "source and sink" :
+ (this->role == source_role ? "sink" : "source")));
// check their signature
if (app.db.public_key_exists(their_key_hash))
@@ -3039,15 +2991,16 @@
"anonymous netcmd received in source or source/sink role");
{
protocol_role role;
- string pattern;
+ utf8 their_include_pattern, their_exclude_pattern;
rsa_oaep_sha_data hmac_key_encrypted;
- cmd.read_anonymous_cmd(role, pattern, hmac_key_encrypted);
- L(F("received 'anonymous' netcmd from client for pattern '%s' "
+ cmd.read_anonymous_cmd(role, their_include_pattern, their_exclude_pattern, hmac_key_encrypted);
+ L(F("received 'anonymous' netcmd from client for pattern '%s' excluding '%s' "
"in %s mode\n")
- % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink")));
+ % their_include_pattern % their_exclude_pattern
+ % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink")));
- if (!process_anonymous_cmd(role, pattern))
+ if (!process_anonymous_cmd(role, their_include_pattern, their_exclude_pattern))
return false;
respond_to_auth_cmd(hmac_key_encrypted);
return true;
@@ -3059,11 +3012,12 @@
require(voice == server_voice, "auth netcmd received in server voice");
{
protocol_role role;
- string pattern, signature;
+ string signature;
+ utf8 their_include_pattern, their_exclude_pattern;
id client, nonce1, nonce2;
rsa_oaep_sha_data hmac_key_encrypted;
- cmd.read_auth_cmd(role, pattern, client, nonce1,
- hmac_key_encrypted, signature);
+ cmd.read_auth_cmd(role, their_include_pattern, their_exclude_pattern,
+ client, nonce1, hmac_key_encrypted, signature);
hexenc their_key_hash;
encode_hexenc(client, their_key_hash);
@@ -3071,12 +3025,14 @@
encode_hexenc(nonce1, hnonce1);
L(F("received 'auth(hmac)' netcmd from client '%s' for pattern '%s' "
- "in %s mode with nonce1 '%s'\n")
- % their_key_hash % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink"))
+ "exclude '%s' in %s mode with nonce1 '%s'\n")
+ % their_key_hash % their_include_pattern % their_exclude_pattern
+ % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink"))
% hnonce1);
- if (!process_auth_cmd(role, pattern, client, nonce1, signature))
+ if (!process_auth_cmd(role, their_include_pattern, their_exclude_pattern,
+ client, nonce1, signature))
return false;
respond_to_auth_cmd(hmac_key_encrypted);
return true;
@@ -3251,7 +3207,8 @@
static void
call_server(protocol_role role,
- vector const & patterns,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
app_state & app,
utf8 const & address,
Netxx::port_type default_port,
@@ -3264,8 +3221,8 @@
P(F("connecting to %s\n") % address());
Netxx::Stream server(address().c_str(), default_port, timeout);
- session sess(role, client_voice, patterns, app,
- address(), server.get_socketfd(), timeout);
+ session sess(role, client_voice, include_pattern, exclude_pattern,
+ app, address(), server.get_socketfd(), timeout);
while (true)
{
@@ -3394,7 +3351,8 @@
Netxx::StreamServer & server,
Netxx::Timeout & timeout,
protocol_role role,
- vector const & patterns,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
map > & sessions,
app_state & app)
{
@@ -3409,7 +3367,8 @@
else
{
P(F("accepted new client connection from %s\n") % client);
- shared_ptr sess(new session(role, server_voice, patterns,
+ shared_ptr sess(new session(role, server_voice,
+ include_pattern, exclude_pattern,
app,
lexical_cast(client),
client.get_socketfd(), timeout));
@@ -3524,7 +3483,8 @@
static void
serve_connections(protocol_role role,
- vector const & patterns,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
app_state & app,
utf8 const & address,
Netxx::port_type default_port,
@@ -3577,7 +3537,7 @@
// we either got a new connection
else if (fd == server)
handle_new_connection(addr, server, timeout, role,
- patterns, sessions, app);
+ include_pattern, exclude_pattern, sessions, app);
// or an existing session woke up
else
@@ -3776,7 +3736,8 @@
run_netsync_protocol(protocol_voice voice,
protocol_role role,
utf8 const & addr,
- vector patterns,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
app_state & app)
{
try
@@ -3784,7 +3745,7 @@
start_platform_netsync();
if (voice == server_voice)
{
- serve_connections(role, patterns, app,
+ serve_connections(role, include_pattern, exclude_pattern, app,
addr, static_cast(constants::netsync_default_port),
static_cast(constants::netsync_timeout_seconds),
static_cast(constants::netsync_connection_limit));
@@ -3793,7 +3754,7 @@
{
I(voice == client_voice);
transaction_guard guard(app.db);
- call_server(role, patterns, app,
+ call_server(role, include_pattern, exclude_pattern, app,
addr, static_cast(constants::netsync_default_port),
static_cast(constants::netsync_timeout_seconds));
guard.commit();
--- netsync.hh
+++ netsync.hh
@@ -22,7 +22,8 @@
void run_netsync_protocol(protocol_voice voice,
protocol_role role,
utf8 const & addr,
- std::vector patterns,
+ utf8 const & include_pattern,
+ utf8 const & exclude_pattern,
app_state & app);
#endif // __NETSYNC_H__
--- tests/t_netsync_defaults.at
+++ tests/t_netsync_defaults.at
@@ -22,21 +22,21 @@
# First make sure netsync with explicit server/pattern override defaults
AT_CHECK(MONOTONE2 set database default-server nonsense, [], [ignore], [ignore])
-AT_CHECK(MONOTONE2 set database default-pattern nonsense, [], [ignore], [ignore])
+AT_CHECK(MONOTONE2 set database default-include-pattern nonsense, [], [ignore], [ignore])
NETSYNC_CLIENT_RUN(pull, testbranch, 0)
AT_CHECK(MONOTONE2 checkout --branch=testbranch --revision=$TESTBRANCH_R testdir1, [], [ignore], [ignore])
AT_CHECK(test -f testdir1/testfile)
# Now make sure explicit server with default pattern works...
AT_CHECK(MONOTONE2 set database default-server nonsense, [], [ignore], [ignore])
-AT_CHECK(MONOTONE2 set database default-pattern otherbranch, [], [ignore], [ignore])
+AT_CHECK(MONOTONE2 set database default-include-pattern otherbranch, [], [ignore], [ignore])
NETSYNC_CLIENT_RUN(pull, [])
AT_CHECK(MONOTONE2 checkout --branch=otherbranch --revision=$OTHERBRANCH_R testdir2, [], [ignore], [ignore])
AT_CHECK(test -f testdir2/testfile)
-# And finally,
+# And finally, make sure that passing nothing at all also works (uses default)
AT_CHECK(MONOTONE2 set database default-server 127.0.0.1:$_PORT, [], [ignore], [ignore])
-AT_CHECK(MONOTONE2 set database default-pattern thirdbranch, [], [ignore], [ignore])
+AT_CHECK(MONOTONE2 set database default-include-pattern thirdbranch, [], [ignore], [ignore])
AT_CHECK(MONOTONE2 sync, [], [ignore], [ignore])
AT_CHECK(MONOTONE2 checkout --branch=thirdbranch --revision=$THIRDBRANCH_R testdir3, [], [ignore], [ignore])
AT_CHECK(test -f testdir3/testfile)
--- tests/t_netsync_permissions.at
+++ tests/t_netsync_permissions.at
@@ -18,7 +18,7 @@
return true
end
-function get_netsync_write_permitted(pattern, identity)
+function get_netsync_write_permitted(identity)
return true
end
@@ -106,7 +106,7 @@
return false
end
-function get_netsync_write_permitted(pattern, identity)
+function get_netsync_write_permitted(identity)
if (identity == "address@hidden") then return true end
return false
end
--- tests/t_netsync_single.at
+++ tests/t_netsync_single.at
@@ -11,7 +11,7 @@
return true
end
-function get_netsync_write_permitted(pattern, identity)
+function get_netsync_write_permitted(identity)
return true
end
])
--- testsuite.at
+++ testsuite.at
@@ -327,7 +327,7 @@
return true
end
-function get_netsync_write_permitted(pattern, identity)
+function get_netsync_write_permitted(identity)
return true
end
])
--- transforms.cc
+++ transforms.cc
@@ -870,142 +870,8 @@
dst += linesep_str;
}
-// glob_to_regexp converts a sh file glob to a regexp. The regexp should
-// be usable by the Boost regexp library.
-//
-// Pattern tranformation:
-//
-// - Any character except those described below are copied as they are.
-// - The backslash (\) escapes the following character. The escaping
-// backslash is copied to the regexp along with the following character.
-// - * is transformed to .* in the regexp.
-// - ? is transformed to . in the regexp.
-// - { is transformed to ( in the regexp, unless within [ and ].
-// - } is transformed to ) in the regexp, unless within [ and ].
-// - , is transformed to | in the regexp, if within { and } and not
-// within [ and ].
-// - ^ is escaped unless it comes directly after an unescaped [.
-// - ! is transformed to ^ in the regexp if it comes directly after an
-// unescaped [.
-// - ] directly following an unescaped [ is escaped.
-string glob_to_regexp(const string & glob)
-{
- int in_braces = 0; // counter for levels if {}
- bool in_brackets = false; // flags if we're inside a [], which
- // has higher precedence than {}.
- // Also, [ is accepted inside [] unescaped.
- bool this_was_opening_bracket = false;
- string tmp;
- tmp.reserve(glob.size() * 2);
-
#ifdef BUILD_UNIT_TESTS
- cerr << "DEBUG[glob_to_regexp]: input = \"" << glob << "\"" << endl;
-#endif
-
- for (string::const_iterator i = glob.begin(); i != glob.end(); ++i)
- {
- char c = *i;
- bool last_was_opening_bracket = this_was_opening_bracket;
- this_was_opening_bracket = false;
-
- // Special case ^ and ! at the beginning of a [] expression.
- if (in_brackets && last_was_opening_bracket
- && (c == '!' || c == '^'))
- {
- tmp += '^';
- if (++i == glob.end())
- break;
- c = *i;
- }
-
- if (c == '\\')
- {
- tmp += c;
- if (++i == glob.end())
- break;
- tmp += *i;
- }
- else if (in_brackets)
- {
- switch(c)
- {
- case ']':
- if (!last_was_opening_bracket)
- {
- in_brackets = false;
- tmp += c;
- break;
- }
- // Trickling through to the standard character conversion,
- // because ] as the first character of a set is regarded as
- // a normal character.
- default:
- if (!(isalnum(c) || c == '_'))
- {
- tmp += '\\';
- }
- tmp += c;
- break;
- }
- }
- else
- {
- switch(c)
- {
- case '*':
- tmp += ".*";
- break;
- case '?':
- tmp += '.';
- break;
- case '{':
- in_braces++;
- tmp += '(';
- break;
- case '}':
- N(in_braces != 0,
- F("trying to end a brace expression in a glob when none is started"));
- tmp += ')';
- in_braces--;
- break;
- case '[':
- in_brackets = true;
- this_was_opening_bracket = true;
- tmp += c;
- break;
- case ',':
- if (in_braces > 0)
- {
- tmp += '|';
- break;
- }
- // Trickling through to default: here, since a comma outside of
- // brace notation is just a normal character.
- default:
- if (!(isalnum(c) || c == '_'))
- {
- tmp += '\\';
- }
- tmp += c;
- break;
- }
- }
- }
-
- N(!in_brackets,
- F("run-away bracket expression in glob"));
- N(in_braces == 0,
- F("run-away brace expression in glob"));
-
-#ifdef BUILD_UNIT_TESTS
- cerr << "DEBUG[glob_to_regexp]: output = \"" << tmp << "\"" << endl;
-#endif
-
- return tmp;
-}
-
-#ifdef BUILD_UNIT_TESTS
#include "unit_tests.hh"
static void
@@ -1283,15 +1149,6 @@
check_idna_encoding();
}
-static void glob_to_regexp_test()
-{
- BOOST_CHECK(glob_to_regexp("abc,v") == "abc\\,v");
- BOOST_CHECK(glob_to_regexp("foo[12m,]") == "foo[12m\\,]");
- // A full fledged, use all damn features test...
- BOOST_CHECK(glob_to_regexp("foo.{bar*,cookie?{haha,hehe[^\\123!,]}}[!]a^b]")
- == "foo\\.(bar.*|cookie.(haha|hehe[^\\123\\!\\,]))[^\\]a\\^b]");
-}
-
void
add_transform_tests(test_suite * suite)
{
@@ -1303,7 +1160,6 @@
suite->add(BOOST_TEST_CASE(&join_lines_test));
suite->add(BOOST_TEST_CASE(&strip_ws_test));
suite->add(BOOST_TEST_CASE(&encode_test));
- suite->add(BOOST_TEST_CASE(&glob_to_regexp_test));
}
#endif // BUILD_UNIT_TESTS
--- transforms.hh
+++ transforms.hh
@@ -193,6 +193,4 @@
// line-ending conversion
void line_end_convert(std::string const & linesep, std::string const & src, std::string & dst);
-
-
#endif // __TRANSFORMS_HH__
--- unit_tests.cc
+++ unit_tests.cc
@@ -69,9 +69,11 @@
if (t.empty() || t.find("netcmd") != t.end())
add_netcmd_tests(suite);
- if (t.empty() || t.find("netcmd") != t.end())
+ if (t.empty() || t.find("path_component") != t.end())
add_path_component_tests(suite);
+ if (t.empty() || t.find("globish") != t.end())
+ add_globish_tests(suite);
// all done, add our clean-shutdown-indicator
suite->add(BOOST_TEST_CASE(&clean_shutdown_dummy_test));
--- unit_tests.hh
+++ unit_tests.hh
@@ -32,5 +32,6 @@
void add_packet_tests(test_suite * suite);
void add_netcmd_tests(test_suite * suite);
void add_path_component_tests(test_suite * suite);
+void add_globish_tests(test_suite * suite);
#endif