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