monotone

monotone Mtn Source Tree

Root/src/transforms.cc

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#include "base.hh"
11#include <iterator>
12#include <botan/botan.h>
13#include <botan/sha160.h>
14
15#include "botan_pipe_cache.hh"
16#include "gzip.hh"
17
18#include "transforms.hh"
19#include "char_classifiers.hh"
20
21using std::string;
22using Botan::Pipe;
23using Botan::Base64_Encoder;
24using Botan::Base64_Decoder;
25using Botan::Hex_Encoder;
26using Botan::Hex_Decoder;
27using Botan::Gzip_Compression;
28using Botan::Gzip_Decompression;
29using Botan::Hash_Filter;
30
31// this file contans various sorts of string transformations. each
32// transformation should be self-explanatory from its type signature. see
33// transforms.hh for the summary.
34
35// NB this file uses very "value-centric" functional approach; even though
36// many of the underlying transformations are "stream-centric" and the
37// underlying libraries (eg. crypto++) are stream oriented. this will
38// probably strike some people as contemptably inefficient, since it means
39// that occasionally 1, 2, or even 3 copies of an entire file will wind up
40// in memory at once. I am taking this approach for 3 reasons: first, I
41// want the type system to help me and value types are much easier to work
42// with than stream types. second, it is *much* easier to debug a program
43// that operates on values than streams, and correctness takes precedence
44// over all other features of this program. third, this is a peer-to-peer
45// sort of program for small-ish source-code text files, not a fileserver,
46// and is memory-limited anyways (for example, storing things in sqlite
47// requires they be able to fit in memory). you're hopefully not going to
48// be dealing with hundreds of users hammering on locks and memory
49// concurrently.
50//
51// if future analysis proves these assumptions wrong, feel free to revisit
52// the matter, but bring strong evidence along with you that the stream
53// paradigm "must" be used. this program is intended for source code
54// control and I make no bones about it.
55
56NORETURN(static inline void error_in_transform(std::exception & e));
57
58static inline void
59error_in_transform(std::exception & e, origin::type caused_by)
60{
61 // these classes can all indicate data corruption
62 if (typeid(e) == typeid(Botan::Encoding_Error)
63 || typeid(e) == typeid(Botan::Decoding_Error)
64 || typeid(e) == typeid(Botan::Stream_IO_Error)
65 || typeid(e) == typeid(Botan::Invalid_Argument)
66 || typeid(e) == typeid(Botan::Integrity_Failure))
67 {
68 // clean up the what() string a little: throw away the
69 // "botan: TYPE: " part...
70 string w(e.what());
71 string::size_type pos = w.find(':');
72 pos = w.find(':', pos+1);
73 w = string(w.begin() + pos + 2, w.end());
74
75 // ... downcase the rest of it and replace underscores with spaces.
76 for (string::iterator p = w.begin(); p != w.end(); p++)
77 {
78 *p = to_lower(*p);
79 if (*p == '_')
80 *p = ' ';
81 }
82
83 E(false, caused_by,
84 F("%s\n"
85 "This may be due to a memory glitch, data corruption during\n"
86 "a network transfer, corruption of your database or workspace,\n"
87 "or a bug in monotone. If the error persists, please report\n"
88 "it to '%s'.")
89 % w % PACKAGE_BUGREPORT);
90 }
91 else
92 throw;
93
94 I(false); // can't get here
95}
96
97// full specializations for the usable cases of xform<XFM>()
98// use extra error checking in base64 and hex decoding
99#define SPECIALIZE_XFORM(T, carg) \
100 template<> string xform<T>(string const & in, origin::type made_from) \
101 { \
102 string out; \
103 static cached_botan_pipe pipe(new Pipe(new T(carg))); \
104 try \
105 { \
106 /* this might actually be a problem here */ \
107 I(pipe->message_count() < Pipe::LAST_MESSAGE); \
108 pipe->process_msg(in); \
109 out = pipe->read_all_as_string(Pipe::LAST_MESSAGE); \
110 } \
111 catch (std::exception & e) \
112 { \
113 pipe.reset(new Pipe(new T(carg))); \
114 error_in_transform(e, made_from); \
115 } \
116 return out; \
117 }
118
119SPECIALIZE_XFORM(Base64_Encoder,);
120SPECIALIZE_XFORM(Base64_Decoder, Botan::IGNORE_WS);
121//SPECIALIZE_XFORM(Hex_Encoder, Hex_Encoder::Lowercase);
122template<> string xform<Botan::Hex_Encoder>(string const & in,
123 origin::type made_from)
124{
125 string out;
126 out.reserve(in.size()<<1);
127 for (string::const_iterator i = in.begin();
128 i != in.end(); ++i)
129 {
130 int h = (*i>>4) & 0x0f;
131 if (h < 10)
132 out.push_back(h + '0');
133 else
134 out.push_back(h + 'a' - 10);
135 int l = *i & 0x0f;
136 if (l < 10)
137 out.push_back(l + '0');
138 else
139 out.push_back(l + 'a' - 10);
140 }
141 return out;
142}
143//SPECIALIZE_XFORM(Hex_Decoder, Botan::IGNORE_WS);
144template<> string xform<Botan::Hex_Decoder>(string const & in,
145 origin::type made_from)
146{
147 string out;
148 out.reserve(in.size()>>1);
149 bool high(true);
150 int o = 0;
151 for (string::const_iterator i = in.begin();
152 i != in.end(); ++i)
153 {
154 int c = *i;
155 if (c >= '0' && c <= '9')
156 {
157 o += (c - '0');
158 }
159 else if (c >= 'a' && c <= 'f')
160 {
161 o += (c - 'a' + 10);
162 }
163 else if (c >= 'A' && c <= 'F')
164 {
165 o += (c - 'A' + 10);
166 }
167 else if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
168 {
169 continue;
170 }
171 else // garbage
172 {
173 try
174 {
175 throw Botan::Decoding_Error(string("invalid hex character '") + (char)c + "'");
176 }
177 catch(std::exception & e)
178 {
179 error_in_transform(e, made_from);
180 }
181 }
182 if (high)
183 {
184 o <<= 4;
185 }
186 else
187 {
188 out.push_back(o);
189 o = 0;
190 }
191 high = !high;
192 }
193 if (!high)
194 { // Hex string wasn't a whole number of bytes
195 //I(false); // Drop the last char (!!)
196 }
197 return out;
198}
199SPECIALIZE_XFORM(Gzip_Compression,);
200SPECIALIZE_XFORM(Gzip_Decompression,);
201
202template <>
203std::string decode_base64_as<std::string>(std::string const & in,
204 origin::type made_from)
205{
206 return xform<Botan::Base64_Decoder>(in, made_from);
207}
208
209template <typename T>
210void pack(T const & in, base64< gzip<T> > & out)
211{
212 string tmp;
213 tmp.reserve(in().size()); // FIXME: do some benchmarking and make this a constant::
214
215 static cached_botan_pipe pipe(new Pipe(new Gzip_Compression,
216 new Base64_Encoder));
217 try
218 {
219 pipe->process_msg(in());
220 tmp = pipe->read_all_as_string(Pipe::LAST_MESSAGE);
221 out = base64< gzip<T> >(tmp, in.made_from);
222 }
223 catch (std::exception & e)
224 {
225 pipe.reset(new Pipe(new Gzip_Compression,
226 new Base64_Encoder));
227 error_in_transform(e, in.made_from);
228 }
229}
230
231template <typename T>
232void unpack(base64< gzip<T> > const & in, T & out)
233{
234 static cached_botan_pipe pipe(new Pipe(new Base64_Decoder,
235 new Gzip_Decompression));
236 try
237 {
238 pipe->process_msg(in());
239 out = T(pipe->read_all_as_string(Pipe::LAST_MESSAGE), in.made_from);
240 }
241 catch (std::exception & e)
242 {
243 pipe.reset(new Pipe(new Base64_Decoder,
244 new Gzip_Decompression));
245 error_in_transform(e, in.made_from);
246 }
247}
248
249// specialise them
250template void pack<data>(data const &, base64< gzip<data> > &);
251template void pack<delta>(delta const &, base64< gzip<delta> > &);
252template void unpack<data>(base64< gzip<data> > const &, data &);
253template void unpack<delta>(base64< gzip<delta> > const &, delta &);
254
255
256// identifier (a.k.a. sha1 signature) calculation
257
258void
259calculate_ident(data const & dat,
260 id & ident)
261{
262 static cached_botan_pipe p(new Pipe(new Hash_Filter("SHA-160")));
263 try
264 {
265 p->process_msg(dat());
266 ident = id(p->read_all_as_string(Pipe::LAST_MESSAGE), dat.made_from);
267 }
268 catch (std::exception & e)
269 {
270 p.reset(new Pipe(new Hash_Filter("SHA-160")));
271 error_in_transform(e, dat.made_from);
272 }
273}
274
275void
276calculate_ident(file_data const & dat,
277 file_id & ident)
278{
279 id tmp;
280 calculate_ident(dat.inner(), tmp);
281 ident = file_id(tmp);
282}
283
284void
285calculate_ident(manifest_data const & dat,
286 manifest_id & ident)
287{
288 id tmp;
289 calculate_ident(dat.inner(), tmp);
290 ident = manifest_id(tmp);
291}
292
293void
294calculate_ident(revision_data const & dat,
295 revision_id & ident)
296{
297 id tmp;
298 calculate_ident(dat.inner(), tmp);
299 ident = revision_id(tmp);
300}
301
302
303// Local Variables:
304// mode: C++
305// fill-column: 76
306// c-file-style: "gnu"
307// indent-tabs-mode: nil
308// End:
309// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

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