• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

kpimutils

email.cpp

Go to the documentation of this file.
00001 /*
00002   This file is part of the kpimutils library.
00003   Copyright (c) 2004 Matt Douhan <matt@fruitsalad.org>
00004 
00005   This library is free software; you can redistribute it and/or
00006   modify it under the terms of the GNU Library General Public
00007   License as published by the Free Software Foundation; either
00008   version 2 of the License, or (at your option) any later version.
00009 
00010   This library is distributed in the hope that it will be useful,
00011   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013   Library General Public License for more details.
00014 
00015   You should have received a copy of the GNU Library General Public License
00016   along with this library; see the file COPYING.LIB.  If not, write to
00017   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018   Boston, MA 02110-1301, USA.
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   // Features:
00044   // - always ignores quoted characters
00045   // - ignores everything (including parentheses and commas)
00046   //   inside quoted strings
00047   // - supports nested comments
00048   // - ignores everything (including double quotes and commas)
00049   //   inside comments
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     // the following conversion to latin1 is o.k. because
00064     // we can safely ignore all non-latin1 characters
00065     switch ( aStr[index].toLatin1() ) {
00066     case '"' : // start or end of quoted string
00067       if ( commentlevel == 0 ) {
00068         insidequote = !insidequote;
00069       }
00070       break;
00071     case '(' : // start of comment
00072       if ( !insidequote ) {
00073         commentlevel++;
00074       }
00075       break;
00076     case ')' : // end of comment
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 '\\' : // quoted character
00087       index++; // ignore the quoted character
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   // append the last address to the list
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 // Used by KPIMUtils::splitAddress(...) and KPIMUtils::firstEmailAddress(...).
00116 KPIMUtils::EmailParseResult splitAddressInternal( const QByteArray  address,
00117                                                   QByteArray &displayName,
00118                                                   QByteArray &addrSpec,
00119                                                   QByteArray &comment,
00120                                                   bool allowMultipleAddresses )
00121 {
00122   //  kDebug(5321) << "KMMessage::splitAddress(" << address << ")";
00123 
00124   displayName = "";
00125   addrSpec = "";
00126   comment = "";
00127 
00128   if ( address.isEmpty() ) {
00129     return AddressEmpty;
00130   }
00131 
00132   // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
00133   // The purpose is to extract a displayable string from the mailboxes.
00134   // Comments in the addr-spec are not handled. No error checking is done.
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 '\\' : // quoted character
00170         displayName += *p;
00171         ++p; // skip the '\'
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 += ' '; // separate the text of several comments
00206         } else {
00207           comment += *p;
00208         }
00209         break;
00210       case '\\' : // quoted character
00211         comment += *p;
00212         ++p; // skip the '\'
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 '\\' : // quoted character
00239         addrSpec += *p;
00240         ++p; // skip the '\'
00241         if ( *p ) {
00242           addrSpec += *p;
00243         } else {
00244           return UnexpectedEnd;
00245         }
00246         break;
00247       default :
00248         addrSpec += *p;
00249       }
00250       break;
00251     }
00252     } // switch ( context )
00253   }
00254   // check for errors
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     kDebug(5321) << "display-name : \"" << displayName << "\"";
00279     kDebug(5321) << "comment      : \"" << comment << "\"";
00280     kDebug(5321) << "addr-spec    : \"" << addrSpec << "\"";
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/* don't allow multiple addresses */ );
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   // If we are passed an empty string bail right away no need to process
00316   // further and waste resources
00317   if ( aStr.isEmpty() ) {
00318     return AddressEmpty;
00319   }
00320 
00321   // count how many @'s are in the string that is passed to us
00322   // if 0 or > 1 take action
00323   // at this point to many @'s cannot bail out right away since
00324   // @ is allowed in qoutes, so we use a bool to keep track
00325   // and then make a judgment further down in the parser
00326   // FIXME count only @ not in double quotes
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   // The main parser, try and catch all weird and wonderful
00338   // mistakes users and/or machines can create
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 '\\' : // quoted character
00385           ++index; // skip the '\'
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 ) {  // Missing local part
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 '\\' : // quoted character
00436           ++index; // skip the '\'
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 '\\' : // quoted character
00471           ++index; // skip the '\'
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   // If we are passed an empty string bail right away no need to process further
00589   // and waste resources
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/* don't allow multiple addresses */ );
00648   if ( result != AddressOk ) {
00649     addrSpec = QByteArray();
00650     kDebug(5321) // << "\n"
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/* allow multiple addresses */ );
00671   if ( result != AddressOk ) {
00672     addrSpec = QByteArray();
00673     kDebug(5321) // << "\n"
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   // Find the '@' of the email address
00704   // skipping all '@' inside "(...)" comments:
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; // found it
00717       }
00718     }
00719     ++i;
00720   }
00721 
00722   if ( !iAd ) {
00723     // We suppose the user is typing the string manually and just
00724     // has not finished typing the mail address part.
00725     // So we take everything that's left of the '<' as name and the rest as mail
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     // Loop backwards until we find the start of the string
00741     // or a ',' that is outside of a comment
00742     //          and outside of quoted text before the leading '<'.
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 ); // all comment stuff is part of the name
00755         }
00756       } else if ( bInQuotesOutsideOfEmail ) {
00757         if ( cQuotes == c ) {
00758           bInQuotesOutsideOfEmail = false;
00759         } else {
00760           name.prepend( c );
00761         }
00762       } else {
00763         // found the start of this addressee ?
00764         if ( ',' == c ) {
00765           break;
00766         }
00767         // stuff is before the leading '<' ?
00768         if ( iMailStart ) {
00769           if ( cQuotes == c ) {
00770             bInQuotesOutsideOfEmail = true; // end of quoted text found
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     // Loop forward until we find the end of the string
00804     // or a ',' that is outside of a comment
00805     //          and outside of quoted text behind the trailing '>'.
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             // nested ")", add it
00820             name.append( ')' ); // name can't be empty here
00821           }
00822         } else {
00823           if ( '(' == c ) {
00824             // nested "("
00825             ++parenthesesNesting;
00826           }
00827           name.append( c ); // all comment stuff is part of the name
00828         }
00829       } else if ( bInQuotesOutsideOfEmail ) {
00830         if ( cQuotes == c ) {
00831           bInQuotesOutsideOfEmail = false;
00832         } else {
00833           name.append( c );
00834         }
00835       } else {
00836         // found the end of this addressee ?
00837         if ( ',' == c ) {
00838           break;
00839         }
00840         // stuff is behind the trailing '>' ?
00841         if ( iMailEnd ){
00842           if ( cQuotes == c ) {
00843             bInQuotesOutsideOfEmail = true; // start of quoted text found
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   //  kDebug(5321) << "KPIMUtils::normalizeAddressesAndDecodeIDNs( \""
00942   //                << str << "\" )";
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     kDebug(5321) << "normalizedAddressList: \""
00970     << normalizedAddressList.join( ", " )
00971     << "\"";
00972   */
00973   return normalizedAddressList.join( ", " );
00974 }
00975 
00976 //-----------------------------------------------------------------------------
00977 QString KPIMUtils::normalizeAddressesAndEncodeIdn( const QString &str )
00978 {
00979   //kDebug(5321) << "KPIMUtils::normalizeAddressesAndEncodeIDNs( \""
00980   //              << str << "\" )";
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     kDebug(5321) << "normalizedAddressList: \""
01009     << normalizedAddressList.join( ", " )
01010     << "\"";
01011   */
01012   return normalizedAddressList.join( ", " );
01013 }
01014 
01015 //-----------------------------------------------------------------------------
01016 // Escapes unescaped doublequotes in str.
01017 static QString escapeQuotes( const QString &str )
01018 {
01019   if ( str.isEmpty() ) {
01020     return QString();
01021   }
01022 
01023   QString escaped;
01024   // reserve enough memory for the worst case ( """..."" -> \"\"\"...\"\" )
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] == '"' ) { // unescaped doublequote
01029       escaped[len] = '\\';
01030       ++len;
01031     } else if ( str[i] == '\\' ) { // escaped character
01032       escaped[len] = '\\';
01033       ++len;
01034       ++i;
01035       if ( i >= str.length() ) { // handle trailing '\' gracefully
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   // avoid double quoting
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 }

kpimutils

Skip menu "kpimutils"
  • Main Page
  • Modules
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal