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 }