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 | |
53 | using namespace KMime; |
54 | |
55 | namespace KMime { |
56 | |
57 | // global list of KMime::Codec's |
58 | //@cond PRIVATE |
59 | KAutoDeleteHash<QByteArray, Codec> *Codec::all = 0; |
60 | K_GLOBAL_STATIC( QMutex, dictLock ) |
61 | //@endcond |
62 | |
63 | void Codec::cleanupCodec() |
64 | { |
65 | delete all; |
66 | all = 0; |
67 | } |
68 | |
69 | void 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 | |
82 | Codec *Codec::codecForName( const char *name ) |
83 | { |
84 | const QByteArray ba( name ); |
85 | return codecForName( ba ); |
86 | } |
87 | |
88 | Codec *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 | |
108 | bool 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 | |
137 | QByteArray 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 | |
160 | QByteArray 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 | |
183 | bool 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. |
214 | bool 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 | |