1/* -*- c++ -*-
2
3 KMime, the KDE Internet mail/usenet news message library.
4
5 Copyright (c) 2001-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 MIME data and
25 defines the Codec class.
26
27 @brief
28 Defines the Codec class.
29
30 @authors Marc Mutz \<mutz@kde.org\>
31*/
32
33#include "kmime_codecs.h"
34#include "kmime_util.h"
35#include "kmime_codec_base64.h"
36#include "kmime_codec_qp.h"
37#include "kmime_codec_uuencode.h"
38#include "kmime_codec_identity.h"
39
40#include "kautodeletehash.h"
41
42#include <kascii.h>
43#include <kdebug.h>
44#include <kglobal.h>
45
46#include <QtCore/QCoreApplication>
47#include <QtCore/QMutex>
48
49#include <cassert>
50#include <cstring>
51#include <string.h>
52
53using namespace KMime;
54
55namespace KMime {
56
57// global list of KMime::Codec's
58//@cond PRIVATE
59KAutoDeleteHash<QByteArray, Codec> *Codec::all = 0;
60K_GLOBAL_STATIC( QMutex, dictLock )
61//@endcond
62
63void Codec::cleanupCodec()
64{
65 delete all;
66 all = 0;
67}
68
69void Codec::fillDictionary()
70{
71 //all->insert( "7bit", new SevenBitCodec() );
72 //all->insert( "8bit", new EightBitCodec() );
73 all->insert( "base64", new Base64Codec() );
74 all->insert( "quoted-printable", new QuotedPrintableCodec() );
75 all->insert( "b", new Rfc2047BEncodingCodec() );
76 all->insert( "q", new Rfc2047QEncodingCodec() );
77 all->insert( "x-kmime-rfc2231", new Rfc2231EncodingCodec() );
78 all->insert( "x-uuencode", new UUCodec() );
79 //all->insert( "binary", new BinaryCodec() );
80}
81
82Codec *Codec::codecForName( const char *name )
83{
84 const QByteArray ba( name );
85 return codecForName( ba );
86}
87
88Codec *Codec::codecForName( const QByteArray &name )
89{
90 dictLock->lock(); // protect "all"
91 if ( !all ) {
92 all = new KAutoDeleteHash<QByteArray, Codec>();
93 qAddPostRoutine( cleanupCodec );
94 fillDictionary();
95 }
96 QByteArray lowerName = name;
97 kAsciiToLower( lowerName.data() );
98 Codec *codec = ( *all )[ lowerName ]; // FIXME: operator[] adds an entry into the hash
99 dictLock->unlock();
100
101 if ( !codec ) {
102 kDebug() << "Unknown codec \"" << name << "\" requested!";
103 }
104
105 return codec;
106}
107
108bool Codec::encode( const char* &scursor, const char * const send,
109 char* &dcursor, const char * const dend,
110 bool withCRLF ) const
111{
112 // get an encoder:
113 Encoder *enc = makeEncoder( withCRLF );
114 assert( enc );
115
116 // encode and check for output buffer overflow:
117 while ( !enc->encode( scursor, send, dcursor, dend ) ) {
118 if ( dcursor == dend ) {
119 delete enc;
120 return false; // not enough space in output buffer
121 }
122 }
123
124 // finish and check for output buffer overflow:
125 while ( !enc->finish( dcursor, dend ) ) {
126 if ( dcursor == dend ) {
127 delete enc;
128 return false; // not enough space in output buffer
129 }
130 }
131
132 // cleanup and return:
133 delete enc;
134 return true; // successfully encoded.
135}
136
137QByteArray Codec::encode( const QByteArray &src, bool withCRLF ) const
138{
139 // allocate buffer for the worst case:
140 QByteArray result;
141 result.resize( maxEncodedSizeFor( src.size(), withCRLF ) );
142
143 // set up iterators:
144 QByteArray::ConstIterator iit = src.begin();
145 QByteArray::ConstIterator iend = src.end();
146 QByteArray::Iterator oit = result.begin();
147 QByteArray::ConstIterator oend = result.end();
148
149 // encode
150 if ( !encode( iit, iend, oit, oend, withCRLF ) ) {
151 kFatal() << name() << "codec lies about it's mEncodedSizeFor()";
152 }
153
154 // shrink result to actual size:
155 result.truncate( oit - result.begin() );
156
157 return result;
158}
159
160QByteArray Codec::decode( const QByteArray &src, bool withCRLF ) const
161{
162 // allocate buffer for the worst case:
163 QByteArray result;
164 result.resize( maxDecodedSizeFor( src.size(), withCRLF ) );
165
166 // set up iterators:
167 QByteArray::ConstIterator iit = src.begin();
168 QByteArray::ConstIterator iend = src.end();
169 QByteArray::Iterator oit = result.begin();
170 QByteArray::ConstIterator oend = result.end();
171
172 // decode
173 if ( !decode( iit, iend, oit, oend, withCRLF ) ) {
174 kFatal() << name() << "codec lies about it's maxDecodedSizeFor()";
175 }
176
177 // shrink result to actual size:
178 result.truncate( oit - result.begin() );
179
180 return result;
181}
182
183bool Codec::decode( const char* &scursor, const char * const send,
184 char* &dcursor, const char * const dend,
185 bool withCRLF ) const
186{
187 // get a decoder:
188 Decoder *dec = makeDecoder( withCRLF );
189 assert( dec );
190
191 // decode and check for output buffer overflow:
192 while ( !dec->decode( scursor, send, dcursor, dend ) ) {
193 if ( dcursor == dend ) {
194 delete dec;
195 return false; // not enough space in output buffer
196 }
197 }
198
199 // finish and check for output buffer overflow:
200 while ( !dec->finish( dcursor, dend ) ) {
201 if ( dcursor == dend ) {
202 delete dec;
203 return false; // not enough space in output buffer
204 }
205 }
206
207 // cleanup and return:
208 delete dec;
209 return true; // successfully encoded.
210}
211
212// write as much as possible off the output buffer. Return true if
213// flushing was complete, false if some chars could not be flushed.
214bool Encoder::flushOutputBuffer( char* &dcursor, const char * const dend )
215{
216 int i;
217 // copy output buffer to output stream:
218 for ( i = 0 ; dcursor != dend && i < mOutputBufferCursor ; ++i ) {
219 *dcursor++ = mOutputBuffer[i];
220 }
221
222 // calculate the number of missing chars:
223 int numCharsLeft = mOutputBufferCursor - i;
224 // push the remaining chars to the begin of the buffer:
225 if ( numCharsLeft ) {
226 ::memmove( mOutputBuffer, mOutputBuffer + i, numCharsLeft );
227 }
228 // adjust cursor:
229 mOutputBufferCursor = numCharsLeft;
230
231 return !numCharsLeft;
232}
233
234} // namespace KMime
235