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