1 | // Copyright (C) 2002 Graydon Hoare <graydon@pobox.com>␊ |
2 | //␊ |
3 | // This program is made available under the GNU GPL version 2.0 or␊ |
4 | // greater. See the accompanying file COPYING for details.␊ |
5 | //␊ |
6 | // This program is distributed WITHOUT ANY WARRANTY; without even the␊ |
7 | // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR␊ |
8 | // PURPOSE.␊ |
9 | ␊ |
10 | ␊ |
11 | #include "base.hh"␊ |
12 | #include "constants.hh"␊ |
13 | #include "hash_map.hh"␊ |
14 | #include "sanity.hh"␊ |
15 | #include "vocab.hh"␊ |
16 | #include "char_classifiers.hh"␊ |
17 | #include "transforms.hh"␊ |
18 | ␊ |
19 | using std::string;␊ |
20 | ␊ |
21 | // verifiers for various types of data␊ |
22 | ␊ |
23 | // Every ENCODING and ATOMIC type not defined with the _NOVERIFY variant in␊ |
24 | // vocab_terms.hh must have a verify function defined here. DECORATE types␊ |
25 | // use the verify function of their inner type.␊ |
26 | ␊ |
27 | // ENCODING types ... hexenc<id> has a fixed size, hexenc<other> doesn't.␊ |
28 | template <typename INNER>␊ |
29 | inline void␊ |
30 | verify(hexenc<INNER> const & val)␊ |
31 | {␊ |
32 | for (string::const_iterator i = val().begin(); i != val().end(); ++i)␊ |
33 | {␊ |
34 | N(is_xdigit(*i),␊ |
35 | F("bad character '%c' in '%s'") % *i % val);␊ |
36 | }␊ |
37 | }␊ |
38 | ␊ |
39 | template <>␊ |
40 | inline void␊ |
41 | verify(hexenc<id> const & val)␊ |
42 | {␊ |
43 | if (val().empty())␊ |
44 | return;␊ |
45 | ␊ |
46 | N(val().size() == constants::idlen,␊ |
47 | F("hex encoded ID '%s' size != %d") % val % constants::idlen);␊ |
48 | for (string::const_iterator i = val().begin(); i != val().end(); ++i)␊ |
49 | {␊ |
50 | N(is_xdigit(*i),␊ |
51 | F("bad character '%c' in id name '%s'") % *i % val);␊ |
52 | }␊ |
53 | }␊ |
54 | ␊ |
55 | // ATOMIC types ...␊ |
56 | inline void␊ |
57 | verify(id & val)␊ |
58 | {␊ |
59 | if (val().empty())␊ |
60 | return;␊ |
61 | ␊ |
62 | N(val().size() == constants::idlen_bytes,␊ |
63 | F("invalid ID '%s'") % val);␊ |
64 | }␊ |
65 | ␊ |
66 | inline void␊ |
67 | verify(symbol const & val)␊ |
68 | {␊ |
69 | for (string::const_iterator i = val().begin(); i != val().end(); ++i)␊ |
70 | {␊ |
71 | N(is_alnum(*i) || *i == '_',␊ |
72 | F("bad character '%c' in symbol '%s'") % *i % val);␊ |
73 | }␊ |
74 | }␊ |
75 | ␊ |
76 | inline void␊ |
77 | verify(cert_name const & val)␊ |
78 | {␊ |
79 | string::size_type pos = val().find_first_not_of(constants::legal_cert_name_bytes);␊ |
80 | N(pos == string::npos,␊ |
81 | F("bad character '%c' in cert name '%s'") % val().at(pos) % val);␊ |
82 | }␊ |
83 | ␊ |
84 | inline void␊ |
85 | verify(rsa_keypair_id const & val)␊ |
86 | {␊ |
87 | string::size_type pos = val().find_first_not_of(constants::legal_key_name_bytes);␊ |
88 | N(pos == string::npos,␊ |
89 | F("bad character '%c' in key name '%s'") % val().at(pos) % val);␊ |
90 | }␊ |
91 | ␊ |
92 | // These two may modify their argument, to set a more sensible value when␊ |
93 | // initializing from the empty string or the default constructor; therefore␊ |
94 | // they cannot take a const argument and must be friends with their class.␊ |
95 | ␊ |
96 | inline void␊ |
97 | verify(netsync_session_key & val)␊ |
98 | {␊ |
99 | if (val().size() == 0)␊ |
100 | {␊ |
101 | val.s = std::string(constants::netsync_session_key_length_in_bytes, 0);␊ |
102 | return;␊ |
103 | }␊ |
104 | ␊ |
105 | N(val().size() == constants::netsync_session_key_length_in_bytes,␊ |
106 | F("Invalid key length of %d bytes") % val().length());␊ |
107 | }␊ |
108 | ␊ |
109 | inline void␊ |
110 | verify(netsync_hmac_value & val)␊ |
111 | {␊ |
112 | if (val().size() == 0)␊ |
113 | {␊ |
114 | val.s = std::string(constants::netsync_hmac_value_length_in_bytes, 0);␊ |
115 | return;␊ |
116 | }␊ |
117 | ␊ |
118 | N(val().size() == constants::netsync_hmac_value_length_in_bytes,␊ |
119 | F("Invalid hmac length of %d bytes") % val().length());␊ |
120 | }␊ |
121 | ␊ |
122 | ␊ |
123 | // Note that ATOMIC types each keep a static symbol-table object and a␊ |
124 | // counter of activations, and when there is an activation, the␊ |
125 | // members of the ATOMIC type initialize their internal string using a␊ |
126 | // copy of the string found in the symtab. Since some (all?) C++␊ |
127 | // string implementations are copy-on-write, this has the affect␊ |
128 | // of making the ATOMIC(foo) values constructed within a symbol table␊ |
129 | // scope share string storage.␊ |
130 | struct␊ |
131 | symtab_impl␊ |
132 | {␊ |
133 | typedef hashmap::hash_set<string> hset;␊ |
134 | hset vals;␊ |
135 | symtab_impl() : vals() {}␊ |
136 | void clear() { vals.clear(); }␊ |
137 | string const & unique(string const & in)␊ |
138 | {␊ |
139 | // This produces a pair <iter,bool> where iter points to an␊ |
140 | // element of the table; the bool indicates whether the element is␊ |
141 | // new, but we don't actually care. We just want the iter.␊ |
142 | return *(vals.insert(in).first);␊ |
143 | }␊ |
144 | };␊ |
145 | ␊ |
146 | // Sometimes it's handy to have a non-colliding, meaningless id.␊ |
147 | ␊ |
148 | id␊ |
149 | fake_id()␊ |
150 | {␊ |
151 | static u32 counter = 0;␊ |
152 | ++counter;␊ |
153 | I(counter >= 1); // detect overflow␊ |
154 | string s((FL("00000000000000000000000000000000%08x") % counter).str());␊ |
155 | return id(decode_hexenc(s));␊ |
156 | }␊ |
157 | ␊ |
158 | // instantiation of various vocab functions␊ |
159 | ␊ |
160 | ␊ |
161 | ␊ |
162 | #include "vocab_macros.hh"␊ |
163 | #define ENCODING(enc) cc_ENCODING(enc)␊ |
164 | #define ENCODING_NOVERIFY(enc) cc_ENCODING_NOVERIFY(enc)␊ |
165 | #define DECORATE(dec) cc_DECORATE(dec)␊ |
166 | #define ATOMIC(ty) cc_ATOMIC(ty)␊ |
167 | #define ATOMIC_HOOKED(ty,hook) cc_ATOMIC(ty)␊ |
168 | #define ATOMIC_NOVERIFY(ty) cc_ATOMIC_NOVERIFY(ty)␊ |
169 | #define ATOMIC_BINARY(ty) cc_ATOMIC_BINARY(ty)␊ |
170 | ␊ |
171 | #undef EXTERN␊ |
172 | #define EXTERN␊ |
173 | ␊ |
174 | #include "vocab_terms.hh"␊ |
175 | ␊ |
176 | #undef EXTERN␊ |
177 | #undef ATOMIC␊ |
178 | #undef ATOMIC_HOOKED␊ |
179 | #undef ATOMIC_NOVERIFY␊ |
180 | #undef DECORATE␊ |
181 | #undef ENCODING␊ |
182 | #undef ENCODING_NOVERIFY␊ |
183 | ␊ |
184 | template void dump(revision_id const & r, string &);␊ |
185 | template void dump(manifest_id const & r, string &);␊ |
186 | template void dump(file_id const & r, string &);␊ |
187 | template void dump(hexenc<id> const & r, string &);␊ |
188 | template void dump(rsa_pub_key const&, string &);␊ |
189 | template void dump(roster_data const & d, string &);␊ |
190 | template void dump(roster_delta const & d, string &);␊ |
191 | template void dump(manifest_data const & d, string &);␊ |
192 | template void dump(revision_data const & d, string &);␊ |
193 | ␊ |
194 | template std::ostream & operator<< <>(std::ostream &, epoch<id> const &);␊ |
195 | template std::ostream & operator<< <>(std::ostream &, file<id> const &);␊ |
196 | template std::ostream & operator<< <>(std::ostream &, hexenc<id> const &);␊ |
197 | template std::ostream & operator<< <>(std::ostream &, key<id> const &);␊ |
198 | template std::ostream & operator<< <>(std::ostream &, manifest<id> const &);␊ |
199 | template std::ostream & operator<< <>(std::ostream &, revision<id> const &);␊ |
200 | template std::ostream & operator<< <>(std::ostream &, roster<id> const &);␊ |
201 | ␊ |
202 | template std::ostream & operator<< <>(std::ostream &, epoch<data> const &);␊ |
203 | template std::ostream & operator<< <>(std::ostream &, file<data> const &);␊ |
204 | template std::ostream & operator<< <>(std::ostream &, manifest<data> const &);␊ |
205 | template std::ostream & operator<< <>(std::ostream &, revision<data> const &);␊ |
206 | template std::ostream & operator<< <>(std::ostream &, roster<data> const &);␊ |
207 | ␊ |
208 | /*␊ |
209 | * specializations for id, which allows the encoded id␊ |
210 | * to be dumped out as a human readable, hex encoded␊ |
211 | * string.␊ |
212 | */␊ |
213 | template <>␊ |
214 | void dump (id const & obj, std::string & out)␊ |
215 | {␊ |
216 | out = encode_hexenc(obj());␊ |
217 | }␊ |
218 | ␊ |
219 | #ifdef BUILD_UNIT_TESTS␊ |
220 | ␊ |
221 | #include "unit_tests.hh"␊ |
222 | ␊ |
223 | UNIT_TEST(vocab, verify_hexenc_id)␊ |
224 | {␊ |
225 | // -------- magic empty string and default constructor are okay:␊ |
226 | UNIT_TEST_CHECK(hexenc<id>("")() == "");␊ |
227 | hexenc<id> my_default_id;␊ |
228 | UNIT_TEST_CHECK(my_default_id() == "");␊ |
229 | ␊ |
230 | // -------- wrong length:␊ |
231 | UNIT_TEST_CHECK_THROW(hexenc<id>("a"), informative_failure);␊ |
232 | // 39 letters␊ |
233 | UNIT_TEST_CHECK_THROW(hexenc<id>("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),␊ |
234 | informative_failure);␊ |
235 | // 41 letters␊ |
236 | UNIT_TEST_CHECK_THROW(hexenc<id>("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),␊ |
237 | informative_failure);␊ |
238 | // but 40 is okay␊ |
239 | UNIT_TEST_CHECK(hexenc<id>("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")()␊ |
240 | == "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");␊ |
241 | ␊ |
242 | // -------- bad characters:␊ |
243 | UNIT_TEST_CHECK_THROW(hexenc<id>("g000000000000000000000000000000000000000"), informative_failure);␊ |
244 | UNIT_TEST_CHECK_THROW(hexenc<id>("h000000000000000000000000000000000000000"), informative_failure);␊ |
245 | UNIT_TEST_CHECK_THROW(hexenc<id>("G000000000000000000000000000000000000000"), informative_failure);␊ |
246 | UNIT_TEST_CHECK_THROW(hexenc<id>("H000000000000000000000000000000000000000"), informative_failure);␊ |
247 | UNIT_TEST_CHECK_THROW(hexenc<id>("*000000000000000000000000000000000000000"), informative_failure);␊ |
248 | UNIT_TEST_CHECK_THROW(hexenc<id>("`000000000000000000000000000000000000000"), informative_failure);␊ |
249 | UNIT_TEST_CHECK_THROW(hexenc<id>("z000000000000000000000000000000000000000"), informative_failure);␊ |
250 | UNIT_TEST_CHECK_THROW(hexenc<id>("Z000000000000000000000000000000000000000"), informative_failure);␊ |
251 | // different positions:␊ |
252 | UNIT_TEST_CHECK_THROW(hexenc<id>("g000000000000000000000000000000000000000"), informative_failure);␊ |
253 | UNIT_TEST_CHECK_THROW(hexenc<id>("0g00000000000000000000000000000000000000"), informative_failure);␊ |
254 | UNIT_TEST_CHECK_THROW(hexenc<id>("00g0000000000000000000000000000000000000"), informative_failure);␊ |
255 | UNIT_TEST_CHECK_THROW(hexenc<id>("000g000000000000000000000000000000000000"), informative_failure);␊ |
256 | UNIT_TEST_CHECK_THROW(hexenc<id>("0000g00000000000000000000000000000000000"), informative_failure);␊ |
257 | UNIT_TEST_CHECK_THROW(hexenc<id>("000000000000000000000g000000000000000000"), informative_failure);␊ |
258 | UNIT_TEST_CHECK_THROW(hexenc<id>("0000000000000000000000g00000000000000000"), informative_failure);␊ |
259 | UNIT_TEST_CHECK_THROW(hexenc<id>("000000000000000000000000000000g000000000"), informative_failure);␊ |
260 | UNIT_TEST_CHECK_THROW(hexenc<id>("000000000000000000000000000000000000g000"), informative_failure);␊ |
261 | UNIT_TEST_CHECK_THROW(hexenc<id>("0000000000000000000000000000000000000g00"), informative_failure);␊ |
262 | UNIT_TEST_CHECK_THROW(hexenc<id>("00000000000000000000000000000000000000g0"), informative_failure);␊ |
263 | UNIT_TEST_CHECK_THROW(hexenc<id>("000000000000000000000000000000000000000g"), informative_failure);␊ |
264 | // uppercase hex is bad too!␊ |
265 | UNIT_TEST_CHECK_THROW(hexenc<id>("A000000000000000000000000000000000000000"), informative_failure);␊ |
266 | UNIT_TEST_CHECK_THROW(hexenc<id>("B000000000000000000000000000000000000000"), informative_failure);␊ |
267 | UNIT_TEST_CHECK_THROW(hexenc<id>("C000000000000000000000000000000000000000"), informative_failure);␊ |
268 | UNIT_TEST_CHECK_THROW(hexenc<id>("D000000000000000000000000000000000000000"), informative_failure);␊ |
269 | UNIT_TEST_CHECK_THROW(hexenc<id>("E000000000000000000000000000000000000000"), informative_failure);␊ |
270 | UNIT_TEST_CHECK_THROW(hexenc<id>("F000000000000000000000000000000000000000"), informative_failure);␊ |
271 | // but lowercase and digits are all fine␊ |
272 | UNIT_TEST_CHECK(hexenc<id>("0123456789abcdef0123456789abcdef01234567")()␊ |
273 | == "0123456789abcdef0123456789abcdef01234567");␊ |
274 | }␊ |
275 | ␊ |
276 | #endif // BUILD_UNIT_TESTS␊ |
277 | ␊ |
278 | // Local Variables:␊ |
279 | // mode: C++␊ |
280 | // fill-column: 76␊ |
281 | // c-file-style: "gnu"␊ |
282 | // indent-tabs-mode: nil␊ |
283 | // End:␊ |
284 | // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:␊ |