summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_subr/quoprint.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/quoprint.c')
-rw-r--r--subversion/libsvn_subr/quoprint.c309
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;
+}
OpenPOWER on IntegriCloud