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