/kdepim-4.3.4/kmail/stringutil.cpp
C++ | 1014 lines | 858 code | 80 blank | 76 comment | 146 complexity | 0c74400eb9d420c1ec6aa1e41ddfff10 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-2.1, GPL-2.0
- /* Copyright 2009 Thomas McGuire <mcguire@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 ) version 3 or, at the discretion of KDE e.V.
- ( which shall act as a proxy as in section 14 of the GPLv3 ), 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 "stringutil.h"
- #ifndef KMAIL_UNITTESTS
- #include "kmaddrbook.h"
- #include "kmkernel.h"
- #include <libkdepim/kaddrbookexternal.h>
- #include <mimelib/enum.h>
- #include <kmime/kmime_charfreq.h>
- #include <kmime/kmime_header_parsing.h>
- #include <KPIMUtils/Email>
- #include <KPIMIdentities/IdentityManager>
- #include <kascii.h>
- #include <KConfigGroup>
- #include <KDebug>
- #include <KUrl>
- #include <kuser.h>
- #include <QHostInfo>
- #include <QRegExp>
- #endif
- #include <QStringList>
- #ifndef KMAIL_UNITTESTS
- using namespace KMime;
- using namespace KMime::Types;
- using namespace KMime::HeaderParsing;
- #endif
- namespace KMail
- {
- namespace StringUtil
- {
- static void removeTrailingSpace( QString &line )
- {
- int i = line.length()-1;
- while( (i >= 0) && ((line[i] == ' ') || (line[i] == '\t')))
- i--;
- line.truncate( i+1);
- }
- static QString splitLine( QString &line)
- {
- removeTrailingSpace( line );
- int i = 0;
- int j = -1;
- int l = line.length();
- // TODO: Replace tabs with spaces first.
- while(i < l)
- {
- QChar c = line[i];
- if ((c == '>') || (c == ':') || (c == '|'))
- j = i+1;
- else if ((c != ' ') && (c != '\t'))
- break;
- i++;
- }
- if ( j <= 0 )
- {
- return "";
- }
- if ( i == l )
- {
- QString result = line.left(j);
- line.clear();
- return result;
- }
- QString result = line.left(j);
- line = line.mid(j);
- return result;
- }
- static QString flowText(QString &text, const QString& indent, int maxLength)
- {
- maxLength--;
- if (text.isEmpty())
- {
- return indent+"<NULL>\n";
- }
- QString result;
- while (1)
- {
- int i;
- if ((int) text.length() > maxLength)
- {
- i = maxLength;
- while( (i >= 0) && (text[i] != ' '))
- i--;
- if (i <= 0)
- {
- // Couldn't break before maxLength.
- i = maxLength;
- // while( (i < (int) text.length()) && (text[i] != ' '))
- // i++;
- }
- }
- else
- {
- i = text.length();
- }
- QString line = text.left(i);
- if (i < (int) text.length())
- text = text.mid(i);
- else
- text.clear();
- result += indent + line + '\n';
- if (text.isEmpty())
- return result;
- }
- }
- static bool flushPart(QString &msg, QStringList &part,
- const QString &indent, int maxLength)
- {
- maxLength -= indent.length();
- if (maxLength < 20) maxLength = 20;
- // Remove empty lines at end of quote
- while ((part.begin() != part.end()) && part.last().isEmpty())
- {
- part.removeLast();
- }
- QString text;
- for(QStringList::Iterator it2 = part.begin();
- it2 != part.end();
- ++it2)
- {
- QString line = (*it2);
- if (line.isEmpty())
- {
- if (!text.isEmpty())
- msg += flowText(text, indent, maxLength);
- msg += indent + '\n';
- }
- else
- {
- if (text.isEmpty())
- text = line;
- else
- text += ' '+line.trimmed();
- if (((int) text.length() < maxLength) || ((int) line.length() < (maxLength-10)))
- msg += flowText(text, indent, maxLength);
- }
- }
- if (!text.isEmpty())
- msg += flowText(text, indent, maxLength);
- bool appendEmptyLine = true;
- if (!part.count())
- appendEmptyLine = false;
- part.clear();
- return appendEmptyLine;
- }
- QString stripSignature ( const QString & msg, bool clearSigned )
- {
- // Following RFC 3676, only > before --
- // I prefer to not delete a SB instead of delete good mail content.
- const QRegExp sbDelimiterSearch = clearSigned ?
- QRegExp( "(^|\n)[> ]*--\\s?\n" ) : QRegExp( "(^|\n)[> ]*-- \n" );
- // The regular expression to look for prefix change
- const QRegExp commonReplySearch = QRegExp( "^[ ]*>" );
- QString res = msg;
- int posDeletingStart = 1; // to start looking at 0
- // While there are SB delimiters (start looking just before the deleted SB)
- while ( ( posDeletingStart = res.indexOf( sbDelimiterSearch , posDeletingStart -1 ) ) >= 0 )
- {
- QString prefix; // the current prefix
- QString line; // the line to check if is part of the SB
- int posNewLine = -1;
- int posSignatureBlock = -1;
- // Look for the SB beginning
- posSignatureBlock = res.indexOf( '-', posDeletingStart );
- // The prefix before "-- "$
- if ( res[posDeletingStart] == '\n' ) ++posDeletingStart;
- prefix = res.mid( posDeletingStart, posSignatureBlock - posDeletingStart );
- posNewLine = res.indexOf( '\n', posSignatureBlock ) + 1;
- // now go to the end of the SB
- while ( posNewLine < res.size() && posNewLine > 0 )
- {
- // handle the undefined case for mid ( x , -n ) where n>1
- int nextPosNewLine = res.indexOf( '\n', posNewLine );
- if ( nextPosNewLine < 0 ) nextPosNewLine = posNewLine - 1;
- line = res.mid( posNewLine, nextPosNewLine - posNewLine );
- // check when the SB ends:
- // * does not starts with prefix or
- // * starts with prefix+(any substring of prefix)
- if ( ( prefix.isEmpty() && line.indexOf( commonReplySearch ) < 0 ) ||
- ( !prefix.isEmpty() && line.startsWith( prefix ) &&
- line.mid( prefix.size() ).indexOf( commonReplySearch ) < 0 ) )
- {
- posNewLine = res.indexOf( '\n', posNewLine ) + 1;
- }
- else
- break; // end of the SB
- }
- // remove the SB or truncate when is the last SB
- if ( posNewLine > 0 )
- res.remove( posDeletingStart, posNewLine - posDeletingStart );
- else
- res.truncate( posDeletingStart );
- }
- return res;
- }
- #ifndef KMAIL_UNITTESTS
- //-----------------------------------------------------------------------------
- QList<int> determineAllowedCtes( const CharFreq& cf,
- bool allow8Bit,
- bool willBeSigned )
- {
- QList<int> allowedCtes;
- switch ( cf.type() ) {
- case CharFreq::SevenBitText:
- allowedCtes << DwMime::kCte7bit;
- case CharFreq::EightBitText:
- if ( allow8Bit )
- allowedCtes << DwMime::kCte8bit;
- case CharFreq::SevenBitData:
- if ( cf.printableRatio() > 5.0/6.0 ) {
- // let n the length of data and p the number of printable chars.
- // Then base64 \approx 4n/3; qp \approx p + 3(n-p)
- // => qp < base64 iff p > 5n/6.
- allowedCtes << DwMime::kCteQp;
- allowedCtes << DwMime::kCteBase64;
- } else {
- allowedCtes << DwMime::kCteBase64;
- allowedCtes << DwMime::kCteQp;
- }
- break;
- case CharFreq::EightBitData:
- allowedCtes << DwMime::kCteBase64;
- break;
- case CharFreq::None:
- default:
- // just nothing (avoid compiler warning)
- ;
- }
- // In the following cases only QP and Base64 are allowed:
- // - the buffer will be OpenPGP/MIME signed and it contains trailing
- // whitespace (cf. RFC 3156)
- // - a line starts with "From "
- if ( ( willBeSigned && cf.hasTrailingWhitespace() ) ||
- cf.hasLeadingFrom() ) {
- allowedCtes.removeAll( DwMime::kCte8bit );
- allowedCtes.removeAll( DwMime::kCte7bit );
- }
- return allowedCtes;
- }
- AddressList splitAddrField( const QByteArray & str )
- {
- AddressList result;
- const char * scursor = str.begin();
- if ( !scursor )
- return AddressList();
- const char * const send = str.begin() + str.length();
- if ( !parseAddressList( scursor, send, result ) )
- kDebug() << "Error in address splitting: parseAddressList returned false!";
- return result;
- }
- QString generateMessageId( const QString& addr )
- {
- QDateTime datetime = QDateTime::currentDateTime();
- QString msgIdStr;
- msgIdStr = '<' + datetime.toString( "yyyyMMddhhmm.sszzz" );
- QString msgIdSuffix;
- KConfigGroup general( KMKernel::config(), "General" );
- if( general.readEntry( "useCustomMessageIdSuffix", false ) )
- msgIdSuffix = general.readEntry( "myMessageIdSuffix" );
- if( !msgIdSuffix.isEmpty() )
- msgIdStr += '@' + msgIdSuffix;
- else
- msgIdStr += '.' + KPIMUtils::toIdn( addr );
- msgIdStr += '>';
- return msgIdStr;
- }
- #endif
- QByteArray html2source( const QByteArray & src )
- {
- QByteArray result( 1 + 6*src.length(), '\0' ); // maximal possible length
- QByteArray::ConstIterator s = src.begin();
- QByteArray::Iterator d = result.begin();
- while ( *s ) {
- switch ( *s ) {
- case '<': {
- *d++ = '&';
- *d++ = 'l';
- *d++ = 't';
- *d++ = ';';
- ++s;
- }
- break;
- case '\r': {
- ++s;
- }
- break;
- case '\n': {
- *d++ = '<';
- *d++ = 'b';
- *d++ = 'r';
- *d++ = '>';
- ++s;
- }
- break;
- case '>': {
- *d++ = '&';
- *d++ = 'g';
- *d++ = 't';
- *d++ = ';';
- ++s;
- }
- break;
- case '&': {
- *d++ = '&';
- *d++ = 'a';
- *d++ = 'm';
- *d++ = 'p';
- *d++ = ';';
- ++s;
- }
- break;
- case '"': {
- *d++ = '&';
- *d++ = 'q';
- *d++ = 'u';
- *d++ = 'o';
- *d++ = 't';
- *d++ = ';';
- ++s;
- }
- break;
- case '\'': {
- *d++ = '&';
- *d++ = 'a';
- *d++ = 'p';
- *d++ = 's';
- *d++ = ';';
- ++s;
- }
- break;
- default:
- *d++ = *s++;
- }
- }
- result.truncate( d - result.begin() );
- return result;
- }
- #ifndef KMAIL_UNITTESTS
- QString encodeMailtoUrl( const QString& str )
- {
- QString result;
- result = QString::fromLatin1( KMMsgBase::encodeRFC2047String( str,
- "utf-8" ) );
- result = KUrl::toPercentEncoding( result );
- return result;
- }
- QString decodeMailtoUrl( const QString& url )
- {
- QString result;
- result = KUrl::fromPercentEncoding( url.toLatin1() );
- result = KMMsgBase::decodeRFC2047String( result.toLatin1() );
- return result;
- }
- #endif
- QByteArray stripEmailAddr( const QByteArray& aStr )
- {
- //kDebug() << "(" << aStr <<" )";
- if ( aStr.isEmpty() )
- return QByteArray();
- QByteArray result;
- // 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.
- QByteArray name;
- QByteArray comment;
- QByteArray angleAddress;
- enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
- bool inQuotedString = false;
- int commentLevel = 0;
- for ( const char* p = aStr.data(); *p; ++p ) {
- switch ( context ) {
- case TopLevel : {
- switch ( *p ) {
- case '"' : inQuotedString = !inQuotedString;
- break;
- case '(' : if ( !inQuotedString ) {
- context = InComment;
- commentLevel = 1;
- }
- else
- name += *p;
- break;
- case '<' : if ( !inQuotedString ) {
- context = InAngleAddress;
- }
- else
- name += *p;
- break;
- case '\\' : // quoted character
- ++p; // skip the '\'
- if ( *p )
- name += *p;
- break;
- case ',' : if ( !inQuotedString ) {
- // next email address
- if ( !result.isEmpty() )
- result += ", ";
- name = name.trimmed();
- comment = comment.trimmed();
- angleAddress = angleAddress.trimmed();
- /*
- kDebug() <<"Name : \"" << name
- << "\"";
- kDebug() <<"Comment : \"" << comment
- << "\"";
- kDebug() <<"Address : \"" << angleAddress
- << "\"";
- */
- if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
- // handle Outlook-style addresses like
- // john.doe@invalid (John Doe)
- result += comment;
- }
- else if ( !name.isEmpty() ) {
- result += name;
- }
- else if ( !comment.isEmpty() ) {
- result += comment;
- }
- else if ( !angleAddress.isEmpty() ) {
- result += angleAddress;
- }
- name = QByteArray();
- comment = QByteArray();
- angleAddress = QByteArray();
- }
- else
- name += *p;
- break;
- default : name += *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
- ++p; // skip the '\'
- if ( *p )
- comment += *p;
- break;
- default : comment += *p;
- }
- break;
- }
- case InAngleAddress : {
- switch ( *p ) {
- case '"' : inQuotedString = !inQuotedString;
- angleAddress += *p;
- break;
- case '>' : if ( !inQuotedString ) {
- context = TopLevel;
- }
- else
- angleAddress += *p;
- break;
- case '\\' : // quoted character
- ++p; // skip the '\'
- if ( *p )
- angleAddress += *p;
- break;
- default : angleAddress += *p;
- }
- break;
- }
- } // switch ( context )
- }
- if ( !result.isEmpty() )
- result += ", ";
- name = name.trimmed();
- comment = comment.trimmed();
- angleAddress = angleAddress.trimmed();
- /*
- kDebug() <<"Name : \"" << name <<"\"";
- kDebug() <<"Comment : \"" << comment <<"\"";
- kDebug() <<"Address : \"" << angleAddress <<"\"";
- */
- if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
- // handle Outlook-style addresses like
- // john.doe@invalid (John Doe)
- result += comment;
- }
- else if ( !name.isEmpty() ) {
- result += name;
- }
- else if ( !comment.isEmpty() ) {
- result += comment;
- }
- else if ( !angleAddress.isEmpty() ) {
- result += angleAddress;
- }
- //kDebug() << "Returns \"" << result << "\"";
- return result;
- }
- QString stripEmailAddr( const QString& aStr )
- {
- //kDebug() << "(" << aStr << ")";
- if ( aStr.isEmpty() )
- return QString();
- QString result;
- // 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.
- QString name;
- QString comment;
- QString angleAddress;
- enum { TopLevel, InComment, InAngleAddress } context = TopLevel;
- bool inQuotedString = false;
- int commentLevel = 0;
- QChar ch;
- int strLength(aStr.length());
- for ( int index = 0; index < strLength; ++index ) {
- ch = aStr[index];
- switch ( context ) {
- case TopLevel : {
- switch ( ch.toLatin1() ) {
- case '"' : inQuotedString = !inQuotedString;
- break;
- case '(' : if ( !inQuotedString ) {
- context = InComment;
- commentLevel = 1;
- }
- else
- name += ch;
- break;
- case '<' : if ( !inQuotedString ) {
- context = InAngleAddress;
- }
- else
- name += ch;
- break;
- case '\\' : // quoted character
- ++index; // skip the '\'
- if ( index < aStr.length() )
- name += aStr[index];
- break;
- case ',' : if ( !inQuotedString ) {
- // next email address
- if ( !result.isEmpty() )
- result += ", ";
- name = name.trimmed();
- comment = comment.trimmed();
- angleAddress = angleAddress.trimmed();
- /*
- kDebug() <<"Name : \"" << name
- << "\"";
- kDebug() <<"Comment : \"" << comment
- << "\"";
- kDebug() <<"Address : \"" << angleAddress
- << "\"";
- */
- if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
- // handle Outlook-style addresses like
- // john.doe@invalid (John Doe)
- result += comment;
- }
- else if ( !name.isEmpty() ) {
- result += name;
- }
- else if ( !comment.isEmpty() ) {
- result += comment;
- }
- else if ( !angleAddress.isEmpty() ) {
- result += angleAddress;
- }
- name.clear();
- comment.clear();
- angleAddress.clear();
- }
- else
- name += ch;
- break;
- default : name += ch;
- }
- break;
- }
- case InComment : {
- switch ( ch.toLatin1() ) {
- case '(' : ++commentLevel;
- comment += ch;
- break;
- case ')' : --commentLevel;
- if ( commentLevel == 0 ) {
- context = TopLevel;
- comment += ' '; // separate the text of several comments
- }
- else
- comment += ch;
- break;
- case '\\' : // quoted character
- ++index; // skip the '\'
- if ( index < aStr.length() )
- comment += aStr[index];
- break;
- default : comment += ch;
- }
- break;
- }
- case InAngleAddress : {
- switch ( ch.toLatin1() ) {
- case '"' : inQuotedString = !inQuotedString;
- angleAddress += ch;
- break;
- case '>' : if ( !inQuotedString ) {
- context = TopLevel;
- }
- else
- angleAddress += ch;
- break;
- case '\\' : // quoted character
- ++index; // skip the '\'
- if ( index < aStr.length() )
- angleAddress += aStr[index];
- break;
- default : angleAddress += ch;
- }
- break;
- }
- } // switch ( context )
- }
- if ( !result.isEmpty() )
- result += ", ";
- name = name.trimmed();
- comment = comment.trimmed();
- angleAddress = angleAddress.trimmed();
- /*
- kDebug() <<"Name : \"" << name <<"\"";
- kDebug() <<"Comment : \"" << comment <<"\"";
- kDebug() <<"Address : \"" << angleAddress <<"\"";
- */
- if ( angleAddress.isEmpty() && !comment.isEmpty() ) {
- // handle Outlook-style addresses like
- // john.doe@invalid (John Doe)
- result += comment;
- }
- else if ( !name.isEmpty() ) {
- result += name;
- }
- else if ( !comment.isEmpty() ) {
- result += comment;
- }
- else if ( !angleAddress.isEmpty() ) {
- result += angleAddress;
- }
- //kDebug() << "Returns \"" << result << "\"";
- return result;
- }
- QString quoteHtmlChars( const QString& str, bool removeLineBreaks )
- {
- QString result;
- unsigned int strLength(str.length());
- result.reserve( 6*strLength ); // maximal possible length
- for( unsigned int i = 0; i < strLength; ++i ) {
- switch ( str[i].toLatin1() ) {
- case '<':
- result += "<";
- break;
- case '>':
- result += ">";
- break;
- case '&':
- result += "&";
- break;
- case '"':
- result += """;
- break;
- case '\n':
- if ( !removeLineBreaks )
- result += "<br>";
- break;
- case '\r':
- // ignore CR
- break;
- default:
- result += str[i];
- }
- }
- result.squeeze();
- return result;
- }
- #ifndef KMAIL_UNITTESTS
- QString emailAddrAsAnchor( const QString& aEmail, bool stripped, const QString& cssStyle,
- bool aLink )
- {
- if( aEmail.isEmpty() )
- return aEmail;
- const QStringList addressList = KPIMUtils::splitAddressList( aEmail );
- QString result;
- for( QStringList::ConstIterator it = addressList.constBegin();
- ( it != addressList.constEnd() );
- ++it ) {
- if( !(*it).isEmpty() ) {
- QString address = *it;
- if( aLink ) {
- result += "<a href=\"mailto:"
- + encodeMailtoUrl( address )
- + "\" "+cssStyle+">";
- }
- if( stripped )
- address = stripEmailAddr( address );
- result += quoteHtmlChars( address, true );
- if( aLink ) {
- result += "</a>, ";
- }
- }
- }
- // cut of the trailing ", "
- if( aLink ) {
- result.truncate( result.length() - 2 );
- }
- //kDebug() << "('" << aEmail << "') returns:\n-->" << result << "<--";
- return result;
- }
- QStringList stripAddressFromAddressList( const QString& address,
- const QStringList& list )
- {
- QStringList addresses( list );
- QString addrSpec( KPIMUtils::extractEmailAddress( address ) );
- for ( QStringList::Iterator it = addresses.begin();
- it != addresses.end(); ) {
- if ( kasciistricmp( addrSpec.toUtf8().data(),
- KPIMUtils::extractEmailAddress( *it ).toUtf8().data() ) == 0 ) {
- kDebug() << "Removing" << *it <<" from the address list";
- it = addresses.erase( it );
- }
- else
- ++it;
- }
- return addresses;
- }
- QStringList stripMyAddressesFromAddressList( const QStringList& list )
- {
- QStringList addresses = list;
- for( QStringList::Iterator it = addresses.begin();
- it != addresses.end(); ) {
- kDebug() << "Check whether" << *it <<"is one of my addresses";
- if( kmkernel->identityManager()->thatIsMe( KPIMUtils::extractEmailAddress( *it ) ) ) {
- kDebug() << "Removing" << *it <<"from the address list";
- it = addresses.erase( it );
- }
- else
- ++it;
- }
- return addresses;
- }
- bool addressIsInAddressList( const QString& address,
- const QStringList& addresses )
- {
- QString addrSpec = KPIMUtils::extractEmailAddress( address );
- for( QStringList::ConstIterator it = addresses.begin();
- it != addresses.end(); ++it ) {
- if ( kasciistricmp( addrSpec.toUtf8().data(),
- KPIMUtils::extractEmailAddress( *it ).toUtf8().data() ) == 0 )
- return true;
- }
- return false;
- }
- QString expandAliases( const QString& recipients )
- {
- if ( recipients.isEmpty() )
- return QString();
- QStringList recipientList = KPIMUtils::splitAddressList( recipients );
- QString expandedRecipients;
- for ( QStringList::Iterator it = recipientList.begin();
- it != recipientList.end(); ++it ) {
- if ( !expandedRecipients.isEmpty() )
- expandedRecipients += ", ";
- QString receiver = (*it).trimmed();
- // try to expand distribution list
- QString expandedList = KPIM::KAddrBookExternal::expandDistributionList( receiver );
- if ( !expandedList.isEmpty() ) {
- expandedRecipients += expandedList;
- continue;
- }
- // try to expand nick name
- QString expandedNickName = KabcBridge::expandNickName( receiver );
- if ( !expandedNickName.isEmpty() ) {
- expandedRecipients += expandedNickName;
- continue;
- }
- // check whether the address is missing the domain part
- QByteArray displayName, addrSpec, comment;
- KPIMUtils::splitAddress( receiver.toLatin1(), displayName, addrSpec, comment );
- if ( !addrSpec.contains('@') ) {
- KConfigGroup general( KMKernel::config(), "General" );
- QString defaultdomain = general.readEntry( "Default domain" );
- if ( !defaultdomain.isEmpty() ) {
- expandedRecipients += KPIMUtils::normalizedAddress( displayName, addrSpec + '@' + defaultdomain, comment );
- }
- else {
- expandedRecipients += guessEmailAddressFromLoginName( addrSpec );
- }
- }
- else
- expandedRecipients += receiver;
- }
- return expandedRecipients;
- }
- QString guessEmailAddressFromLoginName( const QString& loginName )
- {
- if ( loginName.isEmpty() )
- return QString();
- QString address = loginName;
- address += '@';
- address += QHostInfo::localHostName();
- // try to determine the real name
- const KUser user( loginName );
- if ( user.isValid() ) {
- QString fullName = user.property( KUser::FullName ).toString();
- if ( fullName.contains( QRegExp( "[^ 0-9A-Za-z\\x0080-\\xFFFF]" ) ) )
- address = '"' + fullName.replace( '\\', "\\" ).replace( '"', "\\" )
- + "\" <" + address + '>';
- else
- address = fullName + " <" + address + '>';
- }
- return address;
- }
- #endif
- QString smartQuote( const QString & msg, int maxLineLength )
- {
- QStringList part;
- QString oldIndent;
- bool firstPart = true;
- const QStringList lines = msg.split('\n');
- QString result;
- for(QStringList::const_iterator it = lines.begin();
- it != lines.end();
- ++it)
- {
- QString line = *it;
- const QString indent = splitLine( line );
- if ( line.isEmpty())
- {
- if (!firstPart)
- part.append(QString());
- continue;
- };
- if (firstPart)
- {
- oldIndent = indent;
- firstPart = false;
- }
- if (oldIndent != indent)
- {
- QString fromLine;
- // Search if the last non-blank line could be "From" line
- if (part.count() && (oldIndent.length() < indent.length()))
- {
- QStringList::Iterator it2 = part.isEmpty() ? part.end() : --part.end();
- // FIXME: what if all strings are empty? Then we'll decrement part.begin().
- // Shouldn't we also check for .begin()?
- while( (it2 != part.end()) && (*it2).isEmpty())
- --it2;
- if ((it2 != part.end()) && ((*it2).endsWith(':')))
- {
- fromLine = oldIndent + (*it2) + '\n';
- part.erase(it2);
- }
- }
- if (flushPart( result, part, oldIndent, maxLineLength))
- {
- if (oldIndent.length() > indent.length())
- result += indent + '\n';
- else
- result += oldIndent + '\n';
- }
- if (!fromLine.isEmpty())
- {
- result += fromLine;
- }
- oldIndent = indent;
- }
- part.append(line);
- }
- flushPart( result, part, oldIndent, maxLineLength);
- return result;
- }
- #ifndef KMAIL_UNITTESTS
- QMap<QString, QString> parseMailtoUrl ( const KUrl& url )
- {
- kDebug() << url.pathOrUrl();
- QMap<QString, QString> values = url.queryItems( KUrl::CaseInsensitiveKeys );
- QString to = decodeMailtoUrl( url.path() );
- to = to.isEmpty() ? values.value( "to" ) : to + QString( ", " ) + values.value( "to" );
- values.insert( "to", to );
- return values;
- }
- #endif
- }
- }