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