monotone

monotone Mtn Source Tree

Root/vocab.cc

1// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
2// all rights reserved.
3// licensed to the public under the terms of the GNU GPL (>= 2)
4// see the file COPYING for details
5
6#include <string>
7#include <iostream>
8#include <boost/filesystem/path.hpp>
9#include <boost/filesystem/exception.hpp>
10#include <boost/version.hpp>
11
12#include "constants.hh"
13#include "file_io.hh"
14#include "sanity.hh"
15#include "vocab.hh"
16
17// verifiers for various types of data
18
19using namespace std;
20
21// the verify() stuff gets a little complicated; there doesn't seem to be a
22// really nice way to achieve what we want with c++'s type system. the
23// problem is this: we want to give verify(file_path) and verify(local_path)
24// access to the internals of file_path and local_path, i.e. make them
25// friends, so they can normalize the file paths they're given. this means
26// that verify() needs to be declared publically, so that the definition of
27// these classes can refer to them. it also means that they -- and all other
28// ATOMIC types -- cannot fall back on a templated version of verify if no
29// other version is defined, because, well, the friend thing and the template
30// thing just don't work out, as far as I can tell. So, every ATOMIC type
31// needs an explicitly defined verify() function, so we have both ATOMIC() and
32// ATOMIC_NOVERIFY() macros, the latter of which defines a type-specific noop
33// verify function. DECORATE and ENCODING, on the other hand, cannot make use
34// of a trick like these, because they are template types themselves, and we
35// want to be able to define verify(hexenc<id>) without defining
36// verify(hexenc<data>) at the same time, for instance. Fortunately, these
37// types never need to be friends with their verify functions (yet...), so we
38// _can_ use a templated fallback function. This templated function is used
39// _only_ by DECORATE and ENCODING; it would be nice to make it take an
40// argument of type T1<T2> to document that, but for some reason that doesn't
41// work either.
42template <typename T>
43static inline void
44verify(T & val)
45{}
46
47inline void
48verify(hexenc<id> & val)
49{
50 if (val.ok)
51 return;
52
53 if (val() == "")
54 return;
55
56 N(val().size() == constants::idlen,
57 F("hex encoded ID '%s' size != %d") % val % constants::idlen);
58 string::size_type pos = val().find_first_not_of(constants::legal_id_bytes);
59 N(pos == string::npos,
60 F("bad character '%c' in id name '%s'") % val().at(pos) % val);
61
62 val.ok = true;
63}
64
65inline void
66verify(ace & val)
67{
68 if (val.ok)
69 return;
70
71 string::size_type pos = val().find_first_not_of(constants::legal_ace_bytes);
72 N(pos == string::npos,
73 F("bad character '%c' in ace string '%s'") % val().at(pos) % val);
74
75 val.ok = true;
76}
77
78
79inline void
80verify(cert_name & val)
81{
82 if (val.ok)
83 return;
84
85 string::size_type pos = val().find_first_not_of(constants::legal_cert_name_bytes);
86 N(pos == string::npos,
87 F("bad character '%c' in cert name '%s'") % val().at(pos) % val);
88
89 val.ok = true;
90}
91
92inline void
93verify(rsa_keypair_id & val)
94{
95 if (val.ok)
96 return;
97
98 string::size_type pos = val().find_first_not_of(constants::legal_key_name_bytes);
99 N(pos == string::npos,
100 F("bad character '%c' in key name '%s'") % val().at(pos) % val);
101
102 val.ok = true;
103}
104
105
106inline void
107verify(local_path & val)
108{
109
110 if (val.ok)
111 return;
112
113 using boost::filesystem::path;
114 boost::filesystem::path p;
115 try
116 {
117 p = mkpath(val());
118 p = p.normalize();
119 }
120 catch (std::runtime_error &re)
121 {
122 throw informative_failure(re.what());
123 }
124 catch (fs::filesystem_error &fse)
125 {
126 throw informative_failure(fse.what());
127 }
128
129 N(! (p.has_root_path() || p.has_root_name() || p.has_root_directory()),
130 F("prohibited absolute path '%s'") % val);
131
132 for(path::iterator i = p.begin(); i != p.end(); ++i)
133 {
134 N(!( *i == "" && (! p.empty())),
135 F("empty path component in '%s'") % val);
136
137 N((*i != ".."),
138 F("prohibited path component '%s' in '%s'") % *i % val);
139
140 string::size_type pos = i->find_first_of(constants::illegal_path_bytes);
141 N(pos == string::npos,
142 F("bad character '%d' in path component '%s' of '%s'")
143 % static_cast<int>(i->at(pos)) % *i % val);
144
145 string s = val();
146 for (string::const_iterator j = s.begin(); j != s.end(); ++j)
147 N(*j != '\0',
148 F("null byte in path component '%s' of '%s'") % *i % val);
149
150 }
151
152 // save back the normalized string
153 val.s = p.string();
154
155 val.ok = true;
156}
157
158// returns true if the given string is obviously a normalized file path (no
159// . or .. components, a relative path, no doubled //s, does not end in /,
160// does not start with MT)
161inline bool
162trivially_safe_file_path(std::string const & f)
163{
164 const static std::string bad_chars = std::string("\\:") + constants::illegal_path_bytes + std::string(1, '\0');
165 const static char sep_char('/');
166 const static std::string bad_after_sep_chars("./");
167 if (f.empty())
168 return true;
169 char prev = sep_char;
170 for (std::string::const_iterator i = f.begin(); i != f.end(); ++i)
171 {
172 if (bad_chars.find(*i) != std::string::npos)
173 return false;
174 if (prev == sep_char && bad_after_sep_chars.find(*i) != std::string::npos)
175 return false;
176 prev = *i;
177 }
178 if (prev == sep_char)
179 return false;
180 if (f.size() >= 2 && f[0] == 'M' && f[1] == 'T')
181 return false;
182 return true;
183}
184
185inline void
186verify(file_path & val)
187{
188 static std::map<std::string, std::string> known_good;
189
190 if (val.ok)
191 return;
192
193 std::map<std::string, std::string>::const_iterator j = known_good.find(val());
194 if (j == known_good.end())
195 {
196 if (trivially_safe_file_path(val()))
197 known_good.insert(std::make_pair(val(), val()));
198 else
199 {
200 local_path loc(val());
201 verify(loc);
202 N(!book_keeping_file(loc),
203 F("prohibited book-keeping path in '%s'") % val);
204 const std::string & normalized_val = loc();
205 val.s = normalized_val;
206 known_good.insert(std::make_pair(val(), normalized_val));
207 }
208 }
209 else
210 {
211 val.s = j->second;
212 }
213
214 val.ok = true;
215}
216
217
218// instantiation of various vocab functions
219
220#define ATOMIC(ty) \
221 \
222ty::ty(string const & str) : \
223 s(str), ok(false) \
224{ verify(*this); } \
225 \
226ty::ty(ty const & other) : \
227 s(other.s), ok(other.ok) \
228{ verify(*this); } \
229 \
230ty const & ty::operator=(ty const & other) \
231{ s = other.s; ok = other.ok; \
232 verify(*this); return *this; } \
233 \
234ostream & operator<<(ostream & o, \
235 ty const & a) \
236{ return (o << a.s); }
237
238#define ATOMIC_NOVERIFY(ty) ATOMIC(ty)
239
240
241
242#define ENCODING(enc) \
243 \
244template<typename INNER> \
245enc<INNER>::enc(string const & s) : i(s), ok(false) \
246 { verify(*this); } \
247 \
248template<typename INNER> \
249enc<INNER>::enc(enc<INNER> const & other) \
250 : i(other.i()), ok(other.ok) { verify(*this); } \
251 \
252template<typename INNER> \
253enc<INNER>::enc(INNER const & inner) : \
254 i(inner), ok(false) \
255 { verify(*this); } \
256 \
257template<typename INNER> \
258enc<INNER> const & \
259enc<INNER>::operator=(enc<INNER> const & other) \
260 { i = other.i; ok = other.ok; \
261 verify(*this); return *this;} \
262 \
263template <typename INNER> \
264ostream & operator<<(ostream & o, enc<INNER> const & e) \
265{ return (o << e.i); }
266
267
268#define DECORATE(dec) \
269 \
270template<typename INNER> \
271dec<INNER>::dec(dec<INNER> const & other) \
272 : i(other.i), ok(other.ok) { verify(*this); } \
273 \
274template<typename INNER> \
275dec<INNER>::dec(INNER const & inner) : \
276 i(inner), ok(false) \
277 { verify(*this); } \
278 \
279template<typename INNER> \
280dec<INNER> const & \
281dec<INNER>::operator=(dec<INNER> const & other) \
282 { i = other.i; ok = other.ok; \
283 verify(*this); return *this;} \
284 \
285template <typename INNER> \
286ostream & operator<<(ostream & o, dec<INNER> const & d) \
287{ return (o << d.i); }
288
289
290#define EXTERN
291
292#include "vocab_terms.hh"
293
294#undef EXTERN
295#undef ATOMIC
296#undef DECORATE
297
298template class revision<cert>;
299template class manifest<cert>;
300
301// the rest is unit tests
302
303#ifdef BUILD_UNIT_TESTS
304#include "unit_tests.hh"
305
306static void test_file_path_verification()
307{
308 char const * baddies [] = {"../escape",
309 "foo/../../escape",
310 "/rooted",
311 "foo//nonsense",
312 "MT/foo",
313#ifdef _WIN32
314 "c:\\windows\\rooted",
315 "c:/windows/rooted",
316 "c:thing",
317 "//unc/share",
318 "//unc",
319#endif
320 0 };
321
322 for (char const ** c = baddies; *c; ++c)
323 BOOST_CHECK_THROW(file_path p(*c), informative_failure);
324
325 char const * bad = "\t\r\n\v\f\a\b";
326 char badboy[] = "bad";
327 for (char const * c = bad; *c; ++c)
328 {
329 badboy[1] = *c;
330 BOOST_CHECK_THROW(file_path p(badboy), informative_failure);
331 }
332 BOOST_CHECK_THROW(file_path p(std::string(1, '\0')), informative_failure);
333
334 char const * goodies [] = {"unrooted",
335 "unrooted.txt",
336 "fun_with_underscore.png",
337 "fun-with-hyphen.tiff",
338 "unrooted/../unescaping",
339 "unrooted/general/path",
340 "here/..",
341 0 };
342
343 for (char const ** c = goodies; *c; ++c)
344 BOOST_CHECK_NOT_THROW(file_path p(*c), informative_failure);
345}
346
347static void test_file_path_normalization()
348{
349 BOOST_CHECK(file_path("./foo") == file_path("foo"));
350 BOOST_CHECK(file_path("foo/bar/./baz") == file_path("foo/bar/baz"));
351 BOOST_CHECK(file_path("foo/bar/../baz") == file_path("foo/baz"));
352 BOOST_CHECK(file_path("foo/bar/baz/") == file_path("foo/bar/baz"));
353}
354
355void add_vocab_tests(test_suite * suite)
356{
357 I(suite);
358 suite->add(BOOST_TEST_CASE(&test_file_path_verification));
359 suite->add(BOOST_TEST_CASE(&test_file_path_normalization));
360}
361
362#endif // BUILD_UNIT_TESTS

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status