libemailfunctions

email.cpp

00001 /*  -*- mode: C++; c-file-style: "gnu" -*-
00002 
00003     This file is part of kdepim.
00004     Copyright (c) 2004 KDEPIM developers
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "email.h"
00023 
00024 #include <kdebug.h>
00025 #include <klocale.h>
00026 #include <kidna.h>
00027 
00028 #include <qregexp.h>
00029 
00030 //-----------------------------------------------------------------------------
00031 QStringList KPIM::splitEmailAddrList(const QString& aStr)
00032 {
00033   // Features:
00034   // - always ignores quoted characters
00035   // - ignores everything (including parentheses and commas)
00036   //   inside quoted strings
00037   // - supports nested comments
00038   // - ignores everything (including double quotes and commas)
00039   //   inside comments
00040 
00041   QStringList list;
00042 
00043   if (aStr.isEmpty())
00044     return list;
00045 
00046   QString addr;
00047   uint addrstart = 0;
00048   int commentlevel = 0;
00049   bool insidequote = false;
00050 
00051   for (uint index=0; index<aStr.length(); index++) {
00052     // the following conversion to latin1 is o.k. because
00053     // we can safely ignore all non-latin1 characters
00054     switch (aStr[index].latin1()) {
00055     case '"' : // start or end of quoted string
00056       if (commentlevel == 0)
00057         insidequote = !insidequote;
00058       break;
00059     case '(' : // start of comment
00060       if (!insidequote)
00061         commentlevel++;
00062       break;
00063     case ')' : // end of comment
00064       if (!insidequote) {
00065         if (commentlevel > 0)
00066           commentlevel--;
00067         else {
00068           kdDebug(5300) << "Error in address splitting: Unmatched ')'"
00069                         << endl;
00070           return list;
00071         }
00072       }
00073       break;
00074     case '\\' : // quoted character
00075       index++; // ignore the quoted character
00076       break;
00077     case ',' :
00078       if (!insidequote && (commentlevel == 0)) {
00079         addr = aStr.mid(addrstart, index-addrstart);
00080         if (!addr.isEmpty())
00081           list += addr.simplifyWhiteSpace();
00082         addrstart = index+1;
00083       }
00084       break;
00085     }
00086   }
00087   // append the last address to the list
00088   if (!insidequote && (commentlevel == 0)) {
00089     addr = aStr.mid(addrstart, aStr.length()-addrstart);
00090     if (!addr.isEmpty())
00091       list += addr.simplifyWhiteSpace();
00092   }
00093   else
00094     kdDebug(5300) << "Error in address splitting: "
00095                   << "Unexpected end of address list"
00096                   << endl;
00097 
00098   return list;
00099 }
00100 
00101 //-----------------------------------------------------------------------------
00102 // Used by KPIM::splitAddress(...) and KPIM::getFirstEmailAddress(...).
00103 KPIM::EmailParseResult splitAddressInternal( const QCString& address,
00104                                              QCString & displayName,
00105                                              QCString & addrSpec,
00106                                              QCString & comment,
00107                                              bool allowMultipleAddresses )
00108 {
00109 //  kdDebug() << "KMMessage::splitAddress( " << address << " )" << endl;
00110 
00111   displayName = "";
00112   addrSpec = "";
00113   comment = "";
00114 
00115   if ( address.isEmpty() )
00116     return KPIM::AddressEmpty;
00117 
00118   // The following is a primitive parser for a mailbox-list (cf. RFC 2822).
00119   // The purpose is to extract a displayable string from the mailboxes.
00120   // Comments in the addr-spec are not handled. No error checking is done.
00121 
00122   enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
00123   bool inQuotedString = false;
00124   int commentLevel = 0;
00125   bool stop = false;
00126 
00127   for ( char* p = address.data(); *p && !stop; ++p ) {
00128     switch ( context ) {
00129     case TopLevel : {
00130       switch ( *p ) {
00131       case '"' : inQuotedString = !inQuotedString;
00132                  displayName += *p;
00133                  break;
00134       case '(' : if ( !inQuotedString ) {
00135                    context = InComment;
00136                    commentLevel = 1;
00137                  }
00138                  else
00139                    displayName += *p;
00140                  break;
00141       case '<' : if ( !inQuotedString ) {
00142                    context = InAngleAddress;
00143                  }
00144                  else
00145                    displayName += *p;
00146                  break;
00147       case '\\' : // quoted character
00148                  displayName += *p;
00149                  ++p; // skip the '\'
00150                  if ( *p )
00151                    displayName += *p;
00152                  else
00153                    return KPIM::UnexpectedEnd;
00154                  break;
00155       case ',' : if ( !inQuotedString ) {
00156                    if ( allowMultipleAddresses )
00157                      stop = true;
00158                    else
00159                      return KPIM::UnexpectedComma;
00160                  }
00161                  else
00162                    displayName += *p;
00163                  break;
00164       default :  displayName += *p;
00165       }
00166       break;
00167     }
00168     case InComment : {
00169       switch ( *p ) {
00170       case '(' : ++commentLevel;
00171                  comment += *p;
00172                  break;
00173       case ')' : --commentLevel;
00174                  if ( commentLevel == 0 ) {
00175                    context = TopLevel;
00176                    comment += ' '; // separate the text of several comments
00177                  }
00178                  else
00179                    comment += *p;
00180                  break;
00181       case '\\' : // quoted character
00182                  comment += *p;
00183                  ++p; // skip the '\'
00184                  if ( *p )
00185                    comment += *p;
00186                  else
00187                    return KPIM::UnexpectedEnd;
00188                  break;
00189       default :  comment += *p;
00190       }
00191       break;
00192     }
00193     case InAngleAddress : {
00194       switch ( *p ) {
00195       case '"' : inQuotedString = !inQuotedString;
00196                  addrSpec += *p;
00197                  break;
00198       case '>' : if ( !inQuotedString ) {
00199                    context = TopLevel;
00200                  }
00201                  else
00202                    addrSpec += *p;
00203                  break;
00204       case '\\' : // quoted character
00205                  addrSpec += *p;
00206                  ++p; // skip the '\'
00207                  if ( *p )
00208                    addrSpec += *p;
00209                  else
00210                    return KPIM::UnexpectedEnd;
00211                  break;
00212       default :  addrSpec += *p;
00213       }
00214       break;
00215     }
00216     } // switch ( context )
00217   }
00218   // check for errors
00219   if ( inQuotedString )
00220     return KPIM::UnbalancedQuote;
00221   if ( context == InComment )
00222     return KPIM::UnbalancedParens;
00223   if ( context == InAngleAddress )
00224     return KPIM::UnclosedAngleAddr;
00225 
00226   displayName = displayName.stripWhiteSpace();
00227   comment = comment.stripWhiteSpace();
00228   addrSpec = addrSpec.stripWhiteSpace();
00229 
00230   if ( addrSpec.isEmpty() ) {
00231     if ( displayName.isEmpty() )
00232       return KPIM::NoAddressSpec;
00233     else {
00234       addrSpec = displayName;
00235       displayName.truncate( 0 );
00236     }
00237   }
00238 /*
00239   kdDebug() << "display-name : \"" << displayName << "\"" << endl;
00240   kdDebug() << "comment      : \"" << comment << "\"" << endl;
00241   kdDebug() << "addr-spec    : \"" << addrSpec << "\"" << endl;
00242 */
00243   return KPIM::AddressOk;
00244 }
00245 
00246 
00247 //-----------------------------------------------------------------------------
00248 KPIM::EmailParseResult KPIM::splitAddress( const QCString& address,
00249                                            QCString & displayName,
00250                                            QCString & addrSpec,
00251                                            QCString & comment )
00252 {
00253   return splitAddressInternal( address, displayName, addrSpec, comment,
00254                                false /* don't allow multiple addresses */ );
00255 }
00256 
00257 
00258 //-----------------------------------------------------------------------------
00259 KPIM::EmailParseResult KPIM::splitAddress( const QString & address,
00260                                            QString & displayName,
00261                                            QString & addrSpec,
00262                                            QString & comment )
00263 {
00264   QCString d, a, c;
00265   KPIM::EmailParseResult result = splitAddress( address.utf8(), d, a, c );
00266   if ( result == AddressOk ) {
00267     displayName = QString::fromUtf8( d );
00268     addrSpec = QString::fromUtf8( a );
00269     comment = QString::fromUtf8( c );
00270   }
00271   return result;
00272 }
00273 
00274 
00275 //-----------------------------------------------------------------------------
00276 KPIM::EmailParseResult KPIM::isValidEmailAddress( const QString& aStr )
00277 {
00278   // If we are passed an empty string bail right away no need to process further
00279   // and waste resources
00280   if ( aStr.isEmpty() ) {
00281     return AddressEmpty;
00282   }
00283 
00284   // count how many @'s are in the string that is passed to us
00285   // if 0 or > 1 take action
00286   // at this point to many @'s cannot bail out right away since
00287   // @ is allowed in qoutes, so we use a bool to keep track
00288   // and then make a judgement further down in the parser
00289   // FIXME count only @ not in double quotes
00290 
00291   bool tooManyAtsFlag = false;
00292 
00293   int atCount = aStr.contains('@');
00294   if ( atCount > 1 ) {
00295     tooManyAtsFlag = true;;
00296   } else if ( atCount == 0 ) {
00297       return TooFewAts;
00298   }
00299 
00300   // The main parser, try and catch all weird and wonderful
00301   // mistakes users and/or machines can create
00302 
00303   enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
00304   bool inQuotedString = false;
00305   int commentLevel = 0;
00306 
00307   unsigned int strlen = aStr.length();
00308 
00309   for ( unsigned int index=0; index < strlen; index++ ) {
00310     switch ( context ) {
00311     case TopLevel : {
00312       switch ( aStr[index].latin1() ) {
00313         case '"' : inQuotedString = !inQuotedString;
00314           break;
00315         case '(' :
00316           if ( !inQuotedString ) {
00317             context = InComment;
00318             commentLevel = 1;
00319           }
00320           break;
00321         case '[' :
00322           if ( !inQuotedString ) {
00323             return InvalidDisplayName;
00324           }
00325           break;
00326         case ']' :
00327           if ( !inQuotedString ) {
00328             return InvalidDisplayName;
00329           }
00330           break;
00331         case ':' :
00332           if ( !inQuotedString ) {
00333             return DisallowedChar;
00334           }
00335           break;
00336         case '<' :
00337           if ( !inQuotedString ) {
00338             context = InAngleAddress;
00339           }
00340           break;
00341         case '\\' : // quoted character
00342           ++index; // skip the '\'
00343           if (( index + 1 )> strlen ) {
00344             return UnexpectedEnd;
00345           }
00346           break;
00347         case ',' :
00348           if ( !inQuotedString )
00349             return UnexpectedComma;
00350           break;
00351         case ')' :
00352           if ( !inQuotedString )
00353             return UnbalancedParens;
00354           break;
00355         case '>' :
00356           if ( !inQuotedString )
00357             return UnopenedAngleAddr;
00358           break;
00359         case '@' :
00360           if ( !inQuotedString ) {
00361             if ( index == 0 ) {  // Missing local part
00362               return MissingLocalPart;
00363             } else if( index == strlen-1 ) {
00364               return MissingDomainPart;
00365             }
00366           } else if ( inQuotedString ) {
00367             --atCount;
00368             if ( atCount == 1 ) {
00369               tooManyAtsFlag = false;
00370             }
00371           }
00372           break;
00373       }
00374       break;
00375     }
00376     case InComment : {
00377       switch ( aStr[index] ) {
00378         case '(' : ++commentLevel;
00379           break;
00380         case ')' : --commentLevel;
00381           if ( commentLevel == 0 ) {
00382             context = TopLevel;
00383           }
00384           break;
00385         case '\\' : // quoted character
00386           ++index; // skip the '\'
00387           if (( index + 1 )> strlen ) {
00388             return UnexpectedEnd;
00389           }
00390           break;
00391         }
00392         break;
00393     }
00394 
00395     case InAngleAddress : {
00396       switch ( aStr[index] ) {
00397         case ',' :
00398           if ( !inQuotedString ) {
00399             return UnexpectedComma;
00400           }
00401           break;
00402         case '"' : inQuotedString = !inQuotedString;
00403             break;
00404         case '@' :
00405           if ( inQuotedString ) {
00406             --atCount;
00407             if ( atCount == 1 ) {
00408               tooManyAtsFlag = false;
00409             }
00410           }
00411           break;
00412         case '>' :
00413           if ( !inQuotedString ) {
00414             context = TopLevel;
00415             break;
00416           }
00417           break;
00418         case '\\' : // quoted character
00419           ++index; // skip the '\'
00420           if (( index + 1 )> strlen ) {
00421             return UnexpectedEnd;
00422           }
00423           break;
00424         }
00425         break;
00426       }
00427     }
00428   }
00429 
00430   if ( atCount == 0 && !inQuotedString )
00431     return TooFewAts;
00432 
00433   if ( inQuotedString )
00434     return UnbalancedQuote;
00435 
00436   if ( context == InComment )
00437     return UnbalancedParens;
00438 
00439   if ( context == InAngleAddress )
00440     return UnclosedAngleAddr;
00441 
00442   if ( tooManyAtsFlag ) {
00443     return TooManyAts;
00444   }
00445   return AddressOk;
00446 }
00447 
00448 //-----------------------------------------------------------------------------
00449 QString KPIM::emailParseResultToString( EmailParseResult errorCode )
00450 {
00451   switch ( errorCode ) {
00452     case TooManyAts :
00453       return i18n("The email address you entered is not valid because it "
00454                 "contains more than one @. "
00455                 "You will not create valid messages if you do not "
00456                 "change your address.");
00457     case TooFewAts :
00458       return i18n("The email address you entered is not valid because it "
00459                 "does not contain a @."
00460                 "You will not create valid messages if you do not "
00461                 "change your address.");
00462     case AddressEmpty :
00463       return i18n("You have to enter something in the email address field.");
00464     case MissingLocalPart :
00465       return i18n("The email address you entered is not valid because it "
00466                 "does not contain a local part.");
00467     case MissingDomainPart :
00468       return i18n("The email address you entered is not valid because it "
00469                 "does not contain a domain part.");
00470     case UnbalancedParens :
00471       return i18n("The email address you entered is not valid because it "
00472                 "contains unclosed comments/brackets.");
00473     case AddressOk :
00474       return i18n("The email address you entered is valid.");
00475     case UnclosedAngleAddr :
00476       return i18n("The email address you entered is not valid because it "
00477                 "contains an unclosed anglebracket.");
00478     case UnopenedAngleAddr :
00479       return i18n("The email address you entered is not valid because it "
00480                 "contains an unopened anglebracket.");
00481     case UnexpectedComma :
00482       return i18n("The email address you have entered is not valid because it "
00483                 "contains an unexpected comma.");
00484     case UnexpectedEnd :
00485       return i18n("The email address you entered is not valid because it ended "
00486                 "unexpectedly, this probably means you have used an escaping type "
00487                 "character like an \\  as the last character in your email "
00488                 "address.");
00489     case UnbalancedQuote :
00490       return i18n("The email address you entered is not valid because it "
00491                   "contains quoted text which does not end.");
00492     case NoAddressSpec :
00493       return i18n("The email address you entered is not valid because it "
00494                   "does not seem to contain an actual email address, i.e. "
00495                   "something of the form joe@kde.org.");
00496     case DisallowedChar :
00497       return i18n("The email address you entered is not valid because it "
00498                   "contains an illegal character.");
00499     case InvalidDisplayName :
00500       return i18n("The email address you have entered is not valid because it "
00501                   "contains an invalid displayname.");
00502   }
00503   return i18n("Unknown problem with email address");
00504 }
00505 
00506 //-----------------------------------------------------------------------------
00507 bool KPIM::isValidSimpleEmailAddress( const QString& aStr )
00508 {
00509   // If we are passed an empty string bail right away no need to process further
00510   // and waste resources
00511   if ( aStr.isEmpty() ) {
00512     return false;
00513   }
00514 
00515   int atChar = aStr.findRev( '@' );
00516   QString domainPart = aStr.mid( atChar + 1);
00517   QString localPart = aStr.left( atChar );
00518   bool tooManyAtsFlag = false;
00519   bool inQuotedString = false;
00520   int atCount = localPart.contains( '@' );
00521 
00522   unsigned int strlen = localPart.length();
00523   for ( unsigned int index=0; index < strlen; index++ ) {
00524     switch( localPart[ index ].latin1() ) {
00525       case '"' : inQuotedString = !inQuotedString;
00526         break;
00527       case '@' :
00528         if ( inQuotedString ) {
00529           --atCount;
00530           if ( atCount == 0 ) {
00531             tooManyAtsFlag = false;
00532           }
00533         }
00534         break;
00535       }
00536   }
00537 
00538   QString addrRx = "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@";
00539   if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) {
00540     addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@";
00541   }
00542   if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) {
00543     addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]";
00544   } else {
00545     addrRx += "[\\w-]+(\\.[\\w-]+)*";
00546   }
00547   QRegExp rx( addrRx );
00548   return  rx.exactMatch( aStr ) && !tooManyAtsFlag;
00549 }
00550 
00551 //-----------------------------------------------------------------------------
00552 QString KPIM::simpleEmailAddressErrorMsg()
00553 {
00554       return i18n("The email address you entered is not valid because it "
00555                   "does not seem to contain an actual email address, i.e. "
00556                   "something of the form joe@kde.org.");
00557 }
00558 //-----------------------------------------------------------------------------
00559 QCString KPIM::getEmailAddress( const QCString & address )
00560 {
00561   QCString dummy1, dummy2, addrSpec;
00562   KPIM::EmailParseResult result =
00563     splitAddressInternal( address, dummy1, addrSpec, dummy2,
00564                           false /* don't allow multiple addresses */ );
00565   if ( result != AddressOk ) {
00566     addrSpec = QCString();
00567     kdDebug() // << k_funcinfo << "\n"
00568               << "Input: aStr\nError:"
00569               << emailParseResultToString( result ) << endl;
00570   }
00571 
00572   return addrSpec;
00573 }
00574 
00575 
00576 //-----------------------------------------------------------------------------
00577 QString KPIM::getEmailAddress( const QString & address )
00578 {
00579   return QString::fromUtf8( getEmailAddress( address.utf8() ) );
00580 }
00581 
00582 
00583 //-----------------------------------------------------------------------------
00584 QCString KPIM::getFirstEmailAddress( const QCString & addresses )
00585 {
00586   QCString dummy1, dummy2, addrSpec;
00587   KPIM::EmailParseResult result =
00588     splitAddressInternal( addresses, dummy1, addrSpec, dummy2,
00589                           true /* allow multiple addresses */ );
00590   if ( result != AddressOk ) {
00591     addrSpec = QCString();
00592     kdDebug() // << k_funcinfo << "\n"
00593               << "Input: aStr\nError:"
00594               << emailParseResultToString( result ) << endl;
00595   }
00596 
00597   return addrSpec;
00598 }
00599 
00600 
00601 //-----------------------------------------------------------------------------
00602 QString KPIM::getFirstEmailAddress( const QString & addresses )
00603 {
00604   return QString::fromUtf8( getFirstEmailAddress( addresses.utf8() ) );
00605 }
00606 
00607 
00608 //-----------------------------------------------------------------------------
00609 bool KPIM::getNameAndMail(const QString& aStr, QString& name, QString& mail)
00610 {
00611   name = QString::null;
00612   mail = QString::null;
00613 
00614   const int len=aStr.length();
00615   const char cQuotes = '"';
00616 
00617   bool bInComment, bInQuotesOutsideOfEmail;
00618   int i=0, iAd=0, iMailStart=0, iMailEnd=0;
00619   QChar c;
00620 
00621   // Find the '@' of the email address
00622   // skipping all '@' inside "(...)" comments:
00623   bInComment = false;
00624   while( i < len ){
00625     c = aStr[i];
00626     if( !bInComment ){
00627       if( '(' == c ){
00628         bInComment = true;
00629       }else{
00630         if( '@' == c ){
00631           iAd = i;
00632           break; // found it
00633         }
00634       }
00635     }else{
00636       if( ')' == c ){
00637         bInComment = false;
00638       }
00639     }
00640     ++i;
00641   }
00642 
00643   if ( !iAd ) {
00644     // We suppose the user is typing the string manually and just
00645     // has not finished typing the mail address part.
00646     // So we take everything that's left of the '<' as name and the rest as mail
00647     for( i = 0; len > i; ++i ) {
00648       c = aStr[i];
00649       if( '<' != c )
00650         name.append( c );
00651       else
00652         break;
00653     }
00654     mail = aStr.mid( i+1 );
00655     if ( mail.endsWith( ">" ) )
00656       mail.truncate( mail.length() - 1 );
00657 
00658   } else {
00659     // Loop backwards until we find the start of the string
00660     // or a ',' that is outside of a comment
00661     //          and outside of quoted text before the leading '<'.
00662     bInComment = false;
00663     bInQuotesOutsideOfEmail = false;
00664     for( i = iAd-1; 0 <= i; --i ) {
00665       c = aStr[i];
00666       if( bInComment ) {
00667         if( '(' == c ) {
00668           if( !name.isEmpty() )
00669             name.prepend( ' ' );
00670           bInComment = false;
00671         } else {
00672           name.prepend( c ); // all comment stuff is part of the name
00673         }
00674       }else if( bInQuotesOutsideOfEmail ){
00675         if( cQuotes == c )
00676           bInQuotesOutsideOfEmail = false;
00677         else
00678           name.prepend( c );
00679       }else{
00680         // found the start of this addressee ?
00681         if( ',' == c )
00682           break;
00683         // stuff is before the leading '<' ?
00684         if( iMailStart ){
00685           if( cQuotes == c )
00686             bInQuotesOutsideOfEmail = true; // end of quoted text found
00687           else
00688             name.prepend( c );
00689         }else{
00690           switch( c ){
00691             case '<':
00692               iMailStart = i;
00693               break;
00694             case ')':
00695               if( !name.isEmpty() )
00696                 name.prepend( ' ' );
00697               bInComment = true;
00698               break;
00699             default:
00700               if( ' ' != c )
00701                 mail.prepend( c );
00702           }
00703         }
00704       }
00705     }
00706 
00707     name = name.simplifyWhiteSpace();
00708     mail = mail.simplifyWhiteSpace();
00709 
00710     if( mail.isEmpty() )
00711       return false;
00712 
00713     mail.append('@');
00714 
00715     // Loop forward until we find the end of the string
00716     // or a ',' that is outside of a comment
00717     //          and outside of quoted text behind the trailing '>'.
00718     bInComment = false;
00719     bInQuotesOutsideOfEmail = false;
00720     int parenthesesNesting = 0;
00721     for( i = iAd+1; len > i; ++i ) {
00722       c = aStr[i];
00723       if( bInComment ){
00724         if( ')' == c ){
00725           if ( --parenthesesNesting == 0 ) {
00726             bInComment = false;
00727             if( !name.isEmpty() )
00728               name.append( ' ' );
00729           } else {
00730             // nested ")", add it
00731             name.append( ')' ); // name can't be empty here
00732           }
00733         } else {
00734           if( '(' == c ) {
00735             // nested "("
00736             ++parenthesesNesting;
00737           }
00738           name.append( c ); // all comment stuff is part of the name
00739         }
00740       }else if( bInQuotesOutsideOfEmail ){
00741         if( cQuotes == c )
00742           bInQuotesOutsideOfEmail = false;
00743         else
00744           name.append( c );
00745       }else{
00746         // found the end of this addressee ?
00747         if( ',' == c )
00748           break;
00749         // stuff is behind the trailing '>' ?
00750         if( iMailEnd ){
00751           if( cQuotes == c )
00752             bInQuotesOutsideOfEmail = true; // start of quoted text found
00753           else
00754             name.append( c );
00755         }else{
00756           switch( c ){
00757             case '>':
00758               iMailEnd = i;
00759               break;
00760             case '(':
00761               if( !name.isEmpty() )
00762                 name.append( ' ' );
00763               if ( ++parenthesesNesting > 0 )
00764                 bInComment = true;
00765               break;
00766             default:
00767               if( ' ' != c )
00768                 mail.append( c );
00769           }
00770         }
00771       }
00772     }
00773   }
00774 
00775   name = name.simplifyWhiteSpace();
00776   mail = mail.simplifyWhiteSpace();
00777 
00778   return ! (name.isEmpty() || mail.isEmpty());
00779 }
00780 
00781 
00782 //-----------------------------------------------------------------------------
00783 bool KPIM::compareEmail( const QString& email1, const QString& email2,
00784                          bool matchName )
00785 {
00786   QString e1Name, e1Email, e2Name, e2Email;
00787 
00788   getNameAndMail( email1, e1Name, e1Email );
00789   getNameAndMail( email2, e2Name, e2Email );
00790 
00791   return e1Email == e2Email &&
00792     ( !matchName || ( e1Name == e2Name ) );
00793 }
00794 
00795 
00796 //-----------------------------------------------------------------------------
00797 QString KPIM::normalizedAddress( const QString & displayName,
00798                                  const QString & addrSpec,
00799                                  const QString & comment )
00800 {
00801   if ( displayName.isEmpty() && comment.isEmpty() )
00802     return addrSpec;
00803   else if ( comment.isEmpty() )
00804     return displayName + " <" + addrSpec + ">";
00805   else if ( displayName.isEmpty() ) {
00806     QString commentStr = comment;
00807     return quoteNameIfNecessary( commentStr ) + " <" + addrSpec + ">";
00808   }
00809   else
00810     return displayName + " (" + comment + ") <" + addrSpec + ">";
00811 }
00812 
00813 
00814 //-----------------------------------------------------------------------------
00815 QString KPIM::decodeIDN( const QString & addrSpec )
00816 {
00817   const int atPos = addrSpec.findRev( '@' );
00818   if ( atPos == -1 )
00819     return addrSpec;
00820 
00821   QString idn = KIDNA::toUnicode( addrSpec.mid( atPos + 1 ) );
00822   if ( idn.isEmpty() )
00823     return QString::null;
00824 
00825   return addrSpec.left( atPos + 1 ) + idn;
00826 }
00827 
00828 
00829 //-----------------------------------------------------------------------------
00830 QString KPIM::encodeIDN( const QString & addrSpec )
00831 {
00832   const int atPos = addrSpec.findRev( '@' );
00833   if ( atPos == -1 )
00834     return addrSpec;
00835 
00836   QString idn = KIDNA::toAscii( addrSpec.mid( atPos + 1 ) );
00837   if ( idn.isEmpty() )
00838     return addrSpec;
00839 
00840   return addrSpec.left( atPos + 1 ) + idn;
00841 }
00842 
00843 
00844 //-----------------------------------------------------------------------------
00845 QString KPIM::normalizeAddressesAndDecodeIDNs( const QString & str )
00846 {
00847 //  kdDebug() << "KPIM::normalizeAddressesAndDecodeIDNs( \""
00848 //                << str << "\" )" << endl;
00849   if( str.isEmpty() )
00850     return str;
00851 
00852   const QStringList addressList = KPIM::splitEmailAddrList( str );
00853   QStringList normalizedAddressList;
00854 
00855   QCString displayName, addrSpec, comment;
00856 
00857   for( QStringList::ConstIterator it = addressList.begin();
00858        ( it != addressList.end() );
00859        ++it ) {
00860     if( !(*it).isEmpty() ) {
00861       if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment )
00862            == AddressOk ) {
00863 
00864         normalizedAddressList <<
00865           normalizedAddress( QString::fromUtf8( displayName ),
00866                              decodeIDN( QString::fromUtf8( addrSpec ) ),
00867                              QString::fromUtf8( comment ) );
00868       }
00869       else {
00870         kdDebug() << "splitting address failed: " << *it << endl;
00871       }
00872     }
00873   }
00874 /*
00875   kdDebug() << "normalizedAddressList: \""
00876                 << normalizedAddressList.join( ", " )
00877                 << "\"" << endl;
00878 */
00879   return normalizedAddressList.join( ", " );
00880 }
00881 
00882 //-----------------------------------------------------------------------------
00883 QString KPIM::normalizeAddressesAndEncodeIDNs( const QString & str )
00884 {
00885   //kdDebug() << "KPIM::normalizeAddressesAndEncodeIDNs( \""
00886   //              << str << "\" )" << endl;
00887   if( str.isEmpty() )
00888     return str;
00889 
00890   const QStringList addressList = KPIM::splitEmailAddrList( str );
00891   QStringList normalizedAddressList;
00892 
00893   QCString displayName, addrSpec, comment;
00894 
00895   for( QStringList::ConstIterator it = addressList.begin();
00896        ( it != addressList.end() );
00897        ++it ) {
00898     if( !(*it).isEmpty() ) {
00899       if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment )
00900            == AddressOk ) {
00901 
00902         normalizedAddressList <<
00903           normalizedAddress( QString::fromUtf8( displayName ),
00904                              encodeIDN( QString::fromUtf8( addrSpec ) ),
00905                              QString::fromUtf8( comment ) );
00906       }
00907       else {
00908         kdDebug() << "splitting address failed: " << *it << endl;
00909       }
00910     }
00911   }
00912 
00913   /*
00914   kdDebug() << "normalizedAddressList: \""
00915                 << normalizedAddressList.join( ", " )
00916                 << "\"" << endl;
00917   */
00918   return normalizedAddressList.join( ", " );
00919 }
00920 
00921 
00922 //-----------------------------------------------------------------------------
00923 // Escapes unescaped doublequotes in str.
00924 static QString escapeQuotes( const QString & str )
00925 {
00926   if ( str.isEmpty() )
00927     return QString();
00928 
00929   QString escaped;
00930   // reserve enough memory for the worst case ( """..."" -> \"\"\"...\"\" )
00931   escaped.reserve( 2*str.length() );
00932   unsigned int len = 0;
00933   for ( unsigned int i = 0; i < str.length(); ++i, ++len ) {
00934     if ( str[i] == '"' ) { // unescaped doublequote
00935       escaped[len] = '\\';
00936       ++len;
00937     }
00938     else if ( str[i] == '\\' ) { // escaped character
00939       escaped[len] = '\\';
00940       ++len;
00941       ++i;
00942       if ( i >= str.length() ) // handle trailing '\' gracefully
00943         break;
00944     }
00945     escaped[len] = str[i];
00946   }
00947   escaped.truncate( len );
00948   return escaped;
00949 }
00950 
00951 //-----------------------------------------------------------------------------
00952 QString KPIM::quoteNameIfNecessary( const QString &str )
00953 {
00954   QString quoted = str;
00955 
00956   QRegExp needQuotes(  "[^ 0-9A-Za-z\\x0080-\\xFFFF]" );
00957   // avoid double quoting
00958   if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) {
00959     quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\"";
00960   }
00961   else if ( quoted.find( needQuotes ) != -1 ) {
00962     quoted = "\"" + escapeQuotes( quoted ) + "\"";
00963   }
00964 
00965   return quoted;
00966 }
00967 
KDE Home | KDE Accessibility Home | Description of Access Keys