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