]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/quoprint.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / quoprint.c
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 \f
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) != '&')
65 static const char hextab[] = "0123456789ABCDEF";
66
67
68 \f
69 /* Binary input --> quoted-printable-encoded output */
70
71 struct 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.  */
82 static void
83 encode_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.  */
118 static svn_error_t *
119 encode_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().  */
138 static svn_error_t *
139 finish_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
160 svn_stream_t *
161 svn_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
177 svn_stringbuf_t *
178 svn_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 \f
191 /* Quoted-printable-encoded input --> binary output */
192
193 struct 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.  */
206 static void
207 decode_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.  */
246 static svn_error_t *
247 decode_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().  */
270 static svn_error_t *
271 finish_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
283 svn_stream_t *
284 svn_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
300 svn_stringbuf_t *
301 svn_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 }