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 57 44 45struct private_b64encode { 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 54static int archive_filter_b64encode_options(struct archive_write_filter *, 55 const char *, const char *); 56static int archive_filter_b64encode_open(struct archive_write_filter *); 57static int archive_filter_b64encode_write(struct archive_write_filter *, 58 const void *, size_t); 59static int archive_filter_b64encode_close(struct archive_write_filter *); 60static int archive_filter_b64encode_free(struct archive_write_filter *); 61static void la_b64_encode(struct archive_string *, const unsigned char *, size_t); 62static int64_t atol8(const char *, size_t); 63 64static const char base64[] = { 65 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 66 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 67 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 68 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 69 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 70 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 71 'w', 'x', 'y', 'z', '0', '1', '2', '3', 72 '4', '5', '6', '7', '8', '9', '+', '/' 73}; 74 75/* 76 * Add a compress filter to this write handle. 77 */ 78int 79archive_write_add_filter_b64encode(struct archive *_a) 80{ 81 struct archive_write *a = (struct archive_write *)_a; 82 struct archive_write_filter *f = __archive_write_allocate_filter(_a); 83 struct private_b64encode *state; 84 85 archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC, 86 ARCHIVE_STATE_NEW, "archive_write_add_filter_uu"); 87 88 state = (struct private_b64encode *)calloc(1, sizeof(*state)); 89 if (state == NULL) { 90 archive_set_error(f->archive, ENOMEM, 91 "Can't allocate data for b64encode filter"); 92 return (ARCHIVE_FATAL); 93 } 94 archive_strcpy(&state->name, "-"); 95 state->mode = 0644; 96 97 f->data = state; 98 f->name = "b64encode"; 99 f->code = ARCHIVE_FILTER_UU; 100 f->open = archive_filter_b64encode_open; 101 f->options = archive_filter_b64encode_options; 102 f->write = archive_filter_b64encode_write; 103 f->close = archive_filter_b64encode_close; 104 f->free = archive_filter_b64encode_free; 105 106 return (ARCHIVE_OK); 107} 108 109/* 110 * Set write options. 111 */ 112static int 113archive_filter_b64encode_options(struct archive_write_filter *f, const char *key, 114 const char *value) 115{ 116 struct private_b64encode *state = (struct private_b64encode *)f->data; 117 118 if (strcmp(key, "mode") == 0) { 119 if (value == NULL) { 120 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 121 "mode option requires octal digits"); 122 return (ARCHIVE_FAILED); 123 } 124 state->mode = (int)atol8(value, strlen(value)) & 0777; 125 return (ARCHIVE_OK); 126 } else if (strcmp(key, "name") == 0) { 127 if (value == NULL) { 128 archive_set_error(f->archive, ARCHIVE_ERRNO_MISC, 129 "name option requires a string"); 130 return (ARCHIVE_FAILED); 131 } 132 archive_strcpy(&state->name, value); 133 return (ARCHIVE_OK); 134 } 135 136 /* Note: The "warn" return is just to inform the options 137 * supervisor that we didn't handle it. It will generate 138 * a suitable error if no one used this option. */ 139 return (ARCHIVE_WARN); 140} 141 142/* 143 * Setup callback. 144 */ 145static int 146archive_filter_b64encode_open(struct archive_write_filter *f) 147{ 148 struct private_b64encode *state = (struct private_b64encode *)f->data; 149 size_t bs = 65536, bpb; 150 151 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 152 /* Buffer size should be a multiple number of the of bytes 153 * per block for performance. */ 154 bpb = archive_write_get_bytes_per_block(f->archive); 155 if (bpb > bs) 156 bs = bpb; 157 else if (bpb != 0) 158 bs -= bs % bpb; 159 } 160 161 state->bs = bs; 162 if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { 163 archive_set_error(f->archive, ENOMEM, 164 "Can't allocate data for b64encode buffer"); 165 return (ARCHIVE_FATAL); 166 } 167 168 archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", 169 state->mode, state->name.s); 170 171 f->data = state; 172 return (0); 173} 174 175static void 176la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len) 177{ 178 int c; 179 180 for (; len >= 3; p += 3, len -= 3) { 181 c = p[0] >> 2; 182 archive_strappend_char(as, base64[c]); 183 c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); 184 archive_strappend_char(as, base64[c]); 185 c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); 186 archive_strappend_char(as, base64[c]); 187 c = p[2] & 0x3f; 188 archive_strappend_char(as, base64[c]); 189 } 190 if (len > 0) { 191 c = p[0] >> 2; 192 archive_strappend_char(as, base64[c]); 193 c = (p[0] & 0x03) << 4; 194 if (len == 1) { 195 archive_strappend_char(as, base64[c]); 196 archive_strappend_char(as, '='); 197 archive_strappend_char(as, '='); 198 } else { 199 c |= (p[1] & 0xf0) >> 4; 200 archive_strappend_char(as, base64[c]); 201 c = (p[1] & 0x0f) << 2; 202 archive_strappend_char(as, base64[c]); 203 archive_strappend_char(as, '='); 204 } 205 } 206 archive_strappend_char(as, '\n'); 207} 208 209/* 210 * Write data to the encoded stream. 211 */ 212static int 213archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, 214 size_t length) 215{ 216 struct private_b64encode *state = (struct private_b64encode *)f->data; 217 const unsigned char *p = buff; 218 int ret = ARCHIVE_OK; 219 220 if (length == 0) 221 return (ret); 222 223 if (state->hold_len) { 224 while (state->hold_len < LBYTES && length > 0) { 225 state->hold[state->hold_len++] = *p++; 226 length--; 227 } 228 if (state->hold_len < LBYTES) 229 return (ret); 230 la_b64_encode(&state->encoded_buff, state->hold, LBYTES); 231 state->hold_len = 0; 232 } 233 234 for (; length >= LBYTES; length -= LBYTES, p += LBYTES) 235 la_b64_encode(&state->encoded_buff, p, LBYTES); 236 237 /* Save remaining bytes. */ 238 if (length > 0) { 239 memcpy(state->hold, p, length); 240 state->hold_len = length; 241 } 242 while (archive_strlen(&state->encoded_buff) >= state->bs) { 243 ret = __archive_write_filter(f->next_filter, 244 state->encoded_buff.s, state->bs); 245 memmove(state->encoded_buff.s, 246 state->encoded_buff.s + state->bs, 247 state->encoded_buff.length - state->bs); 248 state->encoded_buff.length -= state->bs; 249 } 250 251 return (ret); 252} 253 254 255/* 256 * Finish the compression... 257 */ 258static int 259archive_filter_b64encode_close(struct archive_write_filter *f) 260{ 261 struct private_b64encode *state = (struct private_b64encode *)f->data; 262 263 /* Flush remaining bytes. */ 264 if (state->hold_len != 0) 265 la_b64_encode(&state->encoded_buff, state->hold, state->hold_len); 266 archive_string_sprintf(&state->encoded_buff, "====\n"); 267 /* Write the last block */ 268 archive_write_set_bytes_in_last_block(f->archive, 1); 269 return __archive_write_filter(f->next_filter, 270 state->encoded_buff.s, archive_strlen(&state->encoded_buff)); 271} 272 273static int 274archive_filter_b64encode_free(struct archive_write_filter *f) 275{ 276 struct private_b64encode *state = (struct private_b64encode *)f->data; 277 278 archive_string_free(&state->name); 279 archive_string_free(&state->encoded_buff); 280 free(state); 281 return (ARCHIVE_OK); 282} 283 284static int64_t 285atol8(const char *p, size_t char_cnt) 286{ 287 int64_t l; 288 int digit; 289 290 l = 0; 291 while (char_cnt-- > 0) { 292 if (*p >= '0' && *p <= '7') 293 digit = *p - '0'; 294 else 295 break; 296 p++; 297 l <<= 3; 298 l |= digit; 299 } 300 return (l); 301} 302 303