IBR-DTNSuite  0.10
Base64Reader.cpp
Go to the documentation of this file.
1 /*
2  * Base64Reader.cpp
3  *
4  * Copyright (C) 2011 IBR, TU Braunschweig
5  *
6  * Written-by: Johannes Morgenroth <morgenroth@ibr.cs.tu-bs.de>
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  */
21 
23 #include "ibrcommon/Logger.h"
24 #include <stdio.h>
25 #include <vector>
26 
27 namespace ibrcommon
28 {
29  Base64Reader::Base64Reader(std::istream &stream, const size_t limit, const size_t buffer)
30  : std::istream(this), _stream(stream), data_buf_(buffer), data_size_(buffer), _base64_state(0), _base64_padding(0), _byte_read(0), _byte_limit(limit)
31  {
32  setg(0, 0, 0);
33  }
34 
36  {
37  }
38 
39  void Base64Reader::set_b64(char val)
40  {
41  switch (_base64_state)
42  {
43  case 0:
44  _group.b64_0(val);
45  _base64_state++;
46  break;
47 
48  case 1:
49  _group.b64_1(val);
50  _base64_state++;
51  break;
52 
53  case 2:
54  _group.b64_2(val);
55  _base64_state++;
56  break;
57 
58  case 3:
59  _group.b64_3(val);
60  _base64_state++;
61  break;
62  }
63  }
64 
65  std::char_traits<char>::int_type Base64Reader::underflow()
66  {
67  // signal EOF if end of stream is reached
68  if (_stream.eof())
69  {
70  return std::char_traits<char>::eof();
71  }
72 
73  if ((_byte_limit > 0) && (_byte_read >= _byte_limit))
74  {
75  return std::char_traits<char>::eof();
76  }
77 
78  // read some data
79  std::vector<char> buffer(data_size_);
80 
81  if (_byte_limit > 0)
82  {
83  // get the remaining bytes
84  size_t bytes_to_read = _byte_limit - _byte_read;
85 
86  // calculate base64 length for given plaintext length
87  bytes_to_read = Base64::getLength(bytes_to_read);
88 
89  // minus pending bytes in the buffer
90  bytes_to_read -= _base64_state;
91 
92  // debug
93  IBRCOMMON_LOGGER_DEBUG_TAG("Base64Reader", 60) <<
94  "[Base64Reader] base64 bytes to read: " << bytes_to_read <<
95  "; payload bytes: " << (_byte_limit - _byte_read) <<
96  "; byte in buffer: " << (int)_base64_state << IBRCOMMON_LOGGER_ENDL;
97 
98  // choose buffer size of remaining bytes
99  if (bytes_to_read > data_size_) bytes_to_read = data_size_;
100 
101  // read from the stream
102  _stream.read((char*)&buffer[0], bytes_to_read);
103  }
104  else
105  {
106  _stream.read((char*)&buffer[0], data_size_);
107  }
108 
109  size_t len = _stream.gcount();
110 
111  // position in array
112  size_t decoded_bytes = 0;
113 
114  for (size_t i = 0; i < len; ++i)
115  {
116  const int c = Base64::getCharType( buffer[i] );
117 
118  switch (c)
119  {
120  case Base64::UNKOWN_CHAR:
121  // skip unknown chars
122  continue;
123 
124  case Base64::EQUAL_CHAR:
125  {
126  switch (_base64_state)
127  {
128  case 0:
129  // error - first character can not be a '='
130  return std::char_traits<char>::eof();
131 
132  case 1:
133  // error - second character can not be a '='
134  return std::char_traits<char>::eof();
135 
136  case 2:
137  // only one byte left
138  _base64_padding = 2;
139  break;
140 
141  case 3:
142  // only one byte left
143  if (_base64_padding == 0)
144  {
145  _base64_padding = 3;
146  }
147  break;
148  }
149 
150  set_b64(0);
151  break;
152  }
153 
154  default:
155  {
156  // put char into the decode buffer
157  set_b64(static_cast<char>(c));
158  break;
159  }
160  }
161 
162  // when finished
163  if (_base64_state == 4)
164  {
165  switch (_base64_padding)
166  {
167  case 0:
168  data_buf_[decoded_bytes] = _group.get_0(); decoded_bytes++;
169  data_buf_[decoded_bytes] = _group.get_1(); decoded_bytes++;
170  data_buf_[decoded_bytes] = _group.get_2(); decoded_bytes++;
171  break;
172 
173  case 3:
174  data_buf_[decoded_bytes] = _group.get_0(); decoded_bytes++;
175  data_buf_[decoded_bytes] = _group.get_1(); decoded_bytes++;
176  break;
177 
178  case 2:
179  data_buf_[decoded_bytes] = _group.get_0(); decoded_bytes++;
180  break;
181  }
182 
183  _base64_state = 0;
184  _base64_padding = 0;
185  _group.zero();
186  }
187  }
188 
189  // if we could not decode any byte, we have to get more input bytes
190  if (decoded_bytes == 0)
191  {
192  // call underflow() recursively
193  return underflow();
194  }
195 
196  // Since the input buffer content is now valid (or is new)
197  // the get pointer should be initialized (or reset).
198  setg(&data_buf_[0], &data_buf_[0], &data_buf_[0] + decoded_bytes);
199 
200  if (_byte_limit > 0)
201  {
202  _byte_read += decoded_bytes;
203  }
204 
205  return std::char_traits<char>::not_eof(data_buf_[0]);
206  }
207 }