1228753Smm/*- 2231200Smm * Copyright (c) 2009-2011 Michihiro NAKAJIMA 3228753Smm * All rights reserved. 4228753Smm * 5228753Smm * Redistribution and use in source and binary forms, with or without 6228753Smm * modification, are permitted provided that the following conditions 7228753Smm * are met: 8228753Smm * 1. Redistributions of source code must retain the above copyright 9228753Smm * notice, this list of conditions and the following disclaimer. 10228753Smm * 2. Redistributions in binary form must reproduce the above copyright 11228753Smm * notice, this list of conditions and the following disclaimer in the 12228753Smm * documentation and/or other materials provided with the distribution. 13228753Smm * 14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24228753Smm */ 25228753Smm 26228753Smm#include "archive_platform.h" 27231200Smm__FBSDID("$FreeBSD$"); 28228753Smm 29228753Smm#ifdef HAVE_ERRNO_H 30228753Smm#include <errno.h> 31228753Smm#endif 32228753Smm#ifdef HAVE_STDLIB_H 33228753Smm#include <stdlib.h> 34228753Smm#endif 35228753Smm#ifdef HAVE_STRING_H 36228753Smm#include <string.h> 37228753Smm#endif 38228753Smm 39228753Smm#include "archive.h" 40228753Smm#include "archive_private.h" 41228753Smm#include "archive_read_private.h" 42228753Smm 43231200Smm/* Maximum lookahead during bid phase */ 44231200Smm#define UUENCODE_BID_MAX_READ 128*1024 /* in bytes */ 45231200Smm 46228753Smmstruct uudecode { 47228753Smm int64_t total; 48228753Smm unsigned char *in_buff; 49228753Smm#define IN_BUFF_SIZE (1024) 50228753Smm int in_cnt; 51228753Smm size_t in_allocated; 52228753Smm unsigned char *out_buff; 53228753Smm#define OUT_BUFF_SIZE (64 * 1024) 54228753Smm int state; 55228753Smm#define ST_FIND_HEAD 0 56228753Smm#define ST_READ_UU 1 57228753Smm#define ST_UUEND 2 58228753Smm#define ST_READ_BASE64 3 59248616Smm#define ST_IGNORE 4 60228753Smm}; 61228753Smm 62228753Smmstatic int uudecode_bidder_bid(struct archive_read_filter_bidder *, 63228753Smm struct archive_read_filter *filter); 64228753Smmstatic int uudecode_bidder_init(struct archive_read_filter *); 65228753Smm 66228753Smmstatic ssize_t uudecode_filter_read(struct archive_read_filter *, 67228753Smm const void **); 68228753Smmstatic int uudecode_filter_close(struct archive_read_filter *); 69228753Smm 70231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000 71231200Smm/* Deprecated; remove in libarchive 4.0 */ 72228753Smmint 73231200Smmarchive_read_support_compression_uu(struct archive *a) 74228753Smm{ 75231200Smm return archive_read_support_filter_uu(a); 76231200Smm} 77231200Smm#endif 78231200Smm 79231200Smmint 80231200Smmarchive_read_support_filter_uu(struct archive *_a) 81231200Smm{ 82228753Smm struct archive_read *a = (struct archive_read *)_a; 83228753Smm struct archive_read_filter_bidder *bidder; 84228753Smm 85231200Smm archive_check_magic(_a, ARCHIVE_READ_MAGIC, 86231200Smm ARCHIVE_STATE_NEW, "archive_read_support_filter_uu"); 87231200Smm 88231200Smm if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK) 89228753Smm return (ARCHIVE_FATAL); 90228753Smm 91228753Smm bidder->data = NULL; 92248616Smm bidder->name = "uu"; 93228753Smm bidder->bid = uudecode_bidder_bid; 94228753Smm bidder->init = uudecode_bidder_init; 95228753Smm bidder->options = NULL; 96228753Smm bidder->free = NULL; 97228753Smm return (ARCHIVE_OK); 98228753Smm} 99228753Smm 100228753Smmstatic const unsigned char ascii[256] = { 101228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '\n', 0, 0, '\r', 0, 0, /* 00 - 0F */ 102228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 103228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ 104228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ 105228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ 106228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ 107228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ 108228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, /* 70 - 7F */ 109228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 110228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 111228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 112228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 113228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 114228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 115228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 116228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 117228753Smm}; 118228753Smm 119228753Smmstatic const unsigned char uuchar[256] = { 120228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 121228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 122228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 20 - 2F */ 123228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 30 - 3F */ 124228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ 125228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 50 - 5F */ 126228753Smm 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 6F */ 127228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 7F */ 128228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 129228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 130228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 131228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 132228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 133228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 134228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 135228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 136228753Smm}; 137228753Smm 138228753Smmstatic const unsigned char base64[256] = { 139228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 140228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 141228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, /* 20 - 2F */ 142228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, /* 30 - 3F */ 143228753Smm 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 40 - 4F */ 144228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 50 - 5F */ 145228753Smm 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 60 - 6F */ 146228753Smm 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 70 - 7F */ 147228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 80 - 8F */ 148228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 90 - 9F */ 149228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A0 - AF */ 150228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* B0 - BF */ 151228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* C0 - CF */ 152228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* D0 - DF */ 153228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */ 154228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */ 155228753Smm}; 156228753Smm 157228753Smmstatic const int base64num[128] = { 158228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 159228753Smm 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 0F */ 160228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 161228753Smm 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 1F */ 162228753Smm 0, 0, 0, 0, 0, 0, 0, 0, 163228753Smm 0, 0, 0, 62, 0, 0, 0, 63, /* 20 - 2F */ 164228753Smm 52, 53, 54, 55, 56, 57, 58, 59, 165228753Smm 60, 61, 0, 0, 0, 0, 0, 0, /* 30 - 3F */ 166228753Smm 0, 0, 1, 2, 3, 4, 5, 6, 167228753Smm 7, 8, 9, 10, 11, 12, 13, 14, /* 40 - 4F */ 168228753Smm 15, 16, 17, 18, 19, 20, 21, 22, 169228753Smm 23, 24, 25, 0, 0, 0, 0, 0, /* 50 - 5F */ 170228753Smm 0, 26, 27, 28, 29, 30, 31, 32, 171228753Smm 33, 34, 35, 36, 37, 38, 39, 40, /* 60 - 6F */ 172228753Smm 41, 42, 43, 44, 45, 46, 47, 48, 173228753Smm 49, 50, 51, 0, 0, 0, 0, 0, /* 70 - 7F */ 174228753Smm}; 175228753Smm 176228753Smmstatic ssize_t 177228753Smmget_line(const unsigned char *b, ssize_t avail, ssize_t *nlsize) 178228753Smm{ 179228753Smm ssize_t len; 180228753Smm 181228753Smm len = 0; 182228753Smm while (len < avail) { 183228753Smm switch (ascii[*b]) { 184228753Smm case 0: /* Non-ascii character or control character. */ 185228753Smm if (nlsize != NULL) 186228753Smm *nlsize = 0; 187228753Smm return (-1); 188228753Smm case '\r': 189228753Smm if (avail-len > 1 && b[1] == '\n') { 190228753Smm if (nlsize != NULL) 191228753Smm *nlsize = 2; 192228753Smm return (len+2); 193228753Smm } 194228753Smm /* FALL THROUGH */ 195228753Smm case '\n': 196228753Smm if (nlsize != NULL) 197228753Smm *nlsize = 1; 198228753Smm return (len+1); 199228753Smm case 1: 200228753Smm b++; 201228753Smm len++; 202228753Smm break; 203228753Smm } 204228753Smm } 205228753Smm if (nlsize != NULL) 206228753Smm *nlsize = 0; 207228753Smm return (avail); 208228753Smm} 209228753Smm 210228753Smmstatic ssize_t 211228753Smmbid_get_line(struct archive_read_filter *filter, 212231200Smm const unsigned char **b, ssize_t *avail, ssize_t *ravail, 213231200Smm ssize_t *nl, size_t* nbytes_read) 214228753Smm{ 215228753Smm ssize_t len; 216228753Smm int quit; 217228753Smm 218228753Smm quit = 0; 219228753Smm if (*avail == 0) { 220228753Smm *nl = 0; 221228753Smm len = 0; 222228753Smm } else 223228753Smm len = get_line(*b, *avail, nl); 224231200Smm 225228753Smm /* 226228753Smm * Read bytes more while it does not reach the end of line. 227228753Smm */ 228231200Smm while (*nl == 0 && len == *avail && !quit && 229231200Smm *nbytes_read < UUENCODE_BID_MAX_READ) { 230228753Smm ssize_t diff = *ravail - *avail; 231231200Smm size_t nbytes_req = (*ravail+1023) & ~1023U; 232231200Smm ssize_t tested; 233228753Smm 234231200Smm /* Increase reading bytes if it is not enough to at least 235231200Smm * new two lines. */ 236231200Smm if (nbytes_req < (size_t)*ravail + 160) 237231200Smm nbytes_req <<= 1; 238231200Smm 239231200Smm *b = __archive_read_filter_ahead(filter, nbytes_req, avail); 240228753Smm if (*b == NULL) { 241228753Smm if (*ravail >= *avail) 242228753Smm return (0); 243231200Smm /* Reading bytes reaches the end of a stream. */ 244228753Smm *b = __archive_read_filter_ahead(filter, *avail, avail); 245228753Smm quit = 1; 246228753Smm } 247231200Smm *nbytes_read = *avail; 248228753Smm *ravail = *avail; 249228753Smm *b += diff; 250228753Smm *avail -= diff; 251231200Smm tested = len;/* Skip some bytes we already determinated. */ 252231200Smm len = get_line(*b + tested, *avail - tested, nl); 253231200Smm if (len >= 0) 254231200Smm len += tested; 255228753Smm } 256228753Smm return (len); 257228753Smm} 258228753Smm 259228753Smm#define UUDECODE(c) (((c) - 0x20) & 0x3f) 260228753Smm 261228753Smmstatic int 262228753Smmuudecode_bidder_bid(struct archive_read_filter_bidder *self, 263228753Smm struct archive_read_filter *filter) 264228753Smm{ 265228753Smm const unsigned char *b; 266228753Smm ssize_t avail, ravail; 267228753Smm ssize_t len, nl; 268228753Smm int l; 269228753Smm int firstline; 270231200Smm size_t nbytes_read; 271228753Smm 272228753Smm (void)self; /* UNUSED */ 273228753Smm 274228753Smm b = __archive_read_filter_ahead(filter, 1, &avail); 275228753Smm if (b == NULL) 276228753Smm return (0); 277228753Smm 278228753Smm firstline = 20; 279228753Smm ravail = avail; 280231200Smm nbytes_read = avail; 281228753Smm for (;;) { 282231200Smm len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read); 283228753Smm if (len < 0 || nl == 0) 284231200Smm return (0); /* No match found. */ 285231200Smm if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0) 286228753Smm l = 6; 287231200Smm else if (len -nl >= 18 && memcmp(b, "begin-base64 ", 13) == 0) 288228753Smm l = 13; 289228753Smm else 290228753Smm l = 0; 291228753Smm 292228753Smm if (l > 0 && (b[l] < '0' || b[l] > '7' || 293228753Smm b[l+1] < '0' || b[l+1] > '7' || 294228753Smm b[l+2] < '0' || b[l+2] > '7' || b[l+3] != ' ')) 295228753Smm l = 0; 296228753Smm 297228753Smm b += len; 298228753Smm avail -= len; 299228753Smm if (l) 300228753Smm break; 301228753Smm firstline = 0; 302231200Smm 303231200Smm /* Do not read more than UUENCODE_BID_MAX_READ bytes */ 304231200Smm if (nbytes_read >= UUENCODE_BID_MAX_READ) 305231200Smm return (0); 306228753Smm } 307228753Smm if (!avail) 308228753Smm return (0); 309231200Smm len = bid_get_line(filter, &b, &avail, &ravail, &nl, &nbytes_read); 310228753Smm if (len < 0 || nl == 0) 311228753Smm return (0);/* There are non-ascii characters. */ 312228753Smm avail -= len; 313228753Smm 314228753Smm if (l == 6) { 315228753Smm if (!uuchar[*b]) 316228753Smm return (0); 317228753Smm /* Get a length of decoded bytes. */ 318228753Smm l = UUDECODE(*b++); len--; 319228753Smm if (l > 45) 320228753Smm /* Normally, maximum length is 45(character 'M'). */ 321228753Smm return (0); 322228753Smm while (l && len-nl > 0) { 323228753Smm if (l > 0) { 324228753Smm if (!uuchar[*b++]) 325228753Smm return (0); 326228753Smm if (!uuchar[*b++]) 327228753Smm return (0); 328228753Smm len -= 2; 329228753Smm --l; 330228753Smm } 331228753Smm if (l > 0) { 332228753Smm if (!uuchar[*b++]) 333228753Smm return (0); 334228753Smm --len; 335228753Smm --l; 336228753Smm } 337228753Smm if (l > 0) { 338228753Smm if (!uuchar[*b++]) 339228753Smm return (0); 340228753Smm --len; 341228753Smm --l; 342228753Smm } 343228753Smm } 344228753Smm if (len-nl < 0) 345228753Smm return (0); 346228753Smm if (len-nl == 1 && 347228753Smm (uuchar[*b] || /* Check sum. */ 348228753Smm (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */ 349228753Smm ++b; 350228753Smm --len; 351228753Smm } 352228753Smm b += nl; 353228753Smm if (avail && uuchar[*b]) 354228753Smm return (firstline+30); 355228753Smm } 356228753Smm if (l == 13) { 357228753Smm while (len-nl > 0) { 358228753Smm if (!base64[*b++]) 359228753Smm return (0); 360228753Smm --len; 361228753Smm } 362228753Smm b += nl; 363228753Smm 364228753Smm if (avail >= 5 && memcmp(b, "====\n", 5) == 0) 365228753Smm return (firstline+40); 366228753Smm if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0) 367228753Smm return (firstline+40); 368228753Smm if (avail > 0 && base64[*b]) 369228753Smm return (firstline+30); 370228753Smm } 371228753Smm 372228753Smm return (0); 373228753Smm} 374228753Smm 375228753Smmstatic int 376228753Smmuudecode_bidder_init(struct archive_read_filter *self) 377228753Smm{ 378228753Smm struct uudecode *uudecode; 379228753Smm void *out_buff; 380228753Smm void *in_buff; 381228753Smm 382248616Smm self->code = ARCHIVE_FILTER_UU; 383228753Smm self->name = "uu"; 384228753Smm self->read = uudecode_filter_read; 385228753Smm self->skip = NULL; /* not supported */ 386228753Smm self->close = uudecode_filter_close; 387228753Smm 388228753Smm uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1); 389228753Smm out_buff = malloc(OUT_BUFF_SIZE); 390228753Smm in_buff = malloc(IN_BUFF_SIZE); 391228753Smm if (uudecode == NULL || out_buff == NULL || in_buff == NULL) { 392228753Smm archive_set_error(&self->archive->archive, ENOMEM, 393228753Smm "Can't allocate data for uudecode"); 394228753Smm free(uudecode); 395228753Smm free(out_buff); 396228753Smm free(in_buff); 397228753Smm return (ARCHIVE_FATAL); 398228753Smm } 399228753Smm 400228753Smm self->data = uudecode; 401228753Smm uudecode->in_buff = in_buff; 402228753Smm uudecode->in_cnt = 0; 403228753Smm uudecode->in_allocated = IN_BUFF_SIZE; 404228753Smm uudecode->out_buff = out_buff; 405228753Smm uudecode->state = ST_FIND_HEAD; 406228753Smm 407228753Smm return (ARCHIVE_OK); 408228753Smm} 409228753Smm 410228753Smmstatic int 411228753Smmensure_in_buff_size(struct archive_read_filter *self, 412228753Smm struct uudecode *uudecode, size_t size) 413228753Smm{ 414228753Smm 415228753Smm if (size > uudecode->in_allocated) { 416228753Smm unsigned char *ptr; 417228753Smm size_t newsize; 418228753Smm 419228753Smm /* 420228753Smm * Calculate a new buffer size for in_buff. 421228753Smm * Increase its value until it has enough size we need. 422228753Smm */ 423228753Smm newsize = uudecode->in_allocated; 424228753Smm do { 425228753Smm if (newsize < IN_BUFF_SIZE*32) 426228753Smm newsize <<= 1; 427228753Smm else 428228753Smm newsize += IN_BUFF_SIZE; 429228753Smm } while (size > newsize); 430231200Smm /* Allocate the new buffer. */ 431228753Smm ptr = malloc(newsize); 432231200Smm if (ptr == NULL) { 433228753Smm free(ptr); 434228753Smm archive_set_error(&self->archive->archive, 435228753Smm ENOMEM, 436228753Smm "Can't allocate data for uudecode"); 437228753Smm return (ARCHIVE_FATAL); 438228753Smm } 439231200Smm /* Move the remaining data in in_buff into the new buffer. */ 440228753Smm if (uudecode->in_cnt) 441231200Smm memmove(ptr, uudecode->in_buff, uudecode->in_cnt); 442231200Smm /* Replace in_buff with the new buffer. */ 443228753Smm free(uudecode->in_buff); 444228753Smm uudecode->in_buff = ptr; 445228753Smm uudecode->in_allocated = newsize; 446228753Smm } 447228753Smm return (ARCHIVE_OK); 448228753Smm} 449228753Smm 450228753Smmstatic ssize_t 451228753Smmuudecode_filter_read(struct archive_read_filter *self, const void **buff) 452228753Smm{ 453228753Smm struct uudecode *uudecode; 454228753Smm const unsigned char *b, *d; 455228753Smm unsigned char *out; 456228753Smm ssize_t avail_in, ravail; 457228753Smm ssize_t used; 458228753Smm ssize_t total; 459228753Smm ssize_t len, llen, nl; 460228753Smm 461228753Smm uudecode = (struct uudecode *)self->data; 462228753Smm 463228753Smmread_more: 464228753Smm d = __archive_read_filter_ahead(self->upstream, 1, &avail_in); 465228753Smm if (d == NULL && avail_in < 0) 466228753Smm return (ARCHIVE_FATAL); 467228753Smm /* Quiet a code analyzer; make sure avail_in must be zero 468228753Smm * when d is NULL. */ 469228753Smm if (d == NULL) 470228753Smm avail_in = 0; 471228753Smm used = 0; 472228753Smm total = 0; 473228753Smm out = uudecode->out_buff; 474228753Smm ravail = avail_in; 475248616Smm if (uudecode->state == ST_IGNORE) { 476248616Smm used = avail_in; 477248616Smm goto finish; 478248616Smm } 479228753Smm if (uudecode->in_cnt) { 480228753Smm /* 481228753Smm * If there is remaining data which is saved by 482228753Smm * previous calling, use it first. 483228753Smm */ 484228753Smm if (ensure_in_buff_size(self, uudecode, 485228753Smm avail_in + uudecode->in_cnt) != ARCHIVE_OK) 486228753Smm return (ARCHIVE_FATAL); 487228753Smm memcpy(uudecode->in_buff + uudecode->in_cnt, 488228753Smm d, avail_in); 489228753Smm d = uudecode->in_buff; 490228753Smm avail_in += uudecode->in_cnt; 491228753Smm uudecode->in_cnt = 0; 492228753Smm } 493228753Smm for (;used < avail_in; d += llen, used += llen) { 494248616Smm int64_t l, body; 495228753Smm 496228753Smm b = d; 497228753Smm len = get_line(b, avail_in - used, &nl); 498228753Smm if (len < 0) { 499228753Smm /* Non-ascii character is found. */ 500248616Smm if (uudecode->state == ST_FIND_HEAD && 501248616Smm (uudecode->total > 0 || total > 0)) { 502248616Smm uudecode->state = ST_IGNORE; 503248616Smm used = avail_in; 504248616Smm goto finish; 505248616Smm } 506228753Smm archive_set_error(&self->archive->archive, 507228753Smm ARCHIVE_ERRNO_MISC, 508228753Smm "Insufficient compressed data"); 509228753Smm return (ARCHIVE_FATAL); 510228753Smm } 511228753Smm llen = len; 512228753Smm if (nl == 0) { 513228753Smm /* 514228753Smm * Save remaining data which does not contain 515228753Smm * NL('\n','\r'). 516228753Smm */ 517228753Smm if (ensure_in_buff_size(self, uudecode, len) 518228753Smm != ARCHIVE_OK) 519228753Smm return (ARCHIVE_FATAL); 520228753Smm if (uudecode->in_buff != b) 521228753Smm memmove(uudecode->in_buff, b, len); 522248616Smm uudecode->in_cnt = (int)len; 523228753Smm if (total == 0) { 524228753Smm /* Do not return 0; it means end-of-file. 525228753Smm * We should try to read bytes more. */ 526228753Smm __archive_read_filter_consume( 527228753Smm self->upstream, ravail); 528228753Smm goto read_more; 529228753Smm } 530228753Smm break; 531228753Smm } 532228753Smm switch (uudecode->state) { 533228753Smm default: 534228753Smm case ST_FIND_HEAD: 535231200Smm /* Do not read more than UUENCODE_BID_MAX_READ bytes */ 536231200Smm if (total + len >= UUENCODE_BID_MAX_READ) { 537231200Smm archive_set_error(&self->archive->archive, 538231200Smm ARCHIVE_ERRNO_FILE_FORMAT, 539231200Smm "Invalid format data"); 540231200Smm return (ARCHIVE_FATAL); 541231200Smm } 542228753Smm if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0) 543228753Smm l = 6; 544228753Smm else if (len - nl >= 18 && 545228753Smm memcmp(b, "begin-base64 ", 13) == 0) 546228753Smm l = 13; 547228753Smm else 548228753Smm l = 0; 549228753Smm if (l != 0 && b[l] >= '0' && b[l] <= '7' && 550228753Smm b[l+1] >= '0' && b[l+1] <= '7' && 551228753Smm b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') { 552228753Smm if (l == 6) 553228753Smm uudecode->state = ST_READ_UU; 554228753Smm else 555228753Smm uudecode->state = ST_READ_BASE64; 556228753Smm } 557228753Smm break; 558228753Smm case ST_READ_UU: 559231200Smm if (total + len * 2 > OUT_BUFF_SIZE) 560248616Smm goto finish; 561228753Smm body = len - nl; 562228753Smm if (!uuchar[*b] || body <= 0) { 563228753Smm archive_set_error(&self->archive->archive, 564228753Smm ARCHIVE_ERRNO_MISC, 565228753Smm "Insufficient compressed data"); 566228753Smm return (ARCHIVE_FATAL); 567228753Smm } 568228753Smm /* Get length of undecoded bytes of curent line. */ 569228753Smm l = UUDECODE(*b++); 570228753Smm body--; 571228753Smm if (l > body) { 572228753Smm archive_set_error(&self->archive->archive, 573228753Smm ARCHIVE_ERRNO_MISC, 574228753Smm "Insufficient compressed data"); 575228753Smm return (ARCHIVE_FATAL); 576228753Smm } 577228753Smm if (l == 0) { 578228753Smm uudecode->state = ST_UUEND; 579228753Smm break; 580228753Smm } 581228753Smm while (l > 0) { 582228753Smm int n = 0; 583228753Smm 584228753Smm if (l > 0) { 585228753Smm if (!uuchar[b[0]] || !uuchar[b[1]]) 586228753Smm break; 587228753Smm n = UUDECODE(*b++) << 18; 588228753Smm n |= UUDECODE(*b++) << 12; 589228753Smm *out++ = n >> 16; total++; 590228753Smm --l; 591228753Smm } 592228753Smm if (l > 0) { 593228753Smm if (!uuchar[b[0]]) 594228753Smm break; 595228753Smm n |= UUDECODE(*b++) << 6; 596228753Smm *out++ = (n >> 8) & 0xFF; total++; 597228753Smm --l; 598228753Smm } 599228753Smm if (l > 0) { 600228753Smm if (!uuchar[b[0]]) 601228753Smm break; 602228753Smm n |= UUDECODE(*b++); 603228753Smm *out++ = n & 0xFF; total++; 604228753Smm --l; 605228753Smm } 606228753Smm } 607228753Smm if (l) { 608228753Smm archive_set_error(&self->archive->archive, 609228753Smm ARCHIVE_ERRNO_MISC, 610228753Smm "Insufficient compressed data"); 611228753Smm return (ARCHIVE_FATAL); 612228753Smm } 613228753Smm break; 614228753Smm case ST_UUEND: 615228753Smm if (len - nl == 3 && memcmp(b, "end ", 3) == 0) 616228753Smm uudecode->state = ST_FIND_HEAD; 617228753Smm else { 618228753Smm archive_set_error(&self->archive->archive, 619228753Smm ARCHIVE_ERRNO_MISC, 620228753Smm "Insufficient compressed data"); 621228753Smm return (ARCHIVE_FATAL); 622228753Smm } 623228753Smm break; 624228753Smm case ST_READ_BASE64: 625231200Smm if (total + len * 2 > OUT_BUFF_SIZE) 626248616Smm goto finish; 627228753Smm l = len - nl; 628228753Smm if (l >= 3 && b[0] == '=' && b[1] == '=' && 629228753Smm b[2] == '=') { 630228753Smm uudecode->state = ST_FIND_HEAD; 631228753Smm break; 632228753Smm } 633228753Smm while (l > 0) { 634228753Smm int n = 0; 635228753Smm 636228753Smm if (l > 0) { 637228753Smm if (!base64[b[0]] || !base64[b[1]]) 638228753Smm break; 639228753Smm n = base64num[*b++] << 18; 640228753Smm n |= base64num[*b++] << 12; 641228753Smm *out++ = n >> 16; total++; 642228753Smm l -= 2; 643228753Smm } 644228753Smm if (l > 0) { 645228753Smm if (*b == '=') 646228753Smm break; 647228753Smm if (!base64[*b]) 648228753Smm break; 649228753Smm n |= base64num[*b++] << 6; 650228753Smm *out++ = (n >> 8) & 0xFF; total++; 651228753Smm --l; 652228753Smm } 653228753Smm if (l > 0) { 654228753Smm if (*b == '=') 655228753Smm break; 656228753Smm if (!base64[*b]) 657228753Smm break; 658228753Smm n |= base64num[*b++]; 659228753Smm *out++ = n & 0xFF; total++; 660228753Smm --l; 661228753Smm } 662228753Smm } 663228753Smm if (l && *b != '=') { 664228753Smm archive_set_error(&self->archive->archive, 665228753Smm ARCHIVE_ERRNO_MISC, 666228753Smm "Insufficient compressed data"); 667228753Smm return (ARCHIVE_FATAL); 668228753Smm } 669228753Smm break; 670228753Smm } 671228753Smm } 672248616Smmfinish: 673248616Smm if (ravail < avail_in) 674248616Smm used -= avail_in - ravail; 675248616Smm __archive_read_filter_consume(self->upstream, used); 676228753Smm 677228753Smm *buff = uudecode->out_buff; 678228753Smm uudecode->total += total; 679228753Smm return (total); 680228753Smm} 681228753Smm 682228753Smmstatic int 683228753Smmuudecode_filter_close(struct archive_read_filter *self) 684228753Smm{ 685228753Smm struct uudecode *uudecode; 686228753Smm 687228753Smm uudecode = (struct uudecode *)self->data; 688228753Smm free(uudecode->in_buff); 689228753Smm free(uudecode->out_buff); 690228753Smm free(uudecode); 691228753Smm 692228753Smm return (ARCHIVE_OK); 693228753Smm} 694228753Smm 695