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 57 46248590Smm 47248590Smmstruct private_b64encode { 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_b64encode_options(struct archive_write_filter *, 57248590Smm const char *, const char *); 58248590Smmstatic int archive_filter_b64encode_open(struct archive_write_filter *); 59248590Smmstatic int archive_filter_b64encode_write(struct archive_write_filter *, 60248590Smm const void *, size_t); 61248590Smmstatic int archive_filter_b64encode_close(struct archive_write_filter *); 62248590Smmstatic int archive_filter_b64encode_free(struct archive_write_filter *); 63248590Smmstatic void b64_encode(struct archive_string *, const unsigned char *, size_t); 64248590Smmstatic int64_t atol8(const char *, size_t); 65248590Smm 66248590Smmstatic const char base64[] = { 67248590Smm 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 68248590Smm 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 69248590Smm 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 70248590Smm 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 71248590Smm 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 72248590Smm 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 73248590Smm 'w', 'x', 'y', 'z', '0', '1', '2', '3', 74248590Smm '4', '5', '6', '7', '8', '9', '+', '/' 75248590Smm}; 76248590Smm 77248590Smm/* 78248590Smm * Add a compress filter to this write handle. 79248590Smm */ 80248590Smmint 81248590Smmarchive_write_add_filter_b64encode(struct archive *_a) 82248590Smm{ 83248590Smm struct archive_write *a = (struct archive_write *)_a; 84248590Smm struct archive_write_filter *f = __archive_write_allocate_filter(_a); 85248590Smm struct private_b64encode *state; 86248590Smm 87248590Smm archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 88248590Smm ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); 89248590Smm 90248590Smm state = (struct private_b64encode *)calloc(1, sizeof(*state)); 91248590Smm if (state == NULL) { 92248590Smm archive_set_error(f->archive, ENOMEM, 93248590Smm "Can't allocate data for b64encode filter"); 94248590Smm return (ARCHIVE_FATAL); 95248590Smm } 96248590Smm archive_strcpy(&state->name, "-"); 97248590Smm state->mode = 0644; 98248590Smm 99248590Smm f->data = state; 100248590Smm f->name = "b64encode"; 101248590Smm f->code = ARCHIVE_FILTER_UU; 102248590Smm f->open = archive_filter_b64encode_open; 103248590Smm f->options = archive_filter_b64encode_options; 104248590Smm f->write = archive_filter_b64encode_write; 105248590Smm f->close = archive_filter_b64encode_close; 106248590Smm f->free = archive_filter_b64encode_free; 107248590Smm 108248590Smm return (ARCHIVE_OK); 109248590Smm} 110248590Smm 111248590Smm/* 112248590Smm * Set write options. 113248590Smm */ 114248590Smmstatic int 115248590Smmarchive_filter_b64encode_options(struct archive_write_filter *f, const char *key, 116248590Smm const char *value) 117248590Smm{ 118248590Smm struct private_b64encode *state = (struct private_b64encode *)f->data; 119248590Smm 120248590Smm if (strcmp(key, "mode") == 0) { 121248590Smm if (value == NULL) { 122248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 123248590Smm "mode option requires octal digits"); 124248590Smm return (ARCHIVE_FAILED); 125248590Smm } 126248590Smm state->mode = (int)atol8(value, strlen(value)) & 0777; 127248590Smm return (ARCHIVE_OK); 128248590Smm } else if (strcmp(key, "name") == 0) { 129248590Smm if (value == NULL) { 130248590Smm archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 131248590Smm "name option requires a string"); 132248590Smm return (ARCHIVE_FAILED); 133248590Smm } 134248590Smm archive_strcpy(&state->name, value); 135248590Smm return (ARCHIVE_OK); 136248590Smm } 137248590Smm 138248590Smm /* Note: The "warn" return is just to inform the options 139248590Smm * supervisor that we didn't handle it. It will generate 140248590Smm * a suitable error if no one used this option. */ 141248590Smm return (ARCHIVE_WARN); 142248590Smm} 143248590Smm 144248590Smm/* 145248590Smm * Setup callback. 146248590Smm */ 147248590Smmstatic int 148248590Smmarchive_filter_b64encode_open(struct archive_write_filter *f) 149248590Smm{ 150248590Smm struct private_b64encode *state = (struct private_b64encode *)f->data; 151248590Smm size_t bs = 65536, bpb; 152248590Smm int ret; 153248590Smm 154248590Smm ret = __archive_write_open_filter(f->next_filter); 155248590Smm if (ret != ARCHIVE_OK) 156248590Smm return (ret); 157248590Smm 158248590Smm if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 159248590Smm /* Buffer size should be a multiple number of the of bytes 160248590Smm * per block for performance. */ 161248590Smm bpb = archive_write_get_bytes_per_block(f->archive); 162248590Smm if (bpb > bs) 163248590Smm bs = bpb; 164248590Smm else if (bpb != 0) 165248590Smm bs -= bs % bpb; 166248590Smm } 167248590Smm 168248590Smm state->bs = bs; 169248590Smm if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { 170248590Smm archive_set_error(f->archive, ENOMEM, 171248590Smm "Can't allocate data for b64encode buffer"); 172248590Smm return (ARCHIVE_FATAL); 173248590Smm } 174248590Smm 175248590Smm archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", 176248590Smm state->mode, state->name.s); 177248590Smm 178248590Smm f->data = state; 179248590Smm return (0); 180248590Smm} 181248590Smm 182248590Smmstatic void 183248590Smmb64_encode(struct archive_string *as, const unsigned char *p, size_t len) 184248590Smm{ 185248590Smm int c; 186248590Smm 187248590Smm for (; len >= 3; p += 3, len -= 3) { 188248590Smm c = p[0] >> 2; 189248590Smm archive_strappend_char(as, base64[c]); 190248590Smm c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); 191248590Smm archive_strappend_char(as, base64[c]); 192248590Smm c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); 193248590Smm archive_strappend_char(as, base64[c]); 194248590Smm c = p[2] & 0x3f; 195248590Smm archive_strappend_char(as, base64[c]); 196248590Smm } 197248590Smm if (len > 0) { 198248590Smm c = p[0] >> 2; 199248590Smm archive_strappend_char(as, base64[c]); 200248590Smm c = (p[0] & 0x03) << 4; 201248590Smm if (len == 1) { 202248590Smm archive_strappend_char(as, base64[c]); 203248590Smm archive_strappend_char(as, '='); 204248590Smm archive_strappend_char(as, '='); 205248590Smm } else { 206248590Smm c |= (p[1] & 0xf0) >> 4; 207248590Smm archive_strappend_char(as, base64[c]); 208248590Smm c = (p[1] & 0x0f) << 2; 209248590Smm archive_strappend_char(as, base64[c]); 210248590Smm archive_strappend_char(as, '='); 211248590Smm } 212248590Smm } 213248590Smm archive_strappend_char(as, '\n'); 214248590Smm} 215248590Smm 216248590Smm/* 217248590Smm * Write data to the encoded stream. 218248590Smm */ 219248590Smmstatic int 220248590Smmarchive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, 221248590Smm size_t length) 222248590Smm{ 223248590Smm struct private_b64encode *state = (struct private_b64encode *)f->data; 224248590Smm const unsigned char *p = buff; 225248590Smm int ret = ARCHIVE_OK; 226248590Smm 227248590Smm if (length == 0) 228248590Smm return (ret); 229248590Smm 230248590Smm if (state->hold_len) { 231248590Smm while (state->hold_len < LBYTES && length > 0) { 232248590Smm state->hold[state->hold_len++] = *p++; 233248590Smm length--; 234248590Smm } 235248590Smm if (state->hold_len < LBYTES) 236248590Smm return (ret); 237248590Smm b64_encode(&state->encoded_buff, state->hold, LBYTES); 238248590Smm state->hold_len = 0; 239248590Smm } 240248590Smm 241248590Smm for (; length >= LBYTES; length -= LBYTES, p += LBYTES) 242248590Smm b64_encode(&state->encoded_buff, p, LBYTES); 243248590Smm 244248590Smm /* Save remaining bytes. */ 245248590Smm if (length > 0) { 246248590Smm memcpy(state->hold, p, length); 247248590Smm state->hold_len = length; 248248590Smm } 249248590Smm while (archive_strlen(&state->encoded_buff) >= state->bs) { 250248590Smm ret = __archive_write_filter(f->next_filter, 251248590Smm state->encoded_buff.s, state->bs); 252248590Smm memmove(state->encoded_buff.s, 253248590Smm state->encoded_buff.s + state->bs, 254248590Smm state->encoded_buff.length - state->bs); 255248590Smm state->encoded_buff.length -= state->bs; 256248590Smm } 257248590Smm 258248590Smm return (ret); 259248590Smm} 260248590Smm 261248590Smm 262248590Smm/* 263248590Smm * Finish the compression... 264248590Smm */ 265248590Smmstatic int 266248590Smmarchive_filter_b64encode_close(struct archive_write_filter *f) 267248590Smm{ 268248590Smm struct private_b64encode *state = (struct private_b64encode *)f->data; 269248590Smm int ret, ret2; 270248590Smm 271248590Smm /* Flush remaining bytes. */ 272248590Smm if (state->hold_len != 0) 273248590Smm b64_encode(&state->encoded_buff, state->hold, state->hold_len); 274248590Smm archive_string_sprintf(&state->encoded_buff, "====\n"); 275248590Smm /* Write the last block */ 276248590Smm archive_write_set_bytes_in_last_block(f->archive, 1); 277248590Smm ret = __archive_write_filter(f->next_filter, 278248590Smm state->encoded_buff.s, archive_strlen(&state->encoded_buff)); 279248590Smm ret2 = __archive_write_close_filter(f->next_filter); 280248590Smm if (ret > ret2) 281248590Smm ret = ret2; 282248590Smm return (ret); 283248590Smm} 284248590Smm 285248590Smmstatic int 286248590Smmarchive_filter_b64encode_free(struct archive_write_filter *f) 287248590Smm{ 288248590Smm struct private_b64encode *state = (struct private_b64encode *)f->data; 289248590Smm 290248590Smm archive_string_free(&state->name); 291248590Smm archive_string_free(&state->encoded_buff); 292248590Smm free(state); 293248590Smm return (ARCHIVE_OK); 294248590Smm} 295248590Smm 296248590Smmstatic int64_t 297248590Smmatol8(const char *p, size_t char_cnt) 298248590Smm{ 299248590Smm int64_t l; 300248590Smm int digit; 301248590Smm 302248590Smm l = 0; 303248590Smm while (char_cnt-- > 0) { 304248590Smm if (*p >= '0' && *p <= '7') 305248590Smm digit = *p - '0'; 306248590Smm else 307248590Smm break; 308248590Smm p++; 309248590Smm l <<= 3; 310248590Smm l |= digit; 311248590Smm } 312248590Smm return (l); 313248590Smm} 314248590Smm 315