1251881Speter/* 2251881Speter * quoprint.c: quoted-printable encoding and decoding functions 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <string.h> 27251881Speter 28251881Speter#include <apr.h> 29251881Speter#include <apr_pools.h> 30251881Speter#include <apr_general.h> /* for APR_INLINE */ 31251881Speter 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_io.h" 34251881Speter#include "svn_error.h" 35251881Speter#include "svn_quoprint.h" 36251881Speter 37251881Speter 38251881Speter/* Caveats: 39251881Speter 40251881Speter (1) This code is for the encoding and decoding of binary data 41251881Speter only. Thus, CRLF sequences are encoded as =0D=0A, and we 42251881Speter don't have to worry about tabs and spaces coming before 43251881Speter hard newlines, since there aren't any. 44251881Speter 45251881Speter (2) The decoder does no error reporting, and instead throws 46251881Speter away invalid sequences. It also discards CRLF sequences, 47251881Speter since those can only appear in the encoding of text data. 48251881Speter 49251881Speter (3) The decoder does not strip whitespace at the end of a 50251881Speter line, so it is not actually compliant with RFC 2045. 51251881Speter (Such whitespace should never occur, even in the encoding 52251881Speter of text data, but RFC 2045 requires a decoder to detect 53251881Speter that a transport agent has added trailing whitespace). 54251881Speter 55251881Speter (4) The encoder is tailored to make output embeddable in XML, 56251881Speter which means it quotes <>'"& as well as the characters 57251881Speter required by RFC 2045. */ 58251881Speter 59251881Speter#define QUOPRINT_LINELEN 76 60251881Speter#define VALID_LITERAL(c) ((c) == '\t' || ((c) >= ' ' && (c) <= '~' \ 61251881Speter && (c) != '=')) 62251881Speter#define ENCODE_AS_LITERAL(c) (VALID_LITERAL(c) && (c) != '\t' && (c) != '<' \ 63251881Speter && (c) != '>' && (c) != '\'' && (c) != '"' \ 64251881Speter && (c) != '&') 65251881Speterstatic const char hextab[] = "0123456789ABCDEF"; 66251881Speter 67251881Speter 68251881Speter 69251881Speter/* Binary input --> quoted-printable-encoded output */ 70251881Speter 71251881Speterstruct encode_baton { 72251881Speter svn_stream_t *output; 73251881Speter int linelen; /* Bytes output so far on this line */ 74251881Speter apr_pool_t *pool; 75251881Speter}; 76251881Speter 77251881Speter 78251881Speter/* Quoted-printable-encode a byte string which may or may not be the 79251881Speter totality of the data being encoded. *LINELEN carries the length of 80251881Speter the current output line; initialize it to 0. Output will be 81251881Speter appended to STR. */ 82251881Speterstatic void 83251881Speterencode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, 84251881Speter int *linelen) 85251881Speter{ 86251881Speter char buf[3]; 87251881Speter const char *p; 88251881Speter 89251881Speter /* Keep encoding three-byte groups until we run out. */ 90251881Speter for (p = data; p < data + len; p++) 91251881Speter { 92251881Speter /* Encode this character. */ 93251881Speter if (ENCODE_AS_LITERAL(*p)) 94251881Speter { 95251881Speter svn_stringbuf_appendbyte(str, *p); 96251881Speter (*linelen)++; 97251881Speter } 98251881Speter else 99251881Speter { 100251881Speter buf[0] = '='; 101251881Speter buf[1] = hextab[(*p >> 4) & 0xf]; 102251881Speter buf[2] = hextab[*p & 0xf]; 103251881Speter svn_stringbuf_appendbytes(str, buf, 3); 104251881Speter *linelen += 3; 105251881Speter } 106251881Speter 107251881Speter /* Make sure our output lines don't exceed QUOPRINT_LINELEN. */ 108251881Speter if (*linelen + 3 > QUOPRINT_LINELEN) 109251881Speter { 110251881Speter svn_stringbuf_appendcstr(str, "=\n"); 111251881Speter *linelen = 0; 112251881Speter } 113251881Speter } 114251881Speter} 115251881Speter 116251881Speter 117251881Speter/* Write handler for svn_quoprint_encode. */ 118251881Speterstatic svn_error_t * 119251881Speterencode_data(void *baton, const char *data, apr_size_t *len) 120251881Speter{ 121251881Speter struct encode_baton *eb = baton; 122251881Speter apr_pool_t *subpool = svn_pool_create(eb->pool); 123251881Speter svn_stringbuf_t *encoded = svn_stringbuf_create_empty(subpool); 124251881Speter apr_size_t enclen; 125251881Speter svn_error_t *err = SVN_NO_ERROR; 126251881Speter 127251881Speter /* Encode this block of data and write it out. */ 128251881Speter encode_bytes(encoded, data, *len, &eb->linelen); 129251881Speter enclen = encoded->len; 130251881Speter if (enclen != 0) 131251881Speter err = svn_stream_write(eb->output, encoded->data, &enclen); 132251881Speter svn_pool_destroy(subpool); 133251881Speter return err; 134251881Speter} 135251881Speter 136251881Speter 137251881Speter/* Close handler for svn_quoprint_encode(). */ 138251881Speterstatic svn_error_t * 139251881Speterfinish_encoding_data(void *baton) 140251881Speter{ 141251881Speter struct encode_baton *eb = baton; 142251881Speter svn_error_t *err = SVN_NO_ERROR; 143251881Speter apr_size_t len; 144251881Speter 145251881Speter /* Terminate the current output line if it's not empty. */ 146251881Speter if (eb->linelen > 0) 147251881Speter { 148251881Speter len = 2; 149251881Speter err = svn_stream_write(eb->output, "=\n", &len); 150251881Speter } 151251881Speter 152251881Speter /* Pass on the close request and clean up the baton. */ 153251881Speter if (err == SVN_NO_ERROR) 154251881Speter err = svn_stream_close(eb->output); 155251881Speter svn_pool_destroy(eb->pool); 156251881Speter return err; 157251881Speter} 158251881Speter 159251881Speter 160251881Spetersvn_stream_t * 161251881Spetersvn_quoprint_encode(svn_stream_t *output, apr_pool_t *pool) 162251881Speter{ 163251881Speter apr_pool_t *subpool = svn_pool_create(pool); 164251881Speter struct encode_baton *eb = apr_palloc(subpool, sizeof(*eb)); 165251881Speter svn_stream_t *stream; 166251881Speter 167251881Speter eb->output = output; 168251881Speter eb->linelen = 0; 169251881Speter eb->pool = subpool; 170251881Speter stream = svn_stream_create(eb, pool); 171251881Speter svn_stream_set_write(stream, encode_data); 172251881Speter svn_stream_set_close(stream, finish_encoding_data); 173251881Speter return stream; 174251881Speter} 175251881Speter 176251881Speter 177251881Spetersvn_stringbuf_t * 178251881Spetersvn_quoprint_encode_string(const svn_stringbuf_t *str, apr_pool_t *pool) 179251881Speter{ 180251881Speter svn_stringbuf_t *encoded = svn_stringbuf_create_empty(pool); 181251881Speter int linelen = 0; 182251881Speter 183251881Speter encode_bytes(encoded, str->data, str->len, &linelen); 184251881Speter if (linelen > 0) 185251881Speter svn_stringbuf_appendcstr(encoded, "=\n"); 186251881Speter return encoded; 187251881Speter} 188251881Speter 189251881Speter 190251881Speter 191251881Speter/* Quoted-printable-encoded input --> binary output */ 192251881Speter 193251881Speterstruct decode_baton { 194251881Speter svn_stream_t *output; 195251881Speter char buf[3]; /* Bytes waiting to be decoded */ 196251881Speter int buflen; /* Number of bytes waiting */ 197251881Speter apr_pool_t *pool; 198251881Speter}; 199251881Speter 200251881Speter 201251881Speter/* Decode a byte string which may or may not be the total amount of 202251881Speter data being decoded. INBUF and *INBUFLEN carry the leftover bytes 203251881Speter from call to call. Have room for four bytes in INBUF and 204251881Speter initialize *INBUFLEN to 0 and *DONE to FALSE. Output will be 205251881Speter appended to STR. */ 206251881Speterstatic void 207251881Speterdecode_bytes(svn_stringbuf_t *str, const char *data, apr_size_t len, 208251881Speter char *inbuf, int *inbuflen) 209251881Speter{ 210251881Speter const char *p, *find1, *find2; 211251881Speter char c; 212251881Speter 213251881Speter for (p = data; p <= data + len; p++) 214251881Speter { 215251881Speter /* Append this byte to the buffer and see what we have. */ 216251881Speter inbuf[(*inbuflen)++] = *p; 217251881Speter if (*inbuf != '=') 218251881Speter { 219251881Speter /* Literal character; append it if it's valid as such. */ 220251881Speter if (VALID_LITERAL(*inbuf)) 221251881Speter svn_stringbuf_appendbyte(str, *inbuf); 222251881Speter *inbuflen = 0; 223251881Speter } 224251881Speter else if (*inbuf == '=' && *inbuflen == 2 && inbuf[1] == '\n') 225251881Speter { 226251881Speter /* Soft newline; ignore. */ 227251881Speter *inbuflen = 0; 228251881Speter } 229251881Speter else if (*inbuf == '=' && *inbuflen == 3) 230251881Speter { 231251881Speter /* Encoded character; decode it and append. */ 232251881Speter find1 = strchr(hextab, inbuf[1]); 233251881Speter find2 = strchr(hextab, inbuf[2]); 234251881Speter if (find1 != NULL && find2 != NULL) 235251881Speter { 236251881Speter c = (char)(((find1 - hextab) << 4) | (find2 - hextab)); 237251881Speter svn_stringbuf_appendbyte(str, c); 238251881Speter } 239251881Speter *inbuflen = 0; 240251881Speter } 241251881Speter } 242251881Speter} 243251881Speter 244251881Speter 245251881Speter/* Write handler for svn_quoprint_decode. */ 246251881Speterstatic svn_error_t * 247251881Speterdecode_data(void *baton, const char *data, apr_size_t *len) 248251881Speter{ 249251881Speter struct decode_baton *db = baton; 250251881Speter apr_pool_t *subpool; 251251881Speter svn_stringbuf_t *decoded; 252251881Speter apr_size_t declen; 253251881Speter svn_error_t *err = SVN_NO_ERROR; 254251881Speter 255251881Speter /* Decode this block of data. */ 256251881Speter subpool = svn_pool_create(db->pool); 257251881Speter decoded = svn_stringbuf_create_empty(subpool); 258251881Speter decode_bytes(decoded, data, *len, db->buf, &db->buflen); 259251881Speter 260251881Speter /* Write the output, clean up, go home. */ 261251881Speter declen = decoded->len; 262251881Speter if (declen != 0) 263251881Speter err = svn_stream_write(db->output, decoded->data, &declen); 264251881Speter svn_pool_destroy(subpool); 265251881Speter return err; 266251881Speter} 267251881Speter 268251881Speter 269251881Speter/* Close handler for svn_quoprint_decode(). */ 270251881Speterstatic svn_error_t * 271251881Speterfinish_decoding_data(void *baton) 272251881Speter{ 273251881Speter struct decode_baton *db = baton; 274251881Speter svn_error_t *err; 275251881Speter 276251881Speter /* Pass on the close request and clean up the baton. */ 277251881Speter err = svn_stream_close(db->output); 278251881Speter svn_pool_destroy(db->pool); 279251881Speter return err; 280251881Speter} 281251881Speter 282251881Speter 283251881Spetersvn_stream_t * 284251881Spetersvn_quoprint_decode(svn_stream_t *output, apr_pool_t *pool) 285251881Speter{ 286251881Speter apr_pool_t *subpool = svn_pool_create(pool); 287251881Speter struct decode_baton *db = apr_palloc(subpool, sizeof(*db)); 288251881Speter svn_stream_t *stream; 289251881Speter 290251881Speter db->output = output; 291251881Speter db->buflen = 0; 292251881Speter db->pool = subpool; 293251881Speter stream = svn_stream_create(db, pool); 294251881Speter svn_stream_set_write(stream, decode_data); 295251881Speter svn_stream_set_close(stream, finish_decoding_data); 296251881Speter return stream; 297251881Speter} 298251881Speter 299251881Speter 300251881Spetersvn_stringbuf_t * 301251881Spetersvn_quoprint_decode_string(const svn_stringbuf_t *str, apr_pool_t *pool) 302251881Speter{ 303251881Speter svn_stringbuf_t *decoded = svn_stringbuf_create_empty(pool); 304251881Speter char ingroup[4]; 305251881Speter int ingrouplen = 0; 306251881Speter 307251881Speter decode_bytes(decoded, str->data, str->len, ingroup, &ingrouplen); 308251881Speter return decoded; 309251881Speter} 310