/[debian]/mimetic/branches/upstream/current/mimetic/codec/qp.h
ViewVC logotype

Contents of /mimetic/branches/upstream/current/mimetic/codec/qp.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 128 - (show annotations)
Sat Feb 25 16:49:20 2006 UTC (15 years, 6 months ago) by gregoa
File MIME type: text/plain
File size: 13686 byte(s)
[svn-inject] Installing original source of mimetic
1 /***************************************************************************
2 copyright : (C) 2002-2005 by Stefano Barbato
3 email : stefano@codesink.org
4
5 $Id: qp.h,v 1.16 2005/02/23 11:48:15 tat Exp $
6 ***************************************************************************/
7
8 /***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16 #ifndef _MIMETIC_CODEC_QP_H_
17 #define _MIMETIC_CODEC_QP_H_
18 #include <iostream>
19 #include <string>
20 #include <sstream>
21 #include <cassert>
22 #include <mimetic/libconfig.h>
23 #include <mimetic/circular_buffer.h>
24 #include <mimetic/codec/codec_base.h>
25 #include <mimetic/codec/codec_chain.h>
26
27 namespace mimetic
28 {
29
30 class QP
31 {
32 friend class test_qp;
33 enum { LF = 0xA, CR = 0xD, NL = LF, TAB = 9, SP = 32 };
34 enum { default_maxlen = 76 };
35 enum {
36 printable, /* print as-is */
37 tab, /* print if !isBinary */
38 sp, /* ' ' */
39 newline, /* cr or lf; encode if isBinary*/
40 binary, /* rest of the ascii map */
41 unsafe /* "!\"#$@[]\\^`{}|~" */
42 };
43 static char sTb[256];
44
45 public:
46
47 /// quoted-printable encoder
48 /*!
49
50 \sa encode decode
51 */
52 class Encoder: public buffered_codec, public chainable_codec<Encoder>
53 {
54 enum { laBufSz = 5 }; // look-ahead buffer
55 size_t m_pos, m_maxlen;
56 bool m_binary;
57 circular_buffer<char_type> m_cbuf;
58
59 template<typename OutIt>
60 void hardLineBrk(OutIt& out)
61 {
62 *out = NL; ++out;
63 m_pos = 1;
64 }
65 template<typename OutIt>
66 void softLineBrk(OutIt& out)
67 {
68 *out = '='; ++out;
69 hardLineBrk(out);
70 }
71 template<typename OutIt>
72 void write(char_type ch, OutIt& out)
73 {
74 bool is_last_ch = m_cbuf.empty();
75 if(!is_last_ch && m_pos == m_maxlen)
76 softLineBrk(out);
77 *out = ch; ++out;
78 m_pos++;
79 }
80 template<typename OutIt>
81 void writeHex(char_type ch, OutIt& out)
82 {
83 static char_type hexc[] =
84 {
85 '0', '1', '2', '3', '4', '5' ,'6', '7', '8', '9',
86 'A', 'B', 'C', 'D', 'E', 'F'
87 };
88 bool is_last_ch = m_cbuf.empty();
89 if(m_pos + (is_last_ch ? 1 : 2) >= m_maxlen)
90 softLineBrk(out);
91 // write out =HH
92 *out = '='; ++out;
93 *out = hexc[ch >> 4]; ++out;
94 *out = hexc[ch & 0xf]; ++out;
95 m_pos += 3;
96 }
97 template<typename OutIt>
98 void encodeChar(char_type c, OutIt& out)
99 {
100 int cnt = m_cbuf.count();
101 switch(sTb[c])
102 {
103 case printable:
104 if(m_pos == 1)
105 {
106 switch(c)
107 {
108 case 'F': // hex enc on "^From .*"
109 if(cnt>=4 && m_cbuf.compare(0,4,"rom "))
110 {
111 writeHex(c,out);
112 return;
113 }
114 break;
115 case '.': // hex encode if "^.[\r\n]" or on eof
116 if(!cnt || sTb[ m_cbuf[0] ] == newline)
117 {
118 writeHex(c,out);
119 return;
120 }
121 break;
122 }
123 }
124 write(c,out);
125 break;
126 case tab:
127 case sp:
128 // on binary encoding, or last input ch or newline
129 if(m_binary || !cnt || sTb[ m_cbuf[0] ] == newline)
130 writeHex(c,out);
131 else
132 write(c,out);
133 break;
134 case newline:
135 if(m_binary)
136 writeHex(c, out);
137 else {
138 if(cnt && m_cbuf[0] == (c == CR ? LF : CR))
139 m_cbuf.pop_front(); // eat it
140 hardLineBrk(out);
141 }
142 break;
143 case binary:
144 if(!m_binary) m_binary = 1; // switch to binary mode
145 writeHex(c, out);
146 break;
147 case unsafe:
148 writeHex(c, out);
149 break;
150 }
151 }
152 public:
153 /*!
154 Constructor
155 \param isBinary if true all space and newline characters will be
156 treated like binary chars and will be hex encoded (useful if you
157 want to encode a binary file).
158 */
159 Encoder(bool isBinary = false)
160 : m_pos(1), m_maxlen(default_maxlen),
161 m_binary(isBinary), m_cbuf(laBufSz)
162 {
163 }
164 /*! Returns the name of the codec ("Quoted-Printable") */
165 const char* name() const { return "Quoted-Printable"; }
166 /*! Returns the max line length */
167 size_t maxlen()
168 {
169 return m_maxlen;
170 }
171 /*!
172 Set the max line length. No more then \p i chars will be
173 printed on one line.
174 */
175 void maxlen(size_t i)
176 {
177 m_maxlen = i;
178 }
179 /*!
180 Encodes [\p bit,\p eit) and write any encoded char to \p out.
181 */
182 template<typename InIt, typename OutIt>
183 void process(InIt bit, InIt eit, OutIt out)
184 {
185 for(; bit != eit; ++bit)
186 process(*bit, out);
187 flush(out);
188 }
189 /*!
190 Encodes \p ic and write any encoded output char to \p out.
191 \warning You must call flush() when all chars have been
192 processed by the encode funcion.
193 \n
194 \code
195 while( (c = getchar()) != EOF )
196 qp.process(c, out);
197 qp.flush();
198 \endcode
199 \n
200 \sa flush()
201 */
202 template<typename OutIt>
203 void process(char_type ic, OutIt& out)
204 {
205 m_cbuf.push_back(ic);
206 if(m_cbuf.count() < laBufSz)
207 return;
208 char_type c = m_cbuf.front();
209 m_cbuf.pop_front();
210 encodeChar(c, out);
211 }
212 /*!
213 Write to \p out any buffered encoded char.
214 */
215 template<typename OutIt>
216 void flush(OutIt& out)
217 {
218 char_type c;
219 while(!m_cbuf.empty())
220 {
221 c = m_cbuf.front();
222 m_cbuf.pop_front();
223 encodeChar(c, out);
224 }
225 }
226 };
227
228 /// quoted-printable decoder
229 /*!
230
231 \sa encode decode
232 */
233 class Decoder: public buffered_codec, public chainable_codec<Encoder>
234 {
235 enum { laBufSz = 80 }; // look-ahead buffer
236 enum {
237 sWaitingChar,
238 sAfterEq,
239 sWaitingFirstHex,
240 sWaitingSecondHex,
241 sBlank,
242 sNewline,
243 sOtherChar
244 };
245 size_t m_pos, m_maxlen;
246
247
248 int m_state, m_nl;
249 std::string m_prev;
250
251 template<typename OutIt>
252 void hardLineBrk(OutIt& out) const
253 {
254 *out = NL; ++out;
255 }
256 template<typename OutIt>
257 void write(char_type ch, OutIt& out) const
258 {
259 *out = ch; ++out;
260 }
261 bool isnl(char_type c) const
262 {
263 return (c == CR || c == LF);
264 }
265 template<typename OutIt>
266 void flushPrev(OutIt& out)
267 {
268 copy(m_prev.begin(), m_prev.end(), out);
269 m_prev.clear();
270 }
271 int hex_to_int(char_type c) const
272 {
273 if( c >= '0' && c <='9') return c - '0';
274 else if( c >= 'A' && c <='F') return c - 'A' + 10;
275 else if( c >= 'a' && c <='f') return c - 'a' + 10;
276 else return 0;
277 }
278 bool ishex(char_type c) const
279 {
280 return (c >= '0' && c <= '9') ||
281 (c >= 'A' && c <= 'F') ||
282 (c >= 'a' && c <= 'f');
283 }
284 template<typename OutIt>
285 void decodeChar(char_type c, OutIt& out)
286 {
287 for(;;)
288 {
289 switch(m_state)
290 {
291 case sBlank:
292 if(isblank(c))
293 m_prev.append(1,c);
294 else if(isnl(c)) {
295 // soft linebrk & ignore trailing blanks
296 m_prev.clear();
297 m_state = sWaitingChar;
298 } else {
299 flushPrev(out);
300 m_state = sWaitingChar;
301 continue;
302 }
303 return;
304 case sAfterEq:
305 if(isblank(c))
306 m_prev.append(1,c);
307 else if(isnl(c)) {
308 // soft linebrk
309 m_state = sNewline;
310 continue;
311 } else {
312 if(m_prev.length() > 1)
313 {
314 // there're blanks after =
315 flushPrev(out);
316 m_state = sWaitingChar;
317 } else
318 m_state = sWaitingFirstHex;
319 continue;
320 }
321 return;
322 case sWaitingFirstHex:
323 if(!ishex(c))
324 {
325 // malformed: =[not-hexch]
326 flushPrev(out);
327 write(c, out);
328 m_state = sWaitingChar;
329 return;
330 } else {
331 m_prev.append(1,c);
332 m_state = sWaitingSecondHex;
333 }
334 return;
335 case sWaitingSecondHex:
336 if(!ishex(c))
337 { // malformed (=[hexch][not-hexch])
338 flushPrev(out);
339 write(c, out);
340 } else {
341 char_type oc, last;
342 assert(m_prev.length());
343 last = m_prev[m_prev.length()-1];
344 oc = hex_to_int(last) << 4 |
345 hex_to_int(c) ;
346 write(oc,out);
347 m_prev.clear();
348 }
349 m_state = sWaitingChar;
350 return;
351 case sNewline:
352 if(m_nl == 0)
353 {
354 m_nl = c;
355 return;
356 } else {
357 int len = m_prev.length();
358 if(!len || m_prev[0] != '=')
359 hardLineBrk(out);
360 m_prev.clear();
361 m_state = sWaitingChar;
362 bool is2Ch;
363 is2Ch = (c == (m_nl == CR ? LF : CR));
364 m_nl = 0;
365 if(is2Ch)
366 return;
367 continue;
368 }
369 case sWaitingChar:
370 if(isblank(c))
371 {
372 m_state = sBlank;
373 continue;
374 } else if(isnl(c)) {
375 m_state = sNewline;
376 continue;
377 } else if(c == '=') {
378 m_state = sAfterEq;
379 m_prev.append(1, c);
380 return;
381 } else {
382 // WARNING: NOT ignoring chars > 126
383 // as suggested in rfc2045 6.7 note 4
384 if(c < 32 && c != TAB)
385 {
386 // malformed, CTRL ch found
387 // ignore (rfc2045 6.7 note 4)
388 return;
389 }
390 write(c,out);
391 }
392 return;
393 }
394 }
395 }
396 public:
397 /*! Constructor */
398 Decoder()
399 : m_state(sWaitingChar), m_nl(0)
400 {
401 }
402 /*! Returns the name of the codec ("Quoted-Printable") */
403 const char* name() const { return "Quoted-Printable"; }
404 /*! Returns the max line length */
405 size_t maxlen()
406 {
407 return m_maxlen;
408 }
409 /*!
410 Set the max line length. No more then \p i chars will be
411 printed on one line.
412 */
413 void maxlen(size_t i)
414 {
415 m_maxlen = i;
416 }
417 /*!
418 Decodes [\p bit,\p eit) and write any decoded char to \p out.
419 */
420 template<typename InIt, typename OutIt>
421 void process(InIt bit, InIt eit, OutIt out)
422 {
423 for(;bit != eit; ++bit)
424 decodeChar(*bit, out);
425 flush(out);
426 }
427 /*!
428 Decodes \p ic and write any decoded output char to \p out.
429
430 \warning You must call flush() when all chars have been
431 processed by the code(...) funcion.
432 \n
433 \code
434 while( (c = getchar()) != EOF )
435 qp.process(c, out);
436 qp.flush();
437 \endcode
438 \n
439 \sa flush()
440 */
441 template<typename OutIt>
442 void process(char_type ic, OutIt& out)
443 {
444 decodeChar(ic, out);
445 }
446 /*!
447 Write to \p out any buffered decoded char.
448 */
449 template<typename OutIt>
450 void flush(OutIt& out)
451 {
452 /* m_prev can be (regex):
453 empty:
454 ok
455 '=' :
456 malformed, '=' is last stream char, print as is
457 (rfc2045 6.7 note 3)
458 '=[a-zA-Z]'
459 malformed, print as is
460 (rfc2045 6.7 note 2)
461 '= +'
462 malformed, just print '=' and ignore trailing
463 blanks (rfc2045 6.7 (3) )
464 */
465 int len = m_prev.length();
466 if(len)
467 {
468 if(len == 1)
469 {
470 assert(m_prev[0] == '=');
471 write('=', out);
472 } else {
473 write('=', out);
474 if(m_prev[1] != ' ')
475 write(m_prev[1], out);
476 }
477 } else if(m_nl != 0) // stream ends with newline
478 hardLineBrk(out);
479
480 }
481 };
482
483 };
484
485
486 } // namespace
487
488 #endif
489

  ViewVC Help
Powered by ViewVC 1.1.26