// Copyright (C) 2002 Graydon Hoare // // This program is made available under the GNU GPL version 2.0 or // greater. See the accompanying file COPYING for details. // // This program is distributed WITHOUT ANY WARRANTY; without even the // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. #include "base.hh" #include "constants.hh" #include "hash_map.hh" #include "sanity.hh" #include "vocab.hh" using std::string; // verifiers for various types of data // the verify() stuff gets a little complicated; there doesn't seem to be a // really nice way to achieve what we want with c++'s type system. the // problem is this: we want to give verify(file_path) and verify(local_path) // access to the internals of file_path and local_path, i.e. make them // friends, so they can normalize the file paths they're given. this means // that verify() needs to be declared publically, so that the definition of // these classes can refer to them. it also means that they -- and all other // ATOMIC types -- cannot fall back on a templated version of verify if no // other version is defined, because, well, the friend thing and the template // thing just don't work out, as far as I can tell. So, every ATOMIC type // needs an explicitly defined verify() function, so we have both ATOMIC() and // ATOMIC_NOVERIFY() macros, the latter of which defines a type-specific noop // verify function. DECORATE and ENCODING, on the other hand, cannot make use // of a trick like these, because they are template types themselves, and we // want to be able to define verify(hexenc) without defining // verify(hexenc) at the same time, for instance. Fortunately, these // types never need to be friends with their verify functions (yet...), so we // _can_ use a templated fallback function. This templated function is used // _only_ by DECORATE and ENCODING; it would be nice to make it take an // argument of type T1 to document that, but for some reason that doesn't // work either. template static inline void verify(T & val) {} template static inline void verify_full(T & val) { val.ok = true; } // NOTE: _not_ verify_full; you use verify_full for ATOMICs, verify() for // everything else. inline void verify(hexenc & val) { if (val.ok) return; if (val().empty()) return; N(val().size() == constants::idlen, F("hex encoded ID '%s' size != %d") % val % constants::idlen); for (string::const_iterator i = val().begin(); i != val().end(); ++i) { N(is_xdigit(*i), F("bad character '%c' in id name '%s'") % *i % val); } val.ok = true; } inline void verify_full(symbol & val) { for (string::const_iterator i = val().begin(); i != val().end(); ++i) { N(is_alnum(*i) || *i == '_', F("bad character '%c' in symbol '%s'") % *i % val); } val.ok = true; } inline void verify_full(cert_name & val) { string::size_type pos = val().find_first_not_of(constants::legal_cert_name_bytes); N(pos == string::npos, F("bad character '%c' in cert name '%s'") % val().at(pos) % val); val.ok = true; } inline void verify_full(rsa_keypair_id & val) { string::size_type pos = val().find_first_not_of(constants::legal_key_name_bytes); N(pos == string::npos, F("bad character '%c' in key name '%s'") % val().at(pos) % val); val.ok = true; } inline void verify_full(netsync_session_key & val) { if (val().size() == 0) { val.s = std::string(constants::netsync_session_key_length_in_bytes, 0); return; } N(val().size() == constants::netsync_session_key_length_in_bytes, F("Invalid key length of %d bytes") % val().length()); val.ok = true; } inline void verify_full(netsync_hmac_value & val) { if (val().size() == 0) { val.s = std::string(constants::netsync_hmac_value_length_in_bytes, 0); return; } N(val().size() == constants::netsync_hmac_value_length_in_bytes, F("Invalid hmac length of %d bytes") % val().length()); val.ok = true; } // Note that ATOMIC types each keep a static symbol-table object and a // counter of activations, and when there is an activation, the // members of the ATOMIC type initialize their internal string using a // copy of the string found in the symtab. Since some (all?) C++ // string implementations are copy-on-write, this has the affect // of making the ATOMIC(foo) values constructed within a symbol table // scope share string storage. struct symtab_impl { typedef hashmap::hash_set hset; hset vals; symtab_impl() : vals() {} void clear() { vals.clear(); } string const & unique(string const & in) { // This produces a pair where iter points to an // element of the table; the bool indicates whether the element is // new, but we don't actually care. We just want the iter. return *(vals.insert(in).first); } }; // Sometimes it's handy to have a non-colliding, meaningless id. hexenc fake_id() { static u32 counter = 0; ++counter; I(counter >= 1); // detect overflow return hexenc((FL("00000000000000000000000000000000%08x") % counter).str()); } // instantiation of various vocab functions #include "vocab_macros.hh" #define ENCODING(enc) cc_ENCODING(enc) #define DECORATE(dec) cc_DECORATE(dec) #define ATOMIC(ty) cc_ATOMIC(ty) #define ATOMIC_NOVERIFY(ty) cc_ATOMIC_NOVERIFY(ty) #ifdef EXTERN #undef EXTERN #endif #define EXTERN #include "vocab_terms.hh" #undef EXTERN #undef ATOMIC #undef DECORATE template void dump(base64 const&, string &); template void dump(revision_id const & r, string &); template void dump(manifest_id const & r, string &); template void dump(file_id const & r, string &); template void dump(hexenc const & r, string &); template void dump(roster_data const & d, string &); template void dump(roster_delta const & d, string &); template void dump(manifest_data const & d, string &); #ifdef BUILD_UNIT_TESTS #include "unit_tests.hh" UNIT_TEST(vocab, verify_hexenc_id) { // -------- magic empty string and default constructor are okay: UNIT_TEST_CHECK(hexenc("")() == ""); hexenc my_default_id; UNIT_TEST_CHECK(my_default_id() == ""); // -------- wrong length: UNIT_TEST_CHECK_THROW(hexenc("a"), informative_failure); // 39 letters UNIT_TEST_CHECK_THROW(hexenc("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), informative_failure); // 41 letters UNIT_TEST_CHECK_THROW(hexenc("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), informative_failure); // but 40 is okay UNIT_TEST_CHECK(hexenc("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")() == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); // -------- bad characters: UNIT_TEST_CHECK_THROW(hexenc("g000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("h000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("G000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("H000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("*000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("`000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("z000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("Z000000000000000000000000000000000000000"), informative_failure); // different positions: UNIT_TEST_CHECK_THROW(hexenc("g000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("0g00000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("00g0000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("000g000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("0000g00000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("000000000000000000000g000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("0000000000000000000000g00000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("000000000000000000000000000000g000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("000000000000000000000000000000000000g000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("0000000000000000000000000000000000000g00"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("00000000000000000000000000000000000000g0"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("000000000000000000000000000000000000000g"), informative_failure); // uppercase hex is bad too! UNIT_TEST_CHECK_THROW(hexenc("A000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("B000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("C000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("D000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("E000000000000000000000000000000000000000"), informative_failure); UNIT_TEST_CHECK_THROW(hexenc("F000000000000000000000000000000000000000"), informative_failure); // but lowercase and digits are all fine UNIT_TEST_CHECK(hexenc("0123456789abcdef0123456789abcdef01234567")() == "0123456789abcdef0123456789abcdef01234567"); } #endif // BUILD_UNIT_TESTS // Local Variables: // mode: C++ // fill-column: 76 // c-file-style: "gnu" // indent-tabs-mode: nil // End: // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: