1 /**
2 * DataSource
3 * 
4 * Copyright:
5 * (C) 1999-2007 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *     2012 Markus Wanner
8 *
9 * License:
10 * Botan is released under the Simplified BSD License (see LICENSE.md)
11 */
12 module botan.filters.data_src;
13 
14 import botan.constants;
15 import memutils.allocators;
16 import memutils.utils;
17 import memutils.vector;
18 import botan.utils.types;
19 import std.stdio;
20 import botan.utils.exceptn;
21 import botan.utils.mem_ops;
22 import std.algorithm;
23 
24 alias DataSource = RefCounted!(DataSourceImpl, AppMem);
25 alias DataSourceMemory = RefCounted!(DataSourceMemoryImpl, AppMem);
26 alias DataSourceStream = RefCounted!(DataSourceStreamImpl, AppMem);
27 
28 /**
29 * This class represents an abstract data source object.
30 */
31 interface DataSourceImpl
32 {
33 public:
34     /**
35     * Read from the source. Moves the internal offset so that every
36     * call to read will return a new portion of the source.
37     *
38     * Params:
39     *  output = the ubyte array to write the result to
40     *  length = the length of the ubyte array out
41     * Returns: length in bytes that was actually read and put
42     * into out
43     */
44     size_t read(ubyte* output, size_t length);
45 
46     /**
47     * Read from the source but do not modify the internal
48     * offset. Consecutive calls to peek() will return portions of
49     * the source starting at the same position.
50     *
51     * Params:
52     *  output = the ubyte array to write the output to
53     *  length = the length of the ubyte array out
54     *  peek_offset = the offset into the stream to read at
55     * Returns: length in bytes that was actually read and put
56     * into out
57     */
58     size_t peek(ubyte* output, size_t length, size_t peek_offset) const;
59 
60     /**
61     * Test whether the source still has data that can be read.
62     * Returns: true if there is still data to read, false otherwise
63     */
64     bool endOfData() const;
65     /**
66     * return the id of this data source
67     * Returns: string representing the id of this data source
68     */
69     string id() const;
70 
71     /**
72     * Read one ubyte.
73     *
74     * Params:
75     *  output = the ubyte to read to
76     * Returns: length in bytes that was actually read and put
77     * into out
78     */
79     final size_t readByte(ref ubyte output)
80     {
81         return read(&output, 1);
82     }
83 
84 
85     /**
86     * Peek at one ubyte.
87     *
88     * Params:
89     *  output = an output ubyte
90     * Returns: length in bytes that was actually read and put
91     * into out
92     */
93     final size_t peekByte(ref ubyte output) const
94     {
95         return peek(&output, 1, 0);
96     }
97 
98 
99     /**
100     * Discard the next N bytes of the data
101     * Params:
102     *  n = the number of bytes to discard
103     * Returns: number of bytes actually discarded
104     */
105     final size_t discardNext(size_t n)
106     {
107         size_t discarded = 0;
108         ubyte dummy;
109         foreach (size_t j; 0 .. n)
110             discarded += readByte(dummy);
111         return discarded;
112     }
113 
114 
115     /**
116     * Returns: number of bytes read so far.
117     */
118     size_t getBytesRead() const;
119 
120 }
121 
122 
123 /**
124 * This class represents a Memory-Based DataSource
125 */
126 class DataSourceMemoryImpl : DataSourceImpl
127 {
128 public:
129     override size_t read(ubyte* output, size_t length)
130     {
131         if (m_offset == m_source.length) return 0;
132         size_t got = std.algorithm.min(m_source.length - m_offset, length);
133         copyMem(output, &m_source[m_offset], got);
134         m_offset += got;
135         return got;
136     }
137 
138     /*
139     * Peek into a memory buffer
140     */
141     override size_t peek(ubyte* output, size_t length, size_t peek_offset) const
142     {
143         const size_t bytes_left = m_source.length - m_offset;
144         if (peek_offset >= bytes_left) return 0;
145         
146         size_t got = std.algorithm.min(bytes_left - peek_offset, length);
147         copyMem(output, &m_source[m_offset + peek_offset], got);
148         return got;
149     }
150 
151     /*
152     * Check if the memory buffer is empty
153     */
154     override bool endOfData() const
155     {
156         return (m_offset == m_source.length);
157     }
158 
159 
160     /**
161     * Construct a memory source that reads from a string
162     * Params:
163     *  input = the string to read from
164     */
165     this(in string input) 
166     {
167         m_source = SecureVector!ubyte((cast(const(ubyte)*)input.ptr)[0 .. input.length]);
168         m_offset = 0;
169     }
170 
171 
172     /**
173     * Construct a memory source that reads from a ubyte array
174     * Params:
175     *  input = the ubyte array to read from
176     *  length = the length of the ubyte array
177     */
178     this(const(ubyte)* input, size_t length)
179     {
180         m_source = SecureVector!ubyte(input[0 .. length]);
181         m_offset = 0; 
182     }
183 
184     /**
185     * Construct a memory source that reads from a referenced vector
186     * Params:
187     *  input = the MemoryRegion to read from
188     */
189     this(T, ALLOC)(auto const ref RefCounted!(Vector!(T, ALLOC), ALLOC) input)
190     {
191         m_source = SecureVector!ubyte(input[]);
192         m_offset = 0;
193     }
194 
195     /**
196     * Construct a memory source that reads from a vector
197     * Params:
198     *  input = the MemoryRegion to read from
199     */
200     this(T, ALLOC)(auto const ref Vector!(T, ALLOC) input) {
201         m_source = SecureVector!ubyte(input.ptr[0 .. input.length]);
202         m_offset = 0;
203     }
204 
205     /**
206     * Construct a memory source that reads from a vector*
207     * Params:
208     *  input = the MemoryRegion to read from
209     */
210     this(T, ALLOC)(const Vector!(T, ALLOC)* input) {
211         m_source = SecureVector!ubyte(input.ptr[0 .. input.length]);
212         m_offset = 0;
213     }
214 
215     override size_t getBytesRead() const { return m_offset; }
216     override string id() const { return ""; }
217 private:
218     SecureVector!ubyte m_source;
219     size_t m_offset;
220 }
221 
222 /**
223 * This class represents a Stream-Based DataSource.
224 */
225 class DataSourceStreamImpl : DataSourceImpl
226 {
227 public:
228     /*
229     * Read from a stream
230     */
231     override size_t read(ubyte* output, size_t length)
232     {
233         //logTrace("Read for ", cast(void*)this, " len: ", length, " offset ", m_total_read);
234         ubyte[] data;
235         try data = m_source.rawRead(output[0..length]);
236         catch (Exception e)
237             throw new StreamIOError("read: Source failure..." ~ e.toString());
238         
239         size_t got = data.length;
240         m_total_read += got;
241         //logTrace("Read total: ", m_total_read, " end of stream? ", endOfData().to!string);
242         return got;
243     }
244 
245     /*
246     * Peek into a stream
247     */
248     override size_t peek(ubyte* output, size_t length, size_t offset) const
249     {
250         //logTrace("Peek for ", cast(void*)this, " len: ", length, " offset ", offset, " total read ", m_total_read);
251         File file;
252         if (endOfData()) {
253             file = File(m_identifier, "rb");
254         }
255            // throw new InvalidState("DataSourceStream: Cannot peek when out of data " ~ m_source.name);
256         else file = cast(File)m_source;
257         size_t got = 0;
258         
259         file.seek(offset, SEEK_SET);
260         ubyte[] data;
261         ubyte[] output_buf = output[0 .. length];
262         try data = file.rawRead(output_buf);
263         catch (Exception e)
264             throw new StreamIOError("peek: Source failure..." ~ e.toString());
265         
266         got = data.length;
267         //logTrace("Read total: ", got, " data: ", data);
268         if (!file.isOpen) {
269             file = File(m_identifier, "r");
270         }
271         else
272         if (file.eof || file.error()) {
273             file.clearerr();
274             file.rewind();
275         }
276         
277         file.seek(m_total_read, SEEK_SET);
278         return got;
279     }
280 
281     /*
282     * Check if the stream is empty or in error
283     */
284     override bool endOfData() const
285     {
286         return !m_source.isOpen || m_source.eof || m_source.error();
287     }
288 
289     /*
290     * Return a human-readable ID for this stream
291     */
292     override string id() const
293     {
294         return m_identifier;
295     }
296 
297     /*
298     * DataSourceStream Constructor
299     */
300     this(ref File input, in string name)
301     {
302         m_identifier = name;
303         m_source = input;
304         m_total_read = 0;
305     }
306 
307     /**
308     * Construct a Stream-Based DataSource from file
309     * Params:
310     *  path = the name of the file
311     *  use_binary = whether to treat the file as binary or not
312     */
313     this(in string path, bool use_binary = false)
314     {
315         
316         m_identifier = path;
317         m_source = File(path, use_binary ? "rb" : "r");
318         m_source.open(path);
319         m_total_read = 0;
320         if (m_source.error())
321         {
322             throw new StreamIOError("DataSource: Failure opening file " ~ path);
323         }
324     }
325 
326     /*
327     * DataSourceStream Destructor
328     */
329     ~this()
330     {
331 
332     }
333 
334     override size_t getBytesRead() const { return m_total_read; }
335 private:
336     const string m_identifier;
337 
338     File m_source;
339     size_t m_total_read;
340 }