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