You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/mimelib/uuencode.cpp

478 lines
10 KiB

//=============================================================================
// File: uuencode.cpp
// Contents: Definitions for DwUuencode
// Maintainer: Doug Sauder <dwsauder@fwb.gulf.net>
// WWW: http://www.fwb.gulf.net/~dwsauder/mimepp.html
//
// Copyright (c) 1996, 1997 Douglas W. Sauder
// All rights reserved.
//
// IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT,
// INDIRECT, SPECIAL, INCIDENTAL, OR CONSETQUENTIAL DAMAGES ARISING OUT OF
// THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER
// HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT
// NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
// BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
// SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
//
//=============================================================================
#define DW_IMPLEMENTATION
#include <mimelib/config.h>
#include <mimelib/debug.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <mimelib/uuencode.h>
#include <config.h>
#if defined(DW_TESTING_UUENCODE)
#include <stdlib.h>
#include <time.h>
#include <iostream>
#include <fstream>
#endif
DwUuencode::DwUuencode()
{
memset(mFileName, 0, sizeof(mFileName));
mMode = 0644;
}
DwUuencode::~DwUuencode()
{
}
void DwUuencode::SetFileName(const char* aName)
{
size_t n = sizeof(mFileName);
strlcpy(mFileName, aName, n);
mFileName[n-1] = 0; // Superfluous
}
const char* DwUuencode::FileName() const
{
return mFileName;
}
void DwUuencode::SetFileMode(DwUint16 aMode)
{
mMode = aMode;
}
DwUint16 DwUuencode::FileMode() const
{
return mMode;
}
void DwUuencode::SetBinaryChars(const DwString& aStr)
{
mBinaryChars = aStr;
}
const DwString& DwUuencode::BinaryChars() const
{
return mBinaryChars;
}
void DwUuencode::SetAsciiChars(const DwString& aStr)
{
mAsciiChars = aStr;
}
const DwString& DwUuencode::AsciiChars() const
{
return mAsciiChars;
}
#define ENC(c) ((char) ((c) ? ((c) & 0x3F) + ' ' : 96 ))
void DwUuencode::Encode()
{
// Get input buffer
size_t binLen = mBinaryChars.length();
const char* binBuf = mBinaryChars.data();
size_t binPos = 0;
// Allocate buffer for binary chars
size_t ascSize = (binLen+2)/3*4
+ ((binLen+44)/45+1)*(strlen(DW_EOL)+1)
+ strlen(mFileName)
+ 13 + 2*strlen(DW_EOL)
+ 100;
DwString ascStr(ascSize, (char)0);
char* ascBuf = (char*)ascStr.data();
size_t ascPos = 0;
// Write the "begin" line
snprintf(ascBuf, ascSize, "begin %o %s" DW_EOL, mMode, mFileName);
ascPos = strlen(ascBuf);
// Encode the binary chars
while (ascPos < ascSize) {
int numBinChars = binLen - binPos;
numBinChars = (numBinChars <= 45) ? numBinChars : 45;
ascBuf[ascPos++] = ENC(numBinChars);
if (numBinChars == 0) {
strcpy(&ascBuf[ascPos], DW_EOL);
ascPos += strlen(DW_EOL);
break;
}
int bin, asc;
int binCharsDone = 0;
while (binCharsDone <= numBinChars - 3) {
bin = binBuf[binPos++];
asc = (bin & 0xFC) >> 2;
ascBuf[ascPos++] = ENC(asc);
asc = (bin & 0x03) << 4;
bin = binBuf[binPos++];
asc |= (bin & 0xF0) >> 4;
ascBuf[ascPos++] = ENC(asc);
asc = (bin & 0x0F) << 2;
bin = binBuf[binPos++];
asc |= (bin & 0xC0) >> 6;
ascBuf[ascPos++] = ENC(asc);
asc = bin & 0x3F;
ascBuf[ascPos++] = ENC(asc);
binCharsDone += 3;
}
if (binCharsDone < numBinChars) {
int binCharsLeft = numBinChars - binCharsDone;
switch (binCharsLeft) {
case 1:
bin = binBuf[binPos++];
asc = (bin & 0xFC) >> 2;
ascBuf[ascPos++] = ENC(asc);
asc = (bin & 0x03) << 4;
ascBuf[ascPos++] = ENC(asc);
ascBuf[ascPos++] = 96;
ascBuf[ascPos++] = 96;
break;
case 2:
bin = binBuf[binPos++];
asc = (bin & 0xFC) >> 2;
ascBuf[ascPos++] = ENC(asc);
asc = (bin & 0x03) << 4;
bin = binBuf[binPos++];
asc |= (bin & 0xF0) >> 4;
ascBuf[ascPos++] = ENC(asc);
asc = (bin & 0x0F) << 2;
ascBuf[ascPos++] = ENC(asc);
ascBuf[ascPos++] = 96;
break;
default:
break;
}
}
strcpy(&ascBuf[ascPos], DW_EOL);
ascPos += strlen(DW_EOL);
}
// Write the "end" line
strcpy(&ascBuf[ascPos], "end" DW_EOL);
ascPos += 3 + strlen(DW_EOL);
ascBuf[ascPos] = 0;
mAsciiChars.assign(ascStr, 0, ascPos);
}
#define DEC(c) (((c) - ' ') & 0x3F)
int DwUuencode::Decode()
{
int retVal = -1;
// Get input buffer
size_t ascLen = mAsciiChars.length();
const char* ascBuf = mAsciiChars.data();
size_t ascPos = 0;
// Allocate destination buffer
size_t binSize = (ascLen+3)/4*3;
mBinaryChars.reserve(binSize);
// Look for "begin " at beginning of buffer
if (ascPos + 6 <= ascLen &&
strncmp(&ascBuf[ascPos], "begin ", 6) == 0) {
ascPos += 6;
}
else {
// Find "\nbegin " or "\rbegin "
while (ascPos < ascLen) {
int ch = ascBuf[ascPos++] & 0xff;
switch (ch) {
case '\n':
case '\r':
if (ascPos + 6 <= ascLen &&
strncmp(&ascBuf[ascPos], "begin ", 6) == 0) {
ascPos += 6;
goto LOOP_EXIT_1;
}
break;
default:
break;
}
}
}
LOOP_EXIT_1:
// Get mode
mMode = 0;
while (ascPos < ascLen && isdigit(ascBuf[ascPos])) {
mMode <<= 3;
mMode += (DwUint16) (ascBuf[ascPos++] - '0');
}
// Get file name
while (ascPos < ascLen &&
(ascBuf[ascPos] == ' ' || ascBuf[ascPos] == '\t')) {
++ascPos;
}
size_t p1 = 0;
while (ascPos < ascLen && p1 < sizeof(mFileName)-1 &&
!isspace(ascBuf[ascPos])) {
mFileName[p1++] = ascBuf[ascPos++];
}
mFileName[p1] = 0;
// Advance to beginning of next line
while (ascPos < ascLen) {
int ch = ascBuf[ascPos++];
switch (ch) {
case '\n':
goto LOOP_EXIT_2;
case '\r':
if (ascPos < ascLen && ascBuf[ascPos] == '\n') {
++ascPos;
}
goto LOOP_EXIT_2;
default:
break;
}
}
LOOP_EXIT_2:
// Decode chars
while (ascPos < ascLen) {
int asc, bin;
// Get number of binary chars in this line
asc = ascBuf[ascPos++] & 0xff;
size_t numBinChars = DEC(asc);
if (numBinChars == 0) {
break;
}
// Decode this line
size_t binCharsEaten = 0;
while (binCharsEaten <= numBinChars - 3 && ascPos <= ascLen - 4) {
asc = ascBuf[ascPos++] & 0xff;
bin = (DEC(asc) & 0x3F) << 2;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x30) >> 4;
mBinaryChars.append((size_t) 1, (char) bin);
bin = (DEC(asc) & 0x0F) << 4;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x3C) >> 2;
mBinaryChars.append((size_t) 1, (char) bin);
bin = (DEC(asc) & 0x03) << 6;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x3F);
mBinaryChars.append((size_t) 1, (char) bin);
binCharsEaten += 3;
}
// Special case if number of binary chars is not divisible by 3
if (binCharsEaten < numBinChars) {
int binCharsLeft = numBinChars - binCharsEaten;
switch (binCharsLeft) {
case 2:
if (ascPos >= ascLen)
break;
asc = ascBuf[ascPos++] & 0xff;
bin = (DEC(asc) & 0x3F) << 2;
if (ascPos >= ascLen)
break;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x30) >> 4;
mBinaryChars.append((size_t) 1, (char) bin);
bin = (DEC(asc) & 0x0F) << 4;
if (ascPos >= ascLen)
break;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x3C) >> 2;
mBinaryChars.append((size_t) 1, (char) bin);
break;
case 1:
if (ascPos >= ascLen)
break;
asc = ascBuf[ascPos++] & 0xff;
bin = (DEC(asc) & 0x3F) << 2;
if (ascPos >= ascLen)
break;
asc = ascBuf[ascPos++] & 0xff;
bin |= (DEC(asc) & 0x30) >> 4;
mBinaryChars.append((size_t) 1, (char) bin);
break;
default:
break;
}
}
// Advance to beginning of next line
while (ascPos < ascLen) {
int ch = ascBuf[ascPos++];
switch (ch) {
case '\n':
goto LOOP_EXIT_3;
case '\r':
if (ascPos < ascLen &&
ascBuf[ascPos] == '\n') {
++ascPos;
}
goto LOOP_EXIT_3;
default:
break;
}
}
LOOP_EXIT_3:
;
}
while (ascPos < ascLen) {
int ch = ascBuf[ascPos++];
switch (ch) {
case '\n':
goto LOOP_EXIT_4;
case '\r':
if (ascPos < ascLen &&
ascBuf[ascPos] == '\n') {
++ascPos;
}
goto LOOP_EXIT_4;
default:
break;
}
}
LOOP_EXIT_4:
if (ascPos + 3 <= ascLen &&
strncmp(&ascBuf[ascPos], "end", 3) == 0) {
retVal = 0;
}
return retVal;
}
#if defined(DW_TESTING_UUENCODE)
// Test harness for DwUudecode
int main(int argc, char** argv)
{
srand(time(0));
DwString binStr;
binStr.reserve(5000);
char ch;
int i;
for (i=0; i < 4000; ++i) {
ch = rand()/(double)RAND_MAX*256;
binStr += (char) ch;
}
for ( ; i < 4100; ++i) {
binStr += (char) 0;
}
DwUuencode uu;
uu.SetFileName("Testfile.dat");
uu.SetMode(0600);
uu.SetBinaryChars(binStr);
uu.Encode();
DwString asciiStr = uu.AsciiChars();
// std::ofstream out("test.out", ios::out|ios::binary);
std::ofstream out("test.out", ios::out);
out << asciiStr;
DwUuencode uu1;
uu1.SetAsciiChars(uu.AsciiChars());
uu1.Decode();
size_t n = uu1.BinaryChars().length();
const char* b1 = binStr.data();
const char* b2 = uu1.BinaryChars().data();
int bad = 0;
for (i=0; i < n; ++i) {
if (b1[i] != b2[i]) {
cout << "Binary chars not equal at position " << i << "\n";
bad = 1;
break;
}
}
if (! bad) {
cout << "A-okay\n";
}
return 0;
}
#endif