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 ( const 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 ( const 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 QByteArray 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.toUtf8();
00152 onlySpacesSinceLastWord = true;
00153 spaceBuffer.clear();
00154 } else {
00155 if ( onlySpacesSinceLastWord ) {
00156 result += spaceBuffer;
00157 onlySpacesSinceLastWord = false;
00158 }
00159 result += '=';
00160 scursor = start;
00161 }
00162 continue;
00163 } else {
00164
00165 if ( onlySpacesSinceLastWord ) {
00166 result += spaceBuffer;
00167 onlySpacesSinceLastWord = false;
00168 }
00169 result += *scursor;
00170 ++scursor;
00171 }
00172 }
00173
00174 return QString::fromUtf8(result);
00175 }
00176
00177 QString decodeRFC2047String( const QByteArray &src )
00178 {
00179 QByteArray usedCS;
00180 return decodeRFC2047String( src, usedCS, "utf-8", false );
00181 }
00182
00183 QByteArray encodeRFC2047String( const QString &src, const QByteArray &charset,
00184 bool addressHeader, bool allow8BitHeaders )
00185 {
00186 QByteArray encoded8Bit, result, usedCS;
00187 int start=0, end=0;
00188 bool nonAscii=false, ok=true, useQEncoding=false;
00189 QTextCodec *codec=0;
00190
00191 usedCS = charset;
00192 codec = KGlobal::charsets()->codecForName( usedCS, ok );
00193
00194 if ( !ok ) {
00195
00196 usedCS = KGlobal::locale()->encoding();
00197 codec = KGlobal::charsets()->codecForName( usedCS, ok );
00198 }
00199
00200 if ( usedCS.contains( "8859-" ) ) {
00201 useQEncoding = true;
00202 }
00203
00204 encoded8Bit = codec->fromUnicode( src );
00205
00206 if ( allow8BitHeaders ) {
00207 return encoded8Bit;
00208 }
00209
00210 uint encoded8BitLength = encoded8Bit.length();
00211 for ( unsigned int i=0; i<encoded8BitLength; i++ ) {
00212 if ( encoded8Bit[i] == ' ' ) {
00213 start = i + 1;
00214 }
00215
00216
00217 if ( ( (signed char)encoded8Bit[i] < 0 ) || ( encoded8Bit[i] == '\033' ) ||
00218 ( addressHeader && ( strchr( "\"()<>@,.;:\\[]=", encoded8Bit[i] ) != 0 ) ) ) {
00219 end = start;
00220 nonAscii = true;
00221 break;
00222 }
00223 }
00224
00225 if ( nonAscii ) {
00226 while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00227
00228 end++;
00229 }
00230
00231 for ( int x=end; x<encoded8Bit.length(); x++ ) {
00232 if ( ( (signed char)encoded8Bit[x]<0) || ( encoded8Bit[x] == '\033' ) ||
00233 ( addressHeader && ( strchr("\"()<>@,.;:\\[]=",encoded8Bit[x]) != 0 ) ) ) {
00234 end = encoded8Bit.length();
00235
00236 while ( ( end < encoded8Bit.length() ) && ( encoded8Bit[end] != ' ' ) ) {
00237
00238 end++;
00239 }
00240 }
00241 }
00242
00243 result = encoded8Bit.left( start ) + "=?" + usedCS;
00244
00245 if ( useQEncoding ) {
00246 result += "?Q?";
00247
00248 char c, hexcode;
00249 for ( int i=start; i<end; i++ ) {
00250 c = encoded8Bit[i];
00251 if ( c == ' ' ) {
00252 result += '_';
00253 } else {
00254 if ( ( ( c >= 'a' ) && ( c <= 'z' ) ) ||
00255 ( ( c >= 'A' ) && ( c <= 'Z' ) ) ||
00256 ( ( c >= '0' ) && ( c <= '9' ) ) ) {
00257 result += c;
00258 } else {
00259 result += '=';
00260 hexcode = ((c & 0xF0) >> 4) + 48;
00261 if ( hexcode >= 58 ) {
00262 hexcode += 7;
00263 }
00264 result += hexcode;
00265 hexcode = (c & 0x0F) + 48;
00266 if ( hexcode >= 58 ) {
00267 hexcode += 7;
00268 }
00269 result += hexcode;
00270 }
00271 }
00272 }
00273 } else {
00274 result += "?B?" + encoded8Bit.mid( start, end - start ).toBase64();
00275 }
00276
00277 result +="?=";
00278 result += encoded8Bit.right( encoded8Bit.length() - end );
00279 } else {
00280 result = encoded8Bit;
00281 }
00282
00283 return result;
00284 }
00285
00286 QByteArray uniqueString()
00287 {
00288 static char chars[] = "0123456789abcdefghijklmnopqrstuvxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
00289 time_t now;
00290 char p[11];
00291 int pos, ran;
00292 unsigned int timeval;
00293
00294 p[10] = '\0';
00295 now = time( 0 );
00296 ran = 1 + (int)(1000.0*rand() / (RAND_MAX + 1.0));
00297 timeval = (now / ran) + getpid();
00298
00299 for ( int i=0; i<10; i++ ) {
00300 pos = (int) (61.0*rand() / (RAND_MAX + 1.0));
00301
00302 p[i] = chars[pos];
00303 }
00304
00305 QByteArray ret;
00306 ret.setNum( timeval );
00307 ret += '.';
00308 ret += p;
00309
00310 return ret;
00311 }
00312
00313 QByteArray multiPartBoundary()
00314 {
00315 return "nextPart" + uniqueString();
00316 }
00317
00318 QByteArray unfoldHeader( const QByteArray &header )
00319 {
00320 QByteArray result;
00321 int pos = 0, foldBegin = 0, foldMid = 0, foldEnd = 0;
00322 while ( ( foldMid = header.indexOf( '\n', pos ) ) >= 0 ) {
00323 foldBegin = foldEnd = foldMid;
00324
00325 while ( foldBegin > 0 ) {
00326 if ( !QChar( header[foldBegin - 1] ).isSpace() ) {
00327 break;
00328 }
00329 --foldBegin;
00330 }
00331
00332 while ( foldEnd <= header.length() - 1 ) {
00333 if ( !QChar( header[foldEnd] ).isSpace() ) {
00334 break;
00335 }
00336 ++foldEnd;
00337 }
00338 result += header.mid( pos, foldBegin - pos );
00339 if ( foldEnd < header.length() -1 )
00340 result += ' ';
00341 pos = foldEnd;
00342 }
00343 result += header.mid( pos, header.length() - pos );
00344 return result;
00345 }
00346
00347 int indexOfHeader( const QByteArray &src, const QByteArray &name, int &end, int &dataBegin, bool *folded )
00348 {
00349 QByteArray n = name;
00350 n.append( ':' );
00351 int begin = -1;
00352
00353 if ( qstrnicmp( n.constData(), src.constData(), n.length() ) == 0 ) {
00354 begin = 0;
00355 } else {
00356 n.prepend('\n');
00357 const char *p = strcasestr( src.constData(), n.constData() );
00358 if ( !p ) {
00359 begin = -1;
00360 } else {
00361 begin = p - src.constData();
00362 ++begin;
00363 }
00364 }
00365
00366 if ( begin > -1) {
00367 dataBegin = begin + name.length() + 1;
00368
00369 if ( src.at( dataBegin ) == ' ' ) {
00370 ++dataBegin;
00371 }
00372 end = dataBegin;
00373 int len = src.length() - 1;
00374 if ( folded )
00375 *folded = false;
00376
00377 if ( src.at(end) != '\n' ) {
00378 while ( true ) {
00379 end = src.indexOf( '\n', end + 1 );
00380 if ( end == -1 || end == len ||
00381 ( src[end+1] != ' ' && src[end+1] != '\t' ) ) {
00382
00383 break;
00384 } else {
00385 if ( folded )
00386 *folded = true;
00387 }
00388 }
00389 }
00390
00391 if ( end < 0 ) {
00392 end = len + 1;
00393 }
00394 return begin;
00395
00396 } else {
00397 dataBegin = -1;
00398 return -1;
00399 }
00400 }
00401
00402 QByteArray extractHeader( const QByteArray &src, const QByteArray &name )
00403 {
00404 int begin, end;
00405 bool folded;
00406 indexOfHeader( src, name, end, begin, &folded );
00407
00408 if ( begin >= 0 ) {
00409 if ( !folded ) {
00410 return src.mid( begin, end - begin );
00411 } else {
00412 QByteArray hdrValue = src.mid( begin, end - begin );
00413 return unfoldHeader( hdrValue );
00414 }
00415 } else {
00416 return QByteArray();
00417 }
00418 }
00419
00420 QList<QByteArray> extractHeaders( const QByteArray &src, const QByteArray &name )
00421 {
00422 int begin, end;
00423 bool folded;
00424 QList<QByteArray> result;
00425 QByteArray copySrc( src );
00426
00427 indexOfHeader( copySrc, name, end, begin, &folded );
00428 while ( begin >= 0 ) {
00429 if ( !folded ) {
00430 result.append( copySrc.mid( begin, end - begin ) );
00431 } else {
00432 QByteArray hdrValue = copySrc.mid( begin, end - begin );
00433 result.append( unfoldHeader( hdrValue ) );
00434 }
00435
00436
00437 copySrc = copySrc.mid( end );
00438 indexOfHeader( copySrc, name, end, begin, &folded );
00439 }
00440
00441 return result;
00442 }
00443
00444 void removeHeader( QByteArray &header, const QByteArray &name )
00445 {
00446 int begin, end, dummy;
00447 begin = indexOfHeader( header, name, end, dummy );
00448 if ( begin >= 0 ) {
00449 header.remove( begin, end - begin + 1 );
00450 }
00451 }
00452
00453 QByteArray CRLFtoLF( const QByteArray &s )
00454 {
00455 QByteArray ret = s;
00456 ret.replace( "\r\n", "\n" );
00457 return ret;
00458 }
00459
00460 QByteArray LFtoCRLF( const QByteArray &s )
00461 {
00462 QByteArray ret = s;
00463 ret.replace( "\n", "\r\n" );
00464 return ret;
00465 }
00466
00467 namespace {
00468 template < typename T > void removeQuotesGeneric( T & str )
00469 {
00470 bool inQuote = false;
00471 for ( int i = 0; i < str.length(); ++i ) {
00472 if ( str[i] == '"' ) {
00473 str.remove( i, 1 );
00474 i--;
00475 inQuote = !inQuote;
00476 } else {
00477 if ( inQuote && ( str[i] == '\\' ) ) {
00478 str.remove( i, 1 );
00479 }
00480 }
00481 }
00482 }
00483 }
00484
00485 void removeQuots( QByteArray &str )
00486 {
00487 removeQuotesGeneric( str );
00488 }
00489
00490 void removeQuots( QString &str )
00491 {
00492 removeQuotesGeneric( str );
00493 }
00494
00495 void addQuotes( QByteArray &str, bool forceQuotes )
00496 {
00497 bool needsQuotes=false;
00498 for ( int i=0; i < str.length(); i++ ) {
00499 if ( strchr("()<>@,.;:[]=\\\"", str[i] ) != 0 ) {
00500 needsQuotes = true;
00501 }
00502 if ( str[i] == '\\' || str[i] == '\"' ) {
00503 str.insert( i, '\\' );
00504 i++;
00505 }
00506 }
00507
00508 if ( needsQuotes || forceQuotes ) {
00509 str.insert( 0, '\"' );
00510 str.append( "\"" );
00511 }
00512 }
00513
00514 }