summaryrefslogtreecommitdiffstats
path: root/libemailfunctions
diff options
context:
space:
mode:
Diffstat (limited to 'libemailfunctions')
-rw-r--r--libemailfunctions/Mainpage.dox10
-rw-r--r--libemailfunctions/Makefile.am19
-rw-r--r--libemailfunctions/email.cpp973
-rw-r--r--libemailfunctions/email.h260
-rw-r--r--libemailfunctions/idmapper.cpp191
-rw-r--r--libemailfunctions/idmapper.h159
-rw-r--r--libemailfunctions/kasciistricmp.cpp47
-rw-r--r--libemailfunctions/kasciistricmp.h28
-rw-r--r--libemailfunctions/kasciistringtools.cpp62
-rw-r--r--libemailfunctions/kasciistringtools.h49
-rw-r--r--libemailfunctions/networkstatus.cpp88
-rw-r--r--libemailfunctions/networkstatus.h98
-rw-r--r--libemailfunctions/tests/Makefile.am13
-rw-r--r--libemailfunctions/tests/testemail.cpp486
-rw-r--r--libemailfunctions/tests/testidmapper.cpp54
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) &lt;addrSpec&gt;
+ - displayName &lt;addrSpec&gt;
+ - comment &lt;addrSpec&gt;
+ - 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;
+}