mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-23 18:32:51 +00:00
244 lines
7.1 KiB
C++
244 lines
7.1 KiB
C++
/* -*- c++ -*-
|
|
kmime_codec_uuencode.cpp
|
|
|
|
KMime, the KDE Internet mail/usenet news message library.
|
|
Copyright (c) 2002 Marc Mutz <mutz@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
/**
|
|
@file
|
|
This file is part of the API for handling @ref MIME data and
|
|
defines a @ref uuencode @ref Codec class.
|
|
|
|
@brief
|
|
Defines the UUCodec class.
|
|
|
|
@authors Marc Mutz \<mutz@kde.org\>
|
|
*/
|
|
|
|
#include "kmime_codec_uuencode.h"
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <cassert>
|
|
|
|
using namespace KMime;
|
|
|
|
namespace KMime {
|
|
|
|
class UUDecoder : public Decoder
|
|
{
|
|
uint mStepNo;
|
|
uchar mAnnouncedOctetCount; // (on current line)
|
|
uchar mCurrentOctetCount; // (on current line)
|
|
uchar mOutbits;
|
|
bool mLastWasCRLF : 1;
|
|
bool mSawBegin : 1; // whether we already saw ^begin...
|
|
uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
|
|
bool mSawEnd : 1; // whether we already saw ^end...
|
|
uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3
|
|
|
|
void searchForBegin( const char* &scursor, const char * const send );
|
|
|
|
protected:
|
|
friend class UUCodec;
|
|
UUDecoder( bool withCRLF=false )
|
|
: Decoder( withCRLF ), mStepNo( 0 ),
|
|
mAnnouncedOctetCount( 0 ), mCurrentOctetCount( 0 ),
|
|
mOutbits( 0 ), mLastWasCRLF( true ),
|
|
mSawBegin( false ), mIntoBeginLine( 0 ),
|
|
mSawEnd( false ), mIntoEndLine( 0 ) {}
|
|
|
|
public:
|
|
virtual ~UUDecoder() {}
|
|
|
|
bool decode( const char* &scursor, const char * const send,
|
|
char* &dcursor, const char * const dend );
|
|
// ### really needs no finishing???
|
|
bool finish( char* &dcursor, const char * const dend )
|
|
{ Q_UNUSED( dcursor ); Q_UNUSED( dend ); return true; }
|
|
};
|
|
|
|
Encoder * UUCodec::makeEncoder( bool ) const
|
|
{
|
|
return 0; // encoding not supported
|
|
}
|
|
|
|
Decoder * UUCodec::makeDecoder( bool withCRLF ) const
|
|
{
|
|
return new UUDecoder( withCRLF );
|
|
}
|
|
|
|
/********************************************************/
|
|
/********************************************************/
|
|
/********************************************************/
|
|
|
|
void UUDecoder::searchForBegin( const char* &scursor, const char * const send )
|
|
{
|
|
static const char begin[] = "begin\n";
|
|
static const uint beginLength = 5; // sic!
|
|
|
|
assert( !mSawBegin || mIntoBeginLine > 0 );
|
|
|
|
while ( scursor != send ) {
|
|
uchar ch = *scursor++;
|
|
if ( ch == begin[mIntoBeginLine] ) {
|
|
if ( mIntoBeginLine < beginLength ) {
|
|
// found another char
|
|
++mIntoBeginLine;
|
|
if ( mIntoBeginLine == beginLength ) {
|
|
mSawBegin = true; // "begin" complete, now search the next \n...
|
|
}
|
|
} else { // mIntoBeginLine == beginLength
|
|
// found '\n': begin line complete
|
|
mLastWasCRLF = true;
|
|
mIntoBeginLine = 0;
|
|
return;
|
|
}
|
|
} else if ( mSawBegin ) {
|
|
// OK, skip stuff until the next \n
|
|
} else {
|
|
kWarning() << "UUDecoder: garbage before \"begin\", resetting parser";
|
|
mIntoBeginLine = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
|
|
// which gets mapped to 0x60
|
|
static inline uchar uuDecode( uchar c )
|
|
{
|
|
return ( c - ' ' ) // undo shift and
|
|
& 0x3F; // map 0x40 (0x60-' ') to 0...
|
|
}
|
|
|
|
bool UUDecoder::decode( const char* &scursor, const char * const send,
|
|
char* &dcursor, const char * const dend )
|
|
{
|
|
// First, check whether we still need to find the "begin" line:
|
|
if ( !mSawBegin || mIntoBeginLine != 0 ) {
|
|
searchForBegin( scursor, send );
|
|
} else if ( mSawEnd ) {
|
|
// or if we are past the end line:
|
|
scursor = send; // do nothing anymore...
|
|
return true;
|
|
}
|
|
|
|
while ( dcursor != dend && scursor != send ) {
|
|
uchar ch = *scursor++;
|
|
uchar value;
|
|
|
|
// Check whether we need to look for the "end" line:
|
|
if ( mIntoEndLine > 0 ) {
|
|
static const char end[] = "end";
|
|
static const uint endLength = 3;
|
|
|
|
if ( ch == end[mIntoEndLine] ) {
|
|
++mIntoEndLine;
|
|
if ( mIntoEndLine == endLength ) {
|
|
mSawEnd = true;
|
|
scursor = send; // shortcut to the end
|
|
return true;
|
|
}
|
|
continue;
|
|
} else {
|
|
kWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine ="
|
|
<< mIntoEndLine << ")!";
|
|
mIntoEndLine = 0;
|
|
// fall through...
|
|
}
|
|
}
|
|
|
|
// Normal parsing:
|
|
|
|
// The first char of a line is an encoding of the length of the
|
|
// current line. We simply ignore it:
|
|
if ( mLastWasCRLF ) {
|
|
// reset char-per-line counter:
|
|
mLastWasCRLF = false;
|
|
mCurrentOctetCount = 0;
|
|
|
|
// try to decode the chars-on-this-line announcement:
|
|
if ( ch == 'e' ) { // maybe the beginning of the "end"? ;-)
|
|
mIntoEndLine = 1;
|
|
} else if ( ch > 0x60 ) {
|
|
// ### invalid line length char: what shall we do??
|
|
} else if ( ch > ' ' ) {
|
|
mAnnouncedOctetCount = uuDecode( ch );
|
|
} else if ( ch == '\n' ) {
|
|
mLastWasCRLF = true; // oops, empty line
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// try converting ch to a 6-bit value:
|
|
if ( ch > 0x60 ) {
|
|
continue; // invalid char
|
|
} else if ( ch > ' ' ) {
|
|
value = uuDecode( ch );
|
|
} else if ( ch == '\n' ) { // line end
|
|
mLastWasCRLF = true;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
// add the new bits to the output stream and flush full octets:
|
|
switch ( mStepNo ) {
|
|
case 0:
|
|
mOutbits = value << 2;
|
|
break;
|
|
case 1:
|
|
if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
|
|
*dcursor++ = (char)( mOutbits | value >> 4 );
|
|
}
|
|
++mCurrentOctetCount;
|
|
mOutbits = value << 4;
|
|
break;
|
|
case 2:
|
|
if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
|
|
*dcursor++ = (char)( mOutbits | value >> 2 );
|
|
}
|
|
++mCurrentOctetCount;
|
|
mOutbits = value << 6;
|
|
break;
|
|
case 3:
|
|
if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
|
|
*dcursor++ = (char)( mOutbits | value );
|
|
}
|
|
++mCurrentOctetCount;
|
|
mOutbits = 0;
|
|
break;
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
mStepNo = ( mStepNo + 1 ) % 4;
|
|
|
|
// check whether we ran over the announced octet count for this line:
|
|
kWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 )
|
|
<< "UUDecoder: mismatch between announced ("
|
|
<< mAnnouncedOctetCount << ") and actual line octet count!";
|
|
|
|
}
|
|
|
|
// return false when caller should call us again:
|
|
return scursor == send;
|
|
} // UUDecoder::decode()
|
|
|
|
} // namespace KMime
|