1/* -*- c++ -*-
2 kmime_codec_uuencode.cpp
3
4 KMime, the KDE Internet mail/usenet news message library.
5 Copyright (c) 2002 Marc Mutz <mutz@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22/**
23 @file
24 This file is part of the API for handling @ref MIME data and
25 defines a @ref uuencode @ref Codec class.
26
27 @brief
28 Defines the UUCodec class.
29
30 @authors Marc Mutz \<mutz@kde.org\>
31*/
32
33#include "kmime_codec_uuencode.h"
34
35#include <kdebug.h>
36
37#include <cassert>
38
39using namespace KMime;
40
41namespace KMime {
42
43class UUDecoder : public Decoder
44{
45 uint mStepNo;
46 uchar mAnnouncedOctetCount; // (on current line)
47 uchar mCurrentOctetCount; // (on current line)
48 uchar mOutbits;
49 bool mLastWasCRLF : 1;
50 bool mSawBegin : 1; // whether we already saw ^begin...
51 uint mIntoBeginLine : 3; // count #chars we compared against "begin" 0..5
52 bool mSawEnd : 1; // whether we already saw ^end...
53 uint mIntoEndLine : 2; // count #chars we compared against "end" 0..3
54
55 void searchForBegin( const char* &scursor, const char * const send );
56
57 protected:
58 friend class UUCodec;
59 UUDecoder( bool withCRLF=false )
60 : Decoder( withCRLF ), mStepNo( 0 ),
61 mAnnouncedOctetCount( 0 ), mCurrentOctetCount( 0 ),
62 mOutbits( 0 ), mLastWasCRLF( true ),
63 mSawBegin( false ), mIntoBeginLine( 0 ),
64 mSawEnd( false ), mIntoEndLine( 0 ) {}
65
66 public:
67 virtual ~UUDecoder() {}
68
69 bool decode( const char* &scursor, const char * const send,
70 char* &dcursor, const char * const dend );
71 // ### really needs no finishing???
72 bool finish( char* &dcursor, const char * const dend )
73 { Q_UNUSED( dcursor ); Q_UNUSED( dend ); return true; }
74};
75
76Encoder * UUCodec::makeEncoder( bool ) const
77{
78 return 0; // encoding not supported
79}
80
81Decoder * UUCodec::makeDecoder( bool withCRLF ) const
82{
83 return new UUDecoder( withCRLF );
84}
85
86/********************************************************/
87/********************************************************/
88/********************************************************/
89
90void UUDecoder::searchForBegin( const char* &scursor, const char * const send )
91{
92 static const char begin[] = "begin\n";
93 static const uint beginLength = 5; // sic!
94
95 assert( !mSawBegin || mIntoBeginLine > 0 );
96
97 while ( scursor != send ) {
98 uchar ch = *scursor++;
99 if ( ch == begin[mIntoBeginLine] ) {
100 if ( mIntoBeginLine < beginLength ) {
101 // found another char
102 ++mIntoBeginLine;
103 if ( mIntoBeginLine == beginLength ) {
104 mSawBegin = true; // "begin" complete, now search the next \n...
105 }
106 } else { // mIntoBeginLine == beginLength
107 // found '\n': begin line complete
108 mLastWasCRLF = true;
109 mIntoBeginLine = 0;
110 return;
111 }
112 } else if ( mSawBegin ) {
113 // OK, skip stuff until the next \n
114 } else {
115 kWarning() << "UUDecoder: garbage before \"begin\", resetting parser";
116 mIntoBeginLine = 0;
117 }
118 }
119
120}
121
122// uuencoding just shifts all 6-bit octets by 32 (SP/' '), except NUL,
123// which gets mapped to 0x60
124static inline uchar uuDecode( uchar c )
125{
126 return ( c - ' ' ) // undo shift and
127 & 0x3F; // map 0x40 (0x60-' ') to 0...
128}
129
130bool UUDecoder::decode( const char* &scursor, const char * const send,
131 char* &dcursor, const char * const dend )
132{
133 // First, check whether we still need to find the "begin" line:
134 if ( !mSawBegin || mIntoBeginLine != 0 ) {
135 searchForBegin( scursor, send );
136 } else if ( mSawEnd ) {
137 // or if we are past the end line:
138 scursor = send; // do nothing anymore...
139 return true;
140 }
141
142 while ( dcursor != dend && scursor != send ) {
143 uchar ch = *scursor++;
144 uchar value;
145
146 // Check whether we need to look for the "end" line:
147 if ( mIntoEndLine > 0 ) {
148 static const char end[] = "end";
149 static const uint endLength = 3;
150
151 if ( ch == end[mIntoEndLine] ) {
152 ++mIntoEndLine;
153 if ( mIntoEndLine == endLength ) {
154 mSawEnd = true;
155 scursor = send; // shortcut to the end
156 return true;
157 }
158 continue;
159 } else {
160 kWarning() << "UUDecoder: invalid line octet count looks like \"end\" (mIntoEndLine ="
161 << mIntoEndLine << ")!";
162 mIntoEndLine = 0;
163 // fall through...
164 }
165 }
166
167 // Normal parsing:
168
169 // The first char of a line is an encoding of the length of the
170 // current line. We simply ignore it:
171 if ( mLastWasCRLF ) {
172 // reset char-per-line counter:
173 mLastWasCRLF = false;
174 mCurrentOctetCount = 0;
175
176 // try to decode the chars-on-this-line announcement:
177 if ( ch == 'e' ) { // maybe the beginning of the "end"? ;-)
178 mIntoEndLine = 1;
179 } else if ( ch > 0x60 ) {
180 // ### invalid line length char: what shall we do??
181 } else if ( ch > ' ' ) {
182 mAnnouncedOctetCount = uuDecode( ch );
183 } else if ( ch == '\n' ) {
184 mLastWasCRLF = true; // oops, empty line
185 }
186
187 continue;
188 }
189
190 // try converting ch to a 6-bit value:
191 if ( ch > 0x60 ) {
192 continue; // invalid char
193 } else if ( ch > ' ' ) {
194 value = uuDecode( ch );
195 } else if ( ch == '\n' ) { // line end
196 mLastWasCRLF = true;
197 continue;
198 } else {
199 continue;
200 }
201
202 // add the new bits to the output stream and flush full octets:
203 switch ( mStepNo ) {
204 case 0:
205 mOutbits = value << 2;
206 break;
207 case 1:
208 if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
209 *dcursor++ = (char)( mOutbits | value >> 4 );
210 }
211 ++mCurrentOctetCount;
212 mOutbits = value << 4;
213 break;
214 case 2:
215 if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
216 *dcursor++ = (char)( mOutbits | value >> 2 );
217 }
218 ++mCurrentOctetCount;
219 mOutbits = value << 6;
220 break;
221 case 3:
222 if ( mCurrentOctetCount < mAnnouncedOctetCount ) {
223 *dcursor++ = (char)( mOutbits | value );
224 }
225 ++mCurrentOctetCount;
226 mOutbits = 0;
227 break;
228 default:
229 assert( 0 );
230 }
231 mStepNo = ( mStepNo + 1 ) % 4;
232
233 // check whether we ran over the announced octet count for this line:
234 kWarning( mCurrentOctetCount == mAnnouncedOctetCount + 1 )
235 << "UUDecoder: mismatch between announced ("
236 << mAnnouncedOctetCount << ") and actual line octet count!";
237
238 }
239
240 // return false when caller should call us again:
241 return scursor == send;
242} // UUDecoder::decode()
243
244} // namespace KMime
245