2 * quoprint.c: quoted-printable encoding and decoding functions
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
24 #include <apr_pools.h>
25 #include <apr_general.h> /* for APR_INLINE */
27 #include "svn_pools.h"
29 #include "svn_error.h"
30 #include "svn_quoprint.h"
35 (1) This code is for the encoding and decoding of binary data
36 only. Thus, CRLF sequences are encoded as =0D=0A, and we
37 don't have to worry about tabs and spaces coming before
38 hard newlines, since there aren't any.
40 (2) The decoder does no error reporting, and instead throws
41 away invalid sequences. It also discards CRLF sequences,
42 since those can only appear in the encoding of text data.
44 (3) The decoder does not strip whitespace at the end of a
45 line, so it is not actually compliant with RFC 2045.
46 (Such whitespace should never occur, even in the encoding
47 of text data, but RFC 2045 requires a decoder to detect
48 that a transport agent has added trailing whitespace).
50 (4) The encoder is tailored to make output embeddable in XML,
51 which means it quotes <>'"& as well as the characters
52 required by RFC 2045. */
54 #define QUOPRINT_LINELEN 76
55 #define VALID_LITERAL(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~' \
57 #define ENCODE_AS_LITERAL(c) (VALID_LITERAL(c) && (c) != '\t' && (c) != '<' \
58 && (c) != '>' && (c) != '\'' && (c) != '"' \
60 static const char hextab
[] = "0123456789ABCDEF";
64 /* Binary input --> quoted-printable-encoded output */
68 int linelen
; /* Bytes output so far on this line */
73 /* Quoted-printable-encode a byte string which may or may not be the
74 totality of the data being encoded. *LINELEN carries the length of
75 the current output line; initialize it to 0. Output will be
78 encode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
84 /* Keep encoding three-byte groups until we run out. */
85 for (p
= data
; p
< data
+ len
; p
++)
87 /* Encode this character. */
88 if (ENCODE_AS_LITERAL(*p
))
90 svn_stringbuf_appendbytes(str
, p
, 1);
96 buf
[1] = hextab
[(*p
>> 4) & 0xf];
97 buf
[2] = hextab
[*p
& 0xf];
98 svn_stringbuf_appendbytes(str
, buf
, 3);
102 /* Make sure our output lines don't exceed QUOPRINT_LINELEN. */
103 if (*linelen
+ 3 > QUOPRINT_LINELEN
)
105 svn_stringbuf_appendcstr(str
, "=\n");
112 /* Write handler for svn_quoprint_encode. */
114 encode_data(void *baton
, const char *data
, apr_size_t
*len
)
116 struct encode_baton
*eb
= baton
;
117 apr_pool_t
*subpool
= svn_pool_create(eb
->pool
);
118 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", subpool
);
120 svn_error_t
*err
= SVN_NO_ERROR
;
122 /* Encode this block of data and write it out. */
123 encode_bytes(encoded
, data
, *len
, &eb
->linelen
);
124 enclen
= encoded
->len
;
126 err
= svn_stream_write(eb
->output
, encoded
->data
, &enclen
);
127 svn_pool_destroy(subpool
);
132 /* Close handler for svn_quoprint_encode(). */
134 finish_encoding_data(void *baton
)
136 struct encode_baton
*eb
= baton
;
137 svn_error_t
*err
= SVN_NO_ERROR
;
140 /* Terminate the current output line if it's not empty. */
144 err
= svn_stream_write(eb
->output
, "=\n", &len
);
147 /* Pass on the close request and clean up the baton. */
148 if (err
== SVN_NO_ERROR
)
149 err
= svn_stream_close(eb
->output
);
150 svn_pool_destroy(eb
->pool
);
156 svn_quoprint_encode(svn_stream_t
*output
, apr_pool_t
*pool
)
158 apr_pool_t
*subpool
= svn_pool_create(pool
);
159 struct encode_baton
*eb
= apr_palloc(subpool
, sizeof(*eb
));
160 svn_stream_t
*stream
;
165 stream
= svn_stream_create(eb
, pool
);
166 svn_stream_set_write(stream
, encode_data
);
167 svn_stream_set_close(stream
, finish_encoding_data
);
173 svn_quoprint_encode_string(svn_stringbuf_t
*str
, apr_pool_t
*pool
)
175 svn_stringbuf_t
*encoded
= svn_stringbuf_create("", pool
);
178 encode_bytes(encoded
, str
->data
, str
->len
, &linelen
);
180 svn_stringbuf_appendcstr(encoded
, "=\n");
186 /* Quoted-printable-encoded input --> binary output */
188 struct decode_baton
{
189 svn_stream_t
*output
;
190 char buf
[3]; /* Bytes waiting to be decoded */
191 int buflen
; /* Number of bytes waiting */
196 /* Decode a byte string which may or may not be the total amount of
197 data being decoded. INBUF and *INBUFLEN carry the leftover bytes
198 from call to call. Have room for four bytes in INBUF and
199 initialize *INBUFLEN to 0 and *DONE to FALSE. Output will be
202 decode_bytes(svn_stringbuf_t
*str
, const char *data
, apr_size_t len
,
203 char *inbuf
, int *inbuflen
)
205 const char *p
, *find1
, *find2
;
208 for (p
= data
; p
<= data
+ len
; p
++)
210 /* Append this byte to the buffer and see what we have. */
211 inbuf
[(*inbuflen
)++] = *p
;
214 /* Literal character; append it if it's valid as such. */
215 if (VALID_LITERAL(*inbuf
))
216 svn_stringbuf_appendbytes(str
, inbuf
, 1);
219 else if (*inbuf
== '=' && *inbuflen
== 2 && inbuf
[1] == '\n')
221 /* Soft newline; ignore. */
224 else if (*inbuf
== '=' && *inbuflen
== 3)
226 /* Encoded character; decode it and append. */
227 find1
= strchr(hextab
, inbuf
[1]);
228 find2
= strchr(hextab
, inbuf
[2]);
229 if (find1
!= NULL
&& find2
!= NULL
)
231 c
= ((find1
- hextab
) << 4) | (find2
- hextab
);
232 svn_stringbuf_appendbytes(str
, &c
, 1);
240 /* Write handler for svn_quoprint_decode. */
242 decode_data(void *baton
, const char *data
, apr_size_t
*len
)
244 struct decode_baton
*db
= baton
;
246 svn_stringbuf_t
*decoded
;
248 svn_error_t
*err
= SVN_NO_ERROR
;
250 /* Decode this block of data. */
251 subpool
= svn_pool_create(db
->pool
);
252 decoded
= svn_stringbuf_create("", subpool
);
253 decode_bytes(decoded
, data
, *len
, db
->buf
, &db
->buflen
);
255 /* Write the output, clean up, go home. */
256 declen
= decoded
->len
;
258 err
= svn_stream_write(db
->output
, decoded
->data
, &declen
);
259 svn_pool_destroy(subpool
);
264 /* Close handler for svn_quoprint_decode(). */
266 finish_decoding_data(void *baton
)
268 struct decode_baton
*db
= baton
;
271 /* Pass on the close request and clean up the baton. */
272 err
= svn_stream_close(db
->output
);
273 svn_pool_destroy(db
->pool
);
279 svn_quoprint_decode(svn_stream_t
*output
, apr_pool_t
*pool
)
281 apr_pool_t
*subpool
= svn_pool_create(pool
);
282 struct decode_baton
*db
= apr_palloc(subpool
, sizeof(*db
));
283 svn_stream_t
*stream
;
288 stream
= svn_stream_create(db
, pool
);
289 svn_stream_set_write(stream
, decode_data
);
290 svn_stream_set_close(stream
, finish_decoding_data
);
296 svn_quoprint_decode_string(svn_stringbuf_t
*str
, apr_pool_t
*pool
)
298 svn_stringbuf_t
*decoded
= svn_stringbuf_create("", pool
);
302 decode_bytes(decoded
, str
->data
, str
->len
, ingroup
, &ingrouplen
);