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