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 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 bool tooManyAtsFlag = false;
00594 bool inQuotedString = false;
00595 int atCount = localPart.count( '@' );
00596
00597 unsigned int strlen = localPart.length();
00598 for ( unsigned int index=0; index < strlen; index++ ) {
00599 switch( localPart[ index ].toLatin1() ) {
00600 case '"' :
00601 inQuotedString = !inQuotedString;
00602 break;
00603 case '@' :
00604 if ( inQuotedString ) {
00605 --atCount;
00606 if ( atCount == 0 ) {
00607 tooManyAtsFlag = false;
00608 }
00609 }
00610 break;
00611 }
00612 }
00613
00614 QString addrRx =
00615 "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@";
00616
00617 if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
00618 addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
00619 }
00620 if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
00621 addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
00622 } else {
00623 addrRx += "[\\w-]+(\\.[\\w-]+)*";
00624 }
00625 QRegExp rx( addrRx );
00626 return rx.exactMatch( aStr ) && !tooManyAtsFlag;
00627 }
00628
00629
00630 QString KPIMUtils::simpleEmailAddressErrorMsg()
00631 {
00632 return i18n( "The email address you entered is not valid because it "
00633 "does not seem to contain an actual email address, i.e. "
00634 "something of the form joe@example.org." );
00635 }
00636
00637
00638 QByteArray KPIMUtils::extractEmailAddress( const QByteArray &address )
00639 {
00640 QByteArray dummy1, dummy2, addrSpec;
00641 EmailParseResult result =
00642 splitAddressInternal( address, dummy1, addrSpec, dummy2,
00643 false );
00644 if ( result != AddressOk ) {
00645 addrSpec = QByteArray();
00646 if ( result != AddressEmpty ) {
00647 kDebug()
00648 << "Input:" << address << "\nError:"
00649 << emailParseResultToString( result );
00650 }
00651 }
00652
00653 return addrSpec;
00654 }
00655
00656
00657 QString KPIMUtils::extractEmailAddress( const QString &address )
00658 {
00659 return QString::fromUtf8( extractEmailAddress( address.toUtf8() ) );
00660 }
00661
00662
00663 QByteArray KPIMUtils::firstEmailAddress( const QByteArray &addresses )
00664 {
00665 QByteArray dummy1, dummy2, addrSpec;
00666 EmailParseResult result =
00667 splitAddressInternal( addresses, dummy1, addrSpec, dummy2,
00668 true );
00669 if ( result != AddressOk ) {
00670 addrSpec = QByteArray();
00671 if ( result != AddressEmpty ) {
00672 kDebug()
00673 << "Input: aStr\nError:"
00674 << emailParseResultToString( result );
00675 }
00676 }
00677
00678 return addrSpec;
00679 }
00680
00681
00682 QString KPIMUtils::firstEmailAddress( const QString &addresses )
00683 {
00684 return QString::fromUtf8( firstEmailAddress( addresses.toUtf8() ) );
00685 }
00686
00687
00688 bool KPIMUtils::extractEmailAddressAndName( const QString &aStr,
00689 QString &mail, QString &name )
00690 {
00691 name.clear();
00692 mail.clear();
00693
00694 const int len = aStr.length();
00695 const char cQuotes = '"';
00696
00697 bool bInComment = false;
00698 bool bInQuotesOutsideOfEmail = false;
00699 int i=0, iAd=0, iMailStart=0, iMailEnd=0;
00700 QChar c;
00701 unsigned int commentstack = 0;
00702
00703
00704
00705 while ( i < len ) {
00706 c = aStr[i];
00707 if ( '(' == c ) {
00708 commentstack++;
00709 }
00710 if ( ')' == c ) {
00711 commentstack--;
00712 }
00713 bInComment = commentstack != 0;
00714 if ( '"' == c && !bInComment ) {
00715 bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail;
00716 }
00717
00718 if( !bInComment && !bInQuotesOutsideOfEmail ) {
00719 if ( '@' == c ) {
00720 iAd = i;
00721 break;
00722 }
00723 }
00724 ++i;
00725 }
00726
00727 if ( !iAd ) {
00728
00729
00730
00731 for ( i = 0; len > i; ++i ) {
00732 c = aStr[i];
00733 if ( '<' != c ) {
00734 name.append( c );
00735 } else {
00736 break;
00737 }
00738 }
00739 mail = aStr.mid( i + 1 );
00740 if ( mail.endsWith( '>' ) ) {
00741 mail.truncate( mail.length() - 1 );
00742 }
00743
00744 } else {
00745
00746
00747
00748 bInComment = false;
00749 bInQuotesOutsideOfEmail = false;
00750 for ( i = iAd-1; 0 <= i; --i ) {
00751 c = aStr[i];
00752 if ( bInComment ) {
00753 if ( '(' == c ) {
00754 if ( !name.isEmpty() ) {
00755 name.prepend( ' ' );
00756 }
00757 bInComment = false;
00758 } else {
00759 name.prepend( c );
00760 }
00761 } else if ( bInQuotesOutsideOfEmail ) {
00762 if ( cQuotes == c ) {
00763 bInQuotesOutsideOfEmail = false;
00764 } else {
00765 name.prepend( c );
00766 }
00767 } else {
00768
00769 if ( ',' == c ) {
00770 break;
00771 }
00772
00773 if ( iMailStart ) {
00774 if ( cQuotes == c ) {
00775 bInQuotesOutsideOfEmail = true;
00776 } else {
00777 name.prepend( c );
00778 }
00779 } else {
00780 switch ( c.toLatin1() ) {
00781 case '<':
00782 iMailStart = i;
00783 break;
00784 case ')':
00785 if ( !name.isEmpty() ) {
00786 name.prepend( ' ' );
00787 }
00788 bInComment = true;
00789 break;
00790 default:
00791 if ( ' ' != c ) {
00792 mail.prepend( c );
00793 }
00794 }
00795 }
00796 }
00797 }
00798
00799 name = name.simplified();
00800 mail = mail.simplified();
00801
00802 if ( mail.isEmpty() ) {
00803 return false;
00804 }
00805
00806 mail.append( '@' );
00807
00808
00809
00810
00811 bInComment = false;
00812 bInQuotesOutsideOfEmail = false;
00813 int parenthesesNesting = 0;
00814 for ( i = iAd+1; len > i; ++i ) {
00815 c = aStr[i];
00816 if ( bInComment ) {
00817 if ( ')' == c ) {
00818 if ( --parenthesesNesting == 0 ) {
00819 bInComment = false;
00820 if ( !name.isEmpty() ) {
00821 name.append( ' ' );
00822 }
00823 } else {
00824
00825 name.append( ')' );
00826 }
00827 } else {
00828 if ( '(' == c ) {
00829
00830 ++parenthesesNesting;
00831 }
00832 name.append( c );
00833 }
00834 } else if ( bInQuotesOutsideOfEmail ) {
00835 if ( cQuotes == c ) {
00836 bInQuotesOutsideOfEmail = false;
00837 } else {
00838 name.append( c );
00839 }
00840 } else {
00841
00842 if ( ',' == c ) {
00843 break;
00844 }
00845
00846 if ( iMailEnd ){
00847 if ( cQuotes == c ) {
00848 bInQuotesOutsideOfEmail = true;
00849 } else {
00850 name.append( c );
00851 }
00852 } else {
00853 switch ( c.toLatin1() ) {
00854 case '>':
00855 iMailEnd = i;
00856 break;
00857 case '(':
00858 if ( !name.isEmpty() ) {
00859 name.append( ' ' );
00860 }
00861 if ( ++parenthesesNesting > 0 ) {
00862 bInComment = true;
00863 }
00864 break;
00865 default:
00866 if ( ' ' != c ) {
00867 mail.append( c );
00868 }
00869 }
00870 }
00871 }
00872 }
00873 }
00874
00875 name = name.simplified();
00876 mail = mail.simplified();
00877
00878 return ! ( name.isEmpty() || mail.isEmpty() );
00879 }
00880
00881
00882 bool KPIMUtils::compareEmail( const QString &email1, const QString &email2,
00883 bool matchName )
00884 {
00885 QString e1Name, e1Email, e2Name, e2Email;
00886
00887 extractEmailAddressAndName( email1, e1Email, e1Name );
00888 extractEmailAddressAndName( email2, e2Email, e2Name );
00889
00890 return e1Email == e2Email &&
00891 ( !matchName || ( e1Name == e2Name ) );
00892 }
00893
00894
00895 QString KPIMUtils::normalizedAddress( const QString &displayName,
00896 const QString &addrSpec,
00897 const QString &comment )
00898 {
00899 const QString realDisplayName = KMime::removeBidiControlChars( displayName );
00900 if ( realDisplayName.isEmpty() && comment.isEmpty() ) {
00901 return addrSpec;
00902 } else if ( comment.isEmpty() ) {
00903 if ( !realDisplayName.startsWith( '\"' ) ) {
00904 return quoteNameIfNecessary( realDisplayName ) + " <" + addrSpec + '>';
00905 } else {
00906 return realDisplayName + " <" + addrSpec + '>';
00907 }
00908 } else if ( realDisplayName.isEmpty() ) {
00909 QString commentStr = comment;
00910 return quoteNameIfNecessary( commentStr ) + " <" + addrSpec + '>';
00911 } else {
00912 return realDisplayName + " (" + comment + ") <" + addrSpec + '>';
00913 }
00914 }
00915
00916
00917 QString KPIMUtils::fromIdn( const QString &addrSpec )
00918 {
00919 const int atPos = addrSpec.lastIndexOf( '@' );
00920 if ( atPos == -1 ) {
00921 return addrSpec;
00922 }
00923
00924 QString idn = KUrl::fromAce( addrSpec.mid( atPos + 1 ).toLatin1() );
00925 if ( idn.isEmpty() ) {
00926 return QString();
00927 }
00928
00929 return addrSpec.left( atPos + 1 ) + idn;
00930 }
00931
00932
00933 QString KPIMUtils::toIdn( const QString &addrSpec )
00934 {
00935 const int atPos = addrSpec.lastIndexOf( '@' );
00936 if ( atPos == -1 ) {
00937 return addrSpec;
00938 }
00939
00940 QString idn = KUrl::toAce( addrSpec.mid( atPos + 1 ) );
00941 if ( idn.isEmpty() ) {
00942 return addrSpec;
00943 }
00944
00945 return addrSpec.left( atPos + 1 ) + idn;
00946 }
00947
00948
00949 QString KPIMUtils::normalizeAddressesAndDecodeIdn( const QString &str )
00950 {
00951
00952 if ( str.isEmpty() ) {
00953 return str;
00954 }
00955
00956 const QStringList addressList = splitAddressList( str );
00957 QStringList normalizedAddressList;
00958
00959 QByteArray displayName, addrSpec, comment;
00960
00961 for ( QStringList::ConstIterator it = addressList.begin();
00962 ( it != addressList.end() );
00963 ++it ) {
00964 if ( !(*it).isEmpty() ) {
00965 if ( splitAddress( (*it).toUtf8(),
00966 displayName, addrSpec, comment ) == AddressOk ) {
00967
00968 displayName = KMime::decodeRFC2047String(displayName).toUtf8();
00969 comment = KMime::decodeRFC2047String(comment).toUtf8();
00970
00971 normalizedAddressList
00972 << normalizedAddress( QString::fromUtf8( displayName ),
00973 fromIdn( QString::fromUtf8( addrSpec ) ),
00974 QString::fromUtf8( comment ) );
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 }
01010 }
01011 }
01012
01013
01014
01015
01016
01017
01018 return normalizedAddressList.join( ", " );
01019 }
01020
01021
01022
01023 static QString escapeQuotes( const QString &str )
01024 {
01025 if ( str.isEmpty() ) {
01026 return QString();
01027 }
01028
01029 QString escaped;
01030
01031 escaped.reserve( 2 * str.length() );
01032 unsigned int len = 0;
01033 for ( int i = 0; i < str.length(); ++i, ++len ) {
01034 if ( str[i] == '"' ) {
01035 escaped[len] = '\\';
01036 ++len;
01037 } else if ( str[i] == '\\' ) {
01038 escaped[len] = '\\';
01039 ++len;
01040 ++i;
01041 if ( i >= str.length() ) {
01042 break;
01043 }
01044 }
01045 escaped[len] = str[i];
01046 }
01047 escaped.truncate( len );
01048 return escaped;
01049 }
01050
01051
01052 QString KPIMUtils::quoteNameIfNecessary( const QString &str )
01053 {
01054 QString quoted = str;
01055
01056 QRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
01057
01058 if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
01059 quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
01060 } else if ( quoted.indexOf( needQuotes ) != -1 ) {
01061 quoted = "\"" + escapeQuotes( quoted ) + "\"";
01062 }
01063
01064 return quoted;
01065 }