src/messaging/win32wce/qmailaddress.cpp
changeset 0 876b1a06bc25
equal deleted inserted replaced
-1:000000000000 0:876b1a06bc25
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qmailaddress.h"
       
    43 #include "qmaillog.h"
       
    44 #include "qmailmessage.h"
       
    45 #include "qmailnamespace.h"
       
    46 
       
    47 namespace {
       
    48 
       
    49 struct CharacterProcessor
       
    50 {
       
    51     virtual ~CharacterProcessor();
       
    52 
       
    53     void processCharacters(const QString& input);
       
    54     virtual void process(QChar, bool, bool, int) = 0;
       
    55 
       
    56     virtual void finished();
       
    57 };
       
    58 
       
    59 CharacterProcessor::~CharacterProcessor()
       
    60 {
       
    61 }
       
    62 
       
    63 void CharacterProcessor::processCharacters(const QString& input)
       
    64 {
       
    65     int commentDepth = 0;
       
    66     bool quoted = false;
       
    67     bool escaped = false;
       
    68 
       
    69     const QChar* it = input.constData();
       
    70     const QChar* const end = it + input.length();
       
    71     for ( ; it != end; ++it ) {
       
    72         if ( !escaped && ( *it == '\\' ) ) {
       
    73             escaped = true;
       
    74             continue;
       
    75         }
       
    76 
       
    77         bool quoteProcessed = false;
       
    78         if ( *it == '(' && !escaped && !quoted ) {
       
    79             commentDepth += 1;
       
    80         }
       
    81         else if ( !quoted && *it == '"' && !escaped ) {
       
    82             quoted = true;
       
    83             quoteProcessed = true;
       
    84         }
       
    85 
       
    86         process((*it), quoted, escaped, commentDepth);
       
    87 
       
    88         if ( *it == ')' && !escaped && !quoted && ( commentDepth > 0 ) ) {
       
    89             commentDepth -= 1;
       
    90         }
       
    91         else if ( quoted && *it == '"' && !quoteProcessed && !escaped ) {
       
    92             quoted = false;
       
    93         }
       
    94 
       
    95         escaped = false;
       
    96     }
       
    97 
       
    98     finished();
       
    99 }
       
   100 
       
   101 void CharacterProcessor::finished()
       
   102 {
       
   103 }
       
   104 
       
   105 struct Decommentor : public CharacterProcessor
       
   106 {
       
   107     Decommentor(bool (QChar::*classifier)() const, bool accepted);
       
   108 
       
   109     QString _result;
       
   110     bool (QChar::*_classifier)() const;
       
   111     bool _accepted;
       
   112 
       
   113     virtual void process(QChar, bool, bool, int);
       
   114 };
       
   115 
       
   116 Decommentor::Decommentor(bool (QChar::*classifier)() const, bool accepted)
       
   117     : _classifier(classifier),
       
   118       _accepted(accepted)
       
   119 {
       
   120 }
       
   121 
       
   122 void Decommentor::process(QChar character, bool quoted, bool escaped, int commentDepth)
       
   123 {
       
   124     if ( commentDepth == 0 ) {
       
   125         if ( quoted || ((character.*_classifier)() == _accepted) )
       
   126             _result.append( character );
       
   127     }
       
   128 
       
   129     Q_UNUSED(escaped)
       
   130 }
       
   131 
       
   132 static QString removeComments(const QString& input, bool (QChar::*classifier)() const, bool acceptedResult = true)
       
   133 {
       
   134     Decommentor decommentor(classifier, acceptedResult);
       
   135     decommentor.processCharacters(input);
       
   136     return decommentor._result;
       
   137 }
       
   138 
       
   139 
       
   140 struct AddressSeparator : public CharacterProcessor
       
   141 {
       
   142     enum TokenType { Unknown = 0, Address, Name, Suffix, Comment, Group, TypeCount };
       
   143 
       
   144     AddressSeparator();
       
   145 
       
   146     virtual void process(QChar, bool, bool, int);
       
   147     virtual void finished();
       
   148 
       
   149     virtual void accept(QChar) = 0;
       
   150     virtual QString progress() const = 0;
       
   151     virtual void complete(TokenType type, bool) = 0;
       
   152 
       
   153 private:
       
   154     void separator(bool);
       
   155 
       
   156     bool _inAddress;
       
   157     bool _inGroup;
       
   158     bool _tokenStarted;
       
   159     bool _tokenCompleted;
       
   160     TokenType _type;
       
   161 };
       
   162 
       
   163 AddressSeparator::AddressSeparator()
       
   164     : _inAddress(false),
       
   165       _inGroup(false),
       
   166       _tokenStarted(false),
       
   167       _tokenCompleted(false),
       
   168       _type(Unknown)
       
   169 {
       
   170 }
       
   171 
       
   172 void AddressSeparator::process(QChar character, bool quoted, bool escaped, int commentDepth)
       
   173 {
       
   174     if (_tokenCompleted && (!character.isSpace())) {
       
   175         separator(false);
       
   176     }
       
   177 
       
   178     // RFC 2822 requires comma as the separator, but we'll allow the semi-colon as well.
       
   179     if ( ( character == ',' || character == ';' || character.isSpace()) && 
       
   180          !_inGroup && !quoted && !escaped && commentDepth == 0 ) {
       
   181         if (character.isSpace()) {
       
   182             // We'll also attempt to separate on whitespace, but we need to append it to 
       
   183             // the token to preserve the input data
       
   184             accept(character);
       
   185             _tokenCompleted = true;
       
   186         } else {
       
   187             separator(true);
       
   188         }
       
   189     }
       
   190     else {
       
   191         if (commentDepth && _type == Unknown && _tokenStarted == false) {
       
   192             // This could be a purely comment element
       
   193             _type = Comment;
       
   194         }
       
   195         else if (quoted && (_type == Unknown || _type == Comment)) {
       
   196             // This must be a name element
       
   197             _type = Name;
       
   198         }
       
   199 
       
   200         accept(character);
       
   201         _tokenStarted = true;
       
   202 
       
   203         if ( character == '<' && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
       
   204             _inAddress = true;
       
   205 
       
   206             if (_type == Unknown || _type == Comment)
       
   207                 _type = Address;
       
   208         } else if ( character == '>' && _inAddress && !quoted && !escaped && commentDepth == 0 ) {
       
   209             _inAddress = false;
       
   210         }
       
   211         else if ( character == ':' && !_inGroup && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
       
   212             static const QString collectiveTag;
       
   213 
       
   214             // Don't parse as a group if we match the IM format
       
   215             // TODO: what if the group name actually matches the tag?
       
   216             if (progress() != collectiveTag) {
       
   217                 _inGroup = true;
       
   218                 _type = Group;
       
   219             }
       
   220         }
       
   221         else if ( character == ';' && _inGroup && !_inAddress && !quoted && !escaped && commentDepth == 0 ) {
       
   222             _inGroup = false;
       
   223 
       
   224             // This is a soft separator, because the group construct could have a trailing comment
       
   225             separator(false);
       
   226         }
       
   227     }
       
   228 }
       
   229 
       
   230 void AddressSeparator::separator(bool hardSeparator)
       
   231 {
       
   232     complete(_type, hardSeparator);
       
   233 
       
   234     _tokenStarted = false;
       
   235     _tokenCompleted = false;
       
   236     _type = Unknown;
       
   237 }
       
   238 
       
   239 void AddressSeparator::finished()
       
   240 {
       
   241     complete(_type, true);
       
   242 }
       
   243 
       
   244 
       
   245 struct AddressListGenerator : public AddressSeparator
       
   246 {
       
   247     virtual void accept(QChar);
       
   248     virtual QString progress() const;
       
   249     virtual void complete(TokenType, bool);
       
   250 
       
   251     QStringList result();
       
   252 
       
   253 private:
       
   254     typedef QPair<TokenType, QString> Token;
       
   255 
       
   256     int combinableElements();
       
   257     void processPending();
       
   258 
       
   259     QStringList _result;
       
   260     QList<Token> _pending;
       
   261     QString _partial;
       
   262 };
       
   263 
       
   264 void AddressListGenerator::accept(QChar character)
       
   265 {
       
   266     _partial.append(character);
       
   267 }
       
   268 
       
   269 QString AddressListGenerator::progress() const
       
   270 {
       
   271     return _partial;
       
   272 }
       
   273 
       
   274 void AddressListGenerator::complete(TokenType type, bool hardSeparator)
       
   275 {
       
   276     if (_partial.trimmed().length()) {
       
   277         if (type == Unknown) {
       
   278             // We need to know what type of token this is
       
   279 
       
   280             // Test whether the token is a suffix
       
   281             QRegExp suffixPattern("\\s*/TYPE=.*");
       
   282             if (suffixPattern.exactMatch(_partial)) {
       
   283                 type = Suffix;
       
   284             } 
       
   285             else {
       
   286                 // See if the token is a bare email address; otherwise it must be a name element
       
   287                 QRegExp emailPattern(QMailAddress::emailAddressPattern());
       
   288                 type = (emailPattern.exactMatch(_partial.trimmed()) ? Address : Name);
       
   289             }
       
   290         }
       
   291 
       
   292         _pending.append(qMakePair(type, _partial));
       
   293         _partial.clear();
       
   294     }
       
   295 
       
   296     if (hardSeparator) {
       
   297         // We know that this is a boundary between addresses
       
   298         processPending();
       
   299     }
       
   300 }
       
   301 
       
   302 int AddressListGenerator::combinableElements()
       
   303 {
       
   304     bool used[TypeCount] = { false };
       
   305 
       
   306     int combinable = 0;
       
   307     int i = _pending.count();
       
   308     while (i > 0) {
       
   309         int type = _pending.value(i - 1).first;
       
   310 
       
   311         // If this type has already appeared in this address, this must be part of the preceding address
       
   312         if (used[type])
       
   313             return combinable;
       
   314 
       
   315         // A suffix can only appear at the end of an address
       
   316         if (type == Suffix && (combinable > 0))
       
   317             return combinable;
       
   318 
       
   319         if (type == Comment) {
       
   320             // Comments can be combined with anything else...
       
   321         } else {
       
   322             // Everything else can be used once at most, and not with groups
       
   323             if (used[Group])
       
   324                 return combinable;
       
   325 
       
   326             if (type == Group) {
       
   327                 // Groups can only be combined with comments
       
   328                 if (used[Name] || used[Address] || used[Suffix])
       
   329                     return combinable;
       
   330             }
       
   331 
       
   332             used[type] = true;
       
   333         }
       
   334 
       
   335         // Combine this element
       
   336         ++combinable;
       
   337         --i;
       
   338     }
       
   339 
       
   340     return combinable;
       
   341 }
       
   342 
       
   343 void AddressListGenerator::processPending()
       
   344 {
       
   345     if (!_pending.isEmpty()) {
       
   346         // Compress any consecutive name parts into a single part
       
   347         for (int i = 1; i < _pending.count(); ) {
       
   348             TokenType type = _pending.value(i).first;
       
   349             // Also, a name could precede a group part, since the group name may contain multiple atoms
       
   350             if ((_pending.value(i - 1).first == Name) && ((type == Name) || (type == Group))) {
       
   351                 _pending.replace(i - 1, qMakePair(type, _pending.value(i - 1).second + _pending.value(i).second));
       
   352                 _pending.removeAt(i);
       
   353             } 
       
   354             else {
       
   355                 ++i;
       
   356             }
       
   357         }
       
   358 
       
   359         // Combine the tokens as necessary, proceding in reverse from the known boundary at the end
       
   360         QStringList addresses;
       
   361         int combinable = 0;
       
   362         while ((combinable = combinableElements()) != 0) {
       
   363             QString combined;
       
   364             while (combinable) {
       
   365                 combined.prepend(_pending.last().second);
       
   366                 _pending.removeLast();
       
   367                 --combinable;
       
   368             }
       
   369             addresses.append(combined);
       
   370         }
       
   371 
       
   372         // Add the address to the result set, in the original order
       
   373         for (int i = addresses.count(); i > 0; --i)
       
   374             _result.append(addresses.value(i - 1));
       
   375 
       
   376         _pending.clear();
       
   377     }
       
   378 }
       
   379 
       
   380 QStringList AddressListGenerator::result()
       
   381 {
       
   382     return _result;
       
   383 }
       
   384 
       
   385 static QStringList generateAddressList(const QString& list)
       
   386 {
       
   387     AddressListGenerator generator;
       
   388     generator.processCharacters(list);
       
   389     return generator.result();
       
   390 }
       
   391 
       
   392 static bool containsMultipleFields(const QString& input)
       
   393 {
       
   394     // There is no shortcut; we have to parse the addresses
       
   395     AddressListGenerator generator;
       
   396     generator.processCharacters(input);
       
   397     return (generator.result().count() > 1);
       
   398 }
       
   399 
       
   400 
       
   401 struct GroupDetector : public CharacterProcessor
       
   402 {
       
   403     GroupDetector();
       
   404 
       
   405     virtual void process(QChar, bool, bool, int);
       
   406 
       
   407     bool result() const;
       
   408 
       
   409 private:
       
   410     bool _nameDelimiter;
       
   411     bool _listTerminator;
       
   412 };
       
   413 
       
   414 GroupDetector::GroupDetector()
       
   415     : _nameDelimiter(false),
       
   416       _listTerminator(false)
       
   417 {
       
   418 }
       
   419 
       
   420 void GroupDetector::process(QChar character, bool quoted, bool escaped, int commentDepth)
       
   421 {
       
   422     if ( character == ':' && !_nameDelimiter && !quoted && !escaped && commentDepth == 0 )
       
   423         _nameDelimiter = true;
       
   424     else if ( character == ';' && !_listTerminator && _nameDelimiter && !quoted && !escaped && commentDepth == 0 )
       
   425         _listTerminator = true;
       
   426 }
       
   427 
       
   428 bool GroupDetector::result() const
       
   429 {
       
   430     return _listTerminator;
       
   431 }
       
   432 
       
   433 static bool containsGroupSpecifier(const QString& input)
       
   434 {
       
   435     GroupDetector detector;
       
   436     detector.processCharacters(input);
       
   437     return detector.result();
       
   438 }
       
   439 
       
   440 
       
   441 struct WhitespaceRemover : public CharacterProcessor
       
   442 {
       
   443     virtual void process(QChar, bool, bool, int);
       
   444 
       
   445     QString _result;
       
   446 };
       
   447 
       
   448 void WhitespaceRemover::process(QChar character, bool quoted, bool escaped, int commentDepth)
       
   449 {
       
   450     if ( !character.isSpace() || quoted || escaped || commentDepth > 0 )
       
   451         _result.append(character);
       
   452 }
       
   453 
       
   454 static QString removeWhitespace(const QString& input)
       
   455 {
       
   456     WhitespaceRemover remover;
       
   457     remover.processCharacters(input);
       
   458     return remover._result;
       
   459 }
       
   460 
       
   461 QPair<int, int> findDelimiters(const QString& text)
       
   462 {
       
   463     int first = -1;
       
   464     int second = -1;
       
   465 
       
   466     bool quoted = false;
       
   467     bool escaped = false;
       
   468 
       
   469     const QChar* const begin = text.constData();
       
   470     const QChar* const end = begin + text.length();
       
   471     for (const QChar* it = begin; it != end; ++it ) {
       
   472         if ( !escaped && ( *it == '\\' ) ) {
       
   473             escaped = true;
       
   474             continue;
       
   475         }
       
   476 
       
   477         if ( !quoted && *it == '"' && !escaped ) {
       
   478             quoted = true;
       
   479         }
       
   480         else if ( quoted && *it == '"' && !escaped ) {
       
   481             quoted = false;
       
   482         }
       
   483 
       
   484         if ( !quoted ) {
       
   485             if ( first == -1 && *it == '<' ) {
       
   486                 first = (it - begin);
       
   487             }
       
   488             else if ( second == -1 && *it == '>' ) {
       
   489                 second = (it - begin);
       
   490                 break;
       
   491             }
       
   492         }
       
   493 
       
   494         escaped = false;
       
   495     }
       
   496 
       
   497     return qMakePair(first, second);
       
   498 }
       
   499 
       
   500 void parseMailbox(QString& input, QString& name, QString& address, QString& suffix)
       
   501 {
       
   502     // See if there is a trailing suffix
       
   503     int pos = input.indexOf("/TYPE=");
       
   504     if (pos != -1)
       
   505     {
       
   506         suffix = input.mid(pos + 6);
       
   507         input = input.left(pos);
       
   508     }
       
   509 
       
   510     // Separate the email address from the name
       
   511     QPair<int, int> delimiters = findDelimiters(input);
       
   512 
       
   513     if (delimiters.first == -1 && delimiters.second == -1)
       
   514     {
       
   515         name = address = input.trimmed();
       
   516     }
       
   517     else 
       
   518     {
       
   519         if (delimiters.first == -1)
       
   520         {
       
   521             // Unmatched '>'
       
   522             address = input.left( delimiters.second );
       
   523         }
       
   524         else
       
   525         {
       
   526             name = input.left( delimiters.first );
       
   527 
       
   528             if (delimiters.second == -1)
       
   529                 address = input.right(input.length() - delimiters.first - 1);
       
   530             else
       
   531                 address = input.mid(delimiters.first + 1, (delimiters.second - delimiters.first - 1)).trimmed();
       
   532         }
       
   533 
       
   534         if ( name.isEmpty() ) 
       
   535             name = address;
       
   536     } 
       
   537 }
       
   538 
       
   539 }
       
   540 
       
   541 
       
   542 /* QMailAddress */
       
   543 class QMailAddressPrivate : public QSharedData
       
   544 {
       
   545 public:
       
   546     QMailAddressPrivate();
       
   547     QMailAddressPrivate(const QString& addressText);
       
   548     QMailAddressPrivate(const QString& name, const QString& address);
       
   549     QMailAddressPrivate(const QMailAddressPrivate& other);
       
   550     ~QMailAddressPrivate();
       
   551 
       
   552     bool isNull() const;
       
   553 
       
   554     bool isGroup() const;
       
   555     QList<QMailAddress> groupMembers() const;
       
   556 
       
   557     QString name() const;
       
   558     bool isPhoneNumber() const;
       
   559     bool isEmailAddress() const;
       
   560 
       
   561     QString minimalPhoneNumber() const;
       
   562 
       
   563     QString toString(bool forceDelimited) const;
       
   564 
       
   565     QString _name;
       
   566     QString _address;
       
   567     QString _suffix;
       
   568     bool _group;
       
   569 
       
   570     bool operator==(const QMailAddressPrivate& other) const;
       
   571 
       
   572     template <typename Stream>
       
   573     void serialize(Stream &stream) const;
       
   574 
       
   575     template <typename Stream>
       
   576     void deserialize(Stream &stream);
       
   577 
       
   578 private:
       
   579     void setComponents(const QString& nameText, const QString& addressText);
       
   580 
       
   581     mutable bool _searchCompleted;
       
   582 };
       
   583 
       
   584 QMailAddressPrivate::QMailAddressPrivate()
       
   585     : _group(false),
       
   586       _searchCompleted(false)
       
   587 {
       
   588 }
       
   589 
       
   590 QMailAddressPrivate::QMailAddressPrivate(const QString& addressText) 
       
   591     : _group(false),
       
   592       _searchCompleted(false)
       
   593 {
       
   594     if (!addressText.isEmpty())
       
   595     {
       
   596         QString input = addressText.trimmed();
       
   597 
       
   598         // See whether this address is a group
       
   599         if (containsGroupSpecifier(input))
       
   600         {
       
   601             QRegExp groupFormat("(.*):(.*);");
       
   602             if (groupFormat.indexIn(input) != -1)
       
   603             {
       
   604                 _name = groupFormat.cap(1).trimmed();
       
   605                 _address = groupFormat.cap(2).trimmed();
       
   606                 _group = true;
       
   607             }
       
   608         }
       
   609         else
       
   610         {
       
   611             parseMailbox(input, _name, _address, _suffix);
       
   612             setComponents(_name, _address);
       
   613         }
       
   614     }
       
   615 }
       
   616 
       
   617 QMailAddressPrivate::QMailAddressPrivate(const QString& name, const QString& address) 
       
   618     : _group(false),
       
   619       _searchCompleted(false)
       
   620 {
       
   621     // See whether the address part contains a group
       
   622     if (containsMultipleFields(address))
       
   623     {
       
   624         _name = name;
       
   625         _address = address;
       
   626         _group = true;
       
   627     }
       
   628     else
       
   629     {
       
   630         setComponents(name, address);
       
   631     }
       
   632 }
       
   633 
       
   634 void QMailAddressPrivate::setComponents(const QString& nameText, const QString& addressText )
       
   635 {
       
   636     _name = nameText.trimmed();
       
   637     _address = addressText.trimmed();
       
   638 
       
   639     int charIndex = _address.indexOf( "/TYPE=" );
       
   640     if ( charIndex != -1 ) {
       
   641         _suffix = _address.mid( charIndex + 6 );
       
   642         _address = _address.left( charIndex ).trimmed();
       
   643     }
       
   644 
       
   645     if ( ( charIndex = _address.indexOf( '<' ) ) != -1 )
       
   646         _address.remove( charIndex, 1 );
       
   647     if ( ( charIndex = _address.lastIndexOf( '>' ) ) != -1 )
       
   648         _address.remove( charIndex, 1 );
       
   649 }
       
   650 
       
   651 QMailAddressPrivate::QMailAddressPrivate(const QMailAddressPrivate& other) 
       
   652     : QSharedData(other),
       
   653       _searchCompleted(false)
       
   654 {
       
   655     _name = other._name;
       
   656     _address = other._address;
       
   657     _suffix = other._suffix;
       
   658     _group = other._group;
       
   659 }
       
   660 
       
   661 QMailAddressPrivate::~QMailAddressPrivate()
       
   662 {
       
   663 }
       
   664 
       
   665 bool QMailAddressPrivate::operator==(const QMailAddressPrivate& other) const
       
   666 {
       
   667     return (_name == other._name && _address == other._address && _suffix == other._suffix && _group == other._group);
       
   668 }
       
   669 
       
   670 bool QMailAddressPrivate::isNull() const
       
   671 {
       
   672     return (_name.isNull() && _address.isNull() && _suffix.isNull());
       
   673 }
       
   674 
       
   675 bool QMailAddressPrivate::isGroup() const
       
   676 {
       
   677     return _group;
       
   678 }
       
   679 
       
   680 QList<QMailAddress> QMailAddressPrivate::groupMembers() const
       
   681 {
       
   682     if (_group)
       
   683         return QMailAddress::fromStringList(_address);
       
   684 
       
   685     return QList<QMailAddress>();
       
   686 }
       
   687 
       
   688 // We need to keep a default copy of this, because constructing a new
       
   689 // one is expensive and sends multiple QCOP messages, whose responses it
       
   690 // will not survive to receive...
       
   691 
       
   692 QString QMailAddressPrivate::name() const
       
   693 {
       
   694     return QMail::unquoteString(_name);
       
   695 }
       
   696 
       
   697 bool QMailAddressPrivate::isPhoneNumber() const
       
   698 {
       
   699     static const QRegExp pattern(QMailAddress::phoneNumberPattern());
       
   700     return pattern.exactMatch(_address);
       
   701 }
       
   702 
       
   703 bool QMailAddressPrivate::isEmailAddress() const
       
   704 {
       
   705     static const QRegExp pattern(QMailAddress::emailAddressPattern());
       
   706     return pattern.exactMatch(QMailAddress::removeWhitespace(QMailAddress::removeComments(_address)));
       
   707 }
       
   708 
       
   709 QString QMailAddressPrivate::minimalPhoneNumber() const
       
   710 {
       
   711     static const QRegExp nondiallingChars("[^\\d,xpwXPW\\+\\*#]");
       
   712 
       
   713     // Remove any characters which don't affect dialling
       
   714     QString minimal(_address);
       
   715     minimal.remove(nondiallingChars);
       
   716 
       
   717     // Convert any 'p' or 'x' to comma
       
   718     minimal.replace(QRegExp("[xpXP]"), ",");
       
   719 
       
   720     // Ensure any permitted alphabetical chars are lower-case
       
   721     return minimal.toLower();
       
   722 }
       
   723 
       
   724 static bool needsQuotes(const QString& src)
       
   725 {
       
   726     static const QRegExp specials = QRegExp("[<>\\[\\]:;@\\\\,.]");
       
   727 
       
   728     QString characters(src);
       
   729 
       
   730     // Remove any quoted-pair characters, since they don't require quoting
       
   731     int index = 0;
       
   732     while ((index = characters.indexOf('\\', index)) != -1)
       
   733         characters.remove(index, 2);
       
   734 
       
   735     if ( specials.indexIn( characters ) != -1 )
       
   736         return true;
       
   737 
       
   738     // '(' and ')' also need quoting, if they don't conform to nested comments
       
   739     const QChar* it = characters.constData();
       
   740     const QChar* const end = it + characters.length();
       
   741 
       
   742     int commentDepth = 0;
       
   743     for (; it != end; ++it)
       
   744         if (*it == '(') {
       
   745             ++commentDepth;
       
   746         }
       
   747         else if (*it == ')') {
       
   748             if (--commentDepth < 0)
       
   749                 return true;
       
   750         }
       
   751 
       
   752     return (commentDepth != 0);
       
   753 }
       
   754 
       
   755 QString QMailAddressPrivate::toString(bool forceDelimited) const
       
   756 {
       
   757     QString result;
       
   758 
       
   759     if ( _name == _address )
       
   760         return _name;
       
   761 
       
   762     if ( _group ) {
       
   763         result.append( _name ).append( ": " ).append( _address ).append( ';' );
       
   764     } else {
       
   765         // If there are any 'special' characters in the name it needs to be quoted
       
   766         if ( !_name.isEmpty() )
       
   767             result = ( needsQuotes( _name ) ? QMail::quoteString( _name ) : _name );
       
   768 
       
   769         if ( !_address.isEmpty() ) {
       
   770             if ( !forceDelimited && result.isEmpty() ) {
       
   771                 result = _address;
       
   772             } else {
       
   773                 if ( !result.isEmpty() )
       
   774                     result.append( ' ' );
       
   775                 result.append( '<' ).append( _address ).append( '>' );
       
   776             }
       
   777         }
       
   778 
       
   779         if ( !_suffix.isEmpty() )
       
   780             result.append( " /TYPE=" ).append( _suffix );
       
   781     }
       
   782 
       
   783     return result;
       
   784 }
       
   785 
       
   786 template <typename Stream> 
       
   787 void QMailAddressPrivate::serialize(Stream &stream) const
       
   788 {
       
   789     stream << _name << _address << _suffix << _group;
       
   790 }
       
   791 
       
   792 template <typename Stream> 
       
   793 void QMailAddressPrivate::deserialize(Stream &stream)
       
   794 {
       
   795     _searchCompleted = false;
       
   796     stream >> _name >> _address >> _suffix >> _group;
       
   797 }
       
   798 
       
   799 
       
   800 /*!
       
   801     \class QMailAddress
       
   802 
       
   803     \brief The QMailAddress class provides an interface for manipulating message address strings.
       
   804     \ingroup messaginglibrary
       
   805 
       
   806     QMailAddress provides functions for splitting the address strings of messages into name and
       
   807     address components, and for combining the individual components into correctly formatted
       
   808     address strings.  QMailAddress can be used to manipulate the address elements exposed by the 
       
   809     QMailMessage class.
       
   810 
       
   811     Address strings are expected to use the format "name_part '<'address_part'>'", where 
       
   812     \i name_part describes a message sender or recipient and \i address_part defines the address 
       
   813     at which they can be contacted.  The address component is not validated, so it can contain an 
       
   814     email address, phone number, or any other type of textual address representation.
       
   815 
       
   816     \sa QMailMessage
       
   817 */
       
   818 
       
   819 /*!
       
   820     Constructs an empty QMailAddress object.
       
   821 */
       
   822 QMailAddress::QMailAddress()
       
   823 {
       
   824     d = new QMailAddressPrivate();
       
   825 }
       
   826 
       
   827 /*!
       
   828     Constructs a QMailAddress object, extracting the name and address components from \a addressText.
       
   829 
       
   830     If \a addressText cannot be separated into name and address components, both name() and address() 
       
   831     will return the entirety of \a addressText.
       
   832 
       
   833     \sa name(), address()
       
   834 */
       
   835 QMailAddress::QMailAddress(const QString& addressText)
       
   836 {
       
   837     d = new QMailAddressPrivate(addressText);
       
   838 }
       
   839 
       
   840 /*!
       
   841     Constructs a QMailAddress object with the given \a name and \a address.
       
   842 */
       
   843 QMailAddress::QMailAddress(const QString& name, const QString& address)
       
   844 {
       
   845     d = new QMailAddressPrivate(name, address);
       
   846 }
       
   847 
       
   848 /*! \internal */
       
   849 QMailAddress::QMailAddress(const QMailAddress& other)
       
   850 {
       
   851     this->operator=(other);
       
   852 }
       
   853 
       
   854 /*!
       
   855     Destroys a QMailAddress object.
       
   856 */
       
   857 QMailAddress::~QMailAddress()
       
   858 {
       
   859 }
       
   860 
       
   861 /*! \internal */
       
   862 const QMailAddress& QMailAddress::operator= (const QMailAddress& other)
       
   863 {
       
   864     d = other.d;
       
   865     return *this;
       
   866 }
       
   867 
       
   868 /*! \internal */
       
   869 bool QMailAddress::operator== (const QMailAddress& other) const
       
   870 {
       
   871     return d->operator==(*other.d);
       
   872 }
       
   873 
       
   874 /*! \internal */
       
   875 bool QMailAddress::operator!= (const QMailAddress& other) const
       
   876 {
       
   877     return !(d->operator==(*other.d));
       
   878 }
       
   879 
       
   880 /*!
       
   881     Returns true if the address object has not been initialized.
       
   882 */
       
   883 bool QMailAddress::isNull() const
       
   884 {
       
   885     return d->isNull();
       
   886 }
       
   887 
       
   888 /*!
       
   889     Returns the name component of a mail address string.
       
   890 */
       
   891 QString QMailAddress::name() const
       
   892 {
       
   893     return d->name();
       
   894 }
       
   895 
       
   896 /*!
       
   897     Returns the address component of a mail address string.
       
   898 */
       
   899 QString QMailAddress::address() const
       
   900 {
       
   901     return d->_address;
       
   902 }
       
   903 
       
   904 /*!
       
   905     Returns true if the address is that of a group.
       
   906 */
       
   907 bool QMailAddress::isGroup() const
       
   908 {
       
   909     return d->isGroup();
       
   910 }
       
   911 
       
   912 /*!
       
   913     Returns a list containing the individual addresses that comprise the address group.  
       
   914     If the address is not a group address, an empty list is returned.
       
   915 
       
   916     \sa isGroup()
       
   917 */
       
   918 QList<QMailAddress> QMailAddress::groupMembers() const
       
   919 {
       
   920     return d->groupMembers();
       
   921 }
       
   922 
       
   923 /*!
       
   924     Returns true if the address component has the form of a phone number; otherwise returns false.
       
   925 
       
   926     \sa isEmailAddress()
       
   927 */
       
   928 bool QMailAddress::isPhoneNumber() const
       
   929 {
       
   930     return d->isPhoneNumber();
       
   931 }
       
   932 
       
   933 /*!
       
   934     Returns true if the address component has the form of an email address; otherwise returns false.
       
   935 
       
   936     \sa isPhoneNumber()
       
   937 */
       
   938 bool QMailAddress::isEmailAddress() const
       
   939 {
       
   940     return d->isEmailAddress();
       
   941 }
       
   942 
       
   943 /*! \internal */
       
   944 QString QMailAddress::minimalPhoneNumber() const
       
   945 {
       
   946     return d->minimalPhoneNumber();
       
   947 }
       
   948 
       
   949 /*!
       
   950     Returns a string containing the name and address in a standardised format.
       
   951     If \a forceDelimited is true, the address element will be delimited by '<' and '>', even if this is unnecessary.
       
   952 */
       
   953 QString QMailAddress::toString(bool forceDelimited) const
       
   954 {
       
   955     return d->toString(forceDelimited);
       
   956 }
       
   957 
       
   958 /*!
       
   959     Returns a string list containing the result of calling toString() on each address in \a list.
       
   960     If \a forceDelimited is true, the address elements will be delimited by '<' and '>', even if this is unnecessary.
       
   961 
       
   962     \sa toString()
       
   963 */
       
   964 QStringList QMailAddress::toStringList(const QList<QMailAddress>& list, bool forceDelimited)
       
   965 {
       
   966     QStringList result;
       
   967 
       
   968     foreach (const QMailAddress& address, list)
       
   969         result.append(address.toString(forceDelimited));
       
   970 
       
   971     return result;
       
   972 }
       
   973 
       
   974 /*!
       
   975     Returns a list containing a QMailAddress object constructed from each 
       
   976     comma-separated address in \a list.
       
   977 */
       
   978 QList<QMailAddress> QMailAddress::fromStringList(const QString& list)
       
   979 {
       
   980     return fromStringList(generateAddressList(list));
       
   981 }
       
   982 
       
   983 /*!
       
   984     Returns a list containing a QMailAddress object constructed from each 
       
   985     address string in \a list.
       
   986 */
       
   987 QList<QMailAddress> QMailAddress::fromStringList(const QStringList& list)
       
   988 {
       
   989     QList<QMailAddress> result;
       
   990 
       
   991     foreach (const QString& address, list)
       
   992         result.append(QMailAddress(address));
       
   993 
       
   994     return result;
       
   995 }
       
   996 
       
   997 /*!
       
   998     Returns the content of \a input with any comment sections removed.
       
   999     Any subsequent leading or trailing whitespace is then also removed.
       
  1000 */
       
  1001 QString QMailAddress::removeComments(const QString& input)
       
  1002 {
       
  1003     return ::removeComments(input, &QChar::isPrint).trimmed();
       
  1004 }
       
  1005 
       
  1006 /*!
       
  1007     Returns the content of \a input with any whitespace characters removed.
       
  1008     Whitespace within quotes or comment sections is preserved.
       
  1009 */
       
  1010 QString QMailAddress::removeWhitespace(const QString& input)
       
  1011 {
       
  1012     return ::removeWhitespace(input);
       
  1013 }
       
  1014 
       
  1015 /*! \internal */
       
  1016 QString QMailAddress::phoneNumberPattern()
       
  1017 {
       
  1018     static const QString pattern("\"?"                              // zero-or-one:'"'
       
  1019                                  "("                                // start capture
       
  1020                                  "(?:\\+ ?)?"                       // zero-or-one:('+', zero-or-one:space)
       
  1021                                  "(?:\\(\\d+\\)[ -]?)?"             // zero-or-one:'(', one-or-more:digits, ')', zero-or-one:separator 
       
  1022                                  "(?:\\d{1,14})"                    // one:(one-to-fourteen):digits
       
  1023                                  "(?:[ -]?[\\d#\\*]{1,10}){0,4}"    // zero-to-four:(zero-or-one:separator), one-to-ten:(digits | '#' | '*')
       
  1024                                  "(?:"                              // zero-or-one:
       
  1025                                      "[ -]?"                            // zero-or-one:separator,
       
  1026                                      "\\(?"                             // zero-or-one:'('
       
  1027                                      "[,xpwXPW]\\d{1,4}"                // one:extension, one-to-four:digits
       
  1028                                      "\\)?"                             // zero-or-one:')'
       
  1029                                  ")?"                               // end of optional group
       
  1030                                  ")"                                // end capture
       
  1031                                  "\"?");                            // zero-or-one:'"'
       
  1032 
       
  1033     return pattern;
       
  1034 }
       
  1035 
       
  1036 /*! \internal */
       
  1037 QString QMailAddress::emailAddressPattern()
       
  1038 {
       
  1039     // Taken from: http://www.regular-expressions.info/email.html, but 
       
  1040     // modified to accept uppercase characters as well as lower-case
       
  1041     // Also - RFC 1034 seems to prohibit domain name elements beginning
       
  1042     // with digits, but they exist in practise...
       
  1043     static const QString pattern("[A-Za-z\\d!#$%&'*+/=?^_`{|}~-]+"      // one-or-more: legal chars (some punctuation permissible)
       
  1044                                  "(?:"                                  // zero-or-more: 
       
  1045                                      "\\."                                  // '.',
       
  1046                                      "[A-Za-z\\d!#$%&'*+/=?^_`{|}~-]+"      // one-or-more: legal chars
       
  1047                                  ")*"                                   // end of optional group
       
  1048                                  "@"                                    // '@'
       
  1049                                  "(?:"                                  // either:
       
  1050                                      "localhost"                            // 'localhost'
       
  1051                                  "|"                                    // or:
       
  1052                                      "(?:"                                  // one-or-more: 
       
  1053                                          "[A-Za-z\\d]"                          // one: legal char, 
       
  1054                                          "(?:"                                  // zero-or-one:
       
  1055                                              "[A-Za-z\\d-]*[A-Za-z\\d]"             // (zero-or-more: (legal char or '-'), one: legal char)
       
  1056                                          ")?"                                   // end of optional group
       
  1057                                          "\\."                                  // '.'
       
  1058                                      ")+"                                   // end of mandatory group
       
  1059                                      "[A-Za-z\\d]"                          // one: legal char
       
  1060                                      "(?:"                                  // zero-or-one:
       
  1061                                          "[A-Za-z\\d-]*[A-Za-z\\d]"             // (zero-or-more: (legal char or '-'), one: legal char)
       
  1062                                      ")?"                                   // end of optional group
       
  1063                                  ")");                                  // end of alternation
       
  1064 
       
  1065     return pattern;
       
  1066 }
       
  1067 
       
  1068 /*! 
       
  1069     \fn QMailAddress::serialize(Stream&) const
       
  1070     \internal 
       
  1071 */
       
  1072 template <typename Stream> 
       
  1073 void QMailAddress::serialize(Stream &stream) const
       
  1074 {
       
  1075     d->serialize(stream);
       
  1076 }
       
  1077 
       
  1078 /*! 
       
  1079     \fn QMailAddress::deserialize(Stream&)
       
  1080     \internal 
       
  1081 */
       
  1082 template <typename Stream> 
       
  1083 void QMailAddress::deserialize(Stream &stream)
       
  1084 {
       
  1085     d->deserialize(stream);
       
  1086 }
       
  1087 
       
  1088 Q_IMPLEMENT_USER_METATYPE(QMailAddress)
       
  1089 
       
  1090 Q_IMPLEMENT_USER_METATYPE_TYPEDEF(QMailAddressList, QMailAddressList)
       
  1091 
       
  1092 
       
  1093 //Q_IMPLEMENT_USER_METATYPE_NO_OPERATORS(QList<QMailAddress>)
       
  1094