1/* 2 * quoprint.c: quoted-printable encoding and decoding functions 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <string.h> 27 28#include <apr.h> 29#include <apr_pools.h> 30#include <apr_general.h> /* for APR_INLINE */ 31 32#include "svn_pools.h" 33#include "svn_io.h" 34#include "svn_error.h" 35#include "svn_quoprint.h" 36 37 38/* Caveats: 39 40 (1) This code is for the encoding and decoding of binary data 41 only. Thus, CRLF sequences are encoded as =0D=0A, and we 42 don't have to worry about tabs and spaces coming before 43 hard newlines, since there aren't any. 44 45 (2) The decoder does no error reporting, and instead throws 46 away invalid sequences. It also discards CRLF sequences, 47 since those can only appear in the encoding of text data. 48 49 (3) The decoder does not strip whitespace at the end of a 50 line, so it is not actually compliant with RFC 2045. 51 (Such whitespace should never occur, even in the encoding 52 of text data, but RFC 2045 requires a decoder to detect 53 that a transport agent has added trailing whitespace). 54 55 (4) The encoder is tailored to make output embeddable in XML, 56 which means it quotes <>'"& as well as the characters 57 required by RFC 2045. */ 58 59#define QUOPRINT_LINELEN 76 60#define VALID_LITERAL(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~' \ 61 && (c) != '=')) 62#define ENCODE_AS_LITERAL(c) (VALID_LITERAL(c) && (c) != '\t' && (c) != '<' \ 63 && (c) != '>' && (c) != '\'' && (c) != '"' \ 64 && (c) != '&') 65static const char hextab[] = "0123456789ABCDEF"; 66 67 68 69/* Binary input --> quoted-printable-encoded output */ 70 71struct encode_baton { 72 svn_stream_t *output; 73 int linelen; /* Bytes output so far on this line */ 74 apr_pool_t *pool; 75}; 76 77 78/* Quoted-printable-encode a byte string which may or may not be the 79 totality of the data being encoded. *LINELEN carries the length of 80 the current output line; initialize it to 0. Output will be 81 appended to STR. */ 82static void 83encode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, 84 int *linelen) 85{ 86 char buf[3]; 87 const char *p; 88 89 /* Keep encoding three-byte groups until we run out. */ 90 for (p = data; p < data + len; p++) 91 { 92 /* Encode this character. */ 93 if (ENCODE_AS_LITERAL(*p)) 94 { 95 svn_stringbuf_appendbyte(str, *p); 96 (*linelen)++; 97 } 98 else 99 { 100 buf[0] = '='; 101 buf[1] = hextab[(*p >> 4) & 0xf]; 102 buf[2] = hextab[*p & 0xf]; 103 svn_stringbuf_appendbytes(str, buf, 3); 104 *linelen += 3; 105 } 106 107 /* Make sure our output lines don't exceed QUOPRINT_LINELEN. */ 108 if (*linelen + 3 > QUOPRINT_LINELEN) 109 { 110 svn_stringbuf_appendcstr(str, "=\n"); 111 *linelen = 0; 112 } 113 } 114} 115 116 117/* Write handler for svn_quoprint_encode. */ 118static svn_error_t * 119encode_data(void *baton, const char *data, apr_size_t *len) 120{ 121 struct encode_baton *eb = baton; 122 apr_pool_t *subpool = svn_pool_create(eb->pool); 123 svn_stringbuf_t *encoded = svn_stringbuf_create_empty(subpool); 124 apr_size_t enclen; 125 svn_error_t *err = SVN_NO_ERROR; 126 127 /* Encode this block of data and write it out. */ 128 encode_bytes(encoded, data, *len, &eb->linelen); 129 enclen = encoded->len; 130 if (enclen != 0) 131 err = svn_stream_write(eb->output, encoded->data, &enclen); 132 svn_pool_destroy(subpool); 133 return err; 134} 135 136 137/* Close handler for svn_quoprint_encode(). */ 138static svn_error_t * 139finish_encoding_data(void *baton) 140{ 141 struct encode_baton *eb = baton; 142 svn_error_t *err = SVN_NO_ERROR; 143 apr_size_t len; 144 145 /* Terminate the current output line if it's not empty. */ 146 if (eb->linelen > 0) 147 { 148 len = 2; 149 err = svn_stream_write(eb->output, "=\n", &len); 150 } 151 152 /* Pass on the close request and clean up the baton. */ 153 if (err == SVN_NO_ERROR) 154 err = svn_stream_close(eb->output); 155 svn_pool_destroy(eb->pool); 156 return err; 157} 158 159 160svn_stream_t * 161svn_quoprint_encode(svn_stream_t *output, apr_pool_t *pool) 162{ 163 apr_pool_t *subpool = svn_pool_create(pool); 164 struct encode_baton *eb = apr_palloc(subpool, sizeof(*eb)); 165 svn_stream_t *stream; 166 167 eb->output = output; 168 eb->linelen = 0; 169 eb->pool = subpool; 170 stream = svn_stream_create(eb, pool); 171 svn_stream_set_write(stream, encode_data); 172 svn_stream_set_close(stream, finish_encoding_data); 173 return stream; 174} 175 176 177svn_stringbuf_t * 178svn_quoprint_encode_string(const svn_stringbuf_t *str, apr_pool_t *pool) 179{ 180 svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool); 181 int linelen = 0; 182 183 encode_bytes(encoded, str->data, str->len, &linelen); 184 if (linelen > 0) 185 svn_stringbuf_appendcstr(encoded, "=\n"); 186 return encoded; 187} 188 189 190 191/* Quoted-printable-encoded input --> binary output */ 192 193struct decode_baton { 194 svn_stream_t *output; 195 char buf[3]; /* Bytes waiting to be decoded */ 196 int buflen; /* Number of bytes waiting */ 197 apr_pool_t *pool; 198}; 199 200 201/* Decode a byte string which may or may not be the total amount of 202 data being decoded. INBUF and *INBUFLEN carry the leftover bytes 203 from call to call. Have room for four bytes in INBUF and 204 initialize *INBUFLEN to 0 and *DONE to FALSE. Output will be 205 appended to STR. */ 206static void 207decode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, 208 char *inbuf, int *inbuflen) 209{ 210 const char *p, *find1, *find2; 211 char c; 212 213 for (p = data; p <= data + len; p++) 214 { 215 /* Append this byte to the buffer and see what we have. */ 216 inbuf[(*inbuflen)++] = *p; 217 if (*inbuf != '=') 218 { 219 /* Literal character; append it if it's valid as such. */ 220 if (VALID_LITERAL(*inbuf)) 221 svn_stringbuf_appendbyte(str, *inbuf); 222 *inbuflen = 0; 223 } 224 else if (*inbuf == '=' && *inbuflen == 2 && inbuf[1] == '\n') 225 { 226 /* Soft newline; ignore. */ 227 *inbuflen = 0; 228 } 229 else if (*inbuf == '=' && *inbuflen == 3) 230 { 231 /* Encoded character; decode it and append. */ 232 find1 = strchr(hextab, inbuf[1]); 233 find2 = strchr(hextab, inbuf[2]); 234 if (find1 != NULL && find2 != NULL) 235 { 236 c = (char)(((find1 - hextab) << 4) | (find2 - hextab)); 237 svn_stringbuf_appendbyte(str, c); 238 } 239 *inbuflen = 0; 240 } 241 } 242} 243 244 245/* Write handler for svn_quoprint_decode. */ 246static svn_error_t * 247decode_data(void *baton, const char *data, apr_size_t *len) 248{ 249 struct decode_baton *db = baton; 250 apr_pool_t *subpool; 251 svn_stringbuf_t *decoded; 252 apr_size_t declen; 253 svn_error_t *err = SVN_NO_ERROR; 254 255 /* Decode this block of data. */ 256 subpool = svn_pool_create(db->pool); 257 decoded = svn_stringbuf_create_empty(subpool); 258 decode_bytes(decoded, data, *len, db->buf, &db->buflen); 259 260 /* Write the output, clean up, go home. */ 261 declen = decoded->len; 262 if (declen != 0) 263 err = svn_stream_write(db->output, decoded->data, &declen); 264 svn_pool_destroy(subpool); 265 return err; 266} 267 268 269/* Close handler for svn_quoprint_decode(). */ 270static svn_error_t * 271finish_decoding_data(void *baton) 272{ 273 struct decode_baton *db = baton; 274 svn_error_t *err; 275 276 /* Pass on the close request and clean up the baton. */ 277 err = svn_stream_close(db->output); 278 svn_pool_destroy(db->pool); 279 return err; 280} 281 282 283svn_stream_t * 284svn_quoprint_decode(svn_stream_t *output, apr_pool_t *pool) 285{ 286 apr_pool_t *subpool = svn_pool_create(pool); 287 struct decode_baton *db = apr_palloc(subpool, sizeof(*db)); 288 svn_stream_t *stream; 289 290 db->output = output; 291 db->buflen = 0; 292 db->pool = subpool; 293 stream = svn_stream_create(db, pool); 294 svn_stream_set_write(stream, decode_data); 295 svn_stream_set_close(stream, finish_decoding_data); 296 return stream; 297} 298 299 300svn_stringbuf_t * 301svn_quoprint_decode_string(const svn_stringbuf_t *str, apr_pool_t *pool) 302{ 303 svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool); 304 char ingroup[4]; 305 int ingrouplen = 0; 306 307 decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen); 308 return decoded; 309} 310