diff options
Diffstat (limited to 'subversion/libsvn_subr/quoprint.c')
-rw-r--r-- | subversion/libsvn_subr/quoprint.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/subversion/libsvn_subr/quoprint.c b/subversion/libsvn_subr/quoprint.c new file mode 100644 index 0000000..bb9869d --- /dev/null +++ b/subversion/libsvn_subr/quoprint.c @@ -0,0 +1,309 @@ +/* + * quoprint.c: quoted-printable encoding and decoding functions + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + + + +#include <string.h> + +#include <apr.h> +#include <apr_pools.h> +#include <apr_general.h> /* for APR_INLINE */ + +#include "svn_pools.h" +#include "svn_io.h" +#include "svn_error.h" +#include "svn_quoprint.h" + + +/* Caveats: + + (1) This code is for the encoding and decoding of binary data + only. Thus, CRLF sequences are encoded as =0D=0A, and we + don't have to worry about tabs and spaces coming before + hard newlines, since there aren't any. + + (2) The decoder does no error reporting, and instead throws + away invalid sequences. It also discards CRLF sequences, + since those can only appear in the encoding of text data. + + (3) The decoder does not strip whitespace at the end of a + line, so it is not actually compliant with RFC 2045. + (Such whitespace should never occur, even in the encoding + of text data, but RFC 2045 requires a decoder to detect + that a transport agent has added trailing whitespace). + + (4) The encoder is tailored to make output embeddable in XML, + which means it quotes <>'"& as well as the characters + required by RFC 2045. */ + +#define QUOPRINT_LINELEN 76 +#define VALID_LITERAL(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~' \ + && (c) != '=')) +#define ENCODE_AS_LITERAL(c) (VALID_LITERAL(c) && (c) != '\t' && (c) != '<' \ + && (c) != '>' && (c) != '\'' && (c) != '"' \ + && (c) != '&') +static const char hextab[] = "0123456789ABCDEF"; + + + +/* Binary input --> quoted-printable-encoded output */ + +struct encode_baton { + svn_stream_t *output; + int linelen; /* Bytes output so far on this line */ + apr_pool_t *pool; +}; + + +/* Quoted-printable-encode a byte string which may or may not be the + totality of the data being encoded. *LINELEN carries the length of + the current output line; initialize it to 0. Output will be + appended to STR. */ +static void +encode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, + int *linelen) +{ + char buf[3]; + const char *p; + + /* Keep encoding three-byte groups until we run out. */ + for (p = data; p < data + len; p++) + { + /* Encode this character. */ + if (ENCODE_AS_LITERAL(*p)) + { + svn_stringbuf_appendbyte(str, *p); + (*linelen)++; + } + else + { + buf[0] = '='; + buf[1] = hextab[(*p >> 4) & 0xf]; + buf[2] = hextab[*p & 0xf]; + svn_stringbuf_appendbytes(str, buf, 3); + *linelen += 3; + } + + /* Make sure our output lines don't exceed QUOPRINT_LINELEN. */ + if (*linelen + 3 > QUOPRINT_LINELEN) + { + svn_stringbuf_appendcstr(str, "=\n"); + *linelen = 0; + } + } +} + + +/* Write handler for svn_quoprint_encode. */ +static svn_error_t * +encode_data(void *baton, const char *data, apr_size_t *len) +{ + struct encode_baton *eb = baton; + apr_pool_t *subpool = svn_pool_create(eb->pool); + svn_stringbuf_t *encoded = svn_stringbuf_create_empty(subpool); + apr_size_t enclen; + svn_error_t *err = SVN_NO_ERROR; + + /* Encode this block of data and write it out. */ + encode_bytes(encoded, data, *len, &eb->linelen); + enclen = encoded->len; + if (enclen != 0) + err = svn_stream_write(eb->output, encoded->data, &enclen); + svn_pool_destroy(subpool); + return err; +} + + +/* Close handler for svn_quoprint_encode(). */ +static svn_error_t * +finish_encoding_data(void *baton) +{ + struct encode_baton *eb = baton; + svn_error_t *err = SVN_NO_ERROR; + apr_size_t len; + + /* Terminate the current output line if it's not empty. */ + if (eb->linelen > 0) + { + len = 2; + err = svn_stream_write(eb->output, "=\n", &len); + } + + /* Pass on the close request and clean up the baton. */ + if (err == SVN_NO_ERROR) + err = svn_stream_close(eb->output); + svn_pool_destroy(eb->pool); + return err; +} + + +svn_stream_t * +svn_quoprint_encode(svn_stream_t *output, apr_pool_t *pool) +{ + apr_pool_t *subpool = svn_pool_create(pool); + struct encode_baton *eb = apr_palloc(subpool, sizeof(*eb)); + svn_stream_t *stream; + + eb->output = output; + eb->linelen = 0; + eb->pool = subpool; + stream = svn_stream_create(eb, pool); + svn_stream_set_write(stream, encode_data); + svn_stream_set_close(stream, finish_encoding_data); + return stream; +} + + +svn_stringbuf_t * +svn_quoprint_encode_string(const svn_stringbuf_t *str, apr_pool_t *pool) +{ + svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool); + int linelen = 0; + + encode_bytes(encoded, str->data, str->len, &linelen); + if (linelen > 0) + svn_stringbuf_appendcstr(encoded, "=\n"); + return encoded; +} + + + +/* Quoted-printable-encoded input --> binary output */ + +struct decode_baton { + svn_stream_t *output; + char buf[3]; /* Bytes waiting to be decoded */ + int buflen; /* Number of bytes waiting */ + apr_pool_t *pool; +}; + + +/* Decode a byte string which may or may not be the total amount of + data being decoded. INBUF and *INBUFLEN carry the leftover bytes + from call to call. Have room for four bytes in INBUF and + initialize *INBUFLEN to 0 and *DONE to FALSE. Output will be + appended to STR. */ +static void +decode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, + char *inbuf, int *inbuflen) +{ + const char *p, *find1, *find2; + char c; + + for (p = data; p <= data + len; p++) + { + /* Append this byte to the buffer and see what we have. */ + inbuf[(*inbuflen)++] = *p; + if (*inbuf != '=') + { + /* Literal character; append it if it's valid as such. */ + if (VALID_LITERAL(*inbuf)) + svn_stringbuf_appendbyte(str, *inbuf); + *inbuflen = 0; + } + else if (*inbuf == '=' && *inbuflen == 2 && inbuf[1] == '\n') + { + /* Soft newline; ignore. */ + *inbuflen = 0; + } + else if (*inbuf == '=' && *inbuflen == 3) + { + /* Encoded character; decode it and append. */ + find1 = strchr(hextab, inbuf[1]); + find2 = strchr(hextab, inbuf[2]); + if (find1 != NULL && find2 != NULL) + { + c = (char)(((find1 - hextab) << 4) | (find2 - hextab)); + svn_stringbuf_appendbyte(str, c); + } + *inbuflen = 0; + } + } +} + + +/* Write handler for svn_quoprint_decode. */ +static svn_error_t * +decode_data(void *baton, const char *data, apr_size_t *len) +{ + struct decode_baton *db = baton; + apr_pool_t *subpool; + svn_stringbuf_t *decoded; + apr_size_t declen; + svn_error_t *err = SVN_NO_ERROR; + + /* Decode this block of data. */ + subpool = svn_pool_create(db->pool); + decoded = svn_stringbuf_create_empty(subpool); + decode_bytes(decoded, data, *len, db->buf, &db->buflen); + + /* Write the output, clean up, go home. */ + declen = decoded->len; + if (declen != 0) + err = svn_stream_write(db->output, decoded->data, &declen); + svn_pool_destroy(subpool); + return err; +} + + +/* Close handler for svn_quoprint_decode(). */ +static svn_error_t * +finish_decoding_data(void *baton) +{ + struct decode_baton *db = baton; + svn_error_t *err; + + /* Pass on the close request and clean up the baton. */ + err = svn_stream_close(db->output); + svn_pool_destroy(db->pool); + return err; +} + + +svn_stream_t * +svn_quoprint_decode(svn_stream_t *output, apr_pool_t *pool) +{ + apr_pool_t *subpool = svn_pool_create(pool); + struct decode_baton *db = apr_palloc(subpool, sizeof(*db)); + svn_stream_t *stream; + + db->output = output; + db->buflen = 0; + db->pool = subpool; + stream = svn_stream_create(db, pool); + svn_stream_set_write(stream, decode_data); + svn_stream_set_close(stream, finish_decoding_data); + return stream; +} + + +svn_stringbuf_t * +svn_quoprint_decode_string(const svn_stringbuf_t *str, apr_pool_t *pool) +{ + svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool); + char ingroup[4]; + int ingrouplen = 0; + + decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen); + return decoded; +} |