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