/[debian]/mimetic/trunk/mimetic/codec/qp.h
ViewVC logotype

Contents of /mimetic/trunk/mimetic/codec/qp.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 197 - (show annotations)
Sun Apr 16 12:21:05 2006 UTC (15 years, 3 months ago) by gregoa
File MIME type: text/plain
File size: 14091 byte(s)
New upstream release.

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

  ViewVC Help
Powered by ViewVC 1.1.26