// // aegis - project change supervisor // Copyright (C) 2001-2008 Peter Miller // Copyright (C) 2008 Walter Franzini // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include #include #include // // The NOT_DECODABLE value is placed into the itab array to annotate // characters which are not part of the encoding table. It must be // positive. The values 0..63 indicate valid decode values. // #define NOT_DECODABLE 127 static char default_encoding_table[] = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"; input_uudecode::~input_uudecode() { } input_uudecode::input_uudecode(input &arg) : deeper(arg), pos(0), state(0), checksum(-1), padding(-1) { } long input_uudecode::read_inner(void *data, size_t len) { // // Look for the line which starts with 'begin ' // if (state >= 6) return 0; unsigned char *cp = (unsigned char *)data; unsigned char *end = cp + len; assert(len >= 64); if (len < 64) deeper->fatal_error("uudecode: read too little"); while (cp + 64 < end) { unsigned char buf[1000]; int ilen; unsigned char *bp; unsigned char *ep; int sum; int n; int check_the_sum; // // read a line // bp = buf; for (;;) { int c = deeper->getch(); if (c < 0) { if (bp == buf && state == 0) { deeper->fatal_error("uudecode: no data in file"); } else { deeper->fatal_error("uudecode: premature end-of-file"); } } if (c == '\n') break; if (bp >= ENDOF(buf) - 1) { deeper->fatal_error("uudecode: line too long"); } *bp++ = c; } *bp = 0; ilen = bp - buf; ep = bp; bp = buf; switch (state) { case 0: // before "table" or "begin " if (!strcmp((const char *)buf, "table")) state = 1; else if (!memcmp(buf, "begin ", 6)) { state = 4; // // use default encoding // memcpy(etab, default_encoding_table, 64); // // invert the encoding table // invert: for (unsigned int k = 0; k <= UCHAR_MAX; ++k) itab[k] = NOT_DECODABLE; for (int j = 0; j < 64; ++j) { n = (unsigned char)etab[j]; if (itab[n] != NOT_DECODABLE) { deeper->fatal_error("uudecode: table has duplicate"); } itab[n] = j; } // // Hack to cope with common encoding // extension (because many e-mail // forwarders rip of trailing spaces). // if (etab[0] == ' ' && itab[(unsigned char)'`'] == NOT_DECODABLE) itab[(unsigned char)'`'] = 0; } continue; case 1: // after "table" if (ilen != 32) { deeper->fatal_error("uudecode: broken table section"); } memcpy(etab, buf, 32); state++; continue; case 2: // after "table" + 1 if (ilen != 32) { deeper->fatal_error("uudecode: broken table section"); } memcpy(etab + 32, buf, 32); state++; goto invert; case 3: // after "table" + 2 if (!ilen) continue; if (!memcmp(buf, "begin ", 6)) { state++; } else { deeper->fatal_error("uudecode: expected \"begin\""); } continue; case 4: // data section if (!ilen) { deeper->fatal_error("uudecode: blank line in data section"); } // // get length // n = itab[*bp++]; if (n == NOT_DECODABLE) { broken: deeper->fatal_error ( "uudecode: broken data section (character not in encoding " "table)" ); } assert(n < 64); check_the_sum = 0; switch (n % 3) { case 0: switch ((n * 4 + 2) / 3 + 2 - ilen) { case 0: if (checksum < 0) checksum = 1; break; case 1: if (padding < 0) padding = 1; if (checksum < 0) checksum = 0; break; default: broken_length: deeper->fatal_error("uudecode: data line has wrong length"); } check_the_sum = checksum; break; case 1: switch ((n + 2) / 3 * 4 + 2 - ilen) { case 0: if (padding < 0) padding = 1; if (checksum < 0) checksum = 1; break; case 1: if (padding < 0) padding = 1; if (checksum < 0) checksum = 0; break; case 2: if (padding < 0) padding = 0; if (checksum < 0) checksum = 1; break; case 3: if (padding < 0) padding = 0; if (checksum < 0) checksum = 0; break; default: goto broken_length; } check_the_sum = checksum; break; case 2: switch ((n + 2) / 3 * 4 + 2 - ilen) { case 0: if (padding < 0) padding = 1; if (checksum < 0) checksum = 1; break; case 1: // padding or checksum, but not both if (checksum >= 0) padding = !checksum; break; case 2: if (padding < 0) padding = 0; if (checksum < 0) checksum = 0; break; default: goto broken_length; } check_the_sum = checksum && padding >= 0; break; } // // read the bytes // sum = n; for (int j = 0; j < n; j += 3) { int c1 = itab[*bp++]; if (c1 == NOT_DECODABLE) goto broken; assert(c1 < 64); sum += c1; int c2 = itab[*bp++]; if (c2 == NOT_DECODABLE) goto broken; assert(c2 < 64); sum += c2; int c = (c1 << 2) | ((c2 >> 4) & 0x03); *cp++ = c; if (j + 1 < n || padding == 1) { int c3 = itab[*bp++]; if (c3 == NOT_DECODABLE) goto broken; assert(c3 < 64); sum += c3; if (j + 1 < n) { c = ((c2 << 4) & 0xF0) | ((c3 >> 2) & 0x0F); *cp++ = c; } if (j + 2 < n || padding == 1) { int c4 = itab[*bp++]; if (c4 == NOT_DECODABLE) goto broken; assert(c4 < 64); sum += c4; if (j + 2 < n) { c = ((c3 << 6) & 0xC0) | c4; *cp++ = c; } } } } if (check_the_sum) { if (bp >= ep) { deeper->fatal_error("uudecode: checksum required"); } int c = itab[*bp++]; if (c == NOT_DECODABLE) goto broken; assert(c < 64); if (c != (sum & 077)) { deeper->fatal_error("uudecode: checksum is wrong"); } } if (n == 0) state++; continue; case 5: if (!strcmp((const char *)buf, "end")) { state++; break; } deeper->fatal_error("uudecode: \"end\" required"); case 6: break; } break; } size_t nbytes = (cp - (unsigned char *)data); pos += nbytes; return nbytes; } long input_uudecode::ftell_inner() { return pos; } nstring input_uudecode::name() { return deeper->name(); } long input_uudecode::length() { return -1; } void input_uudecode::keepalive() { deeper->keepalive(); } bool input_uudecode::candidate(input &ifp) { trace(("input_uudecode_recognise()\n{\n")); static char magic[] = "begin "; bool result = 0; nstring_accumulator sac; int char_state = 0; while (sac.size() < 8000) { int c = ifp->getch(); if (c < 0) break; sac.push_back(c); if (c == '\n') char_state = 0; else if ((size_t)char_state < sizeof(magic) && c == magic[char_state]) { ++char_state; if (!magic[char_state]) { result = 1; break; } } else char_state = 666; } ifp->unread(sac.get_data(), sac.size()); trace(("return %d\n", result)); trace(("}\n")); return result; } bool input_uudecode::is_remote() const { return deeper->is_remote(); }