summaryrefslogtreecommitdiffstats
path: root/libkmime/kmime_codec_uuencode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libkmime/kmime_codec_uuencode.cpp')
-rw-r--r--libkmime/kmime_codec_uuencode.cpp240
1 files changed, 240 insertions, 0 deletions
diff --git a/libkmime/kmime_codec_uuencode.cpp b/libkmime/kmime_codec_uuencode.cpp
new file mode 100644
index 000000000..2c6152a95
--- /dev/null
+++ b/libkmime/kmime_codec_uuencode.cpp
@@ -0,0 +1,240 @@
+/* -*- c++ -*-
+ kmime_codec_uuencode.cpp
+
+ This file is part of KMime, the KDE internet mail/usenet news message library.
+ Copyright (c) 2002 Marc Mutz <mutz@kde.org>
+
+ KMime is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License, version 2, as
+ published by the Free Software Foundation.
+
+ KMime 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 library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this library with any edition of
+ the Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ Qt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#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*/ ) { 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 {
+ kdWarning() << "UUDecoder: garbage before \"begin\", resetting parser"
+ << endl;
+ 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 );
+ // or if we are past the end line:
+ else if ( mSawEnd ) {
+ 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 {
+ kdWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine = " << mIntoEndLine << " )!" << endl;
+ 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:
+ kdWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 )
+ << "UUDecoder: mismatch between announced ("
+ << mAnnouncedOctetCount << ") and actual line octet count!" << endl;
+
+ }
+
+ // return false when caller should call us again:
+ return (scursor == send);
+} // UUDecoder::decode()
+
+
+} // namespace KMime