00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "kmime_util.h"
00025 #include "kmime_util_p.h"
00026 #include "kmime_header_parsing.h"
00027
00028 #include <config-kmime.h>
00029 #include <kdefakes.h>
00030 #include <kglobal.h>
00031 #include <klocale.h>
00032 #include <kcharsets.h>
00033 #include <kcodecs.h>
00034
00035 #include <QtCore/QList>
00036 #include <QtCore/QString>
00037 #include <QtCore/QTextCodec>
00038
00039 #include <ctype.h>
00040 #include <time.h>
00041 #include <stdlib.h>
00042 #include <unistd.h>
00043
00044 using namespace KMime;
00045
00046 namespace KMime {
00047
00048 QList<QByteArray> c_harsetCache;
00049 QList<QByteArray> l_anguageCache;
00050
00051 QByteArray cachedCharset( const QByteArray &name )
00052 {
00053 foreach ( QByteArray charset, c_harsetCache ) {
00054 if ( qstricmp( name.data(), charset.data() ) == 0 ) {
00055 return charset;
00056 }
00057 }
00058
00059 c_harsetCache.append( name.toUpper() );
00060
00061 return c_harsetCache.last();
00062 }
00063
00064 QByteArray cachedLanguage( const QByteArray &name )
00065 {
00066 foreach ( QByteArray language, l_anguageCache ) {
00067 if ( qstricmp( name.data(), language.data() ) == 0 ) {
00068 return language;
00069 }
00070 }
00071
00072 l_anguageCache.append( name.toUpper() );
00073
00074 return l_anguageCache.last();
00075 }
00076
00077 bool isUsAscii( const QString &s )
00078 {
00079 uint sLength = s.length();
00080 for ( uint i=0; i<sLength; i++ ) {
00081 if ( s.at( i ).toLatin1() <= 0 ) {
00082 return false;
00083 }
00084 }
00085 return true;
00086 }
00087
00088
00089 const uchar specialsMap[16] = {
00090 0x00, 0x00, 0x00, 0x00,
00091 0x20, 0xCA, 0x00, 0x3A,
00092 0x80, 0x00, 0x00, 0x1C,
00093 0x00, 0x00, 0x00, 0x00
00094 };
00095
00096
00097 const uchar tSpecialsMap[16] = {
00098 0x00, 0x00, 0x00, 0x00,
00099 0x20, 0xC9, 0x00, 0x3F,
00100 0x80, 0x00, 0x00, 0x1C,
00101 0x00, 0x00, 0x00, 0x00
00102 };
00103
00104
00105 const uchar aTextMap[16] = {
00106 0x00, 0x00, 0x00, 0x00,
00107 0x5F, 0x35, 0xFF, 0xC5,
00108 0x7F, 0xFF, 0xFF, 0xE3,
00109 0xFF, 0xFF, 0xFF, 0xFE
00110 };
00111
00112
00113 const uchar tTextMap[16] = {
00114 0x00, 0x00, 0x00, 0x00,
00115 0x5F, 0x36, 0xFF, 0xC0,
00116 0x7F, 0xFF, 0xFF, 0xE3,
00117 0xFF, 0xFF, 0xFF, 0xFE
00118 };
00119
00120
00121 const uchar eTextMap[16] = {
00122 0x00, 0x00, 0x00, 0x00,
00123 0x40, 0x35, 0xFF, 0xC0,
00124 0x7F, 0xFF, 0xFF, 0xE0,
00125 0x7F, 0xFF, 0xFF, 0xE0
00126 };
00127
00128 QString decodeRFC2047String( const QByteArray &src, QByteArray &usedCS,
00129 const QByteArray &defaultCS, bool forceCS )
00130 {
00131 QString result;
00132 QByteArray spaceBuffer;
00133 const char *scursor = src.constData();
00134 const char *send = scursor + src.length();
00135 bool onlySpacesSinceLastWord = false;
00136
00137 while ( scursor != send ) {
00138
00139 if ( isspace( *scursor ) && onlySpacesSinceLastWord ) {
00140 spaceBuffer += *scursor++;
00141 continue;
00142 }
00143
00144
00145 if ( *scursor == '=' ) {
00146 QByteArray language;
00147 QString decoded;
00148 ++scursor;
00149 const char *start = scursor;
00150 if ( HeaderParsing::parseEncodedWord( scursor, send, decoded, language, usedCS, defaultCS, forceCS ) ) {
00151 result += decoded;
00152 onlySpacesSinceLastWord = true;
00153 spaceBuffer.clear();
00154 } else {
00155 if ( onlySpacesSinceLastWord ) {
00156 result += QString::fromLatin1( spaceBuffer );
00157 onlySpacesSinceLastWord = false;
00158 }
00159 result += QLatin1Char( '=' );
00160 scursor = start;
00161 }
00162 continue;
00163 } else {
00164
00165 if ( onlySpacesSinceLastWord ) {
00166 result += QString::fromLatin1( spaceBuffer );
00167 onlySpacesSinceLastWord = false;
00168 }
00169 result += QLatin1Char( *scursor );
00170 ++scursor;
00171 }
00172 }
00173
00174 return result;
00175 }
00176
00177 QByteArray encodeRFC2047String( const QString &src, const QByteArray &charset,
00178 bool addressHeader, bool allow8BitHeaders )
00179 {
00180 QByteArray encoded8Bit, result, usedCS;
00181 int start=0, end=0;
00182 bool nonAscii=false, ok=true, useQEncoding=false;
00183 QTextCodec *codec=0;
00184
00185 usedCS = charset;
00186 codec = KGlobal::charsets()->codecForName( usedCS, ok );
00187
00188 if ( !ok ) {
00189
00190 usedCS = KGlobal::locale()->encoding();
00191 codec = KGlobal::charsets()->codecForName( usedCS, ok );
00192 }
00193
00194 if ( usedCS.contains( "8859-" ) ) {
00195 useQEncoding = true;
00196 }
00197
00198 encoded8Bit = codec->fromUnicode( src );
00199
00200 if ( allow8BitHeaders ) {
00201 return encoded8Bit;
00202 }
00203
00204 uint encoded8BitLength = encoded8Bit.length();
00205 for ( unsigned int i=0; i<encoded8BitLength; i++ ) {
00206 if ( encoded8Bit[i] == ' ' ) {
00207 start = i + 1;
00208 }
00209
00210
00211 if ( ( (signed char)encoded8Bit[i] < 0 ) || ( encoded8Bit[i] == '\033' ) ||
00212 ( addressHeader && ( strchr( "\"()<>@,.;:\\[]=", encoded8Bit[i] ) != 0 ) ) ) {
00213 end = start;
00214 nonAscii = true;
00215 break;
00216 }
00217 }
00218
00219 if ( nonAscii ) {
00220 while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00221
00222 end++;
00223 }
00224
00225 for ( int x=end; x<encoded8Bit.length(); x++ ) {
00226 if ( ( (signed char)encoded8Bit[x]<0) || ( encoded8Bit[x] == '\033' ) ||
00227 ( addressHeader && ( strchr("\"()<>@,.;:\\[]=",encoded8Bit[x]) != 0 ) ) ) {
00228 end = encoded8Bit.length();
00229
00230 while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00231
00232 end++;
00233 }
00234 }
00235 }
00236
00237 result = encoded8Bit.left( start ) + "=?" + usedCS;
00238
00239 if ( useQEncoding ) {
00240 result += "?Q?";
00241
00242 char c, hexcode;
00243 for ( int i=start; i<end; i++ ) {
00244 c = encoded8Bit[i];
00245 if ( c == ' ' ) {
00246 result += '_';
00247 } else {
00248 if ( ( ( c >= 'a' ) && ( c <= 'z' ) ) ||
00249 ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
00250 ( ( c >= '0' ) && ( c <= '9' ) ) ) {
00251 result += c;
00252 } else {
00253 result += '=';
00254 hexcode = ((c & 0xF0) >> 4) + 48;
00255 if ( hexcode >= 58 ) {
00256 hexcode += 7;
00257 }
00258 result += hexcode;
00259 hexcode = (c & 0x0F) + 48;
00260 if ( hexcode >= 58 ) {
00261 hexcode += 7;
00262 }
00263 result += hexcode;
00264 }
00265 }
00266 }
00267 } else {
00268 result += "?B?" + encoded8Bit.mid( start, end - start ).toBase64();
00269 }
00270
00271 result +="?=";
00272 result += encoded8Bit.right( encoded8Bit.length() - end );
00273 } else {
00274 result = encoded8Bit;
00275 }
00276
00277 return result;
00278 }
00279
00280 QByteArray uniqueString()
00281 {
00282 static char chars[] = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00283 time_t now;
00284 char p[11];
00285 int pos, ran;
00286 unsigned int timeval;
00287
00288 p[10] = '\0';
00289 now = time( 0 );
00290 ran = 1 + (int)(1000.0*rand() / (RAND_MAX + 1.0));
00291 timeval = (now / ran) + getpid();
00292
00293 for ( int i=0; i<10; i++ ) {
00294 pos = (int) (61.0*rand() / (RAND_MAX + 1.0));
00295
00296 p[i] = chars[pos];
00297 }
00298
00299 QByteArray ret;
00300 ret.setNum( timeval );
00301 ret += '.';
00302 ret += p;
00303
00304 return ret;
00305 }
00306
00307 QByteArray multiPartBoundary()
00308 {
00309 return "nextPart" + uniqueString();
00310 }
00311
00312 QByteArray unfoldHeader( const QByteArray &header )
00313 {
00314 QByteArray result;
00315 int pos = 0, foldBegin = 0, foldMid = 0, foldEnd = 0;
00316 while ( ( foldMid = header.indexOf( '\n', pos ) ) >= 0 ) {
00317 foldBegin = foldEnd = foldMid;
00318
00319 while ( foldBegin > 0 ) {
00320 if ( !QChar( header[foldBegin - 1] ).isSpace() ) {
00321 break;
00322 }
00323 --foldBegin;
00324 }
00325
00326 while ( foldEnd < header.length() - 1 ) {
00327 if ( !QChar( header[foldEnd] ).isSpace() ) {
00328 break;
00329 }
00330 ++foldEnd;
00331 }
00332 result += header.mid( pos, foldBegin - pos );
00333 result += ' ';
00334 pos = foldEnd;
00335 }
00336 result += header.mid( pos, header.length() - pos );
00337 return result;
00338 }
00339
00340 int indexOfHeader( const QByteArray &src, const QByteArray &name, int &end, int &dataBegin, bool *folded )
00341 {
00342 QByteArray n = name;
00343 n.append( ':' );
00344 int begin = -1;
00345
00346 if ( qstrnicmp( n.constData(), src.constData(), n.length() ) == 0 ) {
00347 begin = 0;
00348 } else {
00349 n.prepend('\n');
00350 const char *p = strcasestr( src.constData(), n.constData() );
00351 if ( !p ) {
00352 begin = -1;
00353 } else {
00354 begin = p - src.constData();
00355 ++begin;
00356 }
00357 }
00358
00359 if ( begin > -1) {
00360 dataBegin = begin + name.length() + 1;
00361
00362 if ( src.at( dataBegin ) == ' ' ) {
00363 ++dataBegin;
00364 }
00365 end = dataBegin;
00366 int len = src.length() - 1;
00367 if ( folded )
00368 *folded = false;
00369
00370 if ( src.at(end) != '\n' ) {
00371 while ( true ) {
00372 end = src.indexOf( '\n', end + 1 );
00373 if ( end == -1 || end == len ||
00374 ( src[end+1] != ' ' && src[end+1] != '\t' ) ) {
00375
00376 break;
00377 } else {
00378 if ( folded )
00379 *folded = true;
00380 }
00381 }
00382 }
00383
00384 if ( end < 0 ) {
00385 end = len + 1;
00386 }
00387 return begin;
00388
00389 } else {
00390 dataBegin = -1;
00391 return -1;
00392 }
00393 }
00394
00395 QByteArray extractHeader( const QByteArray &src, const QByteArray &name )
00396 {
00397 int begin, end;
00398 bool folded;
00399 indexOfHeader( src, name, end, begin, &folded );
00400
00401 if ( begin >= 0 ) {
00402 if ( !folded ) {
00403 return src.mid( begin, end - begin );
00404 } else {
00405 QByteArray hdrValue = src.mid( begin, end - begin );
00406 return unfoldHeader( hdrValue );
00407 }
00408 } else {
00409 return QByteArray();
00410 }
00411 }
00412
00413 void removeHeader( QByteArray &header, const QByteArray &name )
00414 {
00415 int begin, end, dummy;
00416 begin = indexOfHeader( header, name, end, dummy );
00417 if ( begin >= 0 ) {
00418 header.remove( begin, end - begin + 1 );
00419 }
00420 }
00421
00422 QByteArray CRLFtoLF( const QByteArray &s )
00423 {
00424 QByteArray ret = s;
00425 ret.replace( "\r\n", "\n" );
00426 return ret;
00427 }
00428
00429 QByteArray LFtoCRLF( const QByteArray &s )
00430 {
00431 QByteArray ret = s;
00432 ret.replace( "\n", "\r\n" );
00433 return ret;
00434 }
00435
00436 namespace {
00437 template < typename T > void removeQuotesGeneric( T & str )
00438 {
00439 bool inQuote = false;
00440 for ( int i = 0; i < str.length(); ++i ) {
00441 if ( str[i] == '"' ) {
00442 str.remove( i, 1 );
00443 i--;
00444 inQuote = !inQuote;
00445 } else {
00446 if ( inQuote && ( str[i] == '\\' ) ) {
00447 str.remove( i, 1 );
00448 }
00449 }
00450 }
00451 }
00452 }
00453
00454 void removeQuots( QByteArray &str )
00455 {
00456 removeQuotesGeneric( str );
00457 }
00458
00459 void removeQuots( QString &str )
00460 {
00461 removeQuotesGeneric( str );
00462 }
00463
00464 void addQuotes( QByteArray &str, bool forceQuotes )
00465 {
00466 bool needsQuotes=false;
00467 for ( int i=0; i < str.length(); i++ ) {
00468 if ( strchr("()<>@,.;:[]=\\\"", str[i] ) != 0 ) {
00469 needsQuotes = true;
00470 }
00471 if ( str[i] == '\\' || str[i] == '\"' ) {
00472 str.insert( i, '\\' );
00473 i++;
00474 }
00475 }
00476
00477 if ( needsQuotes || forceQuotes ) {
00478 str.insert( 0, '\"' );
00479 str.append( "\"" );
00480 }
00481 }
00482
00483 }