00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00028 #include "email.h"
00029
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kurl.h>
00033 #include <kmime_util.h>
00034
00035 #include <QtCore/QRegExp>
00036 #include <QtCore/QByteArray>
00037
00038 using namespace KPIMUtils;
00039
00040
00041 QStringList KPIMUtils::splitAddressList( const QString &aStr )
00042 {
00043
00044
00045
00046
00047
00048
00049
00050
00051 QStringList list;
00052
00053 if ( aStr.isEmpty() ) {
00054 return list;
00055 }
00056
00057 QString addr;
00058 uint addrstart = 0;
00059 int commentlevel = 0;
00060 bool insidequote = false;
00061
00062 for ( int index=0; index<aStr.length(); index++ ) {
00063
00064
00065 switch ( aStr[index].toLatin1() ) {
00066 case '"' :
00067 if ( commentlevel == 0 ) {
00068 insidequote = !insidequote;
00069 }
00070 break;
00071 case '(' :
00072 if ( !insidequote ) {
00073 commentlevel++;
00074 }
00075 break;
00076 case ')' :
00077 if ( !insidequote ) {
00078 if ( commentlevel > 0 ) {
00079 commentlevel--;
00080 } else {
00081 kDebug() << "Error in address splitting: Unmatched ')'";
00082 return list;
00083 }
00084 }
00085 break;
00086 case '\\' :
00087 index++;
00088 break;
00089 case ',' :
00090 if ( !insidequote && ( commentlevel == 0 ) ) {
00091 addr = aStr.mid( addrstart, index - addrstart );
00092 if ( !addr.isEmpty() ) {
00093 list += addr.simplified();
00094 }
00095 addrstart = index + 1;
00096 }
00097 break;
00098 }
00099 }
00100
00101 if ( !insidequote && ( commentlevel == 0 ) ) {
00102 addr = aStr.mid( addrstart, aStr.length() - addrstart );
00103 if ( !addr.isEmpty() ) {
00104 list += addr.simplified();
00105 }
00106 } else {
00107 kDebug() << "Error in address splitting: Unexpected end of address list";
00108 }
00109
00110 return list;
00111 }
00112
00113
00114
00115 KPIMUtils::EmailParseResult splitAddressInternal( const QByteArray address,
00116 QByteArray &displayName,
00117 QByteArray &addrSpec,
00118 QByteArray &comment,
00119 bool allowMultipleAddresses )
00120 {
00121
00122
00123 displayName = "";
00124 addrSpec = "";
00125 comment = "";
00126
00127 if ( address.isEmpty() ) {
00128 return AddressEmpty;
00129 }
00130
00131
00132
00133
00134
00135 enum {
00136 TopLevel,
00137 InComment,
00138 InAngleAddress
00139 } context = TopLevel;
00140 bool inQuotedString = false;
00141 int commentLevel = 0;
00142 bool stop = false;
00143
00144 for ( const char *p = address.data(); *p && !stop; ++p ) {
00145 switch ( context ) {
00146 case TopLevel :
00147 {
00148 switch ( *p ) {
00149 case '"' :
00150 inQuotedString = !inQuotedString;
00151 displayName += *p;
00152 break;
00153 case '(' :
00154 if ( !inQuotedString ) {
00155 context = InComment;
00156 commentLevel = 1;
00157 } else {
00158 displayName += *p;
00159 }
00160 break;
00161 case '<' :
00162 if ( !inQuotedString ) {
00163 context = InAngleAddress;
00164 } else {
00165 displayName += *p;
00166 }
00167 break;
00168 case '\\' :
00169 displayName += *p;
00170 ++p;
00171 if ( *p ) {
00172 displayName += *p;
00173 } else {
00174 return UnexpectedEnd;
00175 }
00176 break;
00177 case ',' :
00178 if ( !inQuotedString ) {
00179 if ( allowMultipleAddresses ) {
00180 stop = true;
00181 } else {
00182 return UnexpectedComma;
00183 }
00184 } else {
00185 displayName += *p;
00186 }
00187 break;
00188 default :
00189 displayName += *p;
00190 }
00191 break;
00192 }
00193 case InComment :
00194 {
00195 switch ( *p ) {
00196 case '(' :
00197 ++commentLevel;
00198 comment += *p;
00199 break;
00200 case ')' :
00201 --commentLevel;
00202 if ( commentLevel == 0 ) {
00203 context = TopLevel;
00204 comment += ' ';
00205 } else {
00206 comment += *p;
00207 }
00208 break;
00209 case '\\' :
00210 comment += *p;
00211 ++p;
00212 if ( *p ) {
00213 comment += *p;
00214 } else {
00215 return UnexpectedEnd;
00216 }
00217 break;
00218 default :
00219 comment += *p;
00220 }
00221 break;
00222 }
00223 case InAngleAddress :
00224 {
00225 switch ( *p ) {
00226 case '"' :
00227 inQuotedString = !inQuotedString;
00228 addrSpec += *p;
00229 break;
00230 case '>' :
00231 if ( !inQuotedString ) {
00232 context = TopLevel;
00233 } else {
00234 addrSpec += *p;
00235 }
00236 break;
00237 case '\\' :
00238 addrSpec += *p;
00239 ++p;
00240 if ( *p ) {
00241 addrSpec += *p;
00242 } else {
00243 return UnexpectedEnd;
00244 }
00245 break;
00246 default :
00247 addrSpec += *p;
00248 }
00249 break;
00250 }
00251 }
00252 }
00253
00254 if ( inQuotedString ) {
00255 return UnbalancedQuote;
00256 }
00257 if ( context == InComment ) {
00258 return UnbalancedParens;
00259 }
00260 if ( context == InAngleAddress ) {
00261 return UnclosedAngleAddr;
00262 }
00263
00264 displayName = displayName.trimmed();
00265 comment = comment.trimmed();
00266 addrSpec = addrSpec.trimmed();
00267
00268 if ( addrSpec.isEmpty() ) {
00269 if ( displayName.isEmpty() ) {
00270 return NoAddressSpec;
00271 } else {
00272 addrSpec = displayName;
00273 displayName.truncate( 0 );
00274 }
00275 }
00276
00277
00278
00279
00280
00281 return AddressOk;
00282 }
00283
00284
00285 EmailParseResult KPIMUtils::splitAddress( const QByteArray &address,
00286 QByteArray &displayName,
00287 QByteArray &addrSpec,
00288 QByteArray &comment )
00289 {
00290 return splitAddressInternal( address, displayName, addrSpec, comment,
00291 false );
00292 }
00293
00294
00295 EmailParseResult KPIMUtils::splitAddress( const QString &address,
00296 QString &displayName,
00297 QString &addrSpec,
00298 QString &comment )
00299 {
00300 QByteArray d, a, c;
00301 EmailParseResult result = splitAddress( address.toUtf8(), d, a, c );
00302
00303 if ( result == AddressOk ) {
00304 displayName = QString::fromUtf8( d );
00305 addrSpec = QString::fromUtf8( a );
00306 comment = QString::fromUtf8( c );
00307 }
00308 return result;
00309 }
00310
00311
00312 EmailParseResult KPIMUtils::isValidAddress( const QString &aStr )
00313 {
00314
00315
00316 if ( aStr.isEmpty() ) {
00317 return AddressEmpty;
00318 }
00319
00320
00321
00322
00323
00324
00325
00326
00327 bool tooManyAtsFlag = false;
00328
00329 int atCount = aStr.count( '@' );
00330 if ( atCount > 1 ) {
00331 tooManyAtsFlag = true;
00332 } else if ( atCount == 0 ) {
00333 return TooFewAts;
00334 }
00335
00336
00337
00338
00339 enum {
00340 TopLevel,
00341 InComment,
00342 InAngleAddress
00343 } context = TopLevel;
00344 bool inQuotedString = false;
00345 int commentLevel = 0;
00346
00347 unsigned int strlen = aStr.length();
00348
00349 for ( unsigned int index=0; index < strlen; index++ ) {
00350 switch ( context ) {
00351 case TopLevel :
00352 {
00353 switch ( aStr[index].toLatin1() ) {
00354 case '"' :
00355 inQuotedString = !inQuotedString;
00356 break;
00357 case '(' :
00358 if ( !inQuotedString ) {
00359 context = InComment;
00360 commentLevel = 1;
00361 }
00362 break;
00363 case '[' :
00364 if ( !inQuotedString ) {
00365 return InvalidDisplayName;
00366 }
00367 break;
00368 case ']' :
00369 if ( !inQuotedString ) {
00370 return InvalidDisplayName;
00371 }
00372 break;
00373 case ':' :
00374 if ( !inQuotedString ) {
00375 return DisallowedChar;
00376 }
00377 break;
00378 case '<' :
00379 if ( !inQuotedString ) {
00380 context = InAngleAddress;
00381 }
00382 break;
00383 case '\\' :
00384 ++index;
00385 if ( ( index + 1 ) > strlen ) {
00386 return UnexpectedEnd;
00387 }
00388 break;
00389 case ',' :
00390 if ( !inQuotedString ) {
00391 return UnexpectedComma;
00392 }
00393 break;
00394 case ')' :
00395 if ( !inQuotedString ) {
00396 return UnbalancedParens;
00397 }
00398 break;
00399 case '>' :
00400 if ( !inQuotedString ) {
00401 return UnopenedAngleAddr;
00402 }
00403 break;
00404 case '@' :
00405 if ( !inQuotedString ) {
00406 if ( index == 0 ) {
00407 return MissingLocalPart;
00408 } else if ( index == strlen-1 ) {
00409 return MissingDomainPart;
00410 break;
00411 }
00412 } else if ( inQuotedString ) {
00413 --atCount;
00414 if ( atCount == 1 ) {
00415 tooManyAtsFlag = false;
00416 }
00417 }
00418 break;
00419 }
00420 break;
00421 }
00422 case InComment :
00423 {
00424 switch ( aStr[index].toLatin1() ) {
00425 case '(' :
00426 ++commentLevel;
00427 break;
00428 case ')' :
00429 --commentLevel;
00430 if ( commentLevel == 0 ) {
00431 context = TopLevel;
00432 }
00433 break;
00434 case '\\' :
00435 ++index;
00436 if ( ( index + 1 ) > strlen ) {
00437 return UnexpectedEnd;
00438 }
00439 break;
00440 }
00441 break;
00442 }
00443
00444 case InAngleAddress :
00445 {
00446 switch ( aStr[index].toLatin1() ) {
00447 case ',' :
00448 if ( !inQuotedString ) {
00449 return UnexpectedComma;
00450 }
00451 break;
00452 case '"' :
00453 inQuotedString = !inQuotedString;
00454 break;
00455 case '@' :
00456 if ( inQuotedString ) {
00457 --atCount;
00458 if ( atCount == 1 ) {
00459 tooManyAtsFlag = false;
00460 }
00461 }
00462 break;
00463 case '>' :
00464 if ( !inQuotedString ) {
00465 context = TopLevel;
00466 break;
00467 }
00468 break;
00469 case '\\' :
00470 ++index;
00471 if ( ( index + 1 ) > strlen ) {
00472 return UnexpectedEnd;
00473 }
00474 break;
00475 }
00476 break;
00477 }
00478 }
00479 }
00480
00481 if ( atCount == 0 && !inQuotedString ) {
00482 return TooFewAts;
00483 }
00484
00485 if ( inQuotedString ) {
00486 return UnbalancedQuote;
00487 }
00488
00489 if ( context == InComment ) {
00490 return UnbalancedParens;
00491 }
00492
00493 if ( context == InAngleAddress ) {
00494 return UnclosedAngleAddr;
00495 }
00496
00497 if ( tooManyAtsFlag ) {
00498 return TooManyAts;
00499 }
00500
00501 return AddressOk;
00502 }
00503
00504
00505 KPIMUtils::EmailParseResult KPIMUtils::isValidAddressList( const QString &aStr,
00506 QString &badAddr )
00507 {
00508 if ( aStr.isEmpty() ) {
00509 return AddressEmpty;
00510 }
00511
00512 QStringList list = splitAddressList( aStr );
00513
00514 QStringList::const_iterator it = list.begin();
00515 EmailParseResult errorCode = AddressOk;
00516 for ( it = list.begin(); it != list.end(); ++it ) {
00517 errorCode = isValidAddress( *it );
00518 if ( errorCode != AddressOk ) {
00519 badAddr = ( *it );
00520 break;
00521 }
00522 }
00523 return errorCode;
00524 }
00525
00526
00527 QString KPIMUtils::emailParseResultToString( EmailParseResult errorCode )
00528 {
00529 switch ( errorCode ) {
00530 case TooManyAts :
00531 return i18n( "The email address you entered is not valid because it "
00532 "contains more than one @. "
00533 "You will not create valid messages if you do not "
00534 "change your address." );
00535 case TooFewAts :
00536 return i18n( "The email address you entered is not valid because it "
00537 "does not contain a @."
00538 "You will not create valid messages if you do not "
00539 "change your address." );
00540 case AddressEmpty :
00541 return i18n( "You have to enter something in the email address field." );
00542 case MissingLocalPart :
00543 return i18n( "The email address you entered is not valid because it "
00544 "does not contain a local part." );
00545 case MissingDomainPart :
00546 return i18n( "The email address you entered is not valid because it "
00547 "does not contain a domain part." );
00548 case UnbalancedParens :
00549 return i18n( "The email address you entered is not valid because it "
00550 "contains unclosed comments/brackets." );
00551 case AddressOk :
00552 return i18n( "The email address you entered is valid." );
00553 case UnclosedAngleAddr :
00554 return i18n( "The email address you entered is not valid because it "
00555 "contains an unclosed anglebracket." );
00556 case UnopenedAngleAddr :
00557 return i18n( "The email address you entered is not valid because it "
00558 "contains an unopened anglebracket." );
00559 case UnexpectedComma :
00560 return i18n( "The email address you have entered is not valid because it "
00561 "contains an unexpected comma." );
00562 case UnexpectedEnd :
00563 return i18n( "The email address you entered is not valid because it ended "
00564 "unexpectedly, this probably means you have used an escaping "
00565 "type character like an \\ as the last character in your "
00566 "email address." );
00567 case UnbalancedQuote :
00568 return i18n( "The email address you entered is not valid because it "
00569 "contains quoted text which does not end." );
00570 case NoAddressSpec :
00571 return i18n( "The email address you entered is not valid because it "
00572 "does not seem to contain an actual email address, i.e. "
00573 "something of the form joe@example.org." );
00574 case DisallowedChar :
00575 return i18n( "The email address you entered is not valid because it "
00576 "contains an illegal character." );
00577 case InvalidDisplayName :
00578 return i18n( "The email address you have entered is not valid because it "
00579 "contains an invalid displayname." );
00580 }
00581 return i18n( "Unknown problem with email address" );
00582 }
00583
00584
00585 bool KPIMUtils::isValidSimpleAddress( const QString &aStr )
00586 {
00587
00588
00589 if ( aStr.isEmpty() ) {
00590 return false;
00591 }
00592
00593 int atChar = aStr.lastIndexOf( '@' );
00594 QString domainPart = aStr.mid( atChar + 1 );
00595 QString localPart = aStr.left( atChar );
00596 bool tooManyAtsFlag = false;
00597 bool inQuotedString = false;
00598 int atCount = localPart.count( '@' );
00599
00600 unsigned int strlen = localPart.length();
00601 for ( unsigned int index=0; index < strlen; index++ ) {
00602 switch( localPart[ index ].toLatin1() ) {
00603 case '"' :
00604 inQuotedString = !inQuotedString;
00605 break;
00606 case '@' :
00607 if ( inQuotedString ) {
00608 --atCount;
00609 if ( atCount == 0 ) {
00610 tooManyAtsFlag = false;
00611 }
00612 }
00613 break;
00614 }
00615 }
00616
00617 QString addrRx =
00618 "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@";
00619
00620 if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
00621 addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
00622 }
00623 if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
00624 addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
00625 } else {
00626 addrRx += "[\\w-]+(\\.[\\w-]+)*";
00627 }
00628 QRegExp rx( addrRx );
00629 return rx.exactMatch( aStr ) && !tooManyAtsFlag;
00630 }
00631
00632
00633 QString KPIMUtils::simpleEmailAddressErrorMsg()
00634 {
00635 return i18n( "The email address you entered is not valid because it "
00636 "does not seem to contain an actual email address, i.e. "
00637 "something of the form joe@example.org." );
00638 }
00639
00640
00641 QByteArray KPIMUtils::extractEmailAddress( const QByteArray &address )
00642 {
00643 QByteArray dummy1, dummy2, addrSpec;
00644 EmailParseResult result =
00645 splitAddressInternal( address, dummy1, addrSpec, dummy2,
00646 false );
00647 if ( result != AddressOk ) {
00648 addrSpec = QByteArray();
00649 kDebug()
00650 << "Input: aStr\nError:"
00651 << emailParseResultToString( result );
00652 }
00653
00654 return addrSpec;
00655 }
00656
00657
00658 QString KPIMUtils::extractEmailAddress( const QString &address )
00659 {
00660 return QString::fromUtf8( extractEmailAddress( address.toUtf8() ) );
00661 }
00662
00663
00664 QByteArray KPIMUtils::firstEmailAddress( const QByteArray &addresses )
00665 {
00666 QByteArray dummy1, dummy2, addrSpec;
00667 EmailParseResult result =
00668 splitAddressInternal( addresses, dummy1, addrSpec, dummy2,
00669 true );
00670 if ( result != AddressOk ) {
00671 addrSpec = QByteArray();
00672 kDebug()
00673 << "Input: aStr\nError:"
00674 << emailParseResultToString( result );
00675 }
00676
00677 return addrSpec;
00678 }
00679
00680
00681 QString KPIMUtils::firstEmailAddress( const QString &addresses )
00682 {
00683 return QString::fromUtf8( firstEmailAddress( addresses.toUtf8() ) );
00684 }
00685
00686
00687 bool KPIMUtils::extractEmailAddressAndName( const QString &aStr,
00688 QString &mail, QString &name )
00689 {
00690 name.clear();
00691 mail.clear();
00692
00693 const int len = aStr.length();
00694 const char cQuotes = '"';
00695
00696 bool bInComment = false;
00697 bool bInQuotesOutsideOfEmail = false;
00698 int i=0, iAd=0, iMailStart=0, iMailEnd=0;
00699 QChar c;
00700 unsigned int commentstack = 0;
00701
00702
00703
00704 while ( i < len ) {
00705 c = aStr[i];
00706 if ( '(' == c ) {
00707 commentstack++;
00708 }
00709 if ( ')' == c ) {
00710 commentstack--;
00711 }
00712 bInComment = commentstack != 0;
00713 if ( '"' == c && !bInComment ) {
00714 bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail;
00715 }
00716
00717 if( !bInComment && !bInQuotesOutsideOfEmail ) {
00718 if ( '@' == c ) {
00719 iAd = i;
00720 break;
00721 }
00722 }
00723 ++i;
00724 }
00725
00726 if ( !iAd ) {
00727
00728
00729
00730 for ( i = 0; len > i; ++i ) {
00731 c = aStr[i];
00732 if ( '<' != c ) {
00733 name.append( c );
00734 } else {
00735 break;
00736 }
00737 }
00738 mail = aStr.mid( i + 1 );
00739 if ( mail.endsWith( '>' ) ) {
00740 mail.truncate( mail.length() - 1 );
00741 }
00742
00743 } else {
00744
00745
00746
00747 bInComment = false;
00748 bInQuotesOutsideOfEmail = false;
00749 for ( i = iAd-1; 0 <= i; --i ) {
00750 c = aStr[i];
00751 if ( bInComment ) {
00752 if ( '(' == c ) {
00753 if ( !name.isEmpty() ) {
00754 name.prepend( ' ' );
00755 }
00756 bInComment = false;
00757 } else {
00758 name.prepend( c );
00759 }
00760 } else if ( bInQuotesOutsideOfEmail ) {
00761 if ( cQuotes == c ) {
00762 bInQuotesOutsideOfEmail = false;
00763 } else {
00764 name.prepend( c );
00765 }
00766 } else {
00767
00768 if ( ',' == c ) {
00769 break;
00770 }
00771
00772 if ( iMailStart ) {
00773 if ( cQuotes == c ) {
00774 bInQuotesOutsideOfEmail = true;
00775 } else {
00776 name.prepend( c );
00777 }
00778 } else {
00779 switch ( c.toLatin1() ) {
00780 case '<':
00781 iMailStart = i;
00782 break;
00783 case ')':
00784 if ( !name.isEmpty() ) {
00785 name.prepend( ' ' );
00786 }
00787 bInComment = true;
00788 break;
00789 default:
00790 if ( ' ' != c ) {
00791 mail.prepend( c );
00792 }
00793 }
00794 }
00795 }
00796 }
00797
00798 name = name.simplified();
00799 mail = mail.simplified();
00800
00801 if ( mail.isEmpty() ) {
00802 return false;
00803 }
00804
00805 mail.append( '@' );
00806
00807
00808
00809
00810 bInComment = false;
00811 bInQuotesOutsideOfEmail = false;
00812 int parenthesesNesting = 0;
00813 for ( i = iAd+1; len > i; ++i ) {
00814 c = aStr[i];
00815 if ( bInComment ) {
00816 if ( ')' == c ) {
00817 if ( --parenthesesNesting == 0 ) {
00818 bInComment = false;
00819 if ( !name.isEmpty() ) {
00820 name.append( ' ' );
00821 }
00822 } else {
00823
00824 name.append( ')' );
00825 }
00826 } else {
00827 if ( '(' == c ) {
00828
00829 ++parenthesesNesting;
00830 }
00831 name.append( c );
00832 }
00833 } else if ( bInQuotesOutsideOfEmail ) {
00834 if ( cQuotes == c ) {
00835 bInQuotesOutsideOfEmail = false;
00836 } else {
00837 name.append( c );
00838 }
00839 } else {
00840
00841 if ( ',' == c ) {
00842 break;
00843 }
00844
00845 if ( iMailEnd ){
00846 if ( cQuotes == c ) {
00847 bInQuotesOutsideOfEmail = true;
00848 } else {
00849 name.append( c );
00850 }
00851 } else {
00852 switch ( c.toLatin1() ) {
00853 case '>':
00854 iMailEnd = i;
00855 break;
00856 case '(':
00857 if ( !name.isEmpty() ) {
00858 name.append( ' ' );
00859 }
00860 if ( ++parenthesesNesting > 0 ) {
00861 bInComment = true;
00862 }
00863 break;
00864 default:
00865 if ( ' ' != c ) {
00866 mail.append( c );
00867 }
00868 }
00869 }
00870 }
00871 }
00872 }
00873
00874 name = name.simplified();
00875 mail = mail.simplified();
00876
00877 return ! ( name.isEmpty() || mail.isEmpty() );
00878 }
00879
00880
00881 bool KPIMUtils::compareEmail( const QString &email1, const QString &email2,
00882 bool matchName )
00883 {
00884 QString e1Name, e1Email, e2Name, e2Email;
00885
00886 extractEmailAddressAndName( email1, e1Email, e1Name );
00887 extractEmailAddressAndName( email2, e2Email, e2Name );
00888
00889 return e1Email == e2Email &&
00890 ( !matchName || ( e1Name == e2Name ) );
00891 }
00892
00893
00894 QString KPIMUtils::normalizedAddress( const QString &displayName,
00895 const QString &addrSpec,
00896 const QString &comment )
00897 {
00898 if ( displayName.isEmpty() && comment.isEmpty() ) {
00899 return addrSpec;
00900 } else if ( comment.isEmpty() ) {
00901 if ( !displayName.startsWith('\"') ) {
00902 return quoteNameIfNecessary( displayName ) + " <" + addrSpec + '>';
00903 } else {
00904 return displayName + " <" + addrSpec + '>';
00905 }
00906 } else if ( displayName.isEmpty() ) {
00907 QString commentStr = comment;
00908 return quoteNameIfNecessary( commentStr ) + " <" + addrSpec + '>';
00909 } else {
00910 return displayName + " (" + comment + ") <" + addrSpec + '>';
00911 }
00912 }
00913
00914
00915 QString KPIMUtils::fromIdn( const QString &addrSpec )
00916 {
00917 const int atPos = addrSpec.lastIndexOf( '@' );
00918 if ( atPos == -1 ) {
00919 return addrSpec;
00920 }
00921
00922 QString idn = KUrl::fromAce( addrSpec.mid( atPos + 1 ).toLatin1() );
00923 if ( idn.isEmpty() ) {
00924 return QString();
00925 }
00926
00927 return addrSpec.left( atPos + 1 ) + idn;
00928 }
00929
00930
00931 QString KPIMUtils::toIdn( const QString &addrSpec )
00932 {
00933 const int atPos = addrSpec.lastIndexOf( '@' );
00934 if ( atPos == -1 ) {
00935 return addrSpec;
00936 }
00937
00938 QString idn = KUrl::toAce( addrSpec.mid( atPos + 1 ) );
00939 if ( idn.isEmpty() ) {
00940 return addrSpec;
00941 }
00942
00943 return addrSpec.left( atPos + 1 ) + idn;
00944 }
00945
00946
00947 QString KPIMUtils::normalizeAddressesAndDecodeIdn( const QString &str )
00948 {
00949
00950 if ( str.isEmpty() ) {
00951 return str;
00952 }
00953
00954 const QStringList addressList = splitAddressList( str );
00955 QStringList normalizedAddressList;
00956
00957 QByteArray displayName, addrSpec, comment;
00958
00959 for ( QStringList::ConstIterator it = addressList.begin();
00960 ( it != addressList.end() );
00961 ++it ) {
00962 if ( !(*it).isEmpty() ) {
00963 if ( splitAddress( (*it).toUtf8(),
00964 displayName, addrSpec, comment ) == AddressOk ) {
00965
00966 displayName = KMime::decodeRFC2047String(displayName).toUtf8();
00967 comment = KMime::decodeRFC2047String(comment).toUtf8();
00968
00969 normalizedAddressList
00970 << normalizedAddress( QString::fromUtf8( displayName ),
00971 fromIdn( QString::fromUtf8( addrSpec ) ),
00972 QString::fromUtf8( comment ) );
00973 } else {
00974 kDebug() << "splitting address failed:" << *it;
00975 }
00976 }
00977 }
00978
00979
00980
00981
00982
00983 return normalizedAddressList.join( ", " );
00984 }
00985
00986
00987 QString KPIMUtils::normalizeAddressesAndEncodeIdn( const QString &str )
00988 {
00989
00990 if ( str.isEmpty() ) {
00991 return str;
00992 }
00993
00994 const QStringList addressList = splitAddressList( str );
00995 QStringList normalizedAddressList;
00996
00997 QByteArray displayName, addrSpec, comment;
00998
00999 for ( QStringList::ConstIterator it = addressList.begin();
01000 ( it != addressList.end() );
01001 ++it ) {
01002 if ( !(*it).isEmpty() ) {
01003 if ( splitAddress( (*it).toUtf8(),
01004 displayName, addrSpec, comment ) == AddressOk ) {
01005
01006 normalizedAddressList << normalizedAddress( QString::fromUtf8( displayName ),
01007 toIdn( QString::fromUtf8( addrSpec ) ),
01008 QString::fromUtf8( comment ) );
01009 } else {
01010 kDebug() << "splitting address failed:" << *it;
01011 }
01012 }
01013 }
01014
01015
01016
01017
01018
01019
01020 return normalizedAddressList.join( ", " );
01021 }
01022
01023
01024
01025 static QString escapeQuotes( const QString &str )
01026 {
01027 if ( str.isEmpty() ) {
01028 return QString();
01029 }
01030
01031 QString escaped;
01032
01033 escaped.reserve( 2 * str.length() );
01034 unsigned int len = 0;
01035 for ( int i = 0; i < str.length(); ++i, ++len ) {
01036 if ( str[i] == '"' ) {
01037 escaped[len] = '\\';
01038 ++len;
01039 } else if ( str[i] == '\\' ) {
01040 escaped[len] = '\\';
01041 ++len;
01042 ++i;
01043 if ( i >= str.length() ) {
01044 break;
01045 }
01046 }
01047 escaped[len] = str[i];
01048 }
01049 escaped.truncate( len );
01050 return escaped;
01051 }
01052
01053
01054 QString KPIMUtils::quoteNameIfNecessary( const QString &str )
01055 {
01056 QString quoted = str;
01057
01058 QRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
01059
01060 if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
01061 quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
01062 } else if ( quoted.indexOf( needQuotes ) != -1 ) {
01063 quoted = "\"" + escapeQuotes( quoted ) + "\"";
01064 }
01065
01066 return quoted;
01067 }