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 if ( !insidequote && ( commentlevel == 0 ) ) {
00090 addr = aStr.mid( addrstart, index - addrstart );
00091 if ( !addr.isEmpty() ) {
00092 list += addr.simplified();
00093 }
00094 addrstart = index + 1;
00095 }
00096 break;
00097 }
00098 }
00099
00100 if ( !insidequote && ( commentlevel == 0 ) ) {
00101 addr = aStr.mid( addrstart, aStr.length() - addrstart );
00102 if ( !addr.isEmpty() ) {
00103 list += addr.simplified();
00104 }
00105 }
00106
00107 return list;
00108 }
00109
00110
00111
00112 KPIMUtils::EmailParseResult splitAddressInternal( const QByteArray address,
00113 QByteArray &displayName,
00114 QByteArray &addrSpec,
00115 QByteArray &comment,
00116 bool allowMultipleAddresses )
00117 {
00118
00119 displayName = "";
00120 addrSpec = "";
00121 comment = "";
00122
00123 if ( address.isEmpty() ) {
00124 return AddressEmpty;
00125 }
00126
00127
00128
00129
00130
00131 enum {
00132 TopLevel,
00133 InComment,
00134 InAngleAddress
00135 } context = TopLevel;
00136 bool inQuotedString = false;
00137 int commentLevel = 0;
00138 bool stop = false;
00139
00140 for ( const char *p = address.data(); *p && !stop; ++p ) {
00141 switch ( context ) {
00142 case TopLevel :
00143 {
00144 switch ( *p ) {
00145 case '"' :
00146 inQuotedString = !inQuotedString;
00147 displayName += *p;
00148 break;
00149 case '(' :
00150 if ( !inQuotedString ) {
00151 context = InComment;
00152 commentLevel = 1;
00153 } else {
00154 displayName += *p;
00155 }
00156 break;
00157 case '<' :
00158 if ( !inQuotedString ) {
00159 context = InAngleAddress;
00160 } else {
00161 displayName += *p;
00162 }
00163 break;
00164 case '\\' :
00165 displayName += *p;
00166 ++p;
00167 if ( *p ) {
00168 displayName += *p;
00169 } else {
00170 return UnexpectedEnd;
00171 }
00172 break;
00173 case ',' :
00174 if ( !inQuotedString ) {
00175 if ( allowMultipleAddresses ) {
00176 stop = true;
00177 } else {
00178 return UnexpectedComma;
00179 }
00180 } else {
00181 displayName += *p;
00182 }
00183 break;
00184 default :
00185 displayName += *p;
00186 }
00187 break;
00188 }
00189 case InComment :
00190 {
00191 switch ( *p ) {
00192 case '(' :
00193 ++commentLevel;
00194 comment += *p;
00195 break;
00196 case ')' :
00197 --commentLevel;
00198 if ( commentLevel == 0 ) {
00199 context = TopLevel;
00200 comment += ' ';
00201 } else {
00202 comment += *p;
00203 }
00204 break;
00205 case '\\' :
00206 comment += *p;
00207 ++p;
00208 if ( *p ) {
00209 comment += *p;
00210 } else {
00211 return UnexpectedEnd;
00212 }
00213 break;
00214 default :
00215 comment += *p;
00216 }
00217 break;
00218 }
00219 case InAngleAddress :
00220 {
00221 switch ( *p ) {
00222 case '"' :
00223 inQuotedString = !inQuotedString;
00224 addrSpec += *p;
00225 break;
00226 case '>' :
00227 if ( !inQuotedString ) {
00228 context = TopLevel;
00229 } else {
00230 addrSpec += *p;
00231 }
00232 break;
00233 case '\\' :
00234 addrSpec += *p;
00235 ++p;
00236 if ( *p ) {
00237 addrSpec += *p;
00238 } else {
00239 return UnexpectedEnd;
00240 }
00241 break;
00242 default :
00243 addrSpec += *p;
00244 }
00245 break;
00246 }
00247 }
00248 }
00249
00250 if ( inQuotedString ) {
00251 return UnbalancedQuote;
00252 }
00253 if ( context == InComment ) {
00254 return UnbalancedParens;
00255 }
00256 if ( context == InAngleAddress ) {
00257 return UnclosedAngleAddr;
00258 }
00259
00260 displayName = displayName.trimmed();
00261 comment = comment.trimmed();
00262 addrSpec = addrSpec.trimmed();
00263
00264 if ( addrSpec.isEmpty() ) {
00265 if ( displayName.isEmpty() ) {
00266 return NoAddressSpec;
00267 } else {
00268 addrSpec = displayName;
00269 displayName.truncate( 0 );
00270 }
00271 }
00272
00273
00274
00275
00276
00277 return AddressOk;
00278 }
00279
00280
00281 EmailParseResult KPIMUtils::splitAddress( const QByteArray &address,
00282 QByteArray &displayName,
00283 QByteArray &addrSpec,
00284 QByteArray &comment )
00285 {
00286 return splitAddressInternal( address, displayName, addrSpec, comment,
00287 false );
00288 }
00289
00290
00291 EmailParseResult KPIMUtils::splitAddress( const QString &address,
00292 QString &displayName,
00293 QString &addrSpec,
00294 QString &comment )
00295 {
00296 QByteArray d, a, c;
00297 EmailParseResult result = splitAddress( address.toUtf8(), d, a, c );
00298
00299 if ( result == AddressOk ) {
00300 displayName = QString::fromUtf8( d );
00301 addrSpec = QString::fromUtf8( a );
00302 comment = QString::fromUtf8( c );
00303 }
00304 return result;
00305 }
00306
00307
00308 EmailParseResult KPIMUtils::isValidAddress( const QString &aStr )
00309 {
00310
00311
00312 if ( aStr.isEmpty() ) {
00313 return AddressEmpty;
00314 }
00315
00316
00317
00318
00319
00320
00321
00322
00323 bool tooManyAtsFlag = false;
00324
00325 int atCount = aStr.count( '@' );
00326 if ( atCount > 1 ) {
00327 tooManyAtsFlag = true;
00328 } else if ( atCount == 0 ) {
00329 return TooFewAts;
00330 }
00331
00332
00333
00334
00335 enum {
00336 TopLevel,
00337 InComment,
00338 InAngleAddress
00339 } context = TopLevel;
00340 bool inQuotedString = false;
00341 int commentLevel = 0;
00342
00343 unsigned int strlen = aStr.length();
00344
00345 for ( unsigned int index=0; index < strlen; index++ ) {
00346 switch ( context ) {
00347 case TopLevel :
00348 {
00349 switch ( aStr[index].toLatin1() ) {
00350 case '"' :
00351 inQuotedString = !inQuotedString;
00352 break;
00353 case '(' :
00354 if ( !inQuotedString ) {
00355 context = InComment;
00356 commentLevel = 1;
00357 }
00358 break;
00359 case '[' :
00360 if ( !inQuotedString ) {
00361 return InvalidDisplayName;
00362 }
00363 break;
00364 case ']' :
00365 if ( !inQuotedString ) {
00366 return InvalidDisplayName;
00367 }
00368 break;
00369 case ':' :
00370 if ( !inQuotedString ) {
00371 return DisallowedChar;
00372 }
00373 break;
00374 case '<' :
00375 if ( !inQuotedString ) {
00376 context = InAngleAddress;
00377 }
00378 break;
00379 case '\\' :
00380 ++index;
00381 if ( ( index + 1 ) > strlen ) {
00382 return UnexpectedEnd;
00383 }
00384 break;
00385 case ',' :
00386 if ( !inQuotedString ) {
00387 return UnexpectedComma;
00388 }
00389 break;
00390 case ')' :
00391 if ( !inQuotedString ) {
00392 return UnbalancedParens;
00393 }
00394 break;
00395 case '>' :
00396 if ( !inQuotedString ) {
00397 return UnopenedAngleAddr;
00398 }
00399 break;
00400 case '@' :
00401 if ( !inQuotedString ) {
00402 if ( index == 0 ) {
00403 return MissingLocalPart;
00404 } else if ( index == strlen-1 ) {
00405 return MissingDomainPart;
00406 break;
00407 }
00408 } else if ( inQuotedString ) {
00409 --atCount;
00410 if ( atCount == 1 ) {
00411 tooManyAtsFlag = false;
00412 }
00413 }
00414 break;
00415 }
00416 break;
00417 }
00418 case InComment :
00419 {
00420 switch ( aStr[index].toLatin1() ) {
00421 case '(' :
00422 ++commentLevel;
00423 break;
00424 case ')' :
00425 --commentLevel;
00426 if ( commentLevel == 0 ) {
00427 context = TopLevel;
00428 }
00429 break;
00430 case '\\' :
00431 ++index;
00432 if ( ( index + 1 ) > strlen ) {
00433 return UnexpectedEnd;
00434 }
00435 break;
00436 }
00437 break;
00438 }
00439
00440 case InAngleAddress :
00441 {
00442 switch ( aStr[index].toLatin1() ) {
00443 case ',' :
00444 if ( !inQuotedString ) {
00445 return UnexpectedComma;
00446 }
00447 break;
00448 case '"' :
00449 inQuotedString = !inQuotedString;
00450 break;
00451 case '@' :
00452 if ( inQuotedString ) {
00453 --atCount;
00454 if ( atCount == 1 ) {
00455 tooManyAtsFlag = false;
00456 }
00457 }
00458 break;
00459 case '>' :
00460 if ( !inQuotedString ) {
00461 context = TopLevel;
00462 break;
00463 }
00464 break;
00465 case '\\' :
00466 ++index;
00467 if ( ( index + 1 ) > strlen ) {
00468 return UnexpectedEnd;
00469 }
00470 break;
00471 }
00472 break;
00473 }
00474 }
00475 }
00476
00477 if ( atCount == 0 && !inQuotedString ) {
00478 return TooFewAts;
00479 }
00480
00481 if ( inQuotedString ) {
00482 return UnbalancedQuote;
00483 }
00484
00485 if ( context == InComment ) {
00486 return UnbalancedParens;
00487 }
00488
00489 if ( context == InAngleAddress ) {
00490 return UnclosedAngleAddr;
00491 }
00492
00493 if ( tooManyAtsFlag ) {
00494 return TooManyAts;
00495 }
00496
00497 return AddressOk;
00498 }
00499
00500
00501 KPIMUtils::EmailParseResult KPIMUtils::isValidAddressList( const QString &aStr,
00502 QString &badAddr )
00503 {
00504 if ( aStr.isEmpty() ) {
00505 return AddressEmpty;
00506 }
00507
00508 const QStringList list = splitAddressList( aStr );
00509
00510 QStringList::const_iterator it = list.begin();
00511 EmailParseResult errorCode = AddressOk;
00512 for ( it = list.begin(); it != list.end(); ++it ) {
00513 errorCode = isValidAddress( *it );
00514 if ( errorCode != AddressOk ) {
00515 badAddr = ( *it );
00516 break;
00517 }
00518 }
00519 return errorCode;
00520 }
00521
00522
00523 QString KPIMUtils::emailParseResultToString( EmailParseResult errorCode )
00524 {
00525 switch ( errorCode ) {
00526 case TooManyAts :
00527 return i18n( "The email address you entered is not valid because it "
00528 "contains more than one @. "
00529 "You will not create valid messages if you do not "
00530 "change your address." );
00531 case TooFewAts :
00532 return i18n( "The email address you entered is not valid because it "
00533 "does not contain a @."
00534 "You will not create valid messages if you do not "
00535 "change your address." );
00536 case AddressEmpty :
00537 return i18n( "You have to enter something in the email address field." );
00538 case MissingLocalPart :
00539 return i18n( "The email address you entered is not valid because it "
00540 "does not contain a local part." );
00541 case MissingDomainPart :
00542 return i18n( "The email address you entered is not valid because it "
00543 "does not contain a domain part." );
00544 case UnbalancedParens :
00545 return i18n( "The email address you entered is not valid because it "
00546 "contains unclosed comments/brackets." );
00547 case AddressOk :
00548 return i18n( "The email address you entered is valid." );
00549 case UnclosedAngleAddr :
00550 return i18n( "The email address you entered is not valid because it "
00551 "contains an unclosed angle bracket." );
00552 case UnopenedAngleAddr :
00553 return i18n( "The email address you entered is not valid because it "
00554 "contains too many closing angle brackets." );
00555 case UnexpectedComma :
00556 return i18n( "The email address you have entered is not valid because it "
00557 "contains an unexpected comma." );
00558 case UnexpectedEnd :
00559 return i18n( "The email address you entered is not valid because it ended "
00560 "unexpectedly. This probably means you have used an escaping "
00561 "type character like a '\\' as the last character in your "
00562 "email address." );
00563 case UnbalancedQuote :
00564 return i18n( "The email address you entered is not valid because it "
00565 "contains quoted text which does not end." );
00566 case NoAddressSpec :
00567 return i18n( "The email address you entered is not valid because it "
00568 "does not seem to contain an actual email address, i.e. "
00569 "something of the form joe@example.org." );
00570 case DisallowedChar :
00571 return i18n( "The email address you entered is not valid because it "
00572 "contains an illegal character." );
00573 case InvalidDisplayName :
00574 return i18n( "The email address you have entered is not valid because it "
00575 "contains an invalid display name." );
00576 }
00577 return i18n( "Unknown problem with email address" );
00578 }
00579
00580
00581 bool KPIMUtils::isValidSimpleAddress( const QString &aStr )
00582 {
00583
00584
00585 if ( aStr.isEmpty() ) {
00586 return false;
00587 }
00588
00589 int atChar = aStr.lastIndexOf( '@' );
00590 QString domainPart = aStr.mid( atChar + 1 );
00591 QString localPart = aStr.left( atChar );
00592 bool tooManyAtsFlag = false;
00593 bool inQuotedString = false;
00594 int atCount = localPart.count( '@' );
00595
00596 unsigned int strlen = localPart.length();
00597 for ( unsigned int index=0; index < strlen; index++ ) {
00598 switch( localPart[ index ].toLatin1() ) {
00599 case '"' :
00600 inQuotedString = !inQuotedString;
00601 break;
00602 case '@' :
00603 if ( inQuotedString ) {
00604 --atCount;
00605 if ( atCount == 0 ) {
00606 tooManyAtsFlag = false;
00607 }
00608 }
00609 break;
00610 }
00611 }
00612
00613 QString addrRx =
00614 "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@";
00615
00616 if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
00617 addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
00618 }
00619 if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
00620 addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
00621 } else {
00622 addrRx += "[\\w-]+(\\.[\\w-]+)*";
00623 }
00624 QRegExp rx( addrRx );
00625 return rx.exactMatch( aStr ) && !tooManyAtsFlag;
00626 }
00627
00628
00629 QString KPIMUtils::simpleEmailAddressErrorMsg()
00630 {
00631 return i18n( "The email address you entered is not valid because it "
00632 "does not seem to contain an actual email address, i.e. "
00633 "something of the form joe@example.org." );
00634 }
00635
00636
00637 QByteArray KPIMUtils::extractEmailAddress( const QByteArray &address )
00638 {
00639 QByteArray dummy1, dummy2, addrSpec;
00640 EmailParseResult result =
00641 splitAddressInternal( address, dummy1, addrSpec, dummy2,
00642 false );
00643 if ( result != AddressOk ) {
00644 addrSpec = QByteArray();
00645 if ( result != AddressEmpty ) {
00646 kDebug()
00647 << "Input: aStr\nError:"
00648 << emailParseResultToString( result );
00649 }
00650 }
00651
00652 return addrSpec;
00653 }
00654
00655
00656 QString KPIMUtils::extractEmailAddress( const QString &address )
00657 {
00658 return QString::fromUtf8( extractEmailAddress( address.toUtf8() ) );
00659 }
00660
00661
00662 QByteArray KPIMUtils::firstEmailAddress( const QByteArray &addresses )
00663 {
00664 QByteArray dummy1, dummy2, addrSpec;
00665 EmailParseResult result =
00666 splitAddressInternal( addresses, dummy1, addrSpec, dummy2,
00667 true );
00668 if ( result != AddressOk ) {
00669 addrSpec = QByteArray();
00670 if ( result != AddressEmpty ) {
00671 kDebug()
00672 << "Input: aStr\nError:"
00673 << emailParseResultToString( result );
00674 }
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 }
00974 }
00975 }
00976
00977
00978
00979
00980
00981 return normalizedAddressList.join( ", " );
00982 }
00983
00984
00985 QString KPIMUtils::normalizeAddressesAndEncodeIdn( const QString &str )
00986 {
00987
00988 if ( str.isEmpty() ) {
00989 return str;
00990 }
00991
00992 const QStringList addressList = splitAddressList( str );
00993 QStringList normalizedAddressList;
00994
00995 QByteArray displayName, addrSpec, comment;
00996
00997 for ( QStringList::ConstIterator it = addressList.begin();
00998 ( it != addressList.end() );
00999 ++it ) {
01000 if ( !(*it).isEmpty() ) {
01001 if ( splitAddress( (*it).toUtf8(),
01002 displayName, addrSpec, comment ) == AddressOk ) {
01003
01004 normalizedAddressList << normalizedAddress( QString::fromUtf8( displayName ),
01005 toIdn( QString::fromUtf8( addrSpec ) ),
01006 QString::fromUtf8( comment ) );
01007 }
01008 }
01009 }
01010
01011
01012
01013
01014
01015
01016 return normalizedAddressList.join( ", " );
01017 }
01018
01019
01020
01021 static QString escapeQuotes( const QString &str )
01022 {
01023 if ( str.isEmpty() ) {
01024 return QString();
01025 }
01026
01027 QString escaped;
01028
01029 escaped.reserve( 2 * str.length() );
01030 unsigned int len = 0;
01031 for ( int i = 0; i < str.length(); ++i, ++len ) {
01032 if ( str[i] == '"' ) {
01033 escaped[len] = '\\';
01034 ++len;
01035 } else if ( str[i] == '\\' ) {
01036 escaped[len] = '\\';
01037 ++len;
01038 ++i;
01039 if ( i >= str.length() ) {
01040 break;
01041 }
01042 }
01043 escaped[len] = str[i];
01044 }
01045 escaped.truncate( len );
01046 return escaped;
01047 }
01048
01049
01050 QString KPIMUtils::quoteNameIfNecessary( const QString &str )
01051 {
01052 QString quoted = str;
01053
01054 QRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
01055
01056 if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
01057 quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
01058 } else if ( quoted.indexOf( needQuotes ) != -1 ) {
01059 quoted = "\"" + escapeQuotes( quoted ) + "\"";
01060 }
01061
01062 return quoted;
01063 }