Blame libarchive/archive_write_add_filter_b64encode.c

Packit Service 1d0348
/*-
Packit Service 1d0348
 * Copyright (c) 2012 Michihiro NAKAJIMA
Packit Service 1d0348
 * All rights reserved.
Packit Service 1d0348
 *
Packit Service 1d0348
 * Redistribution and use in source and binary forms, with or without
Packit Service 1d0348
 * modification, are permitted provided that the following conditions
Packit Service 1d0348
 * are met:
Packit Service 1d0348
 * 1. Redistributions of source code must retain the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer.
Packit Service 1d0348
 * 2. Redistributions in binary form must reproduce the above copyright
Packit Service 1d0348
 *    notice, this list of conditions and the following disclaimer in the
Packit Service 1d0348
 *    documentation and/or other materials provided with the distribution.
Packit Service 1d0348
 *
Packit Service 1d0348
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
Packit Service 1d0348
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
Packit Service 1d0348
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
Packit Service 1d0348
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
Packit Service 1d0348
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
Packit Service 1d0348
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
Packit Service 1d0348
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
Packit Service 1d0348
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Packit Service 1d0348
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
Packit Service 1d0348
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit Service 1d0348
 */
Packit Service 1d0348
Packit Service 1d0348
#include "archive_platform.h"
Packit Service 1d0348
Packit Service 1d0348
__FBSDID("$FreeBSD$");
Packit Service 1d0348
Packit Service 1d0348
#ifdef HAVE_ERRNO_H
Packit Service 1d0348
#include <errno.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STDLIB_H
Packit Service 1d0348
#include <stdlib.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
#ifdef HAVE_STRING_H
Packit Service 1d0348
#include <string.h>
Packit Service 1d0348
#endif
Packit Service 1d0348
Packit Service 1d0348
#include "archive.h"
Packit Service 1d0348
#include "archive_private.h"
Packit Service 1d0348
#include "archive_string.h"
Packit Service 1d0348
#include "archive_write_private.h"
Packit Service 1d0348
Packit Service 1d0348
#define LBYTES	57
Packit Service 1d0348
Packit Service 1d0348
struct private_b64encode {
Packit Service 1d0348
	int			mode;
Packit Service 1d0348
	struct archive_string	name;
Packit Service 1d0348
	struct archive_string	encoded_buff;
Packit Service 1d0348
	size_t			bs;
Packit Service 1d0348
	size_t			hold_len;
Packit Service 1d0348
	unsigned char		hold[LBYTES];
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
static int archive_filter_b64encode_options(struct archive_write_filter *,
Packit Service 1d0348
    const char *, const char *);
Packit Service 1d0348
static int archive_filter_b64encode_open(struct archive_write_filter *);
Packit Service 1d0348
static int archive_filter_b64encode_write(struct archive_write_filter *,
Packit Service 1d0348
    const void *, size_t);
Packit Service 1d0348
static int archive_filter_b64encode_close(struct archive_write_filter *);
Packit Service 1d0348
static int archive_filter_b64encode_free(struct archive_write_filter *);
Packit Service 1d0348
static void b64_encode(struct archive_string *, const unsigned char *, size_t);
Packit Service 1d0348
static int64_t atol8(const char *, size_t);
Packit Service 1d0348
Packit Service 1d0348
static const char base64[] = {
Packit Service 1d0348
	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
Packit Service 1d0348
	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
Packit Service 1d0348
	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
Packit Service 1d0348
	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
Packit Service 1d0348
	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
Packit Service 1d0348
	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
Packit Service 1d0348
	'w', 'x', 'y', 'z', '0', '1', '2', '3',
Packit Service 1d0348
	'4', '5', '6', '7', '8', '9', '+', '/'
Packit Service 1d0348
};
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Add a compress filter to this write handle.
Packit Service 1d0348
 */
Packit Service 1d0348
int
Packit Service 1d0348
archive_write_add_filter_b64encode(struct archive *_a)
Packit Service 1d0348
{
Packit Service 1d0348
	struct archive_write *a = (struct archive_write *)_a;
Packit Service 1d0348
	struct archive_write_filter *f = __archive_write_allocate_filter(_a);
Packit Service 1d0348
	struct private_b64encode *state;
Packit Service 1d0348
Packit Service 1d0348
	archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
Packit Service 1d0348
	    ARCHIVE_STATE_NEW, "archive_write_add_filter_uu");
Packit Service 1d0348
Packit Service 1d0348
	state = (struct private_b64encode *)calloc(1, sizeof(*state));
Packit Service 1d0348
	if (state == NULL) {
Packit Service 1d0348
		archive_set_error(f->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate data for b64encode filter");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
	archive_strcpy(&state->name, "-");
Packit Service 1d0348
	state->mode = 0644;
Packit Service 1d0348
Packit Service 1d0348
	f->data = state;
Packit Service 1d0348
	f->name = "b64encode";
Packit Service 1d0348
	f->code = ARCHIVE_FILTER_UU;
Packit Service 1d0348
	f->open = archive_filter_b64encode_open;
Packit Service 1d0348
	f->options = archive_filter_b64encode_options;
Packit Service 1d0348
	f->write = archive_filter_b64encode_write;
Packit Service 1d0348
	f->close = archive_filter_b64encode_close;
Packit Service 1d0348
	f->free = archive_filter_b64encode_free;
Packit Service 1d0348
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Set write options.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_b64encode_options(struct archive_write_filter *f, const char *key,
Packit Service 1d0348
    const char *value)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_b64encode *state = (struct private_b64encode *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	if (strcmp(key, "mode") == 0) {
Packit Service 1d0348
		if (value == NULL) {
Packit Service 1d0348
			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
			    "mode option requires octal digits");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		}
Packit Service 1d0348
		state->mode = (int)atol8(value, strlen(value)) & 0777;
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	} else if (strcmp(key, "name") == 0) {
Packit Service 1d0348
		if (value == NULL) {
Packit Service 1d0348
			archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
Packit Service 1d0348
			    "name option requires a string");
Packit Service 1d0348
			return (ARCHIVE_FAILED);
Packit Service 1d0348
		}
Packit Service 1d0348
		archive_strcpy(&state->name, value);
Packit Service 1d0348
		return (ARCHIVE_OK);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	/* Note: The "warn" return is just to inform the options
Packit Service 1d0348
	 * supervisor that we didn't handle it.  It will generate
Packit Service 1d0348
	 * a suitable error if no one used this option. */
Packit Service 1d0348
	return (ARCHIVE_WARN);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Setup callback.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_b64encode_open(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_b64encode *state = (struct private_b64encode *)f->data;
Packit Service 1d0348
	size_t bs = 65536, bpb;
Packit Service 1d0348
	int ret;
Packit Service 1d0348
Packit Service 1d0348
	ret = __archive_write_open_filter(f->next_filter);
Packit Service 1d0348
	if (ret != ARCHIVE_OK)
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
Packit Service 1d0348
	if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
Packit Service 1d0348
		/* Buffer size should be a multiple number of the of bytes
Packit Service 1d0348
		 * per block for performance. */
Packit Service 1d0348
		bpb = archive_write_get_bytes_per_block(f->archive);
Packit Service 1d0348
		if (bpb > bs)
Packit Service 1d0348
			bs = bpb;
Packit Service 1d0348
		else if (bpb != 0)
Packit Service 1d0348
			bs -= bs % bpb;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	state->bs = bs;
Packit Service 1d0348
	if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) {
Packit Service 1d0348
		archive_set_error(f->archive, ENOMEM,
Packit Service 1d0348
		    "Can't allocate data for b64encode buffer");
Packit Service 1d0348
		return (ARCHIVE_FATAL);
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n",
Packit Service 1d0348
	    state->mode, state->name.s);
Packit Service 1d0348
Packit Service 1d0348
	f->data = state;
Packit Service 1d0348
	return (0);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static void
Packit Service 1d0348
b64_encode(struct archive_string *as, const unsigned char *p, size_t len)
Packit Service 1d0348
{
Packit Service 1d0348
	int c;
Packit Service 1d0348
Packit Service 1d0348
	for (; len >= 3; p += 3, len -= 3) {
Packit Service 1d0348
		c = p[0] >> 2;
Packit Service 1d0348
		archive_strappend_char(as, base64[c]);
Packit Service 1d0348
		c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4);
Packit Service 1d0348
		archive_strappend_char(as, base64[c]);
Packit Service 1d0348
		c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6);
Packit Service 1d0348
		archive_strappend_char(as, base64[c]);
Packit Service 1d0348
		c = p[2] & 0x3f;
Packit Service 1d0348
		archive_strappend_char(as, base64[c]);
Packit Service 1d0348
	}
Packit Service 1d0348
	if (len > 0) {
Packit Service 1d0348
		c = p[0] >> 2;
Packit Service 1d0348
		archive_strappend_char(as, base64[c]);
Packit Service 1d0348
		c = (p[0] & 0x03) << 4;
Packit Service 1d0348
		if (len == 1) {
Packit Service 1d0348
			archive_strappend_char(as, base64[c]);
Packit Service 1d0348
			archive_strappend_char(as, '=');
Packit Service 1d0348
			archive_strappend_char(as, '=');
Packit Service 1d0348
		} else {
Packit Service 1d0348
			c |= (p[1] & 0xf0) >> 4;
Packit Service 1d0348
			archive_strappend_char(as, base64[c]);
Packit Service 1d0348
			c = (p[1] & 0x0f) << 2;
Packit Service 1d0348
			archive_strappend_char(as, base64[c]);
Packit Service 1d0348
			archive_strappend_char(as, '=');
Packit Service 1d0348
		}
Packit Service 1d0348
	}
Packit Service 1d0348
	archive_strappend_char(as, '\n');
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Write data to the encoded stream.
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff,
Packit Service 1d0348
    size_t length)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_b64encode *state = (struct private_b64encode *)f->data;
Packit Service 1d0348
	const unsigned char *p = buff;
Packit Service 1d0348
	int ret = ARCHIVE_OK;
Packit Service 1d0348
Packit Service 1d0348
	if (length == 0)
Packit Service 1d0348
		return (ret);
Packit Service 1d0348
Packit Service 1d0348
	if (state->hold_len) {
Packit Service 1d0348
		while (state->hold_len < LBYTES && length > 0) {
Packit Service 1d0348
			state->hold[state->hold_len++] = *p++;
Packit Service 1d0348
			length--;
Packit Service 1d0348
		}
Packit Service 1d0348
		if (state->hold_len < LBYTES)
Packit Service 1d0348
			return (ret);
Packit Service 1d0348
		b64_encode(&state->encoded_buff, state->hold, LBYTES);
Packit Service 1d0348
		state->hold_len = 0;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	for (; length >= LBYTES; length -= LBYTES, p += LBYTES)
Packit Service 1d0348
		b64_encode(&state->encoded_buff, p, LBYTES);
Packit Service 1d0348
Packit Service 1d0348
	/* Save remaining bytes. */
Packit Service 1d0348
	if (length > 0) {
Packit Service 1d0348
		memcpy(state->hold, p, length);
Packit Service 1d0348
		state->hold_len = length;
Packit Service 1d0348
	}
Packit Service 1d0348
	while (archive_strlen(&state->encoded_buff) >= state->bs) {
Packit Service 1d0348
		ret = __archive_write_filter(f->next_filter,
Packit Service 1d0348
		    state->encoded_buff.s, state->bs);
Packit Service 1d0348
		memmove(state->encoded_buff.s,
Packit Service 1d0348
		    state->encoded_buff.s + state->bs,
Packit Service 1d0348
		    state->encoded_buff.length - state->bs);
Packit Service 1d0348
		state->encoded_buff.length -= state->bs;
Packit Service 1d0348
	}
Packit Service 1d0348
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
Packit Service 1d0348
/*
Packit Service 1d0348
 * Finish the compression...
Packit Service 1d0348
 */
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_b64encode_close(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_b64encode *state = (struct private_b64encode *)f->data;
Packit Service 1d0348
	int ret, ret2;
Packit Service 1d0348
Packit Service 1d0348
	/* Flush remaining bytes. */
Packit Service 1d0348
	if (state->hold_len != 0)
Packit Service 1d0348
		b64_encode(&state->encoded_buff, state->hold, state->hold_len);
Packit Service 1d0348
	archive_string_sprintf(&state->encoded_buff, "====\n");
Packit Service 1d0348
	/* Write the last block */
Packit Service 1d0348
	archive_write_set_bytes_in_last_block(f->archive, 1);
Packit Service 1d0348
	ret = __archive_write_filter(f->next_filter,
Packit Service 1d0348
	    state->encoded_buff.s, archive_strlen(&state->encoded_buff));
Packit Service 1d0348
	ret2 = __archive_write_close_filter(f->next_filter);
Packit Service 1d0348
	if (ret > ret2)
Packit Service 1d0348
		ret = ret2;
Packit Service 1d0348
	return (ret);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int
Packit Service 1d0348
archive_filter_b64encode_free(struct archive_write_filter *f)
Packit Service 1d0348
{
Packit Service 1d0348
	struct private_b64encode *state = (struct private_b64encode *)f->data;
Packit Service 1d0348
Packit Service 1d0348
	archive_string_free(&state->name);
Packit Service 1d0348
	archive_string_free(&state->encoded_buff);
Packit Service 1d0348
	free(state);
Packit Service 1d0348
	return (ARCHIVE_OK);
Packit Service 1d0348
}
Packit Service 1d0348
Packit Service 1d0348
static int64_t
Packit Service 1d0348
atol8(const char *p, size_t char_cnt)
Packit Service 1d0348
{
Packit Service 1d0348
	int64_t l;
Packit Service 1d0348
	int digit;
Packit Service 1d0348
        
Packit Service 1d0348
	l = 0;
Packit Service 1d0348
	while (char_cnt-- > 0) {
Packit Service 1d0348
		if (*p >= '0' && *p <= '7')
Packit Service 1d0348
			digit = *p - '0';
Packit Service 1d0348
		else
Packit Service 1d0348
			break;
Packit Service 1d0348
		p++;
Packit Service 1d0348
		l <<= 3;
Packit Service 1d0348
		l |= digit;
Packit Service 1d0348
	}
Packit Service 1d0348
	return (l);
Packit Service 1d0348
}
Packit Service 1d0348