diff options
Diffstat (limited to 'libemailfunctions')
-rw-r--r-- | libemailfunctions/Mainpage.dox | 10 | ||||
-rw-r--r-- | libemailfunctions/Makefile.am | 19 | ||||
-rw-r--r-- | libemailfunctions/email.cpp | 973 | ||||
-rw-r--r-- | libemailfunctions/email.h | 260 | ||||
-rw-r--r-- | libemailfunctions/idmapper.cpp | 191 | ||||
-rw-r--r-- | libemailfunctions/idmapper.h | 159 | ||||
-rw-r--r-- | libemailfunctions/kasciistricmp.cpp | 47 | ||||
-rw-r--r-- | libemailfunctions/kasciistricmp.h | 28 | ||||
-rw-r--r-- | libemailfunctions/kasciistringtools.cpp | 62 | ||||
-rw-r--r-- | libemailfunctions/kasciistringtools.h | 49 | ||||
-rw-r--r-- | libemailfunctions/networkstatus.cpp | 88 | ||||
-rw-r--r-- | libemailfunctions/networkstatus.h | 98 | ||||
-rw-r--r-- | libemailfunctions/tests/Makefile.am | 13 | ||||
-rw-r--r-- | libemailfunctions/tests/testemail.cpp | 486 | ||||
-rw-r--r-- | libemailfunctions/tests/testidmapper.cpp | 54 |
15 files changed, 2537 insertions, 0 deletions
diff --git a/libemailfunctions/Mainpage.dox b/libemailfunctions/Mainpage.dox new file mode 100644 index 000000000..9fbc10d13 --- /dev/null +++ b/libemailfunctions/Mainpage.dox @@ -0,0 +1,10 @@ +/** @mainpage libemailfunctions + +This library contains utility functions for dealing with email addresses. +This includes parsing, splitting, and munging email addresses represented +as strings (e.g. in email headers); some additional functions handle +non-latin1 upper- and lower-case conversions. + +Everything lives in the KPIM namespace. + +*/ diff --git a/libemailfunctions/Makefile.am b/libemailfunctions/Makefile.am new file mode 100644 index 000000000..e424e947c --- /dev/null +++ b/libemailfunctions/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = $(all_includes) -I$(srcdir)/../libkmime/ + +noinst_LTLIBRARIES = libemailfunctions.la +libemailfunctions_la_SOURCES = email.cpp idmapper.cpp kasciistricmp.cpp \ + kasciistringtools.cpp \ + networkstatus.cpp networkstatus.skel +libemailfunctions_la_LDFLAGS = $(all_libraries) -no-undefined +## Since this is a noinst library, in case of --enable-closure we need +## to link it explicitly to the libraries that it uses. +libemailfunctions_la_LIBADD = ../libkmime/libkmime.la $(LIB_QT) $(LIB_KDECORE) + +emailfunctionsincludedir = $(includedir)/libemailfunctions +emailfunctionsinclude_HEADERS = idmapper.h + +METASOURCES = AUTO + +# No messages target. Those files are part of libkdepim.pot. + +include $(top_srcdir)/admin/Doxyfile.am diff --git a/libemailfunctions/email.cpp b/libemailfunctions/email.cpp new file mode 100644 index 000000000..acbfa2679 --- /dev/null +++ b/libemailfunctions/email.cpp @@ -0,0 +1,973 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + + This file is part of kdepim. + Copyright (c) 2004 KDEPIM developers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "email.h" + +#include <kdebug.h> +#include <klocale.h> +#include <kidna.h> +#include <kmime_util.h> + +#include <qregexp.h> + +//----------------------------------------------------------------------------- +QStringList KPIM::splitEmailAddrList(const QString& aStr) +{ + // Features: + // - always ignores quoted characters + // - ignores everything (including parentheses and commas) + // inside quoted strings + // - supports nested comments + // - ignores everything (including double quotes and commas) + // inside comments + + QStringList list; + + if (aStr.isEmpty()) + return list; + + QString addr; + uint addrstart = 0; + int commentlevel = 0; + bool insidequote = false; + + for (uint index=0; index<aStr.length(); index++) { + // the following conversion to latin1 is o.k. because + // we can safely ignore all non-latin1 characters + switch (aStr[index].latin1()) { + case '"' : // start or end of quoted string + if (commentlevel == 0) + insidequote = !insidequote; + break; + case '(' : // start of comment + if (!insidequote) + commentlevel++; + break; + case ')' : // end of comment + if (!insidequote) { + if (commentlevel > 0) + commentlevel--; + else { + kdDebug(5300) << "Error in address splitting: Unmatched ')'" + << endl; + return list; + } + } + break; + case '\\' : // quoted character + index++; // ignore the quoted character + break; + case ',' : + case ';' : + if (!insidequote && (commentlevel == 0)) { + addr = aStr.mid(addrstart, index-addrstart); + if (!addr.isEmpty()) + list += addr.simplifyWhiteSpace(); + addrstart = index+1; + } + break; + } + } + // append the last address to the list + if (!insidequote && (commentlevel == 0)) { + addr = aStr.mid(addrstart, aStr.length()-addrstart); + if (!addr.isEmpty()) + list += addr.simplifyWhiteSpace(); + } + else + kdDebug(5300) << "Error in address splitting: " + << "Unexpected end of address list" + << endl; + + return list; +} + +//----------------------------------------------------------------------------- +// Used by KPIM::splitAddress(...) and KPIM::getFirstEmailAddress(...). +KPIM::EmailParseResult splitAddressInternal( const QCString& address, + QCString & displayName, + QCString & addrSpec, + QCString & comment, + bool allowMultipleAddresses ) +{ +// kdDebug() << "KMMessage::splitAddress( " << address << " )" << endl; + + displayName = ""; + addrSpec = ""; + comment = ""; + + if ( address.isEmpty() ) + return KPIM::AddressEmpty; + + // The following is a primitive parser for a mailbox-list (cf. RFC 2822). + // The purpose is to extract a displayable string from the mailboxes. + // Comments in the addr-spec are not handled. No error checking is done. + + enum { TopLevel, InComment, InAngleAddress } context = TopLevel; + bool inQuotedString = false; + int commentLevel = 0; + bool stop = false; + + for ( char* p = address.data(); *p && !stop; ++p ) { + switch ( context ) { + case TopLevel : { + switch ( *p ) { + case '"' : inQuotedString = !inQuotedString; + displayName += *p; + break; + case '(' : if ( !inQuotedString ) { + context = InComment; + commentLevel = 1; + } + else + displayName += *p; + break; + case '<' : if ( !inQuotedString ) { + context = InAngleAddress; + } + else + displayName += *p; + break; + case '\\' : // quoted character + displayName += *p; + ++p; // skip the '\' + if ( *p ) + displayName += *p; + else + return KPIM::UnexpectedEnd; + break; + case ',' : + case ';' : if ( !inQuotedString ) { + if ( allowMultipleAddresses ) + stop = true; + else + return KPIM::UnexpectedComma; + } + else + displayName += *p; + break; + default : displayName += *p; + } + break; + } + case InComment : { + switch ( *p ) { + case '(' : ++commentLevel; + comment += *p; + break; + case ')' : --commentLevel; + if ( commentLevel == 0 ) { + context = TopLevel; + comment += ' '; // separate the text of several comments + } + else + comment += *p; + break; + case '\\' : // quoted character + comment += *p; + ++p; // skip the '\' + if ( *p ) + comment += *p; + else + return KPIM::UnexpectedEnd; + break; + default : comment += *p; + } + break; + } + case InAngleAddress : { + switch ( *p ) { + case '"' : inQuotedString = !inQuotedString; + addrSpec += *p; + break; + case '>' : if ( !inQuotedString ) { + context = TopLevel; + } + else + addrSpec += *p; + break; + case '\\' : // quoted character + addrSpec += *p; + ++p; // skip the '\' + if ( *p ) + addrSpec += *p; + else + return KPIM::UnexpectedEnd; + break; + default : addrSpec += *p; + } + break; + } + } // switch ( context ) + } + // check for errors + if ( inQuotedString ) + return KPIM::UnbalancedQuote; + if ( context == InComment ) + return KPIM::UnbalancedParens; + if ( context == InAngleAddress ) + return KPIM::UnclosedAngleAddr; + + displayName = displayName.stripWhiteSpace(); + comment = comment.stripWhiteSpace(); + addrSpec = addrSpec.stripWhiteSpace(); + + if ( addrSpec.isEmpty() ) { + if ( displayName.isEmpty() ) + return KPIM::NoAddressSpec; + else { + addrSpec = displayName; + displayName.truncate( 0 ); + } + } +/* + kdDebug() << "display-name : \"" << displayName << "\"" << endl; + kdDebug() << "comment : \"" << comment << "\"" << endl; + kdDebug() << "addr-spec : \"" << addrSpec << "\"" << endl; +*/ + return KPIM::AddressOk; +} + + +//----------------------------------------------------------------------------- +KPIM::EmailParseResult KPIM::splitAddress( const QCString& address, + QCString & displayName, + QCString & addrSpec, + QCString & comment ) +{ + return splitAddressInternal( address, displayName, addrSpec, comment, + false /* don't allow multiple addresses */ ); +} + + +//----------------------------------------------------------------------------- +KPIM::EmailParseResult KPIM::splitAddress( const QString & address, + QString & displayName, + QString & addrSpec, + QString & comment ) +{ + QCString d, a, c; + KPIM::EmailParseResult result = splitAddress( address.utf8(), d, a, c ); + if ( result == AddressOk ) { + displayName = QString::fromUtf8( d ); + addrSpec = QString::fromUtf8( a ); + comment = QString::fromUtf8( c ); + } + return result; +} + + +//----------------------------------------------------------------------------- +KPIM::EmailParseResult KPIM::isValidEmailAddress( const QString& aStr ) +{ + // If we are passed an empty string bail right away no need to process further + // and waste resources + if ( aStr.isEmpty() ) { + return AddressEmpty; + } + + // count how many @'s are in the string that is passed to us + // if 0 or > 1 take action + // at this point to many @'s cannot bail out right away since + // @ is allowed in qoutes, so we use a bool to keep track + // and then make a judgement further down in the parser + // FIXME count only @ not in double quotes + + bool tooManyAtsFlag = false; + + int atCount = aStr.contains('@'); + if ( atCount > 1 ) { + tooManyAtsFlag = true;; + } else if ( atCount == 0 ) { + return TooFewAts; + } + + // The main parser, try and catch all weird and wonderful + // mistakes users and/or machines can create + + enum { TopLevel, InComment, InAngleAddress } context = TopLevel; + bool inQuotedString = false; + int commentLevel = 0; + + unsigned int strlen = aStr.length(); + + for ( unsigned int index=0; index < strlen; index++ ) { + switch ( context ) { + case TopLevel : { + switch ( aStr[index].latin1() ) { + case '"' : inQuotedString = !inQuotedString; + break; + case '(' : + if ( !inQuotedString ) { + context = InComment; + commentLevel = 1; + } + break; + case '[' : + if ( !inQuotedString ) { + return InvalidDisplayName; + } + break; + case ']' : + if ( !inQuotedString ) { + return InvalidDisplayName; + } + break; + case ':' : + if ( !inQuotedString ) { + return DisallowedChar; + } + break; + case '<' : + if ( !inQuotedString ) { + context = InAngleAddress; + } + break; + case '\\' : // quoted character + ++index; // skip the '\' + if (( index + 1 )> strlen ) { + return UnexpectedEnd; + } + break; + case ',' : + case ';' : + if ( !inQuotedString ) + return UnexpectedComma; + break; + case ')' : + if ( !inQuotedString ) + return UnbalancedParens; + break; + case '>' : + if ( !inQuotedString ) + return UnopenedAngleAddr; + break; + case '@' : + if ( !inQuotedString ) { + if ( index == 0 ) { // Missing local part + return MissingLocalPart; + } else if( index == strlen-1 ) { + return MissingDomainPart; + } + } else if ( inQuotedString ) { + --atCount; + if ( atCount == 1 ) { + tooManyAtsFlag = false; + } + } + break; + } + break; + } + case InComment : { + switch ( aStr[index] ) { + case '(' : ++commentLevel; + break; + case ')' : --commentLevel; + if ( commentLevel == 0 ) { + context = TopLevel; + } + break; + case '\\' : // quoted character + ++index; // skip the '\' + if (( index + 1 )> strlen ) { + return UnexpectedEnd; + } + break; + } + break; + } + + case InAngleAddress : { + switch ( aStr[index] ) { + case ',' : + case ';' : + if ( !inQuotedString ) { + return UnexpectedComma; + } + break; + case '"' : inQuotedString = !inQuotedString; + break; + case '@' : + if ( inQuotedString ) { + --atCount; + if ( atCount == 1 ) { + tooManyAtsFlag = false; + } + } + break; + case '>' : + if ( !inQuotedString ) { + context = TopLevel; + break; + } + break; + case '\\' : // quoted character + ++index; // skip the '\' + if (( index + 1 )> strlen ) { + return UnexpectedEnd; + } + break; + } + break; + } + } + } + + if ( atCount == 0 && !inQuotedString ) + return TooFewAts; + + if ( inQuotedString ) + return UnbalancedQuote; + + if ( context == InComment ) + return UnbalancedParens; + + if ( context == InAngleAddress ) + return UnclosedAngleAddr; + + if ( tooManyAtsFlag ) { + return TooManyAts; + } + return AddressOk; +} + +//----------------------------------------------------------------------------- +QString KPIM::emailParseResultToString( EmailParseResult errorCode ) +{ + switch ( errorCode ) { + case TooManyAts : + return i18n("The email address you entered is not valid because it " + "contains more than one @. " + "You will not create valid messages if you do not " + "change your address."); + case TooFewAts : + return i18n("The email address you entered is not valid because it " + "does not contain a @." + "You will not create valid messages if you do not " + "change your address."); + case AddressEmpty : + return i18n("You have to enter something in the email address field."); + case MissingLocalPart : + return i18n("The email address you entered is not valid because it " + "does not contain a local part."); + case MissingDomainPart : + return i18n("The email address you entered is not valid because it " + "does not contain a domain part."); + case UnbalancedParens : + return i18n("The email address you entered is not valid because it " + "contains unclosed comments/brackets."); + case AddressOk : + return i18n("The email address you entered is valid."); + case UnclosedAngleAddr : + return i18n("The email address you entered is not valid because it " + "contains an unclosed anglebracket."); + case UnopenedAngleAddr : + return i18n("The email address you entered is not valid because it " + "contains an unopened anglebracket."); + case UnexpectedComma : + return i18n("The email address you have entered is not valid because it " + "contains an unexpected comma."); + case UnexpectedEnd : + return i18n("The email address you entered is not valid because it ended " + "unexpectedly, this probably means you have used an escaping type " + "character like an \\ as the last character in your email " + "address."); + case UnbalancedQuote : + return i18n("The email address you entered is not valid because it " + "contains quoted text which does not end."); + case NoAddressSpec : + return i18n("The email address you entered is not valid because it " + "does not seem to contain an actual email address, i.e. " + "something of the form joe@kde.org."); + case DisallowedChar : + return i18n("The email address you entered is not valid because it " + "contains an illegal character."); + case InvalidDisplayName : + return i18n("The email address you have entered is not valid because it " + "contains an invalid displayname."); + } + return i18n("Unknown problem with email address"); +} + +//----------------------------------------------------------------------------- +bool KPIM::isValidSimpleEmailAddress( const QString& aStr ) +{ + // If we are passed an empty string bail right away no need to process further + // and waste resources + if ( aStr.isEmpty() ) { + return false; + } + + int atChar = aStr.findRev( '@' ); + QString domainPart = aStr.mid( atChar + 1); + QString localPart = aStr.left( atChar ); + bool tooManyAtsFlag = false; + bool inQuotedString = false; + int atCount = localPart.contains( '@' ); + + unsigned int strlen = localPart.length(); + for ( unsigned int index=0; index < strlen; index++ ) { + switch( localPart[ index ].latin1() ) { + case '"' : inQuotedString = !inQuotedString; + break; + case '@' : + if ( inQuotedString ) { + --atCount; + if ( atCount == 0 ) { + tooManyAtsFlag = false; + } + } + break; + } + } + + QString addrRx = "[a-zA-Z]*[~|{}`\\^?=/+*'&%$#!_\\w.-]*[~|{}`\\^?=/+*'&%$#!_a-zA-Z0-9-]@"; + if ( localPart[ 0 ] == '\"' || localPart[ localPart.length()-1 ] == '\"' ) { + addrRx = "\"[a-zA-Z@]*[\\w.@-]*[a-zA-Z0-9@]\"@"; + } + if ( domainPart[ 0 ] == '[' || domainPart[ domainPart.length()-1 ] == ']' ) { + addrRx += "\\[[0-9]{,3}(\\.[0-9]{,3}){3}\\]"; + } else { + addrRx += "[\\w-]+(\\.[\\w-]+)*"; + } + QRegExp rx( addrRx ); + return rx.exactMatch( aStr ) && !tooManyAtsFlag; +} + +//----------------------------------------------------------------------------- +QString KPIM::simpleEmailAddressErrorMsg() +{ + return i18n("The email address you entered is not valid because it " + "does not seem to contain an actual email address, i.e. " + "something of the form joe@kde.org."); +} +//----------------------------------------------------------------------------- +QCString KPIM::getEmailAddress( const QCString & address ) +{ + QCString dummy1, dummy2, addrSpec; + KPIM::EmailParseResult result = + splitAddressInternal( address, dummy1, addrSpec, dummy2, + false /* don't allow multiple addresses */ ); + if ( result != AddressOk ) { + addrSpec = QCString(); + kdDebug() // << k_funcinfo << "\n" + << "Input: aStr\nError:" + << emailParseResultToString( result ) << endl; + } + + return addrSpec; +} + + +//----------------------------------------------------------------------------- +QString KPIM::getEmailAddress( const QString & address ) +{ + return QString::fromUtf8( getEmailAddress( address.utf8() ) ); +} + + +//----------------------------------------------------------------------------- +QCString KPIM::getFirstEmailAddress( const QCString & addresses ) +{ + QCString dummy1, dummy2, addrSpec; + KPIM::EmailParseResult result = + splitAddressInternal( addresses, dummy1, addrSpec, dummy2, + true /* allow multiple addresses */ ); + if ( result != AddressOk ) { + addrSpec = QCString(); + kdDebug() // << k_funcinfo << "\n" + << "Input: aStr\nError:" + << emailParseResultToString( result ) << endl; + } + + return addrSpec; +} + + +//----------------------------------------------------------------------------- +QString KPIM::getFirstEmailAddress( const QString & addresses ) +{ + return QString::fromUtf8( getFirstEmailAddress( addresses.utf8() ) ); +} + + +//----------------------------------------------------------------------------- +bool KPIM::getNameAndMail(const QString& aStr, QString& name, QString& mail) +{ + name = QString::null; + mail = QString::null; + + const int len=aStr.length(); + const char cQuotes = '"'; + + bool bInComment = false; + bool bInQuotesOutsideOfEmail = false; + int i=0, iAd=0, iMailStart=0, iMailEnd=0; + QChar c; + unsigned int commentstack = 0; + + // Find the '@' of the email address + // skipping all '@' inside "(...)" comments: + while( i < len ){ + c = aStr[i]; + if( '(' == c ) commentstack++; + if( ')' == c ) commentstack--; + bInComment = commentstack != 0; + if( '"' == c && !bInComment ) + bInQuotesOutsideOfEmail = !bInQuotesOutsideOfEmail; + + if( !bInComment && !bInQuotesOutsideOfEmail ){ + if( '@' == c ){ + iAd = i; + break; // found it + } + } + ++i; + } + + if ( !iAd ) { + // We suppose the user is typing the string manually and just + // has not finished typing the mail address part. + // So we take everything that's left of the '<' as name and the rest as mail + for( i = 0; len > i; ++i ) { + c = aStr[i]; + if( '<' != c ) + name.append( c ); + else + break; + } + mail = aStr.mid( i+1 ); + if ( mail.endsWith( ">" ) ) + mail.truncate( mail.length() - 1 ); + + } else { + // Loop backwards until we find the start of the string + // or a ',' that is outside of a comment + // and outside of quoted text before the leading '<'. + bInComment = false; + bInQuotesOutsideOfEmail = false; + for( i = iAd-1; 0 <= i; --i ) { + c = aStr[i]; + if( bInComment ) { + if( '(' == c ) { + if( !name.isEmpty() ) + name.prepend( ' ' ); + bInComment = false; + } else { + name.prepend( c ); // all comment stuff is part of the name + } + }else if( bInQuotesOutsideOfEmail ){ + if( cQuotes == c ) + bInQuotesOutsideOfEmail = false; + else + name.prepend( c ); + }else{ + // found the start of this addressee ? + if( ',' == c ) + break; + // stuff is before the leading '<' ? + if( iMailStart ){ + if( cQuotes == c ) + bInQuotesOutsideOfEmail = true; // end of quoted text found + else + name.prepend( c ); + }else{ + switch( c ){ + case '<': + iMailStart = i; + break; + case ')': + if( !name.isEmpty() ) + name.prepend( ' ' ); + bInComment = true; + break; + default: + if( ' ' != c ) + mail.prepend( c ); + } + } + } + } + + name = name.simplifyWhiteSpace(); + mail = mail.simplifyWhiteSpace(); + + if( mail.isEmpty() ) + return false; + + mail.append('@'); + + // Loop forward until we find the end of the string + // or a ',' that is outside of a comment + // and outside of quoted text behind the trailing '>'. + bInComment = false; + bInQuotesOutsideOfEmail = false; + int parenthesesNesting = 0; + for( i = iAd+1; len > i; ++i ) { + c = aStr[i]; + if( bInComment ){ + if( ')' == c ){ + if ( --parenthesesNesting == 0 ) { + bInComment = false; + if( !name.isEmpty() ) + name.append( ' ' ); + } else { + // nested ")", add it + name.append( ')' ); // name can't be empty here + } + } else { + if( '(' == c ) { + // nested "(" + ++parenthesesNesting; + } + name.append( c ); // all comment stuff is part of the name + } + }else if( bInQuotesOutsideOfEmail ){ + if( cQuotes == c ) + bInQuotesOutsideOfEmail = false; + else + name.append( c ); + }else{ + // found the end of this addressee ? + if( ',' == c ) + break; + // stuff is behind the trailing '>' ? + if( iMailEnd ){ + if( cQuotes == c ) + bInQuotesOutsideOfEmail = true; // start of quoted text found + else + name.append( c ); + }else{ + switch( c ){ + case '>': + iMailEnd = i; + break; + case '(': + if( !name.isEmpty() ) + name.append( ' ' ); + if ( ++parenthesesNesting > 0 ) + bInComment = true; + break; + default: + if( ' ' != c ) + mail.append( c ); + } + } + } + } + } + + name = name.simplifyWhiteSpace(); + mail = mail.simplifyWhiteSpace(); + + return ! (name.isEmpty() || mail.isEmpty()); +} + + +//----------------------------------------------------------------------------- +bool KPIM::compareEmail( const QString& email1, const QString& email2, + bool matchName ) +{ + QString e1Name, e1Email, e2Name, e2Email; + + getNameAndMail( email1, e1Name, e1Email ); + getNameAndMail( email2, e2Name, e2Email ); + + return e1Email == e2Email && + ( !matchName || ( e1Name == e2Name ) ); +} + + +//----------------------------------------------------------------------------- +QString KPIM::normalizedAddress( const QString & displayName, + const QString & addrSpec, + const QString & comment ) +{ + if ( displayName.isEmpty() && comment.isEmpty() ) + return addrSpec; + else if ( comment.isEmpty() ) + return quoteNameIfNecessary( displayName ) + " <" + addrSpec + ">"; + else if ( displayName.isEmpty() ) { + QString commentStr = comment; + return quoteNameIfNecessary( commentStr ) + " <" + addrSpec + ">"; + } + else + return displayName + " (" + comment + ") <" + addrSpec + ">"; +} + + +//----------------------------------------------------------------------------- +QString KPIM::decodeIDN( const QString & addrSpec ) +{ + const int atPos = addrSpec.findRev( '@' ); + if ( atPos == -1 ) + return addrSpec; + + QString idn = KIDNA::toUnicode( addrSpec.mid( atPos + 1 ) ); + if ( idn.isEmpty() ) + return QString::null; + + return addrSpec.left( atPos + 1 ) + idn; +} + + +//----------------------------------------------------------------------------- +QString KPIM::encodeIDN( const QString & addrSpec ) +{ + const int atPos = addrSpec.findRev( '@' ); + if ( atPos == -1 ) + return addrSpec; + + QString idn = KIDNA::toAscii( addrSpec.mid( atPos + 1 ) ); + if ( idn.isEmpty() ) + return addrSpec; + + return addrSpec.left( atPos + 1 ) + idn; +} + + +//----------------------------------------------------------------------------- +QString KPIM::normalizeAddressesAndDecodeIDNs( const QString & str ) +{ +// kdDebug() << "KPIM::normalizeAddressesAndDecodeIDNs( \"" +// << str << "\" )" << endl; + if( str.isEmpty() ) + return str; + + const QStringList addressList = KPIM::splitEmailAddrList( str ); + QStringList normalizedAddressList; + + QCString displayName, addrSpec, comment; + + for( QStringList::ConstIterator it = addressList.begin(); + ( it != addressList.end() ); + ++it ) { + if( !(*it).isEmpty() ) { + if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment ) + == AddressOk ) { + + displayName = KMime::decodeRFC2047String(displayName).utf8(); + comment = KMime::decodeRFC2047String(comment).utf8(); + + normalizedAddressList << + normalizedAddress( QString::fromUtf8( displayName ), + decodeIDN( QString::fromUtf8( addrSpec ) ), + QString::fromUtf8( comment ) ); + } + else { + kdDebug() << "splitting address failed: " << *it << endl; + } + } + } +/* + kdDebug() << "normalizedAddressList: \"" + << normalizedAddressList.join( ", " ) + << "\"" << endl; +*/ + return normalizedAddressList.join( ", " ); +} + +//----------------------------------------------------------------------------- +QString KPIM::normalizeAddressesAndEncodeIDNs( const QString & str ) +{ + //kdDebug() << "KPIM::normalizeAddressesAndEncodeIDNs( \"" + // << str << "\" )" << endl; + if( str.isEmpty() ) + return str; + + const QStringList addressList = KPIM::splitEmailAddrList( str ); + QStringList normalizedAddressList; + + QCString displayName, addrSpec, comment; + + for( QStringList::ConstIterator it = addressList.begin(); + ( it != addressList.end() ); + ++it ) { + if( !(*it).isEmpty() ) { + if ( KPIM::splitAddress( (*it).utf8(), displayName, addrSpec, comment ) + == AddressOk ) { + + normalizedAddressList << + normalizedAddress( QString::fromUtf8( displayName ), + encodeIDN( QString::fromUtf8( addrSpec ) ), + QString::fromUtf8( comment ) ); + } + else { + kdDebug() << "splitting address failed: " << *it << endl; + } + } + } + + /* + kdDebug() << "normalizedAddressList: \"" + << normalizedAddressList.join( ", " ) + << "\"" << endl; + */ + return normalizedAddressList.join( ", " ); +} + + +//----------------------------------------------------------------------------- +// Escapes unescaped doublequotes in str. +static QString escapeQuotes( const QString & str ) +{ + if ( str.isEmpty() ) + return QString(); + + QString escaped; + // reserve enough memory for the worst case ( """..."" -> \"\"\"...\"\" ) + escaped.reserve( 2*str.length() ); + unsigned int len = 0; + for ( unsigned int i = 0; i < str.length(); ++i, ++len ) { + if ( str[i] == '"' ) { // unescaped doublequote + escaped[len] = '\\'; + ++len; + } + else if ( str[i] == '\\' ) { // escaped character + escaped[len] = '\\'; + ++len; + ++i; + if ( i >= str.length() ) // handle trailing '\' gracefully + break; + } + escaped[len] = str[i]; + } + escaped.truncate( len ); + return escaped; +} + +//----------------------------------------------------------------------------- +QString KPIM::quoteNameIfNecessary( const QString &str ) +{ + QString quoted = str; + + QRegExp needQuotes( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ); + // avoid double quoting + if ( ( quoted[0] == '"' ) && ( quoted[quoted.length() - 1] == '"' ) ) { + quoted = "\"" + escapeQuotes( quoted.mid( 1, quoted.length() - 2 ) ) + "\""; + } + else if ( quoted.find( needQuotes ) != -1 ) { + quoted = "\"" + escapeQuotes( quoted ) + "\""; + } + + return quoted; +} + diff --git a/libemailfunctions/email.h b/libemailfunctions/email.h new file mode 100644 index 000000000..493fa087c --- /dev/null +++ b/libemailfunctions/email.h @@ -0,0 +1,260 @@ +/* -*- mode: C++; c-file-style: "gnu" -*- + + This file is part of kdepim. + Copyright (c) 2004 KDEPIM developers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef EMAIL_H +#define EMAIL_H + +#include <qstringlist.h> +#include <qcstring.h> + +#include <kdepimmacros.h> + +/** @file */ + +/** + \brief KPIM holds all kinds of functions specific to KDE PIM. + + The KPIM namespace hides away functions, enums, and other things + that are KDE PIM specific and that we don't want to have polluting + the global namespace. +*/ +namespace KPIM { + +/** + Result type for splitAddress, isValidEmailAddress. +*/ +enum EmailParseResult { AddressOk, AddressEmpty, UnexpectedEnd, + UnbalancedParens, MissingDomainPart, + UnclosedAngleAddr, UnopenedAngleAddr, + TooManyAts, UnexpectedComma, + TooFewAts, MissingLocalPart, + UnbalancedQuote, NoAddressSpec, + DisallowedChar, InvalidDisplayName }; + +// Helper functions +/** Split a comma separated list of email addresses. */ +KDE_EXPORT QStringList splitEmailAddrList(const QString& aStr); + +/** Splits the given address into display name, email address and comment. + Returns AddressOk if no error was encountered. Otherwise an appropriate + error code is returned. In case of an error the values of displayName, + addrSpec and comment are undefined. + + @param address a single email address, + example: Joe User (comment1) <joe.user@example.org> (comment2) + @param displayName only out: the display-name of the email address, i.e. + "Joe User" in the example; in case of an error the + return value is undefined + @param addrSpec only out: the addr-spec, i.e. "joe.user@example.org" in the + example; in case of an error the return value is + undefined + @param comment only out: the space-separated comments, i.e. + "comment1 comment2" in the example; in case of an + error the return value is undefined + @return AddressOk if no error was encountered. Otherwise an + appropriate error code is returned. +*/ +KDE_EXPORT EmailParseResult splitAddress( const QCString & address, + QCString & displayName, + QCString & addrSpec, + QCString & comment ); + +/** This is an overloaded member function, provided for convenience. It behaves + essentially like the above function. + + Splits the given address into display name, email address and comment. + Returns AddressOk if no error was encountered. Otherwise an appropriate + error code is returned. In case of an error the values of displayName, + addrSpec and comment are undefined. + + @param address a single email address, + example: Joe User (comment1) <joe.user@example.org> (comment2) + @param displayName only out: the display-name of the email address, i.e. + "Joe User" in the example; in case of an error the + return value is undefined + @param addrSpec only out: the addr-spec, i.e. "joe.user@example.org" in the + example; in case of an error the return value is + undefined + @param comment only out: the space-separated comments, i.e. + "comment1 comment2" in the example; in case of an + error the return value is undefined + @return AddressOk if no error was encountered. Otherwise an + appropriate error code is returned. +*/ +KDE_EXPORT EmailParseResult splitAddress( const QString & address, + QString & displayName, + QString & addrSpec, + QString & comment ); + +/** Validates an email address in the form of "Joe User" <joe@example.org>. + Returns AddressOk if no error was encountered. Otherwise an appropriate + error code is returned. + + @param aStr a single email address, + example: Joe User (comment1) <joe.user@example.org> + @return AddressOk if no error was encountered. Otherwise an + appropriate error code is returned. +*/ +KDE_EXPORT EmailParseResult isValidEmailAddress( const QString& aStr ); + +/** Translate the enum errorcodes from emailParseResult + into i18n'd strings that can be used for msg boxes. + + @param errorCode the errorCode from isValidEmailEmailAddress(). + + @return An i18n ready string for use in msgboxes. +*/ +KDE_EXPORT QString emailParseResultToString( EmailParseResult errorCode ); + +/** Validates an email address in the form of joe@example.org. + Returns true if no error was encountered. + This method should be used when the input field should not + allow a "full" email address with comments and other special + cases that normally are valid in an email address. + + @param aStr a single email address, + example: joe.user@example.org + @return true if no error was encountered. +*/ +KDE_EXPORT bool isValidSimpleEmailAddress( const QString& aStr ); + +/** Returns a i18n string to be used in msgboxes + this allows for error messages to be the same + across the board. + + @return An i18n ready string for use in msgboxes. +*/ + +KDE_EXPORT QString simpleEmailAddressErrorMsg(); + +/** Returns the pure email address (addr-spec in RFC2822) of the given address + (mailbox in RFC2822). + + @param address an email address, e.g. "Joe User <joe.user@example.org>" + @return the addr-spec of @a address, i.e. joe.user@example.org in the + example +*/ +KDE_EXPORT QCString getEmailAddress( const QCString & address ); + +/** This is an overloaded member function, provided for convenience. It behaves + essentially like the above function. + + Returns the pure email address (addr-spec in RFC2822) of the given address + (mailbox in RFC2822). + + @param address an email address, e.g. "Joe User <joe.user@example.org>" + @return the addr-spec of @a address, i.e. joe.user@example.org in the + example +*/ +KDE_EXPORT QString getEmailAddress( const QString & address ); + +/** Returns the pure email address (addr-spec in RFC2822) of the first + email address of a list of addresses. + + @param addresses an email address, e.g. "Joe User <joe.user@example.org>" + @return the addr-spec of @a addresses, i.e. joe.user@example.org in the + example +*/ +KDE_EXPORT QCString getFirstEmailAddress( const QCString & addresses ); + +/** This is an overloaded member function, provided for convenience. It behaves + essentially like the above function. + + Returns the pure email address (addr-spec in RFC2822) of the first + email address of a list of addresses. + + @param addresses an email address, e.g. "Joe User <joe.user@example.org>" + @return the addr-spec of @a addresses, i.e. joe.user@example.org in the + example +*/ +KDE_EXPORT QString getFirstEmailAddress( const QString & addresses ); + +/** Return email address and name from string. Examples: + * "Stefan Taferner <taferner@example.org>" returns "taferner@example.org" + * and "Stefan Taferner". "joe@example.com" returns "joe@example.com" + * and "". Note that this only returns the first address. + * Also note that the return value is TRUE if both the name and the + * mail are not empty: this does NOT tell you if mail contains a + * valid email address or just some rubbish. + */ +KDE_EXPORT bool getNameAndMail(const QString& aStr, QString& name, QString& mail); + +/** + * Compare two email addresses. If matchName is false, it just checks + * the email address, and returns true if this matches. If matchName + * is true, both the name and the email must be the same. + */ +KDE_EXPORT bool compareEmail( const QString& email1, const QString& email2, + bool matchName ); + +/** Returns a normalized address built from the given parts. The normalized + address is of one the following forms: + - displayName (comment) <addrSpec> + - displayName <addrSpec> + - comment <addrSpec> + - addrSpec + + @param displayName the display name of the address + @param addrSpec the actual email address (addr-spec in RFC 2822) + @param comment a comment + @return a normalized address built from the given parts + */ +KDE_EXPORT QString normalizedAddress( const QString & displayName, + const QString & addrSpec, + const QString & comment ); + +/** Decodes the punycode domain part of the given addr-spec if it's an IDN. + + @param addrSpec a pure 7-bit email address (addr-spec in RFC2822) + @return the email address with Unicode domain + */ +KDE_EXPORT QString decodeIDN( const QString & addrSpec ); + +/** Encodes the domain part of the given addr-spec in punycode if it's an + IDN. + + @param addrSpec a pure email address with Unicode domain + @return the email address with domain in punycode + */ +KDE_EXPORT QString encodeIDN( const QString & addrSpec ); + +/** Normalizes all email addresses in the given list and decodes all IDNs. + + @param addresses a list of email addresses with punycoded IDNs + @return the email addresses in normalized form with Unicode IDNs + + */ +KDE_EXPORT QString normalizeAddressesAndDecodeIDNs( const QString & addresses ); + +/** Normalizes all email addresses in the given list and encodes all IDNs + in punycode. + */ +KDE_EXPORT QString normalizeAddressesAndEncodeIDNs( const QString & str ); + +/** Add quote characters around the given string if it contains a + * character that makes that necessary, in an email name, such as ",". + */ +KDE_EXPORT QString quoteNameIfNecessary( const QString& str ); + +} // namespace + +#endif /* EMAIL_H */ + diff --git a/libemailfunctions/idmapper.cpp b/libemailfunctions/idmapper.cpp new file mode 100644 index 000000000..a44dc8c83 --- /dev/null +++ b/libemailfunctions/idmapper.cpp @@ -0,0 +1,191 @@ +/* + This file is part of kdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "idmapper.h" + +#include <kstandarddirs.h> +#include <kdebug.h> + +#include <qfile.h> + +using namespace KPIM; + +IdMapper::IdMapper() +{ +} + +IdMapper::IdMapper( const QString &path, const QString &identifier ) + : mPath( path ), mIdentifier( identifier ) +{ +} + +IdMapper::~IdMapper() +{ +} + +void IdMapper::setPath( const QString &path ) +{ + mPath = path; +} + +void IdMapper::setIdentifier( const QString &identifier ) +{ + mIdentifier = identifier; +} + +QString IdMapper::filename() +{ + QString file = mPath; + if ( !file.endsWith( "/" ) ) file += "/"; + file += mIdentifier; + + return locateLocal( "data", file ); +} + +bool IdMapper::load() +{ + QFile file( filename() ); + if ( !file.open( IO_ReadOnly ) ) { + kdError(5800) << "Can't read uid map file '" << filename() << "'" << endl; + return false; + } + + clear(); + + QString line; + while ( file.readLine( line, 1024 ) != -1 ) { + line.truncate( line.length() - 2 ); // strip newline + + QStringList parts = QStringList::split( "\x02\x02", line, true ); + mIdMap.insert( parts[ 0 ], parts[ 1 ] ); + mFingerprintMap.insert( parts[ 0 ], parts[ 2 ] ); + } + + file.close(); + + return true; +} + +bool IdMapper::save() +{ + QFile file( filename() ); + if ( !file.open( IO_WriteOnly ) ) { + kdError(5800) << "Can't write uid map file '" << filename() << "'" << endl; + return false; + } + + QString content; + + QMap<QString, QVariant>::Iterator it; + for ( it = mIdMap.begin(); it != mIdMap.end(); ++it ) { + QString fingerprint( "" ); + if ( mFingerprintMap.contains( it.key() ) ) + fingerprint = mFingerprintMap[ it.key() ]; + content += it.key() + "\x02\x02" + it.data().toString() + "\x02\x02" + fingerprint + "\r\n"; + } + + file.writeBlock( content.latin1(), qstrlen( content.latin1() ) ); + file.close(); + + return true; +} + +void IdMapper::clear() +{ + mIdMap.clear(); + mFingerprintMap.clear(); +} + +void IdMapper::setRemoteId( const QString &localId, const QString &remoteId ) +{ + mIdMap.replace( localId, remoteId ); +} + +void IdMapper::removeRemoteId( const QString &remoteId ) +{ + QMap<QString, QVariant>::Iterator it; + for ( it = mIdMap.begin(); it != mIdMap.end(); ++it ) + if ( it.data().toString() == remoteId ) { + mIdMap.remove( it ); + mFingerprintMap.remove( it.key() ); + return; + } +} + +QString IdMapper::remoteId( const QString &localId ) const +{ + QMap<QString, QVariant>::ConstIterator it; + it = mIdMap.find( localId ); + + if ( it != mIdMap.end() ) + return it.data().toString(); + else + return QString::null; +} + +QString IdMapper::localId( const QString &remoteId ) const +{ + QMap<QString, QVariant>::ConstIterator it; + for ( it = mIdMap.begin(); it != mIdMap.end(); ++it ) + if ( it.data().toString() == remoteId ) + return it.key(); + + return QString::null; +} + +QString IdMapper::asString() const +{ + QString content; + + QMap<QString, QVariant>::ConstIterator it; + for ( it = mIdMap.begin(); it != mIdMap.end(); ++it ) { + QString fp; + if ( mFingerprintMap.contains( it.key() ) ) + fp = mFingerprintMap[ it.key() ]; + content += it.key() + "\t" + it.data().toString() + "\t" + fp + "\r\n"; + } + + return content; +} + +void IdMapper::setFingerprint( const QString &localId, const QString &fingerprint ) +{ + mFingerprintMap.insert( localId, fingerprint ); +} + +const QString& IdMapper::fingerprint( const QString &localId ) const +{ + if ( mFingerprintMap.contains( localId ) ) + return mFingerprintMap[ localId ]; + else + return QString::null; +} + +QMap<QString, QString> IdMapper::remoteIdMap() const +{ + QMap<QString, QString> reverseMap; + QMap<QString, QVariant>::ConstIterator it; + for ( it = mIdMap.begin(); it != mIdMap.end(); ++it ) { + reverseMap.insert( it.data().toString(), it.key() ); + } + return reverseMap; +} diff --git a/libemailfunctions/idmapper.h b/libemailfunctions/idmapper.h new file mode 100644 index 000000000..e09587f4c --- /dev/null +++ b/libemailfunctions/idmapper.h @@ -0,0 +1,159 @@ +/* + This file is part of kdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + Copyright (c) 2004 Cornelius Schumacher <schumacher@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#ifndef KPIM_IDMAPPER_H +#define KPIM_IDMAPPER_H + +#include <qmap.h> +#include <qvariant.h> + +#include <kdepimmacros.h> + +namespace KPIM { + +/** + An Id Mapper maps Ids. What to or what for is not entirely + clear, but maps have categories. This is probably an + adjoint functor, since adjoint functors are everywhere. +*/ +class KDE_EXPORT IdMapper +{ + public: + /** + Create Id mapper. You have to set path and identifier before you can call + load() or save(). + */ + IdMapper(); + /** + Create Id mapper. The path specifies the category of mapping, the + identifier the concrete object. + + If you don't pass an identifier you have to set it before calling load() + or save(). + + The current implementation stores the data at + $(KDEHOME)/share/apps/\<path\>/\<identifier\>. + */ + IdMapper( const QString &path, const QString &identifier = QString::null ); + /** Destructor. */ + ~IdMapper(); + + /** + Set id map path. + */ + void setPath( const QString &path ); + /** + Return id map path. + */ + QString path() const { return mPath; } + + /** + Set id map identifier. + */ + void setIdentifier( const QString &identifier ); + /** + Return id map identifier. + */ + QString identifier() const { return mIdentifier; } + + /** + Loads the map. + */ + bool load(); + + /** + Saves the map. + */ + bool save(); + + /** + Clears the map. + */ + void clear(); + + /** + Stores the remote id for the given local id. + */ + void setRemoteId( const QString &localId, const QString &remoteId ); + + /** + Removes the remote id. + */ + void removeRemoteId( const QString &remoteId ); + + /** + Returns the remote id of the given local id. + */ + QString remoteId( const QString &localId ) const; + + /** + Returns the local id for the given remote id. + */ + QString localId( const QString &remoteId ) const; + + + /** + * Stores a fingerprint for an id which can be used to detect if + * the locally held version differs from what is on the server. + * This can be a sequence number of an md5 hash depending on what + * the server provides + */ + void setFingerprint( const QString &localId, const QString &fingerprint ); + + /** + * Returns the fingerprint for the map. + * + * @todo Figure out if this applies to the last set fingerprint + * or if anything else can change it. + */ + const QString &fingerprint( const QString &localId ) const; + + + /** + * Returns the entire map for the Id mapper. + * + * @todo Document what the map means. + */ + QMap<QString, QString> remoteIdMap() const; + + /** + * Returns a string representation of the id pairs, that's usefull + * for debugging. + */ + QString asString() const; + + protected: + /** + * Returns the filename this mapper is (or will be) stored in. + */ + QString filename(); + + private: + QMap<QString, QVariant> mIdMap; + QMap<QString, QString> mFingerprintMap; + + QString mPath; + QString mIdentifier; +}; + +} + +#endif diff --git a/libemailfunctions/kasciistricmp.cpp b/libemailfunctions/kasciistricmp.cpp new file mode 100644 index 000000000..1a66a52b5 --- /dev/null +++ b/libemailfunctions/kasciistricmp.cpp @@ -0,0 +1,47 @@ +/* This file is part of the KDE libraries + Copyright (C) 2004 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kasciistricmp.h" + +#if ! KDE_IS_VERSION(3,3,89) + +static unsigned char kdepim_ASCIIToLower( unsigned char ch ) +{ + if ( ch >= 'A' && ch <= 'Z' ) + return ch - 'A' + 'a'; + else + return ch; +} + +int kdepim_kasciistricmp( const char *str1, const char *str2 ) +{ + const unsigned char *s1 = (const unsigned char *) str1; + const unsigned char *s2 = (const unsigned char *) str2; + int res; + unsigned char c; + + if ( !s1 || !s2 ) + return s1 ? 1 : ( s2 ? -1 : 0 ); + for ( ; !( res = ( c = kdepim_ASCIIToLower( *s1 ) ) - kdepim_ASCIIToLower( *s2 ) ); + ++s1, ++s2 ) + if ( !c ) // strings are equal + break; + return res; +} + +#endif diff --git a/libemailfunctions/kasciistricmp.h b/libemailfunctions/kasciistricmp.h new file mode 100644 index 000000000..852fd266a --- /dev/null +++ b/libemailfunctions/kasciistricmp.h @@ -0,0 +1,28 @@ +/* This file is part of the KDE libraries + Copyright (C) 2004 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kdeversion.h> + +#if KDE_IS_VERSION(3,3,89) +// get kasciistricmp from kglobal.h +#include <kglobal.h> +#else +// define kasciistricmp to this kdepim symbol (renamed to avoid problems when upgrading kdelibs later) +int kdepim_kasciistricmp( const char *str1, const char *str2 ); +#define kasciistricmp kdepim_kasciistricmp +#endif diff --git a/libemailfunctions/kasciistringtools.cpp b/libemailfunctions/kasciistringtools.cpp new file mode 100644 index 000000000..4057e9481 --- /dev/null +++ b/libemailfunctions/kasciistringtools.cpp @@ -0,0 +1,62 @@ +/* -*- c++ -*- + kasciistringtools.cpp + + This file is part of libkdepim. + + Copyright (c) 2005 Ingo Kloecker <kloecker@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kasciistringtools.h" + +namespace KPIM { + +static unsigned char ASCIIToLower( unsigned char ch ) +{ + if ( ch >= 'A' && ch <= 'Z' ) + return ch - 'A' + 'a'; + else + return ch; +} + +char * kAsciiToLower( char *s ) +{ + if ( !s ) + return 0; + for ( unsigned char *p = (unsigned char *) s; *p; ++p ) + *p = ASCIIToLower( *p ); + return s; +} + +static unsigned char ASCIIToUpper( unsigned char ch ) +{ + if ( ch >= 'a' && ch <= 'z' ) + return ch - 'a' + 'A'; + else + return ch; +} + +char * kAsciiToUpper( char *s ) +{ + if ( !s ) + return 0; + for ( unsigned char *p = (unsigned char *) s; *p; ++p ) + *p = ASCIIToUpper( *p ); + return s; +} + +} // namespace KPIM diff --git a/libemailfunctions/kasciistringtools.h b/libemailfunctions/kasciistringtools.h new file mode 100644 index 000000000..843a7802f --- /dev/null +++ b/libemailfunctions/kasciistringtools.h @@ -0,0 +1,49 @@ +/* -*- c++ -*- + kasciistringtools.h + + This file is part of libkdepim. + + Copyright (c) 2005 Ingo Kloecker <kloecker@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KPIM_KASCIISTRINGTOOLS_H +#define KPIM_KASCIISTRINGTOOLS_H + +namespace KPIM { + +/** + Locale-independent function to convert ASCII strings to lower case ASCII + strings. This means that it affects @em only the ASCII characters A-Z. + + @param str pointer to the string which should be converted to lower case + @return pointer to the converted string (same as @a str) +*/ +char * kAsciiToLower( char *str ); + +/** + Locale-independent function to convert ASCII strings to upper case ASCII + strings. This means that it affects @em only the ASCII characters a-z. + + @param str pointer to the string which should be converted to upper case + @return pointer to the converted string (same as @a str) +*/ +char * kAsciiToUpper( char *str ); + +} // namespace KPIM + +#endif diff --git a/libemailfunctions/networkstatus.cpp b/libemailfunctions/networkstatus.cpp new file mode 100644 index 000000000..fae385220 --- /dev/null +++ b/libemailfunctions/networkstatus.cpp @@ -0,0 +1,88 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2005 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kconfig.h> +#include <kglobal.h> +#include <kstaticdeleter.h> + +#include <dcopref.h> + +#include "networkstatus.h" + +using namespace KPIM; + +static KStaticDeleter<NetworkStatus> networkStatusDeleter; +NetworkStatus *NetworkStatus::mSelf = 0; + +NetworkStatus::NetworkStatus() + : QObject( 0, "NetworkStatus" ), DCOPObject( "NetworkStatus" ) +{ + KConfigGroup group( KGlobal::config(), "NetworkStatus" ); + if ( group.readBoolEntry( "Online", true ) == true ) + mStatus = Online; + else + mStatus = Offline; + + connectDCOPSignal( 0, 0, "onlineStatusChanged()", "onlineStatusChanged()", false ); +} + +NetworkStatus::~NetworkStatus() +{ + KConfigGroup group( KGlobal::config(), "NetworkStatus" ); + group.writeEntry( "Online", mStatus == Online ); +} + +void NetworkStatus::setStatus( Status status ) +{ + mStatus = status; + + emit statusChanged( mStatus ); +} + +NetworkStatus::Status NetworkStatus::status() const +{ + return mStatus; +} + +void NetworkStatus::onlineStatusChanged() +{ + DCOPRef dcopCall( "kded", "networkstatus" ); + DCOPReply reply = dcopCall.call( "onlineStatus()", true ); + if ( reply.isValid() ) { + int status = reply; + if ( status == 3 ) + setStatus( Online ); + else { + if ( mStatus != Offline ) + setStatus( Offline ); + } + } +} + +NetworkStatus *NetworkStatus::self() +{ + if ( !mSelf ) + networkStatusDeleter.setObject( mSelf, new NetworkStatus ); + + return mSelf; +} + +#include "networkstatus.moc" diff --git a/libemailfunctions/networkstatus.h b/libemailfunctions/networkstatus.h new file mode 100644 index 000000000..6bc3b70a4 --- /dev/null +++ b/libemailfunctions/networkstatus.h @@ -0,0 +1,98 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2005 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef NETWORKSTATUS_H +#define NETWORKSTATUS_H + +#include <qobject.h> +#include <dcopobject.h> + +namespace KPIM { + +/** + This is a class for monitoring network status -- basically, + the machine KDE is running on going from "online" mode to + offline. What this means is left as an exercise for the reader. + */ +class NetworkStatus : public QObject, public DCOPObject +{ + Q_OBJECT + + public: + /** + * The possible states. + */ + enum Status { + Online, //< The machine now has internet connectivity + Offline //< The machine has no internet connectivity + }; + + /** + * Destructor. + */ + ~NetworkStatus(); + + /** + * Returns the only instance of this class. + */ + static NetworkStatus *self(); + + /** + * Sets a new status. + * + * @param status The new status. + */ + void setStatus( Status status ); + + /** + * Returns the current status. + */ + Status status() const; + + k_dcop: + /** + * Called by the network interface watcher in KDED. + */ + void onlineStatusChanged(); + + signals: + /** + * Emitted whenever the status has changed. + * + * @param status The new status. + */ + void statusChanged( Status status ); + + protected: + /** + * Constructor. This is protected, so you must use self() + * to get the singleton object of this class. + */ + NetworkStatus(); + + private: + Status mStatus; + static NetworkStatus *mSelf; +}; + +} + +#endif diff --git a/libemailfunctions/tests/Makefile.am b/libemailfunctions/tests/Makefile.am new file mode 100644 index 000000000..95874de94 --- /dev/null +++ b/libemailfunctions/tests/Makefile.am @@ -0,0 +1,13 @@ +AM_CPPFLAGS = -I$(top_srcdir)/libemailfunctions $(all_includes) +LDADD = ../libemailfunctions.la ../../libkmime/libkmime.la $(LIB_KDECORE) + +check_PROGRAMS = testidmapper testemail +TESTS = testemail + +testidmapper_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testidmapper_SOURCES = testidmapper.cpp + +testemail_LDFLAGS = $(all_libraries) $(KDE_RPATH) +testemail_SOURCES = testemail.cpp + +METASOURCES = AUTO diff --git a/libemailfunctions/tests/testemail.cpp b/libemailfunctions/tests/testemail.cpp new file mode 100644 index 000000000..ce2111db0 --- /dev/null +++ b/libemailfunctions/tests/testemail.cpp @@ -0,0 +1,486 @@ +/* This file is part of the KDE project + Copyright (C) 2004 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License version 2 as published by the Free Software Foundation. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +// Test program for libemailfunctions/email.h +#include "email.h" + +#include <kcmdlineargs.h> +#include <kapplication.h> +#include <kdebug.h> + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + +using namespace KPIM; + +static bool check(const QString& txt, const QString& a, const QString& b) +{ + if (a == b) { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "ok" << endl; + } + else { + kdDebug() << txt << " : checking '" << a << "' against expected value '" << b << "'... " << "KO !" << endl; + exit(1); + } + return true; +} + +static bool check(const QString& txt, const QStringList& a, const QStringList& b) +{ + if ( a.join("\n") == b.join("\n") ) { + kdDebug() << txt << " : checking list [ " << a.join( ", " ) << " ] against expected value [ " << b.join( ", " ) << " ]... " << "ok" << endl; + } + else { + kdDebug() << txt << " : checking list [ " << a.join( ",\n" ) << " ] against expected value [ " << b.join( ",\n" ) << " ]... " << "KO !" << endl; + exit(1); + } + return true; +} + +static bool checkGetNameAndEmail(const QString& input, const QString& expName, const QString& expEmail, bool expRetVal) +{ + QString name, email; + bool retVal = KPIM::getNameAndMail(input, name, email); + check( "getNameAndMail " + input + " retVal", retVal?QString::fromLatin1( "true" ):QString::fromLatin1( "false" ), expRetVal?QString::fromLatin1( "true" ):QString::fromLatin1( "false" ) ); + check( "getNameAndMail " + input + " name", name, expName ); + check( "getNameAndMail " + input + " email", email, expEmail ); + return true; +} + +// convert this to a switch instead but hey, nothing speedy in here is needed but still.. it would be nice +static QString emailTestParseResultToString( EmailParseResult errorCode ) +{ + if( errorCode == TooManyAts ) { + return "TooManyAts"; + } else if( errorCode == TooFewAts ) { + return "TooFewAts"; + } else if( errorCode == AddressEmpty ) { + return "AddressEmpty"; + } else if( errorCode == MissingLocalPart ) { + return "MissingLocalPart"; + } else if( errorCode == MissingDomainPart ) { + return "MissingDomainPart"; + } else if( errorCode == UnbalancedParens ) { + return "UnbalancedParens"; + } else if( errorCode == AddressOk ) { + return "AddressOk"; + } else if( errorCode == UnclosedAngleAddr ) { + return "UnclosedAngleAddr"; + } else if( errorCode == UnexpectedEnd ) { + return "UnexpectedEnd"; + } else if( errorCode == UnopenedAngleAddr ) { + return "UnopenedAngleAddr"; + } else if( errorCode == DisallowedChar ) { + return "DisallowedChar"; + } else if( errorCode == UnexpectedComma ) { + return "UnexpectedComma"; + } else if( errorCode == UnbalancedQuote ) { + return "UnbalancedQuote"; + } else if( errorCode == InvalidDisplayName ) { + return "InvalidDisplayName"; + } + return "unknown error code"; +} + +static QString simpleEmailTestParseResultToString( bool validEmail ) +{ + if ( validEmail ) { + return "true"; + } + return "false"; +} + +static bool checkIsValidEmailAddress( const QString& input, const QString& expErrorCode ) +{ + EmailParseResult errorCode = KPIM::isValidEmailAddress( input ); + QString errorC = emailTestParseResultToString( errorCode ); + check( "isValidEmailAddress " + input + " errorCode ", errorC , expErrorCode ); + return true; +} + +static bool checkIsValidSimpleEmailAddress( const QString& input, const QString& expResult ) +{ + bool validEmail = KPIM::isValidSimpleEmailAddress( input ); + QString result = simpleEmailTestParseResultToString( validEmail ); + check( "isValidSimpleEmailAddress " + input + " result ", result, expResult ); + return true; +} + +static bool checkGetEmailAddress( const QString& input, const QString& expResult ) +{ + QString emailAddress = KPIM::getEmailAddress( input ); + QString result = emailAddress; + check( "getEmailAddress " + input + " result ", result, expResult ); + return true; +} + +static bool checkSplitEmailAddrList( const QString& input, const QStringList& expResult ) +{ + QStringList emailAddresses = KPIM::splitEmailAddrList( input ); + check( "splitEmailAddrList( \"" + input + "\" ) result ", emailAddresses, expResult ); + return true; +} + +static bool checkNormalizeAddressesAndEncodeIDNs( const QString& input, const QString& expResult ) +{ + QString result = KPIM::normalizeAddressesAndEncodeIDNs( input ); + check( "normalizeAddressesAndEncodeIDNs( \"" + input + "\" ) result ", result, expResult ); + return true; +} + +static bool checkNormalizeAddressesAndDecodeIDNs( const QString& input, const QString& expResult ) +{ + QString result = KPIM::normalizeAddressesAndDecodeIDNs( input ); + check( "normalizeAddressesAndDecodeIDNs( \"" + input + "\" ) result ", result, expResult ); + return true; +} + +static bool checkQuoteIfNecessary( const QString& input, const QString& expResult ) +{ + QString result = quoteNameIfNecessary( input ); + check( "quoteNameIfNecessary " + input + " result ", result, expResult ); + return true; +} + + +int main(int argc, char *argv[]) +{ + KApplication::disableAutoDcopRegistration(); + KCmdLineArgs::init( argc, argv, "testemail", 0, 0, 0, 0 ); + KApplication app( false, false ); + + // Empty input + checkGetNameAndEmail( QString::null, QString::null, QString::null, false ); + + // Email only + checkGetNameAndEmail( "faure@kde.org", QString::null, "faure@kde.org", false ); + + // Normal case + checkGetNameAndEmail( "David Faure <faure@kde.org>", "David Faure", "faure@kde.org", true ); + + // Double-quotes + checkGetNameAndEmail( "\"Faure, David\" <faure@kde.org>", "Faure, David", "faure@kde.org", true ); + checkGetNameAndEmail( "<faure@kde.org> \"David Faure\"", "David Faure", "faure@kde.org", true ); + + // Parenthesis + checkGetNameAndEmail( "faure@kde.org (David Faure)", "David Faure", "faure@kde.org", true ); + checkGetNameAndEmail( "(David Faure) faure@kde.org", "David Faure", "faure@kde.org", true ); + checkGetNameAndEmail( "My Name (me) <me@home.net>", "My Name (me)", "me@home.net", true ); // #93513 + + // Nested parenthesis as per https://intevation.de/roundup/kolab/issue858 + checkGetNameAndEmail( "faure@kde.org (David (The Man) Faure)", "David (The Man) Faure", "faure@kde.org", true ); + + // Double-quotes inside parenthesis + checkGetNameAndEmail( "faure@kde.org (David \"Crazy\" Faure)", "David \"Crazy\" Faure", "faure@kde.org", true ); + checkGetNameAndEmail( "(David \"Crazy\" Faure) faure@kde.org", "David \"Crazy\" Faure", "faure@kde.org", true ); + + // Parenthesis inside double-quotes + checkGetNameAndEmail( "\"Faure (David)\" <faure@kde.org>", "Faure (David)", "faure@kde.org", true ); + checkGetNameAndEmail( "<faure@kde.org> \"Faure (David)\"", "Faure (David)", "faure@kde.org", true ); + + // Space in email + checkGetNameAndEmail( "David Faure < faure@kde.org >", "David Faure", "faure@kde.org", true ); + + // Check that '@' in name doesn't confuse it + checkGetNameAndEmail( "faure@kde.org (a@b)", "a@b", "faure@kde.org", true ); + // Interestingly, this isn't supported. + //checkGetNameAndEmail( "\"a@b\" <faure@kde.org>", "a@b", "faure@kde.org", true ); + + // While typing, when there's no '@' yet + checkGetNameAndEmail( "foo", "foo", QString::null, false ); + checkGetNameAndEmail( "foo <", "foo", QString::null, false ); + checkGetNameAndEmail( "foo <b", "foo", "b", true ); + + // If multiple emails are there, only return the first one + checkGetNameAndEmail( "\"Faure, David\" <faure@kde.org>, KHZ <khz@khz.khz>", "Faure, David", "faure@kde.org", true ); + + // domain literals also need to work + checkGetNameAndEmail( "Matt Douhan <matt@[123.123.123.123]>", "Matt Douhan", "matt@[123.123.123.123]", true ); + + // @ inside the comment + checkGetNameAndEmail( "\"Matt@Douhan\" <matt@fruitsalad.org>", "Matt@Douhan", "matt@fruitsalad.org", true ); + + // No '@' + checkGetNameAndEmail( "foo <distlist>", "foo", "distlist", true ); + + // To many @'s + checkIsValidEmailAddress( "matt@@fruitsalad.org", "TooManyAts" ); + + // To few @'s + checkIsValidEmailAddress( "mattfruitsalad.org", "TooFewAts" ); + + // An empty string + checkIsValidEmailAddress( QString::null , "AddressEmpty" ); + + // email address starting with a @ + checkIsValidEmailAddress( "@mattfruitsalad.org", "MissingLocalPart" ); + + // make sure that starting @ and an additional @ in the same email address don't conflict + // trap the starting @ first and break + checkIsValidEmailAddress( "@matt@fruitsalad.org", "MissingLocalPart" ); + + // email address ending with a @ + checkIsValidEmailAddress( "mattfruitsalad.org@", "MissingDomainPart" ); + + // make sure that ending with@ and an additional @ in the email address don't conflict + checkIsValidEmailAddress( "matt@fruitsalad.org@", "MissingDomainPart" ); + + // unbalanced Parens + checkIsValidEmailAddress( "mattjongel)@fruitsalad.org", "UnbalancedParens" ); + + // unbalanced Parens the other way around + checkIsValidEmailAddress( "mattjongel(@fruitsalad.org", "UnbalancedParens" ); + + // Correct parens just to make sure it works + checkIsValidEmailAddress( "matt(jongel)@fruitsalad.org", "AddressOk" ); + + // Check that anglebrackets are closed + checkIsValidEmailAddress( "matt douhan<matt@fruitsalad.org", "UnclosedAngleAddr" ); + + // Check that angle brackets are closed the other way around + checkIsValidEmailAddress( "matt douhan>matt@fruitsalad.org", "UnopenedAngleAddr" ); + + // Check that angle brackets are closed the other way around, and anglebrackets in domainpart + // instead of local part + checkIsValidEmailAddress( "matt douhan matt@<fruitsalad.org", "UnclosedAngleAddr" ); + + // check that a properly formated anglebrackets situation is OK + checkIsValidEmailAddress( "matt douhan<matt@fruitsalad.org>", "AddressOk" ); + + // a full email address with comments angle brackets and the works should be valid too + checkIsValidEmailAddress( "Matt (jongel) Douhan <matt@fruitsalad.org>", "AddressOk" ); + + // Double quotes + checkIsValidEmailAddress( "\"Matt Douhan\" <matt@fruitsalad.org>", "AddressOk" ); + + // Double quotes inside parens + checkIsValidEmailAddress( "Matt (\"jongel\") Douhan <matt@fruitsalad.org>", "AddressOk" ); + + // DOuble quotes not closed + checkIsValidEmailAddress( "Matt \"jongel Douhan <matt@fruitsalad.org>", "UnbalancedQuote" ); + + // Parens inside double quotes + checkIsValidEmailAddress( "Matt \"(jongel)\" Douhan <matt@fruitsalad.org>", "AddressOk" ); + + // Space in email + checkIsValidEmailAddress( "Matt Douhan < matt@fruitsalad.org >", "AddressOk" ); + + // @ is allowed inisde doublequotes + checkIsValidEmailAddress( "\"matt@jongel\" <matt@fruitsalad.org>", "AddressOk" ); + + // anglebrackets inside dbl quotes + checkIsValidEmailAddress( "\"matt<blah blah>\" <matt@fruitsalad.org>", "AddressOk" ); + + // a , inside a double quoted string is OK, how do I know this? well Ingo says so + // and it makes sense since it is also a seperator of email addresses + checkIsValidEmailAddress( "\"Douhan, Matt\" <matt@fruitsalad.org>", "AddressOk" ); + + // Domains literals also need to work + checkIsValidEmailAddress( "Matt Douhan <matt@[123.123.123.123]>", "AddressOk" ); + + // Typo in domain literal address + checkIsValidEmailAddress( "Matt Douhan <matt@[123.123.123,123]>", "UnexpectedComma" ); + + // Some more insane tests but still valid so they must work + checkIsValidEmailAddress( "Matt Douhan <\"m@att\"@jongel.com>", "AddressOk" ); + + // BUG 99657 + checkIsValidEmailAddress( "matt@jongel.fibbel.com", "AddressOk" ); + + // BUG 98720 + checkIsValidEmailAddress( "mailto:@mydomain", "DisallowedChar" ); + + // correct error msg when a comma is inside <> + checkIsValidEmailAddress( "Matt Douhan <matt@fruitsalad,org>", "UnexpectedComma" ); + + //several commentlevels + checkIsValidEmailAddress( "Matt Douhan (hey(jongel)fibbel) <matt@fruitsalad.org>", "AddressOk" ); + + // several comment levels and one (the outer) being unbalanced + checkIsValidEmailAddress( "Matt Douhan (hey(jongel)fibbel <matt@fruitsalad.org>", "UnbalancedParens" ); + + // several comment levels and one (the inner) being unbalanced + checkIsValidEmailAddress( "Matt Douhan (hey(jongelfibbel) <matt@fruitsalad.org>", "UnbalancedParens" ); + + // an error inside a double quote is no error + checkIsValidEmailAddress ( "Matt Douhan \"(jongel\" <matt@fruitsalad.org>", "AddressOk" ); + + // inside a quoted string double quotes are only allowed in pairs as per rfc2822 + checkIsValidEmailAddress( "Matt Douhan \"jongel\"fibbel\" <matt@fruitsalad.org>", "UnbalancedQuote" ); + + // a questionmark is valid in an atom + checkIsValidEmailAddress ( "Matt? <matt@fruitsalad.org>", "AddressOk" ); + + // weird but OK + checkIsValidEmailAddress( "\"testing, \\\"testing\" <matt@fruitsalad.org>", "AddressOk" ); + + // escape a quote to many to see if it makes it invalid + checkIsValidEmailAddress( "\"testing, \\\"testing\\\" <matt@fruitsalad.org>", "UnbalancedQuote" ); + + // escape a parens and thus make a comma appear + checkIsValidEmailAddress( "Matt (jongel, fibbel\\) <matt@fruitsalad.org>", "UnbalancedParens" ); + + // several errors inside doublequotes + checkIsValidEmailAddress( "Matt \"(jongel,\\\" < fibbel\\\\)\" <matt@fruitsalad.org>", "AddressOk" ); + + // BUG 105705 + checkIsValidEmailAddress( "matt-@fruitsalad.org", "AddressOk" ); + + // underscore at the end of local part + checkIsValidEmailAddress( "matt_@fruitsalad.org", "AddressOk" ); + + // how about ( comment ) in the domain part + checkIsValidEmailAddress( "matt_@(this is a cool host)fruitsalad.org", "AddressOk" ); + + // To quote rfc2822 the test below is aesthetically displeasing, but perfectly legal. + checkIsValidEmailAddress( "Pete(A wonderful \\) chap) <pete(his account)@silly.test(his host)>", "AddressOk" ); + + // quoted pair or not quoted pair + checkIsValidEmailAddress( "\"jongel '\\\" fibbel\" <matt@fruitsalad.org>", "AddressOk" ); + checkIsValidEmailAddress( "\"jongel '\" fibbel\" <matt@fruitsalad.org>", "UnbalancedQuote" ); + + // full atext support according to rfc2822 + checkIsValidEmailAddress( "!matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "#matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "$matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "%matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "&matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "'matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "*matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "+matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "/matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "=matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "?matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "^matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "_matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "-matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "`matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "{matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "|matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "}matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "~matt@fruitsalad.org", "AddressOk" ); + checkIsValidEmailAddress( "matt%matt@fruitsalad.org", "AddressOk" ); + + //bug 105405 + checkIsValidEmailAddress( "[foobar] <matt@fruitsalad.org>", "InvalidDisplayName" ); + checkIsValidEmailAddress( "matt \"[foobar]\" Douhan <matt@fruitsalad.org>", "AddressOk" ); + + checkIsValidEmailAddress( "Matt Douhan <matt\"@@\"fruitsalad.org>", "TooFewAts" ); + + // checks for "pure" email addresses in the form of xxx@yyy.tld + checkIsValidSimpleEmailAddress( "matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( QString::fromUtf8("test@täst.invalid"), "true" ); + // non-ASCII char as first char of IDN + checkIsValidSimpleEmailAddress( QString::fromUtf8("i_want@øl.invalid"), "true" ); + checkIsValidSimpleEmailAddress( "matt@[123.123.123.123]", "true" ); + checkIsValidSimpleEmailAddress( "matt@[3.3.3.3]", "true" ); + checkIsValidSimpleEmailAddress( "matt@[4.4.4.4]", "true" ); + checkIsValidSimpleEmailAddress( "matt@[192.168.254.254]", "true" ); + checkIsValidSimpleEmailAddress( "\"matt\"@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "-matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "\"-matt\"@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "matt@jongel.fibbel.com", "true" ); + checkIsValidSimpleEmailAddress( "Matt Douhan <matt@fruitsalad.org>", "false" ); + // BUG 105705 + checkIsValidSimpleEmailAddress( "matt-@fibbel.com", "true" ); + checkIsValidSimpleEmailAddress( "matt@fibbel-is-a-geek.com", "true" ); + checkIsValidSimpleEmailAddress( "matt_@fibbel.com", "true" ); + // Check the defined chars for atext according to rfc2822 + checkIsValidSimpleEmailAddress( "!matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "#matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "$matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "%matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "&matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "'matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "*matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "+matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "/matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "=matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "?matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "^matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "_matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "-matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "`matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "{matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "|matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "}matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "~matt@fruitsalad.org", "true" ); + // BUG 108476 + checkIsValidSimpleEmailAddress( "foo+matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "bar=matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "jongel-matt@fruitsalad.org", "true" ); + checkIsValidSimpleEmailAddress( "matt-@fruitsalad.org", "true" ); + + // check if the pure email address is wrong + checkIsValidSimpleEmailAddress( "mattfruitsalad.org", "false" ); + checkIsValidSimpleEmailAddress( "matt@[123.123.123.123", "false" ); + checkIsValidSimpleEmailAddress( "matt@123.123.123.123]", "false" ); + checkIsValidSimpleEmailAddress( "\"matt@fruitsalad.org", "false" ); + checkIsValidSimpleEmailAddress( "matt\"@fruitsalad.org", "false" ); + checkIsValidSimpleEmailAddress( QString::null, "false" ); + + // and here some insane but still valid cases + checkIsValidSimpleEmailAddress( "\"m@tt\"@fruitsalad.org", "true" ); + + checkIsValidSimpleEmailAddress( "matt\"@@\"fruitsalad.org", "false" ); + + // check the getEmailAddress address method + checkGetEmailAddress( "matt@fruitsalad.org", "matt@fruitsalad.org" ); + checkGetEmailAddress( "Matt Douhan <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "\"Matt Douhan <blah blah>\" <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "\"Matt <blah blah>\" <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "Matt Douhan (jongel) <matt@fruitsalad.org", QString() ); + checkGetEmailAddress( "Matt Douhan (m@tt) <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "\"Douhan, Matt\" <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "\"Matt Douhan (m@tt)\" <matt@fruitsalad.org>", "matt@fruitsalad.org" ); + checkGetEmailAddress( "\"Matt Douhan\" (matt <matt@fruitsalad.org>", QString() ); + checkGetEmailAddress( "Matt Douhan <matt@[123.123.123.123]>", "matt@[123.123.123.123]" ); + + // check the splitEmailAddrList method + checkSplitEmailAddrList( "kloecker@kde.org (Kloecker, Ingo)", QStringList() << "kloecker@kde.org (Kloecker, Ingo)" ); + checkSplitEmailAddrList( "Matt Douhan <matt@fruitsalad.org>, Foo Bar <foo@bar.com>", QStringList() << "Matt Douhan <matt@fruitsalad.org>" << "Foo Bar <foo@bar.com>" ); + checkSplitEmailAddrList( "\"Matt, Douhan\" <matt@fruitsalad.org>, Foo Bar <foo@bar.com>", QStringList() << "\"Matt, Douhan\" <matt@fruitsalad.org>" << "Foo Bar <foo@bar.com>" ); + + // check checkNormalizeAddressesAndEncodeIDNs + checkNormalizeAddressesAndEncodeIDNs( "matt@fruitsalad.org", "matt@fruitsalad.org" ); + checkNormalizeAddressesAndEncodeIDNs( "Matt Douhan <matt@fruitsalad.org>", "Matt Douhan <matt@fruitsalad.org>" ); + checkNormalizeAddressesAndEncodeIDNs( "Matt Douhan (jongel) <matt@fruitsalad.org>", "Matt Douhan (jongel) <matt@fruitsalad.org>" ); + checkNormalizeAddressesAndEncodeIDNs( "Matt Douhan (jongel,fibbel) <matt@fruitsalad.org>", "Matt Douhan (jongel,fibbel) <matt@fruitsalad.org>" ); + checkNormalizeAddressesAndEncodeIDNs( "matt@fruitsalad.org (jongel,fibbel)", "\"jongel,fibbel\" <matt@fruitsalad.org>" ); + checkNormalizeAddressesAndEncodeIDNs( "matt@fruitsalad.org (\"jongel,fibbel\")", "\"jongel,fibbel\" <matt@fruitsalad.org>" ); + + // check checkNormalizeAddressesAndDecodeIDNs + checkNormalizeAddressesAndDecodeIDNs( "=?us-ascii?Q?Surname=2C=20Name?= <nobody@example.org>", "\"Surname, Name\" <nobody@example.org>" ); + checkNormalizeAddressesAndDecodeIDNs( "=?iso-8859-1?B?5Hf8b2xmLPZBbmRyZWFz?= <nobody@example.org>", QString::fromUtf8("\"äwüolf,öAndreas\" <nobody@example.org>") ); + + // check the "quote if necessary" method + checkQuoteIfNecessary( "Matt Douhan", "Matt Douhan"); + checkQuoteIfNecessary( "Douhan, Matt", "\"Douhan, Matt\""); + checkQuoteIfNecessary( "Matt \"jongel\" Douhan", "\"Matt \\\"jongel\\\" Douhan\""); + checkQuoteIfNecessary( "Matt \\\"jongel\\\" Douhan", "\"Matt \\\"jongel\\\" Douhan\""); + checkQuoteIfNecessary( "trailing '\\\\' should never occur \\", "\"trailing '\\\\' should never occur \\\""); + checkQuoteIfNecessary( "\"don't quote again\"", "\"don't quote again\"" ); + checkQuoteIfNecessary( "\"leading double quote", "\"\\\"leading double quote\"" ); + checkQuoteIfNecessary( "trailing double quote\"", "\"trailing double quote\\\"\"" ); + + printf("\nTest OK !\n"); + + return 0; +} + diff --git a/libemailfunctions/tests/testidmapper.cpp b/libemailfunctions/tests/testidmapper.cpp new file mode 100644 index 000000000..245895ec9 --- /dev/null +++ b/libemailfunctions/tests/testidmapper.cpp @@ -0,0 +1,54 @@ +/* + This file is part of libkdepim. + + Copyright (c) 2004 Tobias Koenig <tokoe@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <qapplication.h> + +#include "idmapper.h" + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + + KPIM::IdMapper mapper( "test.uidmap" ) ; + + mapper.setRemoteId( "foo", "bar" ); + mapper.setRemoteId( "yes", "klar" ); + mapper.setRemoteId( "no", "nee" ); + + qDebug( "full:\n%s", mapper.asString().latin1() ); + + mapper.save(); + + mapper.clear(); + qDebug( "empty:\n%s", mapper.asString().latin1() ); + + mapper.load(); + qDebug( "full again:\n%s", mapper.asString().latin1() ); + + mapper.save(); + + mapper.clear(); + qDebug( "empty:\n%s", mapper.asString().latin1() ); + + mapper.load(); + qDebug( "full again:\n%s", mapper.asString().latin1() ); + return 0; +} |