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