]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/libarchive/libarchive/archive_write_add_filter_b64encode.c
MFC r368207,368607:
[FreeBSD/stable/10.git] / contrib / libarchive / libarchive / archive_write_add_filter_b64encode.c
1 /*-
2  * Copyright (c) 2012 Michihiro NAKAJIMA
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "archive_platform.h"
27
28 __FBSDID("$FreeBSD$");
29
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #ifdef HAVE_STDLIB_H
34 #include <stdlib.h>
35 #endif
36 #ifdef HAVE_STRING_H
37 #include <string.h>
38 #endif
39
40 #include "archive.h"
41 #include "archive_private.h"
42 #include "archive_string.h"
43 #include "archive_write_private.h"
44
45 #define LBYTES  57
46
47 struct private_b64encode {
48         int                     mode;
49         struct archive_string   name;
50         struct archive_string   encoded_buff;
51         size_t                  bs;
52         size_t                  hold_len;
53         unsigned char           hold[LBYTES];
54 };
55
56 static int archive_filter_b64encode_options(struct archive_write_filter *,
57     const char *, const char *);
58 static int archive_filter_b64encode_open(struct archive_write_filter *);
59 static int archive_filter_b64encode_write(struct archive_write_filter *,
60     const void *, size_t);
61 static int archive_filter_b64encode_close(struct archive_write_filter *);
62 static int archive_filter_b64encode_free(struct archive_write_filter *);
63 static void la_b64_encode(struct archive_string *, const unsigned char *, size_t);
64 static int64_t atol8(const char *, size_t);
65
66 static const char base64[] = {
67         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
68         'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
69         'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
70         'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
71         'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
72         'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
73         'w', 'x', 'y', 'z', '0', '1', '2', '3',
74         '4', '5', '6', '7', '8', '9', '+', '/'
75 };
76
77 /*
78  * Add a compress filter to this write handle.
79  */
80 int
81 archive_write_add_filter_b64encode(struct archive *_a)
82 {
83         struct archive_write *a = (struct archive_write *)_a;
84         struct archive_write_filter *f = __archive_write_allocate_filter(_a);
85         struct private_b64encode *state;
86
87         archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
88             ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
89
90         state = (struct private_b64encode *)calloc(1, sizeof(*state));
91         if (state == NULL) {
92                 archive_set_error(f->archive, ENOMEM,
93                     "Can't allocate data for b64encode filter");
94                 return (ARCHIVE_FATAL);
95         }
96         archive_strcpy(&state->name, "-");
97         state->mode = 0644;
98
99         f->data = state;
100         f->name = "b64encode";
101         f->code = ARCHIVE_FILTER_UU;
102         f->open = archive_filter_b64encode_open;
103         f->options = archive_filter_b64encode_options;
104         f->write = archive_filter_b64encode_write;
105         f->close = archive_filter_b64encode_close;
106         f->free = archive_filter_b64encode_free;
107
108         return (ARCHIVE_OK);
109 }
110
111 /*
112  * Set write options.
113  */
114 static int
115 archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
116     const char *value)
117 {
118         struct private_b64encode *state = (struct private_b64encode *)f->data;
119
120         if (strcmp(key, "mode") == 0) {
121                 if (value == NULL) {
122                         archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
123                             "mode option requires octal digits");
124                         return (ARCHIVE_FAILED);
125                 }
126                 state->mode = (int)atol8(value, strlen(value)) & 0777;
127                 return (ARCHIVE_OK);
128         } else if (strcmp(key, "name") == 0) {
129                 if (value == NULL) {
130                         archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
131                             "name option requires a string");
132                         return (ARCHIVE_FAILED);
133                 }
134                 archive_strcpy(&state->name, value);
135                 return (ARCHIVE_OK);
136         }
137
138         /* Note: The "warn" return is just to inform the options
139          * supervisor that we didn't handle it.  It will generate
140          * a suitable error if no one used this option. */
141         return (ARCHIVE_WARN);
142 }
143
144 /*
145  * Setup callback.
146  */
147 static int
148 archive_filter_b64encode_open(struct archive_write_filter *f)
149 {
150         struct private_b64encode *state = (struct private_b64encode *)f->data;
151         size_t bs = 65536, bpb;
152
153         if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
154                 /* Buffer size should be a multiple number of the of bytes
155                  * per block for performance. */
156                 bpb = archive_write_get_bytes_per_block(f->archive);
157                 if (bpb > bs)
158                         bs = bpb;
159                 else if (bpb != 0)
160                         bs -= bs % bpb;
161         }
162
163         state->bs = bs;
164         if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
165                 archive_set_error(f->archive, ENOMEM,
166                     "Can't allocate data for b64encode buffer");
167                 return (ARCHIVE_FATAL);
168         }
169
170         archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
171             state->mode, state->name.s);
172
173         f->data = state;
174         return (0);
175 }
176
177 static void
178 la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
179 {
180         int c;
181
182         for (; len >= 3; p += 3, len -= 3) {
183                 c = p[0] >> 2;
184                 archive_strappend_char(as, base64[c]);
185                 c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
186                 archive_strappend_char(as, base64[c]);
187                 c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
188                 archive_strappend_char(as, base64[c]);
189                 c = p[2] & 0x3f;
190                 archive_strappend_char(as, base64[c]);
191         }
192         if (len > 0) {
193                 c = p[0] >> 2;
194                 archive_strappend_char(as, base64[c]);
195                 c = (p[0] & 0x03) << 4;
196                 if (len == 1) {
197                         archive_strappend_char(as, base64[c]);
198                         archive_strappend_char(as, '=');
199                         archive_strappend_char(as, '=');
200                 } else {
201                         c |= (p[1] & 0xf0) >> 4;
202                         archive_strappend_char(as, base64[c]);
203                         c = (p[1] & 0x0f) << 2;
204                         archive_strappend_char(as, base64[c]);
205                         archive_strappend_char(as, '=');
206                 }
207         }
208         archive_strappend_char(as, '\n');
209 }
210
211 /*
212  * Write data to the encoded stream.
213  */
214 static int
215 archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
216     size_t length)
217 {
218         struct private_b64encode *state = (struct private_b64encode *)f->data;
219         const unsigned char *p = buff;
220         int ret = ARCHIVE_OK;
221
222         if (length == 0)
223                 return (ret);
224
225         if (state->hold_len) {
226                 while (state->hold_len < LBYTES && length > 0) {
227                         state->hold[state->hold_len++] = *p++;
228                         length--;
229                 }
230                 if (state->hold_len < LBYTES)
231                         return (ret);
232                 la_b64_encode(&state->encoded_buff, state->hold, LBYTES);
233                 state->hold_len = 0;
234         }
235
236         for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
237                 la_b64_encode(&state->encoded_buff, p, LBYTES);
238
239         /* Save remaining bytes. */
240         if (length > 0) {
241                 memcpy(state->hold, p, length);
242                 state->hold_len = length;
243         }
244         while (archive_strlen(&state->encoded_buff) >= state->bs) {
245                 ret = __archive_write_filter(f->next_filter,
246                     state->encoded_buff.s, state->bs);
247                 memmove(state->encoded_buff.s,
248                     state->encoded_buff.s + state->bs,
249                     state->encoded_buff.length - state->bs);
250                 state->encoded_buff.length -= state->bs;
251         }
252
253         return (ret);
254 }
255
256
257 /*
258  * Finish the compression...
259  */
260 static int
261 archive_filter_b64encode_close(struct archive_write_filter *f)
262 {
263         struct private_b64encode *state = (struct private_b64encode *)f->data;
264
265         /* Flush remaining bytes. */
266         if (state->hold_len != 0)
267                 la_b64_encode(&state->encoded_buff, state->hold, state->hold_len);
268         archive_string_sprintf(&state->encoded_buff, "====\n");
269         /* Write the last block */
270         archive_write_set_bytes_in_last_block(f->archive, 1);
271         return __archive_write_filter(f->next_filter,
272             state->encoded_buff.s, archive_strlen(&state->encoded_buff));
273 }
274
275 static int
276 archive_filter_b64encode_free(struct archive_write_filter *f)
277 {
278         struct private_b64encode *state = (struct private_b64encode *)f->data;
279
280         archive_string_free(&state->name);
281         archive_string_free(&state->encoded_buff);
282         free(state);
283         return (ARCHIVE_OK);
284 }
285
286 static int64_t
287 atol8(const char *p, size_t char_cnt)
288 {
289         int64_t l;
290         int digit;
291         
292         l = 0;
293         while (char_cnt-- > 0) {
294                 if (*p >= '0' && *p <= '7')
295                         digit = *p - '0';
296                 else
297                         break;
298                 p++;
299                 l <<= 3;
300                 l |= digit;
301         }
302         return (l);
303 }
304