00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "kmime_header_parsing.h"
00024
00025 #include "kmime_codecs.h"
00026 #include "kmime_headerfactory_p.h"
00027 #include "kmime_headers.h"
00028 #include "kmime_util.h"
00029 #include "kmime_util_p.h"
00030 #include "kmime_dateformatter.h"
00031 #include "kmime_warning.h"
00032
00033 #include <kglobal.h>
00034 #include <kcharsets.h>
00035
00036 #include <QtCore/QTextCodec>
00037 #include <QtCore/QMap>
00038 #include <QtCore/QStringList>
00039 #include <QtCore/QUrl>
00040
00041 #include <ctype.h>
00042 #include <cassert>
00043
00044 using namespace KMime;
00045 using namespace KMime::Types;
00046
00047 namespace KMime {
00048
00049 namespace Types {
00050
00051
00052
00053 static inline QString QUrl_fromAce_wrapper( const QString & domain )
00054 {
00055 if ( domain.contains( QLatin1String( "xn--" ) ) )
00056 return QUrl::fromAce( domain.toLatin1() );
00057 else
00058 return domain;
00059 }
00060
00061 static QString addr_spec_as_string( const AddrSpec & as, bool pretty )
00062 {
00063 if ( as.isEmpty() ) {
00064 return QString();
00065 }
00066
00067 static QChar dotChar = QLatin1Char( '.' );
00068
00069 bool needsQuotes = false;
00070 QString result;
00071 result.reserve( as.localPart.length() + as.domain.length() + 1 );
00072 for ( int i = 0 ; i < as.localPart.length() ; ++i ) {
00073 const QChar ch = as.localPart.at( i );
00074 if ( ch == dotChar || isAText( ch.toLatin1() ) ) {
00075 result += ch;
00076 } else {
00077 needsQuotes = true;
00078 if ( ch == QLatin1Char( '\\' ) || ch == QLatin1Char( '"' ) ) {
00079 result += QLatin1Char( '\\' );
00080 }
00081 result += ch;
00082 }
00083 }
00084 const QString dom = pretty ? QUrl_fromAce_wrapper( as.domain ) : as.domain ;
00085 if ( needsQuotes ) {
00086 result = QLatin1Char( '"' ) + result + QLatin1Char( '"' );
00087 }
00088 if( dom.isEmpty() ) {
00089 return result;
00090 } else {
00091 result += QLatin1Char( '@' );
00092 result += dom;
00093 return result;
00094 }
00095 }
00096
00097 QString AddrSpec::asString() const
00098 {
00099 return addr_spec_as_string( *this, false );
00100 }
00101
00102 QString AddrSpec::asPrettyString() const
00103 {
00104 return addr_spec_as_string( *this, true );
00105 }
00106
00107 bool AddrSpec::isEmpty() const
00108 {
00109 return localPart.isEmpty() && domain.isEmpty();
00110 }
00111
00112 QByteArray Mailbox::address() const
00113 {
00114 return mAddrSpec.asString().toLatin1();
00115 }
00116
00117 AddrSpec Mailbox::addrSpec() const
00118 {
00119 return mAddrSpec;
00120 }
00121
00122 QString Mailbox::name() const
00123 {
00124 return mDisplayName;
00125 }
00126
00127 void Mailbox::setAddress( const AddrSpec &addr )
00128 {
00129 mAddrSpec = addr;
00130 }
00131
00132 void Mailbox::setAddress( const QByteArray &addr )
00133 {
00134 const char *cursor = addr.constData();
00135 if ( !HeaderParsing::parseAngleAddr( cursor,
00136 cursor + addr.length(), mAddrSpec ) ) {
00137 if ( !HeaderParsing::parseAddrSpec( cursor, cursor + addr.length(),
00138 mAddrSpec ) ) {
00139 kWarning() << "Invalid address";
00140 return;
00141 }
00142 }
00143 }
00144
00145 void Mailbox::setName( const QString &name )
00146 {
00147 mDisplayName = removeBidiControlChars( name );
00148 }
00149
00150 void Mailbox::setNameFrom7Bit( const QByteArray &name,
00151 const QByteArray &defaultCharset )
00152 {
00153 QByteArray cs;
00154 setName( decodeRFC2047String( name, cs, defaultCharset, false ) );
00155 }
00156
00157 bool Mailbox::hasAddress() const
00158 {
00159 return !mAddrSpec.isEmpty();
00160 }
00161
00162 bool Mailbox::hasName() const
00163 {
00164 return !mDisplayName.isEmpty();
00165 }
00166
00167 QString Mailbox::prettyAddress() const
00168 {
00169 return prettyAddress( QuoteNever );
00170 }
00171
00172 QString Mailbox::prettyAddress( Quoting quoting ) const
00173 {
00174 if ( !hasName() ) {
00175 return QLatin1String( address() );
00176 }
00177 QString s = name();
00178 if ( quoting != QuoteNever ) {
00179 addQuotes( s, quoting == QuoteAlways );
00180 }
00181
00182 if ( hasAddress() ) {
00183 s += QLatin1String(" <") + QLatin1String( address() ) + QLatin1Char('>');
00184 }
00185 return s;
00186 }
00187
00188 void Mailbox::fromUnicodeString( const QString &s )
00189 {
00190 from7BitString( encodeRFC2047String( s, "utf-8", false ) );
00191 }
00192
00193 void Mailbox::from7BitString( const QByteArray &s )
00194 {
00195 const char *cursor = s.constData();
00196 HeaderParsing::parseMailbox( cursor, cursor + s.length(), *this );
00197 }
00198
00199 QByteArray KMime::Types::Mailbox::as7BitString( const QByteArray &encCharset ) const
00200 {
00201 if ( !hasName() ) {
00202 return address();
00203 }
00204 QByteArray rv;
00205 if ( isUsAscii( name() ) ) {
00206 QByteArray tmp = name().toLatin1();
00207 addQuotes( tmp, false );
00208 rv += tmp;
00209 } else {
00210 rv += encodeRFC2047String( name(), encCharset, true );
00211 }
00212 if ( hasAddress() ) {
00213 rv += " <" + address() + '>';
00214 }
00215 return rv;
00216 }
00217
00218 }
00219
00220 namespace HeaderParsing {
00221
00222
00223 bool parseEncodedWord( const char* &scursor, const char * const send,
00224 QString &result, QByteArray &language,
00225 QByteArray &usedCS, const QByteArray &defaultCS,
00226 bool forceCS )
00227 {
00228
00229 assert( *(scursor-1) == '=' );
00230
00231
00232
00233
00234
00235
00236 char ch = *scursor++;
00237
00238 if ( ch != '?' ) {
00239
00240
00241 return false;
00242 }
00243
00244
00245
00246 const char * charsetStart = scursor;
00247 const char * languageStart = 0;
00248
00249
00250
00251 for ( ; scursor != send ; scursor++ ) {
00252 if ( *scursor == '?') {
00253 break;
00254 } else if ( *scursor == '*' && languageStart == 0 ) {
00255 languageStart = scursor + 1;
00256 }
00257 }
00258
00259
00260 if ( scursor == send || *scursor != '?' ) {
00261
00262 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
00263 return false;
00264 }
00265
00266
00267
00268 QByteArray maybeLanguage( languageStart, scursor - languageStart );
00269
00270
00271 QByteArray maybeCharset( charsetStart,
00272 ( languageStart ? languageStart - 1 : scursor ) - charsetStart );
00273
00274
00275
00276
00277
00278
00279
00280 scursor++;
00281 const char * encodingStart = scursor;
00282
00283
00284 for ( ; scursor != send ; scursor++ ) {
00285 if ( *scursor == '?' ) {
00286 break;
00287 }
00288 }
00289
00290
00291 if ( scursor == send || *scursor != '?' ) {
00292
00293 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
00294 return false;
00295 }
00296
00297
00298 QByteArray maybeEncoding( encodingStart, scursor - encodingStart );
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310 scursor++;
00311 const char * encodedTextStart = scursor;
00312
00313
00314 for ( ; scursor != send ; scursor++ ) {
00315 if ( *scursor == '?' ) {
00316 if ( scursor + 1 != send ) {
00317 if ( *( scursor + 1 ) != '=' ) {
00318 KMIME_WARN << "Stray '?' in q-encoded word, ignoring this.";
00319 continue;
00320 }
00321 else {
00322 scursor += 2;
00323 break;
00324 }
00325 }
00326 else {
00327 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
00328 return false;
00329 }
00330 }
00331 }
00332
00333 if ( *( scursor - 2 ) != '?' || *( scursor - 1 ) != '=' ||
00334 scursor < encodedTextStart + 2 ) {
00335 KMIME_WARN_PREMATURE_END_OF( EncodedWord );
00336 return false;
00337 }
00338
00339
00340 const char * const encodedTextEnd = scursor - 2;
00341
00342
00343
00344
00345
00346
00347
00348 Codec * codec = Codec::codecForName( maybeEncoding );
00349 if ( !codec ) {
00350 KMIME_WARN_UNKNOWN( Encoding, maybeEncoding );
00351 return false;
00352 }
00353
00354
00355 Decoder * dec = codec->makeDecoder();
00356 assert( dec );
00357
00358
00359 bool matchOK = false;
00360 QTextCodec *textCodec = 0;
00361 if ( forceCS || maybeCharset.isEmpty() ) {
00362 textCodec = KGlobal::charsets()->codecForName( QLatin1String( defaultCS ), matchOK );
00363 usedCS = cachedCharset( defaultCS );
00364 } else {
00365 textCodec = KGlobal::charsets()->codecForName( QLatin1String( maybeCharset ), matchOK );
00366 if ( !matchOK ) {
00367 textCodec = KGlobal::charsets()->codecForName( QLatin1String( defaultCS ), matchOK );
00368 usedCS = cachedCharset( defaultCS );
00369 } else {
00370 usedCS = cachedCharset( maybeCharset );
00371 }
00372 }
00373
00374 if ( !matchOK || !textCodec ) {
00375 KMIME_WARN_UNKNOWN( Charset, maybeCharset );
00376 delete dec;
00377 return false;
00378 };
00379
00380
00381
00382
00383 int encodedTextLength = encodedTextEnd - encodedTextStart;
00384 QByteArray buffer;
00385 buffer.resize( codec->maxDecodedSizeFor( encodedTextLength ) );
00386 char *bbegin = buffer.data();
00387 char *bend = bbegin + buffer.length();
00388
00389
00390
00391
00392
00393
00394 if ( !dec->decode( encodedTextStart, encodedTextEnd, bbegin, bend ) ) {
00395 KMIME_WARN << codec->name() << "codec lies about its maxDecodedSizeFor("
00396 << encodedTextLength << ")\nresult may be truncated";
00397 }
00398
00399 result = textCodec->toUnicode( buffer.data(), bbegin - buffer.data() );
00400
00401
00402
00403 delete dec;
00404 language = maybeLanguage;
00405
00406 return true;
00407 }
00408
00409 static inline void eatWhiteSpace( const char* &scursor, const char * const send )
00410 {
00411 while ( scursor != send &&
00412 ( *scursor == ' ' || *scursor == '\n' ||
00413 *scursor == '\t' || *scursor == '\r' ) )
00414 scursor++;
00415 }
00416
00417 bool parseAtom( const char * &scursor, const char * const send,
00418 QString &result, bool allow8Bit )
00419 {
00420 QPair<const char*,int> maybeResult;
00421
00422 if ( parseAtom( scursor, send, maybeResult, allow8Bit ) ) {
00423 result += QString::fromLatin1( maybeResult.first, maybeResult.second );
00424 return true;
00425 }
00426
00427 return false;
00428 }
00429
00430 bool parseAtom( const char * &scursor, const char * const send,
00431 QPair<const char*,int> &result, bool allow8Bit )
00432 {
00433 bool success = false;
00434 const char *start = scursor;
00435
00436 while ( scursor != send ) {
00437 signed char ch = *scursor++;
00438 if ( ch > 0 && isAText( ch ) ) {
00439
00440 success = true;
00441 } else if ( allow8Bit && ch < 0 ) {
00442
00443 KMIME_WARN_8BIT( ch );
00444 success = true;
00445 } else {
00446
00447
00448
00449 scursor--;
00450 break;
00451 }
00452 }
00453 result.first = start;
00454 result.second = scursor - start;
00455 return success;
00456 }
00457
00458
00459
00460 bool parseToken( const char * &scursor, const char * const send,
00461 QString &result, bool allow8Bit )
00462 {
00463 QPair<const char*,int> maybeResult;
00464
00465 if ( parseToken( scursor, send, maybeResult, allow8Bit ) ) {
00466 result += QString::fromLatin1( maybeResult.first, maybeResult.second );
00467 return true;
00468 }
00469
00470 return false;
00471 }
00472
00473 bool parseToken( const char * &scursor, const char * const send,
00474 QPair<const char*,int> &result, bool allow8Bit )
00475 {
00476 bool success = false;
00477 const char * start = scursor;
00478
00479 while ( scursor != send ) {
00480 signed char ch = *scursor++;
00481 if ( ch > 0 && isTText( ch ) ) {
00482
00483 success = true;
00484 } else if ( allow8Bit && ch < 0 ) {
00485
00486 KMIME_WARN_8BIT( ch );
00487 success = true;
00488 } else {
00489
00490
00491
00492 scursor--;
00493 break;
00494 }
00495 }
00496 result.first = start;
00497 result.second = scursor - start;
00498 return success;
00499 }
00500
00501 #define READ_ch_OR_FAIL if ( scursor == send ) { \
00502 KMIME_WARN_PREMATURE_END_OF( GenericQuotedString ); \
00503 return false; \
00504 } else { \
00505 ch = *scursor++; \
00506 }
00507
00508
00509
00510
00511
00512
00513
00514 bool parseGenericQuotedString( const char* &scursor, const char * const send,
00515 QString &result, bool isCRLF,
00516 const char openChar, const char closeChar )
00517 {
00518 char ch;
00519
00520
00521
00522
00523
00524
00525 assert( *(scursor-1) == openChar || *(scursor-1) == closeChar );
00526
00527 while ( scursor != send ) {
00528 ch = *scursor++;
00529
00530 if ( ch == closeChar || ch == openChar ) {
00531
00532
00533 return true;
00534 }
00535
00536 switch( ch ) {
00537 case '\\':
00538
00539 READ_ch_OR_FAIL;
00540 KMIME_WARN_IF_8BIT( ch );
00541 result += QLatin1Char( ch );
00542 break;
00543 case '\r':
00544
00545
00546
00547
00548
00549
00550
00551 READ_ch_OR_FAIL;
00552 if ( ch != '\n' ) {
00553
00554 KMIME_WARN_LONE( CR );
00555 result += QLatin1Char('\r');
00556 scursor--;
00557 } else {
00558
00559
00560 READ_ch_OR_FAIL;
00561 if ( ch == ' ' || ch == '\t' ) {
00562
00563
00564
00565 result += QLatin1Char( ch );
00566 } else {
00567
00568
00569
00570 KMIME_WARN_NON_FOLDING( CRLF );
00571 result += QLatin1String( "\r\n" );
00572
00573
00574
00575 scursor--;
00576 }
00577 }
00578 break;
00579 case '\n':
00580
00581
00582
00583
00584
00585
00586
00587 READ_ch_OR_FAIL;
00588 if ( !isCRLF && ( ch == ' ' || ch == '\t' ) ) {
00589
00590
00591 result += QLatin1Char( ch );
00592 } else {
00593
00594 KMIME_WARN_LONE( LF );
00595 result += QLatin1Char( '\n' );
00596
00597
00598 scursor--;
00599 }
00600 break;
00601 case '=':
00602 {
00603
00604
00605 if( scursor == send )
00606 break;
00607
00608 const char *oldscursor = scursor;
00609 QString tmp;
00610 QByteArray lang, charset;
00611 if( *scursor++ == '?' ) {
00612 --scursor;
00613 if( parseEncodedWord( scursor, send, tmp, lang, charset ) ) {
00614 result += tmp;
00615 break;
00616 } else {
00617 scursor = oldscursor;
00618 }
00619 } else {
00620 scursor = oldscursor;
00621 }
00622
00623 }
00624 default:
00625 KMIME_WARN_IF_8BIT( ch );
00626 result += QLatin1Char( ch );
00627 }
00628 }
00629
00630 return false;
00631 }
00632
00633
00634
00635
00636
00637 bool parseComment( const char* &scursor, const char * const send,
00638 QString &result, bool isCRLF, bool reallySave )
00639 {
00640 int commentNestingDepth = 1;
00641 const char *afterLastClosingParenPos = 0;
00642 QString maybeCmnt;
00643 const char *oldscursor = scursor;
00644
00645 assert( *(scursor-1) == '(' );
00646
00647 while ( commentNestingDepth ) {
00648 QString cmntPart;
00649 if ( parseGenericQuotedString( scursor, send, cmntPart, isCRLF, '(', ')' ) ) {
00650 assert( *(scursor-1) == ')' || *(scursor-1) == '(' );
00651
00652
00653 switch ( *(scursor-1) ) {
00654 case ')':
00655 if ( reallySave ) {
00656
00657 result += maybeCmnt;
00658 result += cmntPart;
00659 if ( commentNestingDepth > 1 ) {
00660
00661 result += QLatin1Char( ')' );
00662 }
00663 maybeCmnt.clear();
00664 }
00665 afterLastClosingParenPos = scursor;
00666 --commentNestingDepth;
00667 break;
00668 case '(':
00669 if ( reallySave ) {
00670
00671
00672 maybeCmnt += cmntPart;
00673 maybeCmnt += QLatin1Char( '(' );
00674 }
00675 ++commentNestingDepth;
00676 break;
00677 default: assert( 0 );
00678 }
00679 } else {
00680
00681 if ( afterLastClosingParenPos ) {
00682 scursor = afterLastClosingParenPos;
00683 } else {
00684 scursor = oldscursor;
00685 }
00686 return false;
00687 }
00688 }
00689
00690 return true;
00691 }
00692
00693
00694
00695 bool parsePhrase( const char* &scursor, const char * const send,
00696 QString &result, bool isCRLF )
00697 {
00698 enum {
00699 None, Phrase, Atom, EncodedWord, QuotedString
00700 } found = None;
00701
00702 QString tmp;
00703 QByteArray lang, charset;
00704 const char *successfullyParsed = 0;
00705
00706 const char *oldscursor;
00707
00708
00709 bool lastWasEncodedWord = false;
00710
00711 while ( scursor != send ) {
00712 char ch = *scursor++;
00713 switch ( ch ) {
00714 case '.':
00715 if ( found == None ) {
00716 --scursor;
00717 return false;
00718 } else {
00719 if ( scursor != send && ( *scursor == ' ' || *scursor == '\t' ) ) {
00720 result += QLatin1String( ". " );
00721 } else {
00722 result += QLatin1Char( '.' );
00723 }
00724 successfullyParsed = scursor;
00725 }
00726 break;
00727 case '"':
00728 tmp.clear();
00729 if ( parseGenericQuotedString( scursor, send, tmp, isCRLF, '"', '"' ) ) {
00730 successfullyParsed = scursor;
00731 assert( *(scursor-1) == '"' );
00732 switch ( found ) {
00733 case None:
00734 found = QuotedString;
00735 break;
00736 case Phrase:
00737 case Atom:
00738 case EncodedWord:
00739 case QuotedString:
00740 found = Phrase;
00741 result += QLatin1Char(' ');
00742 break;
00743 default:
00744 assert( 0 );
00745 }
00746 lastWasEncodedWord = false;
00747 result += tmp;
00748 } else {
00749
00750
00751
00752 if ( found == None ) {
00753 return false;
00754 } else {
00755 result += QLatin1Char(' ');
00756 result += tmp;
00757 return true;
00758 }
00759 }
00760 break;
00761 case '(':
00762
00763 tmp.clear();
00764 if ( parseComment( scursor, send, tmp, isCRLF,
00765 false ) ) {
00766 successfullyParsed = scursor;
00767 lastWasEncodedWord = false;
00768 } else {
00769 if ( found == None ) {
00770 return false;
00771 } else {
00772 scursor = successfullyParsed;
00773 return true;
00774 }
00775 }
00776 break;
00777 case '=':
00778 tmp.clear();
00779 oldscursor = scursor;
00780 lang.clear();
00781 charset.clear();
00782 if ( parseEncodedWord( scursor, send, tmp, lang, charset ) ) {
00783 successfullyParsed = scursor;
00784 switch ( found ) {
00785 case None:
00786 found = EncodedWord;
00787 break;
00788 case Phrase:
00789 case EncodedWord:
00790 case Atom:
00791 case QuotedString:
00792 if ( !lastWasEncodedWord ) {
00793 result += QLatin1Char(' ');
00794 }
00795 found = Phrase;
00796 break;
00797 default: assert( 0 );
00798 }
00799 lastWasEncodedWord = true;
00800 result += tmp;
00801 break;
00802 } else {
00803
00804 scursor = oldscursor;
00805 }
00806
00807
00808 default:
00809 tmp.clear();
00810 scursor--;
00811 if ( parseAtom( scursor, send, tmp, true ) ) {
00812 successfullyParsed = scursor;
00813 switch ( found ) {
00814 case None:
00815 found = Atom;
00816 break;
00817 case Phrase:
00818 case Atom:
00819 case EncodedWord:
00820 case QuotedString:
00821 found = Phrase;
00822 result += QLatin1Char(' ');
00823 break;
00824 default:
00825 assert( 0 );
00826 }
00827 lastWasEncodedWord = false;
00828 result += tmp;
00829 } else {
00830 if ( found == None ) {
00831 return false;
00832 } else {
00833 scursor = successfullyParsed;
00834 return true;
00835 }
00836 }
00837 }
00838 eatWhiteSpace( scursor, send );
00839 }
00840
00841 return found != None;
00842 }
00843
00844
00845 bool parseDotAtom( const char* &scursor, const char * const send,
00846 QString &result, bool isCRLF )
00847 {
00848 eatCFWS( scursor, send, isCRLF );
00849
00850
00851 const char *successfullyParsed;
00852
00853 QString tmp;
00854 if ( !parseAtom( scursor, send, tmp, false ) ) {
00855 return false;
00856 }
00857 result += tmp;
00858 successfullyParsed = scursor;
00859
00860 while ( scursor != send ) {
00861
00862
00863 if ( scursor == send || *scursor != '.' ) {
00864 return true;
00865 }
00866 scursor++;
00867
00868 if ( scursor == send || !isAText( *scursor ) ) {
00869
00870
00871
00872 scursor = successfullyParsed;
00873 return true;
00874 }
00875
00876
00877 QString maybeAtom;
00878 if ( !parseAtom( scursor, send, maybeAtom, false ) ) {
00879 scursor = successfullyParsed;
00880 return true;
00881 }
00882
00883 result += QLatin1Char('.');
00884 result += maybeAtom;
00885 successfullyParsed = scursor;
00886 }
00887
00888 scursor = successfullyParsed;
00889 return true;
00890 }
00891
00892 void eatCFWS( const char* &scursor, const char * const send, bool isCRLF )
00893 {
00894 QString dummy;
00895
00896 while ( scursor != send ) {
00897 const char *oldscursor = scursor;
00898
00899 char ch = *scursor++;
00900
00901 switch( ch ) {
00902 case ' ':
00903 case '\t':
00904 case '\r':
00905 case '\n':
00906 continue;
00907
00908 case '(':
00909 if ( parseComment( scursor, send, dummy, isCRLF, false ) ) {
00910 continue;
00911 }
00912 scursor = oldscursor;
00913 return;
00914
00915 default:
00916 scursor = oldscursor;
00917 return;
00918 }
00919 }
00920 }
00921
00922 bool parseDomain( const char* &scursor, const char * const send,
00923 QString &result, bool isCRLF )
00924 {
00925 eatCFWS( scursor, send, isCRLF );
00926 if ( scursor == send ) {
00927 return false;
00928 }
00929
00930
00931
00932
00933
00934
00935
00936 if ( *scursor == '[' ) {
00937
00938 QString maybeDomainLiteral;
00939
00940 scursor++;
00941 while ( parseGenericQuotedString( scursor, send, maybeDomainLiteral,
00942 isCRLF, '[', ']' ) ) {
00943 if ( scursor == send ) {
00944
00945 if ( *(scursor-1) == ']' ) {
00946
00947 result = maybeDomainLiteral;
00948 return true;
00949 } else {
00950
00951 return false;
00952 }
00953 }
00954
00955
00956 if ( *(scursor-1) == '[' ) {
00957 maybeDomainLiteral += QLatin1Char('[');
00958 continue;
00959 }
00960
00961 result = maybeDomainLiteral;
00962 return true;
00963 }
00964 } else {
00965
00966 QString maybeDotAtom;
00967 if ( parseDotAtom( scursor, send, maybeDotAtom, isCRLF ) ) {
00968 result = maybeDotAtom;
00969
00970 if ( scursor != send && *scursor == '.' ) {
00971 result += QLatin1Char('.');
00972 scursor++;
00973 }
00974 return true;
00975 }
00976 }
00977 return false;
00978 }
00979
00980 bool parseObsRoute( const char* &scursor, const char* const send,
00981 QStringList &result, bool isCRLF, bool save )
00982 {
00983 while ( scursor != send ) {
00984 eatCFWS( scursor, send, isCRLF );
00985 if ( scursor == send ) {
00986 return false;
00987 }
00988
00989
00990 if ( *scursor == ',' ) {
00991 scursor++;
00992 if ( save ) {
00993 result.append( QString() );
00994 }
00995 continue;
00996 }
00997
00998
00999 if ( *scursor == ':' ) {
01000 scursor++;
01001 if ( save ) {
01002 result.append( QString() );
01003 }
01004 return true;
01005 }
01006
01007
01008 if ( *scursor != '@' ) {
01009 return false;
01010 } else {
01011 scursor++;
01012 }
01013
01014 QString maybeDomain;
01015 if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) {
01016 return false;
01017 }
01018 if ( save ) {
01019 result.append( maybeDomain );
01020 }
01021
01022
01023 eatCFWS( scursor, send, isCRLF );
01024 if ( scursor == send ) {
01025 return false;
01026 }
01027 if ( *scursor == ':' ) {
01028 scursor++;
01029 return true;
01030 }
01031 if ( *scursor == ',' ) {
01032 scursor++;
01033 }
01034 }
01035
01036 return false;
01037 }
01038
01039 bool parseAddrSpec( const char* &scursor, const char * const send,
01040 AddrSpec &result, bool isCRLF )
01041 {
01042
01043
01044
01045
01046
01047
01048
01049 QString maybeLocalPart;
01050 QString tmp;
01051
01052 while ( scursor != send ) {
01053
01054 eatCFWS( scursor, send, isCRLF );
01055
01056 char ch = *scursor++;
01057 switch ( ch ) {
01058 case '.':
01059 maybeLocalPart += QLatin1Char('.');
01060 break;
01061
01062 case '@':
01063 goto SAW_AT_SIGN;
01064 break;
01065
01066 case '"':
01067 tmp.clear();
01068 if ( parseGenericQuotedString( scursor, send, tmp, isCRLF, '"', '"' ) ) {
01069 maybeLocalPart += tmp;
01070 } else {
01071 return false;
01072 }
01073 break;
01074
01075 default:
01076 scursor--;
01077 tmp.clear();
01078 if ( parseAtom( scursor, send, tmp, false ) ) {
01079 maybeLocalPart += tmp;
01080 } else {
01081 return false;
01082 }
01083 break;
01084 }
01085 }
01086
01087 return false;
01088
01089
01090
01091
01092
01093
01094 SAW_AT_SIGN:
01095
01096 assert( *(scursor-1) == '@' );
01097
01098 QString maybeDomain;
01099 if ( !parseDomain( scursor, send, maybeDomain, isCRLF ) ) {
01100 return false;
01101 }
01102
01103 result.localPart = maybeLocalPart;
01104 result.domain = maybeDomain;
01105
01106 return true;
01107 }
01108
01109 bool parseAngleAddr( const char* &scursor, const char * const send,
01110 AddrSpec &result, bool isCRLF )
01111 {
01112
01113 eatCFWS( scursor, send, isCRLF );
01114 if ( scursor == send || *scursor != '<' ) {
01115 return false;
01116 }
01117 scursor++;
01118
01119 eatCFWS( scursor, send, isCRLF );
01120 if ( scursor == send ) {
01121 return false;
01122 }
01123
01124 if ( *scursor == '@' || *scursor == ',' ) {
01125
01126 KMIME_WARN << "obsolete source route found! ignoring.";
01127 QStringList dummy;
01128 if ( !parseObsRoute( scursor, send, dummy,
01129 isCRLF, false ) ) {
01130 return false;
01131 }
01132
01133 if ( scursor == send ) {
01134 return false;
01135 }
01136 }
01137
01138
01139 AddrSpec maybeAddrSpec;
01140 if ( !parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) {
01141 return false;
01142 }
01143
01144 eatCFWS( scursor, send, isCRLF );
01145 if ( scursor == send || *scursor != '>' ) {
01146 return false;
01147 }
01148 scursor++;
01149
01150 result = maybeAddrSpec;
01151 return true;
01152
01153 }
01154
01155 static QString stripQuotes( const QString &input )
01156 {
01157 const QLatin1Char quotes( '"' );
01158 if ( input.startsWith( quotes ) && input.endsWith( quotes ) ) {
01159 QString stripped( input.mid( 1, input.size() - 2 ) );
01160 return stripped;
01161 }
01162 else return input;
01163 }
01164
01165 bool parseMailbox( const char* &scursor, const char * const send,
01166 Mailbox &result, bool isCRLF )
01167 {
01168 eatCFWS( scursor, send, isCRLF );
01169 if ( scursor == send ) {
01170 return false;
01171 }
01172
01173 AddrSpec maybeAddrSpec;
01174 QString maybeDisplayName;
01175
01176
01177 const char * oldscursor = scursor;
01178 if ( parseAddrSpec( scursor, send, maybeAddrSpec, isCRLF ) ) {
01179 result.setAddress( maybeAddrSpec );
01180
01181 eatWhiteSpace( scursor, send );
01182 if ( scursor != send && *scursor == '(' ) {
01183 scursor++;
01184 if ( !parseComment( scursor, send, maybeDisplayName, isCRLF, true ) ) {
01185 return false;
01186 }
01187 }
01188 result.setName( stripQuotes( maybeDisplayName ) );
01189 return true;
01190 }
01191 scursor = oldscursor;
01192
01193
01194 if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) {
01195
01196 maybeDisplayName.clear();
01197 scursor = oldscursor;
01198 } else {
01199
01200 eatCFWS( scursor, send, isCRLF );
01201 if ( scursor == send ) {
01202 return false;
01203 }
01204 }
01205
01206
01207 if ( !parseAngleAddr( scursor, send, maybeAddrSpec, isCRLF ) ) {
01208 return false;
01209 }
01210
01211 if ( maybeDisplayName.isNull() ) {
01212
01213 eatWhiteSpace( scursor, send );
01214 if ( scursor != send && *scursor == '(' ) {
01215 scursor++;
01216 if ( !parseComment( scursor, send, maybeDisplayName, isCRLF, true ) ) {
01217 return false;
01218 }
01219 }
01220 }
01221
01222 result.setName( stripQuotes( maybeDisplayName ) );
01223 result.setAddress( maybeAddrSpec );
01224 return true;
01225 }
01226
01227 bool parseGroup( const char* &scursor, const char * const send,
01228 Address &result, bool isCRLF )
01229 {
01230
01231
01232
01233
01234
01235 eatCFWS( scursor, send, isCRLF );
01236 if ( scursor == send ) {
01237 return false;
01238 }
01239
01240
01241 QString maybeDisplayName;
01242 if ( !parsePhrase( scursor, send, maybeDisplayName, isCRLF ) ) {
01243 return false;
01244 }
01245
01246
01247 eatCFWS( scursor, send, isCRLF );
01248 if ( scursor == send || *scursor != ':' ) {
01249 return false;
01250 }
01251
01252
01253
01254 result.displayName = removeBidiControlChars( maybeDisplayName );
01255
01256
01257 scursor++;
01258 while ( scursor != send ) {
01259 eatCFWS( scursor, send, isCRLF );
01260 if ( scursor == send ) {
01261 return false;
01262 }
01263
01264
01265 if ( *scursor == ',' ) {
01266 scursor++;
01267 continue;
01268 }
01269
01270
01271 if ( *scursor == ';' ) {
01272 scursor++;
01273 return true;
01274 }
01275
01276 Mailbox maybeMailbox;
01277 if ( !parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
01278 return false;
01279 }
01280 result.mailboxList.append( maybeMailbox );
01281
01282 eatCFWS( scursor, send, isCRLF );
01283
01284 if ( scursor == send ) {
01285 return false;
01286 }
01287
01288 if ( *scursor == ';' ) {
01289 scursor++;
01290 return true;
01291 }
01292
01293 if ( *scursor == ',' ) {
01294 scursor++;
01295 }
01296 }
01297 return false;
01298 }
01299
01300 bool parseAddress( const char* &scursor, const char * const send,
01301 Address &result, bool isCRLF )
01302 {
01303
01304
01305 eatCFWS( scursor, send, isCRLF );
01306 if ( scursor == send ) {
01307 return false;
01308 }
01309
01310
01311 Mailbox maybeMailbox;
01312 const char * oldscursor = scursor;
01313 if ( parseMailbox( scursor, send, maybeMailbox, isCRLF ) ) {
01314
01315 result.displayName.clear();
01316 result.mailboxList.append( maybeMailbox );
01317 return true;
01318 }
01319 scursor = oldscursor;
01320
01321 Address maybeAddress;
01322
01323
01324 if ( !parseGroup( scursor, send, maybeAddress, isCRLF ) ) {
01325 return false;
01326 }
01327
01328 result = maybeAddress;
01329 return true;
01330 }
01331
01332 bool parseAddressList( const char* &scursor, const char * const send,
01333 AddressList &result, bool isCRLF )
01334 {
01335 while ( scursor != send ) {
01336 eatCFWS( scursor, send, isCRLF );
01337
01338 if ( scursor == send ) {
01339 return true;
01340 }
01341
01342 if ( *scursor == ',' ) {
01343 scursor++;
01344 continue;
01345 }
01346
01347 if ( *scursor == ';' ) {
01348 scursor++;
01349 continue;
01350 }
01351
01352
01353 Address maybeAddress;
01354 if ( !parseAddress( scursor, send, maybeAddress, isCRLF ) ) {
01355 return false;
01356 }
01357 result.append( maybeAddress );
01358
01359 eatCFWS( scursor, send, isCRLF );
01360
01361 if ( scursor == send ) {
01362 return true;
01363 }
01364
01365 if ( *scursor == ',' ) {
01366 scursor++;
01367 }
01368 }
01369 return true;
01370 }
01371
01372 static QString asterisk = QString::fromLatin1( "*0*", 1 );
01373 static QString asteriskZero = QString::fromLatin1( "*0*", 2 );
01374
01375
01376
01377
01378
01379 bool parseParameter( const char* &scursor, const char * const send,
01380 QPair<QString,QStringOrQPair> &result, bool isCRLF )
01381 {
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392 eatCFWS( scursor, send, isCRLF );
01393 if ( scursor == send ) {
01394 return false;
01395 }
01396
01397
01398
01399
01400
01401 QString maybeAttribute;
01402 if ( !parseToken( scursor, send, maybeAttribute, false ) ) {
01403 return false;
01404 }
01405
01406 eatCFWS( scursor, send, isCRLF );
01407
01408 if ( scursor == send || *scursor != '=' ) {
01409 return false;
01410 }
01411 scursor++;
01412
01413 eatCFWS( scursor, send, isCRLF );
01414 if ( scursor == send ) {
01415
01416 if ( maybeAttribute.endsWith( asterisk ) ) {
01417 KMIME_WARN << "attribute ends with \"*\", but value is empty!"
01418 "Chopping away \"*\".";
01419 maybeAttribute.truncate( maybeAttribute.length() - 1 );
01420 }
01421 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
01422 return true;
01423 }
01424
01425 const char * oldscursor = scursor;
01426
01427
01428
01429
01430 QStringOrQPair maybeValue;
01431 if ( *scursor == '"' ) {
01432
01433 scursor++;
01434 if ( maybeAttribute.endsWith( asterisk ) ) {
01435
01436
01437
01438 KMIME_WARN << "attribute ends with \"*\", but value is a quoted-string!"
01439 "Chopping away \"*\".";
01440 maybeAttribute.truncate( maybeAttribute.length() - 1 );
01441 }
01442
01443 if ( !parseGenericQuotedString( scursor, send, maybeValue.qstring, isCRLF ) ) {
01444 scursor = oldscursor;
01445 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
01446 return false;
01447 }
01448 } else {
01449
01450 if ( !parseToken( scursor, send, maybeValue.qpair, false ) ) {
01451 scursor = oldscursor;
01452 result = qMakePair( maybeAttribute.toLower(), QStringOrQPair() );
01453 return false;
01454 }
01455 }
01456
01457 result = qMakePair( maybeAttribute.toLower(), maybeValue );
01458 return true;
01459 }
01460
01461
01462
01463 bool parseRawParameterList( const char* &scursor, const char * const send,
01464 QMap<QString,QStringOrQPair> &result,
01465 bool isCRLF )
01466 {
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477 while ( scursor != send ) {
01478 eatCFWS( scursor, send, isCRLF );
01479
01480 if ( scursor == send ) {
01481 return true;
01482 }
01483
01484 if ( *scursor == ';' ) {
01485 scursor++;
01486 continue;
01487 }
01488
01489 QPair<QString,QStringOrQPair> maybeParameter;
01490 if ( !parseParameter( scursor, send, maybeParameter, isCRLF ) ) {
01491
01492
01493
01494
01495
01496
01497
01498 if ( maybeParameter.first.isNull() ) {
01499 return false;
01500 }
01501 while ( scursor != send ) {
01502 if ( *scursor++ == ';' ) {
01503 goto IS_SEMICOLON;
01504 }
01505 }
01506
01507 return true;
01508 IS_SEMICOLON:
01509
01510 continue;
01511 }
01512
01513 result.insert( maybeParameter.first, maybeParameter.second );
01514
01515 eatCFWS( scursor, send, isCRLF );
01516
01517 if ( scursor == send ) {
01518 return true;
01519 }
01520
01521 if ( *scursor == ';' ) {
01522 scursor++;
01523 }
01524 }
01525 return true;
01526 }
01527
01528 static void decodeRFC2231Value( Codec* &rfc2231Codec,
01529 QTextCodec* &textcodec,
01530 bool isContinuation, QString &value,
01531 QPair<const char*,int> &source, QByteArray& charset )
01532 {
01533
01534
01535
01536
01537 const char * decBegin = source.first;
01538 const char * decCursor = decBegin;
01539 const char * decEnd = decCursor + source.second;
01540
01541 if ( !isContinuation ) {
01542
01543 while ( decCursor != decEnd ) {
01544 if ( *decCursor == '\'' ) {
01545 break;
01546 } else {
01547 decCursor++;
01548 }
01549 }
01550
01551 if ( decCursor == decEnd ) {
01552
01553
01554 KMIME_WARN << "No charset in extended-initial-value."
01555 "Assuming \"iso-8859-1\".";
01556 value += QString::fromLatin1( decBegin, source.second );
01557 return;
01558 }
01559
01560 charset = QByteArray( decBegin, decCursor - decBegin );
01561
01562 const char * oldDecCursor = ++decCursor;
01563
01564 while ( decCursor != decEnd ) {
01565 if ( *decCursor == '\'' ) {
01566 break;
01567 } else {
01568 decCursor++;
01569 }
01570 }
01571 if ( decCursor == decEnd ) {
01572 KMIME_WARN << "No language in extended-initial-value."
01573 "Trying to recover.";
01574 decCursor = oldDecCursor;
01575 } else {
01576 decCursor++;
01577 }
01578
01579
01580
01581
01582
01583
01584
01585
01586 bool matchOK = false;
01587 textcodec = KGlobal::charsets()->codecForName( QLatin1String( charset ), matchOK );
01588 if ( !matchOK ) {
01589 textcodec = 0;
01590 KMIME_WARN_UNKNOWN( Charset, charset );
01591 }
01592 }
01593
01594 if ( !rfc2231Codec ) {
01595 rfc2231Codec = Codec::codecForName("x-kmime-rfc2231");
01596 assert( rfc2231Codec );
01597 }
01598
01599 if ( !textcodec ) {
01600 value += QString::fromLatin1( decCursor, decEnd - decCursor );
01601 return;
01602 }
01603
01604 Decoder * dec = rfc2231Codec->makeDecoder();
01605 assert( dec );
01606
01607
01608
01609
01610
01611 QByteArray buffer;
01612 buffer.resize( rfc2231Codec->maxDecodedSizeFor( decEnd - decCursor ) );
01613 QByteArray::Iterator bit = buffer.begin();
01614 QByteArray::ConstIterator bend = buffer.end();
01615
01616 if ( !dec->decode( decCursor, decEnd, bit, bend ) ) {
01617 KMIME_WARN << rfc2231Codec->name()
01618 << "codec lies about its maxDecodedSizeFor()" << endl
01619 << "result may be truncated";
01620 }
01621
01622 value += textcodec->toUnicode( buffer.begin(), bit - buffer.begin() );
01623
01624
01625
01626 delete dec;
01627 }
01628
01629
01630
01631
01632
01633 bool parseParameterListWithCharset( const char* &scursor,
01634 const char * const send,
01635 QMap<QString,QString> &result,
01636 QByteArray& charset, bool isCRLF )
01637 {
01638
01639 QMap<QString,QStringOrQPair> rawParameterList;
01640 if (!parseRawParameterList( scursor, send, rawParameterList, isCRLF ) ) {
01641 return false;
01642 }
01643
01644 if ( rawParameterList.isEmpty() ) {
01645 return true;
01646 }
01647
01648
01649
01650
01651
01652
01653 Codec * rfc2231Codec = 0;
01654 QTextCodec * textcodec = 0;
01655 QString attribute;
01656 QString value;
01657 enum Mode {
01658 NoMode = 0x0, Continued = 0x1, Encoded = 0x2
01659 };
01660
01661 enum EncodingMode {
01662 NoEncoding,
01663 RFC2047,
01664 RFC2231
01665 };
01666
01667 QMap<QString,QStringOrQPair>::Iterator it, end = rawParameterList.end();
01668
01669 for ( it = rawParameterList.begin() ; it != end ; ++it ) {
01670 if ( attribute.isNull() || !it.key().startsWith( attribute ) ) {
01671
01672
01673
01674
01675
01676 if ( !attribute.isNull() ) {
01677 result.insert( attribute, value );
01678 }
01679
01680 value.clear();
01681 attribute = it.key();
01682 int mode = NoMode;
01683 EncodingMode encodingMode = NoEncoding;
01684
01685
01686 if ( attribute.endsWith( asterisk ) ) {
01687 attribute.truncate( attribute.length() - 1 );
01688 mode |= Encoded;
01689 encodingMode = RFC2231;
01690 }
01691
01692 if( !(*it).qstring.isNull() && (*it).qstring.contains( QLatin1String( "=?" ) ) ) {
01693 mode |= Encoded;
01694 encodingMode = RFC2047;
01695 }
01696
01697 if ( attribute.endsWith( asteriskZero ) ) {
01698 attribute.truncate( attribute.length() - 2 );
01699 mode |= Continued;
01700 }
01701
01702
01703
01704 if ( mode & Encoded ) {
01705 if ( encodingMode == RFC2231 ) {
01706 decodeRFC2231Value( rfc2231Codec, textcodec,
01707 false,
01708 value, (*it).qpair, charset );
01709 }
01710 else if ( encodingMode == RFC2047 ) {
01711 value += decodeRFC2047String( (*it).qstring.toLatin1(), charset );
01712 }
01713 } else {
01714
01715 if ( (*it).qpair.first ) {
01716 value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second );
01717 } else {
01718 value += (*it).qstring;
01719 }
01720 }
01721
01722
01723
01724
01725
01726 if ( !(mode & Continued) ) {
01727
01728 result.insert( attribute, value );
01729
01730 attribute.clear();
01731 }
01732 } else {
01733
01734
01735
01736
01737
01738 if ( it.key().endsWith( asterisk ) ) {
01739
01740 decodeRFC2231Value( rfc2231Codec, textcodec,
01741 true,
01742 value, (*it).qpair, charset );
01743 } else {
01744
01745 if ( (*it).qpair.first ) {
01746 value += QString::fromLatin1( (*it).qpair.first, (*it).qpair.second );
01747 } else {
01748 value += (*it).qstring;
01749 }
01750 }
01751 }
01752 }
01753
01754
01755 if ( !attribute.isNull() ) {
01756 result.insert( attribute, value );
01757 }
01758
01759 return true;
01760 }
01761
01762
01763 bool parseParameterList( const char* &scursor, const char * const send,
01764 QMap<QString,QString> &result, bool isCRLF )
01765 {
01766 QByteArray charset;
01767 return parseParameterListWithCharset( scursor, send, result, charset, isCRLF );
01768 }
01769
01770 static const char * const stdDayNames[] = {
01771 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
01772 };
01773 static const int stdDayNamesLen = sizeof stdDayNames / sizeof *stdDayNames;
01774
01775 static bool parseDayName( const char* &scursor, const char * const send )
01776 {
01777
01778 if ( send - scursor < 3 ) {
01779 return false;
01780 }
01781
01782 for ( int i = 0 ; i < stdDayNamesLen ; ++i ) {
01783 if ( qstrnicmp( scursor, stdDayNames[i], 3 ) == 0 ) {
01784 scursor += 3;
01785
01786 return true;
01787 }
01788 }
01789
01790 return false;
01791 }
01792
01793 static const char * const stdMonthNames[] = {
01794 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
01795 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
01796 };
01797 static const int stdMonthNamesLen =
01798 sizeof stdMonthNames / sizeof *stdMonthNames;
01799
01800 static bool parseMonthName( const char* &scursor, const char * const send,
01801 int &result )
01802 {
01803
01804 if ( send - scursor < 3 ) {
01805 return false;
01806 }
01807
01808 for ( result = 0 ; result < stdMonthNamesLen ; ++result ) {
01809 if ( qstrnicmp( scursor, stdMonthNames[result], 3 ) == 0 ) {
01810 scursor += 3;
01811 return true;
01812 }
01813 }
01814
01815
01816 return false;
01817 }
01818
01819 static const struct {
01820 const char * tzName;
01821 long int secsEastOfGMT;
01822 } timeZones[] = {
01823
01824 { "GMT", 0 },
01825 { "UT", 0 },
01826 { "EDT", -4*3600 },
01827 { "EST", -5*3600 },
01828 { "MST", -5*3600 },
01829 { "CST", -6*3600 },
01830 { "MDT", -6*3600 },
01831 { "MST", -7*3600 },
01832 { "PDT", -7*3600 },
01833 { "PST", -8*3600 },
01834
01835 { "CET", 1*3600 },
01836 { "MET", 1*3600 },
01837 { "UTC", 0 },
01838 { "CEST", 2*3600 },
01839 { "BST", 1*3600 },
01840
01841 { "Z", 0 },
01842 { "A", -1*3600 },
01843 { "B", -2*3600 },
01844 { "C", -3*3600 },
01845 { "D", -4*3600 },
01846 { "E", -5*3600 },
01847 { "F", -6*3600 },
01848 { "G", -7*3600 },
01849 { "H", -8*3600 },
01850 { "I", -9*3600 },
01851
01852 { "K", -10*3600 },
01853 { "L", -11*3600 },
01854 { "M", -12*3600 },
01855 { "N", 1*3600 },
01856 { "O", 2*3600 },
01857 { "P", 3*3600 },
01858 { "Q", 4*3600 },
01859 { "R", 5*3600 },
01860 { "S", 6*3600 },
01861 { "T", 7*3600 },
01862 { "U", 8*3600 },
01863 { "V", 9*3600 },
01864 { "W", 10*3600 },
01865 { "X", 11*3600 },
01866 { "Y", 12*3600 },
01867 };
01868 static const int timeZonesLen = sizeof timeZones / sizeof *timeZones;
01869
01870 static bool parseAlphaNumericTimeZone( const char* &scursor,
01871 const char * const send,
01872 long int &secsEastOfGMT,
01873 bool &timeZoneKnown )
01874 {
01875 QPair<const char*,int> maybeTimeZone( 0, 0 );
01876 if ( !parseToken( scursor, send, maybeTimeZone, false ) ) {
01877 return false;
01878 }
01879 for ( int i = 0 ; i < timeZonesLen ; ++i ) {
01880 if ( qstrnicmp( timeZones[i].tzName,
01881 maybeTimeZone.first, maybeTimeZone.second ) == 0 ) {
01882 scursor += maybeTimeZone.second;
01883 secsEastOfGMT = timeZones[i].secsEastOfGMT;
01884 timeZoneKnown = true;
01885 return true;
01886 }
01887 }
01888
01889
01890 KMIME_WARN_UNKNOWN( time zone,
01891 QByteArray( maybeTimeZone.first, maybeTimeZone.second ) );
01892 secsEastOfGMT = 0;
01893 timeZoneKnown = false;
01894 return true;
01895 }
01896
01897
01898 int parseDigits( const char* &scursor, const char * const send, int &result )
01899 {
01900 result = 0;
01901 int digits = 0;
01902 for ( ; scursor != send && isdigit( *scursor ) ; scursor++, digits++ ) {
01903 result *= 10;
01904 result += int( *scursor - '0' );
01905 }
01906 return digits;
01907 }
01908
01909 static bool parseTimeOfDay( const char* &scursor, const char * const send,
01910 int &hour, int &min, int &sec, bool isCRLF=false )
01911 {
01912
01913
01914
01915
01916
01917 if ( !parseDigits( scursor, send, hour ) ) {
01918 return false;
01919 }
01920
01921 eatCFWS( scursor, send, isCRLF );
01922 if ( scursor == send || *scursor != ':' ) {
01923 return false;
01924 }
01925 scursor++;
01926
01927 eatCFWS( scursor, send, isCRLF );
01928 if ( scursor == send ) {
01929 return false;
01930 }
01931
01932
01933
01934
01935 if ( !parseDigits( scursor, send, min ) ) {
01936 return false;
01937 }
01938
01939 eatCFWS( scursor, send, isCRLF );
01940 if ( scursor == send ) {
01941 return true;
01942 }
01943
01944
01945
01946
01947 if ( *scursor == ':' ) {
01948
01949 scursor++;
01950 eatCFWS( scursor, send, isCRLF );
01951 if ( scursor == send ) {
01952 return false;
01953 }
01954
01955 if ( !parseDigits( scursor, send, sec ) ) {
01956 return false;
01957 }
01958 } else {
01959 sec = 0;
01960 }
01961
01962 return true;
01963 }
01964
01965 bool parseTime( const char* &scursor, const char * send,
01966 int &hour, int &min, int &sec, long int &secsEastOfGMT,
01967 bool &timeZoneKnown, bool isCRLF )
01968 {
01969
01970
01971
01972
01973
01974
01975
01976
01977
01978
01979 eatCFWS( scursor, send, isCRLF );
01980 if ( scursor == send ) {
01981 return false;
01982 }
01983
01984 if ( !parseTimeOfDay( scursor, send, hour, min, sec, isCRLF ) ) {
01985 return false;
01986 }
01987
01988 eatCFWS( scursor, send, isCRLF );
01989 if ( scursor == send ) {
01990 timeZoneKnown = false;
01991 secsEastOfGMT = 0;
01992 return true;
01993 }
01994
01995 timeZoneKnown = true;
01996 if ( *scursor == '+' || *scursor == '-' ) {
01997
01998 const char sign = *scursor++;
01999
02000 int maybeTimeZone;
02001 if ( parseDigits( scursor, send, maybeTimeZone ) != 4 ) {
02002 return false;
02003 }
02004 secsEastOfGMT = 60 * ( maybeTimeZone / 100 * 60 + maybeTimeZone % 100 );
02005 if ( sign == '-' ) {
02006 secsEastOfGMT *= -1;
02007 if ( secsEastOfGMT == 0 ) {
02008 timeZoneKnown = false;
02009 }
02010 }
02011 } else {
02012
02013 if ( !parseAlphaNumericTimeZone( scursor, send, secsEastOfGMT, timeZoneKnown ) ) {
02014 return false;
02015 }
02016 }
02017 return true;
02018 }
02019
02020 bool parseDateTime( const char* &scursor, const char * const send,
02021 KDateTime &result, bool isCRLF )
02022 {
02023
02024
02025
02026
02027
02028
02029
02030
02031
02032
02033 result = KDateTime();
02034 QDateTime maybeDateTime;
02035
02036 eatCFWS( scursor, send, isCRLF );
02037 if ( scursor == send ) {
02038 return false;
02039 }
02040
02041
02042
02043
02044 if ( parseDayName( scursor, send ) ) {
02045 eatCFWS( scursor, send, isCRLF );
02046 if ( scursor == send ) {
02047 return false;
02048 }
02049
02050 if ( *scursor == ',' ) {
02051 scursor++;
02052 eatCFWS( scursor, send, isCRLF );
02053 }
02054 }
02055
02056
02057
02058
02059 int maybeDay;
02060 if ( !parseDigits( scursor, send, maybeDay ) ) {
02061 return false;
02062 }
02063
02064 eatCFWS( scursor, send, isCRLF );
02065 if ( scursor == send ) {
02066 return false;
02067 }
02068
02069
02070
02071
02072 int maybeMonth = 0;
02073 if ( !parseMonthName( scursor, send, maybeMonth ) ) {
02074 return false;
02075 }
02076 if ( scursor == send ) {
02077 return false;
02078 }
02079 assert( maybeMonth >= 0 ); assert( maybeMonth <= 11 );
02080 ++maybeMonth;
02081
02082 eatCFWS( scursor, send, isCRLF );
02083 if ( scursor == send ) {
02084 return false;
02085 }
02086
02087
02088
02089
02090 int maybeYear;
02091 if ( !parseDigits( scursor, send, maybeYear ) ) {
02092 return false;
02093 }
02094
02095 if ( maybeYear < 50 ) {
02096 maybeYear += 2000;
02097 } else if ( maybeYear < 1000 ) {
02098 maybeYear += 1900;
02099 }
02100
02101 if ( maybeYear < 1900 ) {
02102 return false;
02103 }
02104
02105 eatCFWS( scursor, send, isCRLF );
02106 if ( scursor == send ) {
02107 return false;
02108 }
02109
02110 maybeDateTime.setDate( QDate( maybeYear, maybeMonth, maybeDay ) );
02111
02112
02113
02114
02115 int maybeHour, maybeMinute, maybeSecond;
02116 long int secsEastOfGMT;
02117 bool timeZoneKnown = true;
02118
02119 if ( !parseTime( scursor, send,
02120 maybeHour, maybeMinute, maybeSecond,
02121 secsEastOfGMT, timeZoneKnown, isCRLF ) ) {
02122 return false;
02123 }
02124
02125 maybeDateTime.setTime( QTime( maybeHour, maybeMinute, maybeSecond ) );
02126 if ( !maybeDateTime.isValid() )
02127 return false;
02128
02129 result = KDateTime( maybeDateTime, KDateTime::Spec( KDateTime::OffsetFromUTC, secsEastOfGMT ) );
02130 if ( !result.isValid() )
02131 return false;
02132 return true;
02133 }
02134
02135 Headers::Base *extractFirstHeader( QByteArray &head )
02136 {
02137 int endOfFieldBody = 0;
02138 bool folded = false;
02139 Headers::Base *header = 0;
02140
02141 int startOfFieldBody = head.indexOf( ':' );
02142 const int endOfFieldHeader = startOfFieldBody;
02143
02144 if ( startOfFieldBody > -1 ) {
02145 startOfFieldBody++;
02146 if ( head[startOfFieldBody] == ' ' ) {
02147 startOfFieldBody++;
02148 }
02149 endOfFieldBody = findHeaderLineEnd( head, startOfFieldBody, &folded );
02150
02151 QByteArray rawType = head.left( endOfFieldHeader );
02152 QByteArray rawFieldBody = head.mid( startOfFieldBody, endOfFieldBody - startOfFieldBody );
02153 if ( folded ) {
02154 rawFieldBody = unfoldHeader( rawFieldBody );
02155 }
02156
02157 if ( !rawType.isEmpty() ) {
02158 header = HeaderFactory::self()->createHeader( rawType );
02159 }
02160 if( !header ) {
02161
02162 header = new Headers::Generic( rawType );
02163 }
02164 header->from7BitString( rawFieldBody );
02165
02166 head.remove( 0, endOfFieldBody + 1 );
02167 } else {
02168 head.clear();
02169 }
02170
02171 return header;
02172 }
02173
02174 void extractHeaderAndBody( const QByteArray &content, QByteArray &header, QByteArray &body )
02175 {
02176 header.clear();
02177 body.clear();
02178
02179
02180 if ( content.startsWith( '\n' ) ) {
02181 body = content.right( content.length() - 1 );
02182 return;
02183 }
02184
02185 int pos = content.indexOf( "\n\n", 0 );
02186 if ( pos > -1 ) {
02187 header = content.left( ++pos );
02188 body = content.mid( pos + 1, content.length() - pos - 1 );
02189 } else {
02190 header = content;
02191 }
02192 }
02193
02194 Headers::Base::List parseHeaders( const QByteArray &head )
02195 {
02196 Headers::Base::List ret;
02197 Headers::Base *h;
02198
02199 QByteArray copy = head;
02200 while( ( h = extractFirstHeader( copy ) ) ) {
02201 ret << h;
02202 }
02203
02204 return ret;
02205 }
02206
02207 }
02208
02209 }