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