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) { 315311042Smm /* "begin " */ 316228753Smm if (!uuchar[*b]) 317228753Smm return (0); 318228753Smm /* Get a length of decoded bytes. */ 319228753Smm l = UUDECODE(*b++); len--; 320228753Smm if (l > 45) 321228753Smm /* Normally, maximum length is 45(character 'M'). */ 322228753Smm return (0); 323311042Smm if (l > len - nl) 324311042Smm return (0); /* Line too short. */ 325311042Smm while (l) { 326311042Smm if (!uuchar[*b++]) 327311042Smm return (0); 328311042Smm --len; 329311042Smm --l; 330228753Smm } 331228753Smm if (len-nl == 1 && 332228753Smm (uuchar[*b] || /* Check sum. */ 333228753Smm (*b >= 'a' && *b <= 'z'))) {/* Padding data(MINIX). */ 334228753Smm ++b; 335228753Smm --len; 336228753Smm } 337228753Smm b += nl; 338228753Smm if (avail && uuchar[*b]) 339228753Smm return (firstline+30); 340311042Smm } else if (l == 13) { 341311042Smm /* "begin-base64 " */ 342228753Smm while (len-nl > 0) { 343228753Smm if (!base64[*b++]) 344228753Smm return (0); 345228753Smm --len; 346228753Smm } 347228753Smm b += nl; 348228753Smm 349228753Smm if (avail >= 5 && memcmp(b, "====\n", 5) == 0) 350228753Smm return (firstline+40); 351228753Smm if (avail >= 6 && memcmp(b, "====\r\n", 6) == 0) 352228753Smm return (firstline+40); 353228753Smm if (avail > 0 && base64[*b]) 354228753Smm return (firstline+30); 355228753Smm } 356228753Smm 357228753Smm return (0); 358228753Smm} 359228753Smm 360228753Smmstatic int 361228753Smmuudecode_bidder_init(struct archive_read_filter *self) 362228753Smm{ 363228753Smm struct uudecode *uudecode; 364228753Smm void *out_buff; 365228753Smm void *in_buff; 366228753Smm 367248616Smm self->code = ARCHIVE_FILTER_UU; 368228753Smm self->name = "uu"; 369228753Smm self->read = uudecode_filter_read; 370228753Smm self->skip = NULL; /* not supported */ 371228753Smm self->close = uudecode_filter_close; 372228753Smm 373228753Smm uudecode = (struct uudecode *)calloc(sizeof(*uudecode), 1); 374228753Smm out_buff = malloc(OUT_BUFF_SIZE); 375228753Smm in_buff = malloc(IN_BUFF_SIZE); 376228753Smm if (uudecode == NULL || out_buff == NULL || in_buff == NULL) { 377228753Smm archive_set_error(&self->archive->archive, ENOMEM, 378228753Smm "Can't allocate data for uudecode"); 379228753Smm free(uudecode); 380228753Smm free(out_buff); 381228753Smm free(in_buff); 382228753Smm return (ARCHIVE_FATAL); 383228753Smm } 384228753Smm 385228753Smm self->data = uudecode; 386228753Smm uudecode->in_buff = in_buff; 387228753Smm uudecode->in_cnt = 0; 388228753Smm uudecode->in_allocated = IN_BUFF_SIZE; 389228753Smm uudecode->out_buff = out_buff; 390228753Smm uudecode->state = ST_FIND_HEAD; 391228753Smm 392228753Smm return (ARCHIVE_OK); 393228753Smm} 394228753Smm 395228753Smmstatic int 396228753Smmensure_in_buff_size(struct archive_read_filter *self, 397228753Smm struct uudecode *uudecode, size_t size) 398228753Smm{ 399228753Smm 400228753Smm if (size > uudecode->in_allocated) { 401228753Smm unsigned char *ptr; 402228753Smm size_t newsize; 403228753Smm 404228753Smm /* 405228753Smm * Calculate a new buffer size for in_buff. 406228753Smm * Increase its value until it has enough size we need. 407228753Smm */ 408228753Smm newsize = uudecode->in_allocated; 409228753Smm do { 410228753Smm if (newsize < IN_BUFF_SIZE*32) 411228753Smm newsize <<= 1; 412228753Smm else 413228753Smm newsize += IN_BUFF_SIZE; 414228753Smm } while (size > newsize); 415231200Smm /* Allocate the new buffer. */ 416228753Smm ptr = malloc(newsize); 417231200Smm if (ptr == NULL) { 418228753Smm free(ptr); 419228753Smm archive_set_error(&self->archive->archive, 420228753Smm ENOMEM, 421228753Smm "Can't allocate data for uudecode"); 422228753Smm return (ARCHIVE_FATAL); 423228753Smm } 424231200Smm /* Move the remaining data in in_buff into the new buffer. */ 425228753Smm if (uudecode->in_cnt) 426231200Smm memmove(ptr, uudecode->in_buff, uudecode->in_cnt); 427231200Smm /* Replace in_buff with the new buffer. */ 428228753Smm free(uudecode->in_buff); 429228753Smm uudecode->in_buff = ptr; 430228753Smm uudecode->in_allocated = newsize; 431228753Smm } 432228753Smm return (ARCHIVE_OK); 433228753Smm} 434228753Smm 435228753Smmstatic ssize_t 436228753Smmuudecode_filter_read(struct archive_read_filter *self, const void **buff) 437228753Smm{ 438228753Smm struct uudecode *uudecode; 439228753Smm const unsigned char *b, *d; 440228753Smm unsigned char *out; 441228753Smm ssize_t avail_in, ravail; 442228753Smm ssize_t used; 443228753Smm ssize_t total; 444228753Smm ssize_t len, llen, nl; 445228753Smm 446228753Smm uudecode = (struct uudecode *)self->data; 447228753Smm 448228753Smmread_more: 449228753Smm d = __archive_read_filter_ahead(self->upstream, 1, &avail_in); 450228753Smm if (d == NULL && avail_in < 0) 451228753Smm return (ARCHIVE_FATAL); 452228753Smm /* Quiet a code analyzer; make sure avail_in must be zero 453228753Smm * when d is NULL. */ 454228753Smm if (d == NULL) 455228753Smm avail_in = 0; 456228753Smm used = 0; 457228753Smm total = 0; 458228753Smm out = uudecode->out_buff; 459228753Smm ravail = avail_in; 460248616Smm if (uudecode->state == ST_IGNORE) { 461248616Smm used = avail_in; 462248616Smm goto finish; 463248616Smm } 464228753Smm if (uudecode->in_cnt) { 465228753Smm /* 466228753Smm * If there is remaining data which is saved by 467228753Smm * previous calling, use it first. 468228753Smm */ 469228753Smm if (ensure_in_buff_size(self, uudecode, 470228753Smm avail_in + uudecode->in_cnt) != ARCHIVE_OK) 471228753Smm return (ARCHIVE_FATAL); 472228753Smm memcpy(uudecode->in_buff + uudecode->in_cnt, 473228753Smm d, avail_in); 474228753Smm d = uudecode->in_buff; 475228753Smm avail_in += uudecode->in_cnt; 476228753Smm uudecode->in_cnt = 0; 477228753Smm } 478228753Smm for (;used < avail_in; d += llen, used += llen) { 479248616Smm int64_t l, body; 480228753Smm 481228753Smm b = d; 482228753Smm len = get_line(b, avail_in - used, &nl); 483228753Smm if (len < 0) { 484228753Smm /* Non-ascii character is found. */ 485248616Smm if (uudecode->state == ST_FIND_HEAD && 486248616Smm (uudecode->total > 0 || total > 0)) { 487248616Smm uudecode->state = ST_IGNORE; 488248616Smm used = avail_in; 489248616Smm goto finish; 490248616Smm } 491228753Smm archive_set_error(&self->archive->archive, 492228753Smm ARCHIVE_ERRNO_MISC, 493228753Smm "Insufficient compressed data"); 494228753Smm return (ARCHIVE_FATAL); 495228753Smm } 496228753Smm llen = len; 497302001Smm if ((nl == 0) && (uudecode->state != ST_UUEND)) { 498311042Smm if (total == 0 && ravail <= 0) { 499311042Smm /* There is nothing more to read, fail */ 500311042Smm archive_set_error(&self->archive->archive, 501311042Smm ARCHIVE_ERRNO_FILE_FORMAT, 502311042Smm "Missing format data"); 503311042Smm return (ARCHIVE_FATAL); 504311042Smm } 505228753Smm /* 506228753Smm * Save remaining data which does not contain 507228753Smm * NL('\n','\r'). 508228753Smm */ 509228753Smm if (ensure_in_buff_size(self, uudecode, len) 510228753Smm != ARCHIVE_OK) 511228753Smm return (ARCHIVE_FATAL); 512228753Smm if (uudecode->in_buff != b) 513228753Smm memmove(uudecode->in_buff, b, len); 514248616Smm uudecode->in_cnt = (int)len; 515228753Smm if (total == 0) { 516228753Smm /* Do not return 0; it means end-of-file. 517228753Smm * We should try to read bytes more. */ 518228753Smm __archive_read_filter_consume( 519228753Smm self->upstream, ravail); 520228753Smm goto read_more; 521228753Smm } 522302001Smm used += len; 523228753Smm break; 524228753Smm } 525228753Smm switch (uudecode->state) { 526228753Smm default: 527228753Smm case ST_FIND_HEAD: 528231200Smm /* Do not read more than UUENCODE_BID_MAX_READ bytes */ 529231200Smm if (total + len >= UUENCODE_BID_MAX_READ) { 530231200Smm archive_set_error(&self->archive->archive, 531231200Smm ARCHIVE_ERRNO_FILE_FORMAT, 532231200Smm "Invalid format data"); 533231200Smm return (ARCHIVE_FATAL); 534231200Smm } 535228753Smm if (len - nl >= 11 && memcmp(b, "begin ", 6) == 0) 536228753Smm l = 6; 537228753Smm else if (len - nl >= 18 && 538228753Smm memcmp(b, "begin-base64 ", 13) == 0) 539228753Smm l = 13; 540228753Smm else 541228753Smm l = 0; 542228753Smm if (l != 0 && b[l] >= '0' && b[l] <= '7' && 543228753Smm b[l+1] >= '0' && b[l+1] <= '7' && 544228753Smm b[l+2] >= '0' && b[l+2] <= '7' && b[l+3] == ' ') { 545228753Smm if (l == 6) 546228753Smm uudecode->state = ST_READ_UU; 547228753Smm else 548228753Smm uudecode->state = ST_READ_BASE64; 549228753Smm } 550228753Smm break; 551228753Smm case ST_READ_UU: 552231200Smm if (total + len * 2 > OUT_BUFF_SIZE) 553248616Smm goto finish; 554228753Smm body = len - nl; 555228753Smm if (!uuchar[*b] || body <= 0) { 556228753Smm archive_set_error(&self->archive->archive, 557228753Smm ARCHIVE_ERRNO_MISC, 558228753Smm "Insufficient compressed data"); 559228753Smm return (ARCHIVE_FATAL); 560228753Smm } 561311042Smm /* Get length of undecoded bytes of current line. */ 562228753Smm l = UUDECODE(*b++); 563228753Smm body--; 564228753Smm if (l > body) { 565228753Smm archive_set_error(&self->archive->archive, 566228753Smm ARCHIVE_ERRNO_MISC, 567228753Smm "Insufficient compressed data"); 568228753Smm return (ARCHIVE_FATAL); 569228753Smm } 570228753Smm if (l == 0) { 571228753Smm uudecode->state = ST_UUEND; 572228753Smm break; 573228753Smm } 574228753Smm while (l > 0) { 575228753Smm int n = 0; 576228753Smm 577358090Smm if (!uuchar[b[0]] || !uuchar[b[1]]) 578358090Smm break; 579358090Smm n = UUDECODE(*b++) << 18; 580358090Smm n |= UUDECODE(*b++) << 12; 581358090Smm *out++ = n >> 16; total++; 582358090Smm --l; 583358090Smm 584228753Smm if (l > 0) { 585228753Smm if (!uuchar[b[0]]) 586228753Smm break; 587228753Smm n |= UUDECODE(*b++) << 6; 588228753Smm *out++ = (n >> 8) & 0xFF; total++; 589228753Smm --l; 590228753Smm } 591228753Smm if (l > 0) { 592228753Smm if (!uuchar[b[0]]) 593228753Smm break; 594228753Smm n |= UUDECODE(*b++); 595228753Smm *out++ = n & 0xFF; total++; 596228753Smm --l; 597228753Smm } 598228753Smm } 599228753Smm if (l) { 600228753Smm archive_set_error(&self->archive->archive, 601228753Smm ARCHIVE_ERRNO_MISC, 602228753Smm "Insufficient compressed data"); 603228753Smm return (ARCHIVE_FATAL); 604228753Smm } 605228753Smm break; 606228753Smm case ST_UUEND: 607228753Smm if (len - nl == 3 && memcmp(b, "end ", 3) == 0) 608228753Smm uudecode->state = ST_FIND_HEAD; 609228753Smm else { 610228753Smm archive_set_error(&self->archive->archive, 611228753Smm ARCHIVE_ERRNO_MISC, 612228753Smm "Insufficient compressed data"); 613228753Smm return (ARCHIVE_FATAL); 614228753Smm } 615228753Smm break; 616228753Smm case ST_READ_BASE64: 617231200Smm if (total + len * 2 > OUT_BUFF_SIZE) 618248616Smm goto finish; 619228753Smm l = len - nl; 620228753Smm if (l >= 3 && b[0] == '=' && b[1] == '=' && 621228753Smm b[2] == '=') { 622228753Smm uudecode->state = ST_FIND_HEAD; 623228753Smm break; 624228753Smm } 625228753Smm while (l > 0) { 626228753Smm int n = 0; 627228753Smm 628358090Smm if (!base64[b[0]] || !base64[b[1]]) 629358090Smm break; 630358090Smm n = base64num[*b++] << 18; 631358090Smm n |= base64num[*b++] << 12; 632358090Smm *out++ = n >> 16; total++; 633358090Smm l -= 2; 634358090Smm 635228753Smm if (l > 0) { 636228753Smm if (*b == '=') 637228753Smm break; 638228753Smm if (!base64[*b]) 639228753Smm break; 640228753Smm n |= base64num[*b++] << 6; 641228753Smm *out++ = (n >> 8) & 0xFF; total++; 642228753Smm --l; 643228753Smm } 644228753Smm if (l > 0) { 645228753Smm if (*b == '=') 646228753Smm break; 647228753Smm if (!base64[*b]) 648228753Smm break; 649228753Smm n |= base64num[*b++]; 650228753Smm *out++ = n & 0xFF; total++; 651228753Smm --l; 652228753Smm } 653228753Smm } 654228753Smm if (l && *b != '=') { 655228753Smm archive_set_error(&self->archive->archive, 656228753Smm ARCHIVE_ERRNO_MISC, 657228753Smm "Insufficient compressed data"); 658228753Smm return (ARCHIVE_FATAL); 659228753Smm } 660228753Smm break; 661228753Smm } 662228753Smm } 663248616Smmfinish: 664248616Smm if (ravail < avail_in) 665248616Smm used -= avail_in - ravail; 666248616Smm __archive_read_filter_consume(self->upstream, used); 667228753Smm 668228753Smm *buff = uudecode->out_buff; 669228753Smm uudecode->total += total; 670228753Smm return (total); 671228753Smm} 672228753Smm 673228753Smmstatic int 674228753Smmuudecode_filter_close(struct archive_read_filter *self) 675228753Smm{ 676228753Smm struct uudecode *uudecode; 677228753Smm 678228753Smm uudecode = (struct uudecode *)self->data; 679228753Smm free(uudecode->in_buff); 680228753Smm free(uudecode->out_buff); 681228753Smm free(uudecode); 682228753Smm 683228753Smm return (ARCHIVE_OK); 684228753Smm} 685228753Smm 686