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