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 la_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 153 if (f->archive->magic == ARCHIVE_WRITE_MAGIC) { 154 /* Buffer size should be a multiple number of the of bytes 155 * per block for performance. */ 156 bpb = archive_write_get_bytes_per_block(f->archive); 157 if (bpb > bs) 158 bs = bpb; 159 else if (bpb != 0) 160 bs -= bs % bpb; 161 } 162 163 state->bs = bs; 164 if (archive_string_ensure(&state->encoded_buff, bs + 512) == NULL) { 165 archive_set_error(f->archive, ENOMEM, 166 "Can't allocate data for b64encode buffer"); 167 return (ARCHIVE_FATAL); 168 } 169 170 archive_string_sprintf(&state->encoded_buff, "begin-base64 %o %s\n", 171 state->mode, state->name.s); 172 173 f->data = state; 174 return (0); 175 } 176 177 static void 178 la_b64_encode(struct archive_string *as, const unsigned char *p, size_t len) 179 { 180 int c; 181 182 for (; len >= 3; p += 3, len -= 3) { 183 c = p[0] >> 2; 184 archive_strappend_char(as, base64[c]); 185 c = ((p[0] & 0x03) << 4) | ((p[1] & 0xf0) >> 4); 186 archive_strappend_char(as, base64[c]); 187 c = ((p[1] & 0x0f) << 2) | ((p[2] & 0xc0) >> 6); 188 archive_strappend_char(as, base64[c]); 189 c = p[2] & 0x3f; 190 archive_strappend_char(as, base64[c]); 191 } 192 if (len > 0) { 193 c = p[0] >> 2; 194 archive_strappend_char(as, base64[c]); 195 c = (p[0] & 0x03) << 4; 196 if (len == 1) { 197 archive_strappend_char(as, base64[c]); 198 archive_strappend_char(as, '='); 199 archive_strappend_char(as, '='); 200 } else { 201 c |= (p[1] & 0xf0) >> 4; 202 archive_strappend_char(as, base64[c]); 203 c = (p[1] & 0x0f) << 2; 204 archive_strappend_char(as, base64[c]); 205 archive_strappend_char(as, '='); 206 } 207 } 208 archive_strappend_char(as, '\n'); 209 } 210 211 /* 212 * Write data to the encoded stream. 213 */ 214 static int 215 archive_filter_b64encode_write(struct archive_write_filter *f, const void *buff, 216 size_t length) 217 { 218 struct private_b64encode *state = (struct private_b64encode *)f->data; 219 const unsigned char *p = buff; 220 int ret = ARCHIVE_OK; 221 222 if (length == 0) 223 return (ret); 224 225 if (state->hold_len) { 226 while (state->hold_len < LBYTES && length > 0) { 227 state->hold[state->hold_len++] = *p++; 228 length--; 229 } 230 if (state->hold_len < LBYTES) 231 return (ret); 232 la_b64_encode(&state->encoded_buff, state->hold, LBYTES); 233 state->hold_len = 0; 234 } 235 236 for (; length >= LBYTES; length -= LBYTES, p += LBYTES) 237 la_b64_encode(&state->encoded_buff, p, LBYTES); 238 239 /* Save remaining bytes. */ 240 if (length > 0) { 241 memcpy(state->hold, p, length); 242 state->hold_len = length; 243 } 244 while (archive_strlen(&state->encoded_buff) >= state->bs) { 245 ret = __archive_write_filter(f->next_filter, 246 state->encoded_buff.s, state->bs); 247 memmove(state->encoded_buff.s, 248 state->encoded_buff.s + state->bs, 249 state->encoded_buff.length - state->bs); 250 state->encoded_buff.length -= state->bs; 251 } 252 253 return (ret); 254 } 255 256 257 /* 258 * Finish the compression... 259 */ 260 static int 261 archive_filter_b64encode_close(struct archive_write_filter *f) 262 { 263 struct private_b64encode *state = (struct private_b64encode *)f->data; 264 265 /* Flush remaining bytes. */ 266 if (state->hold_len != 0) 267 la_b64_encode(&state->encoded_buff, state->hold, state->hold_len); 268 archive_string_sprintf(&state->encoded_buff, "====\n"); 269 /* Write the last block */ 270 archive_write_set_bytes_in_last_block(f->archive, 1); 271 return __archive_write_filter(f->next_filter, 272 state->encoded_buff.s, archive_strlen(&state->encoded_buff)); 273 } 274 275 static int 276 archive_filter_b64encode_free(struct archive_write_filter *f) 277 { 278 struct private_b64encode *state = (struct private_b64encode *)f->data; 279 280 archive_string_free(&state->name); 281 archive_string_free(&state->encoded_buff); 282 free(state); 283 return (ARCHIVE_OK); 284 } 285 286 static int64_t 287 atol8(const char *p, size_t char_cnt) 288 { 289 int64_t l; 290 int digit; 291 292 l = 0; 293 while (char_cnt-- > 0) { 294 if (*p >= '0' && *p <= '7') 295 digit = *p - '0'; 296 else 297 break; 298 p++; 299 l <<= 3; 300 l |= digit; 301 } 302 return (l); 303 } 304 305