xref: /freebsd/contrib/libarchive/libarchive/archive_write_add_filter_uuencode.c (revision 2e113ef82465598b8c26e0ca415fbe90677fbd47)
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 #ifdef HAVE_ERRNO_H
29 #include <errno.h>
30 #endif
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 
38 #include "archive.h"
39 #include "archive_private.h"
40 #include "archive_string.h"
41 #include "archive_write_private.h"
42 
43 #define LBYTES 45
44 
45 struct private_uuencode {
46 	int			mode;
47 	struct archive_string	name;
48 	struct archive_string	encoded_buff;
49 	size_t			bs;
50 	size_t			hold_len;
51 	unsigned char		hold[LBYTES];
52 };
53 
54 static int archive_filter_uuencode_options(struct archive_write_filter *,
55     const char *, const char *);
56 static int archive_filter_uuencode_open(struct archive_write_filter *);
57 static int archive_filter_uuencode_write(struct archive_write_filter *,
58     const void *, size_t);
59 static int archive_filter_uuencode_close(struct archive_write_filter *);
60 static int archive_filter_uuencode_free(struct archive_write_filter *);
61 static void uu_encode(struct archive_string *, const unsigned char *, size_t);
62 static int64_t atol8(const char *, size_t);
63 
64 /*
65  * Add a compress filter to this write handle.
66  */
67 int
archive_write_add_filter_uuencode(struct archive * _a)68 archive_write_add_filter_uuencode(struct archive *_a)
69 {
70 	struct archive_write *a = (struct archive_write *)_a;
71 	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
72 	struct private_uuencode *state;
73 
74 	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
75 	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
76 
77 	state = calloc(1, sizeof(*state));
78 	if (state == NULL) {
79 		archive_set_error(f->archive, ENOMEM,
80 		    "Can't allocate data for uuencode filter");
81 		return (ARCHIVE_FATAL);
82 	}
83 	archive_strcpy(&state->name, "-");
84 	state->mode = 0644;
85 
86 	f->data = state;
87 	f->name = "uuencode";
88 	f->code = ARCHIVE_FILTER_UU;
89 	f->open = archive_filter_uuencode_open;
90 	f->options = archive_filter_uuencode_options;
91 	f->write = archive_filter_uuencode_write;
92 	f->close = archive_filter_uuencode_close;
93 	f->free = archive_filter_uuencode_free;
94 
95 	return (ARCHIVE_OK);
96 }
97 
98 /*
99  * Set write options.
100  */
101 static int
archive_filter_uuencode_options(struct archive_write_filter * f,const char * key,const char * value)102 archive_filter_uuencode_options(struct archive_write_filter *f, const char *key,
103     const char *value)
104 {
105 	struct private_uuencode *state = (struct private_uuencode *)f->data;
106 
107 	if (strcmp(key, "mode") == 0) {
108 		if (value == NULL) {
109 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
110 			    "mode option requires octal digits");
111 			return (ARCHIVE_FAILED);
112 		}
113 		state->mode = (int)atol8(value, strlen(value)) & 0777;
114 		return (ARCHIVE_OK);
115 	} else if (strcmp(key, "name") == 0) {
116 		if (value == NULL) {
117 			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
118 			    "name option requires a string");
119 			return (ARCHIVE_FAILED);
120 		}
121 		archive_strcpy(&state->name, value);
122 		return (ARCHIVE_OK);
123 	}
124 
125 	/* Note: The "warn" return is just to inform the options
126 	 * supervisor that we didn't handle it.  It will generate
127 	 * a suitable error if no one used this option. */
128 	return (ARCHIVE_WARN);
129 }
130 
131 /*
132  * Setup callback.
133  */
134 static int
archive_filter_uuencode_open(struct archive_write_filter * f)135 archive_filter_uuencode_open(struct archive_write_filter *f)
136 {
137 	struct private_uuencode *state = (struct private_uuencode *)f->data;
138 	size_t bs = 65536, bpb;
139 
140 	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
141 		/* Buffer size should be a multiple number of the of bytes
142 		 * per block for performance. */
143 		bpb = archive_write_get_bytes_per_block(f->archive);
144 		if (bpb > bs)
145 			bs = bpb;
146 		else if (bpb != 0)
147 			bs -= bs % bpb;
148 	}
149 
150 	state->bs = bs;
151 	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
152 		archive_set_error(f->archive, ENOMEM,
153 		    "Can't allocate data for uuencode buffer");
154 		return (ARCHIVE_FATAL);
155 	}
156 
157 	archive_string_sprintf(&state->encoded_buff, "begin %o %s\n",
158 	    (unsigned int)state->mode, state->name.s);
159 
160 	f->data = state;
161 	return (0);
162 }
163 
164 static void
uu_encode(struct archive_string * as,const unsigned char * p,size_t len)165 uu_encode(struct archive_string *as, const unsigned char *p, size_t len)
166 {
167 	int c;
168 
169 	c = (int)len;
170 	archive_strappend_char(as, c?c + 0x20:'`');
171 	for (; len >= 3; p += 3, len -= 3) {
172 		c = p[0] >> 2;
173 		archive_strappend_char(as, c?c + 0x20:'`');
174 		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
175 		archive_strappend_char(as, c?c + 0x20:'`');
176 		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
177 		archive_strappend_char(as, c?c + 0x20:'`');
178 		c = p[2] & 0x3f;
179 		archive_strappend_char(as, c?c + 0x20:'`');
180 	}
181 	if (len > 0) {
182 		c = p[0] >> 2;
183 		archive_strappend_char(as, c?c + 0x20:'`');
184 		c = (p[0] & 0x03) << 4;
185 		if (len == 1) {
186 			archive_strappend_char(as, c?c + 0x20:'`');
187 			archive_strappend_char(as, '`');
188 			archive_strappend_char(as, '`');
189 		} else {
190 			c |= (p[1] & 0xf0) >> 4;
191 			archive_strappend_char(as, c?c + 0x20:'`');
192 			c = (p[1] & 0x0f) << 2;
193 			archive_strappend_char(as, c?c + 0x20:'`');
194 			archive_strappend_char(as, '`');
195 		}
196 	}
197 	archive_strappend_char(as, '\n');
198 }
199 
200 /*
201  * Write data to the encoded stream.
202  */
203 static int
archive_filter_uuencode_write(struct archive_write_filter * f,const void * buff,size_t length)204 archive_filter_uuencode_write(struct archive_write_filter *f, const void *buff,
205     size_t length)
206 {
207 	struct private_uuencode *state = (struct private_uuencode *)f->data;
208 	const unsigned char *p = buff;
209 	int ret = ARCHIVE_OK;
210 
211 	if (length == 0)
212 		return (ret);
213 
214 	if (state->hold_len) {
215 		while (state->hold_len < LBYTES && length > 0) {
216 			state->hold[state->hold_len++] = *p++;
217 			length--;
218 		}
219 		if (state->hold_len < LBYTES)
220 			return (ret);
221 		uu_encode(&state->encoded_buff, state->hold, LBYTES);
222 		state->hold_len = 0;
223 	}
224 
225 	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
226 		uu_encode(&state->encoded_buff, p, LBYTES);
227 
228 	/* Save remaining bytes. */
229 	if (length > 0) {
230 		memcpy(state->hold, p, length);
231 		state->hold_len = length;
232 	}
233 	while (archive_strlen(&state->encoded_buff) >= state->bs) {
234 		ret = __archive_write_filter(f->next_filter,
235 		    state->encoded_buff.s, state->bs);
236 		memmove(state->encoded_buff.s,
237 		    state->encoded_buff.s + state->bs,
238 		    state->encoded_buff.length - state->bs);
239 		state->encoded_buff.length -= state->bs;
240 	}
241 
242 	return (ret);
243 }
244 
245 
246 /*
247  * Finish the compression...
248  */
249 static int
archive_filter_uuencode_close(struct archive_write_filter * f)250 archive_filter_uuencode_close(struct archive_write_filter *f)
251 {
252 	struct private_uuencode *state = (struct private_uuencode *)f->data;
253 
254 	/* Flush remaining bytes. */
255 	if (state->hold_len != 0)
256 		uu_encode(&state->encoded_buff, state->hold, state->hold_len);
257 	archive_string_sprintf(&state->encoded_buff, "`\nend\n");
258 	/* Write the last block */
259 	archive_write_set_bytes_in_last_block(f->archive, 1);
260 	return __archive_write_filter(f->next_filter,
261 	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
262 }
263 
264 static int
archive_filter_uuencode_free(struct archive_write_filter * f)265 archive_filter_uuencode_free(struct archive_write_filter *f)
266 {
267 	struct private_uuencode *state = (struct private_uuencode *)f->data;
268 
269 	archive_string_free(&state->name);
270 	archive_string_free(&state->encoded_buff);
271 	free(state);
272 	return (ARCHIVE_OK);
273 }
274 
275 static int64_t
atol8(const char * p,size_t char_cnt)276 atol8(const char *p, size_t char_cnt)
277 {
278 	int64_t l;
279 	int digit;
280 
281 	l = 0;
282 	while (char_cnt-- > 0) {
283 		if (*p >= '0' && *p <= '7')
284 			digit = *p - '0';
285 		else
286 			break;
287 		p++;
288 		l <<= 3;
289 		l |= digit;
290 	}
291 	return (l);
292 }
293 
294