--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/messaging/win32wce/qmailaddress.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,1094 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmailaddress.h"
+#include "qmaillog.h"
+#include "qmailmessage.h"
+#include "qmailnamespace.h"
+
+namespace {
+
+struct CharacterProcessor
+{
+ virtual ~CharacterProcessor();
+
+ void processCharacters(const QString& input);
+ virtual void process(QChar, bool, bool, int) = 0;
+
+ virtual void finished();
+};
+
+CharacterProcessor::~CharacterProcessor()
+{
+}
+
+void CharacterProcessor::processCharacters(const QString& input)
+{
+ int commentDepth = 0;
+ bool quoted = false;
+ bool escaped = false;
+
+ const QChar* it = input.constData();
+ const QChar* const end = it + input.length();
+ for ( ; it != end; ++it ) {
+ if ( !escaped && ( *it == '\\' ) ) {
+ escaped = true;
+ continue;
+ }
+
+ bool quoteProcessed = false;
+ if ( *it == '(' && !escaped && !quoted ) {
+ commentDepth += 1;
+ }
+ else if ( !quoted && *it == '"' && !escaped ) {
+ quoted = true;
+ quoteProcessed = true;
+ }
+
+ process((*it), quoted, escaped, commentDepth);
+
+ if ( *it == ')' && !escaped && !quoted && ( commentDepth > 0 ) ) {
+ commentDepth -= 1;
+ }
+ else if ( quoted && *it == '"' && !quoteProcessed && !escaped ) {
+ quoted = false;
+ }
+
+ escaped = false;
+ }
+
+ finished();
+}
+
+void CharacterProcessor::finished()
+{
+}
+
+struct Decommentor : public CharacterProcessor
+{
+ Decommentor(bool (QChar::*classifier)() const, bool accepted);
+
+ QString _result;
+ bool (QChar::*_classifier)() const;
+ bool _accepted;
+
+ virtual void process(QChar, bool, bool, int);
+};
+
+Decommentor::Decommentor(bool (QChar::*classifier)() const, bool accepted)
+ : _classifier(classifier),
+ _accepted(accepted)
+{
+}
+
+void Decommentor::process(QChar character, bool quoted, bool escaped, int commentDepth)
+{
+ if ( commentDepth == 0 ) {
+ if ( quoted || ((character.*_classifier)() == _accepted) )
+ _result.append( character );
+ }
+
+ Q_UNUSED(escaped)
+}
+
+static QString removeComments(const QString& input, bool (QChar::*classifier)() const, bool acceptedResult = true)
+{
+ Decommentor decommentor(classifier, acceptedResult);
+ decommentor.processCharacters(input);
+ return decommentor._result;
+}
+
+
+struct AddressSeparator : public CharacterProcessor
+{
+ enum TokenType { Unknown = 0, Address, Name, Suffix, Comment, Group, TypeCount };
+
+ AddressSeparator();
+
+ virtual void process(QChar, bool, bool, int);
+ virtual void finished();
+
+ virtual void accept(QChar) = 0;
+ virtual QString progress() const = 0;
+ virtual void complete(TokenType type, bool) = 0;
+
+private:
+ void separator(bool);
+
+ bool _inAddress;
+ bool _inGroup;
+ bool _tokenStarted;
+ bool _tokenCompleted;
+ TokenType _type;
+};
+
+AddressSeparator::AddressSeparator()
+ : _inAddress(false),
+ _inGroup(false),
+ _tokenStarted(false),
+ _tokenCompleted(false),
+ _type(Unknown)
+{
+}
+
+void AddressSeparator::process(QChar character, bool quoted, bool escaped, int commentDepth)
+{
+ if (_tokenCompleted && (!character.isSpace())) {
+ separator(false);
+ }
+
+ // RFC 2822 requires comma as the separator, but we'll allow the semi-colon as well.
+ if ( ( character == ',' || character == ';' || character.isSpace()) &&
+ !_inGroup && !quoted && !escaped && commentDepth == 0 ) {
+ if (character.isSpace()) {
+ // We'll also attempt to separate on whitespace, but we need to append it to
+ // the token to preserve the input data
+ accept(character);
+ _tokenCompleted = true;
+ } else {
+ separator(true);
+ }
+ }
+ else {
+ if (commentDepth && _type == Unknown && _tokenStarted == false) {
+ // This could be a purely comment element
+ _type = Comment;
+ }
+ else if (quoted && (_type == Unknown || _type == Comment)) {
+ // This must be a name element
+ _type = Name;
+ }
+
+ accept(character);
+ _tokenStarted = true;
+
+ if ( character == '<' && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
+ _inAddress = true;
+
+ if (_type == Unknown || _type == Comment)
+ _type = Address;
+ } else if ( character == '>' && _inAddress && !quoted && !escaped && commentDepth == 0 ) {
+ _inAddress = false;
+ }
+ else if ( character == ':' && !_inGroup && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
+ static const QString collectiveTag;
+
+ // Don't parse as a group if we match the IM format
+ // TODO: what if the group name actually matches the tag?
+ if (progress() != collectiveTag) {
+ _inGroup = true;
+ _type = Group;
+ }
+ }
+ else if ( character == ';' && _inGroup && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
+ _inGroup = false;
+
+ // This is a soft separator, because the group construct could have a trailing comment
+ separator(false);
+ }
+ }
+}
+
+void AddressSeparator::separator(bool hardSeparator)
+{
+ complete(_type, hardSeparator);
+
+ _tokenStarted = false;
+ _tokenCompleted = false;
+ _type = Unknown;
+}
+
+void AddressSeparator::finished()
+{
+ complete(_type, true);
+}
+
+
+struct AddressListGenerator : public AddressSeparator
+{
+ virtual void accept(QChar);
+ virtual QString progress() const;
+ virtual void complete(TokenType, bool);
+
+ QStringList result();
+
+private:
+ typedef QPair<TokenType, QString> Token;
+
+ int combinableElements();
+ void processPending();
+
+ QStringList _result;
+ QList<Token> _pending;
+ QString _partial;
+};
+
+void AddressListGenerator::accept(QChar character)
+{
+ _partial.append(character);
+}
+
+QString AddressListGenerator::progress() const
+{
+ return _partial;
+}
+
+void AddressListGenerator::complete(TokenType type, bool hardSeparator)
+{
+ if (_partial.trimmed().length()) {
+ if (type == Unknown) {
+ // We need to know what type of token this is
+
+ // Test whether the token is a suffix
+ QRegExp suffixPattern("\\s*/TYPE=.*");
+ if (suffixPattern.exactMatch(_partial)) {
+ type = Suffix;
+ }
+ else {
+ // See if the token is a bare email address; otherwise it must be a name element
+ QRegExp emailPattern(QMailAddress::emailAddressPattern());
+ type = (emailPattern.exactMatch(_partial.trimmed()) ? Address : Name);
+ }
+ }
+
+ _pending.append(qMakePair(type, _partial));
+ _partial.clear();
+ }
+
+ if (hardSeparator) {
+ // We know that this is a boundary between addresses
+ processPending();
+ }
+}
+
+int AddressListGenerator::combinableElements()
+{
+ bool used[TypeCount] = { false };
+
+ int combinable = 0;
+ int i = _pending.count();
+ while (i > 0) {
+ int type = _pending.value(i - 1).first;
+
+ // If this type has already appeared in this address, this must be part of the preceding address
+ if (used[type])
+ return combinable;
+
+ // A suffix can only appear at the end of an address
+ if (type == Suffix && (combinable > 0))
+ return combinable;
+
+ if (type == Comment) {
+ // Comments can be combined with anything else...
+ } else {
+ // Everything else can be used once at most, and not with groups
+ if (used[Group])
+ return combinable;
+
+ if (type == Group) {
+ // Groups can only be combined with comments
+ if (used[Name] || used[Address] || used[Suffix])
+ return combinable;
+ }
+
+ used[type] = true;
+ }
+
+ // Combine this element
+ ++combinable;
+ --i;
+ }
+
+ return combinable;
+}
+
+void AddressListGenerator::processPending()
+{
+ if (!_pending.isEmpty()) {
+ // Compress any consecutive name parts into a single part
+ for (int i = 1; i < _pending.count(); ) {
+ TokenType type = _pending.value(i).first;
+ // Also, a name could precede a group part, since the group name may contain multiple atoms
+ if ((_pending.value(i - 1).first == Name) && ((type == Name) || (type == Group))) {
+ _pending.replace(i - 1, qMakePair(type, _pending.value(i - 1).second + _pending.value(i).second));
+ _pending.removeAt(i);
+ }
+ else {
+ ++i;
+ }
+ }
+
+ // Combine the tokens as necessary, proceding in reverse from the known boundary at the end
+ QStringList addresses;
+ int combinable = 0;
+ while ((combinable = combinableElements()) != 0) {
+ QString combined;
+ while (combinable) {
+ combined.prepend(_pending.last().second);
+ _pending.removeLast();
+ --combinable;
+ }
+ addresses.append(combined);
+ }
+
+ // Add the address to the result set, in the original order
+ for (int i = addresses.count(); i > 0; --i)
+ _result.append(addresses.value(i - 1));
+
+ _pending.clear();
+ }
+}
+
+QStringList AddressListGenerator::result()
+{
+ return _result;
+}
+
+static QStringList generateAddressList(const QString& list)
+{
+ AddressListGenerator generator;
+ generator.processCharacters(list);
+ return generator.result();
+}
+
+static bool containsMultipleFields(const QString& input)
+{
+ // There is no shortcut; we have to parse the addresses
+ AddressListGenerator generator;
+ generator.processCharacters(input);
+ return (generator.result().count() > 1);
+}
+
+
+struct GroupDetector : public CharacterProcessor
+{
+ GroupDetector();
+
+ virtual void process(QChar, bool, bool, int);
+
+ bool result() const;
+
+private:
+ bool _nameDelimiter;
+ bool _listTerminator;
+};
+
+GroupDetector::GroupDetector()
+ : _nameDelimiter(false),
+ _listTerminator(false)
+{
+}
+
+void GroupDetector::process(QChar character, bool quoted, bool escaped, int commentDepth)
+{
+ if ( character == ':' && !_nameDelimiter && !quoted && !escaped && commentDepth == 0 )
+ _nameDelimiter = true;
+ else if ( character == ';' && !_listTerminator && _nameDelimiter && !quoted && !escaped && commentDepth == 0 )
+ _listTerminator = true;
+}
+
+bool GroupDetector::result() const
+{
+ return _listTerminator;
+}
+
+static bool containsGroupSpecifier(const QString& input)
+{
+ GroupDetector detector;
+ detector.processCharacters(input);
+ return detector.result();
+}
+
+
+struct WhitespaceRemover : public CharacterProcessor
+{
+ virtual void process(QChar, bool, bool, int);
+
+ QString _result;
+};
+
+void WhitespaceRemover::process(QChar character, bool quoted, bool escaped, int commentDepth)
+{
+ if ( !character.isSpace() || quoted || escaped || commentDepth > 0 )
+ _result.append(character);
+}
+
+static QString removeWhitespace(const QString& input)
+{
+ WhitespaceRemover remover;
+ remover.processCharacters(input);
+ return remover._result;
+}
+
+QPair<int, int> findDelimiters(const QString& text)
+{
+ int first = -1;
+ int second = -1;
+
+ bool quoted = false;
+ bool escaped = false;
+
+ const QChar* const begin = text.constData();
+ const QChar* const end = begin + text.length();
+ for (const QChar* it = begin; it != end; ++it ) {
+ if ( !escaped && ( *it == '\\' ) ) {
+ escaped = true;
+ continue;
+ }
+
+ if ( !quoted && *it == '"' && !escaped ) {
+ quoted = true;
+ }
+ else if ( quoted && *it == '"' && !escaped ) {
+ quoted = false;
+ }
+
+ if ( !quoted ) {
+ if ( first == -1 && *it == '<' ) {
+ first = (it - begin);
+ }
+ else if ( second == -1 && *it == '>' ) {
+ second = (it - begin);
+ break;
+ }
+ }
+
+ escaped = false;
+ }
+
+ return qMakePair(first, second);
+}
+
+void parseMailbox(QString& input, QString& name, QString& address, QString& suffix)
+{
+ // See if there is a trailing suffix
+ int pos = input.indexOf("/TYPE=");
+ if (pos != -1)
+ {
+ suffix = input.mid(pos + 6);
+ input = input.left(pos);
+ }
+
+ // Separate the email address from the name
+ QPair<int, int> delimiters = findDelimiters(input);
+
+ if (delimiters.first == -1 && delimiters.second == -1)
+ {
+ name = address = input.trimmed();
+ }
+ else
+ {
+ if (delimiters.first == -1)
+ {
+ // Unmatched '>'
+ address = input.left( delimiters.second );
+ }
+ else
+ {
+ name = input.left( delimiters.first );
+
+ if (delimiters.second == -1)
+ address = input.right(input.length() - delimiters.first - 1);
+ else
+ address = input.mid(delimiters.first + 1, (delimiters.second - delimiters.first - 1)).trimmed();
+ }
+
+ if ( name.isEmpty() )
+ name = address;
+ }
+}
+
+}
+
+
+/* QMailAddress */
+class QMailAddressPrivate : public QSharedData
+{
+public:
+ QMailAddressPrivate();
+ QMailAddressPrivate(const QString& addressText);
+ QMailAddressPrivate(const QString& name, const QString& address);
+ QMailAddressPrivate(const QMailAddressPrivate& other);
+ ~QMailAddressPrivate();
+
+ bool isNull() const;
+
+ bool isGroup() const;
+ QList<QMailAddress> groupMembers() const;
+
+ QString name() const;
+ bool isPhoneNumber() const;
+ bool isEmailAddress() const;
+
+ QString minimalPhoneNumber() const;
+
+ QString toString(bool forceDelimited) const;
+
+ QString _name;
+ QString _address;
+ QString _suffix;
+ bool _group;
+
+ bool operator==(const QMailAddressPrivate& other) const;
+
+ template <typename Stream>
+ void serialize(Stream &stream) const;
+
+ template <typename Stream>
+ void deserialize(Stream &stream);
+
+private:
+ void setComponents(const QString& nameText, const QString& addressText);
+
+ mutable bool _searchCompleted;
+};
+
+QMailAddressPrivate::QMailAddressPrivate()
+ : _group(false),
+ _searchCompleted(false)
+{
+}
+
+QMailAddressPrivate::QMailAddressPrivate(const QString& addressText)
+ : _group(false),
+ _searchCompleted(false)
+{
+ if (!addressText.isEmpty())
+ {
+ QString input = addressText.trimmed();
+
+ // See whether this address is a group
+ if (containsGroupSpecifier(input))
+ {
+ QRegExp groupFormat("(.*):(.*);");
+ if (groupFormat.indexIn(input) != -1)
+ {
+ _name = groupFormat.cap(1).trimmed();
+ _address = groupFormat.cap(2).trimmed();
+ _group = true;
+ }
+ }
+ else
+ {
+ parseMailbox(input, _name, _address, _suffix);
+ setComponents(_name, _address);
+ }
+ }
+}
+
+QMailAddressPrivate::QMailAddressPrivate(const QString& name, const QString& address)
+ : _group(false),
+ _searchCompleted(false)
+{
+ // See whether the address part contains a group
+ if (containsMultipleFields(address))
+ {
+ _name = name;
+ _address = address;
+ _group = true;
+ }
+ else
+ {
+ setComponents(name, address);
+ }
+}
+
+void QMailAddressPrivate::setComponents(const QString& nameText, const QString& addressText )
+{
+ _name = nameText.trimmed();
+ _address = addressText.trimmed();
+
+ int charIndex = _address.indexOf( "/TYPE=" );
+ if ( charIndex != -1 ) {
+ _suffix = _address.mid( charIndex + 6 );
+ _address = _address.left( charIndex ).trimmed();
+ }
+
+ if ( ( charIndex = _address.indexOf( '<' ) ) != -1 )
+ _address.remove( charIndex, 1 );
+ if ( ( charIndex = _address.lastIndexOf( '>' ) ) != -1 )
+ _address.remove( charIndex, 1 );
+}
+
+QMailAddressPrivate::QMailAddressPrivate(const QMailAddressPrivate& other)
+ : QSharedData(other),
+ _searchCompleted(false)
+{
+ _name = other._name;
+ _address = other._address;
+ _suffix = other._suffix;
+ _group = other._group;
+}
+
+QMailAddressPrivate::~QMailAddressPrivate()
+{
+}
+
+bool QMailAddressPrivate::operator==(const QMailAddressPrivate& other) const
+{
+ return (_name == other._name && _address == other._address && _suffix == other._suffix && _group == other._group);
+}
+
+bool QMailAddressPrivate::isNull() const
+{
+ return (_name.isNull() && _address.isNull() && _suffix.isNull());
+}
+
+bool QMailAddressPrivate::isGroup() const
+{
+ return _group;
+}
+
+QList<QMailAddress> QMailAddressPrivate::groupMembers() const
+{
+ if (_group)
+ return QMailAddress::fromStringList(_address);
+
+ return QList<QMailAddress>();
+}
+
+// We need to keep a default copy of this, because constructing a new
+// one is expensive and sends multiple QCOP messages, whose responses it
+// will not survive to receive...
+
+QString QMailAddressPrivate::name() const
+{
+ return QMail::unquoteString(_name);
+}
+
+bool QMailAddressPrivate::isPhoneNumber() const
+{
+ static const QRegExp pattern(QMailAddress::phoneNumberPattern());
+ return pattern.exactMatch(_address);
+}
+
+bool QMailAddressPrivate::isEmailAddress() const
+{
+ static const QRegExp pattern(QMailAddress::emailAddressPattern());
+ return pattern.exactMatch(QMailAddress::removeWhitespace(QMailAddress::removeComments(_address)));
+}
+
+QString QMailAddressPrivate::minimalPhoneNumber() const
+{
+ static const QRegExp nondiallingChars("[^\\d,xpwXPW\\+\\*#]");
+
+ // Remove any characters which don't affect dialling
+ QString minimal(_address);
+ minimal.remove(nondiallingChars);
+
+ // Convert any 'p' or 'x' to comma
+ minimal.replace(QRegExp("[xpXP]"), ",");
+
+ // Ensure any permitted alphabetical chars are lower-case
+ return minimal.toLower();
+}
+
+static bool needsQuotes(const QString& src)
+{
+ static const QRegExp specials = QRegExp("[<>\\[\\]:;@\\\\,.]");
+
+ QString characters(src);
+
+ // Remove any quoted-pair characters, since they don't require quoting
+ int index = 0;
+ while ((index = characters.indexOf('\\', index)) != -1)
+ characters.remove(index, 2);
+
+ if ( specials.indexIn( characters ) != -1 )
+ return true;
+
+ // '(' and ')' also need quoting, if they don't conform to nested comments
+ const QChar* it = characters.constData();
+ const QChar* const end = it + characters.length();
+
+ int commentDepth = 0;
+ for (; it != end; ++it)
+ if (*it == '(') {
+ ++commentDepth;
+ }
+ else if (*it == ')') {
+ if (--commentDepth < 0)
+ return true;
+ }
+
+ return (commentDepth != 0);
+}
+
+QString QMailAddressPrivate::toString(bool forceDelimited) const
+{
+ QString result;
+
+ if ( _name == _address )
+ return _name;
+
+ if ( _group ) {
+ result.append( _name ).append( ": " ).append( _address ).append( ';' );
+ } else {
+ // If there are any 'special' characters in the name it needs to be quoted
+ if ( !_name.isEmpty() )
+ result = ( needsQuotes( _name ) ? QMail::quoteString( _name ) : _name );
+
+ if ( !_address.isEmpty() ) {
+ if ( !forceDelimited && result.isEmpty() ) {
+ result = _address;
+ } else {
+ if ( !result.isEmpty() )
+ result.append( ' ' );
+ result.append( '<' ).append( _address ).append( '>' );
+ }
+ }
+
+ if ( !_suffix.isEmpty() )
+ result.append( " /TYPE=" ).append( _suffix );
+ }
+
+ return result;
+}
+
+template <typename Stream>
+void QMailAddressPrivate::serialize(Stream &stream) const
+{
+ stream << _name << _address << _suffix << _group;
+}
+
+template <typename Stream>
+void QMailAddressPrivate::deserialize(Stream &stream)
+{
+ _searchCompleted = false;
+ stream >> _name >> _address >> _suffix >> _group;
+}
+
+
+/*!
+ \class QMailAddress
+
+ \brief The QMailAddress class provides an interface for manipulating message address strings.
+ \ingroup messaginglibrary
+
+ QMailAddress provides functions for splitting the address strings of messages into name and
+ address components, and for combining the individual components into correctly formatted
+ address strings. QMailAddress can be used to manipulate the address elements exposed by the
+ QMailMessage class.
+
+ Address strings are expected to use the format "name_part '<'address_part'>'", where
+ \i name_part describes a message sender or recipient and \i address_part defines the address
+ at which they can be contacted. The address component is not validated, so it can contain an
+ email address, phone number, or any other type of textual address representation.
+
+ \sa QMailMessage
+*/
+
+/*!
+ Constructs an empty QMailAddress object.
+*/
+QMailAddress::QMailAddress()
+{
+ d = new QMailAddressPrivate();
+}
+
+/*!
+ Constructs a QMailAddress object, extracting the name and address components from \a addressText.
+
+ If \a addressText cannot be separated into name and address components, both name() and address()
+ will return the entirety of \a addressText.
+
+ \sa name(), address()
+*/
+QMailAddress::QMailAddress(const QString& addressText)
+{
+ d = new QMailAddressPrivate(addressText);
+}
+
+/*!
+ Constructs a QMailAddress object with the given \a name and \a address.
+*/
+QMailAddress::QMailAddress(const QString& name, const QString& address)
+{
+ d = new QMailAddressPrivate(name, address);
+}
+
+/*! \internal */
+QMailAddress::QMailAddress(const QMailAddress& other)
+{
+ this->operator=(other);
+}
+
+/*!
+ Destroys a QMailAddress object.
+*/
+QMailAddress::~QMailAddress()
+{
+}
+
+/*! \internal */
+const QMailAddress& QMailAddress::operator= (const QMailAddress& other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*! \internal */
+bool QMailAddress::operator== (const QMailAddress& other) const
+{
+ return d->operator==(*other.d);
+}
+
+/*! \internal */
+bool QMailAddress::operator!= (const QMailAddress& other) const
+{
+ return !(d->operator==(*other.d));
+}
+
+/*!
+ Returns true if the address object has not been initialized.
+*/
+bool QMailAddress::isNull() const
+{
+ return d->isNull();
+}
+
+/*!
+ Returns the name component of a mail address string.
+*/
+QString QMailAddress::name() const
+{
+ return d->name();
+}
+
+/*!
+ Returns the address component of a mail address string.
+*/
+QString QMailAddress::address() const
+{
+ return d->_address;
+}
+
+/*!
+ Returns true if the address is that of a group.
+*/
+bool QMailAddress::isGroup() const
+{
+ return d->isGroup();
+}
+
+/*!
+ Returns a list containing the individual addresses that comprise the address group.
+ If the address is not a group address, an empty list is returned.
+
+ \sa isGroup()
+*/
+QList<QMailAddress> QMailAddress::groupMembers() const
+{
+ return d->groupMembers();
+}
+
+/*!
+ Returns true if the address component has the form of a phone number; otherwise returns false.
+
+ \sa isEmailAddress()
+*/
+bool QMailAddress::isPhoneNumber() const
+{
+ return d->isPhoneNumber();
+}
+
+/*!
+ Returns true if the address component has the form of an email address; otherwise returns false.
+
+ \sa isPhoneNumber()
+*/
+bool QMailAddress::isEmailAddress() const
+{
+ return d->isEmailAddress();
+}
+
+/*! \internal */
+QString QMailAddress::minimalPhoneNumber() const
+{
+ return d->minimalPhoneNumber();
+}
+
+/*!
+ Returns a string containing the name and address in a standardised format.
+ If \a forceDelimited is true, the address element will be delimited by '<' and '>', even if this is unnecessary.
+*/
+QString QMailAddress::toString(bool forceDelimited) const
+{
+ return d->toString(forceDelimited);
+}
+
+/*!
+ Returns a string list containing the result of calling toString() on each address in \a list.
+ If \a forceDelimited is true, the address elements will be delimited by '<' and '>', even if this is unnecessary.
+
+ \sa toString()
+*/
+QStringList QMailAddress::toStringList(const QList<QMailAddress>& list, bool forceDelimited)
+{
+ QStringList result;
+
+ foreach (const QMailAddress& address, list)
+ result.append(address.toString(forceDelimited));
+
+ return result;
+}
+
+/*!
+ Returns a list containing a QMailAddress object constructed from each
+ comma-separated address in \a list.
+*/
+QList<QMailAddress> QMailAddress::fromStringList(const QString& list)
+{
+ return fromStringList(generateAddressList(list));
+}
+
+/*!
+ Returns a list containing a QMailAddress object constructed from each
+ address string in \a list.
+*/
+QList<QMailAddress> QMailAddress::fromStringList(const QStringList& list)
+{
+ QList<QMailAddress> result;
+
+ foreach (const QString& address, list)
+ result.append(QMailAddress(address));
+
+ return result;
+}
+
+/*!
+ Returns the content of \a input with any comment sections removed.
+ Any subsequent leading or trailing whitespace is then also removed.
+*/
+QString QMailAddress::removeComments(const QString& input)
+{
+ return ::removeComments(input, &QChar::isPrint).trimmed();
+}
+
+/*!
+ Returns the content of \a input with any whitespace characters removed.
+ Whitespace within quotes or comment sections is preserved.
+*/
+QString QMailAddress::removeWhitespace(const QString& input)
+{
+ return ::removeWhitespace(input);
+}
+
+/*! \internal */
+QString QMailAddress::phoneNumberPattern()
+{
+ static const QString pattern("\"?" // zero-or-one:'"'
+ "(" // start capture
+ "(?:\\+ ?)?" // zero-or-one:('+', zero-or-one:space)
+ "(?:\\(\\d+\\)[ -]?)?" // zero-or-one:'(', one-or-more:digits, ')', zero-or-one:separator
+ "(?:\\d{1,14})" // one:(one-to-fourteen):digits
+ "(?:[ -]?[\\d#\\*]{1,10}){0,4}" // zero-to-four:(zero-or-one:separator), one-to-ten:(digits | '#' | '*')
+ "(?:" // zero-or-one:
+ "[ -]?" // zero-or-one:separator,
+ "\\(?" // zero-or-one:'('
+ "[,xpwXPW]\\d{1,4}" // one:extension, one-to-four:digits
+ "\\)?" // zero-or-one:')'
+ ")?" // end of optional group
+ ")" // end capture
+ "\"?"); // zero-or-one:'"'
+
+ return pattern;
+}
+
+/*! \internal */
+QString QMailAddress::emailAddressPattern()
+{
+ // Taken from: http://www.regular-expressions.info/email.html, but
+ // modified to accept uppercase characters as well as lower-case
+ // Also - RFC 1034 seems to prohibit domain name elements beginning
+ // with digits, but they exist in practise...
+ static const QString pattern("[A-Za-z\\d!#$%&'*+/=?^_`{|}~-]+" // one-or-more: legal chars (some punctuation permissible)
+ "(?:" // zero-or-more:
+ "\\." // '.',
+ "[A-Za-z\\d!#$%&'*+/=?^_`{|}~-]+" // one-or-more: legal chars
+ ")*" // end of optional group
+ "@" // '@'
+ "(?:" // either:
+ "localhost" // 'localhost'
+ "|" // or:
+ "(?:" // one-or-more:
+ "[A-Za-z\\d]" // one: legal char,
+ "(?:" // zero-or-one:
+ "[A-Za-z\\d-]*[A-Za-z\\d]" // (zero-or-more: (legal char or '-'), one: legal char)
+ ")?" // end of optional group
+ "\\." // '.'
+ ")+" // end of mandatory group
+ "[A-Za-z\\d]" // one: legal char
+ "(?:" // zero-or-one:
+ "[A-Za-z\\d-]*[A-Za-z\\d]" // (zero-or-more: (legal char or '-'), one: legal char)
+ ")?" // end of optional group
+ ")"); // end of alternation
+
+ return pattern;
+}
+
+/*!
+ \fn QMailAddress::serialize(Stream&) const
+ \internal
+*/
+template <typename Stream>
+void QMailAddress::serialize(Stream &stream) const
+{
+ d->serialize(stream);
+}
+
+/*!
+ \fn QMailAddress::deserialize(Stream&)
+ \internal
+*/
+template <typename Stream>
+void QMailAddress::deserialize(Stream &stream)
+{
+ d->deserialize(stream);
+}
+
+Q_IMPLEMENT_USER_METATYPE(QMailAddress)
+
+Q_IMPLEMENT_USER_METATYPE_TYPEDEF(QMailAddressList, QMailAddressList)
+
+
+//Q_IMPLEMENT_USER_METATYPE_NO_OPERATORS(QList<QMailAddress>)
+