1 /**
2 * Base64 Encoder/Decoder
3 * 
4 * Copyright:
5 * (C) 1999-2010 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.filters.b64_filt;
12 
13 import botan.filters.filter;
14 import botan.codec.base64;
15 import botan.utils.charset;
16 import botan.utils.exceptn;
17 import botan.utils.types;
18 import botan.utils.mem_ops;
19 import std.algorithm;
20 
21 /**
22 * This class represents a Base64 encoder.
23 */
24 final class Base64Encoder : Filter, Filterable
25 {
26 public:
27     override @property string name() const { return "Base64Encoder"; }
28 
29     /**
30     * Input a part of a message to the encoder.
31     *
32     * Params:
33     *  input = the message to input as a ubyte array
34     *  length = the length of the ubyte array input
35     */
36     override void write(const(ubyte)* input, size_t length)
37     {
38         bufferInsert(m_input, m_position, input, length);
39         if (m_position + length >= m_input.length)
40         {
41             encodeAndSend(m_input.ptr, m_input.length);
42             input += (m_input.length - m_position);
43             length -= (m_input.length - m_position);
44             while (length >= m_input.length)
45             {
46                 encodeAndSend(input, m_input.length);
47                 input += m_input.length;
48                 length -= m_input.length;
49             }
50             copyMem(m_input.ptr, input, length);
51             m_position = 0;
52         }
53         m_position += length;
54     }
55 
56 
57     /**
58     * Inform the Encoder that the current message shall be closed.
59     */
60     override void endMsg()
61     {
62         encodeAndSend(m_input.ptr, m_position, true);
63         
64         if (m_trailing_newline || (m_out_position && m_line_length))
65             send('\n');
66         
67         m_out_position = m_position = 0;
68     }
69 
70     /**
71     * Create a base64 encoder.
72     *
73     * Params:
74     *  breaks = whether to use line breaks in the output
75     *  length = the length of the lines of the output
76     *  t_n = whether to use a trailing newline
77     */
78     this(bool breaks = false, size_t length = 72, bool t_n = false) 
79     {
80         m_line_length = breaks ? length : 0;
81         m_trailing_newline = t_n && breaks;
82         m_input = 48;
83         m_output = 64;
84         m_position = 0;
85         m_out_position = 0;
86     }
87 
88     // Interface fallthrough
89     override bool attachable() { return super.attachable(); }
90     override void startMsg() { super.startMsg(); }
91     override void setNext(Filter* filters, size_t sz) { super.setNext(filters, sz); }
92 
93 private:
94     /*
95     * Encode and send a block
96     */
97     void encodeAndSend(const(ubyte)* input, size_t length,
98                          bool final_inputs = false)
99     {
100         while (length)
101         {
102             const size_t proc = std.algorithm.min(length, m_input.length);
103             
104             size_t consumed = 0;
105             size_t produced = base64Encode(cast(char*)(m_output.ptr), input,
106                                             proc, consumed, final_inputs);
107             
108             doOutput(m_output.ptr, produced);
109             
110             // FIXME: s/proc/consumed/?
111             input += proc;
112             length -= proc;
113         }
114     }
115 
116     /*
117     * Handle the output
118     */
119     void doOutput(const(ubyte)* input, size_t length)
120     {
121         if (m_line_length == 0)
122             send(input, length);
123         else
124         {
125             size_t remaining = length, offset = 0;
126             while (remaining)
127             {
128                 size_t sent = std.algorithm.min(m_line_length - m_out_position, remaining);
129                 send(input + offset, sent);
130                 m_out_position += sent;
131                 remaining -= sent;
132                 offset += sent;
133                 if (m_out_position == m_line_length)
134                 {
135                     send('\n');
136                     m_out_position = 0;
137                 }
138             }
139         }
140     }
141 
142 
143     const size_t m_line_length;
144     const bool m_trailing_newline;
145     Vector!ubyte m_input, m_output;
146     size_t m_position, m_out_position;
147 }
148 
149 /**
150 * This object represents a Base64 decoder.
151 */
152 final class Base64Decoder : Filter, Filterable
153 {
154 public:
155     override @property string name() const { return "Base64Decoder"; }
156 
157     /**
158     * Input a part of a message to the decoder.
159     *
160     * Params:
161     *  input = the message to input as a ubyte array
162     *  length = the length of the ubyte array input
163     */
164     override void write(const(ubyte)* input, size_t length)
165     {
166         while (length)
167         {
168             size_t to_copy = std.algorithm.min(length, m_input.length - m_position);
169 			if (to_copy == 0)
170 			{
171 				m_input.resize(m_input.length*2);
172 				m_output.resize(m_output.length*2);
173 			}
174             copyMem(&m_input[m_position], input, to_copy);
175             m_position += to_copy;
176             
177             size_t consumed = 0;
178             size_t written = base64Decode(m_output.ptr,
179                                            cast(const(char)*)(m_input.ptr),
180                                            m_position,
181                                            consumed,
182                                            false,
183                                            m_checking != FULL_CHECK);
184             
185             send(m_output, written);
186             
187             if (consumed != m_position)
188             {
189                 copyMem(m_input.ptr, &m_input[consumed], m_position - consumed);
190                 m_position = m_position - consumed;
191             }
192             else
193                 m_position = 0;
194             
195             length -= to_copy;
196             input += to_copy;
197         }
198     }
199 
200     /**
201     * Finish up the current message
202     */
203     override void endMsg()
204     {
205         size_t consumed = 0;
206         size_t written = base64Decode(m_output.ptr,
207                                        cast(const(char)*)(m_input.ptr),
208                                        m_position,
209                                        consumed,
210                                        true,
211                                        m_checking != FULL_CHECK);
212         
213         send(m_output, written);
214         
215         const bool not_full_bytes = consumed != m_position;
216         
217         m_position = 0;
218         
219         if (not_full_bytes)
220             throw new InvalidArgument("Base64Decoder: Input not full bytes");
221     }
222 
223     /**
224     * Create a base64 decoder.
225     *
226     * Params:
227     *  c = the type of checking that shall be performed by
228     * the decoder
229     */
230     this(DecoderChecking c = NONE)
231     {
232         m_checking = c;
233         m_input = 64;
234         m_output = 48;
235         m_position = 0;
236     }
237 
238     // Interface fallthrough
239     override bool attachable() { return super.attachable(); }
240     override void startMsg() { super.startMsg(); }
241     override void setNext(Filter* filters, size_t sz) { super.setNext(filters, sz); }
242 private:
243     const DecoderChecking m_checking;
244     Vector!ubyte m_input, m_output;
245     size_t m_position;
246 }