* subversion/libsvn_subr/validate.c
[svn.git] / subversion / libsvn_subr / quoprint.c
blobeb6ab2af2ba6d3cc1ec34f40df95dda44f6fe688
1 /*
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 * ====================================================================
21 #include <string.h>
23 #include <apr.h>
24 #include <apr_pools.h>
25 #include <apr_general.h> /* for APR_INLINE */
27 #include "svn_pools.h"
28 #include "svn_io.h"
29 #include "svn_error.h"
30 #include "svn_quoprint.h"
33 /* Caveats:
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) <= '~' \
56 && (c) != '='))
57 #define ENCODE_AS_LITERAL(c) (VALID_LITERAL(c) && (c) != '\t' && (c) != '<' \
58 && (c) != '>' && (c) != '\'' && (c) != '"' \
59 && (c) != '&')
60 static const char hextab[] = "0123456789ABCDEF";
64 /* Binary input --> quoted-printable-encoded output */
66 struct encode_baton {
67 svn_stream_t *output;
68 int linelen; /* Bytes output so far on this line */
69 apr_pool_t *pool;
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
76 appended to STR. */
77 static void
78 encode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len,
79 int *linelen)
81 char buf[3];
82 const char *p;
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);
91 (*linelen)++;
93 else
95 buf[0] = '=';
96 buf[1] = hextab[(*p >> 4) & 0xf];
97 buf[2] = hextab[*p & 0xf];
98 svn_stringbuf_appendbytes(str, buf, 3);
99 *linelen += 3;
102 /* Make sure our output lines don't exceed QUOPRINT_LINELEN. */
103 if (*linelen + 3 > QUOPRINT_LINELEN)
105 svn_stringbuf_appendcstr(str, "=\n");
106 *linelen = 0;
112 /* Write handler for svn_quoprint_encode. */
113 static svn_error_t *
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);
119 apr_size_t enclen;
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;
125 if (enclen != 0)
126 err = svn_stream_write(eb->output, encoded->data, &enclen);
127 svn_pool_destroy(subpool);
128 return err;
132 /* Close handler for svn_quoprint_encode(). */
133 static svn_error_t *
134 finish_encoding_data(void *baton)
136 struct encode_baton *eb = baton;
137 svn_error_t *err = SVN_NO_ERROR;
138 apr_size_t len;
140 /* Terminate the current output line if it's not empty. */
141 if (eb->linelen > 0)
143 len = 2;
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);
151 return err;
155 svn_stream_t *
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;
162 eb->output = output;
163 eb->linelen = 0;
164 eb->pool = subpool;
165 stream = svn_stream_create(eb, pool);
166 svn_stream_set_write(stream, encode_data);
167 svn_stream_set_close(stream, finish_encoding_data);
168 return stream;
172 svn_stringbuf_t *
173 svn_quoprint_encode_string(svn_stringbuf_t *str, apr_pool_t *pool)
175 svn_stringbuf_t *encoded = svn_stringbuf_create("", pool);
176 int linelen = 0;
178 encode_bytes(encoded, str->data, str->len, &linelen);
179 if (linelen > 0)
180 svn_stringbuf_appendcstr(encoded, "=\n");
181 return encoded;
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 */
192 apr_pool_t *pool;
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
200 appended to STR. */
201 static void
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;
206 char c;
208 for (p = data; p <= data + len; p++)
210 /* Append this byte to the buffer and see what we have. */
211 inbuf[(*inbuflen)++] = *p;
212 if (*inbuf != '=')
214 /* Literal character; append it if it's valid as such. */
215 if (VALID_LITERAL(*inbuf))
216 svn_stringbuf_appendbytes(str, inbuf, 1);
217 *inbuflen = 0;
219 else if (*inbuf == '=' && *inbuflen == 2 && inbuf[1] == '\n')
221 /* Soft newline; ignore. */
222 *inbuflen = 0;
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);
234 *inbuflen = 0;
240 /* Write handler for svn_quoprint_decode. */
241 static svn_error_t *
242 decode_data(void *baton, const char *data, apr_size_t *len)
244 struct decode_baton *db = baton;
245 apr_pool_t *subpool;
246 svn_stringbuf_t *decoded;
247 apr_size_t declen;
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;
257 if (declen != 0)
258 err = svn_stream_write(db->output, decoded->data, &declen);
259 svn_pool_destroy(subpool);
260 return err;
264 /* Close handler for svn_quoprint_decode(). */
265 static svn_error_t *
266 finish_decoding_data(void *baton)
268 struct decode_baton *db = baton;
269 svn_error_t *err;
271 /* Pass on the close request and clean up the baton. */
272 err = svn_stream_close(db->output);
273 svn_pool_destroy(db->pool);
274 return err;
278 svn_stream_t *
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;
285 db->output = output;
286 db->buflen = 0;
287 db->pool = subpool;
288 stream = svn_stream_create(db, pool);
289 svn_stream_set_write(stream, decode_data);
290 svn_stream_set_close(stream, finish_decoding_data);
291 return stream;
295 svn_stringbuf_t *
296 svn_quoprint_decode_string(svn_stringbuf_t *str, apr_pool_t *pool)
298 svn_stringbuf_t *decoded = svn_stringbuf_create("", pool);
299 char ingroup[4];
300 int ingrouplen = 0;
302 decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen);
303 return decoded;