1248590Smm/*- 2248590Smm * Copyright (c) 2012 Michihiro NAKAJIMA 3248590Smm * All rights reserved. 4248590Smm * 5248590Smm * Redistribution and use in source and binary forms, with or without 6248590Smm * modification, are permitted provided that the following conditions 7248590Smm * are met: 8248590Smm * 1. Redistributions of source code must retain the above copyright 9248590Smm * notice, this list of conditions and the following disclaimer. 10248590Smm * 2. Redistributions in binary form must reproduce the above copyright 11248590Smm * notice, this list of conditions and the following disclaimer in the 12248590Smm * documentation and/or other materials provided with the distribution. 13248590Smm * 14248590Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15248590Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16248590Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17248590Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18248590Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19248590Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20248590Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21248590Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22248590Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23248590Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24248590Smm */ 25248590Smm 26248590Smm#include "archive_platform.h" 27248590Smm 28248590Smm__FBSDID("$FreeBSD$"); 29248590Smm 30248590Smm#ifdef HAVE_ERRNO_H 31248590Smm#include <errno.h> 32248590Smm#endif 33248590Smm#ifdef HAVE_STDLIB_H 34248590Smm#include <stdlib.h> 35248590Smm#endif 36248590Smm#ifdef HAVE_STRING_H 37248590Smm#include <string.h> 38248590Smm#endif 39248590Smm 40248590Smm#include "archive.h" 41248590Smm#include "archive_private.h" 42248590Smm#include "archive_string.h" 43248590Smm#include "archive_write_private.h" 44248590Smm 45248590Smm#define LBYTES 45 46248590Smm 47248590Smmstruct private_uuencode { 48248590Smm int mode; 49248590Smm struct archive_string name; 50248590Smm struct archive_string encoded_buff; 51248590Smm size_t bs; 52248590Smm size_t hold_len; 53248590Smm unsigned char hold[LBYTES]; 54248590Smm}; 55248590Smm 56248590Smmstatic int archive_filter_uuencode_options(struct archive_write_filter *, 57248590Smm const char *, const char *); 58248590Smmstatic int archive_filter_uuencode_open(struct archive_write_filter *); 59248590Smmstatic int archive_filter_uuencode_write(struct archive_write_filter *, 60248590Smm const void *, size_t); 61248590Smmstatic int archive_filter_uuencode_close(struct archive_write_filter *); 62248590Smmstatic int archive_filter_uuencode_free(struct archive_write_filter *); 63248590Smmstatic void uu_encode(struct archive_string *, const unsigned char *, size_t); 64248590Smmstatic int64_t atol8(const char *, size_t); 65248590Smm 66248590Smm/* 67248590Smm * Add a compress filter to this write handle. 68248590Smm */ 69248590Smmint 70248590Smmarchive_write_add_filter_uuencode(struct archive *_a) 71248590Smm{ 72248590Smm struct archive_write *a = (struct archive_write *)_a; 73248590Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 74248590Smm struct private_uuencode *state; 75248590Smm 76248590Smm archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 77248590Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); 78248590Smm 79248590Smm state = (struct private_uuencode *)calloc(1, sizeof(*state)); 80248590Smm if (state == NULL) { 81248590Smm archive_set_error(f->archive, ENOMEM, 82248590Smm "Can't allocate data for uuencode filter"); 83248590Smm return (ARCHIVE_FATAL); 84248590Smm } 85248590Smm archive_strcpy(&state->name, "-"); 86248590Smm state->mode = 0644; 87248590Smm 88248590Smm f->data = state; 89248590Smm f->name = "uuencode"; 90248590Smm f->code = ARCHIVE_FILTER_UU; 91248590Smm f->open = archive_filter_uuencode_open; 92248590Smm f->options = archive_filter_uuencode_options; 93248590Smm f->write = archive_filter_uuencode_write; 94248590Smm f->close = archive_filter_uuencode_close; 95248590Smm f->free = archive_filter_uuencode_free; 96248590Smm 97248590Smm return (ARCHIVE_OK); 98248590Smm} 99248590Smm 100248590Smm/* 101248590Smm * Set write options. 102248590Smm */ 103248590Smmstatic int 104248590Smmarchive_filter_uuencode_options(struct archive_write_filter *f, const char *key, 105248590Smm const char *value) 106248590Smm{ 107248590Smm struct private_uuencode *state = (struct private_uuencode *)f->data; 108248590Smm 109248590Smm if (strcmp(key, "mode") == 0) { 110248590Smm if (value == NULL) { 111248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 112248590Smm "mode option requires octal digits"); 113248590Smm return (ARCHIVE_FAILED); 114248590Smm } 115248590Smm state->mode = (int)atol8(value, strlen(value)) & 0777; 116248590Smm return (ARCHIVE_OK); 117248590Smm } else if (strcmp(key, "name") == 0) { 118248590Smm if (value == NULL) { 119248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 120248590Smm "name option requires a string"); 121248590Smm return (ARCHIVE_FAILED); 122248590Smm } 123248590Smm archive_strcpy(&state->name, value); 124248590Smm return (ARCHIVE_OK); 125248590Smm } 126248590Smm 127248590Smm /* Note: The "warn" return is just to inform the options 128248590Smm * supervisor that we didn't handle it. It will generate 129248590Smm * a suitable error if no one used this option. */ 130248590Smm return (ARCHIVE_WARN); 131248590Smm} 132248590Smm 133248590Smm/* 134248590Smm * Setup callback. 135248590Smm */ 136248590Smmstatic int 137248590Smmarchive_filter_uuencode_open(struct archive_write_filter *f) 138248590Smm{ 139248590Smm struct private_uuencode *state = (struct private_uuencode *)f->data; 140248590Smm size_t bs = 65536, bpb; 141248590Smm 142248590Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 143248590Smm /* Buffer size should be a multiple number of the of bytes 144248590Smm * per block for performance. */ 145248590Smm bpb = archive_write_get_bytes_per_block(f->archive); 146248590Smm if (bpb > bs) 147248590Smm bs = bpb; 148248590Smm else if (bpb != 0) 149248590Smm bs -= bs % bpb; 150248590Smm } 151248590Smm 152248590Smm state->bs = bs; 153248590Smm if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { 154248590Smm archive_set_error(f->archive, ENOMEM, 155248590Smm "Can't allocate data for uuencode buffer"); 156248590Smm return (ARCHIVE_FATAL); 157248590Smm } 158248590Smm 159248590Smm archive_string_sprintf(&state->encoded_buff, "begin %o %s\n", 160248590Smm state->mode, state->name.s); 161248590Smm 162248590Smm f->data = state; 163248590Smm return (0); 164248590Smm} 165248590Smm 166248590Smmstatic void 167248590Smmuu_encode(struct archive_string *as, const unsigned char *p, size_t len) 168248590Smm{ 169248590Smm int c; 170248590Smm 171248590Smm c = (int)len; 172248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 173248590Smm for (; len >= 3; p += 3, len -= 3) { 174248590Smm c = p[0] >> 2; 175248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 176248590Smm c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); 177248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 178248590Smm c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); 179248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 180248590Smm c = p[2] & 0x3f; 181248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 182248590Smm } 183248590Smm if (len > 0) { 184248590Smm c = p[0] >> 2; 185248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 186248590Smm c = (p[0] & 0x03) << 4; 187248590Smm if (len == 1) { 188248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 189248590Smm archive_strappend_char(as, '`'); 190248590Smm archive_strappend_char(as, '`'); 191248590Smm } else { 192248590Smm c |= (p[1] & 0xf0) >> 4; 193248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 194248590Smm c = (p[1] & 0x0f) << 2; 195248590Smm archive_strappend_char(as, c?c + 0x20:'`'); 196248590Smm archive_strappend_char(as, '`'); 197248590Smm } 198248590Smm } 199248590Smm archive_strappend_char(as, '\n'); 200248590Smm} 201248590Smm 202248590Smm/* 203248590Smm * Write data to the encoded stream. 204248590Smm */ 205248590Smmstatic int 206248590Smmarchive_filter_uuencode_write(struct archive_write_filter *f, const void *buff, 207248590Smm size_t length) 208248590Smm{ 209248590Smm struct private_uuencode *state = (struct private_uuencode *)f->data; 210248590Smm const unsigned char *p = buff; 211248590Smm int ret = ARCHIVE_OK; 212248590Smm 213248590Smm if (length == 0) 214248590Smm return (ret); 215248590Smm 216248590Smm if (state->hold_len) { 217248590Smm while (state->hold_len < LBYTES && length > 0) { 218248590Smm state->hold[state->hold_len++] = *p++; 219248590Smm length--; 220248590Smm } 221248590Smm if (state->hold_len < LBYTES) 222248590Smm return (ret); 223248590Smm uu_encode(&state->encoded_buff, state->hold, LBYTES); 224248590Smm state->hold_len = 0; 225248590Smm } 226248590Smm 227248590Smm for (; length >= LBYTES; length -= LBYTES, p += LBYTES) 228248590Smm uu_encode(&state->encoded_buff, p, LBYTES); 229248590Smm 230248590Smm /* Save remaining bytes. */ 231248590Smm if (length > 0) { 232248590Smm memcpy(state->hold, p, length); 233248590Smm state->hold_len = length; 234248590Smm } 235248590Smm while (archive_strlen(&state->encoded_buff) >= state->bs) { 236248590Smm ret = __archive_write_filter(f->next_filter, 237248590Smm state->encoded_buff.s, state->bs); 238248590Smm memmove(state->encoded_buff.s, 239248590Smm state->encoded_buff.s + state->bs, 240248590Smm state->encoded_buff.length - state->bs); 241248590Smm state->encoded_buff.length -= state->bs; 242248590Smm } 243248590Smm 244248590Smm return (ret); 245248590Smm} 246248590Smm 247248590Smm 248248590Smm/* 249248590Smm * Finish the compression... 250248590Smm */ 251248590Smmstatic int 252248590Smmarchive_filter_uuencode_close(struct archive_write_filter *f) 253248590Smm{ 254248590Smm struct private_uuencode *state = (struct private_uuencode *)f->data; 255248590Smm 256248590Smm /* Flush remaining bytes. */ 257248590Smm if (state->hold_len != 0) 258248590Smm uu_encode(&state->encoded_buff, state->hold, state->hold_len); 259248590Smm archive_string_sprintf(&state->encoded_buff, "`\nend\n"); 260248590Smm /* Write the last block */ 261248590Smm archive_write_set_bytes_in_last_block(f->archive, 1); 262358090Smm return __archive_write_filter(f->next_filter, 263248590Smm state->encoded_buff.s, archive_strlen(&state->encoded_buff)); 264248590Smm} 265248590Smm 266248590Smmstatic int 267248590Smmarchive_filter_uuencode_free(struct archive_write_filter *f) 268248590Smm{ 269248590Smm struct private_uuencode *state = (struct private_uuencode *)f->data; 270248590Smm 271248590Smm archive_string_free(&state->name); 272248590Smm archive_string_free(&state->encoded_buff); 273248590Smm free(state); 274248590Smm return (ARCHIVE_OK); 275248590Smm} 276248590Smm 277248590Smmstatic int64_t 278248590Smmatol8(const char *p, size_t char_cnt) 279248590Smm{ 280248590Smm int64_t l; 281248590Smm int digit; 282248590Smm 283248590Smm l = 0; 284248590Smm while (char_cnt-- > 0) { 285248590Smm if (*p >= '0' && *p <= '7') 286248590Smm digit = *p - '0'; 287248590Smm else 288248590Smm break; 289248590Smm p++; 290248590Smm l <<= 3; 291248590Smm l |= digit; 292248590Smm } 293248590Smm return (l); 294248590Smm} 295248590Smm 296